Recruitment_site/docs/superpowers/plans/2026-03-25-admin-pagination.md

18 KiB
Raw Permalink Blame History

管理后台分页功能实现计划

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.vue
  • offer_frontend/src/views/admin/JobManageView.vue
  • offer_frontend/src/views/admin/ApplicationManageView.vue
  • offer_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 清晰规范

已知限制与后续优化

  1. 下拉框数据(仅部门管理):当前下拉框显示当前页的部门列表。如果部门数量超过 100部分部门无法在下拉框中选择。可在后续优化为搜索型下拉框或添加"加载更多"。

  2. 搜索/过滤:当前不支持与搜索结合的分页。如需要,可在分页基础上添加搜索字段和 API 参数。

  3. 代码复用4 个页面的分页逻辑基本相同。可在后续抽象为 Vue ComposableusePagination),进一步 DRY。


检查清单

完成实现前,请验证:

  • 所有 API 函数已更新,支持 page 参数
  • 所有页面的分页状态变量已添加
  • 所有页面的 fetch 函数已修改支持分页
  • 所有页面的模板已添加 <el-pagination> 控件
  • 所有 CRUD 操作后都重置到第 1 页
  • 所有页面都有 loading 状态指示
  • Git 提交消息遵循约定式提交规范
  • 手动测试通过(所有 4 个页面)