改造流水明细详情弹窗并统一列表样式

This commit is contained in:
wkc
2026-03-11 15:08:36 +08:00
parent 0de248a039
commit d9f3165872
10 changed files with 472 additions and 254 deletions

View File

@@ -90,4 +90,10 @@ public class CcdiBankStatementDetailVO {
/** 创建时间 */ /** 创建时间 */
private Date createDate; private Date createDate;
/** 原始文件名 */
private String originalFileName;
/** 原始文件上传时间 */
private Date uploadTime;
} }

View File

@@ -98,6 +98,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="paymentMethod" column="paymentMethod"/> <result property="paymentMethod" column="paymentMethod"/>
<result property="cretNo" column="cretNo"/> <result property="cretNo" column="cretNo"/>
<result property="createDate" column="createDate"/> <result property="createDate" column="createDate"/>
<result property="originalFileName" column="originalFileName"/>
<result property="uploadTime" column="uploadTime"/>
</resultMap> </resultMap>
<resultMap id="CcdiBankStatementFilterOptionsVOResultMap" <resultMap id="CcdiBankStatementFilterOptionsVOResultMap"
@@ -313,8 +315,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
bs.internal_flag AS internalFlag, bs.internal_flag AS internalFlag,
bs.payment_method AS paymentMethod, bs.payment_method AS paymentMethod,
bs.cret_no AS cretNo, bs.cret_no AS cretNo,
bs.CREATE_DATE AS createDate bs.CREATE_DATE AS createDate,
fur.file_name AS originalFileName,
fur.upload_time AS uploadTime
FROM ccdi_bank_statement bs FROM ccdi_bank_statement bs
LEFT JOIN ccdi_file_upload_record fur ON fur.log_id = bs.batch_id AND fur.project_id = bs.project_id
WHERE bs.bank_statement_id = #{bankStatementId} WHERE bs.bank_statement_id = #{bankStatementId}
</select> </select>

View File

