# Staff Recruitment Dual-Sheet Import Frontend Implementation Plan

> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** 将招聘信息管理前端导入交互改为单入口双 Sheet 模式，统一任务轮询与失败弹窗，并在失败列表中展示失败 Sheet、失败行号、失败原因。

**Architecture:** 前端只保留一个导入按钮和一个上传弹窗，统一使用 `/ccdi/staffRecruitment/importTemplate` 与 `/importData`。页面本地状态从“按导入类型区分任务”收口为“按唯一任务 ID 轮询”，失败记录统一通过一个弹窗展示，并用 `sheetName`、`sheetRowNum` 区分失败来源。

**Tech Stack:** Vue 2, Element UI, axios request wrapper, Node 14.21.3 via nvm, source-inspection unit tests, Playwright browser validation

---

## File Map

- Modify: `ruoyi-ui/src/views/ccdiStaffRecruitment/index.vue`
  - 删除独立“导入工作经历”入口，收口上传弹窗、轮询状态和失败列表
- Modify: `ruoyi-ui/src/api/ccdiStaffRecruitment.js`
  - 去掉独立工作经历导入模板/上传调用，保留统一导入 API
- Create: `ruoyi-ui/tests/unit/staff-recruitment-import-toolbar.test.js`
  - 锁定顶部工具栏已收口为单入口
- Create: `ruoyi-ui/tests/unit/staff-recruitment-import-state.test.js`
  - 锁定统一任务状态与轮询逻辑
- Create: `ruoyi-ui/tests/unit/staff-recruitment-import-failure-dialog.test.js`
  - 锁定失败弹窗列定义与 `sheetRowNum` 展示格式
- Modify: `docs/reports/implementation/2026-04-23-staff-recruitment-dual-sheet-import-implementation.md`
  - 追加前端改造与真实页面验证结果

### Task 1: 收口工具栏与上传 API

**Files:**
- Create: `ruoyi-ui/tests/unit/staff-recruitment-import-toolbar.test.js`
- Modify: `ruoyi-ui/src/views/ccdiStaffRecruitment/index.vue`
- Modify: `ruoyi-ui/src/api/ccdiStaffRecruitment.js`

- [ ] **Step 1: 先写工具栏与 API 契约失败测试**

```js
[
  "handleImport()",
  '"/ccdi/staffRecruitment/importData"',
  "招聘信息管理导入模板"
].forEach((token) => {
  assert(source.includes(token), `招聘导入入口缺少统一双Sheet能力: ${token}`)
})

[
  "handleWorkImport",
  "importWorkData",
  "workImportTemplate"
].forEach((token) => {
  assert(!source.includes(token), `招聘页不应继续保留独立工作经历导入: ${token}`)
})
```

- [ ] **Step 2: 运行测试确认失败**

Run: `node ruoyi-ui/tests/unit/staff-recruitment-import-toolbar.test.js`

Expected: FAIL，提示页面仍保留“导入工作经历”按钮或 API 仍存在旧入口

- [ ] **Step 3: 最小化修改页面与 API**

```js
export function importTemplate() {
  return request({
    url: "/ccdi/staffRecruitment/importTemplate",
    method: "post"
  })
}
```

- [ ] **Step 4: 重跑测试**

Run: `node ruoyi-ui/tests/unit/staff-recruitment-import-toolbar.test.js`

Expected: PASS，页面只剩一个导入入口，API 只调用统一模板与上传接口

- [ ] **Step 5: 提交这一小步**

```bash
git add \
  ruoyi-ui/src/views/ccdiStaffRecruitment/index.vue \
  ruoyi-ui/src/api/ccdiStaffRecruitment.js \
  ruoyi-ui/tests/unit/staff-recruitment-import-toolbar.test.js
git commit -m "收口招聘双Sheet导入前端入口"
```

### Task 2: 收口上传弹窗文案与统一任务状态

**Files:**
- Create: `ruoyi-ui/tests/unit/staff-recruitment-import-state.test.js`
- Modify: `ruoyi-ui/src/views/ccdiStaffRecruitment/index.vue`

- [ ] **Step 1: 先写统一状态失败测试**

```js
[
  "模板包含“招聘信息”和“历史工作经历”两个 Sheet。",
  "this.currentTaskId = taskId",
  "this.showFailureButton = false",
  "this.startImportStatusPolling(taskId)"
].forEach((token) => {
  assert(source.includes(token), `招聘导入状态未统一到单任务: ${token}`)
})

[
  "currentImportType",
  "upload.importType",
  "getImportTypeLabel"
].forEach((token) => {
  assert(!source.includes(token), `招聘导入状态不应再按类型拆分: ${token}`)
})
```

- [ ] **Step 2: 运行测试确认失败**

Run: `node ruoyi-ui/tests/unit/staff-recruitment-import-state.test.js`

Expected: FAIL，提示页面仍保留类型切换状态

- [ ] **Step 3: 最小化实现统一轮询状态**

```js
this.saveImportTaskToStorage({
  taskId,
  status: "PROCESSING",
  hasFailures: false
})
this.currentTaskId = taskId
this.startImportStatusPolling(taskId)
```

- [ ] **Step 4: 重跑测试**

Run: `node ruoyi-ui/tests/unit/staff-recruitment-import-state.test.js`

