xto-fronted 0.4.72 → 0.4.74

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,283 +1,545 @@
1
1
  <script setup lang="ts">
2
2
  import { ref } from 'vue'
3
- import { Card, Tag, Progress } from '@xto/data'
3
+ import { Progress } from '@xto/data'
4
4
 
5
5
  const stats = ref([
6
- { title: '用户总数', value: 1234, icon: '👥', color: '#409eff' },
7
- { title: '今日访问', value: 567, icon: '👀', color: '#67c23a' },
8
- { title: '订单数量', value: 890, icon: '📦', color: '#e6a23c' },
9
- { title: '销售金额', value: 123456, icon: '💰', color: '#f56c6c' }
6
+ { title: '用户总数', value: 1234, icon: '👥', color: '#1677ff', trend: '+12.5%', trendUp: true },
7
+ { title: '今日访问', value: 567, icon: '👀', color: '#52c41a', trend: '+8.2%', trendUp: true },
8
+ { title: '订单数量', value: 890, icon: '📦', color: '#faad14', trend: '-3.1%', trendUp: false },
9
+ { title: '销售金额', value: 123456, icon: '💰', color: '#ff4d4f', trend: '+15.8%', trendUp: true }
10
10
  ])
11
11
 
12
12
  const activities = ref([
13
- { user: '张三', action: '登录系统', time: '2分钟前', type: 'success' },
14
- { user: '李四', action: '修改了用户信息', time: '5分钟前', type: 'warning' },
15
- { user: '王五', action: '创建了新订单', time: '10分钟前', type: 'info' },
16
- { user: '赵六', action: '删除了测试数据', time: '30分钟前', type: 'danger' },
17
- { user: '钱七', action: '更新了系统配置', time: '1小时前', type: 'primary' }
13
+ { user: '张三', action: '登录系统', time: '2分钟前', type: 'success', avatar: '张' },
14
+ { user: '李四', action: '修改了用户信息', time: '5分钟前', type: 'warning', avatar: '李' },
15
+ { user: '王五', action: '创建了新订单', time: '10分钟前', type: 'info', avatar: '王' },
16
+ { user: '赵六', action: '删除了测试数据', time: '30分钟前', type: 'danger', avatar: '赵' },
17
+ { user: '钱七', action: '更新了系统配置', time: '1小时前', type: 'primary', avatar: '钱' }
18
18
  ])
19
19
 
20
20
  const quickLinks = ref([
21
- { title: '用户管理', path: '/system/user', icon: '👤' },
22
- { title: '角色管理', path: '/system/role', icon: '👥' },
23
- { title: '菜单管理', path: '/system/menu', icon: '📋' },
24
- { title: '系统设置', path: '/system', icon: '⚙️' }
21
+ { title: '用户管理', path: '/system/user', icon: '👤', desc: '管理系统用户' },
22
+ { title: '角色管理', path: '/system/role', icon: '👥', desc: '角色权限配置' },
23
+ { title: '菜单管理', path: '/system/menu', icon: '📋', desc: '菜单路由管理' },
24
+ { title: '系统设置', path: '/system', icon: '⚙️', desc: '系统参数配置' }
25
+ ])
26
+
27
+ const systemInfo = ref([
28
+ { label: '系统版本', value: 'v1.0.0' },
29
+ { label: 'Vue 版本', value: '3.4.21' },
30
+ { label: '构建工具', value: 'Vite 5' },
31
+ { label: 'UI 组件库', value: 'XTO UI' }
25
32
  ])
26
33
  </script>
27
34
 
28
35
  <template>
29
36
  <div class="dashboard">
30
37
  <!-- 统计卡片 -->
31
- <div class="dashboard__stats">
32
- <Card v-for="stat in stats" :key="stat.title" class="stat-card">
33
- <div class="stat-card__content">
34
- <div class="stat-card__icon" :style="{ backgroundColor: stat.color + '20' }">
35
- {{ stat.icon }}
38
+ <div class="stats-section">
39
+ <div v-for="stat in stats" :key="stat.title" class="stat-card">
40
+ <div class="stat-icon" :style="{ background: `linear-gradient(135deg, ${stat.color}20 0%, ${stat.color}10 100%)` }">
41
+ <span>{{ stat.icon }}</span>
42
+ </div>
43
+ <div class="stat-content">
44
+ <div class="stat-title">{{ stat.title }}</div>
45
+ <div class="stat-value" :style="{ color: stat.color }">
46
+ {{ stat.value.toLocaleString() }}
36
47
  </div>
