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

> **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 的一致性）：

```javascript
export const manageOrganizations = (page = 1) =>
  client.get('/organizations/manage/', { params: { page } })
```

> 注：这个改动方式与 jobs.js 和 applications.js 中已有的 `{ params: { ... } }` 方式保持一致

- [ ] **Step 2: 验证其他导出函数不受影响**

检查文件中的其他函数（`getOrganizations`, `createOrganization` 等）保持不变。

- [ ] **Step 3: Commit**

```bash
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 参数），改为支持分页：

```javascript
export const manageJobs = (page = 1, params = {}) =>
  client.get('/jobs/manage/', { params: { page, ...params } })
```

> 如果现有函数签名已经是 `(params = {})` 的形式，则改为：
> ```javascript
> export const manageJobs = (params = {}) =>
>   client.get('/jobs/manage/', { params: { page: params.page || 1, ...params } })
> ```

- [ ] **Step 2: 验证其他导出函数**

检查 `getJobs`, `createJob`, `updateJob`, `deleteJob` 等函数保持不变。

- [ ] **Step 3: Commit**

```bash
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` 函数，改为（支持分页 + 保留其他参数）：

```javascript
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**

```bash
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>` 导入部分添加（如果还没有）：

```javascript
import { ElMessageBox } from 'element-plus'
import { deleteOrganization } from '@/api/organizations'
```

**Step 4.2: 添加分页和数据获取状态**

- [ ] 在 `const form = reactive(...)` 之后添加以下状态变量：

```javascript
const allOrgs = ref([])         // 所有部门（用于下拉框）
const currentPage = ref(1)      // 当前页码
const pageSize = ref(20)        // 每页行数
const total = ref(0)            // 总条数
```

**Step 4.3: 修改 fetchOrgs 函数**

- [ ] 将现有的 `fetchOrgs` 函数替换为：

```javascript
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` 函数之后添加（用于获取所有部门给下拉框使用）：

```javascript
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` 函数之后添加：

```javascript
function handlePageChange(newPage) {
  fetchOrgs(newPage)
}
```

**Step 4.6: 新增删除函数**

- [ ] 添加 `handleDelete()` 函数：

```javascript
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()` 函数，修改最后的调用为：

```javascript
currentPage.value = 1
fetchOrgs(1)
fetchAllOrgs()
```

**Step 4.8: 修改初始化**

- [ ] 修改 `onMounted` 钩子：

```javascript
onMounted(() => {
  fetchOrgs()
  fetchAllOrgs()
})
```

**Step 4.9: 修改表格的下拉框**

- [ ] 找到表单中的"上级公司"下拉框，修改为使用 `allOrgs`：

```vue
<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"` 这一行，改为：

```vue
<el-table :data="orgs" v-loading="loading" border>
```

**Step 4.11: 添加删除按钮到操作列**

- [ ] 找到操作列（`<el-table-column label="操作"`），修改为：

```vue
<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` 之前添加：

```vue
<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**

```bash
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(...)` 之后）：

```javascript
const currentPage = ref(1)
const pageSize = ref(20)
const total = ref(0)
```

**Step 5.2: 修改 fetchJobs 函数**

- [ ] 替换现有的 `fetchJobs` 函数为：

```javascript
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` 之后添加：

```javascript
function handlePageChange(newPage) {
  fetchJobs(newPage)
}
```

**Step 5.4: 修改 handleSave 函数**

- [ ] 找到 `handleSave()` 函数中的 `fetchJobs()` 调用，改为：

```javascript
currentPage.value = 1
fetchJobs(1)
```

**Step 5.5: 修改表格 v-loading**

- [ ] 找到 `<el-table :data="jobs"` 这一行，确保有 `v-loading="loading"`（可能已有）

**Step 5.6: 添加分页控件**

- [ ] 在 `</el-table>` 之后添加：

```vue
<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**

```bash
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)` 之后）：

```javascript
const currentPage = ref(1)
const pageSize = ref(20)
const total = ref(0)
```

**Step 6.2: 新建 fetchApplications 函数**

- [ ] 在 `onMounted` 钩子之前添加新函数：

```javascript
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` 之后添加函数：

```javascript
function handlePageChange(newPage) {
  fetchApplications(newPage)
}
```

**Step 6.4: 修改 updateStatus 函数**

- [ ] 找到 `updateStatus()` 函数，修改最后为（停留在当前页，重新加载）：

```javascript
fetchApplications(currentPage.value)
```

**Step 6.5: 修改 onMounted**

- [ ] 修改 `onMounted` 钩子中的初始化调用为：

```javascript
onMounted(() => {
  fetchApplications()
})
```

**Step 6.6: 修改表格**

- [ ] 找到 `<el-table :data="applications"` 这一行，确保有 `v-loading="loading"`：

```vue
<el-table :data="applications" v-loading="loading" border>
```

**Step 6.7: 添加分页控件**

- [ ] 在 `</el-table>` 之后、`<el-dialog` 之前添加：

```vue
<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**

```bash
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)` 之后）：

```javascript
const loading = ref(false)      // 注意：该页面原本没有 loading ref，需要新添加
const currentPage = ref(1)
const pageSize = ref(20)
const total = ref(0)
```

**Step 7.2: 修改 fetchUsers 函数**

- [ ] 替换现有的 `fetchUsers` 函数为：

```javascript
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: 添加分页事件处理**

- [ ] 添加函数：

```javascript
function handlePageChange(newPage) {
  fetchUsers(newPage)
}
```

**Step 7.4: 修改 save 函数**

- [ ] 找到 `save()` 函数，修改其中的 `fetchUsers()` 调用为：

```javascript
currentPage.value = 1
fetchUsers(1)
```

**Step 7.5: 修改 onMounted**

- [ ] 确保 `onMounted` 中调用为：

```javascript
onMounted(() => {
  fetchUsers()
  fetchOrgs()
})
```

**Step 7.6: 修改表格模板**

- [ ] 找到 `<el-table :data="users"` 这一行，改为：

```vue
<el-table :data="users" v-loading="loading" border>
```

**Step 7.7: 添加分页控件**

- [ ] 在 `</el-table>` 之后、`<el-dialog` 之前添加：

```vue
<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**

```bash
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: 启动前端开发服务器**

```bash
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**

```bash
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 Composable（如 `usePagination`），进一步 DRY。

---

## 检查清单

完成实现前，请验证：

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