整理docs目录并补充文档规范

This commit is contained in:
wkc
2026-03-17 15:06:59 +08:00
parent 9cb77b096e
commit 2fd93463b8
111 changed files with 706 additions and 100 deletions

View File

@@ -0,0 +1,888 @@
# 模型参数配置优化 - 前端实施计划
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**目标:** 重构全局配置页面和项目配置页面,取消模型下拉切换,改为垂直堆叠展示所有模型参数,实现统一保存
**技术栈:** Vue 2.6.12 + Element UI 2.15.14 + Axios 0.28.1
**依赖:** 后端接口已完成(参考后端实施计划)
**预计时间:** 2-3小时
---
## 📋 任务概览
| 任务组 | 任务数 | 说明 |
|--------|--------|------|
| API 层 | 2个 | 添加批量接口方法 |
| 全局配置页面 | 4个 | 重构页面结构 |
| 项目配置页面 | 4个 | 重构页面结构 |
| 测试 | 2个 | 功能测试和集成测试 |
| **总计** | **12个** | |
---
## 前置条件
**在开始前端开发前,确保:**
- ✅ 后端接口已部署完成
- ✅ 后端接口测试通过Swagger测试
- ✅ 后端服务正常运行http://localhost:8080
---
## 任务列表
### Task 1: 在API层添加批量查询方法
**文件:**
- Modify: `ruoyi-ui/src/api/ccdi/modelParam.js`
**步骤 1: 打开API文件**
找到并打开 `ruoyi-ui/src/api/ccdi/modelParam.js` 文件
**步骤 2: 添加批量查询方法**
在文件末尾添加:
```javascript
/**
* 查询所有模型及其参数(按模型分组)
* @param {Object} query - 查询参数
* @param {Number} query.projectId - 项目ID0表示全局配置
* @returns {Promise} 返回所有模型的参数配置
*/
export function listAllParams(query) {
return request({
url: '/ccdi/modelParam/listAll',
method: 'get',
params: query
})
}
```
**步骤 3: 验证导入**
确保文件顶部有:
```javascript
import request from '@/utils/request'
```
**步骤 4: 提交代码**
```bash
git add ruoyi-ui/src/api/ccdi/modelParam.js
git commit -m "feat(ui): 在API层添加批量查询方法"
```
---
### Task 2: 在API层添加批量保存方法
**文件:**
- Modify: `ruoyi-ui/src/api/ccdi/modelParam.js`
**步骤 1: 添加批量保存方法**
在文件末尾添加:
```javascript
/**
* 批量保存所有模型的参数修改
* @param {Object} data - 保存数据
* @param {Number} data.projectId - 项目ID
* @param {Array} data.models - 模型参数列表
* @returns {Promise}
*/
export function saveAllParams(data) {
return request({
url: '/ccdi/modelParam/saveAll',
method: 'post',
data: data
})
}
```
**步骤 2: 提交代码**
```bash
git add ruoyi-ui/src/api/ccdi/modelParam.js
git commit -m "feat(ui): 在API层添加批量保存方法"
```
---
### Task 3: 重构全局配置页面 - 模板部分
**文件:**
- Modify: `ruoyi-ui/src/views/ccdi/modelParam/index.vue`
**步骤 1: 备份原文件(可选)**
```bash
cp ruoyi-ui/src/views/ccdi/modelParam/index.vue ruoyi-ui/src/views/ccdi/modelParam/index.vue.backup
```
**步骤 2: 替换整个 template 部分**
找到 `<template>` 标签,完全替换为:
```vue
<template>
<div class="param-config-container">
<!-- 页面标题 -->
<div class="page-header">
<h2>全局模型参数管理</h2>
</div>
<!-- 模型参数卡片组垂直堆叠 -->
<div class="model-cards-container">
<div
v-for="model in modelGroups"
:key="model.modelCode"
class="model-card"
>
<!-- 模型标题 -->
<div class="model-header">
<h3>{{ model.modelName }}</h3>
</div>
<!-- 参数表格 -->
<el-table :data="model.params" border style="width: 100%">
<el-table-column label="监测项" prop="paramName" width="200" />
<el-table-column label="描述" prop="paramDesc" />
<el-table-column label="阈值设置" width="200">
<template #default="{ row }">
<el-input
v-model="row.paramValue"
placeholder="请输入阈值"
@input="markAsModified(model.modelCode, row)"
/>
</template>
</el-table-column>
<el-table-column label="单位" prop="paramUnit" width="120" />
</el-table>
</div>
</div>
<!-- 统一保存按钮 -->
<div class="button-section">
<el-button type="primary" @click="handleSaveAll" :loading="saving">
保存所有修改
</el-button>
<span v-if="modifiedCount > 0" class="modified-tip">
已修改 {{ modifiedCount }} 个参数
</span>
</div>
</div>
</template>
```
**步骤 3: 暂不提交,继续下一步**
---
### Task 4: 重构全局配置页面 - 脚本部分
**文件:**
- Modify: `ruoyi-ui/src/views/ccdi/modelParam/index.vue`
**步骤 1: 替换整个 script 部分**
找到 `<script>` 标签,完全替换为:
```vue
<script>
import { listAllParams, saveAllParams } from "@/api/ccdi/modelParam";
export default {
name: "ModelParam",
data() {
return {
// 模型参数数据(按模型分组)
modelGroups: [],
// 修改记录(记录哪些参数被修改过)
modifiedParams: new Map(),
// 保存状态
saving: false
};
},
computed: {
/** 计算已修改参数数量 */
modifiedCount() {
let count = 0;
this.modifiedParams.forEach(params => {
count += params.size;
});
return count;
}
},
created() {
this.loadAllParams();
},
methods: {
/** 加载所有模型参数 */
async loadAllParams() {
try {
const res = await listAllParams({ projectId: 0 });
this.modelGroups = res.data.models;
// 清空修改记录
this.modifiedParams.clear();
} catch (error) {
this.$message.error('加载参数失败:' + error.message);
console.error('加载参数失败', error);
}
},
/** 标记参数为已修改 */
markAsModified(modelCode, row) {
if (!this.modifiedParams.has(modelCode)) {
this.modifiedParams.set(modelCode, new Set());
}
this.modifiedParams.get(modelCode).add(row.paramCode);
},
/** 保存所有修改 */
async handleSaveAll() {
// 验证是否有修改
if (this.modifiedCount === 0) {
this.$message.info('没有需要保存的修改');
return;
}
// 构造保存数据(只包含修改过的参数)
const saveDTO = {
projectId: 0,
models: []
};
this.modifiedParams.forEach((paramCodes, modelCode) => {
const modelGroup = this.modelGroups.find(m => m.modelCode === modelCode);
const modifiedParamList = modelGroup.params
.filter(p => paramCodes.has(p.paramCode))
.map(p => ({
paramCode: p.paramCode,
paramValue: p.paramValue
}));
if (modifiedParamList.length > 0) {
saveDTO.models.push({
modelCode: modelCode,
params: modifiedParamList
});
}
});
// 保存
this.saving = true;
try {
await saveAllParams(saveDTO);
this.$modal.msgSuccess('保存成功');
// 清空修改记录并重新加载
this.modifiedParams.clear();
await this.loadAllParams();
} catch (error) {
if (error.response && error.response.data && error.response.data.msg) {
this.$message.error('保存失败:' + error.response.data.msg);
} else {
this.$message.error('保存失败:' + error.message);
}
console.error('保存失败', error);
} finally {
this.saving = false;
}
}
}
};
</script>
```
**步骤 2: 暂不提交,继续下一步**
---
### Task 5: 重构全局配置页面 - 样式部分
**文件:**
- Modify: `ruoyi-ui/src/views/ccdi/modelParam/index.vue`
**步骤 1: 替换整个 style 部分**
找到 `<style>` 标签,完全替换为:
```vue
<style scoped lang="scss">
.param-config-container {
padding: 20px;
background-color: #f5f5f5;
min-height: 100vh;
}
.page-header {
margin-bottom: 20px;
padding: 15px;
background: #fff;
border-radius: 4px;
h2 {
font-size: 18px;
font-weight: bold;
color: #333;
margin: 0;
}
}
.model-cards-container {
margin-bottom: 20px;
}
.model-card {
background: #fff;
border-radius: 4px;
padding: 20px;
margin-bottom: 20px;
border: 1px solid #e4e7ed;
.model-header {
margin-bottom: 15px;
h3 {
font-size: 16px;
font-weight: bold;
color: #333;
margin: 0;
}
}
}
.button-section {
padding: 15px;
background: #fff;
border-radius: 4px;
text-align: left;
.modified-tip {
margin-left: 15px;
color: #909399;
font-size: 14px;
}
}
</style>
```
**步骤 2: 提交代码**
```bash
git add ruoyi-ui/src/views/ccdi/modelParam/index.vue
git commit -m "feat(ui): 重构全局模型参数配置页面"
```
---
### Task 6: 测试全局配置页面
**检查点:全局配置页面完成**
**步骤 1: 启动前端开发服务器**
```bash
cd ruoyi-ui
npm run dev
```
等待编译完成,看到 "Compiled successfully" 提示。
**步骤 2: 访问页面**
1. 打开浏览器:`http://localhost:80`
2. 登录系统:
- 用户名:`admin`
- 密码:`admin123`
3. 导航到:系统管理 > 模型参数管理
**步骤 3: 验证页面显示**
检查以下项目:
- [ ] 页面标题显示"全局模型参数管理"
- [ ] 所有模型的参数表格按垂直堆叠方式显示
- [ ] 每个模型卡片有标题和参数表格
- [ ] 参数表格包含:监测项、描述、阈值设置、单位
**步骤 4: 测试修改功能**
1. 修改某个参数的值
2. 观察底部是否显示"已修改 X 个参数"提示
3. 验证修改数量是否准确
4. 点击"保存所有修改"按钮
5. 验证保存成功提示
6. 验证页面是否刷新显示最新数据
**步骤 5: 测试错误处理**
1. 尝试清空必填参数值(如果后端有验证)
2. 尝试保存,验证错误提示是否友好
**步骤 6: 提交测试记录**
```bash
mkdir -p docs/tests/records
echo "## 全局配置页面测试结果\n\n测试时间$(date)\n\n- [x] 页面显示正确\n- [x] 修改功能正常\n- [x] 保存功能正常\n- [x] 错误处理正常" > docs/tests/records/global-config-test.md
git add docs/tests/records/
git commit -m "test(ui): 记录全局配置页面测试结果"
```
---
### Task 7: 重构项目配置页面 - 模板部分
**文件:**
- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/ParamConfig.vue`
**步骤 1: 替换整个 template 部分**
找到 `<template>` 标签,完全替换为:
```vue
<template>
<div class="param-config-container">
<!-- 模型参数卡片组垂直堆叠 -->
<div class="model-cards-container">
<div
v-for="model in modelGroups"
:key="model.modelCode"
class="model-card"
>
<!-- 模型标题 -->
<div class="model-header">
<h3>{{ model.modelName }}</h3>
</div>
<!-- 参数表格 -->
<el-table :data="model.params" border style="width: 100%">
<el-table-column label="监测项" prop="paramName" width="200" />
<el-table-column label="描述" prop="paramDesc" />
<el-table-column label="阈值设置" width="200">
<template #default="{ row }">
<el-input
v-model="row.paramValue"
placeholder="请输入阈值"
@input="markAsModified(model.modelCode, row)"
/>
</template>
</el-table-column>
<el-table-column label="单位" prop="paramUnit" width="120" />
</el-table>
</div>
</div>
<!-- 统一保存按钮 -->
<div class="button-section">
<el-button type="primary" @click="handleSaveAll" :loading="saving">
保存所有修改
</el-button>
<span v-if="modifiedCount > 0" class="modified-tip">
已修改 {{ modifiedCount }} 个参数
</span>
</div>
</div>
</template>
```
**步骤 2: 暂不提交,继续下一步**
---
### Task 8: 重构项目配置页面 - 脚本部分
**文件:**
- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/ParamConfig.vue`
**步骤 1: 替换整个 script 部分**
找到 `<script>` 标签,完全替换为:
```vue
<script>
import { listAllParams, saveAllParams } from "@/api/ccdi/modelParam";
export default {
name: 'ParamConfig',
props: {
projectId: {
type: [String, Number],
required: true
},
projectInfo: {
type: Object,
default: () => ({})
}
},
data() {
return {
// 模型参数数据(按模型分组)
modelGroups: [],
// 修改记录(记录哪些参数被修改过)
modifiedParams: new Map(),
// 保存状态
saving: false
}
},
computed: {
/** 计算已修改参数数量 */
modifiedCount() {
let count = 0;
this.modifiedParams.forEach(params => {
count += params.size;
});
return count;
}
},
watch: {
projectId(newVal) {
if (newVal) {
this.loadAllParams();
}
}
},
created() {
if (this.projectId) {
this.loadAllParams();
}
},
methods: {
/** 加载所有模型参数 */
async loadAllParams() {
try {
const res = await listAllParams({ projectId: this.projectId });
this.modelGroups = res.data.models;
// 清空修改记录
this.modifiedParams.clear();
} catch (error) {
this.$message.error('加载参数失败:' + error.message);
console.error('加载参数失败', error);
}
},
/** 标记参数为已修改 */
markAsModified(modelCode, row) {
if (!this.modifiedParams.has(modelCode)) {
this.modifiedParams.set(modelCode, new Set());
}
this.modifiedParams.get(modelCode).add(row.paramCode);
},
/** 保存所有修改 */
async handleSaveAll() {
// 验证是否有修改
if (this.modifiedCount === 0) {
this.$message.info('没有需要保存的修改');
return;
}
// 构造保存数据(只包含修改过的参数)
const saveDTO = {
projectId: this.projectId,
models: []
};
this.modifiedParams.forEach((paramCodes, modelCode) => {
const modelGroup = this.modelGroups.find(m => m.modelCode === modelCode);
const modifiedParamList = modelGroup.params
.filter(p => paramCodes.has(p.paramCode))
.map(p => ({
paramCode: p.paramCode,
paramValue: p.paramValue
}));
if (modifiedParamList.length > 0) {
saveDTO.models.push({
modelCode: modelCode,
params: modifiedParamList
});
}
});
// 保存
this.saving = true;
try {
await saveAllParams(saveDTO);
this.$message.success('保存成功');
// 清空修改记录并重新加载
this.modifiedParams.clear();
await this.loadAllParams();
} catch (error) {
if (error.response && error.response.data && error.response.data.msg) {
this.$message.error('保存失败:' + error.response.data.msg);
} else {
this.$message.error('保存失败:' + error.message);
}
console.error('保存失败', error);
} finally {
this.saving = false;
}
}
}
}
</script>
```
**步骤 2: 暂不提交,继续下一步**
---
### Task 9: 重构项目配置页面 - 样式部分
**文件:**
- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/ParamConfig.vue`
**步骤 1: 替换整个 style 部分**
找到 `<style>` 标签,完全替换为:
```vue
<style scoped lang="scss">
.param-config-container {
padding: 20px;
background-color: #fff;
min-height: 400px;
}
.model-cards-container {
margin-bottom: 20px;
}
.model-card {
background: #fff;
border-radius: 4px;
padding: 20px;
margin-bottom: 20px;
border: 1px solid #e4e7ed;
.model-header {
margin-bottom: 15px;
h3 {
font-size: 16px;
font-weight: bold;
color: #333;
margin: 0;
}
}
}
.button-section {
padding: 15px;
background: #fff;
border-radius: 4px;
text-align: left;
.modified-tip {
margin-left: 15px;
color: #909399;
font-size: 14px;
}
}
</style>
```
**步骤 2: 提交代码**
```bash
git add ruoyi-ui/src/views/ccdiProject/components/detail/ParamConfig.vue
git commit -m "feat(ui): 重构项目内模型参数配置页面"
```
---
### Task 10: 测试项目配置页面
**检查点:项目配置页面完成**
**步骤 1: 确保前后端都已启动**
- 后端:`http://localhost:8080` 运行中
- 前端:`http://localhost:80` 运行中
**步骤 2: 访问项目页面**
1. 打开浏览器:`http://localhost:80`
2. 登录系统
3. 导航到:初核项目管理
4. 点击任意项目的"进入"按钮
5. 切换到"参数配置"标签页
**步骤 3: 验证页面显示**
- [ ] 页面显示项目的参数配置
- [ ] 所有模型的参数表格按垂直堆叠方式显示
- [ ] 参数表格包含正确数据
**步骤 4: 测试使用默认配置的项目**
1. 创建一个新项目
2. 配置类型选择"使用默认配置"
3. 进入该项目的参数配置页面
4. 验证显示的是系统默认参数
5. 修改某个参数并保存
6. 验证保存成功
7. 验证项目配置类型变为"自定义配置"(可通过项目详情查看)
**步骤 5: 测试已有自定义配置的项目**
1. 进入一个已有自定义配置的项目
2. 修改参数并保存
3. 验证保存成功
**步骤 6: 测试多模型同时修改**
1. 同时修改多个模型的参数
2. 验证"已修改 X 个参数"提示准确
3. 保存并验证所有修改都成功
**步骤 7: 提交测试记录**
```bash
echo "## 项目配置页面测试结果\n\n测试时间$(date)\n\n- [x] 页面显示正确\n- [x] 使用默认配置项目测试通过\n- [x] 自定义配置项目测试通过\n- [x] 多模型修改测试通过" > docs/tests/records/project-config-test.md
git add docs/tests/records/
git commit -m "test(ui): 记录项目配置页面测试结果"
```
---
### Task 11: 端到端集成测试
**检查点:前后端集成完成**
**步骤 1: 测试全局配置影响项目配置**
1. 在全局配置页面修改某个参数LARGE_TRANSACTION 的阈值)
2. 保存成功
3. 创建一个新项目,选择"使用默认配置"
4. 进入该项目的参数配置页面
5. 验证显示的是修改后的默认参数值
**步骤 2: 测试项目配置不影响全局配置**
1. 在项目配置页面修改某个参数
2. 保存成功
3. 返回全局配置页面
4. 验证全局参数值未改变
**步骤 3: 测试并发场景**
1. 打开两个浏览器标签页
2. 标签页1打开全局配置页面
3. 标签页2打开项目配置页面
4. 同时修改参数并保存
5. 验证各自的修改都成功保存
**步骤 4: 性能测试**
1. 打开浏览器开发者工具F12
2. 切换到 Network 标签
3. 访问全局配置页面
4. 记录 `listAll` 接口响应时间(应 < 200ms
5. 修改多个参数并保存
6. 记录 `saveAll` 接口响应时间(应 < 500ms
**步骤 5: 提交测试报告**
```bash
echo "## 端到端集成测试结果\n\n测试时间$(date)\n\n### 功能测试\n- [x] 全局配置影响项目配置\n- [x] 项目配置不影响全局配置\n- [x] 并发操作正常\n\n### 性能测试\n- [x] listAll接口响应时间 < 200ms\n- [x] saveAll接口响应时间 < 500ms\n\n### 结论\n前后端集成测试通过功能正常性能符合要求。" > docs/tests/records/e2e-test.md
git add docs/tests/records/
git commit -m "test(ui): 完成端到端集成测试"
```
---
### Task 12: 最终提交和推送
**检查点:所有前端任务完成**
**步骤 1: 检查所有更改**
```bash
git status
```
确保所有文件都已提交。如果有未提交的文件:
```bash
git add .
git commit -m "feat(ui): 完成模型参数配置页面优化"
```
**步骤 2: 推送到远程仓库**
```bash
git push origin dev
```
**步骤 3: 创建Pull Request可选**
如果需要在GitHub/GitLab上创建PR
**PR标题** `feat(ui): 优化模型参数配置页面布局`
**PR描述**
```markdown
## 变更说明
### 前端优化
- ✅ 取消模型下拉切换
- ✅ 改为垂直堆叠展示所有模型参数
- ✅ 实现统一保存机制
- ✅ 添加修改提示(显示已修改参数数量)
- ✅ 全局配置和项目配置页面同步优化
### 影响范围
- `ruoyi-ui/src/api/ccdi/modelParam.js` - API层
- `ruoyi-ui/src/views/ccdi/modelParam/index.vue` - 全局配置页面
- `ruoyi-ui/src/views/ccdiProject/components/detail/ParamConfig.vue` - 项目配置页面
### 测试结果
- ✅ 全局配置页面功能正常
- ✅ 项目配置页面功能正常
- ✅ 端到端集成测试通过
- ✅ 性能测试通过
### 截图
(如果有,可以添加前后对比截图)
```
**完成!🎉**
---
## ✅ 完成标志
前端实施完成的标志:
- ✅ 所有12个任务执行完成
- ✅ 全局配置页面重构完成并测试通过
- ✅ 项目配置页面重构完成并测试通过
- ✅ 端到端集成测试通过
- ✅ 代码已提交并推送到远程仓库
---
## 🎨 UI效果说明
### 新布局特点:
1. **垂直堆叠**:所有模型的参数表格按顺序垂直排列
2. **卡片式设计**:每个模型一个独立的卡片区域
3. **统一保存**:底部一个"保存所有修改"按钮
4. **修改提示**:实时显示已修改参数数量
5. **响应式**:参数表格自适应宽度
### 用户体验提升:
- 无需切换模型,一目了然查看所有参数
- 统一保存,操作更简便
- 修改提示,避免遗漏
- 性能优化,响应更快
---
**前端实施计划完成!与后端实施计划配合使用,完成整个优化项目。**

View File

@@ -0,0 +1,411 @@
# Project Detail Transaction Query Frontend Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** Replace the placeholder `DetailQuery.vue` with the full project detail bank statement query page and remove the obsolete upload-record jump entry.
**Architecture:** Keep the page inside the existing `ccdiProject/detail.vue` dynamic component system. Use one dedicated API module for list, options, detail, and export calls; preload project-wide select options on page entry; keep the entire interaction inside `DetailQuery.vue` plus a minimal cleanup in `UploadData.vue`.
**Tech Stack:** Vue 2.6, Element UI 2.15, Axios request wrapper, existing global `this.download`, `npm run build:prod` + manual smoke validation
---
### Task 1: Replace the placeholder with a typed page shell and make the build fail first
**Files:**
- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/DetailQuery.vue`
- Create: `ruoyi-ui/src/api/ccdiProjectBankStatement.js`
**Step 1: Write the failing verification**
In `DetailQuery.vue`, replace the placeholder text with the final top-level data structure and import the new API module before creating it:
```javascript
import {
listBankStatement,
getBankStatementOptions,
getBankStatementDetail
} from "@/api/ccdiProjectBankStatement";
```
Also add empty methods `getList`, `getOptions`, and `handleExport`.
**Step 2: Run build to verify it fails**
Run: `cd ruoyi-ui && npm run build:prod`
Expected: FAIL because `@/api/ccdiProjectBankStatement` does not exist yet.
**Step 3: Write minimal implementation**
Create `ruoyi-ui/src/api/ccdiProjectBankStatement.js` with:
```javascript
import request from "@/utils/request";
export function listBankStatement(query) {
return request({
url: "/ccdi/project/bank-statement/list",
method: "get",
params: query
});
}
```
Add stubs for:
- `getBankStatementOptions(projectId)`
- `getBankStatementDetail(bankStatementId)`
Do not add export wrapper here; use `this.download` directly in the component.
**Step 4: Run build to verify it passes**
Run: `cd ruoyi-ui && npm run build:prod`
Expected: PASS or fail only on unfinished component template bindings.
**Step 5: Commit**
```bash
git add ruoyi-ui/src/views/ccdiProject/components/detail/DetailQuery.vue ruoyi-ui/src/api/ccdiProjectBankStatement.js
git commit -m "搭建流水明细查询前端页面骨架"
```
### Task 2: Implement page state, preload list and project-wide select options
**Files:**
- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/DetailQuery.vue`
**Step 1: Write the failing verification**
Add the real component state before wiring the template:
```javascript
data() {
return {
loading: false,
optionsLoading: false,
activeTab: "all",
queryParams: {
projectId: this.projectId,
pageNum: 1,
pageSize: 10,
tabType: "all",
transactionStartTime: "",
transactionEndTime: "",
counterpartyName: "",
counterpartyNameEmpty: false,
userMemo: "",
userMemoEmpty: false,
ourSubjects: [],
ourBanks: [],
ourAccounts: [],
amountMin: "",
amountMax: "",
counterpartyAccount: "",
counterpartyAccountEmpty: false,
transactionType: "",
transactionTypeEmpty: false,
orderBy: "trxDate",
orderDirection: "desc"
},
optionData: {
ourSubjectOptions: [],
ourBankOptions: [],
ourAccountOptions: []
}
};
}
```
Call both `getList()` and `getOptions()` in `created()`.
**Step 2: Run build to verify it fails**
Run: `cd ruoyi-ui && npm run build:prod`
Expected: FAIL because the template has not been updated to use the new reactive state yet, or methods are incomplete.
**Step 3: Write minimal implementation**
Implement:
- `getList()`
- `getOptions()`
- `syncProjectId()`
- `handleQuery()`
- `resetQuery()`
Ensure `created()` and the `projectId` watcher both call:
```javascript
this.queryParams.projectId = this.projectId;
this.getOptions();
this.getList();
```
**Step 4: Run build to verify it passes**
Run: `cd ruoyi-ui && npm run build:prod`
Expected: PASS
**Step 5: Commit**
```bash
git add ruoyi-ui/src/views/ccdiProject/components/detail/DetailQuery.vue
git commit -m "实现流水明细查询页面初始加载逻辑"
```
### Task 3: Build the left filter panel and local-search multi-selects
**Files:**
- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/DetailQuery.vue`
**Step 1: Write the failing verification**
Replace the old placeholder template with the final left column filter markup using Element UI controls:
- `el-date-picker` for transaction time range
- `el-input` + `el-checkbox` for counterparty name, summary, counterparty account, transaction type
- `el-select multiple filterable collapse-tags` for subject, bank, account
- amount range inputs
Do not wire the search/reset buttons yet.
**Step 2: Run build to verify it fails**
Run: `cd ruoyi-ui && npm run build:prod`
Expected: FAIL because methods or bound state used in the template are incomplete.
**Step 3: Write minimal implementation**
Wire the filter controls to `queryParams` and `dateRange`:
```javascript
watch: {
dateRange(value) {
this.queryParams.transactionStartTime = value && value[0] ? value[0] : "";
this.queryParams.transactionEndTime = value && value[1] ? value[1] : "";
}
}
```
Add the left action buttons:
- `查询`
- `重置`
Add local filter support inside each `el-select` using Element UI `filterable`.
**Step 4: Run build to verify it passes**
Run: `cd ruoyi-ui && npm run build:prod`
Expected: PASS
**Step 5: Commit**
```bash
git add ruoyi-ui/src/views/ccdiProject/components/detail/DetailQuery.vue
git commit -m "完成流水明细查询筛选栏布局"
```
### Task 4: Build the right result area, tabs, table, pagination, and detail drawer
**Files:**
- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/DetailQuery.vue`
**Step 1: Write the failing verification**
Add the right-side structure:
- top tabs `全部 / 流入 / 流出`
- export button
- `el-table`
- `pagination`
- detail drawer or dialog
Reference the final columns:
- `trxDate`
- `leAccountNo / leAccountName`
- `customerAccountName / customerAccountNo`
- `userMemo / cashType`
- `displayAmount`
- `详情`
**Step 2: Run build to verify it fails**
Run: `cd ruoyi-ui && npm run build:prod`
Expected: FAIL because event handlers like `handleTabChange`, `handleSortChange`, `handleViewDetail`, or `handlePageChange` are missing.
**Step 3: Write minimal implementation**
Implement:
- `handleTabChange(tab)`
- `handleSortChange({ prop, order })`
- `handlePageChange(pageNum)`
- `handleViewDetail(row)`
- `closeDetailDialog()`
Sort mapping:
```javascript
const sortMap = {
trxDate: "trxDate",
displayAmount: "amount"
};
```
Tab mapping:
```javascript
const tabMap = {
all: "all",
in: "in",
out: "out"
};
```
**Step 4: Run build to verify it passes**
Run: `cd ruoyi-ui && npm run build:prod`
Expected: PASS
**Step 5: Commit**
```bash
git add ruoyi-ui/src/views/ccdiProject/components/detail/DetailQuery.vue
git commit -m "完成流水明细查询结果区与详情展示"
```
### Task 5: Wire export, empty/error states, and responsive styles
**Files:**
- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/DetailQuery.vue`
**Step 1: Write the failing verification**
Add the `导出流水` button click handler without implementation:
```javascript
handleExport() {
// TODO
}
```
Show the button in the UI but keep it clickable.
**Step 2: Run build to verify it fails functionally in manual smoke**
Run:
```bash
cd ruoyi-ui
npm run build:prod
```
Expected: BUILD PASS, but manual smoke in browser should show export button with no behavior yet.
**Step 3: Write minimal implementation**
Implement export with the existing global helper:
```javascript
this.download(
"ccdi/project/bank-statement/export",
{ ...this.queryParams },
`${this.projectInfo.projectName || "项目"}_流水明细_${Date.now()}.xlsx`
);
```
Add:
- empty state when `list.length === 0`
- inline error tip when list load fails
- disabled export when total is `0`
- responsive layout styles for mobile widths
**Step 4: Run build and manual smoke verification**
Run:
```bash
cd ruoyi-ui
npm run build:prod
```
Expected: BUILD PASS
Manual smoke:
1. Open project detail page
2. Switch to `流水明细查询`
3. Confirm left filters render
4. Confirm export button is disabled on empty data and enabled on non-empty data
**Step 5: Commit**
```bash
git add ruoyi-ui/src/views/ccdiProject/components/detail/DetailQuery.vue
git commit -m "补齐流水明细查询导出与状态反馈"
```
### Task 6: Remove obsolete upload-page operation column and finish smoke verification
**Files:**
- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/UploadData.vue`
- Modify if needed: `ruoyi-ui/src/views/ccdiProject/components/detail/DetailQuery.vue`
**Step 1: Write the failing verification**
Delete only the old upload-table operation column usage in the template, but do not clean methods yet.
**Step 2: Run build to verify it fails**
Run: `cd ruoyi-ui && npm run build:prod`
Expected: FAIL because `handleViewFlow` or `handleViewError` remains unused or referenced.
**Step 3: Write minimal implementation**
In `UploadData.vue`:
- remove the upload-record operation column entirely
- remove methods:
- `handleViewFlow`
- `handleViewError`
- remove any event payload that tried to change menu to `detail`
Keep upload, polling, and record-list refresh behavior unchanged.
**Step 4: Run final verification**
Run:
```bash
cd ruoyi-ui
npm run build:prod
```
Expected: BUILD PASS
Manual smoke:
1. Open one project detail page
2. Confirm `上传数据` 页不再出现“查看流水”入口
3. Confirm `流水明细查询` 可独立查询、筛选、分页、导出
4. Confirm手机宽度下左右布局能够折叠为上下布局
**Step 5: Commit**
```bash
git add ruoyi-ui/src/views/ccdiProject/components/detail/UploadData.vue ruoyi-ui/src/views/ccdiProject/components/detail/DetailQuery.vue ruoyi-ui/src/api/ccdiProjectBankStatement.js
git commit -m "完成流水明细查询前端实现并移除旧跳转入口"
```

View File

@@ -0,0 +1,385 @@
# Project Detail Pull Bank Info Frontend Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** Build the “拉取本行信息” modal on the project detail upload page, including ID-card Excel auto-parse and backfill, date-range submission, and reuse of the existing upload-record polling refresh flow.
**Architecture:** Keep the implementation inside the existing `UploadData.vue` page and `ccdiProjectUpload.js` API module instead of introducing a new page or a new API file. Replace the current confirm-only placeholder with a real dialog, call a dedicated parse endpoint as soon as the user chooses an Excel file, merge the returned身份证集合 back into the textarea, then submit the final list and reuse the existing statistics, record list, and polling behavior.
**Tech Stack:** Vue 2.6, Element UI 2.15, Axios request wrapper, existing polling/list refresh logic, `npm run build:prod`
---
### Task 1: Add API contracts and make the build fail first
**Files:**
- Modify: `ruoyi-ui/src/api/ccdiProjectUpload.js`
- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/UploadData.vue`
**Step 1: Write the failing verification**
先在 `UploadData.vue` 中把原来的简单确认流程替换成新的 API 引用,但暂时不创建 API 方法:
```javascript
import {
getImportStatus,
getNameListOptions,
getUploadStatus,
pullBankInfo,
parseIdCardFile,
updateNameListSelection,
uploadFile,
batchUploadFiles,
getFileUploadList,
getFileUploadStatistics,
} from "@/api/ccdiProjectUpload";
```
并把 `handleFetchBankInfo` 改成只打开弹窗:
```javascript
handleFetchBankInfo() {
this.pullBankInfoDialogVisible = true;
}
```
**Step 2: Run build to verify it fails**
Run: `cd ruoyi-ui; npm run build:prod`
Expected: FAIL because `parseIdCardFile` does not exist in `ccdiProjectUpload.js`, and the new dialog state has not been defined.
**Step 3: Write minimal implementation**
`ccdiProjectUpload.js` 中补两个接口:
```javascript
export function parseIdCardFile(file) {
const formData = new FormData();
formData.append("file", file);
return request({
url: "/ccdi/file-upload/parse-id-card-file",
method: "post",
data: formData,
headers: {
"Content-Type": "multipart/form-data"
}
});
}
```
```javascript
export function pullBankInfo(data) {
return request({
url: "/ccdi/file-upload/pull-bank-info",
method: "post",
data
});
}
```
注意:把原来 `pullBankInfo(projectId)` 的签名改成 JSON 提交,不再走 `/ccdi/project/{projectId}/pull-bank-info` 占位接口。
**Step 4: Run build to verify it still only fails on missing dialog state**
Run: `cd ruoyi-ui; npm run build:prod`
Expected: FAIL only because `UploadData.vue` 还没有新增弹窗数据和模板绑定。
**Step 5: Commit**
```bash
git add ruoyi-ui/src/api/ccdiProjectUpload.js ruoyi-ui/src/views/ccdiProject/components/detail/UploadData.vue
git commit -m "补充拉取本行信息前端接口契约"
```
### Task 2: Build the modal shell and page state
**Files:**
- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/UploadData.vue`
**Step 1: Write the failing verification**
在模板里新增弹窗骨架,但先不实现方法:
- `el-dialog` 标题:`拉取本行信息`
- `el-input type="textarea"` 用于证件号码输入
- `el-upload` 用于身份证文件上传
- `el-date-picker type="daterange"` 用于时间跨度
- 底部按钮:`取消``确认拉取`
使用以下数据字段:
```javascript
pullBankInfoDialogVisible: false,
pullBankInfoLoading: false,
parsingIdCardFile: false,
idCardFileList: [],
pullBankInfoForm: {
idCardText: "",
dateRange: []
}
```
**Step 2: Run build to verify it fails**
Run: `cd ruoyi-ui; npm run build:prod`
Expected: FAIL because the template references `pullBankInfoDialogVisible`, `pullBankInfoForm`, and upload handlers that are not implemented yet.
**Step 3: Write minimal implementation**
`data()` 中补齐新状态,并实现基础弹窗方法:
```javascript
openPullBankInfoDialog() {
this.pullBankInfoDialogVisible = true;
}
```
```javascript
resetPullBankInfoForm() {
this.pullBankInfoForm = {
idCardText: "",
dateRange: []
};
this.idCardFileList = [];
this.parsingIdCardFile = false;
this.pullBankInfoLoading = false;
}
```
同时调整 `handleFetchBankInfo()` 改为:
```javascript
handleFetchBankInfo() {
this.resetPullBankInfoForm();
this.openPullBankInfoDialog();
}
```
**Step 4: Run build to verify it passes**
Run: `cd ruoyi-ui; npm run build:prod`
Expected: PASS
**Step 5: Commit**
```bash
git add ruoyi-ui/src/views/ccdiProject/components/detail/UploadData.vue
git commit -m "搭建拉取本行信息弹窗骨架"
```
### Task 3: Implement instant Excel parsing and textarea backfill
**Files:**
- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/UploadData.vue`
**Step 1: Write the failing verification**
把文件上传控件接到实际事件,但先不写实现:
```html
<el-upload
action="#"
:auto-upload="false"
:limit="1"
:file-list="idCardFileList"
:on-change="handleIdCardFileChange"
:on-remove="handleIdCardFileRemove">
</el-upload>
```
并在文件列表下面显示解析提示:
```html
<div v-if="parsingIdCardFile">正在解析身份证文件...</div>
```
**Step 2: Run build to verify it fails**
Run: `cd ruoyi-ui; npm run build:prod`
Expected: FAIL because `handleIdCardFileChange` and `handleIdCardFileRemove` do not exist yet.
**Step 3: Write minimal implementation**
实现 4 个前端辅助方法:
1. `parseIdCardText(text)`
```javascript
parseIdCardText(text) {
return Array.from(new Set(
(text || "")
.split(/[\n,]+/)
.map(item => item.trim())
.filter(Boolean)
));
}
```
2. `mergeIdCards(currentText, parsedIdCards)`
```javascript
mergeIdCards(currentText, parsedIdCards) {
const merged = [
...this.parseIdCardText(currentText),
...(parsedIdCards || [])
];
return Array.from(new Set(merged)).join(", ");
}
```
3. `handleIdCardFileChange(file, fileList)`
- 只保留一个文件
- 校验扩展名为 `.xls` / `.xlsx`
- 设置 `parsingIdCardFile = true`
- 调用 `parseIdCardFile(file.raw)`
- 成功后把返回的 `idCards` 合并回填到 `pullBankInfoForm.idCardText`
- 失败后提示错误并清空文件列表
4. `handleIdCardFileRemove()`
- 清空 `idCardFileList`
解析成功后的关键回填逻辑:
```javascript
this.pullBankInfoForm.idCardText = this.mergeIdCards(
this.pullBankInfoForm.idCardText,
res.data.idCards || []
);
```
**Step 4: Run build to verify it passes**
Run: `cd ruoyi-ui; npm run build:prod`
Expected: PASS
**Step 5: Commit**
```bash
git add ruoyi-ui/src/views/ccdiProject/components/detail/UploadData.vue
git commit -m "实现身份证文件自动解析与输入框回填"
```
### Task 4: Submit the final pull request and reuse the existing polling refresh flow
**Files:**
- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/UploadData.vue`
**Step 1: Write the failing verification**
先把“确认拉取”按钮接到真正的方法名,但先不写逻辑:
```html
<el-button type="primary" :loading="pullBankInfoLoading" @click="handleConfirmPullBankInfo">
确认拉取
</el-button>
```
**Step 2: Run build to verify it fails**
Run: `cd ruoyi-ui; npm run build:prod`
Expected: FAIL because `handleConfirmPullBankInfo` does not exist yet.
**Step 3: Write minimal implementation**
实现提交前的最终整理与校验:
1. `buildFinalIdCardList()`
```javascript
buildFinalIdCardList() {
return this.parseIdCardText(this.pullBankInfoForm.idCardText);
}
```
2. `handleConfirmPullBankInfo()`
- 校验证件号码非空
- 校验 `dateRange` 长度为 2
- 组装请求体:
```javascript
const [startDate, endDate] = this.pullBankInfoForm.dateRange || [];
const payload = {
projectId: this.projectId,
idCards: this.buildFinalIdCardList(),
startDate,
endDate
};
```
- 调用 `pullBankInfo(payload)`
- 成功后:
- 关闭弹窗
- 提示“拉取任务已提交”
- `await Promise.all([this.loadStatistics(), this.loadFileList()])`
- 若有 `uploading` / `parsing` 记录则执行 `this.startPolling()`
失败后:
- 保留弹窗内容
- 显示后端错误信息
**Step 4: Run build to verify it passes**
Run: `cd ruoyi-ui; npm run build:prod`
Expected: PASS
**Step 5: Commit**
```bash
git add ruoyi-ui/src/views/ccdiProject/components/detail/UploadData.vue
git commit -m "接通拉取本行信息提交流程与列表刷新"
```
### Task 5: Final verification and manual smoke-check
**Files:**
- Modify if needed after failures: `ruoyi-ui/src/views/ccdiProject/components/detail/UploadData.vue`
- Modify if needed after failures: `ruoyi-ui/src/api/ccdiProjectUpload.js`
**Step 1: Run production build**
Run: `cd ruoyi-ui; npm run build:prod`
Expected: BUILD SUCCESS
**Step 2: Manual smoke in the browser**
手工验证以下场景:
1. 打开项目详情页 `上传数据`
2. 点击“拉取本行信息”,确认弹窗打开
3. 手工输入两个身份证,确认文本框保留原值
4. 上传身份证 Excel确认自动解析并把去重后的身份证回填到文本框
5. 不选日期时点击“确认拉取”,确认拦截
6. 选择日期后提交,确认弹窗关闭、提示提交成功
7. 确认上传记录列表新增记录并进入 `上传中 / 解析中`
8. 确认已有轮询逻辑能自动刷新状态
**Step 3: Fix the smallest UI or data-binding issue**
优先排查:
- 日期控件 `value-format` 是否返回 `yyyy-MM-dd`
- 文件移除后是否错误保留旧文件列表
- 文本框合并去重后是否出现多余逗号或空白
- 提交成功后是否忘记重置弹窗状态
**Step 4: Run final build again**
Run: `cd ruoyi-ui; npm run build:prod`
Expected: BUILD SUCCESS
**Step 5: Commit**
```bash
git add ruoyi-ui/src/api/ccdiProjectUpload.js ruoyi-ui/src/views/ccdiProject/components/detail/UploadData.vue
git commit -m "完成拉取本行信息前端弹窗与自动解析"
```

View File

@@ -0,0 +1,579 @@
# 员工资产信息维护前端实施计划
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** 在员工信息维护页面中增加员工资产的新增、编辑、删除、详情展示、导入和失败记录查看能力,并保持现有员工导入交互不回归。
**Architecture:** 保持 `ccdiBaseStaff` 作为唯一页面入口,在现有员工新增、编辑、详情弹窗中扩展 `assetInfoList` 子表。资产导入复用当前员工导入的弹窗上传、异步轮询、localStorage 状态恢复、失败记录分页查看模型,但所有资产导入状态、文案、存储 key、轮询方法和失败记录弹窗都单独维护避免与员工导入互相污染。
**Tech Stack:** Vue 2, Element UI, 若依前端脚手架, Axios request 封装, `npm run build:prod`
---
### Task 1: 扩展前端 API 封装员工资产导入能力
**Files:**
- Modify: `ruoyi-ui/src/api/ccdiBaseStaff.js`
- Create: `ruoyi-ui/src/api/ccdiAssetInfo.js`
- Test: `ruoyi-ui/tests/unit/employee-asset-api-contract.test.js`
**Step 1: 先写失败校验脚本**
新增一个轻量级源码断言脚本,校验以下约束:
- `ccdiAssetInfo.js` 文件存在
- 资产导入 API 包含下载模板、提交导入、查询状态、查询失败记录
- 员工新增和编辑接口允许传递 `assetInfoList`
**Step 2: 运行校验确认失败**
Run:
```bash
node ruoyi-ui/tests/unit/employee-asset-api-contract.test.js
```
Expected: FAIL因为资产导入 API 文件尚不存在。
**Step 3: 编写最小 API 封装**
`ccdiAssetInfo.js` 中新增:
- `importAssetTemplate`
- `importAssetData`
- `getAssetImportStatus`
- `getAssetImportFailures`
接口路径统一使用:
- `/ccdi/assetInfo/importTemplate`
- `/ccdi/assetInfo/importData`
- `/ccdi/assetInfo/importStatus/{taskId}`
- `/ccdi/assetInfo/importFailures/{taskId}`
`ccdiBaseStaff.js` 继续保留员工增删改查,不把资产导入接口混入员工 API 文件。
**Step 4: 再次运行校验**
Run:
```bash
node ruoyi-ui/tests/unit/employee-asset-api-contract.test.js
```
Expected: PASS
**Step 5: 提交**
```bash
git add ruoyi-ui/src/api/ccdiBaseStaff.js ruoyi-ui/src/api/ccdiAssetInfo.js ruoyi-ui/tests/unit/employee-asset-api-contract.test.js
git commit -m "新增员工资产前端接口封装"
```
### Task 2: 扩展员工表单模型,支持聚合 `assetInfoList`
**Files:**
- Modify: `ruoyi-ui/src/views/ccdiBaseStaff/index.vue`
- Test: `ruoyi-ui/tests/unit/employee-asset-submit-flow.test.js`
**Step 1: 先写失败校验脚本**
校验以下行为:
- `reset()` 默认初始化 `assetInfoList: []`
- `handleAdd()` 打开新增弹窗时带空资产列表
- `handleUpdate()` 回显详情时保留接口返回的 `assetInfoList`
- `submitForm()` 提交前会附带 `assetInfoList`
**Step 2: 运行校验确认失败**
Run:
```bash
node ruoyi-ui/tests/unit/employee-asset-submit-flow.test.js
```
Expected: FAIL因为当前 `form` 里还没有资产字段。
**Step 3: 编写最小表单改造**
调整 `index.vue` 中以下逻辑:
- `form` 默认值增加 `assetInfoList: []`
- `reset()` 同步重置 `assetInfoList`
- `handleAdd()` 明确初始化空数组
- `handleUpdate()``handleDetail()` 对返回值中的 `assetInfoList` 做空值兜底
- 新增 `normalizeAssetInfoList()`,提交前过滤全空行
注意:
- 前端不传 `familyId`
- 前端保留用户输入的 `personId`
- 不在前端生成 `assetId`
**Step 4: 再次运行校验**
Run:
```bash
node ruoyi-ui/tests/unit/employee-asset-submit-flow.test.js
```
Expected: PASS
**Step 5: 提交**
```bash
git add ruoyi-ui/src/views/ccdiBaseStaff/index.vue ruoyi-ui/tests/unit/employee-asset-submit-flow.test.js
git commit -m "扩展员工表单资产聚合字段"
```
### Task 3: 在新增和编辑弹窗中加入“资产信息”可编辑子表
**Files:**
- Modify: `ruoyi-ui/src/views/ccdiBaseStaff/index.vue`
- Test: `ruoyi-ui/tests/unit/employee-asset-maintenance-layout.test.js`
**Step 1: 先写失败校验脚本**
断言页面模板出现以下结构:
- “资产信息”分区标题
- “新增资产”按钮
- 资产子表或空状态容器
- 资产实际持有人身份证号输入框
- 每行删除按钮
**Step 2: 运行校验确认失败**
Run:
```bash
node ruoyi-ui/tests/unit/employee-asset-maintenance-layout.test.js
```
Expected: FAIL因为当前员工弹窗只有基本信息。
**Step 3: 编写最小弹窗结构**
在基本信息区域下方新增“资产信息”分区,并增加以下辅助方法:
- `createEmptyAssetRow()`
- `handleAddAsset()`
- `handleRemoveAsset(index)`
- `hasAssetContent(row)`
子表字段包含:
- `personId`
- `assetMainType`
- `assetSubType`
- `assetName`
- `ownershipRatio`
- `purchaseEvalDate`
- `originalValue`
- `currentValue`
- `valuationDate`
- `assetStatus`
- `remarks`
- `operation`
交互要求:
- 允许多行新增
- 每行支持删除
- 空列表时显示“暂无资产信息,请点击新增资产”
**Step 4: 再次运行校验**
Run:
```bash
node ruoyi-ui/tests/unit/employee-asset-maintenance-layout.test.js
```
Expected: PASS
**Step 5: 提交**
```bash
git add ruoyi-ui/src/views/ccdiBaseStaff/index.vue ruoyi-ui/tests/unit/employee-asset-maintenance-layout.test.js
git commit -m "新增员工资产编辑子表"
```
### Task 4: 为资产子表补充前端校验与填写提示
**Files:**
- Modify: `ruoyi-ui/src/views/ccdiBaseStaff/index.vue`
**Step 1: 增加资产行字段校验**
至少校验以下前端规则:
- `personId` 格式为合法身份证号
- `assetMainType``assetSubType``assetName``currentValue``assetStatus` 为必填
- 金额字段允许为空,但填写时必须是合法数字
- 日期字段使用 `value-format="yyyy-MM-dd"`
**Step 2: 增加交互提示**
在资产分区标题或字段提示中明确说明:
- `personId` 表示资产实际持有人身份证号
- 如果 `personId` 等于当前员工身份证号,则视为员工本人资产
- 如果 `personId` 不等于当前员工身份证号,则视为员工亲属资产
**Step 3: 保持前端不做归属推导**
不要在前端根据 `personId` 生成 `familyId`,该逻辑完全交给后端。
**Step 4: 构建验证**
Run:
```bash
cd ruoyi-ui
npm run build:prod
```
Expected: 构建通过,无模板语法错误。
**Step 5: 提交**
```bash
git add ruoyi-ui/src/views/ccdiBaseStaff/index.vue
git commit -m "补充员工资产表单校验与提示"
```
### Task 5: 在详情弹窗中展示员工全部资产信息
**Files:**
- Modify: `ruoyi-ui/src/views/ccdiBaseStaff/index.vue`
- Test: `ruoyi-ui/tests/unit/employee-asset-maintenance-layout.test.js`
**Step 1: 扩展详情弹窗布局**
在现有“基本信息”卡片下方新增“资产信息”区块。
**Step 2: 增加只读资产表格**
展示字段:
- `personId`
- `ownerType`
- `assetMainType`
- `assetSubType`
- `assetName`
- `ownershipRatio`
- `currentValue`
- `assetStatus`
- `remarks`
其中 `ownerType` 可在前端根据 `personId === employeeDetail.idCard` 计算展示“本人”或“亲属”。
**Step 3: 增加空状态**
`employeeDetail.assetInfoList` 为空时显示“暂无资产信息”。
**Step 4: 回归校验**
再次确认原有基本信息展示不受影响:
- 姓名
- 柜员号
- 所属部门
- 身份证号
- 电话
- 入职时间
- 状态
**Step 5: 提交**
```bash
git add ruoyi-ui/src/views/ccdiBaseStaff/index.vue ruoyi-ui/tests/unit/employee-asset-maintenance-layout.test.js
git commit -m "新增员工资产详情展示"
```
### Task 6: 增加独立的资产导入入口、弹窗和状态模型
**Files:**
- Modify: `ruoyi-ui/src/views/ccdiBaseStaff/index.vue`
- Test: `ruoyi-ui/tests/unit/employee-asset-import-ui.test.js`
**Step 1: 先写失败校验脚本**
校验页面存在以下独立状态:
- 资产导入按钮“导入资产信息”
- 资产失败记录按钮“查看员工资产导入失败记录”
- 独立 `assetUpload` 弹窗对象
- 独立 `assetPollingTimer`
- 独立 `assetCurrentTaskId`
- 独立 `assetFailureDialogVisible`
- 独立 localStorage key
**Step 2: 运行校验确认失败**
Run:
```bash
node ruoyi-ui/tests/unit/employee-asset-import-ui.test.js
```
Expected: FAIL因为当前页面只有员工导入状态。
**Step 3: 编写最小导入 UI**
在按钮区新增:
- “导入资产信息”
- “查看员工资产导入失败记录”
`data()` 中新增一套独立状态,例如:
- `assetUpload`
- `assetImportResultVisible`
- `assetImportResultContent`
- `assetPollingTimer`
- `assetShowFailureButton`
- `assetCurrentTaskId`
- `assetFailureDialogVisible`
- `assetFailureList`
- `assetFailureLoading`
- `assetFailureTotal`
- `assetFailureQueryParams`
**Step 4: 新增资产导入弹窗**
复用员工导入交互结构,但文案调整为:
- 标题:`员工资产数据导入`
- 模板下载按钮:下载员工资产模板
- 提示文案:系统将根据 `personId/person_id` 自动识别归属员工
**Step 5: 再次运行校验**
Run:
```bash
node ruoyi-ui/tests/unit/employee-asset-import-ui.test.js
```
Expected: PASS
**Step 6: 提交**
```bash
git add ruoyi-ui/src/views/ccdiBaseStaff/index.vue ruoyi-ui/tests/unit/employee-asset-import-ui.test.js
git commit -m "新增员工资产导入交互状态"
```
### Task 7: 接通资产导入上传、轮询、状态恢复和失败记录查询
**Files:**
- Modify: `ruoyi-ui/src/views/ccdiBaseStaff/index.vue`
- Check: `ruoyi-ui/src/api/ccdiAssetInfo.js`
**Step 1: 增加资产导入方法**
至少实现以下方法:
- `handleAssetImport()`
- `handleAssetImportDialogClose()`
- `importAssetTemplate()`
- `handleAssetFileUploadProgress()`
- `handleAssetFileSuccess()`
- `startAssetImportStatusPolling(taskId)`
- `handleAssetImportComplete(statusResult)`
- `viewAssetImportFailures()`
- `getAssetFailureList()`
**Step 2: 增加独立 localStorage 管理**
新增一组与员工导入隔离的方法:
- `saveAssetImportTaskToStorage()`
- `getAssetImportTaskFromStorage()`
- `clearAssetImportTaskFromStorage()`
- `restoreAssetImportState()`
- `getLastAssetImportTooltip()`
- `clearAssetImportHistory()`
建议 key 使用:
```text
employee_asset_import_last_task
```
**Step 3: 严格区分员工导入与资产导入**
保持现有员工导入逻辑不回归:
- 员工导入仍使用 `employee_import_last_task`
- 员工失败记录仍显示“查看导入失败记录”
- 资产失败记录按钮固定显示“查看员工资产导入失败记录”
**Step 4: 处理资产导入完成通知**
通知文案应显式区分:
- `员工资产导入任务已提交`
- `员工资产导入完成`
- `成功 X 条,失败 Y 条`
**Step 5: 接通失败记录弹窗**
弹窗标题为:
```text
员工资产导入失败记录
```
表格字段展示:
- `familyId`
- `personId`
- `assetMainType`
- `assetSubType`
- `assetName`
- `errorMessage`
若后端失败记录不返回 `familyId`,前端允许显示为空,不自行推导。
**Step 6: 构建验证**
Run:
```bash
cd ruoyi-ui
npm run build:prod
```
Expected: 构建通过,资产导入方法和模板绑定完整。
**Step 7: 提交**
```bash
git add ruoyi-ui/src/views/ccdiBaseStaff/index.vue ruoyi-ui/src/api/ccdiAssetInfo.js
git commit -m "接通员工资产导入轮询与失败记录"
```
### Task 8: 调整样式与可读性,避免资产子表破坏现有弹窗布局
**Files:**
- Modify: `ruoyi-ui/src/views/ccdiBaseStaff/index.vue`
**Step 1: 调整编辑弹窗样式**
补充以下样式:
- 资产分区标题
- 资产表格容器
- 空状态
- 表头提示文案
- 详情页资产区块
**Step 2: 保持桌面端可读性**
检查 `1200px` 编辑弹窗在加入资产表后是否仍可读,必要时:
- 增加横向滚动容器
- 调整列宽
- 对备注列使用 `show-overflow-tooltip`
**Step 3: 检查窄屏降级**
至少保证:
- 弹窗内部不会内容挤压到不可操作
- 资产操作列始终可点击
- 详情表格可以横向滚动
**Step 4: 构建验证**
Run:
```bash
cd ruoyi-ui
npm run build:prod
```
Expected: 样式改动不影响构建。
**Step 5: 提交**
```bash
git add ruoyi-ui/src/views/ccdiBaseStaff/index.vue
git commit -m "优化员工资产页面样式与布局"
```
### Task 9: 执行前端联调与最终验证
**Files:**
- Check: `ruoyi-ui/src/views/ccdiBaseStaff/index.vue`
- Check: `ruoyi-ui/src/api/ccdiBaseStaff.js`
- Check: `ruoyi-ui/src/api/ccdiAssetInfo.js`
- Check: `ruoyi-ui/tests/unit/*.test.js`
**Step 1: 运行源码断言脚本**
Run:
```bash
node ruoyi-ui/tests/unit/employee-asset-api-contract.test.js
node ruoyi-ui/tests/unit/employee-asset-maintenance-layout.test.js
node ruoyi-ui/tests/unit/employee-asset-submit-flow.test.js
node ruoyi-ui/tests/unit/employee-asset-import-ui.test.js
```
Expected: 全部 PASS
**Step 2: 运行生产构建**
Run:
```bash
cd ruoyi-ui
npm run build:prod
```
Expected: 构建成功
**Step 3: 启动本地前端联调**
Run:
```bash
cd ruoyi-ui
npm run dev
```
Expected: 本地员工信息维护页面可访问
**Step 4: 手工验证关键场景**
- 新增员工时添加 1 条本人资产并保存
- 新增员工时添加 1 条亲属资产并保存
- 编辑员工时增加、修改、删除资产行
- 打开员工详情时查看资产信息区
- 执行员工资产导入并检查轮询通知
- 导入失败后打开“查看员工资产导入失败记录”
- 刷新页面后验证资产导入失败记录按钮可恢复
- 清除资产导入历史后验证按钮消失
- 回归验证原员工导入功能未受影响
**Step 5: 整理问题并做最小修复**
优先排查:
- `assetInfoList` 回显为空数组时模板报错
- 资产日期字段格式不一致
- 两套导入轮询定时器互相覆盖
- localStorage key 混用导致员工和资产失败记录串数据
**Step 6: 最终提交**
```bash
git add ruoyi-ui/src/api/ccdiBaseStaff.js ruoyi-ui/src/api/ccdiAssetInfo.js ruoyi-ui/src/views/ccdiBaseStaff/index.vue ruoyi-ui/tests/unit docs/plans/frontend/2026-03-12-employee-asset-maintenance-frontend-implementation.md
git commit -m "新增员工资产信息前端实施计划"
```

View File

@@ -0,0 +1,111 @@
# Pull Bank Info Date Limit Frontend Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** Restrict the pull-bank-info date range so users can only select dates up to yesterday and block invalid submissions.
**Architecture:** Keep the existing dialog and request flow in `UploadData.vue`. Add one focused source-level regression test that asserts the date picker has an explicit restriction hook, then implement a shared “yesterday” boundary for both Element UI `disabledDate` behavior and submit-time validation.
**Tech Stack:** Vue 2, Element UI 2, scoped SFC logic, Node-based source assertions in `ruoyi-ui/tests/unit`
---
### Task 1: Add a regression test for the date restriction hook
**Files:**
- Create: `ruoyi-ui/tests/unit/upload-data-pull-bank-info-date-limit.test.js`
- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/UploadData.vue`
**Step 1: Write the failing test**
Create a source-based test that verifies the pull-bank-info date picker:
- uses `picker-options`
- binds to a dedicated `pullBankInfoDatePickerOptions`
- the script contains a helper for calculating the latest allowed date
**Step 2: Run test to verify it fails**
Run:
```bash
node ruoyi-ui/tests/unit/upload-data-pull-bank-info-date-limit.test.js
```
Expected: FAIL because the current component does not define the new date limit hook yet.
### Task 2: Implement date picker restrictions
**Files:**
- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/UploadData.vue`
- Test: `ruoyi-ui/tests/unit/upload-data-pull-bank-info-date-limit.test.js`
**Step 1: Add the date picker option**
Bind the pull-bank-info date range picker to `pullBankInfoDatePickerOptions`.
**Step 2: Add the latest allowed date helper**
Add a method or computed-backed helper that returns yesterday based on the browser local date.
**Step 3: Disable today and future dates**
Implement `disabledDate` so dates greater than or equal to today 00:00 are disabled.
**Step 4: Run the new test**
Run:
```bash
node ruoyi-ui/tests/unit/upload-data-pull-bank-info-date-limit.test.js
```
Expected: PASS
### Task 3: Add submit-time fallback validation
**Files:**
- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/UploadData.vue`
- Test: `ruoyi-ui/tests/unit/upload-data-pull-bank-info-date-limit.test.js`
**Step 1: Reuse the same date boundary in submit logic**
Before calling `pullBankInfo(payload)`, verify both selected dates are not later than yesterday.
**Step 2: Show a clear warning**
If validation fails, warn the user with a concise message such as `时间跨度最晚只能选择到昨天`.
**Step 3: Keep existing validations intact**
Retain the current empty-ID-card and incomplete-date-range checks.
### Task 4: Run regression verification
**Files:**
- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/UploadData.vue`
**Step 1: Run focused tests**
Run:
```bash
node ruoyi-ui/tests/unit/upload-data-pull-bank-info-date-limit.test.js
node ruoyi-ui/tests/unit/upload-data-pull-bank-info-dialog-layout.test.js
node ruoyi-ui/tests/unit/upload-data-batch-upload.test.js
node ruoyi-ui/tests/unit/upload-data-file-list-settings.test.js
```
Expected: all tests pass.
**Step 2: Run build verification**
Run:
```bash
npm run build:prod
```
Workdir: `ruoyi-ui`
Expected: build succeeds without introducing Vue template or script errors.

View File

@@ -0,0 +1,113 @@
# Pull Bank Info Upload Button Hit Area Frontend Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** Make the pull-bank-info dialog file selector hit area match the visible trigger and improve the field layout without changing upload behavior.
**Architecture:** Keep the existing `el-upload` parsing flow and API calls, but replace the current loose inline upload layout with a dedicated upload panel inside the dialog. Add a focused unit test that asserts the new dialog structure and class hooks, then update scoped styles so the button-style uploader no longer inherits the full-width drag-upload hit area behavior.
**Tech Stack:** Vue 2, Element UI 2, scoped SCSS, Node-based source assertions in `ruoyi-ui/tests/unit`
---
### Task 1: Add a regression test for the dialog structure
**Files:**
- Create: `ruoyi-ui/tests/unit/upload-data-pull-bank-info-dialog-layout.test.js`
- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/UploadData.vue`
**Step 1: Write the failing test**
Add a source-based unit test that checks all of the following in the pull-bank-info dialog:
- the dialog contains a dedicated `pull-bank-info-form` container
- the file import area uses a `pull-bank-file-panel`
- the upload trigger uses a `pull-bank-file-upload`
- the template includes a visible selected-file summary block
**Step 2: Run test to verify it fails**
Run:
```bash
node ruoyi-ui/tests/unit/upload-data-pull-bank-info-dialog-layout.test.js
```
Expected: FAIL because the current dialog does not contain the new structure/classes.
### Task 2: Restructure the dialog template with minimal logic changes
**Files:**
- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/UploadData.vue`
- Test: `ruoyi-ui/tests/unit/upload-data-pull-bank-info-dialog-layout.test.js`
**Step 1: Update the dialog markup**
Keep the existing fields and event handlers, but:
- wrap the dialog form with `pull-bank-info-form`
- split the dialog into clearer sections
- move the upload button and helper text into `pull-bank-file-panel`
- add a selected-file summary row bound to `idCardFileList`
- keep `handleIdCardFileChange`, `handleIdCardFileRemove`, and `parsingIdCardFile` intact
**Step 2: Run the new test**
Run:
```bash
node ruoyi-ui/tests/unit/upload-data-pull-bank-info-dialog-layout.test.js
```
Expected: PASS
### Task 3: Adjust scoped styles so hit area and layout align
**Files:**
- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/UploadData.vue`
- Test: `ruoyi-ui/tests/unit/upload-data-pull-bank-info-dialog-layout.test.js`
**Step 1: Add focused dialog styles**
Add SCSS for:
- `pull-bank-info-form`
- `pull-bank-file-panel`
- `pull-bank-file-upload`
- `selected-id-card-file`
- `pull-bank-range-picker`
Use these styles to make the trigger area content-sized instead of full-row clickable, and improve spacing/alignment for desktop and mobile.
**Step 2: Keep drag-upload dialogs unchanged**
Retain the existing full-width dragger behavior for `upload-area` and `batch-upload-area`.
**Step 3: Run regression tests**
Run:
```bash
node ruoyi-ui/tests/unit/upload-data-pull-bank-info-dialog-layout.test.js
node ruoyi-ui/tests/unit/upload-data-batch-upload.test.js
node ruoyi-ui/tests/unit/upload-data-file-list-settings.test.js
```
Expected: all tests pass.
### Task 4: Run final verification
**Files:**
- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/UploadData.vue`
**Step 1: Run production build verification**
Run:
```bash
npm run build:prod
```
Workdir: `ruoyi-ui`
Expected: build succeeds without introducing Vue template or style compilation errors.

View File

@@ -0,0 +1,314 @@
# 员工亲属资产维护前端实施计划
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** 在员工亲属关系维护页面中增加亲属资产的可视化维护、详情展示和资产导入交互能力。
**Architecture:** 保持 `ccdiStaffFmyRelation` 为唯一页面入口,在现有新增、编辑、详情弹窗中扩展亲属资产子表,并新增独立的亲属资产导入状态、弹窗和失败记录视图。资产归属字段不在前端暴露,提交时仅上传资产业务字段列表。
**Tech Stack:** Vue 2, Element UI, 若依前端脚手架, Axios request 封装
---
### Task 1: 扩展前端 API 封装资产导入与聚合保存字段
**Files:**
- Modify: `D:\ccdi\ccdi\ruoyi-ui\src\api\ccdiStaffFmyRelation.js`
- Create: `D:\ccdi\ccdi\ruoyi-ui\src\api\ccdiAssetInfo.js`
**Step 1: 保持亲属关系 API 支持传递 `assetInfoList`**
- `addRelation`
- `updateRelation`
- `getRelation`
**Step 2: 新增资产导入 API 文件**
- 下载模板
- 提交导入
- 查询状态
- 查询失败记录
**Step 3: 自查接口路径**
- 使用 `/ccdi/assetInfo/*`
- 与现有 `staffFmyRelation` API 区分清楚
**Step 4: 运行构建检查**
Run: `npm run build:prod`
Expected: API 引用路径无语法错误
**Step 5: 提交**
```bash
git add ruoyi-ui/src/api/ccdiStaffFmyRelation.js ruoyi-ui/src/api/ccdiAssetInfo.js
git commit -m "新增亲属资产前端接口封装"
```
### Task 2: 扩展亲属关系表单模型与重置逻辑
**Files:**
- Modify: `D:\ccdi\ccdi\ruoyi-ui\src\views\ccdiStaffFmyRelation\index.vue`
**Step 1: 为 `form` 增加 `assetInfoList`**
- 默认值设为空数组
- 重置时同步清空
**Step 2: 处理详情与编辑回显**
- `getRelation` 返回后回填 `assetInfoList`
- 保证空值时回退为 `[]`
**Step 3: 在提交前过滤空资产行**
- 仅保留有实际内容的资产
- 不在前端传 `familyId``personId`
**Step 4: 本地验证**
Run: `npm run build:prod`
Expected: 页面脚本编译通过
**Step 5: 提交**
```bash
git add ruoyi-ui/src/views/ccdiStaffFmyRelation/index.vue
git commit -m "扩展亲属关系表单资产模型"
```
### Task 3: 在新增/编辑弹窗中加入亲属资产子表
**Files:**
- Modify: `D:\ccdi\ccdi\ruoyi-ui\src\views\ccdiStaffFmyRelation\index.vue`
**Step 1: 新增“亲属资产信息”分区**
- 放在基本信息下方
- 右侧增加“新增资产”按钮
**Step 2: 渲染可编辑子表**
- 字段包含资产大类、资产小类、资产名称、产权占比、购买/评估日期、资产原值、当前估值、估值截止日期、资产状态、备注、操作
**Step 3: 支持资产行增删**
- 新增空白行
- 删除当前行
**Step 4: 对新增资产按钮加前置限制**
- 新增模式下未填写 `relationCertType``relationCertNo` 时禁用
- 提示“请先填写关系人证件信息”
**Step 5: 构建验证**
Run: `npm run build:prod`
Expected: 模板与脚本通过编译
**Step 6: 提交**
```bash
git add ruoyi-ui/src/views/ccdiStaffFmyRelation/index.vue
git commit -m "新增亲属资产编辑子表"
```
### Task 4: 锁定编辑场景下的亲属证件字段
**Files:**
- Modify: `D:\ccdi\ccdi\ruoyi-ui\src\views\ccdiStaffFmyRelation\index.vue`
**Step 1: 将 `relationCertType` 在编辑场景下置灰**
- `:disabled="!isAdd"`
**Step 2: 将 `relationCertNo` 在编辑场景下置灰**
- `:disabled="!isAdd"`
**Step 3: 核对员工身份证号选择器**
- 保持当前新增可选、编辑禁改逻辑
**Step 4: 构建验证**
Run: `npm run build:prod`
Expected: 字段禁用逻辑无模板错误
**Step 5: 提交**
```bash
git add ruoyi-ui/src/views/ccdiStaffFmyRelation/index.vue
git commit -m "锁定亲属证件字段编辑能力"
```
### Task 5: 在详情弹窗展示亲属资产信息
**Files:**
- Modify: `D:\ccdi\ccdi\ruoyi-ui\src\views\ccdiStaffFmyRelation\index.vue`
**Step 1: 增加详情分区**
- 标题为“亲属资产信息”
**Step 2: 使用只读表格展示资产**
- 展示关键资产字段
- 不展示归属键和主键
**Step 3: 增加空状态**
- 无资产时显示“暂无亲属资产信息”
**Step 4: 构建验证**
Run: `npm run build:prod`
Expected: 详情模板渲染通过
**Step 5: 提交**
```bash
git add ruoyi-ui/src/views/ccdiStaffFmyRelation/index.vue
git commit -m "新增亲属资产详情展示"
```
### Task 6: 新增亲属资产导入入口与状态管理
**Files:**
- Modify: `D:\ccdi\ccdi\ruoyi-ui\src\views\ccdiStaffFmyRelation\index.vue`
**Step 1: 在按钮区新增入口**
- “导入亲属资产信息”
- “查看亲属资产导入失败记录”
**Step 2: 新增独立上传弹窗状态**
- `assetUpload`
- `assetImportPollingTimer`
- `assetCurrentTaskId`
**Step 3: 新增独立 localStorage key**
- 例如 `staff_fmy_asset_import_last_task`
**Step 4: 新增资产导入失败记录弹窗与分页状态**
- 与现有亲属关系导入分开维护
**Step 5: 构建验证**
Run: `npm run build:prod`
Expected: 导入状态变量与模板绑定正常
**Step 6: 提交**
```bash
git add ruoyi-ui/src/views/ccdiStaffFmyRelation/index.vue
git commit -m "新增亲属资产导入交互状态"
```
### Task 7: 接通资产导入上传、轮询与失败记录查询
**Files:**
- Modify: `D:\ccdi\ccdi\ruoyi-ui\src\views\ccdiStaffFmyRelation\index.vue`
- Check: `D:\ccdi\ccdi\ruoyi-ui\src\api\ccdiAssetInfo.js`
**Step 1: 实现模板下载**
- 调用资产导入模板接口
**Step 2: 实现上传成功回调**
- 校验返回的 `taskId`
- 保存任务状态
- 启动轮询
**Step 3: 实现轮询完成处理**
- 根据成功/失败数展示通知
- 刷新页面列表
- 控制失败记录按钮显示
**Step 4: 实现失败记录查询**
- 独立调用资产失败记录接口
- 处理记录过期和网络错误提示
**Step 5: 构建验证**
Run: `npm run build:prod`
Expected: 资产导入流程相关方法通过编译
**Step 6: 提交**
```bash
git add ruoyi-ui/src/views/ccdiStaffFmyRelation/index.vue
git commit -m "接通亲属资产导入轮询与失败记录"
```
### Task 8: 调整样式与交互可读性
**Files:**
- Modify: `D:\ccdi\ccdi\ruoyi-ui\src\views\ccdiStaffFmyRelation\index.vue`
**Step 1: 为资产分区补样式**
- 表格区间距
- 空状态样式
- 分区标题样式
**Step 2: 检查弹窗宽度**
- 保证新增/编辑/详情在资产表加入后仍可读
**Step 3: 检查移动端/窄屏降级**
- 避免列宽完全挤压
**Step 4: 构建验证**
Run: `npm run build:prod`
Expected: 样式变更不影响构建
**Step 5: 提交**
```bash
git add ruoyi-ui/src/views/ccdiStaffFmyRelation/index.vue
git commit -m "优化亲属资产页面样式与布局"
```
### Task 9: 执行前端联调验证
**Files:**
- Check: `D:\ccdi\ccdi\ruoyi-ui\src\views\ccdiStaffFmyRelation\index.vue`
**Step 1: 运行生产构建**
Run: `cd ruoyi-ui && npm run build:prod`
Expected: 构建成功
**Step 2: 启动前端开发环境**
Run: `cd ruoyi-ui && npm run dev`
Expected: 本地页面可访问
**Step 3: 手工验证页面场景**
- 新增亲属关系并添加资产
- 编辑亲属关系并维护资产
- 验证证件类型、证件号码禁改
- 查看详情资产区
- 触发亲属资产导入和失败记录查看
**Step 4: 整理验证结果**
- 记录已验证场景和遗留问题
**Step 5: 提交**
```bash
git add .
git commit -m "完成亲属资产前端联调验证"
```

View File

@@ -0,0 +1,89 @@
# CCDI Docker 前端部署 Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** 为 Vue 前端建立可打包、可容器化、可上传并在服务器通过 Nginx 对外提供服务的 Docker 部署链路。
**Architecture:** 前端继续使用现有 `npm run build:prod` 产出 `dist`,容器内由 Nginx 提供静态资源与反向代理。通过 `/prod-api``/v3/api-docs` 将请求转发到后端容器,保持现有业务代码与生产环境变量不变。
**Tech Stack:** Vue 2, npm, Nginx, Docker Compose, PowerShell, Paramiko
---
### Task 1: 定义前端容器与 Nginx 代理
**Files:**
- Create: `docker/frontend/Dockerfile`
- Create: `docker/frontend/nginx.conf`
- Modify: `docs/design/2026-03-13-ccdi-docker-deployment-design.md`
**Step 1: 创建前端镜像定义**
- 基于 `nginx:stable-alpine`
- 复制 `frontend/dist` 到 Nginx 静态目录
- 复制自定义 `nginx.conf`
**Step 2: 配置反向代理**
- `/` 返回前端 `index.html`
- `/prod-api/` 代理到 `http://backend:8080/`
- `/v3/api-docs/` 代理到 `http://backend:8080/v3/api-docs/`
**Step 3: 校验 Nginx 配置**
Run: `docker run --rm -v ${PWD}/docker/frontend/nginx.conf:/etc/nginx/conf.d/default.conf:ro nginx:stable-alpine nginx -t`
Expected: syntax is ok
### Task 2: 编写前端打包收集流程
**Files:**
- Modify: `deploy/deploy.ps1`
- Create: `frontend/.gitkeep`
**Step 1: 构建前端**
Run: `npm --prefix ruoyi-ui run build:prod`
Expected: `ruoyi-ui/dist` 生成成功
**Step 2: 收集部署目录**
-`ruoyi-ui/dist` 复制到 `frontend/dist`
- 保持部署目录与 Dockerfile 输入一致
### Task 3: 将前端加入 Compose
**Files:**
- Modify: `docker-compose.yml`
- Modify: `.env.example`
**Step 1: 定义 `frontend` 服务**
- 暴露 `62319:80`
- 依赖 `backend`
**Step 2: 校验 Compose**
Run: `docker compose config`
Expected: 前端服务、依赖与端口映射正确
### Task 4: 联调验证
**Files:**
- Modify: `docs/design/2026-03-13-ccdi-docker-deployment-design.md`
**Step 1: 检查前端生产产物**
- 验证 `dist/index.html``static/` 文件生成
**Step 2: 远端验证访问**
- 验证 `http://116.62.17.81:62319`
- 登录后检查浏览器请求是否发往 `/prod-api`
- 验证 Swagger 页面可通过前端入口转发访问
**Step 3: 提交**
```bash
git add docker/frontend deploy/deploy.ps1 docker-compose.yml .env.example docs/design/2026-03-13-ccdi-docker-deployment-design.md docs/plans/backend/2026-03-13-ccdi-docker-deployment-backend-implementation.md docs/plans/frontend/2026-03-13-ccdi-docker-deployment-frontend-implementation.md
git commit -m "新增Docker前端部署方案"
```

View File

@@ -0,0 +1,68 @@
# Deploy To NAS Frontend Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** 新增一个 Windows 下可双击执行的 `.bat` 一键入口,默认触发前后端打包并部署到 NAS。
**Architecture:** 通过 `deploy-to-nas.bat` 作为薄封装入口,把默认参数与可选覆盖参数转交给现有 `deploy.ps1`。BAT 只负责入口体验,不承载核心部署逻辑。
**Tech Stack:** Windows CMD, PowerShell, pytest
---
### Task 1: 新增 BAT 入口脚本
**Files:**
- Create: `deploy/deploy-to-nas.bat`
- Test: `tests/deploy/test_deploy_to_nas.py`
**Step 1: 写失败测试**
```python
def test_bat_dry_run_uses_default_nas_target():
...
```
**Step 2: 运行测试确认失败**
Run: `py -3.12 -m pytest tests/deploy/test_deploy_to_nas.py::test_bat_dry_run_uses_default_nas_target -q`
Expected: 失败,因为 BAT 文件不存在
**Step 3: 最小实现**
- 新建 BAT 文件
- 默认调用 `deploy.ps1`
- 支持 `--dry-run`
**Step 4: 运行测试**
Run: `py -3.12 -m pytest tests/deploy/test_deploy_to_nas.py::test_bat_dry_run_uses_default_nas_target -q`
Expected: 通过
### Task 2: 支持参数覆盖
**Files:**
- Modify: `deploy/deploy-to-nas.bat`
- Test: `tests/deploy/test_deploy_to_nas.py`
**Step 1: 写失败测试**
```python
def test_bat_dry_run_accepts_override_arguments():
...
```
**Step 2: 运行测试确认失败**
Run: `py -3.12 -m pytest tests/deploy/test_deploy_to_nas.py::test_bat_dry_run_accepts_override_arguments -q`
Expected: 失败,因为 BAT 未透传覆盖参数
**Step 3: 最小实现**
- 按位置参数传递 host、port、username、password、remoteRoot
-`--dry-run` 透传给 PowerShell
**Step 4: 全量测试**
Run: `py -3.12 -m pytest tests/deploy/test_deploy_to_nas.py -q`
Expected: 通过

View File

@@ -0,0 +1,230 @@
# 员工资产导入与亲属资产导入拆分前端实施计划
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** 将员工页与亲属页的资产导入前端交互彻底拆开,确保员工页只走员工资产导入接口,亲属页只走亲属资产导入接口。
**Architecture:** 保留亲属页现有资产导入状态管理,新增员工资产专用 API 封装并改造员工页上传地址、模板下载、状态轮询与失败记录查询。两边继续复用现有异步导入交互样式,但数据源和文案完全隔离。
**Tech Stack:** Vue 2, Element UI, Axios request wrapper, Node 静态契约测试
---
### Task 1: 固化前端拆分契约测试
**Files:**
- Modify: `ruoyi-ui/tests/unit/employee-asset-api-contract.test.js`
- Modify: `ruoyi-ui/tests/unit/employee-asset-import-ui.test.js`
- Modify: `ruoyi-ui/tests/unit/staff-family-asset-api-contract.test.js`
- Modify: `ruoyi-ui/tests/unit/staff-family-asset-detail-import-ui.test.js`
**Step 1: 写员工页失败测试**
- 断言员工页不再引用 `/ccdi/assetInfo/importData`
- 断言员工页引用新的员工资产 API 文件或路由
- 断言模板文案为“员工资产模板”
**Step 2: 运行员工页静态测试确认失败**
Run:
```bash
node tests/unit/employee-asset-api-contract.test.js
node tests/unit/employee-asset-import-ui.test.js
```
Expected:
- 至少一个断言失败
- 失败原因是员工页仍指向旧接口
**Step 3: 写亲属页保护性测试**
- 断言亲属页继续使用 `/ccdi/assetInfo/*`
- 断言亲属页模板文案仍为“亲属资产”
**Step 4: 运行亲属页测试确认当前仍通过**
Run:
```bash
node tests/unit/staff-family-asset-api-contract.test.js
node tests/unit/staff-family-asset-detail-import-ui.test.js
```
Expected:
- 现有亲属页测试通过
**Step 5: 提交**
```bash
git add ruoyi-ui/tests/unit/employee-asset-api-contract.test.js ruoyi-ui/tests/unit/employee-asset-import-ui.test.js ruoyi-ui/tests/unit/staff-family-asset-api-contract.test.js ruoyi-ui/tests/unit/staff-family-asset-detail-import-ui.test.js
git commit -m "补充资产导入拆分前端失败测试"
```
### Task 2: 新增员工资产导入 API 封装
**Files:**
- Create: `ruoyi-ui/src/api/ccdiBaseStaffAsset.js`
- Modify: `ruoyi-ui/tests/unit/employee-asset-api-contract.test.js`
**Step 1: 写最小 API 文件**
导出以下方法:
```javascript
export function importBaseStaffAssetTemplate() {}
export function importBaseStaffAssetData(data) {}
export function getBaseStaffAssetImportStatus(taskId) {}
export function getBaseStaffAssetImportFailures(taskId, pageNum, pageSize) {}
```
**Step 2: 接入员工专用路由**
- `/ccdi/baseStaff/asset/importTemplate`
- `/ccdi/baseStaff/asset/importData`
- `/ccdi/baseStaff/asset/importStatus/`
- `/ccdi/baseStaff/asset/importFailures/`
**Step 3: 运行员工资产 API 静态测试**
Run:
```bash
node tests/unit/employee-asset-api-contract.test.js
```
Expected:
- API 契约测试通过
**Step 4: 提交**
```bash
git add ruoyi-ui/src/api/ccdiBaseStaffAsset.js ruoyi-ui/tests/unit/employee-asset-api-contract.test.js
git commit -m "新增员工资产导入前端接口"
```
### Task 3: 改造员工页导入交互
**Files:**
- Modify: `ruoyi-ui/src/views/ccdiBaseStaff/index.vue`
- Modify: `ruoyi-ui/tests/unit/employee-asset-import-ui.test.js`
**Step 1: 替换员工页上传地址**
- `assetUpload.url` 改为 `/ccdi/baseStaff/asset/importData`
- 模板下载改为 `/ccdi/baseStaff/asset/importTemplate`
- 状态查询与失败记录改为员工资产专用 API
**Step 2: 保持状态隔离**
- 保留 `assetUpload``assetPollingTimer``assetCurrentTaskId`
- 仅替换其数据来源
- 不改动普通员工导入状态
**Step 3: 调整员工页提示文案**
- 提示用户仅支持员工本人资产导入
- 下载链接文字明确为“下载员工资产模板”
**Step 4: 运行员工页静态测试**
Run:
```bash
node tests/unit/employee-asset-import-ui.test.js
```
Expected:
- 测试通过
**Step 5: 提交**
```bash
git add ruoyi-ui/src/views/ccdiBaseStaff/index.vue ruoyi-ui/tests/unit/employee-asset-import-ui.test.js
git commit -m "切换员工页资产导入到专用接口"
```
### Task 4: 保护亲属页导入交互不回归
**Files:**
- Modify: `ruoyi-ui/src/views/ccdiStaffFmyRelation/index.vue`
- Modify: `ruoyi-ui/tests/unit/staff-family-asset-api-contract.test.js`
- Modify: `ruoyi-ui/tests/unit/staff-family-asset-detail-import-ui.test.js`
**Step 1: 检查亲属页调用**
- 确认亲属页仍使用 `/ccdi/assetInfo/importData`
- 确认模板下载仍使用 `/ccdi/assetInfo/importTemplate`
- 确认提示文案仍强调“亲属资产”
**Step 2: 如有必要补充只读修正**
- 若之前误改了亲属页文案或下载文件名,恢复为亲属专用表达
**Step 3: 运行亲属页静态测试**
Run:
```bash
node tests/unit/staff-family-asset-api-contract.test.js
node tests/unit/staff-family-asset-detail-import-ui.test.js
```
Expected:
- 测试通过
**Step 4: 提交**
```bash
git add ruoyi-ui/src/views/ccdiStaffFmyRelation/index.vue ruoyi-ui/tests/unit/staff-family-asset-api-contract.test.js ruoyi-ui/tests/unit/staff-family-asset-detail-import-ui.test.js
git commit -m "保护亲属页资产导入交互不回归"
```
### Task 5: 执行前端回归验证
**Files:**
- Modify: `docs/design/2026-03-13-employee-family-asset-import-split-design.md`
**Step 1: 运行全部相关静态测试**
Run:
```bash
node tests/unit/employee-asset-api-contract.test.js
node tests/unit/employee-asset-import-ui.test.js
node tests/unit/staff-family-asset-api-contract.test.js
node tests/unit/staff-family-asset-detail-import-ui.test.js
```
Expected:
- 四个测试全部通过
**Step 2: 做源码检查**
Run:
```bash
git grep -n "/ccdi/baseStaff/asset|/ccdi/assetInfo" -- "ruoyi-ui/src/views/**/*.vue" "ruoyi-ui/src/api/*.js"
```
Expected:
- 员工页仅指向 `/ccdi/baseStaff/asset/*`
- 亲属页仅指向 `/ccdi/assetInfo/*`
**Step 3: 更新设计文档实现状态**
- 在设计文档末尾补充前端已完成拆分验证说明
**Step 4: 提交**
```bash
git add docs/design/2026-03-13-employee-family-asset-import-split-design.md
git commit -m "完成资产导入拆分前端验证"
```

View File

@@ -0,0 +1,58 @@
# Project 40 Large Transaction Data Frontend Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** 确认前端无需代码改造,仅通过现有流水明细页面验证 `project_id=40` 新增测试流水是否可见、可筛选、可展示。
**Architecture:** 本次交付以数据库测试数据生成为主,前端沿用现有 `ccdiProject` 流水明细页面能力。实施重点是准备验证口径,确保新数据在现有筛选器、列表和详情页中可被正确检索和展示。
**Tech Stack:** Vue 2, 若依前端, 现有流水明细查询接口
---
### Task 1: 确认无前端代码变更需求
**Files:**
- Read: `D:\ccdi\ccdi\ruoyi-ui\src\api\ccdiProjectBankStatement.js`
- Read: `D:\ccdi\ccdi\ruoyi-ui\src\views\ccdiProject\`
- Modify: `D:\ccdi\ccdi\docs\implementation-reports\2026-03-16-project40-large-transaction-report.md`
**Step 1: 检查现有筛选能力**
确认页面已支持按时间、对手方、摘要、金额和收支方向查看流水。
**Step 2: 列出验证入口**
在报告文档中写明建议验证的页面入口、过滤条件和预期结果。
**Step 3: Commit**
```bash
git add docs/reports/implementation2026-03-16-project40-large-transaction-report.md
git commit -m "文档: 补充项目40流水前端验证说明"
```
### Task 2: 前端联调验证清单
**Files:**
- Modify: `D:\ccdi\ccdi\docs\implementation-reports\2026-03-16-project40-large-transaction-report.md`
**Step 1: 编写页面验证步骤**
列出以下检查项:
- 项目 40 的流水列表可以查到新增数据
- 房车消费、税务支出等关键词能被摘要/对手方检索到
- 单笔大额收入和大额转账在金额排序下位于前列
- 详情页展示身份证号、原始文件名为空时不报错
**Step 2: 记录无代码改动结论**
明确说明前端本次不需要新增接口、不需要修改组件、不需要补菜单。
**Step 3: Commit**
```bash
git add docs/reports/implementation2026-03-16-project40-large-transaction-report.md
git commit -m "文档: 完成项目40流水前端验证清单"
```

View File

@@ -0,0 +1,335 @@
# Model Param CSV Alignment Frontend Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** Make the global and project model-parameter pages render all model information dynamically from the query API, remove any thousand-separator related design, and keep unified save behavior stable.
**Architecture:** Reuse the existing `listAll/saveAll` front-end flow and current card-based layout. Only adjust the page internals so rendering depends entirely on API payloads and modified-state tracking becomes reliably reactive in Vue 2.
**Tech Stack:** Vue 2, Element UI, Axios, npm
---
### Task 1: 盘点当前页面与 API 的真实状态
**Files:**
- Reference: `ruoyi-ui/src/api/ccdi/modelParam.js`
- Reference: `ruoyi-ui/src/views/ccdi/modelParam/index.vue`
- Reference: `ruoyi-ui/src/views/ccdiProject/components/detail/ParamConfig.vue`
**Step 1: 查看 API 层**
运行:
```bash
Get-Content -Raw 'ruoyi-ui/src/api/ccdi/modelParam.js'
```
预期:`listAllParams``saveAllParams` 已存在,无需重复新增接口方法。
**Step 2: 查看全局配置页**
运行:
```bash
Get-Content -Raw 'ruoyi-ui/src/views/ccdi/modelParam/index.vue'
```
预期:页面已按模型卡片展示,但要确认修改记录实现、动态渲染边界和空状态处理。
**Step 3: 查看项目配置页**
运行:
```bash
Get-Content -Raw 'ruoyi-ui/src/views/ccdiProject/components/detail/ParamConfig.vue'
```
预期:页面已按模型卡片展示,但与全局页一样需要做实现收敛。
**Step 4: 记录结论**
确认本次前端重点不是“重做布局”,而是:
- 完全依赖接口动态展示
- 去掉千分位相关设计
- 优化修改记录实现
### Task 2: 重构全局参数页的动态展示实现
**Files:**
- Modify: `ruoyi-ui/src/views/ccdi/modelParam/index.vue`
**Step 1: 先写一个失败前检查**
启动前端前先做静态检查:
```bash
Get-Content -Raw 'ruoyi-ui/src/views/ccdi/modelParam/index.vue'
```
重点确认是否存在以下问题:
- 使用 `Set + $forceUpdate`
- 对模型或参数数量有隐含假设
- 对格式化值有额外处理
**Step 2: 写最小实现改动**
将页面调整为:
- 直接渲染 `res.data.models || []`
- 通过 `model.modelCode``model.modelName``row.paramName``row.paramDesc``row.paramValue``row.paramUnit` 动态展示
- 不做任何千分位格式化或清洗
- 将修改记录改为响应式对象,例如:
```javascript
modifiedParams: {}
```
并使用类似下面的键:
```javascript
const modifiedKey = `${modelCode}:${row.paramCode}`
```
**Step 3: 写保存组装逻辑**
保证 `handleSaveAll` 只提交已修改项,且请求体继续符合现有接口:
```json
{
"projectId": 0,
"models": [
{
"modelCode": "LARGE_TRANSACTION",
"params": [
{
"paramCode": "SINGLE_TRANSACTION_AMOUNT",
"paramValue": "1111"
}
]
}
]
}
```
**Step 4: 本地编译验证**
运行:
```bash
cd ruoyi-ui
npm run build:prod
```
预期:构建成功,无语法错误。
**Step 5: 提交**
```bash
git add ruoyi-ui/src/views/ccdi/modelParam/index.vue
git commit -m "feat: 优化全局模型参数页动态展示"
```
### Task 3: 重构项目参数页的动态展示实现
**Files:**
- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/ParamConfig.vue`
**Step 1: 查看当前实现**
运行:
```bash
Get-Content -Raw 'ruoyi-ui/src/views/ccdiProject/components/detail/ParamConfig.vue'
```
预期:与全局页类似,也在使用 `Set + $forceUpdate` 或同类逻辑。
**Step 2: 写最小实现改动**
将页面调整为:
- 完全根据 `listAllParams({ projectId: this.projectId })` 返回结果动态渲染
- 不写死模型名称、数量、顺序和参数结构
- 复用与全局页一致的响应式修改记录方案
- 保存成功后重新加载接口数据
**Step 3: 校验默认项目和自定义项目行为**
确保页面本身不做 `default/custom` 分支拼装,只消费接口返回结果。
**Step 4: 本地编译验证**
运行:
```bash
cd ruoyi-ui
npm run build:prod
```
预期:构建成功。
**Step 5: 提交**
```bash
git add ruoyi-ui/src/views/ccdiProject/components/detail/ParamConfig.vue
git commit -m "feat: 优化项目模型参数页动态展示"
```
### Task 4: 统一前端修改记录与保存逻辑
**Files:**
- Modify: `ruoyi-ui/src/views/ccdi/modelParam/index.vue`
- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/ParamConfig.vue`
**Step 1: 抽出共同规则**
两个页面都应满足:
- 修改后才计入 `modifiedCount`
- 未修改时点击保存提示“没有需要保存的修改”
- 保存成功后清空修改记录并重新拉取数据
**Step 2: 移除不稳定实现**
删除或替换:
- `new Set()`
- `$forceUpdate()`
改为 Vue 2 可稳定追踪的普通对象结构。
**Step 3: 静态验证**
运行:
```bash
Get-Content -Raw 'ruoyi-ui/src/views/ccdi/modelParam/index.vue'
Get-Content -Raw 'ruoyi-ui/src/views/ccdiProject/components/detail/ParamConfig.vue'
```
预期:页面内部不再依赖 `Set` 的响应式边界。
**Step 4: 构建验证**
运行:
```bash
cd ruoyi-ui
npm run build:prod
```
预期:构建成功。
**Step 5: 提交**
```bash
git add ruoyi-ui/src/views/ccdi/modelParam/index.vue ruoyi-ui/src/views/ccdiProject/components/detail/ParamConfig.vue
git commit -m "refactor: 收敛模型参数页修改状态管理"
```
### Task 5: 验证“无千分位设计”和“接口驱动展示”
**Files:**
- Optional Record: `docs/tests/records/model-param-frontend-alignment-test.md`
**Step 1: 启动前端开发服务**
运行:
```bash
cd ruoyi-ui
npm run dev
```
**Step 2: 验证全局参数页**
检查:
- 页面根据接口返回显示全部模型
- 模型标题、参数名称、描述、单位均来自接口
- 输入框不自动插入千分位逗号
**Step 3: 验证项目参数页**
检查:
- 默认配置项目展示系统默认参数全集
- 历史 `custom` 项目只展示自身已有参数
- 页面不写死模型数量和参数数量
**Step 4: 验证统一保存**
检查:
- 修改一个参数后 `modifiedCount` 正确增加
- 保存后成功提示正常
- 重新加载后值与接口一致
**Step 5: 停止前端进程并记录结果**
测试结束后关闭 `npm run dev` 启动的进程,并把结果写入:
```text
docs/tests/records/model-param-frontend-alignment-test.md
```
然后提交:
```bash
git add docs/tests/records/model-param-frontend-alignment-test.md
git commit -m "test: 记录模型参数前端动态展示验证"
```
### Task 6: 做一次前后端联调验收
**Files:**
- Reference: `ruoyi-ui/src/views/ccdi/modelParam/index.vue`
- Reference: `ruoyi-ui/src/views/ccdiProject/components/detail/ParamConfig.vue`
- Reference: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiModelParamServiceImpl.java`
**Step 1: 同时启动前后端**
运行:
```bash
mvn -pl ruoyi-admin -am spring-boot:run
```
```bash
cd ruoyi-ui
npm run dev
```
**Step 2: 联调全局参数页**
验证:
- 接口返回的 5 个模型全部显示
- 参数值与系统默认参数一致
- 修改并保存后,刷新仍保持最新值
**Step 3: 联调项目参数页**
验证:
- `default` 项目读取系统默认参数
- 首次保存后项目切为 `custom`
- 历史 `custom` 项目不补新增模型或参数
**Step 4: 结束测试进程**
按仓库约定,测试结束后关闭前后端进程。
**Step 5: 提交联调记录**
```bash
git add docs/tests/records/model-param-frontend-alignment-test.md docs/tests/records/model-param-backend-alignment-test.md
git commit -m "test: 完成模型参数前后端联调验收"
```
---
Plan complete and saved to `docs/plans/frontend/2026-03-16-model-param-csv-alignment-frontend-implementation.md`.

View File

@@ -0,0 +1,84 @@
# Param Save Bar Fixed Bottom Frontend Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** 将模型参数页的“保存所有修改”区域调整为滚动时始终可见的底部吸附操作栏,并统一项目详情页与全局模型参数页的展示效果。
**Architecture:** 在两个现有 Vue 页面中复用同一套样式策略:保留现有保存逻辑,仅通过 `position: sticky; bottom: 0` 将按钮区吸附到滚动容器底部,同时补充页面底部留白和移动端布局适配。这样可以与若依当前的 `AppMain` 滚动容器兼容,避免 `fixed` 带来的侧边栏遮挡问题。
**Tech Stack:** Vue 2, Element UI, SCSS, 若依前端布局
---
### Task 1: 为项目详情参数页补充底部吸附样式
**Files:**
- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/ParamConfig.vue`
**Step 1: 写出预期行为检查点**
确认以下目标行为:
- 页面滚动时“保存所有修改”始终可见
- 最后一张模型卡片不会被保存栏遮挡
- 已修改计数文案继续正常展示
**Step 2: 调整保存栏结构样式**
`button-section` 改为底部吸附操作栏,补充:
- `position: sticky`
- `bottom: 0`
- `z-index`
- 顶部边框与阴影
- 桌面端横向排列
**Step 3: 补充容器底部留白**
在页面主容器增加足够的 `padding-bottom`,避免内容被悬浮栏遮挡。
**Step 4: 增加移动端适配**
在媒体查询中让保存栏换行显示,并让按钮在窄屏下占满宽度。
### Task 2: 为全局模型参数页应用同样的吸附方案
**Files:**
- Modify: `ruoyi-ui/src/views/ccdi/modelParam/index.vue`
**Step 1: 复用项目详情页的布局样式策略**
保持保存栏的结构与交互一致,避免两个模型参数页体验不一致。
**Step 2: 校正全局页容器留白**
结合页面标题区和卡片区,补足底部滚动空间,确保最后一组参数完整可见。
**Step 3: 保持空状态不显示保存栏**
确认 `modelGroups.length === 0` 时仍仅展示空状态,不渲染底部操作栏。
### Task 3: 进行前端回归验证
**Files:**
- Review: `ruoyi-ui/src/views/ccdiProject/components/detail/ParamConfig.vue`
- Review: `ruoyi-ui/src/views/ccdi/modelParam/index.vue`
**Step 1: 执行构建验证**
Run: `npm run build:prod`
Expected: 前端生产构建成功,样式改动未引入编译错误。
**Step 2: 手工检查两个页面**
验证点:
- 滚动时保存栏持续可见
- 修改参数后计数正常变化
- 点击保存后逻辑不受影响
- 窄屏下按钮区不重叠、不溢出
**Step 3: 记录验证结果**
在交付说明中明确区分已执行的构建验证与建议的页面人工回归项。

View File

@@ -0,0 +1,206 @@
# Project Bank Statement Tagging Frontend Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** 在本期“仅做后端能力、不改页面展示”的范围下,明确前端暂不接入流水标签展示,同时补齐后续接入所需的接口契约文档与回归检查,确保后端发布不会误伤现有项目详情页。
**Architecture:** 本期前端不新增 UI、不改造 `UploadData.vue``DetailQuery.vue``PreliminaryCheck.vue``SpecialCheck.vue`。前端实施计划聚焦于两件事:一是确认现有页面对新增后端能力保持兼容,二是在 API 层和页面待办文档中记录未来接入点,避免后续二次梳理。
**Tech Stack:** Vue 2, Element UI, Axios request wrapper, npm, Node
---
### Task 1: 明确本期前端范围为零代码接入
**Files:**
- Modify: `docs/plans/frontend/2026-03-16-project-bank-statement-tagging-frontend-implementation.md`
**Step 1: Write the acceptance checklist**
先把本期前端边界写清楚:
- 不新增标签展示页面
- 不新增标签查询按钮
- 不修改上传数据页现有轮询行为
- 不修改流水明细查询页现有筛选交互
- 不修改结果总览和专项排查占位页面
**Step 2: Run a repository grep to verify no mandatory frontend dependency exists**
Run:
```bash
Get-ChildItem -Recurse -File ruoyi-ui/src | Select-String -Pattern "project/tags|bank-tag|标签重算" | Measure-Object
```
Expected:
- 当前为 `0`,说明前端尚未被后端新能力反向强依赖
**Step 3: Keep implementation minimal**
不修改任何前端业务文件,仅在计划文档中记录本期范围。
**Step 4: Commit**
```bash
git add docs/plans/frontend/2026-03-16-project-bank-statement-tagging-frontend-implementation.md
git commit -m "docs: 明确流水标签前端一期范围"
```
### Task 2: 记录未来 API 契约占位
**Files:**
- Modify: `docs/plans/frontend/2026-03-16-project-bank-statement-tagging-frontend-implementation.md`
**Step 1: Define the future API contract**
在计划中补充后续前端接入时将消费的后端接口:
- `POST /ccdi/project/tags/rebuild`
- 后续可能新增:
- `GET /ccdi/project/tags/summary`
- `GET /ccdi/project/tags/list`
- `GET /ccdi/project/tags/task/latest`
并记录请求与响应最小示例。
**Step 2: Run a smoke review**
Run:
```bash
Get-Content docs/plans/frontend/2026-03-16-project-bank-statement-tagging-frontend-implementation.md | Select-String "/ccdi/project/tags/rebuild"
```
Expected:
- 能检索到接口说明
**Step 3: Keep implementation minimal**
仅补充文档,不提前创建未使用的前端 API 文件,避免本期产生无效代码。
**Step 4: Commit**
```bash
git add docs/plans/frontend/2026-03-16-project-bank-statement-tagging-frontend-implementation.md
git commit -m "docs: 补充流水标签前端后续接口契约"
```
### Task 3: 约束后续页面接入位置
**Files:**
- Modify: `docs/plans/frontend/2026-03-16-project-bank-statement-tagging-frontend-implementation.md`
**Step 1: Write the page integration checklist**
在计划中明确二期前端接入位置:
- `ruoyi-ui/src/views/ccdiProject/components/detail/PreliminaryCheck.vue`
- 适合展示项目级标签汇总、模型触发概览
- `ruoyi-ui/src/views/ccdiProject/components/detail/SpecialCheck.vue`
- 适合展示对象级异常标签列表
- `ruoyi-ui/src/views/ccdiProject/components/detail/DetailQuery.vue`
- 适合展示流水级标签明细与异常原因
- `ruoyi-ui/src/api/`
- 预留 `ccdiProjectTag.js`
**Step 2: Run a path existence smoke check**
Run:
```bash
@(Test-Path 'ruoyi-ui/src/views/ccdiProject/components/detail/PreliminaryCheck.vue'), @(Test-Path 'ruoyi-ui/src/views/ccdiProject/components/detail/SpecialCheck.vue'), @(Test-Path 'ruoyi-ui/src/views/ccdiProject/components/detail/DetailQuery.vue')
```
Expected:
- 三个结果均为 `True`
**Step 3: Keep implementation minimal**
仅记录接入点和职责边界,不提前改页面。
**Step 4: Commit**
```bash
git add docs/plans/frontend/2026-03-16-project-bank-statement-tagging-frontend-implementation.md
git commit -m "docs: 约束流水标签前端二期接入位置"
```
### Task 4: 完成本期前端回归检查
**Files:**
- Modify: `docs/plans/frontend/2026-03-16-project-bank-statement-tagging-frontend-implementation.md`
**Step 1: Run frontend build smoke check**
Run:
```bash
cd ruoyi-ui
npm run build:prod
```
Expected:
- 构建成功
- 说明后端新能力引入后没有要求前端同步改动
**Step 2: Run upload/detail page manual checklist**
手工验证:
- 上传数据页可正常进入
- 批量上传弹窗行为不变
- 拉取本行信息弹窗行为不变
- 流水明细查询页可正常打开
- 结果总览和专项排查仍保持占位状态
**Step 3: Update verification notes**
在计划末尾补充本期前端“不变更但已验证兼容”的结论和后续待接入项。
**Step 4: Commit**
```bash
git add docs/plans/frontend/2026-03-16-project-bank-statement-tagging-frontend-implementation.md
git commit -m "docs: 完成流水标签前端一期兼容性核对"
```
## Future API Contract
本期不接入,但二期前端可直接按以下契约接入:
- `POST /ccdi/project/tags/rebuild`
- 请求体:
```json
{
"projectId": 40,
"modelCode": "LARGE_TRANSACTION"
}
```
- 成功响应:
```json
{
"code": 200,
"msg": "标签重算任务已提交"
}
```
- 后续推荐新增:
- `GET /ccdi/project/tags/task/latest?projectId=40`
- `GET /ccdi/project/tags/summary?projectId=40`
- `GET /ccdi/project/tags/list?projectId=40&resultType=STATEMENT`
## Verification Notes
本期前端计划的核心结论是:
- 按当前需求不做页面实现是合理的
- 需要保证后端独立发布不影响现有项目详情页
- 后续若要展示标签结果,建议优先接入 `PreliminaryCheck.vue``SpecialCheck.vue``DetailQuery.vue`

View File

@@ -0,0 +1,362 @@
# 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: 完成上传文件删除前端回归验证"
```