# Project Archive 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:** 前端复用现有 `ruoyi-ui/src/api/ccdiProject.js` 中已预留的归档接口,在列表页 `index.vue` 接入真实异步提交,并收敛 `ArchiveConfirmDialog.vue` 的超范围文案。详情页 `detail.vue` 负责归档态页签禁用和 URL 直达拦截,`UploadData.vue` 与 `ParamConfig.vue` 再补一层归档态禁用保护,确保页面状态和组件行为一致。 **Tech Stack:** Vue 2, Element UI, Axios request wrapper, Node, npm ## 前端验收清单 - 列表页点击“归档”后会调用真实归档接口 - 归档成功后关闭弹窗并刷新列表 - 已归档项目在详情页中“上传数据”“参数配置”页签不可点击 - 直接访问 `?tab=upload` / `?tab=config` 时会自动切到 `overview` - 归档确认弹窗不再出现删数据、生成 PDF、归档库等超需求文案 --- ### Task 1: 先锁定列表页归档动作与弹窗文案契约 **Files:** - Modify: `ruoyi-ui/tests/unit/project-list-archive-flow.test.js` - Modify: `ruoyi-ui/src/views/ccdiProject/index.vue` - Modify: `ruoyi-ui/src/views/ccdiProject/components/ArchiveConfirmDialog.vue` - [ ] **Step 1: Write the failing source-based test** 新增 `ruoyi-ui/tests/unit/project-list-archive-flow.test.js`,至少锁定以下行为: ```javascript assert( pageSource.includes("await archiveProject(data.projectId)"), "确认归档后应调用真实归档接口" ); assert( pageSource.includes("this.$modal.msgSuccess(\"项目归档成功\")") || pageSource.includes("this.$modal.msgSuccess('项目归档成功')"), "归档成功后应提示项目归档成功" ); assert( !dialogSource.includes("自动生成项目报告PDF"), "归档弹窗不应保留超范围 PDF 文案" ); ``` - [ ] **Step 2: Run test to verify it fails** Run: ```bash cd ruoyi-ui node tests/unit/project-list-archive-flow.test.js ``` Expected: - `FAIL` - 原因是当前 `handleConfirmArchive` 仍然只有本地提示,弹窗还保留旧文案 - [ ] **Step 3: Implement the minimal list action and dialog cleanup** 在 `ruoyi-ui/src/views/ccdiProject/index.vue` 中: 1. 从 `@/api/ccdiProject` 引入 `archiveProject` 2. 将 `handleConfirmArchive(data)` 改为真实异步提交: ```javascript async handleConfirmArchive(data) { try { await archiveProject(data.projectId) this.$modal.msgSuccess("项目归档成功") this.archiveDialogVisible = false this.currentArchiveProject = null this.getList() } catch (error) { const message = error && error.message ? error.message : "项目归档失败,请稍后重试" this.$modal.msgError(message) } } ``` 在 `ArchiveConfirmDialog.vue` 中: - 删除 `deleteData` - 删除“自动生成项目报告PDF”“归档库”“恢复”“同时删除数据”等文案和交互 - 仅保留“确认后状态变更为已归档”的说明和 loading 提交态 - [ ] **Step 4: Run test to verify it passes** Run: ```bash cd ruoyi-ui node tests/unit/project-list-archive-flow.test.js ``` Expected: - `PASS` - [ ] **Step 5: Commit** ```bash git add ruoyi-ui/src/views/ccdiProject/index.vue ruoyi-ui/src/views/ccdiProject/components/ArchiveConfirmDialog.vue ruoyi-ui/tests/unit/project-list-archive-flow.test.js git commit -m "实现项目列表归档前端交互" ``` ### Task 2: 实现详情页签禁用与 URL 直达拦截 **Files:** - Modify: `ruoyi-ui/src/views/ccdiProject/detail.vue` - Modify: `ruoyi-ui/tests/unit/project-detail-archive-tab-lock.test.js` - [ ] **Step 1: Write the failing detail-page test** 新增 `ruoyi-ui/tests/unit/project-detail-archive-tab-lock.test.js`,锁定以下关键代码痕迹: ```javascript assert( source.includes('index="upload"') && source.includes(':disabled="isArchiveLockedTab(\'upload\')"'), "上传数据页签应支持归档态禁用" ); assert( /initActiveTabFromRoute\(\)\s*\{[\s\S]*?this\.resolveAccessibleTab/.test(source), "详情页应在路由初始化时校正归档态不可访问页签" ); assert( /handleMenuSelect\(index\)\s*\{[\s\S]*?if\s*\(this\.isArchiveLockedTab\(index\)\)\s*return/.test(source), "点击禁用页签时不应切换" ); ``` - [ ] **Step 2: Run test to verify it fails** Run: ```bash cd ruoyi-ui node tests/unit/project-detail-archive-tab-lock.test.js ``` Expected: - `FAIL` - 原因是当前详情页还没有归档态页签禁用逻辑 - [ ] **Step 3: Implement the minimal tab lock** 在 `ruoyi-ui/src/views/ccdiProject/detail.vue` 中增加: ```javascript computed: { isProjectArchived() { return String(this.projectInfo.projectStatus) === "2"; } } ``` 以及两个辅助方法: ```javascript isArchiveLockedTab(tab) { return this.isProjectArchived && ["upload", "config"].includes(tab); }, resolveAccessibleTab(tab) { if (this.isArchiveLockedTab(tab)) { return "overview"; } return tab; } ``` 并修改: - `initActiveTabFromRoute()`:先取 tab,再调用 `resolveAccessibleTab(tab)` - `handleMenuSelect(index)`:若禁用则直接 `return` - 顶部两个 `el-menu-item`:加 `:disabled="isArchiveLockedTab('upload')"` 和 `:disabled="isArchiveLockedTab('config')"` - [ ] **Step 4: Run tests to verify the tab lock passes** Run: ```bash cd ruoyi-ui node tests/unit/project-detail-archive-tab-lock.test.js node tests/unit/project-detail-tagging-polling.test.js ``` Expected: - 两条测试都 `PASS` - 说明归档页签禁用未破坏现有打标轮询结构 - [ ] **Step 5: Commit** ```bash git add ruoyi-ui/src/views/ccdiProject/detail.vue ruoyi-ui/tests/unit/project-detail-archive-tab-lock.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/ParamConfig.vue` - Modify: `ruoyi-ui/tests/unit/upload-data-disabled-cards.test.js` - Create: `ruoyi-ui/tests/unit/project-archive-readonly-guard.test.js` - [ ] **Step 1: Extend the failing tests for archive guard** 在 `upload-data-disabled-cards.test.js` 中补充归档态断言: ```javascript assert( /disabled:\s*this\.isProjectTagging\s*\|\|\s*this\.isProjectArchived/.test(source), "流水导入卡片应在项目已归档时同步置灰" ); ``` 再新增 `project-archive-readonly-guard.test.js` 锁定: ```javascript assert(uploadSource.includes('return String(this.projectInfo.projectStatus) === "2"')); assert(paramSource.includes('return String(this.projectInfo.projectStatus) === "2"')); assert(paramSource.includes("已归档项目暂不可修改参数")); ``` - [ ] **Step 2: Run tests to verify they fail** Run: ```bash cd ruoyi-ui node tests/unit/upload-data-disabled-cards.test.js node tests/unit/project-archive-readonly-guard.test.js ``` Expected: - `FAIL` - 原因是当前组件只处理“打标中”禁用 - [ ] **Step 3: Implement the minimal archived guard** 在 `UploadData.vue` 中补充: ```javascript isProjectArchived() { return String(this.projectInfo.projectStatus) === "2"; } ``` 并把以下禁用逻辑扩展为归档态也成立: - 顶部“拉取本行信息”“征信导入” - 流水上传卡片 `disabled` - 相关上传/拉取方法中的前置 `return` 在 `ParamConfig.vue` 中补充: ```javascript isProjectArchived() { return String(this.projectInfo.projectStatus) === "2"; } ``` 并同步修改: - 顶部提示文案:归档态显示“已归档项目暂不可修改参数” - 输入框和保存按钮禁用条件改为 `isProjectTagging || isProjectArchived` - `handleSaveAll()` 中增加归档态前置拦截 - [ ] **Step 4: Run frontend regression tests** Run: ```bash cd ruoyi-ui node tests/unit/project-list-archive-flow.test.js node tests/unit/project-detail-archive-tab-lock.test.js node tests/unit/upload-data-disabled-cards.test.js node tests/unit/project-archive-readonly-guard.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/ParamConfig.vue ruoyi-ui/tests/unit/upload-data-disabled-cards.test.js ruoyi-ui/tests/unit/project-archive-readonly-guard.test.js git commit -m "补充项目归档前端只读保护" ``` ### Task 4: 沉淀前端实施与验证记录 **Files:** - Create: `docs/tests/records/2026-03-24-project-archive-frontend-verification.md` - Create: `docs/reports/implementation/2026-03-24-project-archive-frontend-record.md` - [ ] **Step 1: Write the verification skeleton** 验证记录至少包含: ```markdown # 项目归档前端验证记录 ## 验证范围 - 列表归档按钮接入真实接口 - 归档确认弹窗文案已收敛 - 详情页上传数据与参数配置页签不可点击 - 归档态下上传页与参数页有组件级保护 ``` - [ ] **Step 2: Record exact frontend test commands** 把实际执行命令写入记录: ```bash cd ruoyi-ui node tests/unit/project-list-archive-flow.test.js node tests/unit/project-detail-archive-tab-lock.test.js node tests/unit/upload-data-disabled-cards.test.js node tests/unit/project-archive-readonly-guard.test.js ``` Expected: - 记录通过结果,或失败原因 - [ ] **Step 3: Write the frontend implementation record** 在实施记录中说明: - 为什么弹窗文案需要收敛 - 为什么页签禁用和 URL 拦截需要同时做 - 为什么组件级归档保护仍要保留 - 本次前端改动覆盖了哪些文件与测试 - [ ] **Step 4: Commit** ```bash git add docs/tests/records/2026-03-24-project-archive-frontend-verification.md docs/reports/implementation/2026-03-24-project-archive-frontend-record.md git commit -m "补充项目归档前端实施记录" ```