# Project Import History 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:** 为项目管理页补齐真实“导入历史项目”交互,支持上方新项目配置、下方多选历史项目、提交后跳转详情页,并在上传记录列表中正确展示且锁定历史导入文件记录。 **Architecture:** 前端在现有 `ccdiProject` 列表与详情页结构上最小化改造,重点落在 `ImportHistoryDialog.vue`、`index.vue`、`ccdiProject.js`、`UploadData.vue` 和 `uploadFileActionRules.js`。弹窗负责表单校验、历史项目查询、多选和真实提交;列表页负责成功后跳转;详情页上传记录区域负责展示历史导入来源并收敛删除操作。 **Tech Stack:** Vue 2, Element UI, Axios request wrapper, Node, npm ## 前端验收清单 - 导入历史项目弹窗采用“新项目配置在上、历史项目列表在下”的纵向布局 - 历史项目支持多选,且只展示后端返回的真实历史项目数据 - 提交成功后关闭弹窗、提示任务已开始并跳转到新项目详情页 - 上传记录列表能展示“历史导入 · 来源项目名” - 历史导入文件记录不显示删除入口 --- ### Task 1: 先锁定 API 层与列表页提交后的跳转契约 **Files:** - Modify: `ruoyi-ui/src/api/ccdiProject.js` - Modify: `ruoyi-ui/src/views/ccdiProject/index.vue` - Modify: `ruoyi-ui/tests/unit/project-list-reanalyze-api.test.js` - Create: `ruoyi-ui/tests/unit/project-import-history-submit-flow.test.js` - [ ] **Step 1: Write the failing source-based test** 新增 `project-import-history-submit-flow.test.js`,至少锁定以下代码痕迹: ```javascript assert(apiSource.includes("url: '/ccdi/project/history'")); assert(apiSource.includes("url: '/ccdi/project/import'")); assert(indexSource.includes("this.$router.push({")); assert(indexSource.includes("path: `/ccdiProject/detail/${project.projectId}`") || indexSource.includes("path: `/ccdiProject/detail/${data.projectId}`")); ``` 并锁定成功提示文案包含“历史项目导入任务已开始”。 - [ ] **Step 2: Run test to verify it fails** Run: ```bash cd ruoyi-ui node tests/unit/project-import-history-submit-flow.test.js ``` Expected: - `FAIL` - 原因是当前列表页提交导入后只本地提示成功,没有跳转详情页 - [ ] **Step 3: Implement the minimal API and jump logic** 在 `ruoyi-ui/src/api/ccdiProject.js` 保留并改用真实接口: ```javascript export function listHistoryProjects(query) { ... } export function importFromHistory(data) { ... } ``` 在 `index.vue` 中将 `handleSubmitImport(data)` 收敛为: ```javascript handleSubmitImport(project) { this.$modal.msgSuccess("历史项目导入任务已开始"); this.importDialogVisible = false; this.getList(); this.$router.push({ path: `/ccdiProject/detail/${project.projectId}` }); } ``` - [ ] **Step 4: Run test to verify it passes** Run: ```bash cd ruoyi-ui node tests/unit/project-import-history-submit-flow.test.js ``` Expected: - `PASS` - [ ] **Step 5: Commit** ```bash git add ruoyi-ui/src/api/ccdiProject.js ruoyi-ui/src/views/ccdiProject/index.vue ruoyi-ui/tests/unit/project-import-history-submit-flow.test.js git commit -m "接通历史项目导入前端提交链路" ``` ### Task 2: 重写导入弹窗为真实纵向表单与多选列表 **Files:** - Modify: `ruoyi-ui/src/views/ccdiProject/components/ImportHistoryDialog.vue` - Create: `ruoyi-ui/tests/unit/project-import-history-dialog-layout.test.js` - Create: `ruoyi-ui/tests/unit/project-import-history-dialog-behavior.test.js` - [ ] **Step 1: Write the failing layout/behavior tests** 在布局测试中锁定: ```javascript assert(source.includes('label="新项目名称"')); assert(source.includes('label="备注"')); assert(source.includes("type=\"daterange\"")); assert(source.includes("data-multiselect") === false); // Vue 模板里应改成 checkbox/multiple 语义,而不是单选 radio assert(source.includes("selectedProjectIds")); ``` 在行为测试中锁定: ```javascript assert(source.includes("listHistoryProjects(")); assert(source.includes("importFromHistory(")); assert(source.includes("this.$emit('submit', response.data)")); ``` - [ ] **Step 2: Run tests to verify they fail** Run: ```bash cd ruoyi-ui node tests/unit/project-import-history-dialog-layout.test.js node tests/unit/project-import-history-dialog-behavior.test.js ``` Expected: - `FAIL` - 原因是当前弹窗还是单选、Mock 数据、上下字段不对 - [ ] **Step 3: Implement the minimal vertical dialog** 在 `ImportHistoryDialog.vue` 中完成以下最小改造: - 新项目配置区放上方: - `newProjectName` - `description` - `dateRange` - 历史项目列表区放下方: - 搜索框 - 多选项目列表,状态只读展示 - 固定高度滚动 - 数据改为: ```javascript selectedProjectIds: [], configForm: { newProjectName: "", description: "", dateRange: [] } ``` 提交时组装: ```javascript const payload = { projectName: this.configForm.newProjectName, description: this.configForm.description, sourceProjectIds: this.selectedProjectIds, startDate: this.configForm.dateRange[0] || "", endDate: this.configForm.dateRange[1] || "" }; ``` - [ ] **Step 4: Run tests to verify they pass** Run: ```bash cd ruoyi-ui node tests/unit/project-import-history-dialog-layout.test.js node tests/unit/project-import-history-dialog-behavior.test.js ``` Expected: - `PASS` - [ ] **Step 5: Commit** ```bash git add ruoyi-ui/src/views/ccdiProject/components/ImportHistoryDialog.vue ruoyi-ui/tests/unit/project-import-history-dialog-layout.test.js ruoyi-ui/tests/unit/project-import-history-dialog-behavior.test.js git commit -m "重构历史项目导入弹窗" ``` ### Task 3: 接入上传记录来源展示与历史导入只读操作 **Files:** - Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/UploadData.vue` - Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/uploadFileActionRules.js` - Modify: `ruoyi-ui/tests/unit/upload-data-file-list-table.test.js` - Modify: `ruoyi-ui/tests/unit/upload-data-delete-retag-copy.test.js` - Create: `ruoyi-ui/tests/unit/upload-data-history-import-readonly.test.js` - [ ] **Step 1: Write the failing file-list tests** 新增或补充断言: ```javascript assert(uploadSource.includes("sourceProjectName")); assert(uploadSource.includes("历史导入")); assert(actionRuleSource.includes("HISTORY_IMPORT")); assert(!deleteBehaviorAllowsHistoryImport); ``` 其中 `upload-data-history-import-readonly.test.js` 至少锁定: ```javascript assert(/if\s*\(row\.sourceType\s*===\s*["']HISTORY_IMPORT["']\)\s*\{\s*return null/.test(actionRuleSource)); ``` - [ ] **Step 2: Run tests to verify they fail** Run: ```bash cd ruoyi-ui node tests/unit/upload-data-file-list-table.test.js node tests/unit/upload-data-delete-retag-copy.test.js node tests/unit/upload-data-history-import-readonly.test.js ``` Expected: - `FAIL` - [ ] **Step 3: Implement the minimal readonly display** 在 `UploadData.vue` 的表格中增加来源展示,例如: ```vue ``` 在 `uploadFileActionRules.js` 中收敛删除规则: ```javascript if (row.sourceType === 'HISTORY_IMPORT') { return null } ``` - [ ] **Step 4: Run tests to verify they pass** Run: ```bash cd ruoyi-ui node tests/unit/upload-data-file-list-table.test.js node tests/unit/upload-data-delete-retag-copy.test.js node tests/unit/upload-data-history-import-readonly.test.js ``` Expected: - `PASS` - [ ] **Step 5: Commit** ```bash git add ruoyi-ui/src/views/ccdiProject/components/detail/UploadData.vue ruoyi-ui/src/views/ccdiProject/components/detail/uploadFileActionRules.js ruoyi-ui/tests/unit/upload-data-file-list-table.test.js ruoyi-ui/tests/unit/upload-data-delete-retag-copy.test.js ruoyi-ui/tests/unit/upload-data-history-import-readonly.test.js git commit -m "收敛历史导入文件前端只读展示" ``` ### Task 4: 收口弹窗校验、详情页衔接与回归验证 **Files:** - Modify: `ruoyi-ui/src/views/ccdiProject/components/ImportHistoryDialog.vue` - Modify: `ruoyi-ui/src/views/ccdiProject/detail.vue` - Modify: `ruoyi-ui/tests/unit/project-detail-tagging-polling.test.js` - Modify: `docs/reports/implementation/2026-03-29-project-import-history-plan-record.md` - [ ] **Step 1: Write the failing validation/regression assertions** 在弹窗行为测试里补充: ```javascript assert(source.includes("请选择历史项目")); assert(source.includes("请输入新项目名称")); ``` 在 `project-detail-tagging-polling.test.js` 中锁定: ```javascript assert(detailSource.includes("this.$route.params.projectId")); assert(detailSource.includes("projectStatusPolling")); ``` 目的是保证本次跳转详情页不会破坏现有轮询结构。 - [ ] **Step 2: Run tests to verify they fail or cover the gap** Run: ```bash cd ruoyi-ui node tests/unit/project-import-history-dialog-behavior.test.js node tests/unit/project-detail-tagging-polling.test.js ``` Expected: - 历史导入弹窗测试先 `FAIL` - 轮询测试保持 `PASS` 或在改动后回归通过 - [ ] **Step 3: Implement the minimal validation and handoff polish** 在 `ImportHistoryDialog.vue` 中补充: - 新项目名称必填 - 至少选择一个历史项目 - 提交态 loading - 成功后 reset 表单 在 `detail.vue` 中不新增新逻辑,只确保项目详情页跳转入口沿用现有 `projectId` 路由参数与状态轮询。 - [ ] **Step 4: Run the frontend regression set** Run: ```bash cd ruoyi-ui node tests/unit/project-import-history-submit-flow.test.js node tests/unit/project-import-history-dialog-layout.test.js node tests/unit/project-import-history-dialog-behavior.test.js node tests/unit/upload-data-file-list-table.test.js node tests/unit/upload-data-delete-retag-copy.test.js node tests/unit/upload-data-history-import-readonly.test.js node tests/unit/project-detail-tagging-polling.test.js ``` Expected: - 全部 `PASS` - [ ] **Step 5: Commit** ```bash git add ruoyi-ui/src/views/ccdiProject/components/ImportHistoryDialog.vue ruoyi-ui/src/views/ccdiProject/detail.vue ruoyi-ui/tests/unit/project-import-history-submit-flow.test.js ruoyi-ui/tests/unit/project-import-history-dialog-layout.test.js ruoyi-ui/tests/unit/project-import-history-dialog-behavior.test.js ruoyi-ui/tests/unit/upload-data-history-import-readonly.test.js docs/reports/implementation/2026-03-29-project-import-history-plan-record.md git commit -m "完成历史项目导入前端闭环" ```