minitest2.0 0.0.5 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,200 @@
1
1
  <script setup lang="ts">
2
2
  defineOptions({ name: 'DashboardPage' })
3
+
4
+ type DashboardMetric = {
5
+ label: string
6
+ symbol: string
7
+ amount: number
8
+ }
9
+
10
+ type DashboardCard = {
11
+ name: string
12
+ code: string
13
+ symbol?: string
14
+ amount?: number
15
+ metrics?: DashboardMetric[]
16
+ image: string
17
+ accent: string
18
+ position?: string
19
+ }
20
+
21
+ type UnitLabel = '亿' | '千万' | '百万' | '十万' | '万' | '原币'
22
+
23
+ const dashboardImage = (file: string) => `${import.meta.env.BASE_URL}images/dashboard/${file}`
24
+
25
+ const unitOptions: Array<{ label: UnitLabel; factor: number }> = [
26
+ { label: '亿', factor: 100000000 },
27
+ { label: '千万', factor: 10000000 },
28
+ { label: '百万', factor: 1000000 },
29
+ { label: '十万', factor: 100000 },
30
+ { label: '万', factor: 10000 },
31
+ { label: '原币', factor: 1 },
32
+ ]
33
+
34
+ const selectedUnit = ref<UnitLabel>('原币')
35
+
36
+ const selectedUnitFactor = computed(
37
+ () => unitOptions.find((unit) => unit.label === selectedUnit.value)?.factor || 1,
38
+ )
39
+
40
+ const cards: DashboardCard[] = [
41
+ {
42
+ name: '人民币',
43
+ code: 'CNY',
44
+ symbol: '¥',
45
+ amount: 8888888.88,
46
+ image: dashboardImage('01-rmb-great-wall-bg.jpg'),
47
+ accent: '#df2f38',
48
+ position: 'center center',
49
+ },
50
+ {
51
+ name: '城银清',
52
+ code: 'CNY',
53
+ symbol: '¥',
54
+ amount: 6888888.88,
55
+ image: dashboardImage('02-city-clearing-skyline-bg.jpg'),
56
+ accent: '#0f4ca7',
57
+ position: 'center center',
58
+ },
59
+ {
60
+ name: '跨境人民币',
61
+ code: 'CNY',
62
+ symbol: '¥',
63
+ amount: 1888888.88,
64
+ image: dashboardImage('03-cross-border-rmb-world-map-bg.jpg'),
65
+ accent: '#078b95',
66
+ position: 'center center',
67
+ },
68
+ {
69
+ name: '数字货币',
70
+ code: 'CNY',
71
+ symbol: '¥',
72
+ amount: 888888.88,
73
+ image: dashboardImage('04-digital-currency-fintech-bg.jpg'),
74
+ accent: '#4423b7',
75
+ position: 'center center',
76
+ },
77
+ {
78
+ name: '美元',
79
+ code: 'USD',
80
+ metrics: [
81
+ { label: '境内', symbol: '$', amount: 888888.88 },
82
+ { label: '境外', symbol: '$', amount: 888888.88 },
83
+ ],
84
+ image: dashboardImage('05-usd-liberty-bg.jpg'),
85
+ accent: '#0d8b3f',
86
+ position: 'center center',
87
+ },
88
+ {
89
+ name: '欧元',
90
+ code: 'EUR',
91
+ metrics: [
92
+ { label: '境内', symbol: '€', amount: 888888.88 },
93
+ { label: '境外', symbol: '€', amount: 888888.88 },
94
+ ],
95
+ image: dashboardImage('06-eur-europe-architecture-bg.jpg'),
96
+ accent: '#1267d5',
97
+ position: 'center center',
98
+ },
99
+ {
100
+ name: '港元',
101
+ code: 'HKD',
102
+ metrics: [
103
+ { label: '境内', symbol: 'HK$', amount: 888888.88 },
104
+ { label: '境外', symbol: 'HK$', amount: 888888.88 },
105
+ ],
106
+ image: dashboardImage('07-hkd-hong-kong-skyline-bg.jpg'),
107
+ accent: '#e06b00',
108
+ position: 'center center',
109
+ },
110
+ {
111
+ name: '日元',
112
+ code: 'JPY',
113
+ metrics: [
114
+ { label: '境内', symbol: '¥', amount: 888888.88 },
115
+ { label: '境外', symbol: '¥', amount: 888888.88 },
116
+ ],
117
+ image: dashboardImage('08-jpy-mount-fuji-bg.jpg'),
118
+ accent: '#6125c8',
119
+ position: 'center center',
120
+ },
121
+ ]
122
+
123
+ function getCardStyle(card: DashboardCard) {
124
+ return {
125
+ '--dashboard-accent': card.accent,
126
+ backgroundImage: `url("${card.image}")`,
127
+ backgroundPosition: card.position || 'center center',
128
+ }
129
+ }
130
+
131
+ function getCardAriaLabel(card: DashboardCard) {
132
+ if (card.metrics) {
133
+ const metrics = card.metrics
134
+ .map((metric) => `${metric.label}${formatMoney(metric.amount, metric.symbol)}`)
135
+ .join(',')
136
+
137
+ return `${card.name},${card.code},单位${selectedUnit.value},${metrics}`
138
+ }
139
+
140
+ return `${card.name},${card.code},单位${selectedUnit.value},${formatMoney(card.amount || 0, card.symbol || '')}`
141
+ }
142
+
143
+ function formatMoney(amount: number, symbol: string) {
144
+ const scaledAmount = amount / selectedUnitFactor.value
145
+ const formattedAmount = scaledAmount.toLocaleString('en-US', {
146
+ minimumFractionDigits: 2,
147
+ maximumFractionDigits: 2,
148
+ })
149
+ const unitSuffix = selectedUnit.value === '原币' ? '' : ` ${selectedUnit.value}`
150
+
151
+ return `${symbol} ${formattedAmount}${unitSuffix}`
152
+ }
3
153
  </script>
