dashboard模块
Vue项目dashboard仪表盘页面设计与架构
#tech / dev / frame
#type / howto
#status / seed
dashboard模块
目标
构建项目 Dashboard 仪表盘模块,作为用户的核心入口页面,支持:
- 以”桌面组件”形式聚合各模块核心信息(任务、目标、提醒等)
- 用户自定义组件布局(拖拽、增删)
- 统计数据快速展示与缓存优化
- 骨架屏加载与权限控制
前置条件
- Vue3 + Pinia 项目已搭建
- 各业务模块(task、goal、reminder)已有基础 API
- 了解组件化设计与状态管理
- 了解骨架屏与性能优化方案
技术方案
整体架构
Dashboard Page
|- TaskWidget (任务实例 + 快速创建)
|- GoalWidget (目标进度 + 统计)
|- ReminderWidget (即将到来的提醒)
|- StatsWidget (全局统计数字)
|
|- 统计信息 Store (聚合缓存)
|- 联合模块 API (跨模块数据)
图表库选择
| 库 | 适用场景 | 特点 |
|---|---|---|
| ECharts | 复杂图表 | 功能全面,体积较大 |
| Chart.js | 轻量需求 | 简单易用,插件生态好 |
| D3.js | 高度定制 | 灵活但学习曲线陡 |
| Vue-ECharts | Vue 集成 | ECharts 的 Vue 封装 |
推荐:简单统计用 Chart.js,复杂可视化用 ECharts。
步骤
1. 需求分析与组件规划
每个业务模块提供一个 Dashboard Widget:
| 组件 | 功能 | 数据源 |
|---|---|---|
| TaskWidget | 查看任务实例 + 快速创建 | task store |
| GoalWidget | 目标进度 + 统计 | goal store |
| ReminderWidget | 即将到来的提醒 | reminder store |
| StatsWidget | 全局统计数字 | 统计聚合 API |
2. 布局设计
使用 CSS Grid 实现可配置的仪表盘布局:
<template>
<div class="dashboard-grid">
<div v-for="widget in activeWidgets" :key="widget.id"
:class="['widget', `widget-${widget.size}`]">
<component :is="widget.component" />
</div>
</div>
</template>
<style scoped>
.dashboard-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 16px;
padding: 16px;
}
.widget-small { grid-column: span 1; }
.widget-large { grid-column: span 2; }
</style>
3. 统计数据接口与缓存
创建专用的聚合统计接口,避免多次请求:
// 后端:联合统计接口
// GET /api/dashboard/stats
// 返回: { taskCount, goalCount, reminderCount, ... }
// 前端:统计 Store
export const useStatsStore = defineStore('stats', () => {
const stats = ref<DashboardStats | null>(null)
const lastFetched = ref(0)
const CACHE_TTL = 5 * 60 * 1000 // 5 分钟缓存
async function fetchStats(force = false) {
if (!force && stats.value && Date.now() - lastFetched.value < CACHE_TTL) {
return // 使用缓存
}
stats.value = await api.getDashboardStats()
lastFetched.value = Date.now()
}
// 本地乐观更新(事件总线 +1)
function incrementStat(key: keyof DashboardStats) {
if (stats.value) stats.value[key]++
}
return { stats, fetchStats, incrementStat }
})
缓存失效策略:
- 本地变化:通过事件总线直接 +1/-1(乐观更新)
- 远程变化:定期轮询(5分钟)或 WebSocket 推送
- 页面聚焦时强制刷新一次
4. 各模块 Widget 组件
每个 Widget 独立放在各自模块目录下:
src/modules/
|- task/widgets/TaskWidget.vue
|- goal/widgets/GoalWidget.vue
|- reminder/widgets/ReminderWidget.vue
Widget 组件内部提供快捷操作(如 TaskWidget 包含创建任务按钮)。
5. 骨架屏
每个 Widget 实现对应的骨架组件:
<template>
<div v-if="loading" class="skeleton">
<div class="skeleton-header"></div>
<div class="skeleton-line" v-for="i in 3" :key="i"></div>
</div>
<div v-else>
<slot />
</div>
</template>
<style scoped>
.skeleton-header {
height: 24px;
width: 60%;
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
border-radius: 4px;
margin-bottom: 12px;
}
.skeleton-line {
height: 16px;
width: 100%;
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
border-radius: 4px;
margin-bottom: 8px;
}
@keyframes shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
</style>
6. 权限控制
根据用户角色决定显示哪些 Widget:
const activeWidgets = computed(() =>
allWidgets.filter(w => w.requiredRole ? hasRole(w.requiredRole) : true)
)
验证
- Dashboard 页面加载时显示骨架屏,数据到达后平滑切换
- 各 Widget 正确展示对应模块数据
- 统计数据缓存生效(5 分钟内不重复请求)
- 本地操作后统计数字实时更新(乐观更新)
- 不同角色用户看到不同的 Widget 组合
- Widget 布局在移动端自适应(响应式)
常见问题
Q: 统计数据不一致(本地与远程不同步)?
乐观更新 + 定期校准。本地操作 +1 的同时将变更入队,下一次 fetchStats 时以服务端数据为准覆盖本地值。
Q: 首屏加载慢?
- 统计接口做聚合,一次请求返回所有数据
- Widget 组件使用懒加载(
defineAsyncComponent) - 骨架屏立即展示,数据异步填充
- 统计数据做 CDN 边缘缓存或 Service Worker 缓存
Q: Widget 拖拽排序如何实现?
使用 vue-draggable-plus 或 @vueuse/integrations 的 useSortable,将布局配置保存到用户偏好 Store 并持久化。
Q: 后端联合统计接口放在哪里?
建议创建一个 dashboard 联合模块,专门存放跨模块的数据聚合服务。该模块依赖各业务模块的 statistic 对象,通过内部 RPC 或直接数据库查询汇总。