8.8 KiB
8.8 KiB
仪表盘页面设计文档
1. 页面概述
仪表盘页面是用户登录后看到的第一个界面,旨在提供系统关键信息的概览。它主要面向管理员、决策者和主管,展示核心业务指标、任务状态分布、近期活动等,以便用户快速掌握系统整体状况并做出决策。
2. 页面布局
2.1 布局结构
页面采用响应式网格布局,确保在不同屏幕尺寸下都有良好的可读性。
- 顶部: 页面标题和一个时间范围选择器,用于筛选仪表盘数据。
- 中部: 关键指标卡片区域,展示核心数据(如总任务数、待处理反馈等)。
- 下部:
- 左侧: 图表区域,通过饼图和柱状图展示任务状态和类型的分布。
- 右侧: 近期活动或任务列表,展示最新的任务分配或状态变更。
2.2 响应式设计
- 桌面端: 采用多列网格布局,充分利用屏幕空间。
- 平板端: 网格列数减少,卡片和图表垂直堆叠。
- 移动端: 单列布局,所有模块垂直排列,保证可读性。
3. 组件结构
<template>
<div class="dashboard-page">
<!-- 页面头部 -->
<el-page-header title="仪表盘" content="系统概览" class="page-header" />
<!-- 时间范围选择器 -->
<div class="dashboard-filters">
<el-date-picker
v-model="dateRange"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
@change="handleDateChange"
/>
</div>
<!-- 加载与错误状态 -->
<div v-if="loading" class="loading-state">
<el-skeleton :rows="5" animated />
</div>
<div v-else-if="error" class="error-state">
<el-alert type="error" :title="error" show-icon :closable="false" />
</div>
<!-- 数据内容 -->
<div v-else class="dashboard-content">
<!-- 关键指标卡片 -->
<el-row :gutter="20" class="summary-cards">
<el-col :span="6" :xs="24" :sm="12" :md="6">
<StatisticCard icon="list" title="总任务数" :value="stats.totalTasks" color="#409eff" />
</el-col>
<el-col :span="6" :xs="24" :sm="12" :md="6">
<StatisticCard icon="clock" title="待处理任务" :value="stats.pendingTasks" color="#e6a23c" />
</el-col>
<el-col :span="6" :xs="24" :sm="12" :md="6">
<StatisticCard icon="chat-dot-round" title="待审核反馈" :value="stats.pendingFeedback" color="#f56c6c" />
</el-col>
<el-col :span="6" :xs="24" :sm="12" :md="6">
<StatisticCard icon="user" title="活跃用户" :value="stats.activeUsers" color="#67c23a" />
</el-col>
</el-row>
<!-- 图表区域 -->
<el-row :gutter="20" class="charts-section">
<el-col :span="12" :xs="24" :sm="24" :md="12">
<el-card shadow="never">
<template #header>任务状态分布</template>
<PieChart :chart-data="taskStatusChartData" />
</el-card>
</el-col>
<el-col :span="12" :xs="24" :sm="24" :md="12">
<el-card shadow="never">
<template #header>任务类型分布</template>
<BarChart :chart-data="taskTypeChartData" />
</el-card>
</el-col>
</el-row>
<!-- 近期任务列表 -->
<el-card shadow="never" class="recent-tasks-section">
<template #header>近期任务动态</template>
<el-table :data="recentTasks" stripe>
<el-table-column prop="title" label="任务标题" />
<el-table-column prop="assignee" label="执行人" />
<el-table-column prop="status" label="状态">
<template #default="{ row }">
<el-tag :type="getStatusTagType(row.status)">{{ row.status }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="updatedAt" label="更新时间" />
</el-table>
</el-card>
</div>
</div>
</template>
4. 数据结构
// 仪表盘统计数据
interface DashboardStats {
totalTasks: number;
pendingTasks: number;
pendingFeedback: number;
activeUsers: number;
}
// 图表数据项
interface ChartDataItem {
name: string;
value: number;
}
// 近期任务项
interface RecentTask {
id: number;
title: string;
assignee: string;
status: 'PENDING' | 'IN_PROGRESS' | 'COMPLETED' | 'REVIEWED';
updatedAt: string;
}
// 仪表盘完整数据
interface DashboardData {
stats: DashboardStats;
taskStatusDistribution: ChartDataItem[];
taskTypeDistribution: ChartDataItem[];
recentTasks: RecentTask[];
}
5. 状态管理
// 组件内状态
const dateRange = ref<[Date, Date]>();
const loading = ref<boolean>(true);
const error = ref<string | null>(null);
// 全局状态 (Pinia Store)
const dashboardStore = useDashboardStore();
const { stats, taskStatusChartData, taskTypeChartData, recentTasks } = storeToRefs(dashboardStore);
6. 交互逻辑
6.1 数据加载
// 在组件挂载时加载数据
onMounted(() => {
fetchDashboardData();
});
const fetchDashboardData = async () => {
try {
loading.value = true;
error.value = null;
// 从 store 中调用 action 加载数据
await dashboardStore.fetchDashboardData({
startDate: dateRange.value?.[0],
endDate: dateRange.value?.[1],
});
} catch (err) {
error.value = '仪表盘数据加载失败,请稍后重试。';
} finally {
loading.value = false;
}
};
// 当日期范围变化时重新加载数据
const handleDateChange = () => {
fetchDashboardData();
};
6.2 辅助函数
// 根据任务状态返回不同的标签类型
const getStatusTagType = (status: RecentTask['status']) => {
switch (status) {
case 'PENDING': return 'warning';
case 'IN_PROGRESS': return 'primary';
case 'COMPLETED': return 'success';
case 'REVIEWED': return 'info';
default: return 'info';
}
};
7. API 调用
7.1 仪表盘 API
// api/dashboard.ts
export const dashboardApi = {
getDashboardData: (params: { startDate?: Date; endDate?: Date }) =>
apiClient.get<DashboardData>('/dashboard', { params })
};
// stores/dashboard.ts
export const useDashboardStore = defineStore('dashboard', {
state: (): DashboardData => ({
stats: { totalTasks: 0, pendingTasks: 0, pendingFeedback: 0, activeUsers: 0 },
taskStatusDistribution: [],
taskTypeDistribution: [],
recentTasks: [],
}),
getters: {
// 将后端数据转换为 ECharts 需要的格式
taskStatusChartData: (state) => ({
legend: state.taskStatusDistribution.map(item => item.name),
series: [{ data: state.taskStatusDistribution }]
}),
taskTypeChartData: (state) => ({
xAxis: state.taskTypeDistribution.map(item => item.name),
series: [{ data: state.taskTypeDistribution.map(item => item.value) }]
})
},
actions: {
async fetchDashboardData(params) {
try {
const { data } = await dashboardApi.getDashboardData(params);
// 更新整个 state
this.$patch(data);
return data;
} catch (error) {
console.error('获取仪表盘数据失败:', error);
throw error;
}
}
}
});
8. 样式设计
.dashboard-page {
padding: 24px;
.page-header {
margin-bottom: 24px;
}
.dashboard-filters {
margin-bottom: 24px;
}
.summary-cards {
margin-bottom: 20px;
}
.charts-section {
margin-bottom: 20px;
}
.el-card {
border: none;
border-radius: 8px;
}
.loading-state,
.error-state {
padding: 40px;
}
}
// 响应式样式
@media screen and (max-width: 768px) {
.dashboard-page {
padding: 16px;
.summary-cards .el-col,
.charts-section .el-col {
margin-bottom: 20px;
}
}
}
9. 测试用例
-
单元测试
- 测试
getStatusTagType辅助函数的正确性。 - 测试 Pinia store中getters的数据转换逻辑。
- 测试
StatisticCard、PieChart、BarChart组件的渲染是否正确。
- 测试
-
集成测试
- 测试页面在加载、成功、失败状态下的显示。
- 测试日期选择器筛选功能是否能正确触发数据重新加载。
- 模拟 API 调用,验证数据是否正确渲染到页面上。
10. 性能优化
- 懒加载图表: 使用
v-if或动态导入,确保图表库只在需要时加载和渲染。 - 骨架屏: 在数据加载时使用骨架屏,提升用户体验。
- 数据缓存: 对不经常变化的数据(如任务类型),可以在 Pinia store 中设置缓存策略,避免重复请求。
- 节流/防抖: 对日期选择器的
change事件使用节流或防抖,防止用户快速操作导致频繁的 API 请求。
