1
This commit is contained in:
313
Design/frontend_Design/DashboardPage_Design.md
Normal file
313
Design/frontend_Design/DashboardPage_Design.md
Normal file
@@ -0,0 +1,313 @@
|
||||
# 仪表盘页面设计文档
|
||||
|
||||
## 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 请求。
|
||||
Reference in New Issue
Block a user