Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3f019c9ac5 | |||
| 90edc4b864 | |||
| d05f8fd636 | |||
| d968c418a8 | |||
| d100e7a271 | |||
| 447b2d9d76 | |||
| 603038d5ff | |||
| 673c47771b | |||
| 3168d365d0 | |||
| 0c4bb7cd82 | |||
| fb91768585 | |||
| 411fdd2425 | |||
| 7ea96bcdc0 | |||
| b7af496a43 | |||
| 9e5f53702f | |||
| 8640238cbe | |||
| ef1a43f947 |
@@ -36,6 +36,8 @@ export const approve = id =>
|
|||||||
url: `${baseUrl}/approve`,
|
url: `${baseUrl}/approve`,
|
||||||
method: 'post',
|
method: 'post',
|
||||||
params: { id },
|
params: { id },
|
||||||
|
// 审批通过后同步推送合规(附件转传可达 20s+),覆盖全局 10s 超时
|
||||||
|
timeout: 120000,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const reject = id =>
|
export const reject = id =>
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
import request from '@/axios';
|
||||||
|
|
||||||
|
const baseUrl = '/blade-transport/hcs';
|
||||||
|
|
||||||
|
// 合规推送涉及 HCS 远程多次调用(风险校验+附件转传+起草),可达 20s+,覆盖全局 10s 超时
|
||||||
|
export const repush = (bizType, id) =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/sync/${bizType}/${id}`,
|
||||||
|
method: 'post',
|
||||||
|
timeout: 60000,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 批量推送为逐条同步推送,耗时随勾选行数线性增长,进一步放宽超时
|
||||||
|
export const repushBatch = (bizType, ids) =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/sync/${bizType}/batch`,
|
||||||
|
method: 'post',
|
||||||
|
data: ids,
|
||||||
|
timeout: 300000,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getTemplates = () =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/master-data/template`,
|
||||||
|
method: 'get',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const refreshMasterData = () =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/master-data/refresh`,
|
||||||
|
method: 'post',
|
||||||
|
timeout: 60000,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getRisk = customerCode =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/risk`,
|
||||||
|
method: 'get',
|
||||||
|
params: { customerCode },
|
||||||
|
timeout: 30000,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 客商风险批量查询(客商列表页展示用):入参编号数组,后端逐个查 C101-1,单个失败跳过
|
||||||
|
export const getRiskBatch = customerCodes =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/risk/batch`,
|
||||||
|
method: 'get',
|
||||||
|
params: { customerCodes: (customerCodes || []).join(',') },
|
||||||
|
timeout: 60000,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 客商风险按名称查询(客商档案表单展示用):名称反查编号后口径同编号查询
|
||||||
|
export const getRiskByName = customerName =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/risk/by-name`,
|
||||||
|
method: 'get',
|
||||||
|
params: { customerName },
|
||||||
|
timeout: 30000,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const uploadAttachment = formData =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/attachment/upload`,
|
||||||
|
method: 'post',
|
||||||
|
data: formData,
|
||||||
|
timeout: 60000,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const downloadAttachment = (fdId, name) =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/attachment/download`,
|
||||||
|
method: 'get',
|
||||||
|
params: { fdId, name },
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 120000,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const applyContractSpec = data =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/contract/spec-apply`,
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
timeout: 60000,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const confirmContractSpec = (id, result) =>
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/contract/spec/confirm`,
|
||||||
|
method: 'post',
|
||||||
|
params: { id, result },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const unwrapData = response => {
|
||||||
|
const data = response?.data?.data ?? response?.data;
|
||||||
|
return data && typeof data === 'object' && 'data' in data ? data.data : data;
|
||||||
|
};
|
||||||
@@ -43,6 +43,8 @@ export const approve = id =>
|
|||||||
url: `${baseUrl}/approve`,
|
url: `${baseUrl}/approve`,
|
||||||
method: 'post',
|
method: 'post',
|
||||||
params: { id },
|
params: { id },
|
||||||
|
// 审批通过后同步推送合规(远程多次调用),覆盖全局 10s 超时
|
||||||
|
timeout: 60000,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const reject = id =>
|
export const reject = id =>
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import request from '@/axios';
|
import request from '@/axios';
|
||||||
|
|
||||||
|
/** 提交审核流(MK)统一超时:客商/项目/合同/预结算/正式结算/付款/开票/收票/汇票付款 */
|
||||||
|
export const MK_SUBMIT_TIMEOUT = 30000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 调用 MK processSubmit 提交审核流
|
* 调用 MK processSubmit 提交审核流
|
||||||
* @param {Object} data
|
* @param {Object} data
|
||||||
@@ -15,6 +18,7 @@ export const processSubmit = data => {
|
|||||||
url: '/blade-system/businessProcess/processSubmit',
|
url: '/blade-system/businessProcess/processSubmit',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data,
|
data,
|
||||||
|
timeout: MK_SUBMIT_TIMEOUT,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -28,6 +32,7 @@ export const processDelete = data => {
|
|||||||
url: '/blade-system/businessProcess/processDelete',
|
url: '/blade-system/businessProcess/processDelete',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data,
|
data,
|
||||||
|
timeout: MK_SUBMIT_TIMEOUT,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export const transportTypeOptions = [
|
export const transportTypeOptions = [
|
||||||
{ label: '公路运输', value: '公路运输' },
|
{ label: '公路运输', value: '公路运输' },
|
||||||
{ label: '铁路运输', value: '铁路运输' },
|
{ label: '铁路运输', value: '铁路运输' },
|
||||||
{ label: '水路运输', value: '水路运输' },
|
{ label: '水路运输', value: '水路运输' },
|
||||||
@@ -143,6 +143,17 @@ export const contractArchiveStatusOptions = [
|
|||||||
{ label: '已归档', value: '已归档' },
|
{ label: '已归档', value: '已归档' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const hcsSyncStatusOptions = [
|
||||||
|
{ label: '待推送', value: 'unsynced' },
|
||||||
|
{ label: '已推送', value: 'synced' },
|
||||||
|
{ label: '推送失败', value: 'failed' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const yesNoOptions = [
|
||||||
|
{ label: '是', value: true },
|
||||||
|
{ label: '否', value: false },
|
||||||
|
];
|
||||||
|
|
||||||
export const nodeOptions = [
|
export const nodeOptions = [
|
||||||
{ label: '接单', value: '接单' },
|
{ label: '接单', value: '接单' },
|
||||||
{ label: '到场', value: '到场' },
|
{ label: '到场', value: '到场' },
|
||||||
|
|||||||
@@ -7,9 +7,11 @@ import {
|
|||||||
createCrudOption,
|
createCrudOption,
|
||||||
effectiveTypeOptions,
|
effectiveTypeOptions,
|
||||||
nonNegativeRule,
|
nonNegativeRule,
|
||||||
|
hcsSyncStatusOptions,
|
||||||
selectRule,
|
selectRule,
|
||||||
signTypeOptions,
|
signTypeOptions,
|
||||||
textRule,
|
textRule,
|
||||||
|
yesNoOptions,
|
||||||
} from './common';
|
} from './common';
|
||||||
|
|
||||||
const contractSettlementModeOptions = [
|
const contractSettlementModeOptions = [
|
||||||
@@ -606,6 +608,50 @@ export const option = createCrudOption([
|
|||||||
addDisplay: false,
|
addDisplay: false,
|
||||||
editDisplay: false,
|
editDisplay: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: '合规状态',
|
||||||
|
prop: 'hcsSyncStatus',
|
||||||
|
type: 'select',
|
||||||
|
search: true,
|
||||||
|
searchOrder: 6,
|
||||||
|
searchPlaceholder: '请选择',
|
||||||
|
slot: true,
|
||||||
|
dicData: hcsSyncStatusOptions,
|
||||||
|
minWidth: 110,
|
||||||
|
addDisplay: false,
|
||||||
|
editDisplay: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '金蝶合同ID',
|
||||||
|
prop: 'kingdeeId',
|
||||||
|
minWidth: 130,
|
||||||
|
addDisplay: false,
|
||||||
|
editDisplay: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '是否同步金蝶',
|
||||||
|
prop: 'kingdeeSynced',
|
||||||
|
type: 'select',
|
||||||
|
search: true,
|
||||||
|
searchPlaceholder: '请选择',
|
||||||
|
searchOrder: 7,
|
||||||
|
dicData: yesNoOptions,
|
||||||
|
hide: true,
|
||||||
|
addDisplay: false,
|
||||||
|
editDisplay: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '是否同步合规',
|
||||||
|
prop: 'hcsSynced',
|
||||||
|
type: 'select',
|
||||||
|
search: true,
|
||||||
|
searchPlaceholder: '请选择',
|
||||||
|
searchOrder: 8,
|
||||||
|
dicData: yesNoOptions,
|
||||||
|
hide: true,
|
||||||
|
addDisplay: false,
|
||||||
|
editDisplay: false,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: '当前节点',
|
label: '当前节点',
|
||||||
prop: 'currentNode',
|
prop: 'currentNode',
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
businessModeOptions,
|
businessModeOptions,
|
||||||
businessTypeOptions,
|
businessTypeOptions,
|
||||||
createCrudOption,
|
createCrudOption,
|
||||||
|
hcsSyncStatusOptions,
|
||||||
nonNegativeRule,
|
nonNegativeRule,
|
||||||
projectApprovalStatusOptions,
|
projectApprovalStatusOptions,
|
||||||
projectSourceOptions,
|
projectSourceOptions,
|
||||||
@@ -10,6 +11,7 @@ import {
|
|||||||
selectRule,
|
selectRule,
|
||||||
settlementModeOptions,
|
settlementModeOptions,
|
||||||
textRule,
|
textRule,
|
||||||
|
yesNoOptions,
|
||||||
} from './common';
|
} from './common';
|
||||||
|
|
||||||
const projectEffectiveTypeOptions = [
|
const projectEffectiveTypeOptions = [
|
||||||
@@ -405,6 +407,57 @@ export const option = createCrudOption([
|
|||||||
addDisplay: false,
|
addDisplay: false,
|
||||||
editDisplay: false,
|
editDisplay: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: '合规状态',
|
||||||
|
prop: 'hcsSyncStatus',
|
||||||
|
type: 'select',
|
||||||
|
search: true,
|
||||||
|
searchPlaceholder: '请选择',
|
||||||
|
searchOrder: 1,
|
||||||
|
slot: true,
|
||||||
|
dicData: hcsSyncStatusOptions,
|
||||||
|
minWidth: 110,
|
||||||
|
addDisplay: false,
|
||||||
|
editDisplay: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '金蝶项目ID',
|
||||||
|
prop: 'kingdeeId',
|
||||||
|
minWidth: 130,
|
||||||
|
addDisplay: false,
|
||||||
|
editDisplay: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '金蝶项目编号',
|
||||||
|
prop: 'kingdeeNumber',
|
||||||
|
minWidth: 130,
|
||||||
|
addDisplay: false,
|
||||||
|
editDisplay: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '是否同步金蝶',
|
||||||
|
prop: 'kingdeeSynced',
|
||||||
|
type: 'select',
|
||||||
|
search: true,
|
||||||
|
searchPlaceholder: '请选择',
|
||||||
|
searchOrder: 2,
|
||||||
|
dicData: yesNoOptions,
|
||||||
|
hide: true,
|
||||||
|
addDisplay: false,
|
||||||
|
editDisplay: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '是否同步合规',
|
||||||
|
prop: 'hcsSynced',
|
||||||
|
type: 'select',
|
||||||
|
search: true,
|
||||||
|
searchPlaceholder: '请选择',
|
||||||
|
searchOrder: 3,
|
||||||
|
dicData: yesNoOptions,
|
||||||
|
hide: true,
|
||||||
|
addDisplay: false,
|
||||||
|
editDisplay: false,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: '生效类型',
|
label: '生效类型',
|
||||||
prop: 'effectiveType',
|
prop: 'effectiveType',
|
||||||
|
|||||||
@@ -205,7 +205,9 @@ export const defaultContractAttachmentTypeOptions = [
|
|||||||
{ label: '双章归档文件', value: '双章归档文件' },
|
{ label: '双章归档文件', value: '双章归档文件' },
|
||||||
{ label: '其他文件', value: CONTRACT_ATTACHMENT_TYPE_OTHER },
|
{ label: '其他文件', value: CONTRACT_ATTACHMENT_TYPE_OTHER },
|
||||||
];
|
];
|
||||||
|
// 特批附件归口附件(佐证材料等)区上传:高风险/黑名单客商合同必传,后端按 fileType=特批附件 校验并挂特批面板(与后端 HcsSyncConstant.ATTACHMENT_TYPE_SPEC_EXPLANATION 对齐)
|
||||||
export const defaultContractSupportingAttachmentTypeOptions = [
|
export const defaultContractSupportingAttachmentTypeOptions = [
|
||||||
|
{ label: '特批附件', value: '特批附件' },
|
||||||
{ label: '其他文件', value: CONTRACT_ATTACHMENT_TYPE_OTHER },
|
{ label: '其他文件', value: CONTRACT_ATTACHMENT_TYPE_OTHER },
|
||||||
];
|
];
|
||||||
/** @deprecated 兼容旧引用,实际选项由业务字典动态加载 */
|
/** @deprecated 兼容旧引用,实际选项由业务字典动态加载 */
|
||||||
@@ -299,6 +301,8 @@ export const resolveContractAttachmentType = (
|
|||||||
item => String(item.label) === aliasedFileType || String(item.label) === rawFileType
|
item => String(item.label) === aliasedFileType || String(item.label) === rawFileType
|
||||||
);
|
);
|
||||||
if (matchedByLabel) return matchedByLabel.value;
|
if (matchedByLabel) return matchedByLabel.value;
|
||||||
|
// 行已带类型但字典未命中(如历史保存值、字典缺失)时保留原值,避免重开页面时被降级为其他文件
|
||||||
|
if (rawFileType) return aliasedFileType;
|
||||||
const fileName = normalizeAttachmentTypeText(attachmentName(row));
|
const fileName = normalizeAttachmentTypeText(attachmentName(row));
|
||||||
if (!fileName) return otherType;
|
if (!fileName) return otherType;
|
||||||
const matched = list
|
const matched = list
|
||||||
@@ -641,7 +645,7 @@ export default {
|
|||||||
this.contractAttachmentTypeOptions
|
this.contractAttachmentTypeOptions
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this.uploadDialogType = this.defaultAttachmentType;
|
this.uploadDialogType = this.uploadDialogType || this.defaultAttachmentType;
|
||||||
}
|
}
|
||||||
this.$message.success('上传成功');
|
this.$message.success('上传成功');
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,271 @@
|
|||||||
|
<template>
|
||||||
|
<section class="contract-manage-form__section contract-manage-form__section--panel hcs-contract-panel">
|
||||||
|
<div class="contract-manage-form__attachment-head">
|
||||||
|
<div class="dialog-section-title">合规信息</div>
|
||||||
|
<div>
|
||||||
|
<el-button
|
||||||
|
v-if="canApplySpec"
|
||||||
|
type="warning"
|
||||||
|
plain
|
||||||
|
:loading="specApplying"
|
||||||
|
@click="openSpecDialog"
|
||||||
|
>
|
||||||
|
发起特批
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
v-if="isSpecProcessing"
|
||||||
|
type="primary"
|
||||||
|
plain
|
||||||
|
@click="confirmSpecResult"
|
||||||
|
>
|
||||||
|
确认特批结果
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-descriptions :column="3" class="contract-manage-detail__descriptions">
|
||||||
|
<el-descriptions-item label="合规同步状态">
|
||||||
|
<el-tooltip
|
||||||
|
v-if="hcsSyncMsg"
|
||||||
|
placement="top"
|
||||||
|
:content="hcsSyncMsg"
|
||||||
|
>
|
||||||
|
<el-tag :type="syncTagType" class="status-text">{{ syncLabel }}</el-tag>
|
||||||
|
</el-tooltip>
|
||||||
|
<el-tag v-else :type="syncTagType" class="status-text">{{ syncLabel }}</el-tag>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="合规合同ID">{{
|
||||||
|
contract.hcsFdId || '-'
|
||||||
|
}}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="客商风险">
|
||||||
|
<template v-if="contract.hcsRiskLevel || contract.hcsBlackStatus">
|
||||||
|
<el-tag
|
||||||
|
v-if="isBlacklist"
|
||||||
|
type="danger"
|
||||||
|
class="status-text"
|
||||||
|
>黑名单</el-tag
|
||||||
|
>
|
||||||
|
<span v-if="contract.hcsRiskLevel" class="hcs-contract-panel__risk">{{
|
||||||
|
`风险等级:${contract.hcsRiskLevel}`
|
||||||
|
}}</span>
|
||||||
|
<span v-if="!contract.hcsRiskLevel && !isBlacklist">-</span>
|
||||||
|
</template>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="特批状态">
|
||||||
|
<el-tag v-if="specLabel" :type="specTagType" class="status-text">{{ specLabel }}</el-tag>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="特批说明" :span="2">{{
|
||||||
|
contract.hcsSpecMsg || '-'
|
||||||
|
}}</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
|
||||||
|
<el-dialog
|
||||||
|
v-model="specDialogVisible"
|
||||||
|
title="发起合同特批"
|
||||||
|
width="640px"
|
||||||
|
append-to-body
|
||||||
|
destroy-on-close
|
||||||
|
class="hcs-contract-panel__spec-dialog"
|
||||||
|
@closed="resetSpecDialog"
|
||||||
|
>
|
||||||
|
<el-alert
|
||||||
|
title="高风险客商合同需经集团特批后方可签约推送;请上传特批申请材料及内部审批流程附件。"
|
||||||
|
type="warning"
|
||||||
|
:closable="false"
|
||||||
|
class="hcs-contract-panel__spec-alert"
|
||||||
|
/>
|
||||||
|
<el-upload
|
||||||
|
drag
|
||||||
|
multiple
|
||||||
|
:show-file-list="false"
|
||||||
|
:http-request="uploadMaterial"
|
||||||
|
:on-error="() => uploadFailed()"
|
||||||
|
class="hcs-contract-panel__spec-upload"
|
||||||
|
>
|
||||||
|
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
|
||||||
|
<div class="el-upload__text">将特批材料拖到此处,或<em>点击上传到合同系统</em></div>
|
||||||
|
</el-upload>
|
||||||
|
<el-table :data="materials" border class="hcs-contract-panel__spec-table">
|
||||||
|
<el-table-column type="index" label="序号" width="70" align="center" />
|
||||||
|
<el-table-column prop="name" label="文件名" min-width="220" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="fdId" label="合规附件ID" min-width="240" show-overflow-tooltip />
|
||||||
|
<el-table-column label="操作" width="90" align="center" fixed="right">
|
||||||
|
<template #default="{ $index }">
|
||||||
|
<el-link type="danger" @click="materials.splice($index, 1)">删除</el-link>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<template #footer>
|
||||||
|
<el-button type="primary" :loading="specApplying" @click="submitSpec">确认发起</el-button>
|
||||||
|
<el-button @click="specDialogVisible = false">取消</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { UploadFilled } from '@element-plus/icons-vue';
|
||||||
|
import {
|
||||||
|
applyContractSpec,
|
||||||
|
confirmContractSpec,
|
||||||
|
uploadAttachment,
|
||||||
|
} from '@/api/business/hcs';
|
||||||
|
|
||||||
|
const SYNC_STATUS_OPTIONS = [
|
||||||
|
{ label: '待推送', value: 'unsynced', type: 'info' },
|
||||||
|
{ label: '已推送', value: 'synced', type: 'success' },
|
||||||
|
{ label: '推送失败', value: 'failed', type: 'danger' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const SPEC_STATUS_OPTIONS = [
|
||||||
|
{ label: '待特批', value: 'need', type: 'warning' },
|
||||||
|
{ label: '特批中', value: 'processing', type: 'warning' },
|
||||||
|
{ label: '特批通过', value: 'approved', type: 'success' },
|
||||||
|
{ label: '特批驳回', value: 'rejected', type: 'danger' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'HcsContractPanel',
|
||||||
|
components: { UploadFilled },
|
||||||
|
props: {
|
||||||
|
// 合同详情行(须含 id/hcsSyncStatus/hcsFdId/hcsRiskLevel/hcsBlackStatus/hcsSpecStatus 等)
|
||||||
|
contract: { type: Object, required: true },
|
||||||
|
},
|
||||||
|
emits: ['refresh'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
specDialogVisible: false,
|
||||||
|
specApplying: false,
|
||||||
|
materials: [],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
syncLabel() {
|
||||||
|
return SYNC_STATUS_OPTIONS.find(item => item.value === this.contract.hcsSyncStatus)?.label || '-';
|
||||||
|
},
|
||||||
|
syncTagType() {
|
||||||
|
return SYNC_STATUS_OPTIONS.find(item => item.value === this.contract.hcsSyncStatus)?.type || 'info';
|
||||||
|
},
|
||||||
|
hcsSyncMsg() {
|
||||||
|
return this.contract.hcsSyncMsg || '';
|
||||||
|
},
|
||||||
|
isBlacklist() {
|
||||||
|
return String(this.contract.hcsBlackStatus || '').toLowerCase() === 'yes';
|
||||||
|
},
|
||||||
|
specLabel() {
|
||||||
|
return SPEC_STATUS_OPTIONS.find(item => item.value === this.contract.hcsSpecStatus)?.label || '';
|
||||||
|
},
|
||||||
|
specTagType() {
|
||||||
|
return SPEC_STATUS_OPTIONS.find(item => item.value === this.contract.hcsSpecStatus)?.type || 'info';
|
||||||
|
},
|
||||||
|
isSpecProcessing() {
|
||||||
|
return this.contract.hcsSpecStatus === 'processing';
|
||||||
|
},
|
||||||
|
canApplySpec() {
|
||||||
|
return ['need', 'rejected'].includes(this.contract.hcsSpecStatus);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
openSpecDialog() {
|
||||||
|
this.specDialogVisible = true;
|
||||||
|
},
|
||||||
|
resetSpecDialog() {
|
||||||
|
this.materials = [];
|
||||||
|
},
|
||||||
|
// 自定义上传:直传合同合规系统(C103-2),成功记录合规附件ID
|
||||||
|
uploadMaterial(options) {
|
||||||
|
const file = options.file;
|
||||||
|
if (file.size / 1024 / 1024 > 50) {
|
||||||
|
this.$message.warning('单文件大小不能超过 50MB');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('fdFile', file);
|
||||||
|
uploadAttachment(formData)
|
||||||
|
.then(response => {
|
||||||
|
const fdId = response?.data?.data || response?.data;
|
||||||
|
if (!fdId || typeof fdId !== 'string') {
|
||||||
|
this.$message.error(response?.data?.msg || '上传合同系统失败');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.materials.push({ fdId, name: file.name });
|
||||||
|
this.$message.success(`已上传合同系统:${file.name}`);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
this.$message.error(error?.response?.data?.msg || '上传合同系统失败');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
uploadFailed() {
|
||||||
|
this.$message.error('上传合同系统失败');
|
||||||
|
},
|
||||||
|
submitSpec() {
|
||||||
|
if (!this.materials.length) {
|
||||||
|
this.$message.warning('请先上传特批申请材料');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.specApplying = true;
|
||||||
|
applyContractSpec({
|
||||||
|
contractId: this.contract.id,
|
||||||
|
attachments: this.materials.map(item => ({ fdId: item.fdId, name: item.name })),
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
if (response?.data?.success === false) {
|
||||||
|
this.$message.error(response?.data?.msg || '特批发起失败');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.$message.success(response?.data?.data || '特批发起成功,等待集团审批');
|
||||||
|
this.specDialogVisible = false;
|
||||||
|
this.$emit('refresh');
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
this.$message.error(error?.response?.data?.msg || '特批发起失败');
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.specApplying = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
confirmSpecResult() {
|
||||||
|
this.$prompt('请输入集团特批结果(approved=通过 / rejected=驳回)', '确认特批结果', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
inputPattern: /^(approved|rejected)$/,
|
||||||
|
inputErrorMessage: '请输入 approved 或 rejected',
|
||||||
|
}).then(({ value }) => {
|
||||||
|
confirmContractSpec(this.contract.id, value).then(() => {
|
||||||
|
this.$message.success('特批结果已确认');
|
||||||
|
this.$emit('refresh');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.hcs-contract-panel__risk {
|
||||||
|
margin-left: 8px;
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hcs-contract-panel__spec-alert {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hcs-contract-panel__spec-upload {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
|
||||||
|
:deep(.el-upload) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-upload-dragger) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hcs-contract-panel__spec-table {
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -203,6 +203,11 @@
|
|||||||
placeholder="请输入其他事项说明"
|
placeholder="请输入其他事项说明"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<!-- 风险客商提示:合同已推送合规并留痕高风险/黑名单时展示,变更提交同样要求特批附件 -->
|
||||||
|
<div v-if="riskTipText" class="contract-manage-form__risk-tip">
|
||||||
|
<div class="contract-manage-form__risk-tip-title">风险客商提示:</div>
|
||||||
|
<div class="contract-manage-form__risk-tip-content">{{ riskTipText }}</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<contract-attachment-section
|
<contract-attachment-section
|
||||||
@@ -489,6 +494,17 @@ export default {
|
|||||||
pageTitle() {
|
pageTitle() {
|
||||||
return this.$route.query.name || '合同变更';
|
return this.$route.query.name || '合同变更';
|
||||||
},
|
},
|
||||||
|
// 高风险/黑名单客商提示:按合同推送合规留痕 hcs_risk_level/hcs_black_status 判定(与合同详情口径一致)
|
||||||
|
riskTipText() {
|
||||||
|
const riskLevel = String(this.form.hcsRiskLevel || '').trim();
|
||||||
|
const blacklist = String(this.form.hcsBlackStatus || '').toLowerCase() === 'yes';
|
||||||
|
if (riskLevel !== '2' && !blacklist) return '';
|
||||||
|
const parties = [this.form.partyA, this.form.partyB]
|
||||||
|
.map(name => String(name || '').trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
.join('、');
|
||||||
|
return `【${parties || '相对方'}】:被列入外部黑名单;涉贪污受贿裁判相关提及方(3年以上及其他);被执行人信息;买卖合同纠纷;劳动纠纷;重大纠纷。提交变更前须上传「特批附件」类型附件`;
|
||||||
|
},
|
||||||
changeAttachmentLocationOptions() {
|
changeAttachmentLocationOptions() {
|
||||||
return ['用印材料', '附件(佐证材料等)', '变更材料'];
|
return ['用印材料', '附件(佐证材料等)', '变更材料'];
|
||||||
},
|
},
|
||||||
@@ -762,6 +778,10 @@ export default {
|
|||||||
}
|
}
|
||||||
.contract-basic-section__date-range { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); align-items: center; gap: 8px; width: 360px; max-width: 100%; }
|
.contract-basic-section__date-range { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); align-items: center; gap: 8px; width: 360px; max-width: 100%; }
|
||||||
.contract-basic-section__date-range span { color: #606266; }
|
.contract-basic-section__date-range span { color: #606266; }
|
||||||
|
// 风险客商提示条(基本信息备注后):橙黄背景 + 红色文字,与合同新增页一致
|
||||||
|
.contract-manage-form__risk-tip { margin: 0 0 18px; padding: 10px 16px; background: #faecd8; border-radius: 4px; }
|
||||||
|
.contract-manage-form__risk-tip-title { color: #f56c6c; font-weight: 600; line-height: 22px; }
|
||||||
|
.contract-manage-form__risk-tip-content { color: #f56c6c; line-height: 22px; }
|
||||||
.contract-basic-section__date-range :deep(.el-date-editor) { width: auto; min-width: 0; }
|
.contract-basic-section__date-range :deep(.el-date-editor) { width: auto; min-width: 0; }
|
||||||
.contract-basic-section :deep(.el-form-item) { align-items: center; }
|
.contract-basic-section :deep(.el-form-item) { align-items: center; }
|
||||||
.contract-basic-section :deep(.el-form-item__label) { flex: 0 0 96px !important; width: 96px !important; height: auto; padding-right: 12px; white-space: normal; word-break: break-all; line-height: 18px; }
|
.contract-basic-section :deep(.el-form-item__label) { flex: 0 0 96px !important; width: 96px !important; height: auto; padding-right: 12px; white-space: normal; word-break: break-all; line-height: 18px; }
|
||||||
|
|||||||
@@ -32,6 +32,14 @@
|
|||||||
>
|
>
|
||||||
批量导出
|
批量导出
|
||||||
</el-button>
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="warning"
|
||||||
|
plain
|
||||||
|
:loading="hcsRepushingId === 'batch'"
|
||||||
|
@click="handleHcsBatchRepush"
|
||||||
|
>
|
||||||
|
<el-icon class="el-icon--left"><Position /></el-icon>批量推送合规
|
||||||
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #header>
|
<template #header>
|
||||||
@@ -72,6 +80,18 @@
|
|||||||
</el-tag>
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template #hcsSyncStatus="{ row }">
|
||||||
|
<el-tooltip v-if="row.hcsSyncMsg" placement="top" :content="row.hcsSyncMsg">
|
||||||
|
<el-tag :type="hcsSyncTagType(row.hcsSyncStatus)" class="status-text">
|
||||||
|
{{ hcsSyncLabel(row.hcsSyncStatus) }}
|
||||||
|
</el-tag>
|
||||||
|
</el-tooltip>
|
||||||
|
<el-tag v-else-if="row.hcsSyncStatus" :type="hcsSyncTagType(row.hcsSyncStatus)" class="status-text">
|
||||||
|
{{ hcsSyncLabel(row.hcsSyncStatus) }}
|
||||||
|
</el-tag>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
<template #menu="{ row }">
|
<template #menu="{ row }">
|
||||||
<el-link
|
<el-link
|
||||||
v-if="hasPermission(`${config.permission}_view`)"
|
v-if="hasPermission(`${config.permission}_view`)"
|
||||||
@@ -92,6 +112,9 @@
|
|||||||
>
|
>
|
||||||
{{ operation.label }}
|
{{ operation.label }}
|
||||||
</el-link>
|
</el-link>
|
||||||
|
<el-link v-if="canHcsRepush(row)" type="warning" @click="handleHcsRepush(row)">
|
||||||
|
{{ hcsRepushingId === row.id ? '推送中…' : '推送合规' }}
|
||||||
|
</el-link>
|
||||||
<el-link v-if="canDelete(row)" type="danger" @click="removeRow(row)">删除</el-link>
|
<el-link v-if="canDelete(row)" type="danger" @click="removeRow(row)">删除</el-link>
|
||||||
</template>
|
</template>
|
||||||
</avue-crud>
|
</avue-crud>
|
||||||
@@ -180,9 +203,18 @@
|
|||||||
<el-descriptions-item label="其他事项说明" :span="3">{{
|
<el-descriptions-item label="其他事项说明" :span="3">{{
|
||||||
detailValue('remark')
|
detailValue('remark')
|
||||||
}}</el-descriptions-item>
|
}}</el-descriptions-item>
|
||||||
|
<el-descriptions-item v-if="detailRiskTipText" label="风险客商提示" :span="3">
|
||||||
|
<span style="color: #f56c6c">{{ detailRiskTipText }}</span>
|
||||||
|
</el-descriptions-item>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<hcs-contract-panel
|
||||||
|
v-if="!isPublicViewPage"
|
||||||
|
:contract="detailRow"
|
||||||
|
@refresh="initDetailPage"
|
||||||
|
/>
|
||||||
|
|
||||||
<contract-attachment-section
|
<contract-attachment-section
|
||||||
title="用印材料"
|
title="用印材料"
|
||||||
description
|
description
|
||||||
@@ -302,6 +334,7 @@
|
|||||||
<contract-attachment-section
|
<contract-attachment-section
|
||||||
title="附件(佐证材料等)"
|
title="附件(佐证材料等)"
|
||||||
description
|
description
|
||||||
|
attachment-type
|
||||||
readonly
|
readonly
|
||||||
:rows="detailAttachmentRows"
|
:rows="detailAttachmentRows"
|
||||||
:preview="previewAttachment"
|
:preview="previewAttachment"
|
||||||
@@ -448,6 +481,7 @@
|
|||||||
clearable
|
clearable
|
||||||
:loading="contractPartyLoading"
|
:loading="contractPartyLoading"
|
||||||
@visible-change="visible => visible && loadContractPartyOptions()"
|
@visible-change="visible => visible && loadContractPartyOptions()"
|
||||||
|
@change="queryPartyRisk('partyA')"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in contractPartyAOptions"
|
v-for="item in contractPartyAOptions"
|
||||||
@@ -465,6 +499,7 @@
|
|||||||
clearable
|
clearable
|
||||||
:loading="contractPartyLoading"
|
:loading="contractPartyLoading"
|
||||||
@visible-change="visible => visible && loadContractPartyOptions()"
|
@visible-change="visible => visible && loadContractPartyOptions()"
|
||||||
|
@change="queryPartyRisk('partyB')"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in contractPartyBOptions"
|
v-for="item in contractPartyBOptions"
|
||||||
@@ -619,6 +654,11 @@
|
|||||||
placeholder="请输入其他事项说明"
|
placeholder="请输入其他事项说明"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<!-- 风险客商提示(基本信息备注后):外部客商高风险/黑名单时展示,文案由后端 riskTip 拼装 -->
|
||||||
|
<div v-if="partyRiskText" class="contract-manage-form__risk-tip">
|
||||||
|
<div class="contract-manage-form__risk-tip-title">风险客商提示:</div>
|
||||||
|
<div class="contract-manage-form__risk-tip-content">{{ partyRiskText }}</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<contract-attachment-section
|
<contract-attachment-section
|
||||||
@@ -1046,6 +1086,8 @@ import NProgress from 'nprogress';
|
|||||||
import 'nprogress/nprogress.css';
|
import 'nprogress/nprogress.css';
|
||||||
import { exportBlob } from '@/api/common';
|
import { exportBlob } from '@/api/common';
|
||||||
import * as api from '@/api/business/contract-manage';
|
import * as api from '@/api/business/contract-manage';
|
||||||
|
import { repush as hcsRepush, repushBatch as hcsRepushBatch, getRisk } from '@/api/business/hcs';
|
||||||
|
import { Position } from '@element-plus/icons-vue';
|
||||||
import { getMkPublicDetail } from '@/api/mk-process';
|
import { getMkPublicDetail } from '@/api/mk-process';
|
||||||
import { getList as getProjectList } from '@/api/business/project-apply';
|
import { getList as getProjectList } from '@/api/business/project-apply';
|
||||||
import { getList as getCustomerArchiveList } from '@/api/vehicle/customer-archive';
|
import { getList as getCustomerArchiveList } from '@/api/vehicle/customer-archive';
|
||||||
@@ -1066,6 +1108,7 @@ import ContractAttachmentSection, {
|
|||||||
isContractSupportingAttachmentLocation,
|
isContractSupportingAttachmentLocation,
|
||||||
normalizeContractFileRows,
|
normalizeContractFileRows,
|
||||||
} from './components/contract-attachment-section.vue';
|
} from './components/contract-attachment-section.vue';
|
||||||
|
import HcsContractPanel from './components/hcs-contract-panel.vue';
|
||||||
import PdfPreview from '@/components/pdf-preview/main.vue';
|
import PdfPreview from '@/components/pdf-preview/main.vue';
|
||||||
import { ElImageViewer, ElCascader } from 'element-plus';
|
import { ElImageViewer, ElCascader } from 'element-plus';
|
||||||
import { OpenFileViewer } from '@open-file-viewer/vue';
|
import { OpenFileViewer } from '@open-file-viewer/vue';
|
||||||
@@ -1171,6 +1214,11 @@ const createCopiedDetail = (detail = {}) => {
|
|||||||
Object.keys(form).forEach(prop => {
|
Object.keys(form).forEach(prop => {
|
||||||
if (Object.prototype.hasOwnProperty.call(detail, prop)) form[prop] = detail[prop];
|
if (Object.prototype.hasOwnProperty.call(detail, prop)) form[prop] = detail[prop];
|
||||||
});
|
});
|
||||||
|
// 特批附件属于原合同的特批佐证,复制后须对新合同重新上传,否则会绕过高风险/黑名单校验
|
||||||
|
const stripSpecAttachments = value => {
|
||||||
|
const rows = parseArray(value).filter(item => item?.fileType !== '特批附件');
|
||||||
|
return rows.length ? JSON.stringify(rows) : '';
|
||||||
|
};
|
||||||
return {
|
return {
|
||||||
...form,
|
...form,
|
||||||
contractNo: '',
|
contractNo: '',
|
||||||
@@ -1178,8 +1226,8 @@ const createCopiedDetail = (detail = {}) => {
|
|||||||
archiveStatus: '未归档',
|
archiveStatus: '未归档',
|
||||||
feeGenerationMode: detail.feeGenerationMode || '',
|
feeGenerationMode: detail.feeGenerationMode || '',
|
||||||
billingEnabled: detail.billingEnabled,
|
billingEnabled: detail.billingEnabled,
|
||||||
contractFileJson: detail.contractFileJson || '',
|
contractFileJson: stripSpecAttachments(detail.contractFileJson),
|
||||||
attachmentsJson: detail.attachmentsJson || '',
|
attachmentsJson: stripSpecAttachments(detail.attachmentsJson),
|
||||||
billingPlanJson: detail.billingPlanJson || '',
|
billingPlanJson: detail.billingPlanJson || '',
|
||||||
paymentRatioJson: detail.paymentRatioJson || '',
|
paymentRatioJson: detail.paymentRatioJson || '',
|
||||||
settlementRuleJson: detail.settlementRuleJson || '',
|
settlementRuleJson: detail.settlementRuleJson || '',
|
||||||
@@ -1238,9 +1286,11 @@ export default {
|
|||||||
ContractAttachmentSection,
|
ContractAttachmentSection,
|
||||||
BillingPlanEditor,
|
BillingPlanEditor,
|
||||||
ChangeRecordDetailDialog,
|
ChangeRecordDetailDialog,
|
||||||
|
HcsContractPanel,
|
||||||
ElImageViewer,
|
ElImageViewer,
|
||||||
OpenFileViewer,
|
OpenFileViewer,
|
||||||
PdfPreview,
|
PdfPreview,
|
||||||
|
Position,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -1316,6 +1366,9 @@ export default {
|
|||||||
contractCategoryDictOptions: [],
|
contractCategoryDictOptions: [],
|
||||||
contractPartyAOptions: [],
|
contractPartyAOptions: [],
|
||||||
contractPartyBOptions: [],
|
contractPartyBOptions: [],
|
||||||
|
// 合规客商风险(C101-1 展示用):甲方/乙方选择后即时查询
|
||||||
|
contractPartyCodeMap: {},
|
||||||
|
partyRisk: {},
|
||||||
contractPartyLoading: false,
|
contractPartyLoading: false,
|
||||||
contractPartyRequestId: 0,
|
contractPartyRequestId: 0,
|
||||||
organizationOptions: [],
|
organizationOptions: [],
|
||||||
@@ -1354,6 +1407,8 @@ export default {
|
|||||||
billCycleTypeOptions: ['固定截单日', '自然月', '自定义多周期'],
|
billCycleTypeOptions: ['固定截单日', '自然月', '自定义多周期'],
|
||||||
detailLoading: false,
|
detailLoading: false,
|
||||||
detailRow: {},
|
detailRow: {},
|
||||||
|
// 合规(HCS)推送/对账状态
|
||||||
|
hcsRepushingId: null,
|
||||||
detailContractFileRows: [],
|
detailContractFileRows: [],
|
||||||
detailAttachmentRows: [],
|
detailAttachmentRows: [],
|
||||||
detailBillingPlanRows: [],
|
detailBillingPlanRows: [],
|
||||||
@@ -1530,6 +1585,32 @@ export default {
|
|||||||
const endDate = this.detailRow.endDate || '-';
|
const endDate = this.detailRow.endDate || '-';
|
||||||
return `${startDate} 至 ${endDate}`;
|
return `${startDate} 至 ${endDate}`;
|
||||||
},
|
},
|
||||||
|
// 高风险/黑名单客商签约提示(C101-1,签约前提示;强制拦截在后端提交校验与推送链路)
|
||||||
|
partyRiskText() {
|
||||||
|
const parts = [];
|
||||||
|
['partyA', 'partyB'].forEach(prop => {
|
||||||
|
const risk = this.partyRisk[prop];
|
||||||
|
if (!risk?.risky) return;
|
||||||
|
// riskTip 由后端按风险项拼装(【公司名称】:黑名单;涉诉;被执行人等),缺失时兜底通用提示
|
||||||
|
parts.push(
|
||||||
|
risk.riskTip ||
|
||||||
|
`${prop === 'partyA' ? '甲方' : '乙方'}[${this.form[prop] || ''}]为高风险/黑名单客商`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
if (!parts.length) return '';
|
||||||
|
return `${parts.join(';')},按集团规定不可直接签约,提交前须上传「特批附件」类型附件,审批通过后将自动发起合同特批`;
|
||||||
|
},
|
||||||
|
// 详情风险客商提示(基本信息备注后展示):按推送留痕 hcs_risk_level/hcs_black_status 判定(甲乙取高/任一),文案与后端 riskTip 一致
|
||||||
|
detailRiskTipText() {
|
||||||
|
const riskLevel = String(this.detailRow.hcsRiskLevel || '').trim();
|
||||||
|
const blacklist = String(this.detailRow.hcsBlackStatus || '').toLowerCase() === 'yes';
|
||||||
|
if (riskLevel !== '2' && !blacklist) return '';
|
||||||
|
const parties = [this.detailRow.partyA, this.detailRow.partyB]
|
||||||
|
.map(name => String(name || '').trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
.join('、');
|
||||||
|
return `【${parties || '相对方'}】:被列入外部黑名单;涉贪污受贿裁判相关提及方(3年以上及其他);被执行人信息;买卖合同纠纷;劳动纠纷;重大纠纷`;
|
||||||
|
},
|
||||||
detailLegalSealText() {
|
detailLegalSealText() {
|
||||||
const value = this.detailRow.legalSealFlag;
|
const value = this.detailRow.legalSealFlag;
|
||||||
if (value === undefined || value === null || value === '') return '-';
|
if (value === undefined || value === null || value === '') return '-';
|
||||||
@@ -1651,6 +1732,84 @@ export default {
|
|||||||
if (stage === 'terminated') return 'danger';
|
if (stage === 'terminated') return 'danger';
|
||||||
return 'info';
|
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'
|
||||||
|
);
|
||||||
|
},
|
||||||
|
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 => {
|
||||||
|
// 拦截器已弹出后端 msg(如特批审核中禁止重推),此处不再重复提示
|
||||||
|
if (!error?._notified) {
|
||||||
|
this.$message.error(error?.response?.data?.msg || '推送合规失败');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.hcsRepushingId = null;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleHcsBatchRepush() {
|
||||||
|
if (this.hcsRepushingId) return;
|
||||||
|
if (!this.selectionList.length) {
|
||||||
|
this.$message.warning('请选择至少一条数据');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const invalidCount = this.selectionList.filter(item => !this.canHcsRepush(item)).length;
|
||||||
|
if (invalidCount) {
|
||||||
|
this.$message.warning(`选中数据中有${invalidCount}条不符合推送条件(仅审批通过且未同步的可推送),请重新选择`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.$confirm(`确定将选中的${this.selectionList.length}条数据推送到合同合规系统?`, '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning',
|
||||||
|
}).then(() => {
|
||||||
|
this.hcsRepushingId = 'batch';
|
||||||
|
hcsRepushBatch('contract', this.selectionList.map(item => item.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 => {
|
||||||
|
// 拦截器已弹出后端 msg,此处不再重复提示
|
||||||
|
if (!error?._notified) {
|
||||||
|
this.$message.error(error?.response?.data?.msg || '批量推送合规失败');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.hcsRepushingId = null;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
canEdit(row) {
|
canEdit(row) {
|
||||||
return (
|
return (
|
||||||
this.hasPermission(`${this.config.permission}_edit`) &&
|
this.hasPermission(`${this.config.permission}_edit`) &&
|
||||||
@@ -2209,8 +2368,21 @@ export default {
|
|||||||
])
|
])
|
||||||
.then(responses => {
|
.then(responses => {
|
||||||
if (requestId !== this.contractPartyRequestId) return;
|
if (requestId !== this.contractPartyRequestId) return;
|
||||||
const names = responses
|
const records = responses.flatMap(extractRecords);
|
||||||
.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(item => item.fullName || item.shortName || item.customerName || item.customerCode)
|
||||||
.map(name => String(name || '').trim())
|
.map(name => String(name || '').trim())
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
@@ -2218,11 +2390,40 @@ export default {
|
|||||||
const options = [...new Set(names)].map(name => ({ label: name, value: name }));
|
const options = [...new Set(names)].map(name => ({ label: name, value: name }));
|
||||||
this.contractPartyAOptions = options;
|
this.contractPartyAOptions = options;
|
||||||
this.contractPartyBOptions = options;
|
this.contractPartyBOptions = options;
|
||||||
|
// 复制/编辑回填的甲乙方不会触发下拉 change,加载选项后主动查一次 C101-1 风险
|
||||||
|
['partyA', 'partyB'].forEach(prop => {
|
||||||
|
if (this.form[prop]) this.queryPartyRisk(prop);
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
if (requestId === this.contractPartyRequestId) this.contractPartyLoading = false;
|
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]: {
|
||||||
|
customerKind: risk.customerKind || '',
|
||||||
|
// risky 由后端统一判定:外部客商高风险(等级=2)或黑名单;内部组织客商默认无风险恒为 false
|
||||||
|
risky: risk.risky === true,
|
||||||
|
riskTip: risk.riskTip || '',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.partyRisk = { ...this.partyRisk, [prop]: null };
|
||||||
|
});
|
||||||
|
},
|
||||||
loadOrganizationOptions() {
|
loadOrganizationOptions() {
|
||||||
if (this.organizationLoading) return;
|
if (this.organizationLoading) return;
|
||||||
this.organizationLoading = true;
|
this.organizationLoading = true;
|
||||||
@@ -3004,10 +3205,26 @@ export default {
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
: request();
|
: request();
|
||||||
afterMk.then(() => {
|
// 合同通过会同步等待附件转传 + 合规推送,加遮罩防重复点击
|
||||||
|
const loading =
|
||||||
|
operation.action === 'approve'
|
||||||
|
? this.$loading({
|
||||||
|
lock: true,
|
||||||
|
text: '正在推送合规,请稍候…',
|
||||||
|
background: 'rgba(255, 255, 255, 0.6)',
|
||||||
|
})
|
||||||
|
: null;
|
||||||
|
afterMk
|
||||||
|
.then(res => {
|
||||||
|
// data=false 表示审批已通过但合规推送失败,msg 带具体原因(照正式结算单审批)
|
||||||
|
if (operation.action === 'approve' && res?.data?.data === false) {
|
||||||
|
this.$message.warning(res.data.msg);
|
||||||
|
} else {
|
||||||
this.$message.success(operation.successMessage || `${operation.label}成功`);
|
this.$message.success(operation.successMessage || `${operation.label}成功`);
|
||||||
|
}
|
||||||
this.onLoad(this.page, this.query);
|
this.onLoad(this.page, this.query);
|
||||||
});
|
})
|
||||||
|
.finally(() => loading && loading.close());
|
||||||
};
|
};
|
||||||
if (operation.prompt) {
|
if (operation.prompt) {
|
||||||
this.$prompt(operation.prompt, operation.label, {
|
this.$prompt(operation.prompt, operation.label, {
|
||||||
@@ -3104,6 +3321,29 @@ export default {
|
|||||||
vertical-align: bottom;
|
vertical-align: bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.contract-manage-form__risk-alert {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 风险客商提示条(基本信息备注后):橙黄背景 + 红色文字(设计稿 2026-10)
|
||||||
|
.contract-manage-form__risk-tip {
|
||||||
|
margin: 0 0 18px;
|
||||||
|
padding: 10px 16px;
|
||||||
|
background: #faecd8;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
.contract-manage-form__risk-tip-title {
|
||||||
|
color: #f56c6c;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contract-manage-form__risk-tip-content {
|
||||||
|
color: #f56c6c;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.contract-manage-page {
|
.contract-manage-page {
|
||||||
&__expiry-search {
|
&__expiry-search {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
<template>
|
||||||
|
<basic-container class="hcs-master-data-page">
|
||||||
|
<div class="hcs-master-data-page__head">
|
||||||
|
<div class="dialog-section-title">合规主数据(HCS)</div>
|
||||||
|
<el-button type="primary" :loading="refreshing" @click="handleRefresh">刷新缓存</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-tabs v-model="activeTab">
|
||||||
|
<el-tab-pane label="合同模板" name="template" />
|
||||||
|
</el-tabs>
|
||||||
|
|
||||||
|
<el-table v-loading="loading" :data="templates" border>
|
||||||
|
<el-table-column type="index" label="序号" width="70" align="center" />
|
||||||
|
<el-table-column prop="fdId" label="模板ID(fdTemplateId)" min-width="320" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="fdName" label="模板名称" min-width="220" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="fdStatus" label="状态" width="100" align="center" />
|
||||||
|
<el-table-column prop="fdContractCharacter" label="合同性质" min-width="140" align="center" />
|
||||||
|
<el-table-column label="创建时间" min-width="170" align="center">
|
||||||
|
<template #default="{ row }">{{ formatTime(row.fdCreateTime) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<el-alert
|
||||||
|
class="hcs-master-data-page__tip"
|
||||||
|
title="合同类别与合规模板的对应关系由 Nacos 配置 thirdParty.hcs.contract.template-mapping 维护;此列表用于核对映射的模板ID 是否存在。"
|
||||||
|
type="info"
|
||||||
|
:closable="false"
|
||||||
|
/>
|
||||||
|
</basic-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getTemplates, refreshMasterData } from '@/api/business/hcs';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'HcsMasterData',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
activeTab: 'template',
|
||||||
|
loading: false,
|
||||||
|
refreshing: false,
|
||||||
|
templates: [],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.loadTemplates();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
loadTemplates() {
|
||||||
|
this.loading = true;
|
||||||
|
getTemplates()
|
||||||
|
.then(response => {
|
||||||
|
const data = response?.data?.data ?? response?.data;
|
||||||
|
this.templates = Array.isArray(data) ? data : [];
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.$message.error('合规模板列表加载失败');
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleRefresh() {
|
||||||
|
this.refreshing = true;
|
||||||
|
refreshMasterData()
|
||||||
|
.then(response => {
|
||||||
|
this.$message.success(response?.data?.data || '合规主数据缓存已刷新');
|
||||||
|
this.loadTemplates();
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
this.$message.error(error?.response?.data?.msg || '刷新失败');
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.refreshing = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
formatTime(value) {
|
||||||
|
if (!value) return '-';
|
||||||
|
return this.$dayjs(Number(value)).format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.hcs-master-data-page__head {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
|
||||||
|
.dialog-section-title {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hcs-master-data-page__tip {
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -52,12 +52,37 @@
|
|||||||
>
|
>
|
||||||
批量删除
|
批量删除
|
||||||
</el-button>
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="warning"
|
||||||
|
plain
|
||||||
|
v-if="!readonlyScope"
|
||||||
|
:loading="hcsRepushingId === 'batch'"
|
||||||
|
@click="handleHcsBatchRepush"
|
||||||
|
>
|
||||||
|
<el-icon class="el-icon--left"><Position /></el-icon>批量推送合规
|
||||||
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #approvalStatus="{ row }">
|
<template #approvalStatus="{ row }">
|
||||||
{{ displayStatus(row, 'approvalStatus') }}
|
{{ displayStatus(row, 'approvalStatus') }}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template #hcsSyncStatus="{ row }">
|
||||||
|
<el-tooltip v-if="row.hcsSyncMsg" placement="top" :content="row.hcsSyncMsg">
|
||||||
|
<el-tag :type="hcsSyncTagType(row.hcsSyncStatus)" class="status-text">
|
||||||
|
{{ hcsSyncLabel(row.hcsSyncStatus) }}
|
||||||
|
</el-tag>
|
||||||
|
</el-tooltip>
|
||||||
|
<el-tag
|
||||||
|
v-else-if="row.hcsSyncStatus"
|
||||||
|
:type="hcsSyncTagType(row.hcsSyncStatus)"
|
||||||
|
class="status-text"
|
||||||
|
>
|
||||||
|
{{ hcsSyncLabel(row.hcsSyncStatus) }}
|
||||||
|
</el-tag>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="project-apply-page__risk-search">
|
<div class="project-apply-page__risk-search">
|
||||||
<span class="project-apply-page__risk-label">
|
<span class="project-apply-page__risk-label">
|
||||||
@@ -128,6 +153,13 @@
|
|||||||
>
|
>
|
||||||
{{ operation.label }}
|
{{ operation.label }}
|
||||||
</el-link>
|
</el-link>
|
||||||
|
<el-link
|
||||||
|
v-if="canHcsRepush(row)"
|
||||||
|
type="warning"
|
||||||
|
@click="handleHcsRepush(row)"
|
||||||
|
>
|
||||||
|
{{ hcsRepushingId === row.id ? '推送中…' : '推送合规' }}
|
||||||
|
</el-link>
|
||||||
<el-link type="danger" v-if="canDelete(row)" @click="rowDel(row)"> 删除 </el-link>
|
<el-link type="danger" v-if="canDelete(row)" @click="rowDel(row)"> 删除 </el-link>
|
||||||
</template>
|
</template>
|
||||||
</avue-crud>
|
</avue-crud>
|
||||||
@@ -916,6 +948,8 @@
|
|||||||
import { exportBlob } from '@/api/common';
|
import { exportBlob } from '@/api/common';
|
||||||
import { defineAsyncComponent } from 'vue';
|
import { defineAsyncComponent } from 'vue';
|
||||||
import * as api from '@/api/business/project-apply';
|
import * as api from '@/api/business/project-apply';
|
||||||
|
import { repush as hcsRepush, repushBatch as hcsRepushBatch } from '@/api/business/hcs';
|
||||||
|
import { Position } from '@element-plus/icons-vue';
|
||||||
import { getMkPublicDetail } from '@/api/mk-process';
|
import { getMkPublicDetail } from '@/api/mk-process';
|
||||||
import {
|
import {
|
||||||
getList as getCustomerArchiveList,
|
getList as getCustomerArchiveList,
|
||||||
@@ -1060,6 +1094,7 @@ export default {
|
|||||||
ElImageViewer,
|
ElImageViewer,
|
||||||
OpenFileViewer,
|
OpenFileViewer,
|
||||||
PdfPreview,
|
PdfPreview,
|
||||||
|
Position,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
const validateAmount = (rule, value, callback) => {
|
const validateAmount = (rule, value, callback) => {
|
||||||
@@ -1121,6 +1156,8 @@ export default {
|
|||||||
{ label: '高风险', value: 'high', statKey: 'high' },
|
{ label: '高风险', value: 'high', statKey: 'high' },
|
||||||
{ label: '中风险', value: 'medium', statKey: 'medium' },
|
{ label: '中风险', value: 'medium', statKey: 'medium' },
|
||||||
],
|
],
|
||||||
|
// 合规(HCS)推送状态
|
||||||
|
hcsRepushingId: null,
|
||||||
page: {
|
page: {
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
pageSizes: [10, 20, 50, 100],
|
pageSizes: [10, 20, 50, 100],
|
||||||
@@ -1612,6 +1649,85 @@ export default {
|
|||||||
return this.canOperation(operation, row);
|
return this.canOperation(operation, row);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
// ==================== 合规(HCS)推送/对账(照“同步金蝶”按钮模式) ====================
|
||||||
|
hcsSyncLabel(value) {
|
||||||
|
return { unsynced: '待推送', synced: '已推送', failed: '推送失败' }[value] || value;
|
||||||
|
},
|
||||||
|
hcsSyncTagType(value) {
|
||||||
|
return { unsynced: 'info', synced: 'success', failed: 'danger' }[value] || 'info';
|
||||||
|
},
|
||||||
|
canHcsRepush(row) {
|
||||||
|
return (
|
||||||
|
!this.readonlyScope &&
|
||||||
|
['approved', 'change_approved'].includes(row.approvalStatus) &&
|
||||||
|
row.hcsSyncStatus !== 'synced'
|
||||||
|
);
|
||||||
|
},
|
||||||
|
handleHcsRepush(row) {
|
||||||
|
if (this.hcsRepushingId) return;
|
||||||
|
this.$confirm('确定推送该数据到合同合规系统?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning',
|
||||||
|
}).then(() => {
|
||||||
|
this.hcsRepushingId = row.id;
|
||||||
|
hcsRepush('project', 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 => {
|
||||||
|
// 拦截器已弹出后端 msg,此处不再重复提示
|
||||||
|
if (!error?._notified) {
|
||||||
|
this.$message.error(error?.response?.data?.msg || '推送合规失败');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.hcsRepushingId = null;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleHcsBatchRepush() {
|
||||||
|
if (this.hcsRepushingId) return;
|
||||||
|
if (!this.selectionList.length) {
|
||||||
|
this.$message.warning('请选择至少一条数据');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const invalidCount = this.selectionList.filter(item => !this.canHcsRepush(item)).length;
|
||||||
|
if (invalidCount) {
|
||||||
|
this.$message.warning(`选中数据中有${invalidCount}条不符合推送条件(仅审批通过且未同步的可推送),请重新选择`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.$confirm(`确定将选中的${this.selectionList.length}条数据推送到合同合规系统?`, '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning',
|
||||||
|
}).then(() => {
|
||||||
|
this.hcsRepushingId = 'batch';
|
||||||
|
hcsRepushBatch('project', this.selectionList.map(item => item.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 => {
|
||||||
|
// 拦截器已弹出后端 msg,此处不再重复提示
|
||||||
|
if (!error?._notified) {
|
||||||
|
this.$message.error(error?.response?.data?.msg || '批量推送合规失败');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.hcsRepushingId = null;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
canOperation(operation, row) {
|
canOperation(operation, row) {
|
||||||
const permission = operation.permission || `${this.config.permission}_${operation.action}`;
|
const permission = operation.permission || `${this.config.permission}_${operation.action}`;
|
||||||
if (!this.hasPermission(permission) || this.readonlyScope || row.readonly) return false;
|
if (!this.hasPermission(permission) || this.readonlyScope || row.readonly) return false;
|
||||||
@@ -1752,14 +1868,31 @@ export default {
|
|||||||
approvalStatus: row.approvalStatus || '',
|
approvalStatus: row.approvalStatus || '',
|
||||||
}).then(() => request())
|
}).then(() => request())
|
||||||
: request();
|
: request();
|
||||||
afterMk.then(res => {
|
// 项目通过会同步等待合规推送,加遮罩防重复点击
|
||||||
if (res.data?.success === false || res.data?.data === false) {
|
const loading =
|
||||||
|
operation.action === 'approve'
|
||||||
|
? this.$loading({
|
||||||
|
lock: true,
|
||||||
|
text: '正在推送合规,请稍候…',
|
||||||
|
background: 'rgba(255, 255, 255, 0.6)',
|
||||||
|
})
|
||||||
|
: null;
|
||||||
|
afterMk
|
||||||
|
.then(res => {
|
||||||
|
// data=false 表示审批已通过但合规推送失败,msg 带具体原因(照正式结算单审批)
|
||||||
|
if (operation.action === 'approve' && res.data?.data === false) {
|
||||||
|
this.$message.warning(res.data?.msg);
|
||||||
|
this.onLoad(this.page, this.query);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (res.data?.success === false) {
|
||||||
this.$message.error(res.data?.msg || `${operation.label}失败,请联系管理员`);
|
this.$message.error(res.data?.msg || `${operation.label}失败,请联系管理员`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.$message.success(`${operation.label}成功`);
|
this.$message.success(`${operation.label}成功`);
|
||||||
this.onLoad(this.page, this.query);
|
this.onLoad(this.page, this.query);
|
||||||
});
|
})
|
||||||
|
.finally(() => loading && loading.close());
|
||||||
};
|
};
|
||||||
if (operation.prompt) {
|
if (operation.prompt) {
|
||||||
this.$prompt(operation.prompt, operation.label, {
|
this.$prompt(operation.prompt, operation.label, {
|
||||||
|
|||||||
@@ -223,6 +223,11 @@
|
|||||||
show-word-limit /></el-form-item
|
show-word-limit /></el-form-item
|
||||||
></el-col>
|
></el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
<!-- 风险客商提示(基本信息备注后,同合同新增页):收款方外部客商高风险/黑名单时展示,文案由后端 riskTip 拼装 -->
|
||||||
|
<div v-if="payeeRiskText" class="payment-form-page__risk-tip">
|
||||||
|
<div class="payment-form-page__risk-tip-title">风险客商提示:</div>
|
||||||
|
<div class="payment-form-page__risk-tip-content">{{ payeeRiskText }}</div>
|
||||||
|
</div>
|
||||||
</section-card>
|
</section-card>
|
||||||
<section-card v-if="isSettlementRelatedType" title="结算信息">
|
<section-card v-if="isSettlementRelatedType" title="结算信息">
|
||||||
<el-table :data="settlementRows" border
|
<el-table :data="settlementRows" border
|
||||||
@@ -669,6 +674,7 @@ import * as billLedgerApi from '@/api/payment/billLedger';
|
|||||||
import { getDictionary } from '@/api/system/dictbiz';
|
import { getDictionary } from '@/api/system/dictbiz';
|
||||||
import { getList as getProjectList } from '@/api/business/project-apply';
|
import { getList as getProjectList } from '@/api/business/project-apply';
|
||||||
import { getDetail as getContractDetail } from '@/api/business/contract-manage';
|
import { getDetail as getContractDetail } from '@/api/business/contract-manage';
|
||||||
|
import { getRisk as getCustomerRisk } from '@/api/business/hcs';
|
||||||
import * as formalApi from '@/api/settlement/formalSettlement';
|
import * as formalApi from '@/api/settlement/formalSettlement';
|
||||||
import * as preApi from '@/api/settlement/preSettlement';
|
import * as preApi from '@/api/settlement/preSettlement';
|
||||||
import {
|
import {
|
||||||
@@ -817,6 +823,11 @@ export default {
|
|||||||
contractPaymentRatioLimit: null,
|
contractPaymentRatioLimit: null,
|
||||||
contractPaymentRatioExceeded: false,
|
contractPaymentRatioExceeded: false,
|
||||||
payeeCustomerLevel: '',
|
payeeCustomerLevel: '',
|
||||||
|
// 收款方 C101-1 风险查询留档:risky=高风险/黑名单,tip=后端拼装提示文案
|
||||||
|
payeeRisky: false,
|
||||||
|
payeeRiskTip: '',
|
||||||
|
payeeRiskLevel: '',
|
||||||
|
payeeBlackStatus: '',
|
||||||
attachmentRuleLoading: false,
|
attachmentRuleLoading: false,
|
||||||
attachmentImagePreviewVisible: false,
|
attachmentImagePreviewVisible: false,
|
||||||
attachmentImagePreviewUrls: [],
|
attachmentImagePreviewUrls: [],
|
||||||
@@ -980,11 +991,22 @@ export default {
|
|||||||
];
|
];
|
||||||
},
|
},
|
||||||
highRiskPayee() {
|
highRiskPayee() {
|
||||||
|
// 收款方 C101-1 高风险/黑名单,或客商等级 D/E/F(本地评级口径),命中须上传特批附件
|
||||||
|
if (this.payeeRisky) return true;
|
||||||
const level = String(this.payeeCustomerLevel || '')
|
const level = String(this.payeeCustomerLevel || '')
|
||||||
.trim()
|
.trim()
|
||||||
.toUpperCase();
|
.toUpperCase();
|
||||||
return ['D', 'D级', 'E', 'E级', 'F', 'F级'].includes(level) || level.includes('高风险');
|
return ['D', 'D级', 'E', 'E级', 'F', 'F级'].includes(level) || level.includes('高风险');
|
||||||
},
|
},
|
||||||
|
// 高风险/黑名单收款方提示(C101-1,同合同新增页风险客商提示口径;强制拦截在后端提交校验与金蝶推送门禁)
|
||||||
|
payeeRiskText() {
|
||||||
|
if (!this.payeeRisky) return '';
|
||||||
|
const levelMap = { 0: '低风险', 1: '中风险', 2: '高风险' };
|
||||||
|
const level = levelMap[this.payeeRiskLevel] || '未知';
|
||||||
|
const blacklist = String(this.payeeBlackStatus || '').trim().toLowerCase() === 'yes' ? '是' : '否';
|
||||||
|
const tip = this.payeeRiskTip || `收款方[${this.form.payeeName || ''}]为高风险/黑名单客商`;
|
||||||
|
return `风险等级:${level}|黑名单:${blacklist}。${tip},按集团规定不可直接付款,提交前须上传「特批附件」类型附件,审批通过后将自动发起付款特批`;
|
||||||
|
},
|
||||||
requiredAttachmentMaterials() {
|
requiredAttachmentMaterials() {
|
||||||
if (this.form.paymentType === 'project_advance') {
|
if (this.form.paymentType === 'project_advance') {
|
||||||
return [ATTACHMENT_MATERIALS.contract];
|
return [ATTACHMENT_MATERIALS.contract];
|
||||||
@@ -1967,6 +1989,10 @@ export default {
|
|||||||
const preserveCurrentValue = autoSelectDefault === false;
|
const preserveCurrentValue = autoSelectDefault === false;
|
||||||
this.receiptAccountOptions = [];
|
this.receiptAccountOptions = [];
|
||||||
this.payeeCustomerLevel = '';
|
this.payeeCustomerLevel = '';
|
||||||
|
this.payeeRisky = false;
|
||||||
|
this.payeeRiskTip = '';
|
||||||
|
this.payeeRiskLevel = '';
|
||||||
|
this.payeeBlackStatus = '';
|
||||||
if (!partyBName) {
|
if (!partyBName) {
|
||||||
if (!preserveCurrentValue) this.clearReceiptAccount();
|
if (!preserveCurrentValue) this.clearReceiptAccount();
|
||||||
return;
|
return;
|
||||||
@@ -1998,6 +2024,19 @@ export default {
|
|||||||
}
|
}
|
||||||
const detail = this.unwrapData(await getCustomerArchiveDetail(customer.id));
|
const detail = this.unwrapData(await getCustomerArchiveDetail(customer.id));
|
||||||
this.payeeCustomerLevel = detail.customerLevel || '';
|
this.payeeCustomerLevel = detail.customerLevel || '';
|
||||||
|
// 收款方实时查 C101-1 客商风险(后端对内部组织客商默认返回无风险):高风险/黑名单在
|
||||||
|
// 基本信息"风险客商提示"条常驻展示并要求特批附件;查询失败不阻断填写,提交时后端强校验兜底
|
||||||
|
if (detail.customerCode) {
|
||||||
|
try {
|
||||||
|
const risk = this.unwrapData(await getCustomerRisk(detail.customerCode)) || {};
|
||||||
|
this.payeeRisky = Boolean(risk.risky);
|
||||||
|
this.payeeRiskTip = risk.riskTip || '';
|
||||||
|
this.payeeRiskLevel = risk.fdRiskLevel || '';
|
||||||
|
this.payeeBlackStatus = risk.fdBlackStatus || '';
|
||||||
|
} catch (error) {
|
||||||
|
// 忽略风险查询异常,提交时后端 C101-1 强校验兜底
|
||||||
|
}
|
||||||
|
}
|
||||||
this.receiptAccountOptions = (detail.receiptAccounts || [])
|
this.receiptAccountOptions = (detail.receiptAccounts || [])
|
||||||
.filter(item => item.accountName || item.accountHolderName || item.bankAccount)
|
.filter(item => item.accountName || item.accountHolderName || item.bankAccount)
|
||||||
.map(item => ({
|
.map(item => ({
|
||||||
@@ -2706,6 +2745,15 @@ export default {
|
|||||||
// 提交前静默保存,避免同时弹出「保存成功」与材料缺失 toast
|
// 提交前静默保存,避免同时弹出「保存成功」与材料缺失 toast
|
||||||
const saved = await this.saveDraft({ showSuccess: false });
|
const saved = await this.saveDraft({ showSuccess: false });
|
||||||
if (!saved) return;
|
if (!saved) return;
|
||||||
|
// 高风险/黑名单收款方必须上传特批附件(与后端提交强校验同口径)
|
||||||
|
if (
|
||||||
|
this.missingAttachmentMaterials.some(
|
||||||
|
item => item.key === ATTACHMENT_MATERIALS.specialApproval.key
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
this.$message.warning('收款方为高风险/黑名单客商,必须上传「特批附件」类型附件后再提交审批');
|
||||||
|
return;
|
||||||
|
}
|
||||||
const previousApprovalStatus = this.form.approvalStatus || '';
|
const previousApprovalStatus = this.form.approvalStatus || '';
|
||||||
await api.submit({ id: this.form.id });
|
await api.submit({ id: this.form.id });
|
||||||
await submitMkApprovalFlow({
|
await submitMkApprovalFlow({
|
||||||
@@ -2734,6 +2782,26 @@ export default {
|
|||||||
.payment-form-page__form {
|
.payment-form-page__form {
|
||||||
padding-bottom: 72px;
|
padding-bottom: 72px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 风险客商提示条(基本信息备注后,同合同新增页):橙黄背景 + 红色文字
|
||||||
|
.payment-form-page__risk-tip {
|
||||||
|
margin: 0 0 18px;
|
||||||
|
padding: 10px 16px;
|
||||||
|
background: #faecd8;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
.payment-form-page__risk-tip-title {
|
||||||
|
color: #f56c6c;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-form-page__risk-tip-content {
|
||||||
|
color: #f56c6c;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.payment-form-page__settlement-nos {
|
.payment-form-page__settlement-nos {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -47,6 +47,29 @@
|
|||||||
{{ row.customerCode }}
|
{{ row.customerCode }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
|
<template #customerRisk="{ row }">
|
||||||
|
<el-tooltip
|
||||||
|
v-if="customerRiskMap[row.customerCode] && customerRiskMap[row.customerCode].risky"
|
||||||
|
placement="top"
|
||||||
|
effect="dark"
|
||||||
|
popper-class="customer-risk-popper"
|
||||||
|
>
|
||||||
|
<template #content>
|
||||||
|
<div>{{ customerRiskMap[row.customerCode].riskTip || '该客商为高风险/黑名单客商' }}</div>
|
||||||
|
</template>
|
||||||
|
<el-tag type="danger" class="status-text">
|
||||||
|
{{ customerRiskLabel(customerRiskMap[row.customerCode]) }}
|
||||||
|
</el-tag>
|
||||||
|
</el-tooltip>
|
||||||
|
<el-tag
|
||||||
|
v-else-if="customerRiskMap[row.customerCode]"
|
||||||
|
:type="customerRiskMap[row.customerCode].fdRiskLevel === '1' ? 'warning' : 'success'"
|
||||||
|
class="status-text"
|
||||||
|
>
|
||||||
|
{{ customerRiskLabel(customerRiskMap[row.customerCode]) }}
|
||||||
|
</el-tag>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
<template #accessType="{ row }">
|
<template #accessType="{ row }">
|
||||||
<el-tag
|
<el-tag
|
||||||
class="status-text"
|
class="status-text"
|
||||||
@@ -332,7 +355,19 @@
|
|||||||
maxlength="100"
|
maxlength="100"
|
||||||
show-word-limit
|
show-word-limit
|
||||||
:disabled="kingdeeLocked"
|
:disabled="kingdeeLocked"
|
||||||
|
@blur="checkArchiveNameRisk"
|
||||||
/>
|
/>
|
||||||
|
<el-tooltip
|
||||||
|
v-if="archiveNameRisky"
|
||||||
|
placement="top"
|
||||||
|
effect="dark"
|
||||||
|
popper-class="customer-risk-popper"
|
||||||
|
>
|
||||||
|
<template #content>
|
||||||
|
<div>{{ archiveNameRiskTip || '该客商为高风险/黑名单客商' }}</div>
|
||||||
|
</template>
|
||||||
|
<div class="archive-name-risk">高风险/黑名单客商</div>
|
||||||
|
</el-tooltip>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
@@ -1557,6 +1592,7 @@ import {
|
|||||||
queryKingdeeCustomer,
|
queryKingdeeCustomer,
|
||||||
syncKingdee,
|
syncKingdee,
|
||||||
} from '@/api/vehicle/customer-archive';
|
} from '@/api/vehicle/customer-archive';
|
||||||
|
import { getRiskBatch, getRiskByName } from '@/api/business/hcs';
|
||||||
import {
|
import {
|
||||||
getList as getCreditScoreQuantificationList,
|
getList as getCreditScoreQuantificationList,
|
||||||
getDetail as getCreditScoreQuantificationDetail,
|
getDetail as getCreditScoreQuantificationDetail,
|
||||||
@@ -1662,6 +1698,9 @@ export default {
|
|||||||
deptSearchInitialized: false,
|
deptSearchInitialized: false,
|
||||||
loading: true,
|
loading: true,
|
||||||
data: [],
|
data: [],
|
||||||
|
customerRiskMap: {},
|
||||||
|
archiveNameRisky: false,
|
||||||
|
archiveNameRiskTip: '',
|
||||||
page: {
|
page: {
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
pageSizes: [10, 20, 50, 100],
|
pageSizes: [10, 20, 50, 100],
|
||||||
@@ -1920,6 +1959,12 @@ export default {
|
|||||||
{ label: '内部组织', value: 'internal' },
|
{ label: '内部组织', value: 'internal' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: '客商风险',
|
||||||
|
prop: 'customerRisk',
|
||||||
|
slot: true,
|
||||||
|
minWidth: 100,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: '客户性质',
|
label: '客户性质',
|
||||||
prop: 'customerNature',
|
prop: 'customerNature',
|
||||||
@@ -2464,6 +2509,7 @@ export default {
|
|||||||
form.kingdeeStatus = 'exist';
|
form.kingdeeStatus = 'exist';
|
||||||
// 工商信息以金蝶为准,直接覆盖(后续字段锁定)
|
// 工商信息以金蝶为准,直接覆盖(后续字段锁定)
|
||||||
if (customer.name) form.fullName = customer.name;
|
if (customer.name) form.fullName = customer.name;
|
||||||
|
if (customer.name) this.checkArchiveNameRisk();
|
||||||
if (customer.artificialperson) form.legalPerson = customer.artificialperson;
|
if (customer.artificialperson) form.legalPerson = customer.artificialperson;
|
||||||
if (customer.bizpartnerAddress) form.registeredDetailAddress = customer.bizpartnerAddress;
|
if (customer.bizpartnerAddress) form.registeredDetailAddress = customer.bizpartnerAddress;
|
||||||
if (customer.regcapital) {
|
if (customer.regcapital) {
|
||||||
@@ -3888,6 +3934,7 @@ export default {
|
|||||||
const scope = this.getBusinessLicenseOcrValue(result, ['经营范围']);
|
const scope = this.getBusinessLicenseOcrValue(result, ['经营范围']);
|
||||||
if (code) this.archiveForm.unifiedCreditCode = code.replace(/\s/g, '').toUpperCase();
|
if (code) this.archiveForm.unifiedCreditCode = code.replace(/\s/g, '').toUpperCase();
|
||||||
if (name) this.archiveForm.fullName = name;
|
if (name) this.archiveForm.fullName = name;
|
||||||
|
if (name) this.checkArchiveNameRisk();
|
||||||
if (legalPerson) this.archiveForm.legalPerson = legalPerson;
|
if (legalPerson) this.archiveForm.legalPerson = legalPerson;
|
||||||
if (address) {
|
if (address) {
|
||||||
this.archiveForm.registeredAddress = address;
|
this.archiveForm.registeredAddress = address;
|
||||||
@@ -4271,6 +4318,7 @@ export default {
|
|||||||
this.selectedQualificationFiles = [];
|
this.selectedQualificationFiles = [];
|
||||||
if (this.isPublicViewPage) this.ensurePublicViewOptions(this.archiveForm);
|
if (this.isPublicViewPage) this.ensurePublicViewOptions(this.archiveForm);
|
||||||
this.applyFixedOwnerDept();
|
this.applyFixedOwnerDept();
|
||||||
|
this.checkArchiveNameRisk();
|
||||||
this.archiveBox = true;
|
this.archiveBox = true;
|
||||||
this.loadChangeRecords();
|
this.loadChangeRecords();
|
||||||
if (this.reviewMode) this.openReviewScore();
|
if (this.reviewMode) this.openReviewScore();
|
||||||
@@ -4289,6 +4337,7 @@ export default {
|
|||||||
this.ocrQualificationUploadFiles = [];
|
this.ocrQualificationUploadFiles = [];
|
||||||
this.selectedQualificationFiles = [];
|
this.selectedQualificationFiles = [];
|
||||||
this.applyFixedOwnerDept();
|
this.applyFixedOwnerDept();
|
||||||
|
this.checkArchiveNameRisk();
|
||||||
this.archiveBox = true;
|
this.archiveBox = true;
|
||||||
},
|
},
|
||||||
resetArchive() {
|
resetArchive() {
|
||||||
@@ -5194,17 +5243,20 @@ export default {
|
|||||||
this.$message.error('获取审核跳转地址失败');
|
this.$message.error('获取审核跳转地址失败');
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleApprove(row) {
|
async handleApprove(row) {
|
||||||
this.$confirm('是否确认审核通过?', '提示', {
|
await this.$confirm('是否确认审核通过?', '提示', {
|
||||||
confirmButtonText: '确定',
|
confirmButtonText: '确定',
|
||||||
cancelButtonText: '取消',
|
cancelButtonText: '取消',
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
})
|
|
||||||
.then(() => approve(row.id))
|
|
||||||
.then(() => {
|
|
||||||
this.onLoad(this.page);
|
|
||||||
this.$message({ type: 'success', message: '审核通过!' });
|
|
||||||
});
|
});
|
||||||
|
const res = await approve(row.id);
|
||||||
|
// data=false 表示审核已通过但推送金蝶失败,msg 带具体原因
|
||||||
|
if (res.data.data === false) {
|
||||||
|
this.$message.warning(res.data.msg);
|
||||||
|
} else {
|
||||||
|
this.$message.success(res.data.msg || '审核通过!');
|
||||||
|
}
|
||||||
|
this.onLoad(this.page);
|
||||||
},
|
},
|
||||||
async handleSyncKingdee(row) {
|
async handleSyncKingdee(row) {
|
||||||
if (this.kingdeeSyncingId) return;
|
if (this.kingdeeSyncingId) return;
|
||||||
@@ -5303,12 +5355,52 @@ export default {
|
|||||||
const pageData = res.data.data;
|
const pageData = res.data.data;
|
||||||
this.page.total = pageData.total;
|
this.page.total = pageData.total;
|
||||||
this.data = pageData.records;
|
this.data = pageData.records;
|
||||||
|
this.loadCustomerRisk(this.data);
|
||||||
this.selectionClear();
|
this.selectionClear();
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
// 客商风险标签文案:后端 riskLevelName 转义(黑名单/高风险/中风险/低风险),缺失时兜底
|
||||||
|
customerRiskLabel(risk) {
|
||||||
|
if (!risk) return '';
|
||||||
|
return risk.riskLevelName || (risk.risky ? '风险客商' : '正常');
|
||||||
|
},
|
||||||
|
// 客商名称风险校验(C101-1,仅展示不作为安全边界):名称失焦/回显时查询,
|
||||||
|
// risky 在名称框下展示红色提示,悬浮见风险项明细;查询失败静默不阻断填写,提交链路由后端强校验兜底
|
||||||
|
checkArchiveNameRisk() {
|
||||||
|
this.archiveNameRisky = false;
|
||||||
|
this.archiveNameRiskTip = '';
|
||||||
|
const customerName = String(this.archiveForm.fullName || '').trim();
|
||||||
|
if (!customerName) return;
|
||||||
|
getRiskByName(customerName)
|
||||||
|
.then(res => {
|
||||||
|
const risk = (res.data && res.data.data) || {};
|
||||||
|
this.archiveNameRisky = risk.risky === true;
|
||||||
|
this.archiveNameRiskTip = risk.riskTip || '';
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// 忽略风险查询异常,表单正常填写
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// 客商列表风险批量查询(C101-1,仅展示不作为安全边界):失败静默,行内显示"-",提交链路由后端强校验兜底
|
||||||
|
loadCustomerRisk(rows) {
|
||||||
|
this.customerRiskMap = {};
|
||||||
|
const customerCodes = [...new Set(rows.map(row => row.customerCode).filter(Boolean))];
|
||||||
|
if (!customerCodes.length) return;
|
||||||
|
getRiskBatch(customerCodes)
|
||||||
|
.then(res => {
|
||||||
|
const riskMap = {};
|
||||||
|
((res.data && res.data.data) || []).forEach(risk => {
|
||||||
|
if (risk && risk.customerCode) riskMap[risk.customerCode] = risk;
|
||||||
|
});
|
||||||
|
this.customerRiskMap = riskMap;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// 忽略风险查询异常,列表正常展示
|
||||||
|
});
|
||||||
|
},
|
||||||
handleExport() {
|
handleExport() {
|
||||||
this.$confirm('是否导出客商档案数据?', '提示', {
|
this.$confirm('是否导出客商档案数据?', '提示', {
|
||||||
confirmButtonText: '确定',
|
confirmButtonText: '确定',
|
||||||
@@ -5373,6 +5465,20 @@ export default {
|
|||||||
color: var(--el-color-danger);
|
color: var(--el-color-danger);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 风险客商悬浮提示:popper 挂在 body 下,需用 :global 约束宽度
|
||||||
|
:global(.customer-risk-popper) {
|
||||||
|
max-width: 360px;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 客商名称框下方的高风险/黑名单提示(对齐表单错误文案形态)
|
||||||
|
.archive-name-risk {
|
||||||
|
color: var(--el-color-danger);
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.5;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
.fund-use-risk-none {
|
.fund-use-risk-none {
|
||||||
color: #c0c4cc;
|
color: #c0c4cc;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user