@@ -115,6 +115,20 @@ class CcdiBankStatementMapperXmlTest {
assertTrue(sql.contains("END ) <= ?"), sql); assertTrue(sql.contains("END ) <= ?"), sql);
} }
@Test
void selectStatementDetailById_shouldJoinUploadRecordForOriginalFileMetadata() throws Exception {
try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(RESOURCE)) {
String xml = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
assertTrue(
xml.contains("LEFT JOIN ccdi_file_upload_record fur ON fur.log_id = bs.batch_id AND fur.project_id = bs.project_id"),
xml
);
assertTrue(xml.contains("fur.file_name AS originalFileName"), xml);
assertTrue(xml.contains("fur.upload_time AS uploadTime"), xml);
}
}
private MappedStatement loadMappedStatement(String statementId) throws Exception { private MappedStatement loadMappedStatement(String statementId) throws Exception {
Configuration configuration = new Configuration(); Configuration configuration = new Configuration();
configuration.setEnvironment(new Environment("test", new JdbcTransactionFactory(), new NoOpDataSource())); configuration.setEnvironment(new Environment("test", new JdbcTransactionFactory(), new NoOpDataSource()));

View File

@@ -5,7 +5,6 @@
:data="dataList" :data="dataList"
style="width: 100%" style="width: 100%"
> >
<!-- 项目名称含描述 -->
<el-table-column <el-table-column
label="项目名称" label="项目名称"
min-width="180" min-width="180"
@@ -14,12 +13,11 @@
<template slot-scope="scope"> <template slot-scope="scope">
<div class="project-info-cell"> <div class="project-info-cell">
<div class="project-name">{{ scope.row.projectName }}</div> <div class="project-name">{{ scope.row.projectName }}</div>
<div class="project-desc">{{ scope.row.description || '暂无描述' }}</div> <div class="project-desc">{{ scope.row.description || "暂无描述" }}</div>
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
<!-- 更新/创建时间 -->
<el-table-column <el-table-column
prop="updateTime" prop="updateTime"
label="更新/创建时间" label="更新/创建时间"
@@ -31,7 +29,6 @@
</template> </template>
</el-table-column> </el-table-column>
<!-- 创建人 -->
<el-table-column <el-table-column
prop="createByName" prop="createByName"
label="创建人" label="创建人"
@@ -39,7 +36,6 @@
align="center" align="center"
/> />
<!-- 状态 -->
<el-table-column <el-table-column
prop="status" prop="status"
label="状态" label="状态"
@@ -49,12 +45,11 @@
<template slot-scope="scope"> <template slot-scope="scope">
<div class="status-tag"> <div class="status-tag">
<span class="status-dot" :style="{ color: getStatusColor(scope.row.status) }"></span> <span class="status-dot" :style="{ color: getStatusColor(scope.row.status) }"></span>
<dict-tag :options="dict.type.ccdi_project_status" :value="scope.row.status"/> <dict-tag :options="dict.type.ccdi_project_status" :value="scope.row.status" />
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
<!-- 目标人数 -->
<el-table-column <el-table-column
prop="targetCount" prop="targetCount"
label="目标人数" label="目标人数"
@@ -62,7 +57,6 @@
align="center" align="center"
/> />
<!-- 预警人数带悬停详情 -->
<el-table-column <el-table-column
label="预警人数" label="预警人数"
width="120" width="120"
@@ -71,26 +65,24 @@
<template slot-scope="scope"> <template slot-scope="scope">
<el-tooltip placement="top" effect="light"> <el-tooltip placement="top" effect="light">
<div slot="content"> <div slot="content">
<div style="padding: 8px;"> <div class="risk-tooltip">
<div style="margin-bottom: 8px; font-weight: bold; color: #303133;"> <div class="risk-tooltip-title">风险人数统计</div>
风险人数统计 <div class="risk-tooltip-item risk-tooltip-item--high">
<span> 高风险</span>
<span class="risk-tooltip-count">{{ scope.row.highRiskCount }} </span>
</div> </div>
<div style="margin-bottom: 6px;"> <div class="risk-tooltip-item risk-tooltip-item--medium">
<span style="color: #f56c6c;"> 风险</span> <span> 风险</span>
<span style="font-weight: bold;">{{ scope.row.highRiskCount }} </span> <span class="risk-tooltip-count">{{ scope.row.mediumRiskCount }} </span>
</div> </div>
<div style="margin-bottom: 6px;"> <div class="risk-tooltip-item risk-tooltip-item--low">
<span style="color: #e6a23c;"> 风险</span> <span> 风险</span>
<span style="font-weight: bold;">{{ scope.row.mediumRiskCount }} </span> <span class="risk-tooltip-count">{{ scope.row.lowRiskCount }} </span>
</div>
<div>
<span style="color: #909399;"> 低风险</span>
<span style="font-weight: bold;">{{ scope.row.lowRiskCount }} </span>
</div> </div>
</div> </div>
</div> </div>
<div class="warning-count-wrapper"> <div class="warning-count-wrapper">
<span :class="getWarningClass(scope.row)" style="cursor: pointer;"> <span :class="getWarningClass(scope.row)" class="warning-count">
{{ scope.row.highRiskCount + scope.row.mediumRiskCount + scope.row.lowRiskCount }} {{ scope.row.highRiskCount + scope.row.mediumRiskCount + scope.row.lowRiskCount }}
</span> </span>
</div> </div>
@@ -98,7 +90,6 @@
</template> </template>
</el-table-column> </el-table-column>
<!-- 操作列 -->
<el-table-column <el-table-column
label="操作" label="操作"
width="350" width="350"
@@ -106,50 +97,56 @@
fixed="right" fixed="right"
> >
<template slot-scope="scope"> <template slot-scope="scope">
<!-- 进行中状态 (status = '0') -->
<el-button <el-button
v-if="scope.row.status === '0'" v-if="scope.row.status === '0'"
size="mini" size="mini"
type="text" type="text"
icon="el-icon-right" icon="el-icon-right"
@click="handleEnter(scope.row)" @click="handleEnter(scope.row)"
>进入项目</el-button> >
进入项目
</el-button>
<!-- 已完成状态 (status = '1') -->
<template v-if="scope.row.status === '1'"> <template v-if="scope.row.status === '1'">
<el-button <el-button
size="mini" size="mini"
type="text" type="text"
icon="el-icon-view" icon="el-icon-view"
@click="handleViewResult(scope.row)" @click="handleViewResult(scope.row)"
>查看结果</el-button> >
查看结果
</el-button>
<el-button <el-button
size="mini" size="mini"
type="text" type="text"
icon="el-icon-refresh" icon="el-icon-refresh"
@click="handleReAnalyze(scope.row)" @click="handleReAnalyze(scope.row)"
>重新分析</el-button> >
重新分析
</el-button>
<el-button <el-button
size="mini" size="mini"
type="text" type="text"
icon="el-icon-folder" icon="el-icon-folder"
@click="handleArchive(scope.row)" @click="handleArchive(scope.row)"
>归档</el-button> >
归档
</el-button>
</template> </template>
<!-- 已归档状态 (status = '2') -->
<el-button <el-button
v-if="scope.row.status === '2'" v-if="scope.row.status === '2'"
size="mini" size="mini"
type="text" type="text"
icon="el-icon-view" icon="el-icon-view"
@click="handleViewResult(scope.row)" @click="handleViewResult(scope.row)"
>查看结果</el-button> >
查看结果
</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<!-- 分页 -->
<el-pagination <el-pagination
v-show="total > 0" v-show="total > 0"
:current-page="pageParams.pageNum" :current-page="pageParams.pageNum"
@@ -159,147 +156,89 @@
layout="total, sizes, prev, pager, next, jumper" layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange" @size-change="handleSizeChange"
@current-change="handleCurrentChange" @current-change="handleCurrentChange"
style="margin-top: 16px; text-align: right;" style="margin-top: 16px; text-align: right"
/> />
</div> </div>
</template> </template>
<script> <script>
export default { export default {
name: 'ProjectTable', name: "ProjectTable",
dicts: ['ccdi_project_status', 'ccdi_config_type'], dicts: ["ccdi_project_status", "ccdi_config_type"],
props: { props: {
dataList: { dataList: {
type: Array, type: Array,
default: () => [] default: () => [],
}, },
loading: { loading: {
type: Boolean, type: Boolean,
default: false default: false,
}, },
total: { total: {
type: Number, type: Number,
default: 0 default: 0,
}, },
pageParams: { pageParams: {
type: Object, type: Object,
default: () => ({ default: () => ({
pageNum: 1, pageNum: 1,
pageSize: 10 pageSize: 10,
}) }),
} },
}, },
methods: { methods: {
getStatusColor(status) { getStatusColor(status) {
const colorMap = { const colorMap = {
'0': '#1890ff', // 进行中 - 蓝色 0: "#1890ff",
'1': '#52c41a', // 已完成 - 绿色 1: "#52c41a",
'2': '#8c8c8c' // 已归档 - 灰色 2: "#8c8c8c",
} };
return colorMap[status] || '#8c8c8c' return colorMap[status] || "#8c8c8c";
}, },
getWarningClass(row) { getWarningClass(row) {
const total = row.highRiskCount + row.mediumRiskCount + row.lowRiskCount const total = row.highRiskCount + row.mediumRiskCount + row.lowRiskCount;
if (row.highRiskCount > 0) { if (row.highRiskCount > 0) {
return 'text-danger text-bold' return "text-danger text-bold";
} else if (row.mediumRiskCount > 0) {
return 'text-warning text-bold'
} else if (total > 0) {
return 'text-info'
} }
return '' if (row.mediumRiskCount > 0) {
return "text-warning text-bold";
}
if (total > 0) {
return "text-info";
}
return "";
}, },
handleEnter(row) { handleEnter(row) {
this.$emit('enter', row) this.$emit("enter", row);
}, },
handleViewResult(row) { handleViewResult(row) {
this.$emit('view-result', row) this.$emit("view-result", row);
}, },
handleReAnalyze(row) { handleReAnalyze(row) {
this.$emit('re-analyze', row) this.$emit("re-analyze", row);
}, },
handleArchive(row) { handleArchive(row) {
this.$emit('archive', row) this.$emit("archive", row);
}, },
handleSizeChange(val) { handleSizeChange(val) {
this.$emit('pagination', { pageNum: this.pageParams.pageNum, pageSize: val }) this.$emit("pagination", {
pageNum: this.pageParams.pageNum,
pageSize: val,
});
}, },
handleCurrentChange(val) { handleCurrentChange(val) {
this.$emit('pagination', { pageNum: val, pageSize: this.pageParams.pageSize }) this.$emit("pagination", {
} pageNum: val,
} pageSize: this.pageParams.pageSize,
} });
},
},
};
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.project-table-container { .project-table-container {
margin-top: 16px; margin-top: 16px;
// 表格整体样式 - 扁平化设计
::v-deep .el-table {
// 移除边框和卡片效果,设置透明背景
border: none !important;
background-color: transparent !important;
overflow: hidden;
// 表头样式 - 扁平化,无背景色
th.el-table__cell {
background-color: transparent !important;
color: #333;
font-weight: 600;
font-size: 14px;
height: 44px;
padding: 12px 10px;
// 只保留底部一条分隔线
border-bottom: 2px solid #e0e0e0 !important;
border-right: none !important;
}
// 数据行样式 - 增加留白,移除分隔线
td.el-table__cell {
color: #333;
font-size: 14px;
height: 48px;
padding: 12px 10px;
border-bottom: none !important;
border-right: none !important;
}
// 移除列分隔线
.el-table__body-wrapper {
.cell {
border-right: none;
}
}
// 悬停效果
.el-table__row {
transition: background-color 0.2s ease;
&:hover > td.el-table__cell {
background-color: #fafafa !important;
}
}
// 移除额外边框
&::before,
&::after {
display: none !important;
}
// 移除 inner border
.el-table__inner-wrapper::before {
display: none !important;
}
}
} }
.status-tag { .status-tag {
@@ -336,10 +275,44 @@ export default {
} }
} }
.risk-tooltip {
padding: 8px;
}
.risk-tooltip-title {
margin-bottom: 8px;
font-weight: 600;
color: #303133;
}
.risk-tooltip-item {
margin-bottom: 6px;
}
.risk-tooltip-item--high {
color: #f56c6c;
}
.risk-tooltip-item--medium {
color: #e6a23c;
}
.risk-tooltip-item--low {
color: #909399;
}
.risk-tooltip-count {
font-weight: 600;
}
.warning-count-wrapper { .warning-count-wrapper {
display: inline-block; display: inline-block;
} }
.warning-count {
cursor: pointer;
}
.text-danger { .text-danger {
color: #f56c6c; color: #f56c6c;
} }
@@ -353,10 +326,9 @@ export default {
} }
.text-bold { .text-bold {
font-weight: bold; font-weight: 700;
} }
// 操作按钮样式 - Material Design 风格
::v-deep .el-button--text { ::v-deep .el-button--text {
color: #1890ff; color: #1890ff;
padding: 8px 12px; padding: 8px 12px;
@@ -373,18 +345,15 @@ export default {
padding-left: 0; padding-left: 0;
} }
// 按钮间距
& + .el-button--text { & + .el-button--text {
margin-left: 4px; margin-left: 4px;
} }
} }
// 分页样式优化 - Material Design 风格
::v-deep .el-pagination { ::v-deep .el-pagination {
margin-top: 24px; margin-top: 24px;
text-align: right; text-align: right;
// 扁平化按钮
.btn-prev, .btn-prev,
.btn-next, .btn-next,
.el-pager li { .el-pager li {
@@ -398,7 +367,7 @@ export default {
.el-pager li.active { .el-pager li.active {
background-color: #1890ff; background-color: #1890ff;
color: white; color: #fff;
border-radius: 4px; border-radius: 4px;
} }

View File

@@ -136,7 +136,7 @@
</div> </div>
<el-input <el-input
v-model="queryParams.userMemo" v-model="queryParams.userMemo"
placeholder="请输入摘要关键" placeholder="请输入摘要关键"
clearable clearable
/> />
</el-form-item> </el-form-item>
@@ -184,8 +184,6 @@
<el-table <el-table
v-loading="loading" v-loading="loading"
:data="list" :data="list"
border
stripe
class="result-table" class="result-table"
@sort-change="handleSortChange" @sort-change="handleSortChange"
> >
@@ -265,103 +263,89 @@
</div> </div>
</div> </div>
<el-drawer <el-dialog
:visible.sync="detailVisible" :visible.sync="detailVisible"
append-to-body append-to-body
custom-class="detail-drawer" custom-class="detail-dialog"
size="520px"
title="流水详情" title="流水详情"
width="980px"
@close="closeDetailDialog" @close="closeDetailDialog"
> >
<div v-loading="detailLoading" class="detail-drawer-body"> <div v-loading="detailLoading" class="detail-dialog-body">
<div class="detail-section"> <div class="detail-overview-grid">
<div class="detail-section-title">基础信息</div> <div class="detail-field">
<div class="detail-grid"> <div class="detail-label">交易时间</div>
<div class="detail-item"> <div class="detail-value">{{ formatField(detailData.trxDate) }}</div>
<span class="detail-label">流水ID</span> </div>
<span class="detail-value">{{ formatField(detailData.bankStatementId) }}</span> <div class="detail-field">
</div> <div class="detail-label">交易金额</div>
<div class="detail-item"> <div class="detail-value amount-text" :class="getAmountClass(detailData.displayAmount)">
<span class="detail-label">交易时间</span> {{ formatSignedAmount(detailData.displayAmount) }}
<span class="detail-value">{{ formatField(detailData.trxDate) }}</span>
</div>
<div class="detail-item">
<span class="detail-label">币种</span>
<span class="detail-value">{{ formatField(detailData.currency) }}</span>
</div>
<div class="detail-item">
<span class="detail-label">交易类型</span>
<span class="detail-value">{{ formatField(detailData.cashType) }}</span>
</div>
<div class="detail-item">
<span class="detail-label">显示金额</span>
<span class="detail-value">{{ formatAmount(detailData.displayAmount) }}</span>
</div>
<div class="detail-item">
<span class="detail-label">余额</span>
<span class="detail-value">{{ formatAmount(detailData.amountBalance) }}</span>
</div> </div>
</div> </div>
</div> <div class="detail-field">
<div class="detail-label">交易后余额</div>
<div class="detail-section"> <div class="detail-value">{{ formatAmount(detailData.amountBalance) }}</div>
<div class="detail-section-title">账户信息</div>
<div class="detail-grid">
<div class="detail-item detail-item-full">
<span class="detail-label">本方账户</span>
<span class="detail-value">
{{ formatField(detailData.leAccountName) }} / {{ formatField(detailData.leAccountNo) }}
</span>
</div>
<div class="detail-item detail-item-full">
<span class="detail-label">对方账户</span>
<span class="detail-value">
{{ formatField(detailData.customerAccountName) }} /
{{ formatField(detailData.customerAccountNo) }}
</span>
</div>
<div class="detail-item">
<span class="detail-label">本方银行</span>
<span class="detail-value">{{ formatField(detailData.bank) }}</span>
</div>
<div class="detail-item">
<span class="detail-label">对方银行</span>
<span class="detail-value">{{ formatField(detailData.customerBank) }}</span>
</div>
</div> </div>
</div>
<div class="detail-section"> <div class="detail-field">
<div class="detail-section-title">补充信息</div> <div class="detail-label">本方主体</div>
<div class="detail-grid"> <div class="detail-value">{{ formatField(detailData.leAccountName) }}</div>
<div class="detail-item detail-item-full"> </div>
<span class="detail-label">摘要</span> <div class="detail-field">
<span class="detail-value">{{ formatField(detailData.userMemo) }}</span> <div class="detail-label">本方账号</div>
</div> <div class="detail-value">{{ formatField(detailData.leAccountNo) }}</div>
<div class="detail-item detail-item-full"> </div>
<span class="detail-label">银行摘要</span> <div class="detail-field">
<span class="detail-value">{{ formatField(detailData.bankComments) }}</span> <div class="detail-label">本方银行</div>
</div> <div class="detail-value">{{ formatField(detailData.bank) }}</div>
<div class="detail-item"> </div>
<span class="detail-label">银行交易号</span>
<span class="detail-value">{{ formatField(detailData.bankTrxNumber) }}</span> <div class="detail-field">
</div> <div class="detail-label">对方名称</div>
<div class="detail-item"> <div class="detail-value">{{ formatCounterpartyName(detailData) }}</div>
<span class="detail-label">交易方式</span> </div>
<span class="detail-value">{{ formatField(detailData.paymentMethod) }}</span> <div class="detail-field">
</div> <div class="detail-label">对方账户</div>
<div class="detail-item"> <div class="detail-value">{{ formatField(detailData.customerAccountNo) }}</div>
<span class="detail-label">异常类型</span> </div>
<span class="detail-value">{{ formatField(detailData.exceptionType) }}</span> <div class="detail-field">
</div> <div class="detail-label">对方银行</div>
<div class="detail-item"> <div class="detail-value">{{ formatField(detailData.customerBank) }}</div>
<span class="detail-label">创建时间</span> </div>
<span class="detail-value">{{ formatDate(detailData.createDate) }}</span>
<div class="detail-field">
<div class="detail-label">摘要</div>
<div class="detail-value">{{ formatField(detailData.userMemo) }}</div>
</div>
<div class="detail-field">
<div class="detail-label">交易类型</div>
<div class="detail-value">{{ formatField(detailData.cashType) }}</div>
</div>
<div class="detail-field">
<div class="detail-label">银行摘要</div>
<div class="detail-value">{{ formatField(detailData.bankComments) }}</div>
</div>
<div class="detail-field detail-field--full">
<div class="detail-label">原始文件</div>
<div class="detail-file-block">
<i class="el-icon-document detail-file-icon"></i>
<div class="detail-file-meta">
<div class="detail-file-name">{{ formatOriginalFileName(detailData) }}</div>
<div class="detail-file-time">
上传时间{{ formatOriginalFileUploadTime(detailData) }}
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</el-drawer> <div slot="footer" class="detail-dialog-footer">
<el-button @click="closeDetailDialog">取消</el-button>
<el-button type="primary" @click="closeDetailDialog">确定</el-button>
</div>
</el-dialog>
</div> </div>
</template> </template>
@@ -414,6 +398,7 @@ const createEmptyOptionData = () => ({
const createEmptyDetailData = () => ({ const createEmptyDetailData = () => ({
bankStatementId: "", bankStatementId: "",
projectId: "",
trxDate: "", trxDate: "",
currency: "", currency: "",
leAccountNo: "", leAccountNo: "",
@@ -421,16 +406,27 @@ const createEmptyDetailData = () => ({
customerAccountName: "", customerAccountName: "",
customerAccountNo: "", customerAccountNo: "",
customerBank: "", customerBank: "",
customerReference: "",
userMemo: "", userMemo: "",
bankComments: "", bankComments: "",
bankTrxNumber: "", bankTrxNumber: "",
bank: "", bank: "",
cashType: "", cashType: "",
amountDr: "",
amountCr: "",
amountBalance: "", amountBalance: "",
displayAmount: "", displayAmount: "",
paymentMethod: "", trxFlag: "",
trxType: "",
exceptionType: "", exceptionType: "",
internalFlag: "",
paymentMethod: "",
cretNo: "",
createDate: "", createDate: "",
originalFileName: "",
uploadTime: "",
sourceFileName: "",
fileName: "",
}); });
export default { export default {
@@ -593,6 +589,7 @@ export default {
}, },
closeDetailDialog() { closeDetailDialog() {
this.detailVisible = false; this.detailVisible = false;
this.detailLoading = false;
this.detailData = createEmptyDetailData(); this.detailData = createEmptyDetailData();
}, },
handleExport() { handleExport() {
@@ -607,7 +604,10 @@ export default {
); );
}, },
formatField(value) { formatField(value) {
return value || "-"; if (value === null || value === undefined || value === "") {
return "-";
}
return String(value);
}, },
formatDate(value) { formatDate(value) {
return value ? parseTime(value, "{y}-{m}-{d} {h}:{i}:{s}") : "-"; return value ? parseTime(value, "{y}-{m}-{d} {h}:{i}:{s}") : "-";
@@ -616,11 +616,55 @@ export default {
if (value === null || value === undefined || value === "") { if (value === null || value === undefined || value === "") {
return "-"; return "-";
} }
return Number(value).toLocaleString("zh-CN", { const amount = Number(value);
if (Number.isNaN(amount)) {
return "-";
}
return amount.toLocaleString("zh-CN", {
minimumFractionDigits: 2, minimumFractionDigits: 2,
maximumFractionDigits: 2, maximumFractionDigits: 2,
}); });
}, },
formatSignedAmount(value) {
if (value === null || value === undefined || value === "") {
return "-";
}
const amount = Number(value);
if (Number.isNaN(amount)) {
return "-";
}
const text = this.formatAmount(amount);
return amount > 0 ? `+${text}` : text;
},
getAmountClass(value) {
if (value === null || value === undefined || value === "") {
return "";
}
return Number(value) >= 0 ? "amount-in" : "amount-out";
},
formatCounterpartyName(detail) {
if (!detail) {
return "-";
}
return this.formatField(detail.customerAccountName);
},
formatOriginalFileName(detail) {
if (!detail) {
return "暂无原始文件";
}
return (
detail.originalFileName ||
detail.sourceFileName ||
detail.fileName ||
"暂无原始文件"
);
},
formatOriginalFileUploadTime(detail) {
if (!detail) {
return "-";
}
return this.formatDate(detail.uploadTime);
},
}, },
}; };
</script> </script>
@@ -747,7 +791,7 @@ export default {
} }
.result-card { .result-card {
border: 1px solid #ebeef5; border: none;
border-radius: 4px; border-radius: 4px;
overflow: hidden; overflow: hidden;
} }
@@ -789,51 +833,106 @@ export default {
color: #f56c6c; color: #f56c6c;
} }
.detail-drawer-body { .detail-dialog-body {
padding: 0 4px 24px; min-height: 280px;
} }
.detail-section { .detail-overview-grid {
margin-bottom: 20px;
padding: 16px;
border: 1px solid #ebeef5;
border-radius: 4px;
}
.detail-section-title {
margin-bottom: 14px;
font-size: 15px;
font-weight: 600;
color: #303133;
}
.detail-grid {
display: grid; display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 14px 16px; gap: 24px 32px;
} }
.detail-item { .detail-field {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 6px; gap: 10px;
min-width: 0;
} }
.detail-item-full { .detail-field--full {
grid-column: 1 / -1; grid-column: 1 / -1;
} }
.detail-label { .detail-label {
font-size: 12px; font-size: 14px;
line-height: 20px;
color: #909399; color: #909399;
} }
.detail-value { .detail-value {
color: #303133; color: #303133;
line-height: 20px; line-height: 22px;
font-size: 16px;
font-weight: 500;
word-break: break-all; word-break: break-all;
} }
.detail-file-block {
display: flex;
align-items: flex-start;
gap: 12px;
padding: 2px 0;
}
.detail-file-icon {
margin-top: 2px;
font-size: 20px;
color: #f59a23;
}
.detail-file-meta {
display: flex;
flex-direction: column;
gap: 6px;
min-width: 0;
}
.detail-file-name {
color: #303133;
line-height: 22px;
word-break: break-all;
}
.detail-file-time {
color: #909399;
font-size: 12px;
line-height: 18px;
}
.detail-dialog-footer {
display: flex;
justify-content: center;
gap: 12px;
}
:deep(.detail-dialog) {
border-radius: 8px;
.el-dialog__header {
padding: 20px 24px;
border-bottom: 1px solid #ebeef5;
}
.el-dialog__title {
font-size: 20px;
font-weight: 600;
color: #303133;
}
.el-dialog__body {
padding: 24px;
}
.el-dialog__footer {
padding: 8px 24px 24px;
}
.el-button {
min-width: 180px;
}
}
@media (max-width: 992px) { @media (max-width: 992px) {
.query-page-shell { .query-page-shell {
grid-template-columns: 1fr; grid-template-columns: 1fr;
@@ -842,6 +941,11 @@ export default {
.filter-actions { .filter-actions {
flex-direction: column; flex-direction: column;
} }
.detail-overview-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 20px 24px;
}
} }
@media (max-width: 768px) { @media (max-width: 768px) {
@@ -862,8 +966,30 @@ export default {
margin: 12px 12px 0; margin: 12px 12px 0;
} }
.detail-grid { .detail-overview-grid {
grid-template-columns: 1fr; grid-template-columns: 1fr;
gap: 18px;
}
.detail-dialog-footer {
flex-direction: column;
}
:deep(.detail-dialog) {
width: calc(100vw - 24px) !important;
margin-top: 8vh !important;
.el-dialog__header,
.el-dialog__body,
.el-dialog__footer {
padding-left: 16px;
padding-right: 16px;
}
.el-button {
width: 100%;
min-width: 0;
}
} }
} }
</style> </style>

View File

@@ -58,7 +58,7 @@
<el-button icon="el-icon-refresh" @click="handleManualRefresh">刷新</el-button> <el-button icon="el-icon-refresh" @click="handleManualRefresh">刷新</el-button>
</div> </div>
<el-table :data="fileUploadList" v-loading="listLoading" stripe border> <el-table :data="fileUploadList" v-loading="listLoading">
<el-table-column prop="fileName" label="文件名" min-width="200"></el-table-column> <el-table-column prop="fileName" label="文件名" min-width="200"></el-table-column>
<el-table-column prop="fileSize" label="文件大小" width="120"> <el-table-column prop="fileSize" label="文件大小" width="120">
<template slot-scope="scope"> <template slot-scope="scope">

View File

@@ -0,0 +1,46 @@
const assert = require("assert");
const fs = require("fs");
const path = require("path");
const componentPath = path.resolve(
__dirname,
"../../src/views/ccdiProject/components/detail/DetailQuery.vue"
);
const source = fs.readFileSync(componentPath, "utf8");
assert(
source.includes("<el-dialog"),
"详情应改为 el-dialog 弹窗展示"
);
assert(
!source.includes("<el-drawer"),
"详情不应再使用抽屉展示"
);
[
'class="detail-dialog"',
'class="detail-overview-grid"',
'class="detail-dialog-footer"',
"formatCounterpartyName(detailData)",
"formatOriginalFileName(detailData)",
'width="980px"',
].forEach((token) => {
assert(
source.includes(token),
`详情弹窗缺少关键结构或方法: ${token}`
);
});
const tableBlockMatch = source.match(/<el-table[\s\S]*?class="result-table"[\s\S]*?>/m);
assert(tableBlockMatch, "未找到流水明细列表表格");
assert(
!/\sborder(\s|>)/.test(tableBlockMatch[0]),
"流水明细列表不应再启用表格边框"
);
assert(
!/\sstripe(\s|>)/.test(tableBlockMatch[0]),
"流水明细列表不应再启用斑马纹"
);
console.log("detail-query-detail-dialog test passed");

View File

@@ -53,12 +53,12 @@ assert(
assert( assert(
!source.includes("queryParams.transactionType"), !source.includes("queryParams.transactionType"),
"筛选逻辑不应再保留交易类型参数" "筛选逻辑不应再保留交易类型参数"
); );
assert( assert(
!source.includes("queryParams.transactionTypeEmpty"), !source.includes("queryParams.transactionTypeEmpty"),
"筛选逻辑不应再保留交易类型空值匹配参数" "筛选逻辑不应再保留交易类型空值匹配参数"
); );
assert( assert(

View File

@@ -0,0 +1,27 @@
const assert = require("assert");
const fs = require("fs");
const path = require("path");
const componentPath = path.resolve(
__dirname,
"../../src/views/ccdiProject/components/ProjectTable.vue"
);
const source = fs.readFileSync(componentPath, "utf8");
const tableBlockMatch = source.match(/<el-table[\s\S]*?style="width: 100%"[\s\S]*?>/m);
assert(tableBlockMatch, "未找到项目列表表格");
assert(
!/\sborder(\s|>)/.test(tableBlockMatch[0]),
"项目列表表格不应启用边框"
);
assert(
!/\sstripe(\s|>)/.test(tableBlockMatch[0]),
"项目列表表格不应启用斑马纹"
);
assert(
!source.includes("::v-deep .el-table"),
"项目列表不应继续保留自定义深度表格皮肤"
);
console.log("project-table-style test passed");

View File

@@ -0,0 +1,25 @@
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 tableBlockMatch = source.match(
/<el-table[\s\S]*?:data="fileUploadList"[\s\S]*?>/m
);
assert(tableBlockMatch, "未找到上传数据文件列表表格");
assert(
!/\sborder(\s|>)/.test(tableBlockMatch[0]),
"上传数据文件列表表格不应再启用边框"
);
assert(
!/\sstripe(\s|>)/.test(tableBlockMatch[0]),
"上传数据文件列表表格不应再启用斑马纹"
);
console.log("upload-data-file-list-table test passed");