37
- <div class="stat-card__info">
38
- <div class="stat-card__title">{{ stat.title }}</div>
39
- <div class="stat-card__value" :style="{ color: stat.color }">
40
- {{ stat.value.toLocaleString() }}
41
- </div>
48
+ <div class="stat-trend" :class="{ up: stat.trendUp, down: !stat.trendUp }">
49
+ <svg v-if="stat.trendUp" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
50
+ <path d="M7 17l5-5 5 5M7 7l5 5 5-5"/>
51
+ </svg>
52
+ <svg v-else viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
53
+ <path d="M7 7l5 5 5-5M7 17l5-5 5 5"/>
54
+ </svg>
55
+ <span>{{ stat.trend }}</span>
42
56
  </div>
43
57
  </div>
44
- </Card>
58
+ <div class="stat-decoration" :style="{ background: `linear-gradient(135deg, ${stat.color}08 0%, transparent 100%)` }"></div>
59
+ </div>
45
60
  </div>
46
61
 
47
62
  <!-- 主体内容 -->
48
- <div class="dashboard__main">
63
+ <div class="main-section">
49
64
  <!-- 快捷入口 -->
50
- <Card class="dashboard__quick">
51
- <template #header>
52
- <span class="card-title">快捷入口</span>
53
- </template>
54
- <div class="quick-links">
65
+ <div class="quick-section">
66
+ <div class="section-header">
67
+ <h3>快捷入口</h3>
68
+ <span class="section-badge">4 个入口</span>
69
+ </div>
70
+ <div class="quick-grid">
55
71
  <router-link
56
72
  v-for="link in quickLinks"
57
73
  :key="link.path"
58
74
  :to="link.path"
59
- class="quick-link"
75
+ class="quick-card"
60
76
  >
61
- <span class="quick-link__icon">{{ link.icon }}</span>
62
- <span class="quick-link__title">{{ link.title }}</span>
77
+ <div class="quick-icon">{{ link.icon }}</div>
78
+ <div class="quick-info">
79
+ <div class="quick-title">{{ link.title }}</div>
80
+ <div class="quick-desc">{{ link.desc }}</div>
81
+ </div>
82
+ <div class="quick-arrow">
83
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
84
+ <path d="M9 18l6-6-6-6"/>
85
+ </svg>
86
+ </div>
63
87
  </router-link>
64
88
  </div>
65
- </Card>
89
+ </div>
66
90
 
67
91
  <!-- 最近活动 -->
68
- <Card class="dashboard__activity">
69
- <template #header>
70
- <span class="card-title">最近活动</span>
71
- </template>
92
+ <div class="activity-section">
93
+ <div class="section-header">
94
+ <h3>最近活动</h3>
95
+ <a href="#" class="section-link">查看全部</a>
96
+ </div>
72
97
  <div class="activity-list">
73
98
  <div v-for="(activity, index) in activities" :key="index" class="activity-item">
74
- <Tag :type="activity.type as any" size="small">{{ activity.user }}</Tag>
75
- <span class="activity-item__action">{{ activity.action }}</span>
76
- <span class="activity-item__time">{{ activity.time }}</span>
99
+ <div class="activity-avatar" :class="activity.type">
100
+ {{ activity.avatar }}
101
+ </div>
102
+ <div class="activity-content">
103
+ <div class="activity-main">
104
+ <span class="activity-user">{{ activity.user }}</span>
105
+ <span class="activity-action">{{ activity.action }}</span>
106
+ </div>
107
+ <div class="activity-time">{{ activity.time }}</div>
108
+ </div>
109
+ <div class="activity-dot" :class="activity.type"></div>
77
110
  </div>
78
111
  </div>
