# Project Upload File Delete Frontend Implementation Plan > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. **Goal:** 在项目详情-上传数据-上传文件列表中新增操作列,实现“查看错误原因”和“删除”交互,并让删除后的记录显示为“已删除”。 **Architecture:** 先把状态到“操作/标签”的判断抽取到一个轻量 helper,通过 Node 可执行脚本做最小自动化校验;然后在 `ccdiProjectUpload.js` 新增删除接口,在 `UploadData.vue` 中接入操作列、删除确认框、删除后刷新列表与统计。保留现有轮询机制,不新增前端全局状态管理。 **Tech Stack:** Vue 2, Element UI, Axios request wrapper, Node script verification, npm build --- ### Task 1: 抽取状态与操作规则并先写失败测试 **Files:** - Create: `ruoyi-ui/src/views/ccdiProject/components/detail/uploadFileActionRules.mjs` - Create: `tests/frontend/upload-file-action-rules.test.mjs` **Step 1: Write the failing test** 新建一个轻量 Node 规则测试脚本,先定义期望行为: ```javascript import assert from "node:assert/strict"; import { getUploadFileAction, getUploadFileStatusText, getUploadFileStatusType } from "../../ruoyi-ui/src/views/ccdiProject/components/detail/uploadFileActionRules.mjs"; assert.deepEqual(getUploadFileAction("parsed_failed"), { key: "viewError", text: "查看错误原因" }); assert.deepEqual(getUploadFileAction("parsed_success"), { key: "delete", text: "删除" }); assert.equal(getUploadFileAction("deleted"), null); assert.equal(getUploadFileStatusText("deleted"), "已删除"); assert.equal(getUploadFileStatusType("deleted"), "info"); ``` **Step 2: Run test to verify it fails** Run: ```bash node tests/frontend/upload-file-action-rules.test.mjs ``` Expected: - `FAIL` - 原因是 helper 文件还不存在 **Step 3: Write minimal implementation** 创建 helper 文件: ```javascript export function getUploadFileAction(status) { const actionMap = { parsed_failed: { key: "viewError", text: "查看错误原因" }, parsed_success: { key: "delete", text: "删除" } }; return actionMap[status] || null; } export function getUploadFileStatusText(status) { const map = { uploading: "上传中", parsing: "解析中", parsed_success: "解析成功", parsed_failed: "解析失败", deleted: "已删除" }; return map[status] || status; } export function getUploadFileStatusType(status) { const map = { uploading: "primary", parsing: "warning", parsed_success: "success", parsed_failed: "danger", deleted: "info" }; return map[status] || "info"; } ``` **Step 4: Run test to verify it passes** Run: ```bash node tests/frontend/upload-file-action-rules.test.mjs ``` Expected: - `PASS` **Step 5: Commit** ```bash git add ruoyi-ui/src/views/ccdiProject/components/detail/uploadFileActionRules.mjs tests/frontend/upload-file-action-rules.test.mjs git commit -m "test: 补充上传文件操作规则测试" ``` ### Task 2: 新增删除 API 契约 **Files:** - Modify: `ruoyi-ui/src/api/ccdiProjectUpload.js` **Step 1: Write the failing usage** 先在计划中约定组件调用的新 API 形式,后续组件实现前必须存在: ```javascript export function deleteFileUploadRecord(id) { return request({ url: `/ccdi/file-upload/${id}`, method: "delete" }); } ``` **Step 2: Run a quick import smoke check** Run: ```bash node -e "const fs=require('fs'); const text=fs.readFileSync('ruoyi-ui/src/api/ccdiProjectUpload.js','utf8'); if(!text.includes('deleteFileUploadRecord')) process.exit(1);" ``` Expected: - `FAIL` **Step 3: Write minimal implementation** 把旧的按 `projectId + uploadType` 删除接口保留不动,新加按记录 ID 删除的方法: ```javascript export function deleteFileUploadRecord(id) { return request({ url: `/ccdi/file-upload/${id}`, method: "delete" }); } ``` **Step 4: Run smoke check to verify it passes** Run: ```bash node -e "const fs=require('fs'); const text=fs.readFileSync('ruoyi-ui/src/api/ccdiProjectUpload.js','utf8'); if(!text.includes('deleteFileUploadRecord')) process.exit(1);" ``` Expected: - 退出码 `0` **Step 5: Commit** ```bash git add ruoyi-ui/src/api/ccdiProjectUpload.js git commit -m "feat: 新增上传文件按记录删除接口" ``` ### Task 3: 在列表中渲染操作列和错误原因弹窗 **Files:** - Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/UploadData.vue` - Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/uploadFileActionRules.mjs` - Test: `tests/frontend/upload-file-action-rules.test.mjs` **Step 1: Write the failing test** 先扩充规则测试,确保无操作状态不会返回按钮: ```javascript assert.equal(getUploadFileAction("uploading"), null); assert.equal(getUploadFileAction("parsing"), null); ``` **Step 2: Run test to verify it fails** Run: ```bash node tests/frontend/upload-file-action-rules.test.mjs ``` Expected: - 如果 helper 暂未覆盖全部状态,则 `FAIL` **Step 3: Write minimal implementation** 在 `UploadData.vue` 中: - 引入 helper - 给表格新增“操作”列 - 根据 `getUploadFileAction(scope.row.fileStatus)` 渲染按钮 - 增加 `handleViewError(row)`,直接读取 `row.errorMessage || "未知错误"` 并调用: ```javascript this.$alert(row.errorMessage || "未知错误", "错误信息", { confirmButtonText: "确定", type: "error" }); ``` 表格模板示例: ```vue ``` **Step 4: Run test to verify it passes** Run: ```bash node tests/frontend/upload-file-action-rules.test.mjs ``` 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.mjs tests/frontend/upload-file-action-rules.test.mjs git commit -m "feat: 新增上传文件列表操作列与错误原因查看" ``` ### Task 4: 接入删除确认与刷新逻辑 **Files:** - Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/UploadData.vue` - Modify: `ruoyi-ui/src/api/ccdiProjectUpload.js` **Step 1: Write the failing manual checklist** 先记录需要被满足的行为: - `parsed_success` 行点击“删除”必须弹出二次确认 - 确认后调用 `deleteFileUploadRecord(row.id)` - 成功后提示“删除成功” - 成功后刷新 `loadStatistics()` 与 `loadFileList()` - 若仍存在 `uploading/parsing` 记录,则继续轮询;否则不重复启动轮询 **Step 2: Implement the minimal component code** 在 `UploadData.vue` 新增删除逻辑: ```javascript async handleDeleteFile(row) { await this.$confirm( "删除后将同步删除流水分析平台中的文件,并清除本系统中该文件对应的所有银行流水数据,是否继续?", "提示", { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning" } ); await deleteFileUploadRecord(row.id); this.$message.success("删除成功"); await Promise.all([this.loadStatistics(), this.loadFileList()]); if (this.statistics.uploading > 0 || this.statistics.parsing > 0) { this.startPolling(); } } ``` 并在统一入口 `handleRowAction(row)` 中根据 action key 分流到: - `viewError` - `delete` **Step 3: Run build to verify no syntax errors** Run: ```bash cd ruoyi-ui npm run build:prod ``` Expected: - `BUILD SUCCESS` 或等价的前端打包成功输出 **Step 4: Commit** ```bash git add ruoyi-ui/src/views/ccdiProject/components/detail/UploadData.vue ruoyi-ui/src/api/ccdiProjectUpload.js git commit -m "feat: 接入上传文件删除确认与刷新逻辑" ``` ### Task 5: 完成前端手工回归验证 **Files:** - Verify only: `ruoyi-ui/src/views/ccdiProject/components/detail/UploadData.vue` - Verify only: `ruoyi-ui/src/api/ccdiProjectUpload.js` - Verify only: `tests/frontend/upload-file-action-rules.test.mjs` **Step 1: Run rules test** Run: ```bash node tests/frontend/upload-file-action-rules.test.mjs ``` Expected: - `PASS` **Step 2: Run production build** Run: ```bash cd ruoyi-ui npm run build:prod ``` Expected: - 打包成功 **Step 3: Perform manual UI verification** 手工验证清单: - `parsed_failed` 行显示“查看错误原因” - 点击后能弹出错误信息 - `parsed_success` 行显示“删除” - 点击删除会出现确认框 - 删除成功后状态显示为“已删除” - `deleted` 行不再显示任何操作按钮 **Step 4: Commit** ```bash git add . git commit -m "test: 完成上传文件删除前端回归验证" ```