main
vue 135 lines 2.81 KB
Raw
1 <template>
2 <n-card content-class="p-0!" :hoverable="clickable" :class="{ 'group cursor-pointer': clickable }" size="small">
3 <div
4 class="card-header border-border flex items-center justify-between gap-4 overflow-hidden border-b px-4 py-3"
5 :class="{ 'group-hover:text-primary': clickable }"
6 >
7 <div class="truncate text-base">{{ title }}</div>
8 <Icon v-if="clickable" :name="icon || 'carbon:arrow-up-right'" />
9 <slot name="header-extra" />
10 </div>
11
12 <div class="card-content divide-border grid grow grid-cols-[repeat(auto-fit,minmax(0,1fr))] divide-x">
13 <div
14 v-for="item of values"
15 :key="JSON.stringify(item)"
16 class="value-box flex flex-col"
17 :class="item.status"
18 >
19 <div class="value flex grow items-center justify-center">
20 {{ item.value }}
21 </div>
22 <div
23 v-if="item.label"
24 class="label"
25 :class="{ 'hover:text-primary! cursor-pointer transition-colors': selectable }"
26 @click="emit('select', item)"
27 >
28 {{ item.label }}
29 <Icon v-if="selectable" :name="icon || 'carbon:arrow-up-right'" />
30 </div>
31 </div>
32 </div>
33 </n-card>
34 </template>
35
36 <script setup lang="ts">
37 import { NCard } from "naive-ui"
38 import Icon from "@/components/common/Icon.vue"
39
40 export interface ItemProps {
41 value: number | string
42 label?: string
43 status?: "success" | "warning" | "error"
44 }
45
46 defineProps<{
47 title: string
48 values: ItemProps[]
49 clickable?: boolean
50 icon?: string
51 selectable?: boolean
52 }>()
53
54 const emit = defineEmits<{
55 select: [item: ItemProps]
56 }>()
57 </script>
58
59 <style scoped lang="scss">
60 .n-card {
61 overflow: hidden;
62
63 .card-content {
64 .value-box {
65 text-align: center;
66 overflow: hidden;
67
68 .value {
69 font-family: var(--font-family-display);
70 padding: 10px 6px;
71 font-size: 22px;
72 text-overflow: ellipsis;
73 white-space: nowrap;
74 overflow: hidden;
75 font-weight: bold;
76 text-overflow: ellipsis;
77 white-space: nowrap;
78 line-height: 1;
79 overflow: hidden;
80 }
81
82 .label {
83 font-family: var(--font-family-mono);
84 border-top: 1px solid var(--border-color);
85 color: var(--fg-secondary-color);
86 font-size: 13px;
87 padding: 6px;
88 line-height: 1;
89 background-color: var(--bg-secondary-color);
90 text-overflow: ellipsis;
91 white-space: nowrap;
92 overflow: hidden;
93 text-transform: uppercase;
94 display: flex;
95 align-items: center;
96 justify-content: center;
97 gap: 4px;
98 }
99
100 &.success {
101 .value {
102 color: var(--success-color);
103 }
104 .label {
105 color: var(--success-color);
106 }
107 }
108
109 &.warning {
110 .value {
111 color: var(--warning-color);
112 }
113 .label {
114 color: var(--warning-color);
115 }
116 }
117
118 &.error {
119 .value {
120 color: var(--error-color);
121 }
122 .label {
123 color: var(--error-color);
124 }
125 }
126 }
127 }
128
129 &.hovered {
130 &:hover {
131 border-color: var(--primary-color);
132 }
133 }
134 }
135 </style>