admin_new/src/modules/dashboard/DashboardView.vue
Sisyphus 6d02a4b5d2 feat(F006): W2 + W4 + W31 admin_new 全部改动首次 commit
- W2: 4 主题 (科技蓝/中国红/Notion 紫/暗夜黑) + LoginView + DashboardView + 询价管理 (InquiryList + InquiryDetail)
- W4.1: 集成管理 - 应用列表 (OpenAppListView)
- W4.2: 集成管理 - 审计日志 (AuditLogListView) + 菜单 label 补全
- W4.2: 菜单从后端拉 + 看板接真 API
- W31: safeParseArray 防御 localStorage permissions 损坏 (运营后台白屏修复)

关联: Mac F002-W31-交接文档.md / F006-W4-交接文档.md / GIT-AUDIT-REPORT-2026-06-24.md
本地备份: ~/projects/_meta/f002-fix/admin-new-w4-*/
2026-06-24 11:05:18 +08:00

145 lines
4.7 KiB
Vue

<script setup lang="ts">
/**
* F006 数据看板
* W1 D3: mock 数据
* W1 D4: 接真实 API(/sys/openapi/inquiry/publish 等)
*/
import { ref, onMounted } from 'vue'
const stats = ref([
{ label: '今日询价', value: '128', delta: '+12.5%', trend: 'up', icon: 'ChatDotRound' },
{ label: '订单总额', value: '¥328K', delta: '+8.3%', trend: 'up', icon: 'Money' },
{ label: '活跃供应商', value: '42', delta: '+3', trend: 'up', icon: 'OfficeBuilding' },
{ label: '待处理事项', value: '7', delta: '-2', trend: 'down', icon: 'Bell' }
])
const recentInquiries = ref([
{ id: 'RFQ-20668', customer: '中铁建设集团', product: 'HRB400 螺纹钢', qty: '200 吨', status: 'pending', time: '10:24' },
{ id: 'RFQ-20667', customer: '中建八局', product: '商混 C30', qty: '500 m³', status: 'quoting', time: '09:15' },
{ id: 'RFQ-20666', customer: '上海建工', product: '盘螺 8mm', qty: '80 吨', status: 'done', time: '08:42' },
{ id: 'RFQ-20665', customer: '北京城建', product: '水泥 P.O 42.5', qty: '100 吨', status: 'cancelled', time: '昨天 16:30' }
])
const STATUS_MAP: Record<string, { label: string; type: string }> = {
pending: { label: '待报价', type: 'primary' },
quoting: { label: '报价中', type: 'warning' },
done: { label: '已成交', type: 'success' },
cancelled: { label: '已取消', type: 'danger' }
}
onMounted(() => {
// W1 D4: 这里替换为真实 API 调用
// const res = await api.get('/sys/openapi/inquiry/publish', { params: { customerCode: 'admin' } })
// stats.value = transformStats(res)
})
</script>
<template>
<div class="rr-dashboard">
<div class="rr-page-header">
<h2 class="rr-page-title">数据看板</h2>
<p class="rr-page-sub">2026 年 6 月 17 日 · 实时数据</p>
</div>
<!-- 4 个统计卡 -->
<div class="rr-stats-grid">
<div v-for="(s, i) in stats" :key="i" class="rr-stat">
<div class="rr-stat-head">
<el-icon class="rr-stat-icon"><component :is="s.icon" /></el-icon>
<span class="rr-stat-label">{{ s.label }}</span>
</div>
<div class="rr-stat-value">{{ s.value }}</div>
<div class="rr-stat-delta" :class="s.trend">
<el-icon><component :is="s.trend === 'up' ? 'CaretTop' : 'CaretBottom'" /></el-icon>
{{ s.delta }} 较昨日
</div>
</div>
</div>
<!-- 近期询价 -->
<div class="rr-card">
<div class="rr-card-header">
<h3 class="rr-card-title">近期询价</h3>
<el-button text type="primary">查看全部 →</el-button>
</div>
<el-table :data="recentInquiries" stripe>
<el-table-column prop="id" label="单号" width="140" />
<el-table-column prop="customer" label="客户" />
<el-table-column prop="product" label="商品" />
<el-table-column prop="qty" label="数量" width="100" />
<el-table-column label="状态" width="110">
<template #default="{ row }">
<el-tag :type="STATUS_MAP[row.status].type as any" size="small" effect="light">
{{ STATUS_MAP[row.status].label }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="time" label="时间" width="100" />
</el-table>
</div>
</div>
</template>
<style scoped lang="scss">
.rr-dashboard { max-width: 1400px; }
.rr-page-header { margin-bottom: 20px; }
.rr-page-title {
font-size: 22px; font-weight: 600; letter-spacing: -0.02em;
color: var(--ink); margin-bottom: 4px;
}
.rr-page-sub { color: var(--ink-3); font-size: 13px; }
.rr-stats-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 12px;
margin-bottom: 20px;
}
.rr-stat {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
padding: 18px 20px;
transition: all 0.15s;
&:hover { border-color: var(--border-strong); box-shadow: var(--shadow); }
}
.rr-stat-head {
display: flex; align-items: center; gap: 8px;
margin-bottom: 8px;
}
.rr-stat-icon {
font-size: 16px;
color: var(--ink-3);
}
.rr-stat-label {
font-size: 12px; color: var(--ink-3); font-weight: 500;
}
.rr-stat-value {
font-size: 28px; font-weight: 700;
letter-spacing: -0.025em;
color: var(--ink);
margin-bottom: 6px;
}
.rr-stat-delta {
font-size: 11.5px; font-weight: 600;
display: inline-flex; align-items: center; gap: 3px;
&.up { color: var(--green); }
&.down { color: var(--red); }
}
.rr-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
overflow: hidden;
}
.rr-card-header {
display: flex; align-items: center; justify-content: space-between;
padding: 14px 20px;
border-bottom: 1px solid var(--border);
}
.rr-card-title {
font-size: 15px; font-weight: 600; color: var(--ink);
}
</style>