313 lines
8.8 KiB
Markdown
313 lines
8.8 KiB
Markdown
# 仪表盘页面设计文档
|
|
|
|
## 1. 页面概述
|
|
|
|
仪表盘页面是用户登录后看到的第一个界面,旨在提供系统关键信息的概览。它主要面向管理员、决策者和主管,展示核心业务指标、任务状态分布、近期活动等,以便用户快速掌握系统整体状况并做出决策。
|
|
|
|
## 2. 页面布局
|
|
|
|

|
|
|
|
### 2.1 布局结构
|
|
|
|
页面采用响应式网格布局,确保在不同屏幕尺寸下都有良好的可读性。
|
|
- **顶部**: 页面标题和一个时间范围选择器,用于筛选仪表盘数据。
|
|
- **中部**: 关键指标卡片区域,展示核心数据(如总任务数、待处理反馈等)。
|
|
- **下部**:
|
|
- **左侧**: 图表区域,通过饼图和柱状图展示任务状态和类型的分布。
|
|
- **右侧**: 近期活动或任务列表,展示最新的任务分配或状态变更。
|
|
|
|
### 2.2 响应式设计
|
|
|
|
- **桌面端**: 采用多列网格布局,充分利用屏幕空间。
|
|
- **平板端**: 网格列数减少,卡片和图表垂直堆叠。
|
|
- **移动端**: 单列布局,所有模块垂直排列,保证可读性。
|
|
|
|
## 3. 组件结构
|
|
|
|
```vue
|
|
<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. 数据结构
|
|
|
|
```typescript
|
|
// 仪表盘统计数据
|
|
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. 状态管理
|
|
|
|
```typescript
|
|
// 组件内状态
|
|
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 数据加载
|
|
|
|
```typescript
|
|
// 在组件挂载时加载数据
|
|
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 辅助函数
|
|
|
|
```typescript
|
|
// 根据任务状态返回不同的标签类型
|
|
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
|
|
|
|
```typescript
|
|
// 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. 样式设计
|
|
|
|
```scss
|
|
.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. 测试用例
|
|
|
|
1. **单元测试**
|
|
- 测试 `getStatusTagType` 辅助函数的正确性。
|
|
- 测试 Pinia store中getters的数据转换逻辑。
|
|
- 测试 `StatisticCard`、`PieChart`、`BarChart` 组件的渲染是否正确。
|
|
|
|
2. **集成测试**
|
|
- 测试页面在加载、成功、失败状态下的显示。
|
|
- 测试日期选择器筛选功能是否能正确触发数据重新加载。
|
|
- 模拟 API 调用,验证数据是否正确渲染到页面上。
|
|
|
|
## 10. 性能优化
|
|
|
|
1. **懒加载图表**: 使用 `v-if` 或动态导入,确保图表库只在需要时加载和渲染。
|
|
2. **骨架屏**: 在数据加载时使用骨架屏,提升用户体验。
|
|
3. **数据缓存**: 对不经常变化的数据(如任务类型),可以在 Pinia store 中设置缓存策略,避免重复请求。
|
|
4. **节流/防抖**: 对日期选择器的 `change` 事件使用节流或防抖,防止用户快速操作导致频繁的 API 请求。 |