4
154
 
5
155
  <template>
6
- <main class="dashboard-page"></main>
156
+ <main class="dashboard-page">
157
+ <div class="dashboard-toolbar" aria-label="看板设置">
158
+ <label class="unit-switch">
159
+ <span class="unit-switch__label">单位</span>
160
+ <select v-model="selectedUnit" class="unit-switch__select" aria-label="切换金额单位">
161
+ <option v-for="unit in unitOptions" :key="unit.label" :value="unit.label">
162
+ {{ unit.label }}
163
+ </option>
164
+ </select>
165
+ <van-icon class="unit-switch__icon" name="arrow-down" aria-hidden="true" />
166
+ </label>
167
+ </div>
168
+
169
+ <section class="dashboard-list" aria-label="资金看板">
170
+ <article
171
+ v-for="card in cards"
172
+ :key="card.name"
173
+ class="dashboard-card"
174
+ :class="{ 'dashboard-card--split': card.metrics }"
175
+ :style="getCardStyle(card)"
176
+ :aria-label="getCardAriaLabel(card)"
177
+ >
178
+ <header class="dashboard-card__header">
179
+ <h2 class="dashboard-card__name">{{ card.name }}</h2>
180
+ <span class="dashboard-card__code">{{ card.code }}</span>
181
+ </header>
182
+
183
+ <p v-if="card.amount" class="dashboard-card__amount">
184
+ {{ formatMoney(card.amount, card.symbol || '') }}
185
+ </p>
186
+
187
+ <div v-else class="dashboard-card__metrics">
188
+ <div v-for="metric in card.metrics" :key="metric.label" class="dashboard-card__metric">
189
+ <span class="dashboard-card__metric-label">{{ metric.label }}</span>
190
+ <strong class="dashboard-card__metric-value">
191
+ {{ formatMoney(metric.amount, metric.symbol) }}
192
+ </strong>
193
+ </div>
194
+ </div>
195
+ </article>
196
+ </section>
197
+ </main>
7
198
  </template>
8
199
 
9
200
  <style scoped>
@@ -12,7 +203,224 @@ defineOptions({ name: 'DashboardPage' })
12
203
  max-width: 750px;
13
204
  min-height: var(--app-page-min-height, 100vh);
14
205
  margin: 0 auto;
15
- background: #f7f8fa;
206
+ padding: 8px 12px 10px;
207
+ color: #ffffff;
208
+ background: #ffffff;
16
209
  box-sizing: border-box;
17
210
  }
