feat: 立项与合同HCS前端接入——列表合规状态列/推送合规/合规对账弹窗、合同详情合规信息面板与特批发起、甲乙方客商风险提示、合规主数据页

This commit is contained in:
刘泉佳
2026-10-09 19:46:56 +08:00
parent 4294c92e43
commit ef1a43f947
9 changed files with 893 additions and 3 deletions
+151 -2
View File
@@ -72,6 +72,13 @@
</el-tag>
</template>
<template #hcsSyncStatus="{ row }">
<el-tag v-if="row.hcsSyncStatus" :type="hcsSyncTagType(row.hcsSyncStatus)" class="status-text">
{{ hcsSyncLabel(row.hcsSyncStatus) }}
</el-tag>
<span v-else>-</span>
</template>
<template #menu="{ row }">
<el-link
v-if="hasPermission(`${config.permission}_view`)"
@@ -92,10 +99,20 @@
>
{{ operation.label }}
</el-link>
<el-link v-if="canHcsRepush(row)" type="warning" @click="handleHcsRepush(row)">
{{ hcsRepushingId === row.id ? '推送中…' : '推送合规' }}
</el-link>
<el-link v-if="canHcsRecon(row)" type="info" @click="openHcsRecon(row)">合规对账</el-link>
<el-link v-if="canDelete(row)" type="danger" @click="removeRow(row)">删除</el-link>
</template>
</avue-crud>
<hcs-recon-dialog
v-model="hcsReconVisible"
title="合同合规对账"
:loader="hcsReconLoader"
/>
<empty-pagination
:page="page"
@size-change="sizeChange"
@@ -177,6 +194,12 @@
</el-descriptions>
</section>
<hcs-contract-panel
v-if="!isPublicViewPage"
:contract="detailRow"
@refresh="initDetailPage"
/>
<contract-attachment-section
title="合同文件"
description
@@ -436,6 +459,7 @@
clearable
:loading="contractPartyLoading"
@visible-change="visible => visible && loadContractPartyOptions()"
@change="queryPartyRisk('partyA')"
>
<el-option
v-for="item in contractPartyAOptions"
@@ -453,6 +477,7 @@
clearable
:loading="contractPartyLoading"
@visible-change="visible => visible && loadContractPartyOptions()"
@change="queryPartyRisk('partyB')"
>
<el-option
v-for="item in contractPartyBOptions"
@@ -462,6 +487,14 @@
/>
</el-select>
</el-form-item>
<el-form-item v-if="partyRiskText" label=" " label-width="24">
<el-alert
:title="partyRiskText"
type="warning"
:closable="false"
class="contract-manage-form__risk-alert"
/>
</el-form-item>
<el-form-item label="签订日期" prop="signDate">
<el-date-picker
v-model="form.signDate"
@@ -1010,6 +1043,7 @@ import NProgress from 'nprogress';
import 'nprogress/nprogress.css';
import { exportBlob } from '@/api/common';
import * as api from '@/api/business/contract-manage';
import { repush as hcsRepush, reconContract, getRisk } from '@/api/business/hcs';
import { getMkPublicDetail } from '@/api/mk-process';
import { getList as getProjectList } from '@/api/business/project-apply';
import { getList as getCustomerArchiveList } from '@/api/vehicle/customer-archive';
@@ -1027,6 +1061,8 @@ import ContractAttachmentSection, {
attachmentUrl as sharedAttachmentUrl,
normalizeContractFileRows,
} from './components/contract-attachment-section.vue';
import HcsReconDialog from './components/hcs-recon-dialog.vue';
import HcsContractPanel from './components/hcs-contract-panel.vue';
import PdfPreview from '@/components/pdf-preview/main.vue';
import { ElImageViewer, ElCascader } from 'element-plus';
import { OpenFileViewer } from '@open-file-viewer/vue';
@@ -1183,6 +1219,8 @@ export default {
ContractAttachmentSection,
BillingPlanEditor,
ChangeRecordDetailDialog,
HcsReconDialog,
HcsContractPanel,
ElImageViewer,
OpenFileViewer,
PdfPreview,
@@ -1246,6 +1284,9 @@ export default {
contractCategoryDictOptions: [],
contractPartyAOptions: [],
contractPartyBOptions: [],
// 合规客商风险(C101-1 展示用):甲方/乙方选择后即时查询
contractPartyCodeMap: {},
partyRisk: {},
contractPartyLoading: false,
contractPartyRequestId: 0,
organizationOptions: [],
@@ -1284,6 +1325,10 @@ export default {
billCycleTypeOptions: ['固定截单日', '自然月', '自定义多周期'],
detailLoading: false,
detailRow: {},
// 合规(HCS)推送/对账状态
hcsRepushingId: null,
hcsReconVisible: false,
hcsReconLoader: null,
detailContractFileRows: [],
detailAttachmentRows: [],
detailBillingPlanRows: [],
@@ -1459,6 +1504,21 @@ export default {
const endDate = this.detailRow.endDate || '-';
return `${startDate} 至 ${endDate}`;
},
// 高风险/黑名单客商签约提示(C101-1,签约前提示;强制拦截在合规推送链路)
partyRiskText() {
const parts = [];
['partyA', 'partyB'].forEach(prop => {
const risk = this.partyRisk[prop];
if (!risk) return;
const name = this.form[prop] || '';
const label = prop === 'partyA' ? '甲方' : '乙方';
if (risk.blacklist) parts.push(`${label}[${name}]为黑名单客商`);
else if (['高', '高风险', 'high'].includes(String(risk.riskLevel).trim()))
parts.push(`${label}[${name}]为高风险客商`);
});
if (!parts.length) return '';
return `${parts.join(';')},按集团规定不可直接签约,审批通过后需在合同详情发起特批`;
},
detailLegalSealText() {
const value = this.detailRow.legalSealFlag;
if (value === undefined || value === null || value === '') return '-';
@@ -1573,6 +1633,55 @@ export default {
if (stage === 'terminated') return 'danger';
return 'info';
},
// ==================== 合规(HCS)推送/对账(照“同步金蝶”按钮模式) ====================
hcsSyncLabel(value) {
return { unsynced: '待推送', synced: '已推送', failed: '推送失败' }[value] || value;
},
hcsSyncTagType(value) {
return { unsynced: 'info', synced: 'success', failed: 'danger' }[value] || 'info';
},
canHcsRepush(row) {
return (
['approved', 'change_approved'].includes(row.approvalStatus) &&
row.hcsSyncStatus !== 'synced'
);
},
canHcsRecon(row) {
return ['approved', 'change_approved'].includes(row.approvalStatus);
},
handleHcsRepush(row) {
if (this.hcsRepushingId) return;
this.$confirm('确定推送该数据到合同合规系统?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
this.hcsRepushingId = row.id;
hcsRepush('contract', row.id)
.then(res => {
if (res.data?.success === false) {
this.$message.error(res.data?.msg || '推送合规失败');
return;
}
this.$message.success(res.data?.data || '推送合规成功');
this.onLoad(this.page, this.query);
})
.catch(error => {
this.$message.error(error?.response?.data?.msg || '推送合规失败');
})
.finally(() => {
this.hcsRepushingId = null;
});
});
},
openHcsRecon(row) {
if (!row.contractNo) {
this.$message.warning('该合同无合同编号,无法对账');
return;
}
this.hcsReconLoader = () => reconContract(row.contractNo);
this.hcsReconVisible = true;
},
canEdit(row) {
return (
this.hasPermission(`${this.config.permission}_edit`) &&
@@ -2113,8 +2222,21 @@ export default {
])
.then(responses => {
if (requestId !== this.contractPartyRequestId) return;
const names = responses
.flatMap(extractRecords)
const records = responses.flatMap(extractRecords);
// 名称→客商编码映射,供 C101-1 风险查询使用(§9-U6 编码体系联调确认)
const codeMap = {};
records.forEach(item => {
const code = item.customerCode;
if (!code) return;
[item.fullName, item.shortName, item.customerName]
.map(name => String(name || '').trim())
.filter(Boolean)
.forEach(name => {
codeMap[name] = code;
});
});
this.contractPartyCodeMap = codeMap;
const names = records
.map(item => item.fullName || item.shortName || item.customerName || item.customerCode)
.map(name => String(name || '').trim())
.filter(Boolean);
@@ -2127,6 +2249,29 @@ export default {
if (requestId === this.contractPartyRequestId) this.contractPartyLoading = false;
});
},
// C101-1 客商风险查询(展示用,强校验在后端推送/付款链路):失败静默不阻塞填写
queryPartyRisk(prop) {
const name = this.form[prop];
const code = name ? this.contractPartyCodeMap[name] : null;
if (!code) {
this.partyRisk = { ...this.partyRisk, [prop]: null };
return;
}
getRisk(code)
.then(res => {
const risk = res?.data?.data ?? res?.data ?? {};
this.partyRisk = {
...this.partyRisk,
[prop]: {
riskLevel: risk.fdRiskLevel || '',
blacklist: String(risk.fdBlackStatus || '').toLowerCase() === 'yes',
},
};
})
.catch(() => {
this.partyRisk = { ...this.partyRisk, [prop]: null };
});
},
loadOrganizationOptions() {
if (this.organizationLoading) return;
this.organizationLoading = true;
@@ -2961,6 +3106,10 @@ export default {
vertical-align: bottom;
}
.contract-manage-form__risk-alert {
width: 100%;
}
.contract-manage-page {
&__expiry-search {
display: flex;