优化拉取本行信息弹窗上传交互

This commit is contained in:
wkc
2026-03-12 10:46:40 +08:00
parent 3d61f7d252
commit 4e696eff1e
4 changed files with 332 additions and 23 deletions

View File

@@ -0,0 +1,40 @@
# Pull Bank Info Upload Button Hit Area Backend Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** Confirm this UI optimization does not require backend changes and document the verification boundary.
**Architecture:** The issue is caused by the frontend dialog structure and scoped styles in the upload page. Backend APIs, request payloads, and parsing logic remain unchanged, so this plan only records the no-op backend conclusion and the checks needed to avoid accidental interface regressions.
**Tech Stack:** Java 21, Spring Boot 3, Maven, existing `ccdi-project` upload APIs
---
### Task 1: Verify backend impact is zero
**Files:**
- Review: `docs/plans/2026-03-12-pull-bank-info-upload-button-hit-area-design.md`
- Review: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/`
- Review: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/`
**Step 1: Confirm the bug scope**
Check that the reported problem is limited to the frontend dialog button hit area and layout, not request construction or backend response handling.
**Step 2: Verify no API contract changes are needed**
Review the existing pull-bank-info request fields and confirm the dialog still submits the same `projectId`, `idCards`, `startDate`, and `endDate`.
**Step 3: Keep backend code unchanged**
Do not modify controller, service, mapper, or DTO classes for this task.
**Step 4: Run targeted regression verification if frontend payload changes are suspected**
Run only if implementation unexpectedly touches request assembly:
```bash
mvn test -Dtest=CcdiFileUploadServiceImplTest
```
Expected: related backend tests pass and no interface behavior changes are introduced.

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

