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-EChartsVue 集成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/integrationsuseSortable,将布局配置保存到用户偏好 Store 并持久化。

Q: 后端联合统计接口放在哪里?

建议创建一个 dashboard 联合模块,专门存放跨模块的数据聚合服务。该模块依赖各业务模块的 statistic 对象,通过内部 RPC 或直接数据库查询汇总。

创建于 2025/1/1 更新于 2026/5/27