82a19101a8
- 创建 rui-frontend 前端仓库 - 迁移 admin-ui 管理后台 - 创建 cashier-mobile 和 customer-mobile 占位项目 - 配置 pnpm workspace
54 lines
2.4 KiB
Vue
54 lines
2.4 KiB
Vue
<script setup lang="ts">
|
|
import { useI18n } from 'vue-i18n'
|
|
const { t } = useI18n()
|
|
|
|
const stats = [
|
|
{ key: 'users', label: '', value: 1280, growth: '+12%', icon: 'UserFilled', color: '#1677ff' },
|
|
{ key: 'today', label: '', value: 23, growth: '+8%', icon: 'TrendCharts', color: '#52c41a' },
|
|
{ key: 'active', label: '', value: 856, growth: '+23%', icon: 'DataLine', color: '#722ed1' },
|
|
{ key: 'orders', label: '', value: 5680, growth: '+15%', icon: 'ShoppingCart', color: '#fa8c16' },
|
|
].map(s => ({ ...s, label: t(`dashboard.${s.key}`) }))
|
|
|
|
const recent = [
|
|
{ name: '张三', actionKey: 'register', time: '10m' },
|
|
{ name: '李四', actionKey: 'login', time: '30m' },
|
|
{ name: '王五', actionKey: 'order', time: '1h' },
|
|
{ name: '赵六', actionKey: 'comment', time: '2h' },
|
|
].map(r => ({ ...r, action: t(`dashboard.action.${r.actionKey}`) }))
|
|
</script>
|
|
|
|
<template>
|
|
<div>
|
|
<h2 class="text-xl font-bold mb-5">{{ t('dashboard.title') }}</h2>
|
|
<div class="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-4 gap-4 mb-5">
|
|
<div v-for="s in stats" :key="s.key" class="stat-card">
|
|
<div class="flex items-start justify-between">
|
|
<div>
|
|
<p class="text-sm text-gray-500 mb-1">{{ s.label }}</p>
|
|
<p class="text-3xl font-bold mb-1">{{ s.value.toLocaleString() }}</p>
|
|
<span class="text-xs" :style="{ color: s.color }">{{ s.growth }} {{ t('dashboard.vsLastWeek') }}</span>
|
|
</div>
|
|
<div class="stat-icon" :style="{ background: s.color + '15', color: s.color }">
|
|
<el-icon :size="24"><component :is="s.icon" /></el-icon>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<h3 class="text-lg font-bold mb-4">{{ t('dashboard.recent') }}</h3>
|
|
<div class="stat-card">
|
|
<el-timeline>
|
|
<el-timeline-item v-for="(r, i) in recent" :key="i" :timestamp="r.time" placement="top">
|
|
<span class="font-medium">{{ r.name }}</span>
|
|
<span class="text-gray-500 ml-2">{{ r.action }}</span>
|
|
</el-timeline-item>
|
|
</el-timeline>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.stat-card { background: var(--el-bg-color-overlay); border-radius: 8px; padding: 20px; box-shadow: 0 1px 3px rgba(0,0,0,0.04); transition: box-shadow 0.2s; }
|
|
.stat-card:hover { box-shadow: 0 4px 12px rgba(0,0,0,0.08); }
|
|
.stat-icon { width: 48px; height: 48px; border-radius: 12px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
|
|
</style>
|