296 lines
8.4 KiB
Markdown
296 lines
8.4 KiB
Markdown
# 人员管理页面设计文档
|
||
|
||
## 1. 页面概述
|
||
|
||
人员管理页面是提供给系统管理员(ADMIN)用于管理所有用户账户的界面。其核心功能包括展示用户列表、添加新用户、编辑现有用户信息、更改用户角色与状态,以及搜索和筛选用户。
|
||
|
||
## 2. 页面布局
|
||
|
||

|
||
|
||
### 2.1 布局结构
|
||
|
||
页面采用经典的后台管理布局,与任务管理、反馈管理页面保持一致:
|
||
- **顶部**: 操作区域,包含"添加用户"按钮。
|
||
- **中部**: 筛选和搜索区域,支持按用户名、角色或状态进行搜索。
|
||
- **下部**: 用户数据表格,展示所有用户及其关键信息,并提供行内操作。
|
||
- **底部**: 分页组件。
|
||
|
||
## 3. 组件结构
|
||
|
||
```vue
|
||
<template>
|
||
<div class="personnel-management-page">
|
||
<el-page-header title="人员管理" content="系统用户列表" />
|
||
|
||
<el-card class="page-container">
|
||
<!-- 搜索与操作区域 -->
|
||
<div class="table-toolbar">
|
||
<el-form :model="filters" inline class="filter-form">
|
||
<el-form-item>
|
||
<el-input v-model="filters.name" placeholder="按姓名或用户名搜索" clearable />
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-select v-model="filters.role" placeholder="按角色筛选" clearable>
|
||
<el-option label="管理员" value="ADMIN" />
|
||
<el-option label="主管" value="SUPERVISOR" />
|
||
<el-option label="网格员" value="GRID_WORKER" />
|
||
<el-option label="决策者" value="DECISION_MAKER" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-button type="primary" icon="Search" @click="handleSearch">搜索</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
<div class="action-buttons">
|
||
<el-button type="primary" icon="Plus" @click="handleCreateUser">添加用户</el-button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 用户表格 -->
|
||
<el-table :data="users" v-loading="loading" stripe>
|
||
<el-table-column prop="username" label="用户名" />
|
||
<el-table-column prop="name" label="姓名" />
|
||
<el-table-column prop="role" label="角色">
|
||
<template #default="{ row }">
|
||
<span>{{ formatRole(row.role) }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="email" label="邮箱" />
|
||
<el-table-column prop="status" label="状态">
|
||
<template #default="{ row }">
|
||
<el-switch
|
||
v-model="row.isActive"
|
||
@change="handleStatusChange(row)"
|
||
:loading="row.statusChanging"
|
||
active-text="启用"
|
||
inactive-text="禁用"
|
||
/>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="操作" width="200" fixed="right">
|
||
<template #default="{ row }">
|
||
<el-button link type="primary" @click="handleEdit(row)">编辑</el-button>
|
||
<el-popconfirm title="确定要删除该用户吗?" @confirm="handleDelete(row)">
|
||
<template #reference>
|
||
<el-button link type="danger">删除</el-button>
|
||
</template>
|
||
</el-popconfirm>
|
||
</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"
|
||
@size-change="handleSizeChange"
|
||
@current-change="handleCurrentChange"
|
||
/>
|
||
</el-card>
|
||
|
||
<!-- 用户表单对话框 -->
|
||
<UserFormDialog
|
||
v-model="dialogVisible"
|
||
:user-id="selectedUserId"
|
||
@success="onFormSuccess"
|
||
/>
|
||
</div>
|
||
</template>
|
||
```
|
||
|
||
## 4. 数据结构
|
||
|
||
```typescript
|
||
// 用户筛选条件
|
||
interface UserFilters {
|
||
name?: string;
|
||
role?: 'ADMIN' | 'SUPERVISOR' | 'GRID_WORKER' | 'DECISION_MAKER';
|
||
}
|
||
|
||
// 用户列表项
|
||
interface UserListItem {
|
||
id: number;
|
||
username: string;
|
||
name: string;
|
||
email: string;
|
||
role: string;
|
||
isActive: boolean;
|
||
statusChanging?: boolean; // 用于控制 Switch 的 loading 状态
|
||
}
|
||
|
||
// 用户表单数据 (用于对话框)
|
||
interface UserFormData {
|
||
id?: number;
|
||
username: string;
|
||
name: string;
|
||
email: string;
|
||
role: string;
|
||
password?: string; // 创建时需要,编辑时可选
|
||
}
|
||
```
|
||
|
||
## 5. 状态管理
|
||
|
||
```typescript
|
||
// 组件内状态
|
||
const filters = ref<UserFilters>({});
|
||
const pagination = ref({ page: 1, pageSize: 10 });
|
||
const dialogVisible = ref(false);
|
||
const selectedUserId = ref<number | null>(null);
|
||
|
||
// 全局状态 (Pinia Store)
|
||
const personnelStore = usePersonnelStore();
|
||
const { users, total, loading } = storeToRefs(personnelStore);
|
||
```
|
||
|
||
## 6. 交互逻辑
|
||
|
||
### 6.1 CRUD 操作
|
||
|
||
```typescript
|
||
const fetchUsers = () => {
|
||
personnelStore.fetchUsers({ ...filters.value, ...pagination.value });
|
||
};
|
||
|
||
onMounted(fetchUsers);
|
||
|
||
const handleSearch = () => {
|
||
pagination.value.page = 1;
|
||
fetchUsers();
|
||
};
|
||
|
||
const handleCreateUser = () => {
|
||
selectedUserId.value = null;
|
||
dialogVisible.value = true;
|
||
};
|
||
|
||
const handleEdit = (user: UserListItem) => {
|
||
selectedUserId.value = user.id;
|
||
dialogVisible.value = true;
|
||
};
|
||
|
||
const handleDelete = async (user: UserListItem) => {
|
||
await personnelStore.deleteUser(user.id);
|
||
ElMessage.success('用户删除成功');
|
||
fetchUsers(); // 刷新列表
|
||
};
|
||
|
||
const handleStatusChange = async (user: UserListItem) => {
|
||
user.statusChanging = true;
|
||
try {
|
||
await personnelStore.updateUserStatus(user.id, user.isActive);
|
||
ElMessage.success('状态更新成功');
|
||
} catch {
|
||
// 失败时将开关拨回原位
|
||
user.isActive = !user.isActive;
|
||
} finally {
|
||
user.statusChanging = false;
|
||
}
|
||
};
|
||
|
||
const onFormSuccess = () => {
|
||
dialogVisible.value = false;
|
||
fetchUsers();
|
||
};
|
||
```
|
||
|
||
### 6.2 辅助函数
|
||
|
||
```typescript
|
||
const formatRole = (role: string) => {
|
||
const roleMap = {
|
||
ADMIN: '管理员',
|
||
SUPERVISOR: '主管',
|
||
GRID_WORKER: '网格员',
|
||
DECISION_MAKER: '决策者'
|
||
};
|
||
return roleMap[role] || '未知角色';
|
||
};
|
||
```
|
||
|
||
## 7. API 调用
|
||
|
||
```typescript
|
||
// api/personnel.ts
|
||
export const personnelApi = {
|
||
getUsers: (params) => apiClient.get('/personnel', { params }),
|
||
getUserById: (id: number) => apiClient.get(`/personnel/${id}`),
|
||
createUser: (data: UserFormData) => apiClient.post('/personnel', data),
|
||
updateUser: (id: number, data: UserFormData) => apiClient.put(`/personnel/${id}`, data),
|
||
deleteUser: (id: number) => apiClient.delete(`/personnel/${id}`),
|
||
};
|
||
|
||
// stores/personnel.ts
|
||
export const usePersonnelStore = defineStore('personnel', {
|
||
state: () => ({
|
||
users: [] as UserListItem[],
|
||
total: 0,
|
||
loading: false,
|
||
}),
|
||
actions: {
|
||
async fetchUsers(params) {
|
||
this.loading = true;
|
||
try {
|
||
const { data } = await personnelApi.getUsers(params);
|
||
this.users = data.items.map(u => ({ ...u, statusChanging: false }));
|
||
this.total = data.total;
|
||
} finally {
|
||
this.loading = false;
|
||
}
|
||
},
|
||
// ...其他 createUser, updateUser, deleteUser 等 actions
|
||
}
|
||
});
|
||
```
|
||
|
||
## 8. 样式设计
|
||
|
||
```scss
|
||
.personnel-management-page {
|
||
padding: 24px;
|
||
|
||
.page-container {
|
||
margin-top: 24px;
|
||
}
|
||
|
||
.table-toolbar {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.pagination-container {
|
||
margin-top: 24px;
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
}
|
||
}
|
||
```
|
||
|
||
## 9. 关联组件
|
||
|
||
### `UserFormDialog.vue`
|
||
|
||
用于创建和编辑用户的对话框组件。
|
||
- **Props**: `modelValue`, `userId`
|
||
- **功能**:
|
||
- 如果 `userId` 存在,则为编辑模式,对话框打开时会根据 ID 加载用户信息。
|
||
- 如果 `userId` 为空,则为创建模式。
|
||
- 包含用户名、姓名、邮箱、角色和密码的表单字段。
|
||
- 密码字段在编辑模式下为可选,并提示"留空则不修改密码"。
|
||
- 表单提交时进行验证,并调用 store 中对应的 `createUser` 或 `updateUser` action。
|
||
- 操作成功后,发出 `success` 事件。
|
||
|
||
## 10. 测试用例
|
||
|
||
- **集成测试**:
|
||
- 测试能否成功添加一个新用户。
|
||
- 测试能否成功编辑一个现有用户的信息(包括修改密码和不修改密码两种情况)。
|
||
- 测试能否成功删除一个用户。
|
||
- 测试启用/禁用开关是否能正确更新用户状态。
|
||
- 测试搜索和筛选功能是否能正确过滤用户列表。 |