调整上传数据页头部按钮与导入卡片布局
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
# 上传数据页卡片精简实施记录
|
||||
|
||||
## 变更时间
|
||||
|
||||
- 2026-03-23
|
||||
|
||||
## 变更内容
|
||||
|
||||
- 调整项目详情上传数据页右上角操作区,新增“导入导入”按钮,按钮直接复用现有流水批量上传弹窗入口。
|
||||
- 删除上传区中的“征信导入”和“名单库选择”卡片,仅保留“流水导入”卡片。
|
||||
- 调整上传卡片区布局,使单张“流水导入”卡片在上传区域内居中展示。
|
||||
- 将“流水导入”卡片宽度由 `320px` 调整为 `420px`,同时保留 `max-width: 100%` 以兼容窄屏。
|
||||
- 调整头部按钮视觉层级,将“查看报告”设为重要按钮,“征信导入”调整为默认按钮样式。
|
||||
- 清理与已删除卡片对应的前端入口代码,避免保留无效页面分支。
|
||||
|
||||
## 涉及文件
|
||||
|
||||
- `ruoyi-ui/src/views/ccdiProject/components/detail/UploadData.vue`
|
||||
- `ruoyi-ui/tests/unit/upload-data-header-import-button.test.js`
|
||||
- `ruoyi-ui/tests/unit/upload-data-disabled-cards.test.js`
|
||||
|
||||
## 验证命令
|
||||
|
||||
- `node ruoyi-ui/tests/unit/upload-data-header-import-button.test.js`
|
||||
- `node ruoyi-ui/tests/unit/upload-data-disabled-cards.test.js`
|
||||
- `node ruoyi-ui/tests/unit/upload-data-batch-upload.test.js`
|
||||
- `node ruoyi-ui/tests/unit/upload-data-pull-bank-info-dialog-layout.test.js`
|
||||
|
||||
## 验证结果
|
||||
|
||||
- `node ruoyi-ui/tests/unit/upload-data-header-import-button.test.js`:通过
|
||||
- `node ruoyi-ui/tests/unit/upload-data-disabled-cards.test.js`:通过
|
||||
- `node ruoyi-ui/tests/unit/upload-data-batch-upload.test.js`:通过
|
||||
- `node ruoyi-ui/tests/unit/upload-data-pull-bank-info-dialog-layout.test.js`:通过
|
||||
@@ -9,10 +9,11 @@
|
||||
<el-button
|
||||
size="small"
|
||||
type="primary"
|
||||
icon="el-icon-document"
|
||||
@click="handleGenerateReport"
|
||||
icon="el-icon-view"
|
||||
:disabled="isReportDisabled"
|
||||
@click="handleViewReport"
|
||||
>
|
||||
生成报告
|
||||
查看报告
|
||||
</el-button>
|
||||
<el-button
|
||||
size="small"
|
||||
@@ -22,6 +23,14 @@
|
||||
>
|
||||
拉取本行信息
|
||||
</el-button>
|
||||
<el-button
|
||||
size="small"
|
||||
icon="el-icon-upload2"
|
||||
:disabled="isProjectTagging"
|
||||
@click="handleOpenCreditUpload"
|
||||
>
|
||||
征信导入
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -158,11 +167,10 @@
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
<!-- 上传弹窗 -->
|
||||
<el-dialog
|
||||
v-if="showUploadDialog"
|
||||
:title="uploadDialogTitle"
|
||||
:visible.sync="showUploadDialog"
|
||||
v-if="showCreditUploadDialog"
|
||||
title="征信导入"
|
||||
:visible.sync="showCreditUploadDialog"
|
||||
:close-on-click-modal="false"
|
||||
width="500px"
|
||||
>
|
||||
@@ -172,53 +180,25 @@
|
||||
action="#"
|
||||
:disabled="isProjectTagging"
|
||||
:auto-upload="false"
|
||||
:on-change="handleFileChange"
|
||||
:file-list="fileList"
|
||||
:on-change="handleCreditFileChange"
|
||||
:file-list="creditFileList"
|
||||
>
|
||||
<i class="el-icon-upload"></i>
|
||||
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
||||
<div class="el-upload__tip" slot="tip">
|
||||
支持 {{ uploadFileTypes }} 格式文件
|
||||
支持 HTML 格式文件
|
||||
</div>
|
||||
</el-upload>
|
||||
<span slot="footer">
|
||||
<el-button @click="showUploadDialog = false">取消</el-button>
|
||||
<el-button @click="showCreditUploadDialog = false">取消</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
:disabled="isProjectTagging"
|
||||
@click="handleConfirmUpload"
|
||||
:loading="uploading"
|
||||
>确定</el-button
|
||||
>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 名单选择弹窗 -->
|
||||
<el-dialog
|
||||
:title="'名单库选择'"
|
||||
:visible.sync="showNameListDialog"
|
||||
width="600px"
|
||||
>
|
||||
<el-form :model="nameListForm" label-width="100px">
|
||||
<el-form-item label="名单类型">
|
||||
<el-select v-model="nameListForm.type" placeholder="请选择名单类型">
|
||||
<el-option label="黑名单" value="blacklist"></el-option>
|
||||
<el-option label="灰名单" value="graylist"></el-option>
|
||||
<el-option label="白名单" value="whitelist"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="名单来源">
|
||||
<el-select v-model="nameListForm.source" placeholder="请选择名单来源">
|
||||
<el-option label="中台管理系统" value="platform"></el-option>
|
||||
<el-option label="本地上传" value="local"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer">
|
||||
<el-button @click="showNameListDialog = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleConfirmNameList"
|
||||
>确定</el-button
|
||||
:loading="creditUploading"
|
||||
@click="handleConfirmCreditUpload"
|
||||
>
|
||||
确定
|
||||
</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
@@ -375,11 +355,9 @@
|
||||
<script>
|
||||
import {
|
||||
getImportStatus,
|
||||
getNameListOptions,
|
||||
getUploadStatus,
|
||||
pullBankInfo,
|
||||
parseIdCardFile,
|
||||
updateNameListSelection,
|
||||
uploadFile,
|
||||
batchUploadFiles,
|
||||
getFileUploadList,
|
||||
@@ -419,13 +397,9 @@ export default {
|
||||
currentMenuTitle: "上传数据",
|
||||
// 圆环周长
|
||||
circumference: 2 * Math.PI * 14,
|
||||
// 上传弹窗
|
||||
showUploadDialog: false,
|
||||
uploadDialogTitle: "",
|
||||
uploadFileType: "",
|
||||
uploadFileTypes: "",
|
||||
fileList: [],
|
||||
uploading: false,
|
||||
showCreditUploadDialog: false,
|
||||
creditFileList: [],
|
||||
creditUploading: false,
|
||||
pullBankInfoDialogVisible: false,
|
||||
pullBankInfoLoading: false,
|
||||
parsingIdCardFile: false,
|
||||
@@ -434,16 +408,8 @@ export default {
|
||||
idCardText: "",
|
||||
dateRange: [],
|
||||
},
|
||||
// 名单选择弹窗
|
||||
showNameListDialog: false,
|
||||
nameListForm: {
|
||||
type: "",
|
||||
source: "platform",
|
||||
},
|
||||
// 上传状态列表
|
||||
uploadStatusList: [],
|
||||
// 名单库选项列表
|
||||
nameListOptions: [],
|
||||
// 侧边栏菜单项
|
||||
menuItems: [
|
||||
{ key: "upload", label: "上传数据", route: "upload" },
|
||||
@@ -463,24 +429,6 @@ export default {
|
||||
uploaded: false,
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
key: "credit",
|
||||
title: "征信导入",
|
||||
desc: "支持 HTML 格式征信数据解析",
|
||||
icon: "el-icon-s-data",
|
||||
btnText: "上传征信",
|
||||
uploaded: false,
|
||||
disabled: true,
|
||||
},
|
||||
{
|
||||
key: "namelist",
|
||||
title: "名单库选择",
|
||||
desc: "选择中台管理系统的名单",
|
||||
icon: "el-icon-s-order",
|
||||
btnText: "选择名单",
|
||||
uploaded: false,
|
||||
disabled: true,
|
||||
},
|
||||
],
|
||||
// 质量指标
|
||||
metrics: [
|
||||
@@ -542,6 +490,9 @@ export default {
|
||||
isProjectTagging() {
|
||||
return String(this.projectInfo.projectStatus) === "3";
|
||||
},
|
||||
isReportDisabled() {
|
||||
return ["0", "3"].includes(String(this.projectInfo.projectStatus));
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
"projectInfo.projectStatus"() {
|
||||
@@ -580,15 +531,8 @@ export default {
|
||||
|
||||
try {
|
||||
this.loading = true;
|
||||
|
||||
// 并行加载上传状态和名单库选项
|
||||
const [uploadStatusRes, nameListRes] = await Promise.all([
|
||||
getUploadStatus(this.projectId),
|
||||
getNameListOptions(),
|
||||
]);
|
||||
|
||||
const uploadStatusRes = await getUploadStatus(this.projectId);
|
||||
this.uploadStatusList = uploadStatusRes.data || [];
|
||||
this.nameListOptions = nameListRes.data || [];
|
||||
|
||||
// 更新上传卡片状态
|
||||
this.updateUploadCards();
|
||||
@@ -669,76 +613,75 @@ export default {
|
||||
this.activeMenu = "upload";
|
||||
this.currentMenuTitle = "上传数据";
|
||||
},
|
||||
handleViewReport() {
|
||||
if (this.isReportDisabled) {
|
||||
return;
|
||||
}
|
||||
this.$emit("menu-change", { key: "overview", route: "overview" });
|
||||
},
|
||||
handleOpenCreditUpload() {
|
||||
if (this.isProjectTagging) {
|
||||
return;
|
||||
}
|
||||
this.creditFileList = [];
|
||||
this.showCreditUploadDialog = true;
|
||||
},
|
||||
/** 上传卡片点击 */
|
||||
handleUploadClick(key) {
|
||||
const card = this.uploadCards.find((c) => c.key === key);
|
||||
if (!card || card.disabled) return;
|
||||
|
||||
if (key === "transaction") {
|
||||
// 流水导入 - 打开批量上传弹窗
|
||||
this.batchUploadDialogVisible = true;
|
||||
this.selectedFiles = [];
|
||||
} else if (key === "credit") {
|
||||
// 征信导入 - 保持现有逻辑
|
||||
this.uploadFileType = key;
|
||||
this.uploadDialogTitle = `上传${card.title}`;
|
||||
this.uploadFileTypes = card.desc.replace(/.*支持|上传/g, "").trim();
|
||||
this.showUploadDialog = true;
|
||||
} else if (key === "namelist") {
|
||||
// 名单库选择 - 保持现有逻辑
|
||||
this.showNameListDialog = true;
|
||||
}
|
||||
},
|
||||
/** 文件选择变化 */
|
||||
handleFileChange(file, fileList) {
|
||||
this.fileList = fileList.slice(-1); // 只保留最后一个文件
|
||||
handleCreditFileChange(file, fileList) {
|
||||
this.creditFileList = fileList.slice(-1);
|
||||
},
|
||||
/** 确认上传 */
|
||||
async handleConfirmUpload() {
|
||||
async handleConfirmCreditUpload() {
|
||||
if (this.isProjectTagging) {
|
||||
this.$message.warning("项目正在进行银行流水打标,暂不可上传或拉取数据");
|
||||
return;
|
||||
}
|
||||
if (this.fileList.length === 0) {
|
||||
if (this.creditFileList.length === 0) {
|
||||
this.$message.warning("请选择要上传的文件");
|
||||
return;
|
||||
}
|
||||
this.uploading = true;
|
||||
|
||||
this.creditUploading = true;
|
||||
|
||||
try {
|
||||
// 调用上传API
|
||||
const res = await uploadFile(
|
||||
this.projectId,
|
||||
this.uploadFileType.toUpperCase(),
|
||||
this.fileList[0].raw
|
||||
"CREDIT",
|
||||
this.creditFileList[0].raw
|
||||
);
|
||||
|
||||
this.uploading = false;
|
||||
this.showUploadDialog = false;
|
||||
this.showCreditUploadDialog = false;
|
||||
this.$message.success("征信文件上传成功,正在处理中...");
|
||||
this.$emit("data-uploaded", { type: "credit" });
|
||||
|
||||
this.$message.success("文件上传成功,正在处理中...");
|
||||
this.$emit("data-uploaded", { type: this.uploadFileType });
|
||||
|
||||
// 刷新上传状态
|
||||
await this.loadUploadStatus();
|
||||
|
||||
// 开始轮询任务状态
|
||||
this.startPolling(res.data);
|
||||
this.pollCreditImportStatus(res && res.data);
|
||||
} catch (error) {
|
||||
this.uploading = false;
|
||||
this.$message.error("上传失败:" + (error.msg || "未知错误"));
|
||||
this.$message.error("征信上传失败:" + (error.msg || "未知错误"));
|
||||
} finally {
|
||||
this.fileList = [];
|
||||
this.creditUploading = false;
|
||||
this.creditFileList = [];
|
||||
}
|
||||
},
|
||||
async pollCreditImportStatus(taskId) {
|
||||
if (!taskId) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** 轮询任务状态 */
|
||||
startPolling(taskId) {
|
||||
const maxAttempts = 20; // 最多轮询20次(约10分钟)
|
||||
const maxAttempts = 20;
|
||||
let attempts = 0;
|
||||
|
||||
const poll = async () => {
|
||||
if (attempts >= maxAttempts) {
|
||||
this.$message.warning("文件处理超时,请稍后查看");
|
||||
this.$message.warning("征信导入处理超时,请稍后查看");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -747,39 +690,36 @@ export default {
|
||||
const status = res.data;
|
||||
|
||||
if (!status) {
|
||||
attempts++;
|
||||
setTimeout(poll, 30000); // 30秒后再次查询
|
||||
attempts += 1;
|
||||
setTimeout(poll, 30000);
|
||||
return;
|
||||
}
|
||||
|
||||
if (status.uploadStatus === "SUCCESS") {
|
||||
// 处理完成,刷新状态
|
||||
await this.loadUploadStatus();
|
||||
this.$message.success("文件处理完成");
|
||||
this.$message.success("征信导入处理完成");
|
||||
return;
|
||||
} else if (status.uploadStatus === "FAILED") {
|
||||
// 处理失败
|
||||
}
|
||||
|
||||
if (status.uploadStatus === "FAILED") {
|
||||
await this.loadUploadStatus();
|
||||
this.$message.error(
|
||||
"文件处理失败:" + (status.errorMessage || "未知错误")
|
||||
"征信导入处理失败:" + (status.errorMessage || "未知错误")
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// 继续处理中
|
||||
attempts++;
|
||||
attempts += 1;
|
||||
setTimeout(poll, 30000);
|
||||
} catch (error) {
|
||||
console.error("轮询任务状态失败:", error);
|
||||
attempts++;
|
||||
console.error("轮询征信导入状态失败:", error);
|
||||
attempts += 1;
|
||||
setTimeout(poll, 30000);
|
||||
}
|
||||
};
|
||||
|
||||
poll();
|
||||
},
|
||||
|
||||
/** 加载上传状态 */
|
||||
async loadUploadStatus() {
|
||||
try {
|
||||
const res = await getUploadStatus(this.projectId);
|
||||
@@ -789,63 +729,6 @@ export default {
|
||||
console.error("加载上传状态失败:", error);
|
||||
}
|
||||
},
|
||||
/** 确认选择名单 */
|
||||
async handleConfirmNameList() {
|
||||
if (!this.nameListForm.type || !this.nameListForm.source) {
|
||||
this.$message.warning("请完善名单选择信息");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// 调用更新名单API
|
||||
await updateNameListSelection(this.projectId, {
|
||||
nameLists: [
|
||||
{
|
||||
type: this.nameListForm.type,
|
||||
source: this.nameListForm.source,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const card = this.uploadCards.find((c) => c.key === "namelist");
|
||||
if (card) {
|
||||
card.uploaded = true;
|
||||
card.btnText = "已选择名单";
|
||||
}
|
||||
|
||||
this.showNameListDialog = false;
|
||||
this.$message.success("名单选择成功");
|
||||
this.$emit("name-selected", this.nameListForm);
|
||||
} catch (error) {
|
||||
this.$message.error("名单选择失败:" + (error.msg || "未知错误"));
|
||||
}
|
||||
},
|
||||
/** 生成报告 */
|
||||
async handleGenerateReport() {
|
||||
this.$confirm("确认生成报告吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
})
|
||||
.then(async () => {
|
||||
try {
|
||||
const loading = this.$loading({
|
||||
lock: true,
|
||||
text: "正在生成报告...",
|
||||
spinner: "el-icon-loading",
|
||||
background: "rgba(0, 0, 0, 0.7)",
|
||||
});
|
||||
|
||||
// await generateReport(this.projectId);
|
||||
|
||||
loading.close();
|
||||
this.$message.success("报告生成成功");
|
||||
this.$emit("generate-report");
|
||||
} catch (error) {
|
||||
this.$message.error("生成报告失败:" + (error.msg || "未知错误"));
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
openPullBankInfoDialog() {
|
||||
this.pullBankInfoDialogVisible = true;
|
||||
},
|
||||
@@ -1491,11 +1374,12 @@ export default {
|
||||
margin-bottom: 16px;
|
||||
|
||||
.upload-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 16px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
.upload-card {
|
||||
width: 420px;
|
||||
max-width: 100%;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 4px;
|
||||
padding: 20px 16px;
|
||||
|
||||
@@ -16,13 +16,22 @@ assert(
|
||||
);
|
||||
|
||||
assert(
|
||||
/key:\s*"credit"[\s\S]*?disabled:\s*true/.test(source),
|
||||
"征信导入卡片应配置为禁用"
|
||||
/uploadCards:\s*\[[\s\S]*?key:\s*"transaction"[\s\S]*?btnText:\s*"上传流水"[\s\S]*?disabled:\s*false[\s\S]*?\],/.test(
|
||||
source
|
||||
),
|
||||
"上传卡片区应只保留一张默认可用的流水导入卡片"
|
||||
);
|
||||
|
||||
assert(
|
||||
/key:\s*"namelist"[\s\S]*?disabled:\s*true/.test(source),
|
||||
"名单库选择卡片应配置为禁用"
|
||||
/syncUploadCardDisabledState\(\)\s*\{[\s\S]*?card\.key === "transaction"[\s\S]*?disabled:\s*this\.isProjectTagging/.test(
|
||||
source
|
||||
),
|
||||
"流水导入卡片应在项目打标中时同步置灰"
|
||||
);
|
||||
|
||||
assert(
|
||||
!/key:\s*"credit"/.test(source) && !/key:\s*"namelist"/.test(source),
|
||||
"上传卡片区不应再保留征信导入或名单库选择卡片配置"
|
||||
);
|
||||
|
||||
assert(
|
||||
|
||||
78
ruoyi-ui/tests/unit/upload-data-header-import-button.test.js
Normal file
78
ruoyi-ui/tests/unit/upload-data-header-import-button.test.js
Normal file
@@ -0,0 +1,78 @@
|
||||
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");
|
||||
|
||||
assert(
|
||||
/<div class="header-actions">[\s\S]*?@click="handleViewReport"[\s\S]*?>\s*查看报告\s*<\/el-button>[\s\S]*?@click="handleFetchBankInfo"[\s\S]*?>\s*拉取本行信息\s*<\/el-button>[\s\S]*?@click="handleOpenCreditUpload"[\s\S]*?>\s*征信导入\s*<\/el-button>/.test(
|
||||
source
|
||||
),
|
||||
"页面右上角按钮顺序应为查看报告、拉取本行信息、征信导入"
|
||||
);
|
||||
|
||||
assert(
|
||||
/<el-button[\s\S]*?:disabled="isReportDisabled"[\s\S]*?@click="handleViewReport"[\s\S]*?>\s*查看报告\s*<\/el-button>/.test(
|
||||
source
|
||||
),
|
||||
"查看报告按钮应绑定禁用状态"
|
||||
);
|
||||
|
||||
assert(
|
||||
/<el-button[\s\S]*?type="primary"[\s\S]*?@click="handleViewReport"[\s\S]*?>\s*查看报告\s*<\/el-button>/.test(
|
||||
source
|
||||
),
|
||||
"查看报告按钮应为重要按钮"
|
||||
);
|
||||
|
||||
assert(
|
||||
/<el-button[\s\S]*?@click="handleOpenCreditUpload"[\s\S]*?>\s*征信导入\s*<\/el-button>/.test(
|
||||
source
|
||||
) &&
|
||||
!/<el-button[\s\S]*?@click="handleOpenCreditUpload"[\s\S]*?type="primary"[\s\S]*?>\s*征信导入\s*<\/el-button>/.test(
|
||||
source
|
||||
),
|
||||
"征信导入按钮应为默认样式"
|
||||
);
|
||||
|
||||
assert(
|
||||
/isReportDisabled\(\)\s*\{[\s\S]*?\["0",\s*"3"\]\.includes\(String\(this\.projectInfo\.projectStatus\)\)/.test(
|
||||
source
|
||||
),
|
||||
"项目状态为进行中或打标中时应禁用查看报告"
|
||||
);
|
||||
|
||||
assert(
|
||||
/uploadCards:\s*\[[\s\S]*?key:\s*"transaction"[\s\S]*?btnText:\s*"上传流水"[\s\S]*?disabled:\s*false[\s\S]*?\],/.test(
|
||||
source
|
||||
),
|
||||
"上传卡片区应只保留流水导入卡片"
|
||||
);
|
||||
|
||||
assert(
|
||||
!/key:\s*"credit"/.test(source),
|
||||
"上传卡片区不应再保留征信导入卡片配置"
|
||||
);
|
||||
|
||||
assert(
|
||||
!/key:\s*"namelist"/.test(source),
|
||||
"上传卡片区不应再保留名单库选择卡片配置"
|
||||
);
|
||||
|
||||
assert(
|
||||
/\.upload-cards\s*\{[\s\S]*?justify-content:\s*center;/.test(source),
|
||||
"单张流水导入卡片应在上传区居中显示"
|
||||
);
|
||||
|
||||
assert(
|
||||
/\.upload-card\s*\{[\s\S]*?width:\s*420px;[\s\S]*?max-width:\s*100%;/.test(
|
||||
source
|
||||
),
|
||||
"流水导入卡片应加宽到 420px,并保持窄屏自适应"
|
||||
);
|
||||
|
||||
console.log("upload-data-header-import-button test passed");
|
||||
Reference in New Issue
Block a user