18 KiB
管理后台分页功能实现计划
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: 为管理后台的所有表格页面(职位管理、投递管理、组织架构、用户管理)添加统一的分页功能。
Architecture:
- 后端已支持分页(DRF 的
PageNumberPagination,每页 20 条) - 前端 API 层:修改各模块的 fetch 函数,接受
page参数 - 前端组件层:添加分页状态管理、分页事件处理、UI 控件,共 4 个页面
- 每个组件遵循相同的模式,便于维护和扩展
Tech Stack: Vue 3 Composition API、Element Plus (el-pagination)、DRF pagination
文件结构
前端 API 层(4 个文件修改)
offer_frontend/src/api/organizations.js- 修改manageOrganizations(page=1)offer_frontend/src/api/jobs.js- 修改manageJobs(page=1)offer_frontend/src/api/applications.js- 修改getManageApplications(page=1)offer_frontend/src/api/client.js或页面内 - 用户管理 API 调用
前端组件层(4 个页面修改)
offer_frontend/src/views/admin/OrganizationManageView.vueoffer_frontend/src/views/admin/JobManageView.vueoffer_frontend/src/views/admin/ApplicationManageView.vueoffer_frontend/src/views/admin/UserManageView.vue
实现任务
Task 1: 修改 organizations.js - 支持分页参数
Files:
-
Modify:
offer_frontend/src/api/organizations.js -
Step 1: 修改 manageOrganizations 函数
编辑 offer_frontend/src/api/organizations.js,将 manageOrganizations 函数改为(使用 params 对象方式保持与其他 API 的一致性):
export const manageOrganizations = (page = 1) =>
client.get('/organizations/manage/', { params: { page } })
注:这个改动方式与 jobs.js 和 applications.js 中已有的
{ params: { ... } }方式保持一致
- Step 2: 验证其他导出函数不受影响
检查文件中的其他函数(getOrganizations, createOrganization 等)保持不变。
- Step 3: Commit
git add offer_frontend/src/api/organizations.js
git commit -m "feat(api): 为 manageOrganizations 添加分页参数"
Task 2: 修改 jobs.js - 支持分页参数
Files:
-
Modify:
offer_frontend/src/api/jobs.js -
Step 1: 修改 manageJobs 函数
编辑 offer_frontend/src/api/jobs.js,找到 manageJobs 函数(通常已有 params 参数),改为支持分页:
export const manageJobs = (page = 1, params = {}) =>
client.get('/jobs/manage/', { params: { page, ...params } })
如果现有函数签名已经是
(params = {})的形式,则改为:export const manageJobs = (params = {}) => client.get('/jobs/manage/', { params: { page: params.page || 1, ...params } })
- Step 2: 验证其他导出函数
检查 getJobs, createJob, updateJob, deleteJob 等函数保持不变。
- Step 3: Commit
git add offer_frontend/src/api/jobs.js
git commit -m "feat(api): 为 manageJobs 添加分页参数"
Task 3: 修改 applications.js - 支持分页参数
Files:
-
Modify:
offer_frontend/src/api/applications.js -
Step 1: 修改 getManageApplications 函数
编辑 offer_frontend/src/api/applications.js,找到 getManageApplications 函数,改为(支持分页 + 保留其他参数):
export const getManageApplications = (params = {}) =>
client.get('/applications/manage/', { params: { page: params.page || 1, ...params } })
调用时改为:
getManageApplications({ page: currentPage.value })
- Step 2: 验证其他函数
确保 applyJob, myApplications, updateApplicationStatus 等函数不变。
- Step 3: Commit
git add offer_frontend/src/api/applications.js
git commit -m "feat(api): 为 getManageApplications 添加分页参数"
Task 4: 修改 OrganizationManageView.vue - 添加分页
Files:
- Modify:
offer_frontend/src/views/admin/OrganizationManageView.vue
Step 4.1: 导入所需函数
- 在
<script setup>导入部分添加(如果还没有):
import { ElMessageBox } from 'element-plus'
import { deleteOrganization } from '@/api/organizations'
Step 4.2: 添加分页和数据获取状态
- 在
const form = reactive(...)之后添加以下状态变量:
const allOrgs = ref([]) // 所有部门(用于下拉框)
const currentPage = ref(1) // 当前页码
const pageSize = ref(20) // 每页行数
const total = ref(0) // 总条数
Step 4.3: 修改 fetchOrgs 函数
- 将现有的
fetchOrgs函数替换为:
const fetchOrgs = async (page = 1) => {
loading.value = true
try {
const { data } = await manageOrganizations(page)
orgs.value = data.results
total.value = data.count
currentPage.value = page
} catch (error) {
ElMessage.error('加载部门列表失败,请重试')
} finally {
loading.value = false
}
}
Step 4.4: 新增 fetchAllOrgs 函数
- 在
fetchOrgs函数之后添加(用于获取所有部门给下拉框使用):
const fetchAllOrgs = async () => {
try {
// 预加载前几页,保证下拉框有足够的数据
let allResults = []
for (let i = 1; i <= 5; i++) {
const { data } = await manageOrganizations(i)
allResults = allResults.concat(data.results)
if (i === Math.ceil(data.count / 20)) break // 已加载全部
}
allOrgs.value = allResults
} catch (error) {
ElMessage.error('加载部门列表失败')
}
}
Step 4.5: 添加分页事件处理函数
- 在
fetchAllOrgs函数之后添加:
function handlePageChange(newPage) {
fetchOrgs(newPage)
}
Step 4.6: 新增删除函数
- 添加
handleDelete()函数:
async function handleDelete(id) {
try {
await ElMessageBox.confirm('确认删除该部门?', '提示', {
type: 'warning',
confirmButtonText: '确认',
cancelButtonText: '取消'
})
await deleteOrganization(id)
ElMessage.success('已删除')
currentPage.value = 1
fetchOrgs(1)
fetchAllOrgs()
} catch (error) {
if (error.name !== 'ElMessageBoxCancel') {
ElMessage.error('删除失败')
}
}
}
Step 4.7: 修改保存逻辑
- 找到
save()函数,修改最后的调用为:
currentPage.value = 1
fetchOrgs(1)
fetchAllOrgs()
Step 4.8: 修改初始化
- 修改
onMounted钩子:
onMounted(() => {
fetchOrgs()
fetchAllOrgs()
})
Step 4.9: 修改表格的下拉框
- 找到表单中的"上级公司"下拉框,修改为使用
allOrgs:
<el-form-item label="上级公司">
<el-select v-model="form.parent" clearable placeholder="不选则为集团顶级">
<el-option v-for="o in allOrgs" :key="o.id" :value="o.id" :label="o.name" />
</el-select>
</el-form-item>
Step 4.10: 修改表格模板
- 找到
<el-table :data="orgs"这一行,改为:
<el-table :data="orgs" v-loading="loading" border>
Step 4.11: 添加删除按钮到操作列
- 找到操作列(
<el-table-column label="操作"),修改为:
<el-table-column label="操作" width="120">
<template #default="{ row }">
<el-button size="small" @click="openDialog(row)">编辑</el-button>
<el-button size="small" type="danger" @click="handleDelete(row.id)">删除</el-button>
</template>
</el-table-column>
Step 4.12: 添加分页控件
- 在
</el-table>之后、<el-dialog之前添加:
<div style="margin-top: 16px; text-align: right;">
<el-pagination
v-model:current-page="currentPage"
:page-size="pageSize"
:total="total"
@current-change="handlePageChange"
layout="total, prev, pager, next, jumper"
/>
</div>
- Commit
git add offer_frontend/src/views/admin/OrganizationManageView.vue
git commit -m "feat(admin): 为部门管理页面添加分页、删除功能
- 添加分页状态管理(currentPage, pageSize, total)
- 修改 fetchOrgs 支持分页参数
- 新增 fetchAllOrgs 用于下拉框完整数据
- 新增 handleDelete 删除功能
- 修改下拉框使用 allOrgs 而非 orgs
- 添加分页控件和 loading 指示
- 删除操作后重置分页和刷新数据"
Task 5: 修改 JobManageView.vue - 添加分页
Files:
- Modify:
offer_frontend/src/views/admin/JobManageView.vue
Step 5.1: 添加分页状态
- 在
<script setup>中添加分页相关状态(在const form = reactive(...)之后):
const currentPage = ref(1)
const pageSize = ref(20)
const total = ref(0)
Step 5.2: 修改 fetchJobs 函数
- 替换现有的
fetchJobs函数为:
const fetchJobs = async (page = 1) => {
loading.value = true
try {
const { data } = await manageJobs(page)
jobs.value = data.results
total.value = data.count
currentPage.value = page
} catch (error) {
ElMessage.error('加载职位列表失败,请重试')
} finally {
loading.value = false
}
}
Step 5.3: 添加分页事件处理
- 在
fetchJobs之后添加:
function handlePageChange(newPage) {
fetchJobs(newPage)
}
Step 5.4: 修改 handleSave 函数
- 找到
handleSave()函数中的fetchJobs()调用,改为:
currentPage.value = 1
fetchJobs(1)
Step 5.5: 修改表格 v-loading
- 找到
<el-table :data="jobs"这一行,确保有v-loading="loading"(可能已有)
Step 5.6: 添加分页控件
- 在
</el-table>之后添加:
<div style="margin-top: 16px; text-align: right;">
<el-pagination
v-model:current-page="currentPage"
:page-size="pageSize"
:total="total"
@current-change="handlePageChange"
layout="total, prev, pager, next, jumper"
/>
</div>
- Commit
git add offer_frontend/src/views/admin/JobManageView.vue
git commit -m "feat(admin): 为职位管理页面添加分页功能"
Task 6: 修改 ApplicationManageView.vue - 添加分页
Files:
- Modify:
offer_frontend/src/views/admin/ApplicationManageView.vue
Step 6.1: 添加分页状态
- 在
<script setup>中添加(在const currentResume = ref(null)之后):
const currentPage = ref(1)
const pageSize = ref(20)
const total = ref(0)
Step 6.2: 新建 fetchApplications 函数
- 在
onMounted钩子之前添加新函数:
const fetchApplications = async (page = 1) => {
loading.value = true
try {
const { data } = await getManageApplications({ page })
applications.value = data.results
total.value = data.count
currentPage.value = page
} catch (error) {
ElMessage.error('加载投递列表失败,请重试')
} finally {
loading.value = false
}
}
Step 6.3: 添加分页事件处理
- 在
fetchApplications之后添加函数:
function handlePageChange(newPage) {
fetchApplications(newPage)
}
Step 6.4: 修改 updateStatus 函数
- 找到
updateStatus()函数,修改最后为(停留在当前页,重新加载):
fetchApplications(currentPage.value)
Step 6.5: 修改 onMounted
- 修改
onMounted钩子中的初始化调用为:
onMounted(() => {
fetchApplications()
})
Step 6.6: 修改表格
- 找到
<el-table :data="applications"这一行,确保有v-loading="loading":
<el-table :data="applications" v-loading="loading" border>
Step 6.7: 添加分页控件
- 在
</el-table>之后、<el-dialog之前添加:
<div style="margin-top: 16px; text-align: right;">
<el-pagination
v-model:current-page="currentPage"
:page-size="pageSize"
:total="total"
@current-change="handlePageChange"
layout="total, prev, pager, next, jumper"
/>
</div>
- Commit
git add offer_frontend/src/views/admin/ApplicationManageView.vue
git commit -m "feat(admin): 为投递管理页面添加分页功能
- 新增 fetchApplications 函数支持分页
- 修改 updateStatus 保留当前页并重新加载数据
- 添加分页状态和事件处理
- 添加分页控件和 loading 指示"
Task 7: 修改 UserManageView.vue - 添加分页
Files:
- Modify:
offer_frontend/src/views/admin/UserManageView.vue
Step 7.1: 添加分页相关状态
- 在
<script setup>中添加(在const saving = ref(false)之后):
const loading = ref(false) // 注意:该页面原本没有 loading ref,需要新添加
const currentPage = ref(1)
const pageSize = ref(20)
const total = ref(0)
Step 7.2: 修改 fetchUsers 函数
- 替换现有的
fetchUsers函数为:
const fetchUsers = async (page = 1) => {
loading.value = true
try {
const { data } = await client.get('/auth/users/', { params: { page } })
users.value = data.results
total.value = data.count
currentPage.value = page
} catch (error) {
ElMessage.error('加载用户列表失败,请重试')
} finally {
loading.value = false
}
}
说明: 假设后端已启用分页,响应格式为
{ count, results, ... }。如果返回格式不同,需要相应调整。
Step 7.3: 添加分页事件处理
- 添加函数:
function handlePageChange(newPage) {
fetchUsers(newPage)
}
Step 7.4: 修改 save 函数
- 找到
save()函数,修改其中的fetchUsers()调用为:
currentPage.value = 1
fetchUsers(1)
Step 7.5: 修改 onMounted
- 确保
onMounted中调用为:
onMounted(() => {
fetchUsers()
fetchOrgs()
})
Step 7.6: 修改表格模板
- 找到
<el-table :data="users"这一行,改为:
<el-table :data="users" v-loading="loading" border>
Step 7.7: 添加分页控件
- 在
</el-table>之后、<el-dialog之前添加:
<div style="margin-top: 16px; text-align: right;">
<el-pagination
v-model:current-page="currentPage"
:page-size="pageSize"
:total="total"
@current-change="handlePageChange"
layout="total, prev, pager, next, jumper"
/>
</div>
- Commit
git add offer_frontend/src/views/admin/UserManageView.vue
git commit -m "feat(admin): 为用户管理页面添加分页功能
- 新增 loading ref
- 修改 fetchUsers 支持分页参数
- 添加分页状态和事件处理
- 添加分页控件和 loading 指示
- 新增用户后重置分页到第1页"
Task 8: 功能测试
Files:
-
Test: 手动测试前端功能
-
Step 1: 启动前端开发服务器
cd offer_frontend
npm run dev
-
Step 2: 测试部门管理页面分页
-
访问
/admin/organizations -
验证首次加载显示第 1 页,总数正确
-
点击下一页,数据更新
-
点击最后一页,显示剩余数据
-
新增部门后,页面返回第 1 页
-
Step 3: 测试职位管理页面分页
-
访问
/admin/jobs -
验证分页功能(翻页、跳转)
-
新增/编辑职位后返回第 1 页
-
Step 4: 测试投递管理页面分页
-
访问
/admin/applications -
验证分页功能(翻页、跳转)
-
关键: 修改投递状态(如改为"已查看")后,页面停留在当前页并重新加载当前页数据(不重置分页)
-
验证修改生效
-
Step 5: 测试用户管理页面分页
-
访问
/admin/users -
验证分页功能
-
新增/编辑用户后返回第 1 页
-
Step 6: 错误处理测试
-
模拟网络错误(F12 打开控制台,修改请求),验证是否显示错误提示
-
点击分页按钮重试,是否能恢复
-
Step 7: Commit
git add -A
git commit -m "test: 手动验证所有管理表格分页功能"
验收标准
✅ 所有 4 个管理表格都支持分页:
- 表格显示当前页数据(最多 20 条)
- 底部分页控件正常工作(上一页、下一页、页码跳转)
- 总数显示正确
- 新增/编辑/删除后自动返回第 1 页
- 网络错误时显示错误提示,允许重试
- 加载中显示 loading 指示器
✅ 代码质量:
- 所有改动遵循现有代码风格
- 分页逻辑与组件逻辑清晰分离
- 无性能问题(不应有多余的 API 调用)
✅ Git 提交:
- 每个页面一个 commit
- API 层改动一个 commit
- Commit message 清晰规范
已知限制与后续优化
-
下拉框数据(仅部门管理):当前下拉框显示当前页的部门列表。如果部门数量超过 100,部分部门无法在下拉框中选择。可在后续优化为搜索型下拉框或添加"加载更多"。
-
搜索/过滤:当前不支持与搜索结合的分页。如需要,可在分页基础上添加搜索字段和 API 参数。
-
代码复用:4 个页面的分页逻辑基本相同。可在后续抽象为 Vue Composable(如
usePagination),进一步 DRY。
检查清单
完成实现前,请验证:
- 所有 API 函数已更新,支持
page参数 - 所有页面的分页状态变量已添加
- 所有页面的 fetch 函数已修改支持分页
- 所有页面的模板已添加
<el-pagination>控件 - 所有 CRUD 操作后都重置到第 1 页
- 所有页面都有 loading 状态指示
- Git 提交消息遵循约定式提交规范
- 手动测试通过(所有 4 个页面)