315 lines
8.9 KiB
Markdown
315 lines
8.9 KiB
Markdown
# 任务管理页面设计文档
|
|
|
|
## 1. 页面概述
|
|
|
|
任务管理页面是系统的核心功能模块之一,允许主管和管理员对任务进行全面的管理。用户可以在此页面上查看所有任务,并使用筛选、排序和搜索功能快速定位任务。此外,页面还支持任务的分配、审核以及查看任务详情等操作。
|
|
|
|
## 2. 页面布局
|
|
|
|

|
|
|
|
### 2.1 布局结构
|
|
|
|
页面采用经典的后台管理布局:
|
|
- **顶部**: 包含页面标题和操作按钮(如"新建任务")。
|
|
- **中部**: 筛选和搜索区域,提供多种条件来过滤任务列表。
|
|
- **下部**: 任务数据表格,以列表形式展示任务,并包含操作列。
|
|
- **底部**: 分页组件,用于浏览大量任务数据。
|
|
|
|
### 2.2 响应式设计
|
|
|
|
- **桌面端**: 多列表格,所有筛选条件水平排列。
|
|
- **平板端**: 表格列数减少,部分次要信息可能隐藏,筛选条件折叠或垂直排列。
|
|
- **移动端**: 表格转为卡片列表视图,每个卡片显示一个任务的核心信息,筛选功能通过抽屉或模态框提供。
|
|
|
|
## 3. 组件结构
|
|
|
|
```vue
|
|
<template>
|
|
<div class="task-management-page">
|
|
<!-- 页面头部 -->
|
|
<el-page-header title="任务管理" content="任务列表与分配" />
|
|
|
|
<el-card class="page-container">
|
|
<!-- 筛选与搜索区域 -->
|
|
<div class="filter-container">
|
|
<el-form :model="filters" inline>
|
|
<el-form-item label="任务状态">
|
|
<el-select v-model="filters.status" placeholder="请选择状态" clearable>
|
|
<el-option label="待处理" value="PENDING" />
|
|
<el-option label="进行中" value="IN_PROGRESS" />
|
|
<el-option label="已完成" value="COMPLETED" />
|
|
<el-option label="已审核" value="REVIEWED" />
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item label="执行人">
|
|
<el-input v-model="filters.assignee" placeholder="请输入执行人" clearable />
|
|
</el-form-item>
|
|
<el-form-item>
|
|
<el-button type="primary" icon="Search" @click="handleSearch">搜索</el-button>
|
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
|
</el-form-item>
|
|
</el-form>
|
|
</div>
|
|
|
|
<!-- 操作按钮区域 -->
|
|
<div class="action-buttons">
|
|
<el-button type="primary" icon="Plus" @click="handleCreateTask" v-permission="'SUPERVISOR'">
|
|
新建任务
|
|
</el-button>
|
|
</div>
|
|
|
|
<!-- 任务表格 -->
|
|
<el-table :data="tasks" v-loading="loading" stripe>
|
|
<el-table-column type="index" width="50" />
|
|
<el-table-column prop="title" label="任务标题" min-width="150" />
|
|
<el-table-column prop="grid" label="所属网格" />
|
|
<el-table-column prop="assignee" label="执行人" />
|
|
<el-table-column prop="status" label="状态">
|
|
<template #default="{ row }">
|
|
<el-tag :type="getStatusTagType(row.status)">{{ formatStatus(row.status) }}</el-tag>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="createdAt" label="创建时间" sortable />
|
|
<el-table-column label="操作" width="200" fixed="right">
|
|
<template #default="{ row }">
|
|
<el-button link type="primary" @click="handleView(row)">查看</el-button>
|
|
<el-button link type="primary" @click="handleAssign(row)" v-if="canAssign(row)">分配</el-button>
|
|
<el-button link type="primary" @click="handleReview(row)" v-if="canReview(row)">审核</el-button>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
|
|
<!-- 分页组件 -->
|
|
<el-pagination
|
|
v-if="total > 0"
|
|
class="pagination-container"
|
|
:current-page="pagination.page"
|
|
:page-size="pagination.pageSize"
|
|
:total="total"
|
|
layout="total, sizes, prev, pager, next, jumper"
|
|
@size-change="handleSizeChange"
|
|
@current-change="handleCurrentChange"
|
|
/>
|
|
</el-card>
|
|
|
|
<!-- 任务分配/审核/新建的对话框 -->
|
|
<TaskFormDialog v-model="dialogVisible" :task-id="selectedTaskId" :mode="dialogMode" @success="handleSuccess" />
|
|
</div>
|
|
</template>
|
|
```
|
|
|
|
## 4. 数据结构
|
|
|
|
```typescript
|
|
// 任务筛选条件
|
|
interface TaskFilters {
|
|
status?: 'PENDING' | 'IN_PROGRESS' | 'COMPLETED' | 'REVIEWED';
|
|
assignee?: string;
|
|
}
|
|
|
|
// 分页信息
|
|
interface Pagination {
|
|
page: number;
|
|
pageSize: number;
|
|
}
|
|
|
|
// 任务列表项
|
|
interface TaskItem {
|
|
id: number;
|
|
title: string;
|
|
grid: string;
|
|
assignee: string | null;
|
|
status: 'PENDING' | 'IN_PROGRESS' | 'COMPLETED' | 'REVIEWED';
|
|
createdAt: string;
|
|
}
|
|
|
|
// 对话框模式
|
|
type DialogMode = 'create' | 'assign' | 'review' | 'view';
|
|
```
|
|
|
|
## 5. 状态管理
|
|
|
|
```typescript
|
|
// 组件内状态
|
|
const filters = ref<TaskFilters>({});
|
|
const pagination = ref<Pagination>({ page: 1, pageSize: 10 });
|
|
const dialogVisible = ref(false);
|
|
const selectedTaskId = ref<number | null>(null);
|
|
const dialogMode = ref<DialogMode>('view');
|
|
|
|
// 全局状态 (Pinia Store)
|
|
const taskStore = useTaskStore();
|
|
const { tasks, total, loading } = storeToRefs(taskStore);
|
|
const authStore = useAuthStore();
|
|
const { user } = storeToRefs(authStore);
|
|
```
|
|
|
|
## 6. 交互逻辑
|
|
|
|
### 6.1 数据获取与筛选
|
|
|
|
```typescript
|
|
const fetchTasks = () => {
|
|
taskStore.fetchTasks({ ...filters.value, ...pagination.value });
|
|
};
|
|
|
|
onMounted(fetchTasks);
|
|
|
|
const handleSearch = () => {
|
|
pagination.value.page = 1;
|
|
fetchTasks();
|
|
};
|
|
|
|
const handleReset = () => {
|
|
filters.value = {};
|
|
handleSearch();
|
|
};
|
|
|
|
const handleSizeChange = (size: number) => {
|
|
pagination.value.pageSize = size;
|
|
fetchTasks();
|
|
};
|
|
|
|
const handleCurrentChange = (page: number) => {
|
|
pagination.value.page = page;
|
|
fetchTasks();
|
|
};
|
|
```
|
|
|
|
### 6.2 表格操作
|
|
|
|
```typescript
|
|
const handleView = (task: TaskItem) => {
|
|
selectedTaskId.value = task.id;
|
|
dialogMode.value = 'view';
|
|
dialogVisible.value = true;
|
|
};
|
|
|
|
const handleAssign = (task: TaskItem) => {
|
|
selectedTaskId.value = task.id;
|
|
dialogMode.value = 'assign';
|
|
dialogVisible.value = true;
|
|
};
|
|
|
|
const handleReview = (task: TaskItem) => {
|
|
selectedTaskId.value = task.id;
|
|
dialogMode.value = 'review';
|
|
dialogVisible.value = true;
|
|
};
|
|
|
|
const handleCreateTask = () => {
|
|
selectedTaskId.value = null;
|
|
dialogMode.value = 'create';
|
|
dialogVisible.value = true;
|
|
};
|
|
|
|
const handleSuccess = () => {
|
|
dialogVisible.value = false;
|
|
fetchTasks(); // 操作成功后刷新列表
|
|
};
|
|
```
|
|
|
|
### 6.3 权限控制
|
|
|
|
```typescript
|
|
const canAssign = (task: TaskItem) => {
|
|
return user.value?.role === 'SUPERVISOR' && task.status === 'PENDING';
|
|
};
|
|
|
|
const canReview = (task: TaskItem) => {
|
|
return user.value?.role === 'SUPERVISOR' && task.status === 'COMPLETED';
|
|
};
|
|
```
|
|
|
|
### 6.4 辅助函数
|
|
|
|
```typescript
|
|
const formatStatus = (status: TaskItem['status']) => {
|
|
const map = { PENDING: '待处理', IN_PROGRESS: '进行中', COMPLETED: '已完成', REVIEWED: '已审核' };
|
|
return map[status] || '未知';
|
|
};
|
|
|
|
// getStatusTagType 函数同仪表盘页面设计
|
|
```
|
|
|
|
## 7. API 调用
|
|
|
|
```typescript
|
|
// api/tasks.ts
|
|
export const taskApi = {
|
|
getTasks: (params) => apiClient.get('/management/tasks', { params }),
|
|
// ... 其他任务相关API
|
|
};
|
|
|
|
// stores/task.ts
|
|
export const useTaskStore = defineStore('task', {
|
|
state: () => ({
|
|
tasks: [] as TaskItem[],
|
|
total: 0,
|
|
loading: false,
|
|
}),
|
|
actions: {
|
|
async fetchTasks(params) {
|
|
this.loading = true;
|
|
try {
|
|
const { data } = await taskApi.getTasks(params);
|
|
this.tasks = data.items;
|
|
this.total = data.total;
|
|
} catch (error) {
|
|
console.error('获取任务列表失败', error);
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
```
|
|
|
|
## 8. 样式设计
|
|
|
|
```scss
|
|
.task-management-page {
|
|
padding: 24px;
|
|
|
|
.page-container {
|
|
margin-top: 24px;
|
|
padding: 24px;
|
|
border-radius: 8px;
|
|
border: none;
|
|
}
|
|
|
|
.filter-container {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.action-buttons {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.pagination-container {
|
|
margin-top: 24px;
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
}
|
|
}
|
|
```
|
|
|
|
## 9. 测试用例
|
|
|
|
1. **单元测试**:
|
|
- 测试权限控制函数 `canAssign` 和 `canReview` 的逻辑。
|
|
- 测试 `formatStatus` 辅助函数的正确性。
|
|
- 测试 Pinia store 的 action 是否能正确调用 API 并更新 state。
|
|
|
|
2. **集成测试**:
|
|
- 测试筛选、搜索和重置功能是否正常工作。
|
|
- 测试分页组件是否能正确切换页面和每页数量。
|
|
- 测试"查看"、"分配"、"审核"、"新建"按钮能否正确打开对应模式的对话框。
|
|
- 测试对话框操作成功后,任务列表是否刷新。
|
|
|
|
## 10. 性能优化
|
|
|
|
1. **后端分页**: 必须使用后端分页来处理大量任务数据。
|
|
2. **防抖**: 对搜索输入框使用防抖处理,避免用户输入时频繁触发 API 请求。
|
|
3. **组件懒加载**: `TaskFormDialog` 组件可以通过动态导入实现懒加载,只在需要时才加载。 |