79
- </Card>
112
+ </div>
80
113
  </div>
81
114
 
82
115
  <!-- 系统信息 -->
83
- <Card class="dashboard__system">
84
- <template #header>
85
- <span class="card-title">系统信息</span>
86
- </template>
87
- <div class="system-info">
88
- <div class="system-info__item">
89
- <span class="system-info__label">系统版本</span>
90
- <span class="system-info__value">v1.0.0</span>
91
- </div>
92
- <div class="system-info__item">
93
- <span class="system-info__label">Vue 版本</span>
94
- <span class="system-info__value">3.4.21</span>
95
- </div>
96
- <div class="system-info__item">
97
- <span class="system-info__label">构建工具</span>
98
- <span class="system-info__value">Vite 5</span>
99
- </div>
100
- <div class="system-info__item">
101
- <span class="system-info__label">UI 组件库</span>
102
- <span class="system-info__value">xto</span>
103
- </div>
104
- <div class="system-info__item">
105
- <span class="system-info__label">服务器状态</span>
106
- <Progress :percentage="75" status="success" />
116
+ <div class="system-section">
117
+ <div class="section-header">
118
+ <h3>系统信息</h3>
119
+ </div>
120
+ <div class="system-grid">
121
+ <div class="system-info-list">
122
+ <div v-for="info in systemInfo" :key="info.label" class="info-item">
123
+ <span class="info-label">{{ info.label }}</span>
124
+ <span class="info-value">{{ info.value }}</span>
125
+ </div>
107
126
  </div>
108
- <div class="system-info__item">
109
- <span class="system-info__label">内存使用</span>
110
- <Progress :percentage="45" />
127
+ <div class="system-status">
128
+ <div class="status-item">
129
+ <div class="status-header">
130
+ <span class="status-label">服务器状态</span>
131
+ <span class="status-value">75%</span>
132
+ </div>
133
+ <Progress :percentage="75" status="success" />
134
+ </div>
135
+ <div class="status-item">
136
+ <div class="status-header">
137
+ <span class="status-label">内存使用</span>
138
+ <span class="status-value">45%</span>
139
+ </div>
140
+ <Progress :percentage="45" />
141
+ </div>
142
+ <div class="status-item">
143
+ <div class="status-header">
144
+ <span class="status-label">CPU 使用率</span>
145
+ <span class="status-value">32%</span>
146
+ </div>
147
+ <Progress :percentage="32" />
148
+ </div>
111
149
  </div>
112
150
  </div>
113
- </Card>
151
+ </div>
114
152
  </div>
115
153
  </template>
116
154
 
117
155
  <style lang="scss" scoped>
