363 lines
8.8 KiB
Markdown
363 lines
8.8 KiB
Markdown
|
|
# 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
|
|||
|
|
<el-table-column label="操作" width="160" fixed="right">
|
|||
|
|
<template slot-scope="scope">
|
|||
|
|
<el-button
|
|||
|
|
v-if="getRowAction(scope.row)"
|
|||
|
|
type="text"
|
|||
|
|
@click="handleRowAction(scope.row)"
|
|||
|
|
>
|
|||
|
|
{{ getRowAction(scope.row).text }}
|
|||
|
|
</el-button>
|
|||
|
|
</template>
|
|||
|
|
</el-table-column>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**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: 完成上传文件删除前端回归验证"
|
|||
|
|
```
|