211
+
212
+ .dashboard-toolbar {
213
+ display: flex;
214
+ justify-content: flex-end;
215
+ margin-bottom: 6px;
216
+ }
217
+
218
+ .unit-switch {
219
+ position: relative;
220
+ display: inline-flex;
221
+ align-items: center;
222
+ height: 24px;
223
+ overflow: hidden;
224
+ border: 1px solid #e8ebf0;
225
+ border-radius: 5px;
226
+ background: #ffffff;
227
+ box-shadow: 0 2px 7px rgba(31, 45, 62, 0.07);
228
+ }
229
+
230
+ .unit-switch__label {
231
+ padding: 0 6px 0 8px;
232
+ color: #5f6672;
233
+ font-size: 12px;
234
+ font-weight: 700;
235
+ line-height: 1;
236
+ }
237
+
238
+ .unit-switch__select {
239
+ width: 56px;
240
+ height: 100%;
241
+ border: 0;
242
+ border-left: 1px solid #eef0f4;
243
+ padding: 0 21px 0 8px;
244
+ color: #171b20;
245
+ font: inherit;
246
+ font-size: 12px;
247
+ font-weight: 800;
248
+ line-height: 24px;
249
+ background: transparent;
250
+ outline: none;
251
+ appearance: none;
252
+ }
253
+
254
+ .unit-switch__icon {
255
+ position: absolute;
256
+ right: 7px;
257
+ top: 50%;
258
+ color: #7d8591;
259
+ font-size: 11px;
260
+ pointer-events: none;
261
+ transform: translateY(-50%);
262
+ }
263
+
264
+ .dashboard-list {
265
+ display: grid;
266
+ gap: 6px;
267
+ }
268
+
269
+ .dashboard-card {
270
+ position: relative;
271
+ isolation: isolate;
272
+ overflow: hidden;
273
+ aspect-ratio: 351 / 80;
274
+ border-radius: 5px;
275
+ background-repeat: no-repeat;
276
+ background-size: cover;
277
+ box-shadow:
278
+ 0 2px 7px rgba(31, 45, 62, 0.13),
279
+ inset 0 0 0 1px rgba(255, 255, 255, 0.18);
280
+ }
281
+
282
+ .dashboard-card::before {
283
+ content: '';
284
+ position: absolute;
285
+ inset: 0;
286
+ z-index: -1;
287
+ background:
288
+ radial-gradient(circle at 51% 56%, rgba(255, 255, 255, 0.13), transparent 28%),
289
+ linear-gradient(90deg, rgba(255, 255, 255, 0.03), rgba(255, 255, 255, 0));
290
+ pointer-events: none;
291
+ }
292
+
293
+ .dashboard-card__header {
294
+ position: absolute;
295
+ inset: 0;
296
+ pointer-events: none;
297
+ }
298
+
299
+ .dashboard-card__name,
300
+ .dashboard-card__code,
301
+ .dashboard-card__amount,
302
+ .dashboard-card__metric-label,
303
+ .dashboard-card__metric-value {
304
+ margin: 0;
305
+ letter-spacing: 0;
306
+ }
307
+
308
+ .dashboard-card__name {
309
+ position: absolute;
310
+ top: 12px;
311
+ left: 12px;
312
+ color: var(--dashboard-accent);
313
+ font-size: 20px;
314
+ font-weight: 800;
315
+ line-height: 1;
316
+ }
317
+
318
+ .dashboard-card__code {
319
+ position: absolute;
320
+ top: 13px;
321
+ right: 13px;
322
+ color: var(--dashboard-accent);
323
+ font-size: 13px;
324
+ font-weight: 700;
325
+ line-height: 1;
326
+ }
327
+
328
+ .dashboard-card__amount {
329
+ position: absolute;
330
+ left: 0;
331
+ right: 0;
332
+ top: 40px;
333
+ overflow: hidden;
334
+ padding: 0 18px;
335
+ color: #ffffff;
336
+ font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Microsoft YaHei', sans-serif;
337
+ font-size: 21px;
338
+ font-weight: 500;
339
+ line-height: 1;
340
+ text-align: center;
341
+ text-overflow: ellipsis;
342
+ white-space: nowrap;
343
+ -webkit-font-smoothing: antialiased;
344
+ text-shadow:
345
+ 0 2px 4px rgba(57, 73, 91, 0.18),
346
+ 0 0 1px rgba(255, 255, 255, 0.28);
347
+ }
348
+
349
+ .dashboard-card__metrics {
350
+ position: absolute;
351
+ left: 52px;
352
+ right: 16px;
353
+ bottom: 13px;
354
+ display: grid;
355
+ grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
356
+ align-items: end;
357
+ column-gap: 29px;
358
+ }
359
+
360
+ .dashboard-card__metrics::before {
361
+ content: '';
362
+ position: absolute;
363
+ left: 50%;
364
+ bottom: 4px;
365
+ width: 1px;
366
+ height: 31px;
367
+ background: rgba(255, 255, 255, 0.78);
368
+ transform: translateX(-15px);
369
+ }
370
+
371
+ .dashboard-card__metric {
372
+ position: relative;
373
+ z-index: 1;
374
+ display: grid;
375
+ min-width: 0;
376
+ gap: 4px;
377
+ }
378
+
379
+ .dashboard-card__metric-label {
380
+ color: var(--dashboard-accent);
381
+ font-size: 12px;
382
+ font-weight: 700;
383
+ line-height: 1;
384
+ }
385
+
386
+ .dashboard-card__metric-value {
387
+ overflow: hidden;
388
+ color: #ffffff;
389
+ font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Microsoft YaHei', sans-serif;
390
+ font-size: 15px;
391
+ font-weight: 500;
392
+ line-height: 1;
393
+ text-overflow: ellipsis;
394
+ white-space: nowrap;
395
+ -webkit-font-smoothing: antialiased;
396
+ text-shadow:
397
+ 0 2px 4px rgba(57, 73, 91, 0.18),
398
+ 0 0 1px rgba(255, 255, 255, 0.28);
399
+ }
400
+
401
+ @media (max-width: 340px) {
402
+ .dashboard-page {
403
+ padding-right: 9px;
404
+ padding-left: 9px;
405
+ }
406
+
407
+ .dashboard-card__name {
408
+ font-size: 18px;
409
+ }
410
+
411
+ .dashboard-card__amount {
412
+ top: 38px;
413
+ font-size: 19px;
414
+ }
415
+
416
+ .dashboard-card__metrics {
417
+ left: 46px;
418
+ right: 11px;
419
+ column-gap: 22px;
420
+ }
421
+
422
+ .dashboard-card__metric-value {
423
+ font-size: 14px;
424
+ }
425
+ }
18
426
  </style>