118
156
  .dashboard {
119
- padding: 20px;
120
-
121
- &__stats {
122
- display: grid;
123
- grid-template-columns: repeat(4, 1fr);
124
- gap: 20px;
125
- margin-bottom: 20px;
126
-
127
- @media (max-width: 1200px) {
128
- grid-template-columns: repeat(2, 1fr);
129
- }
157
+ padding: 24px;
158
+ background: var(--bg-color-page);
159
+ min-height: 100%;
160
+ }
130
161
 
131
- @media (max-width: 768px) {
132
- grid-template-columns: 1fr;
133
- }
134
- }
162
+ // 统计卡片
163
+ .stats-section {
164
+ display: grid;
165
+ grid-template-columns: repeat(4, 1fr);
166
+ gap: 20px;
167
+ margin-bottom: 24px;
135
168
 
136
- &__main {
137
- display: grid;
169
+ @media (max-width: 1200px) {
138
170
  grid-template-columns: repeat(2, 1fr);
139
- gap: 20px;
140
- margin-bottom: 20px;
141
-
142
- @media (max-width: 992px) {
143
- grid-template-columns: 1fr;
144
- }
145
171
  }
146
172
 
147
- &__quick,
148
- &__activity {
149
- min-height: 300px;
173
+ @media (max-width: 768px) {
174
+ grid-template-columns: 1fr;
150
175
  }
151
176
  }
152
177
 
153
- .card-title {
154
- font-size: 16px;
155
- font-weight: 500;
156
- }
157
-
158
178
  .stat-card {
159
- &__content {
160
- display: flex;
161
- align-items: center;
162
- gap: 15px;
179
+ position: relative;
180
+ display: flex;
181
+ align-items: flex-start;
182
+ gap: 16px;
183
+ padding: 20px;
184
+ background: var(--bg-color);
185
+ border-radius: var(--border-radius-large);
186
+ box-shadow: var(--box-shadow-card);
187
+ transition: all 0.3s ease;
188
+ overflow: hidden;
189
+
190
+ &:hover {
191
+ box-shadow: var(--box-shadow-card-hover);
192
+ transform: translateY(-4px);
193
+
194
+ .stat-decoration {
195
+ opacity: 1;
196
+ }
163
197
  }
164
198
 
165
- &__icon {
166
- width: 50px;
167
- height: 50px;
199
+ .stat-icon {
200
+ width: 52px;
201
+ height: 52px;
168
202
  display: flex;
169
203
  align-items: center;
170
204
  justify-content: center;
171
- font-size: 24px;
172
- border-radius: var(--border-radius-base);
205
+ border-radius: var(--border-radius-large);
206
+ flex-shrink: 0;
207
+
208
+ span {
209
+ font-size: 26px;
210
+ }
173
211
  }
174
212
 
175
- &__info {
213
+ .stat-content {
176
214
  flex: 1;
215
+ min-width: 0;
177
216
  }
178
217
 
179
- &__title {
218
+ .stat-title {
180
219
  font-size: 14px;
181
220
  color: var(--color-text-secondary);
182
- margin-bottom: 5px;
221
+ margin-bottom: 8px;
183
222
  }
184
223
 
185
- &__value {
186
- font-size: 24px;
224
+ .stat-value {
225
+ font-size: 28px;
187
226
  font-weight: 600;
227
+ line-height: 1.2;
228
+ margin-bottom: 8px;
229
+ }
230
+
231
+ .stat-trend {
232
+ display: inline-flex;
233
+ align-items: center;
234
+ gap: 4px;
235
+ font-size: 12px;
236
+ padding: 2px 8px;
237
+ border-radius: 12px;
238
+
239
+ svg {
240
+ width: 14px;
241
+ height: 14px;
242
+ }
243
+
244
+ &.up {
245
+ color: var(--color-success);
246
+ background: var(--color-success-lighter);
247
+ }
248
+
249
+ &.down {
250
+ color: var(--color-danger);
251
+ background: var(--color-danger-lighter);
252
+ }
253
+ }
254
+
255
+ .stat-decoration {
256
+ position: absolute;
257
+ top: 0;
258
+ right: 0;
259
+ width: 100px;
260
+ height: 100px;
261
+ border-radius: 50%;
262
+ opacity: 0;
263
+ transition: opacity 0.3s ease;
188
264
  }
189
265
  }
190
266
 
191
- .quick-links {
267
+ // 主体内容
268
+ .main-section {
192
269
  display: grid;
193
- grid-template-columns: repeat(4, 1fr);
194
- gap: 15px;
270
+ grid-template-columns: repeat(2, 1fr);
271
+ gap: 24px;
272
+ margin-bottom: 24px;
195
273
 
196
- @media (max-width: 768px) {
197
- grid-template-columns: repeat(2, 1fr);
274
+ @media (max-width: 992px) {
275
+ grid-template-columns: 1fr;
198
276
  }
199
277
  }
200
278
 
201
- .quick-link {
279
+ .section-header {
202
280
  display: flex;
203
- flex-direction: column;
281
+ justify-content: space-between;
204
282
  align-items: center;
205
- justify-content: center;
283
+ margin-bottom: 20px;
284
+
285
+ h3 {
286
+ font-size: 16px;
287
+ font-weight: 600;
288
+ color: var(--color-text-primary);
289
+ }
290
+
291
+ .section-badge {
292
+ font-size: 12px;
293
+ color: var(--color-text-placeholder);
294
+ background: var(--color-fill);
295
+ padding: 4px 10px;
296
+ border-radius: 12px;
297
+ }
298
+
299
+ .section-link {
300
+ font-size: 14px;
301
+ color: var(--color-primary);
302
+ text-decoration: none;
303
+ transition: color 0.2s;
304
+
305
+ &:hover {
306
+ color: var(--color-primary-dark-1);
307
+ }
308
+ }
309
+ }
310
+
311
+ // 快捷入口
312
+ .quick-section {
313
+ background: var(--bg-color);
314
+ border-radius: var(--border-radius-large);
206
315
  padding: 20px;
316
+ box-shadow: var(--box-shadow-card);
317
+ }
318
+
319
+ .quick-grid {
320
+ display: flex;
321
+ flex-direction: column;
322
+ gap: 12px;
323
+ }
324
+
325
+ .quick-card {
326
+ display: flex;
327
+ align-items: center;
328
+ gap: 16px;
329
+ padding: 16px;
330
+ background: var(--color-fill-light);
207
331
  border-radius: var(--border-radius-base);
208
- background-color: var(--color-fill);
209
332
  text-decoration: none;
210
- transition: all var(--transition-duration-fast);
333
+ transition: all 0.2s ease;
211
334
 
212
335
  &:hover {
213
- background-color: var(--color-primary-light-9);
214
- transform: translateY(-2px);
336
+ background: var(--color-primary-light-6);
337
+ transform: translateX(4px);
338
+
339
+ .quick-arrow {
340
+ color: var(--color-primary);
341
+ transform: translateX(4px);
342
+ }
215
343
  }
216
344
 
217
- &__icon {
218
- font-size: 28px;
219
- margin-bottom: 10px;
345
+ .quick-icon {
346
+ width: 44px;
347
+ height: 44px;
348
+ display: flex;
349
+ align-items: center;
350
+ justify-content: center;
351
+ background: var(--bg-color);
352
+ border-radius: var(--border-radius-base);
353
+ font-size: 22px;
354
+ flex-shrink: 0;
220
355
  }
221
356
 
222
- &__title {
223
- font-size: 14px;
357
+ .quick-info {
358
+ flex: 1;
359
+ min-width: 0;
360
+ }
361
+
362
+ .quick-title {
363
+ font-size: 15px;
364
+ font-weight: 500;
224
365
  color: var(--color-text-primary);
366
+ margin-bottom: 4px;
367
+ }
368
+
369
+ .quick-desc {
370
+ font-size: 13px;
371
+ color: var(--color-text-secondary);
225
372
  }
373
+
374
+ .quick-arrow {
375
+ width: 24px;
376
+ height: 24px;
377
+ display: flex;
378
+ align-items: center;
379
+ justify-content: center;
380
+ color: var(--color-text-placeholder);
381
+ transition: all 0.2s ease;
382
+
383
+ svg {
384
+ width: 16px;
385
+ height: 16px;
386
+ }
387
+ }
388
+ }
389
+
390
+ // 最近活动
391
+ .activity-section {
392
+ background: var(--bg-color);
393
+ border-radius: var(--border-radius-large);
394
+ padding: 20px;
395
+ box-shadow: var(--box-shadow-card);
226
396
  }
227
397
 
228
398
  .activity-list {
229
399
  display: flex;
230
400
  flex-direction: column;
231
- gap: 15px;
232
401
  }
233
402
 
234
403
  .activity-item {
235
404
  display: flex;
236
- align-items: center;
237
- gap: 10px;
238
- padding-bottom: 15px;
405
+ align-items: flex-start;
406
+ gap: 12px;
407
+ padding: 14px 0;
239
408
  border-bottom: 1px solid var(--color-border-lighter);
409
+ position: relative;
240
410
 
241
411
  &:last-child {
242
412
  border-bottom: none;
243
- padding-bottom: 0;
244
413
  }
414
+ }
245
415
 
246
- &__action {
247
- flex: 1;
248
- font-size: 14px;
249
- color: var(--color-text-regular);
250
- }
416
+ .activity-avatar {
417
+ width: 36px;
418
+ height: 36px;
419
+ display: flex;
420
+ align-items: center;
421
+ justify-content: center;
422
+ border-radius: 50%;
423
+ font-size: 14px;
424
+ font-weight: 500;
425
+ flex-shrink: 0;
251
426
 
252
- &__time {
253
- font-size: 12px;
254
- color: var(--color-text-placeholder);
255
- }
427
+ &.success { background: var(--color-success-lighter); color: var(--color-success); }
428
+ &.warning { background: var(--color-warning-lighter); color: var(--color-warning); }
429
+ &.info { background: var(--color-info-lighter); color: var(--color-info); }
430
+ &.danger { background: var(--color-danger-lighter); color: var(--color-danger); }
431
+ &.primary { background: var(--color-primary-light-6); color: var(--color-primary); }
432
+ }
433
+
434
+ .activity-content {
435
+ flex: 1;
436
+ min-width: 0;
437
+ }
438
+
439
+ .activity-main {
440
+ margin-bottom: 4px;
441
+ }
442
+
443
+ .activity-user {
444
+ font-size: 14px;
445
+ font-weight: 500;
446
+ color: var(--color-text-primary);
447
+ margin-right: 8px;
448
+ }
449
+
450
+ .activity-action {
451
+ font-size: 14px;
452
+ color: var(--color-text-secondary);
453
+ }
454
+
455
+ .activity-time {
456
+ font-size: 12px;
457
+ color: var(--color-text-placeholder);
256
458
  }
257
459
 
258
- .system-info {
460
+ .activity-dot {
461
+ position: absolute;
462
+ left: -20px;
463
+ top: 50%;
464
+ transform: translateY(-50%);
465
+ width: 8px;
466
+ height: 8px;
467
+ border-radius: 50%;
468
+ opacity: 0.6;
469
+
470
+ &.success { background: var(--color-success); }
471
+ &.warning { background: var(--color-warning); }
472
+ &.info { background: var(--color-info); }
473
+ &.danger { background: var(--color-danger); }
474
+ &.primary { background: var(--color-primary); }
475
+ }
476
+
477
+ // 系统信息
478
+ .system-section {
479
+ background: var(--bg-color);
480
+ border-radius: var(--border-radius-large);
481
+ padding: 20px;
482
+ box-shadow: var(--box-shadow-card);
483
+ }
484
+
485
+ .system-grid {
259
486
  display: grid;
260
- grid-template-columns: repeat(2, 1fr);
261
- gap: 20px;
487
+ grid-template-columns: 1fr 1.5fr;
488
+ gap: 24px;
262
489
 
263
490
  @media (max-width: 768px) {
264
491
  grid-template-columns: 1fr;
265
492
  }
493
+ }
494
+
495
+ .system-info-list {
496
+ display: flex;
497
+ flex-direction: column;
498
+ gap: 12px;
499
+ }
500
+
501
+ .info-item {
502
+ display: flex;
503
+ justify-content: space-between;
504
+ align-items: center;
505
+ padding: 12px 16px;
506
+ background: var(--color-fill-light);
507
+ border-radius: var(--border-radius-base);
508
+
509
+ .info-label {
510
+ font-size: 14px;
511
+ color: var(--color-text-secondary);
512
+ }
513
+
514
+ .info-value {
515
+ font-size: 14px;
516
+ font-weight: 500;
517
+ color: var(--color-text-primary);
518
+ }
519
+ }
520
+
521
+ .system-status {
522
+ display: flex;
523
+ flex-direction: column;
524
+ gap: 16px;
525
+ }
266
526
 
267
- &__item {
527
+ .status-item {
528
+ .status-header {
268
529
  display: flex;
530
+ justify-content: space-between;
269
531
  align-items: center;
270
- gap: 10px;
532
+ margin-bottom: 8px;
271
533
  }
272
534
 
273
- &__label {
535
+ .status-label {
274
536
  font-size: 14px;
275
537
  color: var(--color-text-secondary);
276
- min-width: 80px;
277
538
  }
278
539
 
279
- &__value {
540
+ .status-value {
280
541
  font-size: 14px;
542
+ font-weight: 500;
281
543
  color: var(--color-text-primary);
282
544
  }
283
545
  }