Expected: PASS，弹窗文案改为双 Sheet，页面状态只围绕一个任务 ID 轮询

- [ ] **Step 5: 提交这一小步**

```bash
git add \
  ruoyi-ui/src/views/ccdiStaffRecruitment/index.vue \
  ruoyi-ui/tests/unit/staff-recruitment-import-state.test.js
git commit -m "统一招聘双Sheet导入轮询状态"
```

### Task 3: 调整统一失败弹窗列定义

**Files:**
- Create: `ruoyi-ui/tests/unit/staff-recruitment-import-failure-dialog.test.js`
- Modify: `ruoyi-ui/src/views/ccdiStaffRecruitment/index.vue`

- [ ] **Step 1: 先写失败弹窗失败测试**

```js
[
  'label="失败Sheet"',
  'label="失败行号"',
  "scope.row.sheetName",
  "scope.row.sheetRowNum",
  "失败原因"
].forEach((token) => {
  assert(source.includes(token), `招聘失败弹窗缺少双Sheet定位列: ${token}`)
})
```

- [ ] **Step 2: 运行测试确认失败**

Run: `node ruoyi-ui/tests/unit/staff-recruitment-import-failure-dialog.test.js`

Expected: FAIL，提示弹窗仍按旧类型列展示

- [ ] **Step 3: 实现统一失败表格**

```vue
<el-table-column label="失败Sheet" prop="sheetName" align="center" width="140" />
<el-table-column label="失败行号" prop="sheetRowNum" align="center" width="120">
  <template slot-scope="scope">
    <span>{{ scope.row.sheetRowNum ? `第${scope.row.sheetRowNum}行` : "-" }}</span>
  </template>
</el-table-column>
```

- [ ] **Step 4: 重跑测试**

Run: `node ruoyi-ui/tests/unit/staff-recruitment-import-failure-dialog.test.js`

Expected: PASS，失败弹窗明确展示失败 Sheet、失败行号、失败原因

- [ ] **Step 5: 提交这一小步**

```bash
git add \
  ruoyi-ui/src/views/ccdiStaffRecruitment/index.vue \
  ruoyi-ui/tests/unit/staff-recruitment-import-failure-dialog.test.js
git commit -m "完善招聘双Sheet失败弹窗展示"
```

### Task 4: 做前端构建、真实页面验证与实施记录

**Files:**
- Modify: `ruoyi-ui/src/views/ccdiStaffRecruitment/index.vue`
- Modify: `ruoyi-ui/src/api/ccdiStaffRecruitment.js`
- Modify: `docs/reports/implementation/2026-04-23-staff-recruitment-dual-sheet-import-implementation.md`

- [ ] **Step 1: 切换 Node 版本并执行前端静态回归**

Run: `source ~/.nvm/nvm.sh && nvm use 14.21.3 >/dev/null && node ruoyi-ui/tests/unit/staff-recruitment-import-toolbar.test.js && node ruoyi-ui/tests/unit/staff-recruitment-import-state.test.js && node ruoyi-ui/tests/unit/staff-recruitment-import-failure-dialog.test.js`

Expected: PASS，三个静态契约测试全部通过

- [ ] **Step 2: 执行前端构建**

Run: `source ~/.nvm/nvm.sh && nvm use 14.21.3 >/dev/null && cd ruoyi-ui && npm run build:prod`

Expected: BUILD SUCCESS

- [ ] **Step 3: 启动真实页面并做浏览器验证**

Run:

```bash
source ~/.nvm/nvm.sh && nvm use 14.21.3 >/dev/null
cd ruoyi-ui
npm run dev -- --port 8080
```

Expected: 前端开发服务启动成功，真实页面 `http://localhost:8080` 可访问

Playwright 验证最少覆盖：

- 进入真实 `招聘信息管理` 页面，不使用 prototype 页面
- 从页面下载双 Sheet 模板
- 只导 `招聘信息` Sheet
- 只导 `历史工作经历` Sheet
- 双 Sheet 同时导入
- 已存在工作经历时报错
- 失败弹窗显示 `失败Sheet / 失败行号 / 失败原因`

- [ ] **Step 4: 补前端实施记录**

```md
- 页面导入入口收口为一个按钮
- 上传弹窗提示调整为双 Sheet 文案
- 页面状态收口为单任务轮询
- 失败弹窗新增失败 Sheet、失败行号、失败原因
- 已完成真实页面 Playwright 验证
```

- [ ] **Step 5: 关闭测试进程并提交前端收尾**

Run: 关闭本轮 `npm run dev` 与后端联调用到的进程，确保无残留端口占用

```bash
git add \
  ruoyi-ui/src/views/ccdiStaffRecruitment/index.vue \
  ruoyi-ui/src/api/ccdiStaffRecruitment.js \
  ruoyi-ui/tests/unit/staff-recruitment-import-toolbar.test.js \
  ruoyi-ui/tests/unit/staff-recruitment-import-state.test.js \
  ruoyi-ui/tests/unit/staff-recruitment-import-failure-dialog.test.js \
  docs/reports/implementation/2026-04-23-staff-recruitment-dual-sheet-import-implementation.md
git commit -m "完成招聘双Sheet导入前端改造"
```