@@ -210,7 +210,11 @@
:close-on-click-modal="false" :close-on-click-modal="false"
width="640px" width="640px"
> >
<el-form :model="pullBankInfoForm" label-width="100px"> <el-form
class="pull-bank-info-form"
:model="pullBankInfoForm"
label-width="100px"
>
<el-form-item label="证件号码"> <el-form-item label="证件号码">
<el-input <el-input
v-model="pullBankInfoForm.idCardText" v-model="pullBankInfoForm.idCardText"
@@ -218,35 +222,57 @@
:rows="5" :rows="5"
placeholder="支持逗号、中文逗号、换行分隔" placeholder="支持逗号、中文逗号、换行分隔"
/> />
<div class="pull-bank-field-tip">
支持逗号中文逗号换行分隔文件解析结果会自动合并并去重
</div>
</el-form-item> </el-form-item>
<el-form-item label="身份证文件"> <el-form-item label="身份证文件">
<el-upload <div class="pull-bank-file-panel">
action="#" <div class="pull-bank-file-actions">
:auto-upload="false" <el-upload
:limit="1" class="pull-bank-file-upload"
:file-list="idCardFileList" action="#"
accept=".xls,.xlsx" :auto-upload="false"
:on-change="handleIdCardFileChange" :limit="1"
:on-remove="handleIdCardFileRemove" :show-file-list="false"
> :file-list="idCardFileList"
<el-button size="small" type="primary">选择文件</el-button> accept=".xls,.xlsx"
<div slot="tip" class="el-upload__tip"> :on-change="handleIdCardFileChange"
支持 .xls.xlsx 文件 :on-remove="handleIdCardFileRemove"
>
<el-button slot="trigger" size="small" type="primary" plain>
选择文件
</el-button>
</el-upload>
<div class="pull-bank-file-tip">
支持 .xls.xlsx 文件解析后自动补充证件号码
</div>
</div>
<div v-if="idCardFileList.length > 0" class="selected-id-card-file">
<div class="selected-id-card-file__info">
<i class="el-icon-document"></i>
<span class="selected-id-card-file__name">
{{ idCardFileList[0].name }}
</span>
</div>
<el-button type="text" @click="handleIdCardFileRemove">
移除
</el-button>
</div>
<div v-if="parsingIdCardFile" class="parse-tip">
正在解析身份证文件...
</div> </div>
</el-upload>
<div v-if="parsingIdCardFile" class="parse-tip">
正在解析身份证文件...
</div> </div>
</el-form-item> </el-form-item>
<el-form-item label="时间跨度"> <el-form-item label="时间跨度">
<el-date-picker <el-date-picker
class="pull-bank-range-picker"
v-model="pullBankInfoForm.dateRange" v-model="pullBankInfoForm.dateRange"
type="daterange" type="daterange"
value-format="yyyy-MM-dd" value-format="yyyy-MM-dd"
range-separator="" range-separator=""
start-placeholder="开始日期" start-placeholder="开始日期"
end-placeholder="结束日期" end-placeholder="结束日期"
style="width: 100%"
/> />
</el-form-item> </el-form-item>
</el-form> </el-form>
@@ -1442,17 +1468,20 @@ export default {
// 上传弹窗样式 // 上传弹窗样式
::v-deep .el-dialog__wrapper { ::v-deep .el-dialog__wrapper {
.upload-area { .upload-area,
.batch-upload-area {
width: 100%; width: 100%;
} }
.el-upload { .upload-area .el-upload,
.batch-upload-area .el-upload {
width: 100%; width: 100%;
}
.el-upload-dragger { .upload-area .el-upload-dragger,
width: 100%; .batch-upload-area .el-upload-dragger {
height: 200px; width: 100%;
} height: 200px;
} }
.el-upload__tip { .el-upload__tip {
@@ -1462,6 +1491,76 @@ export default {
} }
} }
.pull-bank-info-form {
.pull-bank-field-tip {
margin-top: 8px;
font-size: 12px;
color: #909399;
line-height: 1.5;
}
}
.pull-bank-file-panel {
padding: 16px;
border: 1px solid #e4e7ed;
border-radius: 8px;
background: #fafcff;
.pull-bank-file-actions {
display: flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
}
.pull-bank-file-tip {
font-size: 12px;
color: #606266;
line-height: 1.5;
}
}
.pull-bank-file-upload {
display: inline-flex;
flex: 0 0 auto;
}
.selected-id-card-file {
margin-top: 12px;
padding: 10px 12px;
border-radius: 6px;
background: #ffffff;
border: 1px solid #ebeef5;
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
.selected-id-card-file__info {
min-width: 0;
display: flex;
align-items: center;
gap: 8px;
color: #303133;
}
.selected-id-card-file__name {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.el-button {
flex-shrink: 0;
padding: 0;
}
}
.pull-bank-range-picker {
width: 100%;
}
// 批量上传弹窗样式 // 批量上传弹窗样式
.batch-upload-area { .batch-upload-area {
width: 100%; width: 100%;
@@ -1586,5 +1685,23 @@ export default {
align-items: flex-start; align-items: flex-start;
gap: 12px; gap: 12px;
} }
.pull-bank-file-panel {
padding: 12px;
.pull-bank-file-actions {
align-items: stretch;
gap: 10px;
}
}
.pull-bank-file-upload {
width: 100%;
}
.selected-id-card-file {
align-items: flex-start;
flex-direction: column;
}
} }
</style> </style>

View File

@@ -0,0 +1,39 @@
const assert = require("assert");
const fs = require("fs");
const path = require("path");
const componentPath = path.resolve(
__dirname,
"../../src/views/ccdiProject/components/detail/UploadData.vue"
);
const source = fs.readFileSync(componentPath, "utf8");
const dialogIndex = source.indexOf('title="拉取本行信息"');
assert.notStrictEqual(dialogIndex, -1, "未找到拉取本行信息弹窗");
const dialogEndIndex = source.indexOf("</el-dialog>", dialogIndex);
assert.notStrictEqual(dialogEndIndex, -1, "未找到拉取本行信息弹窗结束标签");
const dialogSource = source.slice(dialogIndex, dialogEndIndex);
assert(
/class="pull-bank-info-form"/.test(dialogSource),
"拉取本行信息弹窗应使用独立表单容器,便于控制排版"
);
assert(
/class="pull-bank-file-panel"/.test(dialogSource),
"拉取本行信息弹窗应提供独立的文件导入面板"
);
assert(
/class="pull-bank-file-upload"/.test(dialogSource),
"文件选择区域应有独立样式钩子,避免点击范围铺满整行"
);
assert(
/class="selected-id-card-file"/.test(dialogSource),
"选择文件后应显示已选文件摘要区域"
);
console.log("upload-data-pull-bank-info-dialog-layout test passed");