前端完善
This commit is contained in:
@@ -1,309 +0,0 @@
|
||||
/**
|
||||
* 合同发起模块 - API 层
|
||||
*
|
||||
* 当前使用 mock 数据模拟接口返回。
|
||||
* 【接入后端】:直接替换每个函数内部实现为 axios 请求即可,函数签名不改。
|
||||
*/
|
||||
|
||||
// ========== 合同发起列表 mock 数据 ==========
|
||||
|
||||
const mockLaunchRecords = [
|
||||
{
|
||||
id: 'INIT-202605-001',
|
||||
// 合同编码
|
||||
contractCode: 'HT-2026-0008',
|
||||
// 合同名称
|
||||
contractName: '智慧园区设备采购合同',
|
||||
// 合同类型
|
||||
contractType: '采购合同',
|
||||
// 合同金额(元)
|
||||
contractAmount: 368000,
|
||||
// 有效期起 YYYY-MM-DD
|
||||
effectiveStart: '2026-05-10',
|
||||
// 有效期止 YYYY-MM-DD
|
||||
effectiveEnd: '2027-05-09',
|
||||
// 付款方式
|
||||
paymentMethod: '银行转账(30%预付款,70%验收后支付)',
|
||||
// 所选模板标识
|
||||
templateKey: 'purchase',
|
||||
// 所选模板名称
|
||||
templateName: '采购合同模板',
|
||||
// 合同标的
|
||||
contractSubject: '园区门禁一体机、访客终端及部署实施服务',
|
||||
// 业务备注
|
||||
businessRemark: '首次发起,等待业务确认后提交法务。',
|
||||
// 发起人
|
||||
initiator: '张晓明',
|
||||
// 所属部门
|
||||
initiatorDept: '供应链运营部',
|
||||
// 发起人公司
|
||||
companyName: '浙港物流平台有限公司',
|
||||
// 状态: draft/reviewing/rejected/pending_sign/signing/completed
|
||||
status: 'draft',
|
||||
// 查询级别: personal/company/admin
|
||||
queryScope: 'personal',
|
||||
// 创建时间
|
||||
createTime: '2026-05-10 09:20:00',
|
||||
// 更新时间
|
||||
updateTime: '2026-05-13 14:30:00',
|
||||
// 附件列表
|
||||
attachmentList: [
|
||||
{ uid: 'a1', name: '技术方案V1.pdf', size: 283011, status: 'done' },
|
||||
{ uid: 'a2', name: '补充协议草案.docx', size: 125880, status: 'done' },
|
||||
],
|
||||
// 合同方信息(详见下方 partyState 结构)
|
||||
partyState: {
|
||||
isThreePartyContract: false,
|
||||
partyA: {
|
||||
companyName: '浙港物流平台有限公司',
|
||||
contactPhone: '',
|
||||
registeredAddress: '',
|
||||
bankAccount: '',
|
||||
bankName: '',
|
||||
contactName: '',
|
||||
legalRepresentative: '',
|
||||
creditCode: '',
|
||||
},
|
||||
partyB: {
|
||||
companyName: '宁波港船务货运代理有限公司',
|
||||
companyType: '供应商',
|
||||
contactPhone: '15757460062',
|
||||
registeredAddress: '',
|
||||
bankAccount: '',
|
||||
bankName: '',
|
||||
contactName: '周经理',
|
||||
legalRepresentative: '',
|
||||
creditCode: '',
|
||||
},
|
||||
partyC: { companyName: '' },
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'INIT-202605-002',
|
||||
contractCode: 'XS-2026-0015',
|
||||
contractName: '保税燃料油销售合同',
|
||||
contractType: '销售合同',
|
||||
contractAmount: 1580000,
|
||||
effectiveStart: '2026-05-01',
|
||||
effectiveEnd: '2026-12-31',
|
||||
paymentMethod: '银行转账(按月结算)',
|
||||
templateKey: 'sales',
|
||||
templateName: '销售合同模板',
|
||||
contractSubject: '保税燃料油销售及船舶供油服务',
|
||||
businessRemark: '已提交法务,等待审批中。',
|
||||
initiator: '王倩',
|
||||
initiatorDept: '国际贸易事业部',
|
||||
companyName: '浙港物流平台有限公司',
|
||||
status: 'reviewing',
|
||||
queryScope: 'company',
|
||||
createTime: '2026-05-08 16:15:00',
|
||||
updateTime: '2026-05-14 08:30:00',
|
||||
attachmentList: [
|
||||
{ uid: 'a3', name: '报价确认函.pdf', size: 90211, status: 'done' },
|
||||
],
|
||||
partyState: {
|
||||
isThreePartyContract: false,
|
||||
partyA: {
|
||||
companyName: '宁波浙港供应链科技有限公司',
|
||||
contactPhone: '13900000088',
|
||||
contactName: '陈经理',
|
||||
},
|
||||
partyB: {
|
||||
companyName: '上海远洋能源有限公司',
|
||||
companyType: '客户',
|
||||
legalRepresentative: '孙先生',
|
||||
contactName: '吴经理',
|
||||
contactPhone: '13600000015',
|
||||
contactEmail: 'sales@oceanenergy.com',
|
||||
bankName: '招商银行上海分行',
|
||||
bankAccount: '621483000000000015',
|
||||
},
|
||||
partyC: { companyName: '' },
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'INIT-202605-003',
|
||||
contractCode: 'FW-2026-0021',
|
||||
contractName: '数字化平台运维服务合同',
|
||||
contractType: '服务合同',
|
||||
contractAmount: 520000,
|
||||
effectiveStart: '2026-04-15',
|
||||
effectiveEnd: '2027-04-14',
|
||||
paymentMethod: '银行转账(一次性结清)',
|
||||
templateKey: 'purchase',
|
||||
templateName: '采购合同模板',
|
||||
contractSubject: '合同管理板块运维与驻场实施服务',
|
||||
businessRemark: '法务要求补充服务SLA附件后重新提交。',
|
||||
initiator: '李娜',
|
||||
initiatorDept: '数字化办公室',
|
||||
companyName: '浙港物流平台有限公司',
|
||||
status: 'rejected',
|
||||
queryScope: 'company',
|
||||
createTime: '2026-04-15 10:25:00',
|
||||
updateTime: '2026-05-07 18:05:00',
|
||||
attachmentList: [
|
||||
{ uid: 'a4', name: '实施范围说明.pdf', size: 70123, status: 'done' },
|
||||
],
|
||||
partyState: {
|
||||
isThreePartyContract: false,
|
||||
partyA: { companyName: '浙港物流平台有限公司' },
|
||||
partyB: {
|
||||
companyName: '杭州智云实施服务有限公司',
|
||||
companyType: '供应商',
|
||||
contactName: '刘经理',
|
||||
contactPhone: '13888886666',
|
||||
contactEmail: 'liu@cloudservice.com',
|
||||
},
|
||||
partyC: { companyName: '' },
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'INIT-202605-004',
|
||||
contractCode: 'KJ-2026-0011',
|
||||
contractName: '港口智能设备年度框架合同',
|
||||
contractType: '框架合同',
|
||||
contractAmount: 980000,
|
||||
effectiveStart: '2026-03-01',
|
||||
effectiveEnd: '2027-02-28',
|
||||
paymentMethod: '银行转账(按月结算)',
|
||||
templateKey: 'purchase',
|
||||
templateName: '采购合同模板',
|
||||
contractSubject: '港口智能硬件、集成服务及年度维保合作',
|
||||
businessRemark: '审批完成,待发起线上签署。',
|
||||
initiator: '周立',
|
||||
initiatorDept: '装备采购中心',
|
||||
companyName: '浙港物流平台有限公司',
|
||||
status: 'pending_sign',
|
||||
queryScope: 'admin',
|
||||
createTime: '2026-03-01 11:12:00',
|
||||
updateTime: '2026-05-11 09:45:00',
|
||||
attachmentList: [
|
||||
{ uid: 'a5', name: '年度采购清单.xlsx', size: 154933, status: 'done' },
|
||||
],
|
||||
partyState: {
|
||||
isThreePartyContract: true,
|
||||
partyA: { companyName: '浙港物流平台有限公司' },
|
||||
partyB: {
|
||||
companyName: '宁波保税区港航设备有限公司',
|
||||
companyType: '合作伙伴',
|
||||
contactName: '赵主管',
|
||||
contactPhone: '13500000021',
|
||||
},
|
||||
partyC: {
|
||||
companyName: '浙江临港数智科技有限公司',
|
||||
companyType: '合作伙伴',
|
||||
creditCode: '91330000MAABCDE123',
|
||||
legalRepresentative: '钱先生',
|
||||
registeredAddress: '浙江省杭州市滨江区江南大道2888号',
|
||||
contactName: '何经理',
|
||||
contactPhone: '13700000078',
|
||||
contactAddress: '浙江省杭州市滨江区江南大道2888号',
|
||||
contactEmail: 'hz@example.com',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'INIT-202605-005',
|
||||
contractCode: 'XS-2026-0022',
|
||||
contractName: '沿海航线供应服务合同',
|
||||
contractType: '销售合同',
|
||||
contractAmount: 760000,
|
||||
effectiveStart: '2026-02-18',
|
||||
effectiveEnd: '2026-11-30',
|
||||
paymentMethod: '银行转账(按月结算)',
|
||||
templateKey: 'sales',
|
||||
templateName: '销售合同模板',
|
||||
contractSubject: '沿海航线供应链服务与结算支持',
|
||||
businessRemark: '甲方已签章,等待乙方签署。',
|
||||
initiator: '徐晨',
|
||||
initiatorDept: '客户服务中心',
|
||||
companyName: '浙港物流平台有限公司',
|
||||
status: 'signing',
|
||||
queryScope: 'personal',
|
||||
createTime: '2026-02-18 13:40:00',
|
||||
updateTime: '2026-05-12 17:16:00',
|
||||
attachmentList: [],
|
||||
partyState: {
|
||||
isThreePartyContract: false,
|
||||
partyA: {
|
||||
companyName: '宁波浙港供应链科技有限公司',
|
||||
contactPhone: '13900000088',
|
||||
contactName: '陈经理',
|
||||
},
|
||||
partyB: {
|
||||
companyName: '上海远洋能源有限公司',
|
||||
companyType: '客户',
|
||||
legalRepresentative: '孙先生',
|
||||
contactName: '吴经理',
|
||||
contactPhone: '13600000015',
|
||||
contactEmail: 'sales@oceanenergy.com',
|
||||
bankName: '招商银行上海分行',
|
||||
bankAccount: '621483000000000015',
|
||||
},
|
||||
partyC: { companyName: '' },
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'INIT-202605-006',
|
||||
contractCode: 'CG-2026-0033',
|
||||
contractName: '港区安防升级采购合同',
|
||||
contractType: '采购合同',
|
||||
contractAmount: 2860000,
|
||||
effectiveStart: '2026-01-10',
|
||||
effectiveEnd: '2026-12-31',
|
||||
paymentMethod: '银行转账(30%预付款,70%验收后支付)',
|
||||
templateKey: 'purchase',
|
||||
templateName: '采购合同模板',
|
||||
contractSubject: '港区安防摄像头、门禁和施工实施采购',
|
||||
businessRemark: '合同已签署完毕并推送法务归档。',
|
||||
initiator: '高翔',
|
||||
initiatorDept: '工程保障部',
|
||||
companyName: '浙港物流平台有限公司',
|
||||
status: 'completed',
|
||||
queryScope: 'admin',
|
||||
createTime: '2026-01-10 08:45:00',
|
||||
updateTime: '2026-05-09 16:28:00',
|
||||
attachmentList: [
|
||||
{ uid: 'a6', name: '验收报告.pdf', size: 188221, status: 'done' },
|
||||
],
|
||||
partyState: {
|
||||
isThreePartyContract: false,
|
||||
partyA: { companyName: '浙港物流平台有限公司' },
|
||||
partyB: {
|
||||
companyName: '宁波港船务货运代理有限公司',
|
||||
companyType: '供应商',
|
||||
contactPhone: '15757460062',
|
||||
contactName: '周经理',
|
||||
},
|
||||
partyC: { companyName: '' },
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
// ========== API 函数(接入后端时替换内部实现) ==========
|
||||
|
||||
/** 查询合同发起列表 */
|
||||
export function queryLaunchList() {
|
||||
return Promise.resolve(mockLaunchRecords)
|
||||
}
|
||||
|
||||
/** 查询合同发起详情 */
|
||||
export function getLaunchDetail(id) {
|
||||
const record = mockLaunchRecords.find((item) => item.id === id) || null
|
||||
return Promise.resolve(record)
|
||||
}
|
||||
|
||||
/** 保存合同发起 */
|
||||
export function saveLaunchRecord(record) {
|
||||
return Promise.resolve(record)
|
||||
}
|
||||
|
||||
/** 提交合同到法务 */
|
||||
export function submitLaunchRecord(record) {
|
||||
return Promise.resolve(record)
|
||||
}
|
||||
|
||||
/** 删除合同发起记录 */
|
||||
export function deleteLaunchRecord(idList) {
|
||||
return Promise.resolve(idList)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -71,11 +71,12 @@
|
||||
v-for="field in launchFieldList"
|
||||
:key="field.key"
|
||||
:label="field.label"
|
||||
required
|
||||
:required="!field.readonly"
|
||||
>
|
||||
<a-input
|
||||
:value="getLaunchFieldValue(field.key)"
|
||||
:placeholder="`请输入${field.label}`"
|
||||
:readonly="field.readonly"
|
||||
@update:value="setLaunchFieldValue(field.key, $event)"
|
||||
/>
|
||||
</a-form-item>
|
||||
@@ -196,6 +197,20 @@ import {
|
||||
downloadTemplateFile,
|
||||
queryPublishedManifestList,
|
||||
} from "@/api/manifest.js";
|
||||
import { uploadLaunchFile } from "@/api/launch.js";
|
||||
import {
|
||||
contractSourceOptions,
|
||||
contractTypeOptions,
|
||||
currencyOptions,
|
||||
dateTypeOptions,
|
||||
extraAmountFieldOptions,
|
||||
getOptionLabel,
|
||||
isElectronOptions,
|
||||
paymentDirectionOptions,
|
||||
sealTypeOptions,
|
||||
textSourceOptions,
|
||||
valuationModeOptions,
|
||||
} from "../contractLaunchOptions.js";
|
||||
|
||||
// ========================================================================
|
||||
// 以下为表单模型 + 字典 + 工具函数,直接内联在此文件中。
|
||||
@@ -208,68 +223,68 @@ const contractCreateModeOptions = [
|
||||
{ label: '手动创建', value: 'manual' },
|
||||
]
|
||||
|
||||
// ========== 合同类型选项 ==========
|
||||
const contractTypeOptions = [
|
||||
{ label: '采购合同', value: '采购合同' },
|
||||
{ label: '销售合同', value: '销售合同' },
|
||||
{ label: '服务合同', value: '服务合同' },
|
||||
{ label: '框架合同', value: '框架合同' },
|
||||
]
|
||||
|
||||
// ========== 付款方式选项 ==========
|
||||
const paymentMethodOptions = [
|
||||
{ label: '银行转账(30%预付款,70%验收后支付)', value: '银行转账(30%预付款,70%验收后支付)' },
|
||||
{ label: '银行转账(按月结算)', value: '银行转账(按月结算)' },
|
||||
{ label: '银行转账(一次性结清)', value: '银行转账(一次性结清)' },
|
||||
]
|
||||
|
||||
// ========== 计价方式选项 ==========
|
||||
const valuationModeOptions = [
|
||||
{ label: '无金额', value: '0' },
|
||||
{ label: '固定总价', value: '1' },
|
||||
{ label: '预估总价', value: '2' },
|
||||
{ label: '无法预估总价', value: '3' },
|
||||
{ label: '固定单价', value: '4' },
|
||||
]
|
||||
|
||||
// ========== 币种选项 ==========
|
||||
const currencyOptions = [
|
||||
{ label: '人民币', value: 'RMB' },
|
||||
{ label: '美元', value: 'USD' },
|
||||
{ label: '港币', value: 'HKD' },
|
||||
{ label: '欧元', value: 'EUR' },
|
||||
{ label: '印尼盾', value: 'IDR' },
|
||||
]
|
||||
|
||||
// ========== 签订方式选项 ==========
|
||||
const sealTypeOptions = [
|
||||
{ label: '公章', value: 'GZ' },
|
||||
{ label: '合同专用章', value: 'HTZYZ' },
|
||||
{ label: '公章+法定代表人私章', value: 'GZFDDBRSZ' },
|
||||
{ label: '公章+法定代表人签字', value: 'GZFDDBRQZ' },
|
||||
{ label: '公章+被授权人签字', value: 'GZBSQRQZ' },
|
||||
{ label: '合同专用章+被授权人签字', value: 'HTZYZBSQRQZ' },
|
||||
{ label: '合同专用章+法定代表人签字', value: 'HTZYZFDDBRQZ' },
|
||||
{ label: '其他', value: 'QT' },
|
||||
]
|
||||
|
||||
// ========== 可绑定业务字段列表(用于映射下拉和展示) ==========
|
||||
const templateFieldList = [
|
||||
{ value: 'contractName', label: '合同名称' },
|
||||
{ value: 'contractCode', label: '合同编码' },
|
||||
{ value: 'contractType', label: '合同类型' },
|
||||
{ value: 'contractTypeCode', label: '合同类型编码' },
|
||||
{ value: 'contractAmountDisplay', label: '合同金额' },
|
||||
{ value: 'contractAmount', label: '合同金额数字' },
|
||||
{ value: 'effectiveStart', label: '有效期起' },
|
||||
{ value: 'effectiveEnd', label: '有效期止' },
|
||||
{ value: 'effectivePeriod', label: '有效期区间' },
|
||||
{ value: 'contractPeriodTypeLabel', label: '合同期限类型' },
|
||||
{ value: 'periodExplain', label: '期限说明' },
|
||||
{ value: 'paymentMethod', label: '付款方式' },
|
||||
{ value: 'paymentDirectionLabel', label: '收支方向' },
|
||||
{ value: 'valuationModeLabel', label: '计价方式' },
|
||||
{ value: 'currencyNameLabel', label: '币种' },
|
||||
{ value: 'sealTypeLabel', label: '签订方式' },
|
||||
{ value: 'textSourceLabel', label: '文本来源' },
|
||||
{ value: 'isElectronLabel', label: '签署方式' },
|
||||
{ value: 'contractSourceLabel', label: '合同来源' },
|
||||
{ value: 'dateTypeLabel', label: '固定期限日期类型' },
|
||||
{ value: 'mandateType', label: '委托类型' },
|
||||
{ value: 'cargoType', label: '货种' },
|
||||
{ value: 'primaryContent', label: '合同标的说明' },
|
||||
{ value: 'amountExplain', label: '业务备注' },
|
||||
{ value: 'amountExplain', label: '金额说明' },
|
||||
{ value: 'contractGistRemark', label: '签订依据说明' },
|
||||
{ value: 'bodySummary', label: '正文摘要' },
|
||||
{ value: 'assistDept', label: '协助部门' },
|
||||
{ value: 'executorAccount', label: '合同执行人' },
|
||||
{ value: 'selfCode', label: '二级单位合同编码' },
|
||||
{ value: 'mainContractCode', label: '主合同编码' },
|
||||
{ value: 'signingSubjectCode', label: '签约主体编码' },
|
||||
{ value: 'invoiceDate', label: '开票时间' },
|
||||
{ value: 'receiveDate', label: '收款时间' },
|
||||
{ value: 'partyAName', label: '甲方名称' },
|
||||
{ value: 'partyBName', label: '乙方名称' },
|
||||
{ value: 'partyCName', label: '丙方名称' },
|
||||
{ value: 'partyAContactName', label: '甲方联系人' },
|
||||
{ value: 'partyBContactName', label: '乙方联系人' },
|
||||
{ value: 'partyCContactName', label: '丙方联系人' },
|
||||
{ value: 'partyAContactPhone', label: '甲方电话' },
|
||||
{ value: 'partyBContactPhone', label: '乙方电话' },
|
||||
{ value: 'partyCContactPhone', label: '丙方电话' },
|
||||
{ value: 'partyAAddress', label: '甲方地址' },
|
||||
{ value: 'partyBAddress', label: '乙方地址' },
|
||||
{ value: 'partyCAddress', label: '丙方地址' },
|
||||
{ value: 'partyACreditCode', label: '甲方统一社会信用代码' },
|
||||
{ value: 'partyBCreditCode', label: '乙方统一社会信用代码' },
|
||||
{ value: 'partyCCreditCode', label: '丙方统一社会信用代码' },
|
||||
{ value: 'partyABankName', label: '甲方开户行' },
|
||||
{ value: 'partyBBankName', label: '乙方开户行' },
|
||||
{ value: 'partyCBankName', label: '丙方开户行' },
|
||||
{ value: 'partyABankAccount', label: '甲方银行账号' },
|
||||
{ value: 'partyBBankAccount', label: '乙方银行账号' },
|
||||
{ value: 'partyCBankAccount', label: '丙方银行账号' },
|
||||
{ value: 'counterpartySummary', label: '合同方摘要' },
|
||||
{ value: 'attachmentSummary', label: '附件情况' },
|
||||
{ value: 'contractGistFileSummary', label: '签订依据附件' },
|
||||
{ value: 'contractBodyFileName', label: '合同正文文件名' },
|
||||
{ value: 'extraAmountSummary', label: '额外金额' },
|
||||
{ value: 'performancePlanSummary', label: '履行计划' },
|
||||
{ value: 'templateLabel', label: '模板名称' },
|
||||
{ value: 'signDate', label: '签署日期' },
|
||||
]
|
||||
@@ -283,19 +298,154 @@ function formatContractAmount(value) {
|
||||
}).format(Number(value))} 元`
|
||||
}
|
||||
|
||||
function getContractPeriodTypeLabel(value) {
|
||||
if (value === '1') return '无固定期限'
|
||||
return '固定期限'
|
||||
}
|
||||
|
||||
function normalizeFileItem(item = {}) {
|
||||
return {
|
||||
uid: item.uid || `file_${Date.now()}_${Math.random().toString(16).slice(2, 8)}`,
|
||||
name: item.name || item.fileName || '',
|
||||
size: item.size ?? item.fileSize ?? 0,
|
||||
status: item.status || 'done',
|
||||
filePath: item.filePath || '',
|
||||
fileType: item.fileType || '',
|
||||
fileCategory: item.fileCategory || '',
|
||||
}
|
||||
}
|
||||
|
||||
function splitFilesByCategory(source = {}) {
|
||||
const sourceFiles = source.fileList || []
|
||||
const bodyFile = sourceFiles.find((item) => item.fileCategory === 'body') || {}
|
||||
return {
|
||||
contractBodyFileName:
|
||||
source.contractBodyFileName ||
|
||||
source.contractTextFileName ||
|
||||
bodyFile.fileName ||
|
||||
'',
|
||||
contractBodyFilePath:
|
||||
source.contractBodyFilePath ||
|
||||
source.contractTextFilePath ||
|
||||
bodyFile.filePath ||
|
||||
'',
|
||||
contractBodyFileType:
|
||||
source.contractBodyFileType ||
|
||||
source.contractTextFileType ||
|
||||
bodyFile.fileType ||
|
||||
'',
|
||||
contractBodyFileSize:
|
||||
source.contractBodyFileSize ??
|
||||
source.contractTextFileSize ??
|
||||
bodyFile.fileSize ??
|
||||
null,
|
||||
otherAttachmentList: source.otherAttachmentList?.length
|
||||
? source.otherAttachmentList.map(normalizeFileItem)
|
||||
: sourceFiles
|
||||
.filter((item) => item.fileCategory === 'other')
|
||||
.map(normalizeFileItem),
|
||||
contractGistFileList: source.contractGistFileList?.length
|
||||
? source.contractGistFileList.map(normalizeFileItem)
|
||||
: sourceFiles
|
||||
.filter((item) => item.fileCategory === 'gist')
|
||||
.map(normalizeFileItem),
|
||||
}
|
||||
}
|
||||
|
||||
function formatFileSummary(fileList = []) {
|
||||
return fileList.length
|
||||
? fileList.map((item) => item.name || item.fileName).filter(Boolean).join(';')
|
||||
: '暂无附件'
|
||||
}
|
||||
|
||||
function buildExtraAmountSummary(extraAmounts = {}) {
|
||||
const parts = extraAmountFieldOptions
|
||||
.filter((item) => extraAmounts[`${item.key}Enabled`])
|
||||
.map((item) => `${item.label}${formatContractAmount(extraAmounts[item.key]) || '0.00 元'}`)
|
||||
return parts.length ? parts.join(';') : '无'
|
||||
}
|
||||
|
||||
function restoreExtraAmounts(source = {}, current = {}) {
|
||||
const result = { ...current, ...(source.extraAmounts || {}) }
|
||||
const selectedCodes = String(source.ewAmountType || '')
|
||||
.split(',')
|
||||
.map((item) => item.trim())
|
||||
.filter(Boolean)
|
||||
|
||||
extraAmountFieldOptions.forEach((item) => {
|
||||
const amount = source[item.amountField]
|
||||
if (selectedCodes.includes(item.code) || amount || amount === 0) {
|
||||
result[`${item.key}Enabled`] = selectedCodes.includes(item.code) || amount > 0
|
||||
if (amount !== undefined && amount !== null) {
|
||||
result[item.key] = amount
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
function buildPerformancePlanSummary(list = []) {
|
||||
if (!list.length) return '暂无履行计划'
|
||||
return list
|
||||
.map((item, index) => {
|
||||
const money = item.planMoney || item.planMoney === 0 ? `,金额${formatContractAmount(item.planMoney)}` : ''
|
||||
return `${index + 1}. ${item.planMatter || '未填写事项'},${item.planWhere || '未填写条件'},${item.planTime || '未填写日期'}${money}`
|
||||
})
|
||||
.join('\n')
|
||||
}
|
||||
|
||||
// ========== 合同方相关函数 ==========
|
||||
|
||||
/** 深拷贝合同方状态 */
|
||||
function copyPartyState(input) {
|
||||
const source = input || {}
|
||||
const listState = buildPartyStateFromList(source.partyList)
|
||||
return {
|
||||
isThreePartyContract: Boolean(source.isThreePartyContract),
|
||||
partyA: { roleKey: 'A', roleName: '甲方', companyName: '', ...(source.partyA || source.parties?.A || {}) },
|
||||
partyB: { roleKey: 'B', roleName: '乙方', companyName: '', ...(source.partyB || source.parties?.B || {}) },
|
||||
partyC: { roleKey: 'C', roleName: '丙方', companyName: '', ...(source.partyC || source.parties?.C || {}) },
|
||||
partyA: { roleKey: 'A', roleName: '甲方', companyName: '', ...(source.partyA || source.parties?.A || {}), ...(listState?.partyA || {}) },
|
||||
partyB: { roleKey: 'B', roleName: '乙方', companyName: '', ...(source.partyB || source.parties?.B || {}), ...(listState?.partyB || {}) },
|
||||
partyC: { roleKey: 'C', roleName: '丙方', companyName: '', ...(source.partyC || source.parties?.C || {}), ...(listState?.partyC || {}) },
|
||||
...(listState ? { isThreePartyContract: listState.isThreePartyContract } : {}),
|
||||
}
|
||||
}
|
||||
|
||||
function mapLaunchPartyToFormParty(item = {}) {
|
||||
return {
|
||||
roleKey: item.roleCode || 'B',
|
||||
roleName: item.roleCode === 'A' ? '甲方' : item.roleCode === 'C' ? '丙方' : '乙方',
|
||||
partnerId: item.partnerId || null,
|
||||
oppositeId: item.oppositeId || '',
|
||||
companyName: item.companyName || '',
|
||||
companyType: item.companyType || '',
|
||||
oppCharacter: item.oppositeCharacter || '',
|
||||
contactName: item.contactName || '',
|
||||
contactPhone: item.contactPhone || '',
|
||||
contactEmail: item.contactEmail || '',
|
||||
contactAddress: item.contactAddress || '',
|
||||
registeredAddress: item.registeredAddress || '',
|
||||
legalRepresentative: item.legalRepresentative || '',
|
||||
creditCode: item.creditCode || '',
|
||||
bankName: item.bankName || '',
|
||||
bankAccount: item.bankAccount || '',
|
||||
naturalPersonIdType: item.naturalPersonIdType || '',
|
||||
naturalPersonIdNumber: item.naturalPersonIdNumber || '',
|
||||
isGroupInternal: item.isGroupInternal === '1' || item.isGroupInternal === true,
|
||||
isHongKongMacaoTaiwan: item.isHongKongMacaoTaiwan === '1' || item.isHongKongMacaoTaiwan === true,
|
||||
}
|
||||
}
|
||||
|
||||
function buildPartyStateFromList(list = []) {
|
||||
if (!Array.isArray(list) || !list.length) return null
|
||||
const state = { partyA: {}, partyB: {}, partyC: {}, isThreePartyContract: false }
|
||||
list.forEach((item) => {
|
||||
const key = item.roleCode === 'A' ? 'partyA' : item.roleCode === 'C' ? 'partyC' : 'partyB'
|
||||
state[key] = mapLaunchPartyToFormParty(item)
|
||||
})
|
||||
state.isThreePartyContract = Boolean(state.partyB.companyName && state.partyC.companyName)
|
||||
return state
|
||||
}
|
||||
|
||||
/** 获取有效的合同方列表 */
|
||||
function getPartyList(partyState) {
|
||||
const state = copyPartyState(partyState)
|
||||
@@ -366,6 +516,8 @@ function createPerformancePlan() {
|
||||
planTime: '',
|
||||
planDay: 0,
|
||||
planMoney: '',
|
||||
planSubjectCode: '',
|
||||
planSubjectName: '',
|
||||
planDetail: '',
|
||||
}
|
||||
}
|
||||
@@ -386,6 +538,11 @@ function createContractFormData() {
|
||||
contractPeriodType: '0', // 合同期限类型: 0-固定期限 1-无固定期限
|
||||
periodExplain: '', // 期限说明(无固定期限时填写)
|
||||
paymentMethod: '', // 付款方式
|
||||
paymentDirection: '0', // 收支方向
|
||||
isElectron: '1', // 签署方式: 0-纸质 1-电子
|
||||
textSource: 'standardText', // 文本来源
|
||||
contractSource: 'ECP', // 合同来源
|
||||
dateType: '0', // 固定期间日期类型:0-年 1-月 2-日
|
||||
valuationMode: '', // 计价方式: 0-无金额 1-固定总价 2-预估总价 3-无法预估总价 4-固定单价
|
||||
currencyName: '', // 币种: RMB/USD/HKD/EUR/IDR
|
||||
sealType: '', // 签订方式: GZ/HTZYZ/...
|
||||
@@ -393,13 +550,24 @@ function createContractFormData() {
|
||||
cargoType: '', // 货种
|
||||
primaryContent: '', // 合同主要内容
|
||||
amountExplain: '', // 金额说明
|
||||
contractGistRemark: '', // 签订依据说明
|
||||
bodySummary: '', // 正文摘要
|
||||
assistDept: '', // 协助部门
|
||||
executorAccount: '', // 合同执行人手机号
|
||||
selfCode: '', // 二级单位合同编码
|
||||
mainContractCode: '', // 主合同编码
|
||||
signingSubjectCode: '', // 签约主体编码
|
||||
invoiceDate: '', // 开票时间
|
||||
receiveDate: '', // 收款时间
|
||||
|
||||
// 文件
|
||||
contractBodyFileName: '', // 合同正文文件名
|
||||
contractBodyFilePath: '',
|
||||
contractBodyFileType: '',
|
||||
contractBodyFileSize: null,
|
||||
contractBodyArrayBuffer: null, // 合同正文文件二进制
|
||||
otherAttachmentList: [], // 其他附件列表
|
||||
contractGistFileList: [], // 签订依据附件列表
|
||||
|
||||
// 合同方
|
||||
partyState: {
|
||||
@@ -424,6 +592,11 @@ function createContractFormData() {
|
||||
advanceEnabled: false, advance: 0,
|
||||
guaranteeEnabled: false, guarantee: 0,
|
||||
},
|
||||
ewAmountType: '',
|
||||
djAmount: 0,
|
||||
yjAmount: 0,
|
||||
yfkAmount: 0,
|
||||
bzjAmount: 0,
|
||||
|
||||
// 履行计划
|
||||
performancePlans: [],
|
||||
@@ -437,17 +610,23 @@ function createContractFormData() {
|
||||
function copyContractFormData(data = {}) {
|
||||
const form = createContractFormData()
|
||||
const source = data || {}
|
||||
const fileState = splitFilesByCategory(source)
|
||||
|
||||
form.contractName = source.contractName ?? form.contractName
|
||||
form.contractCode = source.contractCode ?? form.contractCode
|
||||
form.contractType = source.contractType ?? form.contractType
|
||||
form.contractTypeCode = source.contractTypeCode ?? source.contractType ?? form.contractTypeCode
|
||||
form.contractAmount = source.contractAmount ?? form.contractAmount
|
||||
form.effectiveStart = source.effectiveStart ?? form.effectiveStart
|
||||
form.effectiveEnd = source.effectiveEnd ?? form.effectiveEnd
|
||||
form.effectiveStart = source.effectiveStart ? String(source.effectiveStart).slice(0, 10) : form.effectiveStart
|
||||
form.effectiveEnd = source.effectiveEnd ? String(source.effectiveEnd).slice(0, 10) : form.effectiveEnd
|
||||
form.contractPeriodType = source.contractPeriodType ?? form.contractPeriodType
|
||||
form.periodExplain = source.periodExplain ?? form.periodExplain
|
||||
form.paymentMethod = source.paymentMethod ?? form.paymentMethod
|
||||
form.paymentDirection = source.paymentDirection ?? form.paymentDirection
|
||||
form.isElectron = source.isElectron ?? form.isElectron
|
||||
form.textSource = source.textSource ?? form.textSource
|
||||
form.contractSource = source.contractSource ?? form.contractSource
|
||||
form.dateType = source.dateType ?? form.dateType
|
||||
form.valuationMode = source.valuationMode ?? form.valuationMode
|
||||
form.currencyName = source.currencyName ?? form.currencyName
|
||||
form.sealType = source.sealType ?? form.sealType
|
||||
@@ -455,31 +634,48 @@ function copyContractFormData(data = {}) {
|
||||
form.cargoType = source.cargoType ?? form.cargoType
|
||||
form.primaryContent = source.primaryContent ?? form.primaryContent
|
||||
form.amountExplain = source.amountExplain ?? form.amountExplain
|
||||
form.assistDept = source.assistDept ?? form.assistDept
|
||||
form.contractGistRemark = source.contractGistRemark ?? form.contractGistRemark
|
||||
form.bodySummary = source.bodySummary ?? form.bodySummary
|
||||
form.assistDept = source.assistDept ?? source.assistDepartmentName ?? form.assistDept
|
||||
form.executorAccount = source.executorAccount ?? form.executorAccount
|
||||
form.contractBodyFileName = source.contractBodyFileName ?? form.contractBodyFileName
|
||||
form.selfCode = source.selfCode ?? form.selfCode
|
||||
form.mainContractCode = source.mainContractCode ?? form.mainContractCode
|
||||
form.signingSubjectCode = source.signingSubjectCode ?? source.orgId ?? form.signingSubjectCode
|
||||
form.invoiceDate = source.invoiceDate ? String(source.invoiceDate).slice(0, 10) : form.invoiceDate
|
||||
form.receiveDate = source.receiveDate ? String(source.receiveDate).slice(0, 10) : form.receiveDate
|
||||
form.contractBodyFileName = fileState.contractBodyFileName
|
||||
form.contractBodyFilePath = fileState.contractBodyFilePath
|
||||
form.contractBodyFileType = fileState.contractBodyFileType
|
||||
form.contractBodyFileSize = fileState.contractBodyFileSize
|
||||
form.contractBodyArrayBuffer = source.contractBodyArrayBuffer
|
||||
? (source.contractBodyArrayBuffer instanceof ArrayBuffer ? source.contractBodyArrayBuffer.slice(0) : null)
|
||||
: null
|
||||
form.otherAttachmentList = [...(source.otherAttachmentList || source.attachmentList || [])]
|
||||
form.partyState = copyPartyState(source.partyState)
|
||||
form.otherAttachmentList = fileState.otherAttachmentList
|
||||
form.contractGistFileList = fileState.contractGistFileList
|
||||
form.partyState = copyPartyState(source.partyState || source)
|
||||
form.isInvolveHongKongMacaoTaiwan = source.isInvolveHongKongMacaoTaiwan ?? form.isInvolveHongKongMacaoTaiwan
|
||||
form.isPublicTender = source.isPublicTender ?? form.isPublicTender
|
||||
form.isForeignRelatedSubject = source.isForeignRelatedSubject ?? form.isForeignRelatedSubject
|
||||
form.isIncludedInCurrentYearBudget = source.isIncludedInCurrentYearBudget ?? form.isIncludedInCurrentYearBudget
|
||||
form.isShared = source.isShared ?? form.isShared
|
||||
form.isMajorContract = source.isMajorContract ?? form.isMajorContract
|
||||
form.signDate = source.signDate ?? form.signDate
|
||||
form.signDate = source.signDate ? String(source.signDate).slice(0, 10) : form.signDate
|
||||
form.extraAmounts = {
|
||||
depositEnabled: false, deposit: 0,
|
||||
pledgeEnabled: false, pledge: 0,
|
||||
advanceEnabled: false, advance: 0,
|
||||
guaranteeEnabled: false, guarantee: 0,
|
||||
...(source.extraAmounts || {}),
|
||||
}
|
||||
form.extraAmounts = restoreExtraAmounts(source, form.extraAmounts)
|
||||
form.ewAmountType = source.ewAmountType ?? form.ewAmountType
|
||||
form.djAmount = source.djAmount ?? form.djAmount
|
||||
form.yjAmount = source.yjAmount ?? form.yjAmount
|
||||
form.yfkAmount = source.yfkAmount ?? form.yfkAmount
|
||||
form.bzjAmount = source.bzjAmount ?? form.bzjAmount
|
||||
form.performancePlans = (source.performancePlans || []).map((item) => ({
|
||||
...createPerformancePlan(),
|
||||
...item,
|
||||
planTime: item.planTime ? String(item.planTime).slice(0, 10) : '',
|
||||
}))
|
||||
|
||||
return form
|
||||
@@ -490,28 +686,77 @@ function copyContractFormData(data = {}) {
|
||||
/** 将合同表单数据转换为模板变量字典(用于 docxtemplater 渲染) */
|
||||
function buildTemplatePreviewData(formData, templateLabel = '', creationMode = 'template') {
|
||||
const form = copyContractFormData(formData)
|
||||
const attachmentSummary = form.otherAttachmentList.length
|
||||
? form.otherAttachmentList.map((item) => item.name).join(';')
|
||||
: '暂无附件'
|
||||
const attachmentSummary = formatFileSummary(form.otherAttachmentList)
|
||||
const contractGistFileSummary = formatFileSummary(form.contractGistFileList)
|
||||
const partyA = form.partyState.partyA || {}
|
||||
const partyB = form.partyState.partyB || {}
|
||||
const partyC = form.partyState.partyC || {}
|
||||
|
||||
return {
|
||||
contractName: form.contractName || '未填写',
|
||||
contractCode: form.contractCode || '未填写',
|
||||
contractType: form.contractType || '未填写',
|
||||
contractType: getOptionLabel(contractTypeOptions, form.contractType) || form.contractType || '未填写',
|
||||
contractTypeCode: form.contractTypeCode || '',
|
||||
contractAmountDisplay: formatContractAmount(form.contractAmount),
|
||||
contractAmount: form.contractAmount ?? '',
|
||||
effectiveStart: form.effectiveStart || '--',
|
||||
effectiveEnd: form.effectiveEnd || '--',
|
||||
effectivePeriod: `${form.effectiveStart || '--'} 至 ${form.effectiveEnd || '--'}`,
|
||||
effectivePeriod: form.contractPeriodType === '1'
|
||||
? form.periodExplain || '无固定期限'
|
||||
: `${form.effectiveStart || '--'} 至 ${form.effectiveEnd || '--'}`,
|
||||
contractPeriodTypeLabel: getContractPeriodTypeLabel(form.contractPeriodType),
|
||||
periodExplain: form.periodExplain || '',
|
||||
paymentMethod: form.paymentMethod || '未填写',
|
||||
paymentDirectionLabel: getOptionLabel(paymentDirectionOptions, form.paymentDirection),
|
||||
valuationModeLabel: getOptionLabel(valuationModeOptions, form.valuationMode),
|
||||
currencyNameLabel: getOptionLabel(currencyOptions, form.currencyName),
|
||||
sealTypeLabel: getOptionLabel(sealTypeOptions, form.sealType),
|
||||
textSourceLabel: getOptionLabel(textSourceOptions, form.textSource),
|
||||
isElectronLabel: getOptionLabel(isElectronOptions, form.isElectron),
|
||||
contractSourceLabel: getOptionLabel(contractSourceOptions, form.contractSource),
|
||||
dateTypeLabel: getOptionLabel(dateTypeOptions, form.dateType),
|
||||
mandateType: form.mandateType || '',
|
||||
cargoType: form.cargoType || '',
|
||||
primaryContent: form.primaryContent || form.cargoType || '',
|
||||
amountExplain: form.amountExplain || '',
|
||||
contractGistRemark: form.contractGistRemark || '',
|
||||
bodySummary: form.bodySummary || form.primaryContent || '',
|
||||
assistDept: form.assistDept || '',
|
||||
executorAccount: form.executorAccount || '',
|
||||
selfCode: form.selfCode || '',
|
||||
mainContractCode: form.mainContractCode || '',
|
||||
signingSubjectCode: form.signingSubjectCode || '',
|
||||
invoiceDate: form.invoiceDate || '',
|
||||
receiveDate: form.receiveDate || '',
|
||||
counterpartySummary: getPartySummary(form.partyState),
|
||||
attachmentSummary,
|
||||
contractGistFileSummary,
|
||||
contractBodyFileName: form.contractBodyFileName || '',
|
||||
extraAmountSummary: buildExtraAmountSummary(form.extraAmounts),
|
||||
performancePlanSummary: buildPerformancePlanSummary(form.performancePlans),
|
||||
templateLabel: templateLabel || (creationMode === 'manual' ? '手动创建' : ''),
|
||||
signDate: form.signDate || dayjs().format('YYYY-MM-DD'),
|
||||
partyAName: form.partyState.partyA.companyName || '',
|
||||
partyBName: form.partyState.partyB.companyName || '',
|
||||
partyCName: form.partyState.isThreePartyContract ? form.partyState.partyC.companyName || '' : '',
|
||||
partyAName: partyA.companyName || '',
|
||||
partyBName: partyB.companyName || '',
|
||||
partyCName: form.partyState.isThreePartyContract ? partyC.companyName || '' : '',
|
||||
partyAContactName: partyA.contactName || '',
|
||||
partyBContactName: partyB.contactName || '',
|
||||
partyCContactName: partyC.contactName || '',
|
||||
partyAContactPhone: partyA.contactPhone || '',
|
||||
partyBContactPhone: partyB.contactPhone || '',
|
||||
partyCContactPhone: partyC.contactPhone || '',
|
||||
partyAAddress: partyA.registeredAddress || partyA.contactAddress || '',
|
||||
partyBAddress: partyB.registeredAddress || partyB.contactAddress || '',
|
||||
partyCAddress: partyC.registeredAddress || partyC.contactAddress || '',
|
||||
partyACreditCode: partyA.creditCode || '',
|
||||
partyBCreditCode: partyB.creditCode || '',
|
||||
partyCCreditCode: partyC.creditCode || '',
|
||||
partyABankName: partyA.bankName || '',
|
||||
partyBBankName: partyB.bankName || '',
|
||||
partyCBankName: partyC.bankName || '',
|
||||
partyABankAccount: partyA.bankAccount || '',
|
||||
partyBBankAccount: partyB.bankAccount || '',
|
||||
partyCBankAccount: partyC.bankAccount || '',
|
||||
}
|
||||
}
|
||||
|
||||
@@ -536,6 +781,11 @@ function buildContractSubmitPayload(formData, extra = {}) {
|
||||
contractPeriodType: form.contractPeriodType,
|
||||
periodExplain: form.periodExplain,
|
||||
paymentMethod: form.paymentMethod,
|
||||
paymentDirection: form.paymentDirection,
|
||||
isElectron: form.isElectron,
|
||||
textSource: form.textSource,
|
||||
contractSource: form.contractSource,
|
||||
dateType: form.dateType,
|
||||
valuationMode: form.valuationMode,
|
||||
currencyName: form.currencyName,
|
||||
sealType: form.sealType,
|
||||
@@ -543,8 +793,15 @@ function buildContractSubmitPayload(formData, extra = {}) {
|
||||
cargoType: form.cargoType,
|
||||
primaryContent: form.primaryContent,
|
||||
amountExplain: form.amountExplain,
|
||||
contractGistRemark: form.contractGistRemark,
|
||||
bodySummary: form.bodySummary,
|
||||
assistDept: form.assistDept,
|
||||
executorAccount: form.executorAccount,
|
||||
selfCode: form.selfCode,
|
||||
mainContractCode: form.mainContractCode,
|
||||
signingSubjectCode: form.signingSubjectCode,
|
||||
invoiceDate: form.invoiceDate,
|
||||
receiveDate: form.receiveDate,
|
||||
},
|
||||
partyMaintenance: {
|
||||
isThreePartyContract: form.partyState.isThreePartyContract ? 1 : 0,
|
||||
@@ -557,16 +814,29 @@ function buildContractSubmitPayload(formData, extra = {}) {
|
||||
startTime: form.effectiveStart,
|
||||
endTime: form.effectiveEnd,
|
||||
periodExplain: form.periodExplain,
|
||||
dateType: form.dateType,
|
||||
valuationMode: form.valuationMode,
|
||||
currencyName: form.currencyName,
|
||||
sealType: form.sealType,
|
||||
isText: form.textSource,
|
||||
isElectron: form.isElectron,
|
||||
paymentDirection: form.paymentDirection,
|
||||
primaryContent: form.primaryContent,
|
||||
amountExplain: form.amountExplain,
|
||||
contractGistRemark: form.contractGistRemark,
|
||||
isMajorContract: form.isMajorContract ? 1 : 0,
|
||||
contractOpposites: buildContractOpposites(form.partyState),
|
||||
otherAttach: form.otherAttachmentList.map((item) => ({
|
||||
fileName: item.name,
|
||||
fileSize: item.size,
|
||||
fileName: item.name || item.fileName,
|
||||
filePath: item.filePath,
|
||||
fileType: item.fileType,
|
||||
fileSize: item.size ?? item.fileSize,
|
||||
})),
|
||||
contractGistFile: form.contractGistFileList.map((item) => ({
|
||||
fileName: item.name || item.fileName,
|
||||
filePath: item.filePath,
|
||||
fileType: item.fileType,
|
||||
fileSize: item.size ?? item.fileSize,
|
||||
})),
|
||||
contractBodyFileName: form.contractBodyFileName,
|
||||
performancePlans: form.performancePlans,
|
||||
@@ -639,14 +909,21 @@ const launchFieldList = computed(() => {
|
||||
return fieldKeys.map((key) => ({
|
||||
key,
|
||||
label: templateFieldLabelMap.value[key] || key,
|
||||
readonly: Object.prototype.hasOwnProperty.call(templateVariables.value, key),
|
||||
}));
|
||||
});
|
||||
|
||||
function getLaunchFieldValue(fieldKey) {
|
||||
if (Object.prototype.hasOwnProperty.call(templateVariables.value, fieldKey)) {
|
||||
return templateVariables.value[fieldKey] ?? "";
|
||||
}
|
||||
return contractDetailState.value[fieldKey] ?? "";
|
||||
}
|
||||
|
||||
function setLaunchFieldValue(fieldKey, value) {
|
||||
if (Object.prototype.hasOwnProperty.call(templateVariables.value, fieldKey)) {
|
||||
return;
|
||||
}
|
||||
contractDetailState.value[fieldKey] = value;
|
||||
}
|
||||
|
||||
@@ -713,7 +990,11 @@ watch(
|
||||
|
||||
if (mode === "template" && prevMode === "manual") {
|
||||
contractDetailState.value.contractBodyFileName = "";
|
||||
contractDetailState.value.contractBodyFilePath = "";
|
||||
contractDetailState.value.contractBodyFileType = "";
|
||||
contractDetailState.value.contractBodyFileSize = null;
|
||||
contractDetailState.value.contractBodyArrayBuffer = null;
|
||||
contractDetailState.value.textSource = "standardText";
|
||||
syncManualBodyFileList();
|
||||
}
|
||||
|
||||
@@ -846,7 +1127,10 @@ async function buildFilledDocumentBlob() {
|
||||
if (mappings.length) {
|
||||
mappings.forEach((item) => {
|
||||
if (!item.variableKey) return;
|
||||
renderData[item.variableKey] = sourceValues[item.bindField] ?? "";
|
||||
renderData[item.variableKey] =
|
||||
sourceValues[item.bindField] ??
|
||||
contractDetailState.value[item.bindField] ??
|
||||
"";
|
||||
});
|
||||
} else {
|
||||
Object.assign(renderData, sourceValues);
|
||||
@@ -930,7 +1214,20 @@ async function handleManualContractBodyUpload(file) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
const uploadRes = await uploadLaunchFile(formData);
|
||||
if (String(uploadRes?.code || "") !== "0000" || !uploadRes?.result) {
|
||||
message.error(uploadRes?.message || uploadRes?.msg || "合同正文上传失败");
|
||||
return false;
|
||||
}
|
||||
|
||||
contractDetailState.value.contractBodyFileName = file.name;
|
||||
contractDetailState.value.contractBodyFilePath = uploadRes.result;
|
||||
contractDetailState.value.contractBodyFileType =
|
||||
file.name.split(".").pop()?.toLowerCase() || "docx";
|
||||
contractDetailState.value.contractBodyFileSize = file.size;
|
||||
contractDetailState.value.textSource = "draftBySelf";
|
||||
contractDetailState.value.contractBodyArrayBuffer = await readFileAsArrayBuffer(
|
||||
file,
|
||||
);
|
||||
@@ -944,6 +1241,9 @@ async function handleManualContractBodyUpload(file) {
|
||||
|
||||
function handleRemoveManualContractBody() {
|
||||
contractDetailState.value.contractBodyFileName = "";
|
||||
contractDetailState.value.contractBodyFilePath = "";
|
||||
contractDetailState.value.contractBodyFileType = "";
|
||||
contractDetailState.value.contractBodyFileSize = null;
|
||||
contractDetailState.value.contractBodyArrayBuffer = null;
|
||||
syncManualBodyFileList();
|
||||
previewBlob.value = null;
|
||||
@@ -952,11 +1252,38 @@ function handleRemoveManualContractBody() {
|
||||
return true;
|
||||
}
|
||||
|
||||
async function uploadBlobAsLaunchFile(blob, fileName) {
|
||||
const file = new File([blob], fileName, {
|
||||
type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
});
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
const uploadRes = await uploadLaunchFile(formData);
|
||||
if (String(uploadRes?.code || "") !== "0000" || !uploadRes?.result) {
|
||||
throw new Error(uploadRes?.message || uploadRes?.msg || "合同正文上传失败");
|
||||
}
|
||||
contractDetailState.value.contractBodyFileName = fileName;
|
||||
contractDetailState.value.contractBodyFilePath = uploadRes.result;
|
||||
contractDetailState.value.contractBodyFileType = "docx";
|
||||
contractDetailState.value.contractBodyFileSize = file.size;
|
||||
}
|
||||
|
||||
async function ensureTemplateBodyFileUploaded() {
|
||||
if (creationMode.value !== "template") return;
|
||||
const blob = previewBlob.value || (await buildFilledDocumentBlob());
|
||||
previewBlob.value = blob;
|
||||
const fileName = `${contractDetailState.value.contractName || "合同正文"}.docx`;
|
||||
await uploadBlobAsLaunchFile(blob, fileName);
|
||||
}
|
||||
|
||||
function validateMainForm() {
|
||||
if (creationMode.value === "template" && !templateKey.value)
|
||||
return "请选择合同模板";
|
||||
|
||||
for (const field of launchFieldList.value) {
|
||||
if (field.readonly) {
|
||||
continue;
|
||||
}
|
||||
const value = getLaunchFieldValue(field.key);
|
||||
if (value === null || value === undefined || value === "") {
|
||||
return `请输入${field.label}`;
|
||||
@@ -971,21 +1298,26 @@ function validateMainForm() {
|
||||
return "";
|
||||
}
|
||||
|
||||
function handleSave() {
|
||||
async function handleSave() {
|
||||
const errorMessage = validateMainForm();
|
||||
if (errorMessage) {
|
||||
message.warning(errorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
emit("save", {
|
||||
creationMode: creationMode.value,
|
||||
templateKey: templateKey.value,
|
||||
templateName: activeTemplate.value?.templateName || "",
|
||||
detailState: copyContractFormData(contractDetailState.value),
|
||||
});
|
||||
visible.value = false;
|
||||
message.success("合同正文配置已保存");
|
||||
try {
|
||||
await ensureTemplateBodyFileUploaded();
|
||||
emit("save", {
|
||||
creationMode: creationMode.value,
|
||||
templateKey: templateKey.value,
|
||||
templateName: activeTemplate.value?.templateName || "",
|
||||
detailState: copyContractFormData(contractDetailState.value),
|
||||
});
|
||||
visible.value = false;
|
||||
message.success("合同正文配置已保存");
|
||||
} catch (error) {
|
||||
message.warning(error?.message || "合同正文保存失败");
|
||||
}
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
<template>
|
||||
<BaseMixedSignModal ref="innerRef" />
|
||||
<BaseMixedSignModal ref="innerRef" @business-updated="emit('business-updated', $event)" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import BaseMixedSignModal from '../../../seal/mixedSignModal.vue'
|
||||
|
||||
const emit = defineEmits(['business-updated'])
|
||||
const innerRef = ref(null)
|
||||
|
||||
function showModal(...args) {
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
export const contractTypeOptions = [
|
||||
{ label: "MM 买卖合同", value: "MM", simpleLabel: "买卖合同" },
|
||||
{ label: "GY 供用电、水、气、热力合同", value: "GY", simpleLabel: "供用电、水、气、热力合同" },
|
||||
{ label: "ZY 赠与合同", value: "ZY", simpleLabel: "赠与合同" },
|
||||
{ label: "JK 借款合同", value: "JK", simpleLabel: "借款合同" },
|
||||
{ label: "BZ 保证合同", value: "BZ", simpleLabel: "保证合同" },
|
||||
{ label: "ZL 租赁合同", value: "ZL", simpleLabel: "租赁合同" },
|
||||
{ label: "RZ 融资租赁合同", value: "RZ", simpleLabel: "融资租赁合同" },
|
||||
{ label: "BL 保理合同", value: "BL", simpleLabel: "保理合同" },
|
||||
{ label: "CL 承揽合同", value: "CL", simpleLabel: "承揽合同" },
|
||||
{ label: "GC 建设工程合同", value: "GC", simpleLabel: "建设工程合同" },
|
||||
{ label: "YS 运输合同", value: "YS", simpleLabel: "运输合同" },
|
||||
{ label: "JS 技术合同", value: "JS", simpleLabel: "技术合同" },
|
||||
{ label: "BG 保管合同", value: "BG", simpleLabel: "保管合同" },
|
||||
{ label: "CC 仓储合同", value: "CC", simpleLabel: "仓储合同" },
|
||||
{ label: "WT 委托合同", value: "WT", simpleLabel: "委托合同" },
|
||||
{ label: "WY 物业服务合同", value: "WY", simpleLabel: "物业服务合同" },
|
||||
{ label: "HJ 行纪合同", value: "HJ", simpleLabel: "行纪合同" },
|
||||
{ label: "ZJ 中介合同", value: "ZJ", simpleLabel: "中介合同" },
|
||||
{ label: "HH 合伙合同", value: "HH", simpleLabel: "合伙合同" },
|
||||
{ label: "YG 用工合同", value: "YG", simpleLabel: "用工合同" },
|
||||
{ label: "YW 业务合同", value: "YW", simpleLabel: "业务合同" },
|
||||
{ label: "QT 其他合同", value: "QT", simpleLabel: "其他合同" },
|
||||
{ label: "YWBFP 采购合同 / 保税油业务合同", value: "YWBFP", simpleLabel: "采购合同" },
|
||||
{ label: "YWBFS 销售合同 / 保税油业务合同", value: "YWBFS", simpleLabel: "销售合同" },
|
||||
{ label: "YWIOP 采购合同 / 国际原油贸易业务合同", value: "YWIOP", simpleLabel: "采购合同" },
|
||||
{ label: "YWIOS 销售合同 / 国际原油贸易业务合同", value: "YWIOS", simpleLabel: "销售合同" },
|
||||
{ label: "YWDOP 采购合同 / 国内油品贸易业务合同", value: "YWDOP", simpleLabel: "采购合同" },
|
||||
{ label: "YWDOS 销售合同 / 国内油品贸易业务合同", value: "YWDOS", simpleLabel: "销售合同" },
|
||||
{ label: "YWBCP 采购合同 / 大宗贸易业务合同", value: "YWBCP", simpleLabel: "采购合同" },
|
||||
{ label: "YWBCS 销售合同 / 大宗贸易业务合同", value: "YWBCS", simpleLabel: "销售合同" },
|
||||
{ label: "YWOTP 采购合同 / 其他业务合同", value: "YWOTP", simpleLabel: "采购合同" },
|
||||
{ label: "YWOTS 销售合同 / 其他业务合同", value: "YWOTS", simpleLabel: "销售合同" },
|
||||
];
|
||||
|
||||
export const valuationModeOptions = [
|
||||
{ label: "无金额", value: "0" },
|
||||
{ label: "固定总价", value: "1" },
|
||||
{ label: "预估总价", value: "2" },
|
||||
{ label: "无法预估总价", value: "3" },
|
||||
{ label: "固定单价", value: "4" },
|
||||
];
|
||||
|
||||
export const currencyOptions = [
|
||||
{ label: "人民币", value: "RMB" },
|
||||
{ label: "美元", value: "USD" },
|
||||
{ label: "港币", value: "HKD" },
|
||||
{ label: "欧元", value: "EUR" },
|
||||
{ label: "印尼盾", value: "IDR" },
|
||||
];
|
||||
|
||||
export const sealTypeOptions = [
|
||||
{ label: "公章", value: "GZ" },
|
||||
{ label: "合同专用章", value: "HTZYZ" },
|
||||
{ label: "公章+法定代表人私章", value: "GZFDDBRSZ" },
|
||||
{ label: "公章+法定代表人签字", value: "GZFDDBRQZ" },
|
||||
{ label: "公章+被授权人签字", value: "GZBSQRQZ" },
|
||||
{ label: "合同专用章+被授权人签字", value: "HTZYZBSQRQZ" },
|
||||
{ label: "合同专用章+法定代表人签字", value: "HTZYZFDDBRQZ" },
|
||||
{ label: "其他", value: "QT" },
|
||||
];
|
||||
|
||||
export const textSourceOptions = [
|
||||
{ label: "推荐文本", value: "standardText" },
|
||||
{ label: "示范文本", value: "exampleText" },
|
||||
{ label: "自行起草", value: "draftBySelf" },
|
||||
{ label: "对方文本", value: "otherText" },
|
||||
];
|
||||
|
||||
export const paymentDirectionOptions = [
|
||||
{ label: "付款", value: "1" },
|
||||
{ label: "收款", value: "2" },
|
||||
{ label: "收支双向", value: "3" },
|
||||
{ label: "无", value: "0" },
|
||||
];
|
||||
|
||||
export const isElectronOptions = [
|
||||
{ label: "电子合同", value: "1" },
|
||||
{ label: "纸质合同", value: "0" },
|
||||
];
|
||||
|
||||
export const dateTypeOptions = [
|
||||
{ label: "年", value: "0" },
|
||||
{ label: "月", value: "1" },
|
||||
{ label: "日", value: "2" },
|
||||
];
|
||||
|
||||
export const contractSourceOptions = [
|
||||
{ label: "结算平台-ECP", value: "ECP" },
|
||||
{ label: "大宗平台-DSSP", value: "DSSP" },
|
||||
];
|
||||
|
||||
export const yesNoBooleanOptions = [
|
||||
{ label: "是", value: true },
|
||||
{ label: "否", value: false },
|
||||
];
|
||||
|
||||
export const extraAmountFieldOptions = [
|
||||
{ key: "deposit", label: "定金", code: "DJ", amountField: "djAmount" },
|
||||
{ key: "pledge", label: "押金", code: "YJ", amountField: "yjAmount" },
|
||||
{ key: "advance", label: "预付款", code: "YFK", amountField: "yfkAmount" },
|
||||
{ key: "guarantee", label: "保证金", code: "BZJ", amountField: "bzjAmount" },
|
||||
];
|
||||
|
||||
const labelMap = new Map([
|
||||
...contractTypeOptions,
|
||||
...valuationModeOptions,
|
||||
...currencyOptions,
|
||||
...sealTypeOptions,
|
||||
...textSourceOptions,
|
||||
...paymentDirectionOptions,
|
||||
...isElectronOptions,
|
||||
...dateTypeOptions,
|
||||
...contractSourceOptions,
|
||||
].map((item) => [item.value, item.label]));
|
||||
|
||||
export function getOptionLabel(options, value) {
|
||||
return options.find((item) => item.value === value)?.label || value || "";
|
||||
}
|
||||
|
||||
export function getCommonOptionLabel(value) {
|
||||
return labelMap.get(value) || value || "";
|
||||
}
|
||||
|
||||
export function getContractTypeSimpleLabel(value) {
|
||||
return contractTypeOptions.find((item) => item.value === value)?.simpleLabel || value || "";
|
||||
}
|
||||
+260
-218
@@ -10,23 +10,17 @@
|
||||
<a-row :gutter="16" style="width: 100%">
|
||||
<a-col :md="6" :sm="24">
|
||||
<a-form-item label="合同名称">
|
||||
<a-input
|
||||
size="small"
|
||||
v-model:value="queryParam.contractName"
|
||||
allow-clear
|
||||
placeholder="请输入合同名称"
|
||||
/>
|
||||
<a-input v-model:value="queryParam.contractName" allow-clear placeholder="请输入合同名称" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
|
||||
<a-col :md="6" :sm="24">
|
||||
<a-form-item label="合同编码">
|
||||
<a-input
|
||||
size="small"
|
||||
v-model:value="queryParam.contractCode"
|
||||
allow-clear
|
||||
placeholder="请输入合同编码"
|
||||
/>
|
||||
<a-input v-model:value="queryParam.contractCode" allow-clear placeholder="请输入合同编码" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :md="6" :sm="24">
|
||||
<a-form-item label="合同类型">
|
||||
<a-input v-model:value="queryParam.contractType" allow-clear placeholder="请输入合同类型编码" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :md="6" :sm="24">
|
||||
@@ -61,32 +55,10 @@
|
||||
<vxe-toolbar ref="toolbarRef" custom>
|
||||
<template #buttons>
|
||||
<a-space wrap>
|
||||
<a-button
|
||||
type="primary"
|
||||
:icon="h(SearchOutlined)"
|
||||
@click="handleQuery"
|
||||
>
|
||||
查询
|
||||
</a-button>
|
||||
<a-button :icon="h(RedoOutlined)" @click="handleReset">
|
||||
重置
|
||||
</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
ghost
|
||||
:icon="h(PlusOutlined)"
|
||||
@click="handleCreate"
|
||||
>
|
||||
发起合同
|
||||
</a-button>
|
||||
<a-button
|
||||
danger
|
||||
:icon="h(DeleteOutlined)"
|
||||
:disabled="!selectedRowKeys.length"
|
||||
@click="handleBatchDelete"
|
||||
>
|
||||
删除
|
||||
</a-button>
|
||||
<a-button type="primary" :icon="h(SearchOutlined)" @click="handleQuery">查询</a-button>
|
||||
<a-button :icon="h(RedoOutlined)" @click="handleReset">重置</a-button>
|
||||
<a-button type="primary" ghost :icon="h(PlusOutlined)" @click="handleCreate">发起合同</a-button>
|
||||
<a-button danger :icon="h(DeleteOutlined)" :disabled="!selectedRowKeys.length" @click="handleBatchDelete">删除</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</vxe-toolbar>
|
||||
@@ -102,91 +74,47 @@
|
||||
:checkbox-config="{ range: true }"
|
||||
:data="tableData"
|
||||
:loading="loading"
|
||||
:loading-config="{
|
||||
icon: 'vxe-icon-indicator roll',
|
||||
text: '正在拼命加载中...',
|
||||
}"
|
||||
:loading-config="{ icon: 'vxe-icon-indicator roll', text: '正在拼命加载中...' }"
|
||||
@checkbox-range-change="selectChangeEvent"
|
||||
@checkbox-change="selectChangeEvent"
|
||||
@checkbox-all="selectChangeEvent"
|
||||
>
|
||||
<vxe-column type="checkbox" width="56" />
|
||||
<vxe-column type="seq" title="序号" width="68" />
|
||||
<vxe-column
|
||||
field="contractCode"
|
||||
title="合同编码"
|
||||
width="150"
|
||||
show-overflow="title"
|
||||
/>
|
||||
<vxe-column
|
||||
field="contractName"
|
||||
title="合同名称"
|
||||
width="220"
|
||||
show-overflow="title"
|
||||
/>
|
||||
<vxe-column
|
||||
field="contractType"
|
||||
title="合同类型"
|
||||
width="110"
|
||||
show-overflow="title"
|
||||
/>
|
||||
<vxe-column
|
||||
field="templateName"
|
||||
title="所选模板"
|
||||
width="150"
|
||||
show-overflow="title"
|
||||
/>
|
||||
<vxe-column
|
||||
field="counterpartySummary"
|
||||
title="合同相对方"
|
||||
width="260"
|
||||
show-overflow="title"
|
||||
/>
|
||||
<vxe-column
|
||||
field="status"
|
||||
title="状态"
|
||||
width="120"
|
||||
show-overflow="title"
|
||||
>
|
||||
<vxe-column field="contractCode" title="合同编码" width="160" show-overflow="title" />
|
||||
<vxe-column field="lawContractCode" title="法务合同编号" width="160" show-overflow="title" />
|
||||
<vxe-column field="contractName" title="合同名称" width="220" show-overflow="title" />
|
||||
<vxe-column field="contractType" title="合同类型" width="160" show-overflow="title">
|
||||
<template #default="{ row }">
|
||||
<a-tag :color="getStatusColor(row.status)">
|
||||
{{ getStatusLabel(row.status) }}
|
||||
</a-tag>
|
||||
{{ getContractTypeSimpleLabel(row.contractTypeCode || row.contractType) }}
|
||||
</template>
|
||||
</vxe-column>
|
||||
|
||||
<vxe-column
|
||||
field="initiator"
|
||||
title="发起人"
|
||||
width="100"
|
||||
show-overflow="title"
|
||||
/>
|
||||
<vxe-column
|
||||
field="initiatorDept"
|
||||
title="所属部门"
|
||||
width="140"
|
||||
show-overflow="title"
|
||||
/>
|
||||
<vxe-column
|
||||
field="createTime"
|
||||
title="发起时间"
|
||||
width="170"
|
||||
show-overflow="title"
|
||||
/>
|
||||
<vxe-column
|
||||
field="updateTime"
|
||||
title="最后更新时间"
|
||||
width="170"
|
||||
show-overflow="title"
|
||||
/>
|
||||
<vxe-column title="操作" width="260" fixed="right">
|
||||
<vxe-column field="templateName" title="所选模板" width="180" show-overflow="title" />
|
||||
<vxe-column field="counterpartySummary" title="合同相对方" width="260" show-overflow="title" />
|
||||
<vxe-column field="status" title="状态" width="120">
|
||||
<template #default="{ row }">
|
||||
<a-tag :color="getStatusColor(row.status)">{{ getStatusLabel(row.status) }}</a-tag>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="approveMessage" title="审批说明" width="220" show-overflow="title" />
|
||||
<vxe-column field="initiator" title="发起人" width="100" show-overflow="title" />
|
||||
<vxe-column field="initiatorDept" title="所属部门" width="140" show-overflow="title" />
|
||||
<vxe-column field="createTime" title="发起时间" width="170" show-overflow="title" />
|
||||
<vxe-column field="updateTime" title="最后更新时间" width="170" show-overflow="title" />
|
||||
<vxe-column title="操作" width="520" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<div class="table-actions">
|
||||
<a @click="handleView(row)">查看</a>
|
||||
<a v-if="canEdit(row)" @click="handleEdit(row)">编辑</a>
|
||||
<a v-if="canSubmit(row)" @click="handleSubmit(row)">提交法务</a>
|
||||
<a v-if="canMockApprove(row)" @click="handleMockApprove(row)">测试通过</a>
|
||||
<a v-if="canMockReject(row)" @click="handleMockReject(row)">测试驳回</a>
|
||||
<a v-if="canOnlineSign(row)" @click="handleOnlineSign(row)">
|
||||
{{ row.status === "completed" ? "查看签署" : "在线签订" }}
|
||||
</a>
|
||||
<a v-if="canMockFinishSign(row)" @click="handleMockFinishSign(row)">测试签署完成</a>
|
||||
<a v-if="canCollect(row)" @click="handleMockCollect(row)">测试采集</a>
|
||||
<a v-if="canArchive(row)" @click="handleArchive(row)">测试归档</a>
|
||||
<a v-if="canDelete(row)" @click="handleDelete(row)">删除</a>
|
||||
</div>
|
||||
</template>
|
||||
@@ -208,8 +136,8 @@
|
||||
/>
|
||||
</a-card>
|
||||
|
||||
<ContractPartyPlaceholderModal ref="contractModalRef" />
|
||||
<MixedSignModal ref="mixedSignModalRef" />
|
||||
<ContractPartyPlaceholderModal ref="contractModalRef" @save="handleModalSave" />
|
||||
<MixedSignModal ref="mixedSignModalRef" @business-updated="handleSignBusinessUpdated" />
|
||||
</section>
|
||||
</template>
|
||||
|
||||
@@ -224,17 +152,32 @@ import {
|
||||
import { message, Modal } from "ant-design-vue";
|
||||
import ContractPartyPlaceholderModal from "./components/ContractPartyPlaceholderModal.vue";
|
||||
import MixedSignModal from "./components/mixedSignModal.vue";
|
||||
import { deleteLaunchRecord, queryLaunchList } from "./api.js";
|
||||
import { getContractTypeSimpleLabel } from "./contractLaunchOptions.js";
|
||||
import {
|
||||
archiveLaunch,
|
||||
createLaunchDraft,
|
||||
deleteLaunchRecord,
|
||||
getLaunchDetail,
|
||||
mockApproveLaunch,
|
||||
mockCollectLaunch,
|
||||
mockRejectLaunch,
|
||||
finishLaunchSeal,
|
||||
queryLaunchList,
|
||||
submitLaunchRecord,
|
||||
updateLaunchDraft,
|
||||
} from "@/api/launch.js";
|
||||
|
||||
// 页面内字典(后续可由后端接口替换)
|
||||
const contractRecordStatusOptions = [
|
||||
{ label: "草稿", value: "draft" },
|
||||
{ label: "审批中", value: "reviewing" },
|
||||
{ label: "审批驳回", value: "rejected" },
|
||||
{ label: "待签署", value: "pending_sign" },
|
||||
{ label: "签署中", value: "signing" },
|
||||
{ label: "待采集", value: "pending_collect" },
|
||||
{ label: "待归档", value: "pending_archive" },
|
||||
{ label: "已完成", value: "completed" },
|
||||
]
|
||||
{ label: "已终止", value: "terminated" },
|
||||
];
|
||||
|
||||
const formRef = ref(null);
|
||||
const queryContainer = ref(null);
|
||||
@@ -250,7 +193,6 @@ const tableHeight = ref(420);
|
||||
const tableData = ref([]);
|
||||
const selectedRows = ref([]);
|
||||
const selectedRowKeys = ref([]);
|
||||
const allRecords = ref([]);
|
||||
|
||||
const queryParam = ref({
|
||||
pageNo: 1,
|
||||
@@ -259,8 +201,6 @@ const queryParam = ref({
|
||||
contractCode: undefined,
|
||||
contractType: undefined,
|
||||
status: undefined,
|
||||
queryScope: undefined,
|
||||
templateKey: undefined,
|
||||
dateRange: [],
|
||||
});
|
||||
|
||||
@@ -275,18 +215,16 @@ const tableCardBodyStyle = {
|
||||
padding: "8px 12px 6px",
|
||||
};
|
||||
|
||||
const templateQueryOptions = [
|
||||
{ label: "采购合同模板", value: "purchase" },
|
||||
{ label: "销售合同模板", value: "sales" },
|
||||
];
|
||||
|
||||
const statusLabelMap = {
|
||||
draft: "草稿",
|
||||
reviewing: "审批中",
|
||||
rejected: "审批驳回",
|
||||
pending_sign: "待签署",
|
||||
signing: "签署中",
|
||||
pending_collect: "待采集",
|
||||
pending_archive: "待归档",
|
||||
completed: "已完成",
|
||||
terminated: "已终止",
|
||||
};
|
||||
|
||||
const statusColorMap = {
|
||||
@@ -295,19 +233,31 @@ const statusColorMap = {
|
||||
rejected: "error",
|
||||
pending_sign: "warning",
|
||||
signing: "purple",
|
||||
pending_collect: "cyan",
|
||||
pending_archive: "orange",
|
||||
completed: "success",
|
||||
terminated: "default",
|
||||
};
|
||||
|
||||
const scopeLabelMap = {
|
||||
personal: "个人级",
|
||||
company: "公司级",
|
||||
admin: "管理员级",
|
||||
};
|
||||
function isSuccessResponse(res) {
|
||||
return String(res?.code || "") === "0000";
|
||||
}
|
||||
|
||||
function getPageResult(res) {
|
||||
return res?.result || {};
|
||||
}
|
||||
|
||||
function getDataResult(res) {
|
||||
return res?.result || null;
|
||||
}
|
||||
|
||||
function getResponseMessage(res, fallback = "操作失败") {
|
||||
return res?.message || res?.msg || fallback;
|
||||
}
|
||||
|
||||
function connectToolbar() {
|
||||
const table = tableRef.value;
|
||||
const toolbar = toolbarRef.value;
|
||||
|
||||
if (table && toolbar && table.connect) {
|
||||
table.connect(toolbar);
|
||||
}
|
||||
@@ -321,10 +271,6 @@ function getStatusColor(status) {
|
||||
return statusColorMap[status] || "default";
|
||||
}
|
||||
|
||||
function getScopeLabel(scope) {
|
||||
return scopeLabelMap[scope] || scope;
|
||||
}
|
||||
|
||||
function canEdit(row) {
|
||||
return ["draft", "rejected"].includes(row.status);
|
||||
}
|
||||
@@ -333,81 +279,73 @@ function canDelete(row) {
|
||||
return ["draft", "rejected"].includes(row.status);
|
||||
}
|
||||
|
||||
function canSubmit(row) {
|
||||
return ["draft", "rejected"].includes(row.status);
|
||||
}
|
||||
|
||||
function canMockApprove(row) {
|
||||
return row.status === "reviewing";
|
||||
}
|
||||
|
||||
function canMockReject(row) {
|
||||
return row.status === "reviewing";
|
||||
}
|
||||
|
||||
function canOnlineSign(row) {
|
||||
return ["pending_sign", "signing", "completed"].includes(row.status);
|
||||
}
|
||||
|
||||
function canMockFinishSign(row) {
|
||||
return ["pending_sign", "signing"].includes(row.status);
|
||||
}
|
||||
|
||||
function canCollect(row) {
|
||||
return row.status === "pending_collect";
|
||||
}
|
||||
|
||||
function canArchive(row) {
|
||||
return row.status === "pending_archive";
|
||||
}
|
||||
|
||||
function selectChangeEvent({ records }) {
|
||||
selectedRows.value = records;
|
||||
selectedRowKeys.value = records.map((item) => item.id);
|
||||
}
|
||||
|
||||
function filterRecords(records) {
|
||||
return records.filter((item) => {
|
||||
const matchName =
|
||||
!queryParam.value.contractName ||
|
||||
item.contractName.includes(queryParam.value.contractName);
|
||||
|
||||
const matchCode =
|
||||
!queryParam.value.contractCode ||
|
||||
item.contractCode.includes(queryParam.value.contractCode);
|
||||
|
||||
const matchType =
|
||||
!queryParam.value.contractType ||
|
||||
item.contractType === queryParam.value.contractType;
|
||||
|
||||
const matchStatus =
|
||||
!queryParam.value.status || item.status === queryParam.value.status;
|
||||
|
||||
const matchScope =
|
||||
!queryParam.value.queryScope ||
|
||||
item.queryScope === queryParam.value.queryScope;
|
||||
|
||||
const matchTemplate =
|
||||
!queryParam.value.templateKey ||
|
||||
item.templateKey === queryParam.value.templateKey;
|
||||
|
||||
const matchDate = (() => {
|
||||
const range = queryParam.value.dateRange || [];
|
||||
if (!range.length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const day = item.createTime.slice(0, 10);
|
||||
return day >= range[0] && day <= range[1];
|
||||
})();
|
||||
|
||||
return (
|
||||
matchName &&
|
||||
matchCode &&
|
||||
matchType &&
|
||||
matchStatus &&
|
||||
matchScope &&
|
||||
matchTemplate &&
|
||||
matchDate
|
||||
);
|
||||
});
|
||||
function buildQueryPayload() {
|
||||
return {
|
||||
pageNo: queryParam.value.pageNo,
|
||||
pageSize: queryParam.value.pageSize,
|
||||
contractName: queryParam.value.contractName,
|
||||
contractCode: queryParam.value.contractCode,
|
||||
contractType: queryParam.value.contractType,
|
||||
status: queryParam.value.status,
|
||||
dateStart: queryParam.value.dateRange?.[0],
|
||||
dateEnd: queryParam.value.dateRange?.[1],
|
||||
};
|
||||
}
|
||||
|
||||
function getList() {
|
||||
loading.value = true;
|
||||
|
||||
queryLaunchList().then((records) => {
|
||||
// TODO: 这里后续直接使用合同发起列表接口返回值
|
||||
allRecords.value = records;
|
||||
const filtered = filterRecords(allRecords.value);
|
||||
const start = (queryParam.value.pageNo - 1) * queryParam.value.pageSize;
|
||||
const end = start + queryParam.value.pageSize;
|
||||
|
||||
total.value = filtered.length;
|
||||
tableData.value = filtered.slice(start, end);
|
||||
loading.value = false;
|
||||
|
||||
nextTick(() => {
|
||||
connectToolbar();
|
||||
syncTableHeight();
|
||||
queryLaunchList(buildQueryPayload())
|
||||
.then((res) => {
|
||||
if (!isSuccessResponse(res)) {
|
||||
tableData.value = [];
|
||||
total.value = 0;
|
||||
message.error(getResponseMessage(res, "合同发起列表查询失败"));
|
||||
return;
|
||||
}
|
||||
const pageResult = getPageResult(res);
|
||||
tableData.value = pageResult.data || [];
|
||||
total.value = pageResult.totalSize || 0;
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
nextTick(() => {
|
||||
connectToolbar();
|
||||
syncTableHeight();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function handleQuery() {
|
||||
@@ -424,8 +362,6 @@ function handleReset() {
|
||||
contractCode: undefined,
|
||||
contractType: undefined,
|
||||
status: undefined,
|
||||
queryScope: undefined,
|
||||
templateKey: undefined,
|
||||
dateRange: [],
|
||||
});
|
||||
getList();
|
||||
@@ -442,41 +378,144 @@ function handleCreate() {
|
||||
}
|
||||
|
||||
function handleView(row) {
|
||||
contractModalRef.value?.showModal(row, "view");
|
||||
getLaunchDetail(row.id).then((res) => {
|
||||
if (!isSuccessResponse(res) || !getDataResult(res)) {
|
||||
message.error(getResponseMessage(res, "获取合同详情失败"));
|
||||
return;
|
||||
}
|
||||
contractModalRef.value?.showModal(getDataResult(res), "view");
|
||||
});
|
||||
}
|
||||
|
||||
function handleEdit(row) {
|
||||
contractModalRef.value?.showModal(row, "edit");
|
||||
getLaunchDetail(row.id).then((res) => {
|
||||
if (!isSuccessResponse(res) || !getDataResult(res)) {
|
||||
message.error(getResponseMessage(res, "获取合同详情失败"));
|
||||
return;
|
||||
}
|
||||
contractModalRef.value?.showModal(getDataResult(res), "edit");
|
||||
});
|
||||
}
|
||||
|
||||
function handleSubmit(row) {
|
||||
submitLaunchRecord(row.id).then((res) => {
|
||||
if (!isSuccessResponse(res)) {
|
||||
message.error(getResponseMessage(res, "提交法务失败"));
|
||||
return;
|
||||
}
|
||||
message.success("已提交法务,当前为测试环境,后续可由法务系统真实审批");
|
||||
getList();
|
||||
});
|
||||
}
|
||||
|
||||
function handleMockApprove(row) {
|
||||
mockApproveLaunch(row.id, {}).then((res) => {
|
||||
if (!isSuccessResponse(res)) {
|
||||
message.error(getResponseMessage(res, "模拟审批通过失败"));
|
||||
return;
|
||||
}
|
||||
message.success("测试审批通过完成,合同已流转到待签署");
|
||||
getList();
|
||||
});
|
||||
}
|
||||
|
||||
function handleMockReject(row) {
|
||||
mockRejectLaunch(row.id, {}).then((res) => {
|
||||
if (!isSuccessResponse(res)) {
|
||||
message.error(getResponseMessage(res, "模拟审批驳回失败"));
|
||||
return;
|
||||
}
|
||||
message.success("测试审批驳回完成,合同已流转到审批驳回");
|
||||
getList();
|
||||
});
|
||||
}
|
||||
|
||||
function handleMockFinishSign(row) {
|
||||
finishLaunchSeal(row.id, {
|
||||
sealContractId: row.sealContractId || `TEST-SEAL-${row.id}`,
|
||||
sealContractStatus: 2000,
|
||||
signedFileName: `${row.contractName || "合同"}-测试已签署.pdf`,
|
||||
signedFilePath: row.contractTextFilePath || `mock-signed/${row.id}.pdf`,
|
||||
signedFileType: "pdf",
|
||||
signedFileSize: row.contractTextFileSize || null,
|
||||
signedOfflineReason: "测试按钮模拟签署完成,真实签章接入后可删除此按钮",
|
||||
}).then((res) => {
|
||||
if (!isSuccessResponse(res)) {
|
||||
message.error(getResponseMessage(res, "测试签署完成失败"));
|
||||
return;
|
||||
}
|
||||
message.success("测试签署完成,合同已流转到待采集");
|
||||
getList();
|
||||
});
|
||||
}
|
||||
|
||||
function handleOnlineSign(row) {
|
||||
if (!row.sealContractId && !row.signContractId && !row.contractId) {
|
||||
message.info(
|
||||
"当前未接入签署详情接口,将进入在线签署弹窗并保留合同业务信息,你可以继续上传文件后发起签署。",
|
||||
);
|
||||
}
|
||||
mixedSignModalRef.value?.showModal(row);
|
||||
getLaunchDetail(row.id).then((res) => {
|
||||
if (!isSuccessResponse(res) || !getDataResult(res)) {
|
||||
message.error(getResponseMessage(res, "获取合同详情失败"));
|
||||
return;
|
||||
}
|
||||
const detail = getDataResult(res);
|
||||
mixedSignModalRef.value?.showModal({
|
||||
...detail,
|
||||
contractId: detail.sealContractId,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function deleteRowsByIds(ids) {
|
||||
// TODO: 这里后续替换为删除接口后再刷新列表
|
||||
allRecords.value = allRecords.value.filter((item) => !ids.includes(item.id));
|
||||
selectedRows.value = [];
|
||||
selectedRowKeys.value = [];
|
||||
function handleSignBusinessUpdated() {
|
||||
getList();
|
||||
}
|
||||
|
||||
function handleModalSave(payload) {
|
||||
const requestFn = payload.mode === "edit" ? updateLaunchDraft : createLaunchDraft;
|
||||
requestFn(payload.record).then((res) => {
|
||||
if (!isSuccessResponse(res)) {
|
||||
message.error(getResponseMessage(res, "保存合同失败"));
|
||||
return;
|
||||
}
|
||||
message.success(payload.mode === "edit" ? "合同修改成功" : "合同草稿保存成功");
|
||||
getList();
|
||||
});
|
||||
}
|
||||
|
||||
function handleMockCollect(row) {
|
||||
mockCollectLaunch(row.id).then((res) => {
|
||||
if (!isSuccessResponse(res)) {
|
||||
message.error(getResponseMessage(res, "模拟合同采集失败"));
|
||||
return;
|
||||
}
|
||||
message.success("测试合同采集完成,合同已流转到待归档");
|
||||
getList();
|
||||
});
|
||||
}
|
||||
|
||||
function handleArchive(row) {
|
||||
archiveLaunch(row.id, {}).then((res) => {
|
||||
if (!isSuccessResponse(res)) {
|
||||
message.error(getResponseMessage(res, "测试归档失败"));
|
||||
return;
|
||||
}
|
||||
message.success("测试归档完成,合同已流转到已完成");
|
||||
getList();
|
||||
});
|
||||
}
|
||||
|
||||
function handleDelete(row) {
|
||||
Modal.confirm({
|
||||
title: `确认删除合同【${row.contractName}】吗?`,
|
||||
content: "当前仅删除本地 mock 数据,便于后续替换真实接口。",
|
||||
content: "仅草稿或审批驳回状态支持删除。",
|
||||
okText: "删除",
|
||||
cancelText: "取消",
|
||||
okType: "danger",
|
||||
onOk: () => {
|
||||
deleteLaunchRecord([row.id]).then(() => {
|
||||
deleteRowsByIds([row.id]);
|
||||
message.success("已删除发起记录");
|
||||
deleteLaunchRecord(row.id).then((res) => {
|
||||
if (!isSuccessResponse(res)) {
|
||||
message.error(getResponseMessage(res, "删除失败"));
|
||||
return;
|
||||
}
|
||||
message.success("删除成功");
|
||||
getList();
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -488,18 +527,20 @@ function handleBatchDelete() {
|
||||
message.warning("仅草稿或审批驳回状态支持删除,请重新选择");
|
||||
return;
|
||||
}
|
||||
|
||||
Modal.confirm({
|
||||
title: `确认删除选中的 ${selectedRowKeys.value.length} 条记录吗?`,
|
||||
content: "当前仅演示本地数据删除,后续接入真实接口即可替换。",
|
||||
content: "仅草稿或审批驳回状态支持删除。",
|
||||
okText: "删除",
|
||||
cancelText: "取消",
|
||||
okType: "danger",
|
||||
onOk: () => {
|
||||
deleteLaunchRecord(selectedRowKeys.value).then(() => {
|
||||
deleteRowsByIds(selectedRowKeys.value);
|
||||
message.success("批量删除成功");
|
||||
});
|
||||
onOk: async () => {
|
||||
for (const id of selectedRowKeys.value) {
|
||||
await deleteLaunchRecord(id);
|
||||
}
|
||||
message.success("批量删除成功");
|
||||
selectedRows.value = [];
|
||||
selectedRowKeys.value = [];
|
||||
getList();
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -561,6 +602,7 @@ onBeforeUnmount(() => {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.table-pagination {
|
||||
|
||||
@@ -283,16 +283,12 @@ import {
|
||||
queryManifestOrgOptions,
|
||||
uploadTemplateFile,
|
||||
} from '@/api/manifest.js'
|
||||
import { contractTypeTreeOptions } from '../contractTypeOptions.js'
|
||||
|
||||
// ========== 模板管理字典(后续可由后端接口替换) ==========
|
||||
|
||||
// 合同类型选项
|
||||
const contractTypeOptions = [
|
||||
{ label: '采购合同', value: '采购合同' },
|
||||
{ label: '销售合同', value: '销售合同' },
|
||||
{ label: '服务合同', value: '服务合同' },
|
||||
{ label: '框架合同', value: '框架合同' },
|
||||
]
|
||||
const contractTypeOptions = contractTypeTreeOptions
|
||||
|
||||
// 范本类型选项
|
||||
const templateSourceOptions = [
|
||||
@@ -412,7 +408,7 @@ function createDefaultFormState() {
|
||||
templateName: '',
|
||||
templateCode: '',
|
||||
source: '标准模板',
|
||||
contractType: '采购合同',
|
||||
contractType: 'MM',
|
||||
selfPartyRole: '甲方',
|
||||
version: 'V1.0',
|
||||
status: 'draft',
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
export const contractTypeTreeOptions = [
|
||||
{
|
||||
label: "集团合同分类",
|
||||
options: [
|
||||
{ label: "MM 买卖合同", value: "MM", simpleLabel: "买卖合同" },
|
||||
{ label: "GY 供用电、水、气、热力合同", value: "GY", simpleLabel: "供用电、水、气、热力合同" },
|
||||
{ label: "ZY 赠与合同", value: "ZY", simpleLabel: "赠与合同" },
|
||||
{ label: "JK 借款合同", value: "JK", simpleLabel: "借款合同" },
|
||||
{ label: "BZ 保证合同", value: "BZ", simpleLabel: "保证合同" },
|
||||
{ label: "ZL 租赁合同", value: "ZL", simpleLabel: "租赁合同" },
|
||||
{ label: "RZ 融资租赁合同", value: "RZ", simpleLabel: "融资租赁合同" },
|
||||
{ label: "BL 保理合同", value: "BL", simpleLabel: "保理合同" },
|
||||
{ label: "CL 承揽合同", value: "CL", simpleLabel: "承揽合同" },
|
||||
{ label: "GC 建设工程合同", value: "GC", simpleLabel: "建设工程合同" },
|
||||
{ label: "YS 运输合同", value: "YS", simpleLabel: "运输合同" },
|
||||
{ label: "JS 技术合同", value: "JS", simpleLabel: "技术合同" },
|
||||
{ label: "BG 保管合同", value: "BG", simpleLabel: "保管合同" },
|
||||
{ label: "CC 仓储合同", value: "CC", simpleLabel: "仓储合同" },
|
||||
{ label: "WT 委托合同", value: "WT", simpleLabel: "委托合同" },
|
||||
{ label: "WY 物业服务合同", value: "WY", simpleLabel: "物业服务合同" },
|
||||
{ label: "HJ 行纪合同", value: "HJ", simpleLabel: "行纪合同" },
|
||||
{ label: "ZJ 中介合同", value: "ZJ", simpleLabel: "中介合同" },
|
||||
{ label: "HH 合伙合同", value: "HH", simpleLabel: "合伙合同" },
|
||||
{ label: "YG 用工合同", value: "YG", simpleLabel: "用工合同" },
|
||||
{ label: "YW 业务合同", value: "YW", simpleLabel: "业务合同" },
|
||||
{ label: "QT 其他合同", value: "QT", simpleLabel: "其他合同" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "国贸合同分类",
|
||||
options: [
|
||||
{ label: "YWBFP 采购合同 / 保税油业务合同", value: "YWBFP", simpleLabel: "采购合同 / 保税油业务合同" },
|
||||
{ label: "YWBFS 销售合同 / 保税油业务合同", value: "YWBFS", simpleLabel: "销售合同 / 保税油业务合同" },
|
||||
{ label: "YWIOP 采购合同 / 国际原油贸易业务合同", value: "YWIOP", simpleLabel: "采购合同 / 国际原油贸易业务合同" },
|
||||
{ label: "YWIOS 销售合同 / 国际原油贸易业务合同", value: "YWIOS", simpleLabel: "销售合同 / 国际原油贸易业务合同" },
|
||||
{ label: "YWDOP 采购合同 / 国内油品贸易业务合同", value: "YWDOP", simpleLabel: "采购合同 / 国内油品贸易业务合同" },
|
||||
{ label: "YWDOS 销售合同 / 国内油品贸易业务合同", value: "YWDOS", simpleLabel: "销售合同 / 国内油品贸易业务合同" },
|
||||
{ label: "YWBCP 采购合同 / 大宗贸易业务合同", value: "YWBCP", simpleLabel: "采购合同 / 大宗贸易业务合同" },
|
||||
{ label: "YWBCS 销售合同 / 大宗贸易业务合同", value: "YWBCS", simpleLabel: "销售合同 / 大宗贸易业务合同" },
|
||||
{ label: "YWOTP 采购合同 / 其他业务合同", value: "YWOTP", simpleLabel: "采购合同 / 其他业务合同" },
|
||||
{ label: "YWOTS 销售合同 / 其他业务合同", value: "YWOTS", simpleLabel: "销售合同 / 其他业务合同" },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const contractTypeOptions = contractTypeTreeOptions.flatMap((item) => item.options);
|
||||
|
||||
const contractTypeMap = new Map(contractTypeOptions.map((item) => [item.value, item]));
|
||||
|
||||
export function getContractTypeSimpleLabel(value) {
|
||||
return contractTypeMap.get(value)?.simpleLabel || value || "";
|
||||
}
|
||||
@@ -84,7 +84,11 @@
|
||||
<vxe-column type="seq" title="序号" width="68" />
|
||||
<vxe-column field="templateCode" title="模板编码" width="150" show-overflow="title" />
|
||||
<vxe-column field="templateName" title="模板名称" width="180" show-overflow="title" />
|
||||
<vxe-column field="contractType" title="合同类型" width="120" show-overflow="title" />
|
||||
<vxe-column field="contractType" title="合同类型" width="180" show-overflow="title">
|
||||
<template #default="{ row }">
|
||||
{{ getContractTypeSimpleLabel(row.contractType) }}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="source" title="模板来源" width="110" show-overflow="title" />
|
||||
<vxe-column field="version" title="版本" width="90" />
|
||||
<vxe-column field="status" title="状态" width="110">
|
||||
@@ -145,6 +149,7 @@ import {
|
||||
queryManifestList,
|
||||
saveManifest,
|
||||
} from '@/api/manifest.js'
|
||||
import { contractTypeOptions, getContractTypeSimpleLabel } from './contractTypeOptions.js'
|
||||
|
||||
function isSuccessResponse(res) {
|
||||
return String(res?.code || '') === '0000'
|
||||
@@ -162,36 +167,6 @@ function getResponseMessage(res, fallback = '操作失败') {
|
||||
return res?.message || res?.msg || fallback
|
||||
}
|
||||
|
||||
// 页面内字典(后续可由后端接口替换)
|
||||
const contractTypeOptions = [
|
||||
{ label: '买卖合同', value: '买卖合同' },
|
||||
{ label: '采购合同', value: '采购合同' },
|
||||
{ label: '销售合同', value: '销售合同' },
|
||||
{ label: '服务合同', value: '服务合同' },
|
||||
{ label: '框架合同', value: '框架合同' },
|
||||
{ label: '供用电、水、气、热力合同', value: '供用电、水、气、热力合同' },
|
||||
{ label: '赠与合同', value: '赠与合同' },
|
||||
{ label: '借款合同', value: '借款合同' },
|
||||
{ label: '保证合同', value: '保证合同' },
|
||||
{ label: '租赁合同', value: '租赁合同' },
|
||||
{ label: '融资租赁合同', value: '融资租赁合同' },
|
||||
{ label: '保理合同', value: '保理合同' },
|
||||
{ label: '承揽合同', value: '承揽合同' },
|
||||
{ label: '建设工程合同', value: '建设工程合同' },
|
||||
{ label: '运输合同', value: '运输合同' },
|
||||
{ label: '技术合同', value: '技术合同' },
|
||||
{ label: '保管合同', value: '保管合同' },
|
||||
{ label: '仓储合同', value: '仓储合同' },
|
||||
{ label: '委托合同', value: '委托合同' },
|
||||
{ label: '物业服务合同', value: '物业服务合同' },
|
||||
{ label: '行纪合同', value: '行纪合同' },
|
||||
{ label: '中介合同', value: '中介合同' },
|
||||
{ label: '合伙合同', value: '合伙合同' },
|
||||
{ label: '用工合同', value: '用工合同' },
|
||||
{ label: '业务合同', value: '业务合同' },
|
||||
{ label: '其他合同', value: '其他合同' },
|
||||
]
|
||||
|
||||
const templateStatusOptions = [
|
||||
{ label: '已发布', value: 'published' },
|
||||
{ label: '草稿', value: 'draft' },
|
||||
@@ -257,7 +232,12 @@ function getStatusLabel(status) {
|
||||
function getStatusColor(status) {
|
||||
return templateStatusColorMap[status] || 'default'
|
||||
}
|
||||
|
||||
function copyRecord(record) {
|
||||
return {
|
||||
...record,
|
||||
contacts: (record.contacts || []).map((item) => ({ ...item })),
|
||||
};
|
||||
}
|
||||
function getList() {
|
||||
loading.value = true
|
||||
|
||||
@@ -269,9 +249,10 @@ function getList() {
|
||||
message.error(getResponseMessage(res, '模板列表查询失败'))
|
||||
return
|
||||
}
|
||||
const pageResult = getPageResult(res)
|
||||
tableData.value = pageResult.records || []
|
||||
total.value = pageResult.total || 0
|
||||
|
||||
const pageResult = getPageResult(res);
|
||||
tableData.value = (pageResult.data || []).map((item) => copyRecord(item));
|
||||
total.value = pageResult.totalSize || 0;
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false
|
||||
|
||||
@@ -108,13 +108,13 @@
|
||||
<div
|
||||
class="btn btn-primary"
|
||||
:class="{
|
||||
'btn-disabled': !uploadFile || !contractName || creating,
|
||||
'btn-disabled': (!uploadFile && !businessRecord?.id) || !contractName || creating,
|
||||
}"
|
||||
@click="handleCreateContract"
|
||||
@click="businessRecord?.id && !uploadFile ? handleCreateContractFromBusinessFile() : handleCreateContract()"
|
||||
>
|
||||
<a-spin v-if="creating" size="small" />
|
||||
<span v-if="creating"> 发起中...</span>
|
||||
<span v-else>发起签署</span>
|
||||
<span v-else>{{ businessRecord?.id && !uploadFile ? "使用合同正文发起签署" : "发起签署" }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -631,6 +631,11 @@ import {
|
||||
getUserUniqueId,
|
||||
getSealBySignerId,
|
||||
} from "@/api/seal";
|
||||
import {
|
||||
finishLaunchSeal,
|
||||
downloadLaunchFile,
|
||||
saveLaunchSealInfo,
|
||||
} from "@/api/launch.js";
|
||||
|
||||
const SUCCESS_CODE = "0000";
|
||||
const CALLBACK_HOST = "http://47.110.50.12:7005";
|
||||
@@ -650,6 +655,7 @@ function getResponseMessage(res, fallback = "操作失败") {
|
||||
|
||||
const visible = ref(false);
|
||||
const businessRecord = ref(null);
|
||||
const emit = defineEmits(["business-updated"]);
|
||||
|
||||
const businessStatusLabelMap = {
|
||||
draft: "草稿",
|
||||
@@ -790,6 +796,78 @@ function clearContractState() {
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function getBusinessBodyFile(record = {}) {
|
||||
const bodyFile =
|
||||
(record.fileList || []).find((item) => item.fileCategory === "body") || {};
|
||||
return {
|
||||
fileName:
|
||||
record.contractTextFileName ||
|
||||
record.contractBodyFileName ||
|
||||
bodyFile.fileName ||
|
||||
`${record.contractName || "合同正文"}.docx`,
|
||||
filePath:
|
||||
record.contractTextFilePath ||
|
||||
record.contractBodyFilePath ||
|
||||
bodyFile.filePath ||
|
||||
"",
|
||||
fileType:
|
||||
record.contractTextFileType ||
|
||||
record.contractBodyFileType ||
|
||||
bodyFile.fileType ||
|
||||
"docx",
|
||||
};
|
||||
}
|
||||
|
||||
async function buildBusinessBodyUploadFile(record = {}) {
|
||||
const bodyFile = getBusinessBodyFile(record);
|
||||
if (!bodyFile.filePath) {
|
||||
return null;
|
||||
}
|
||||
const buffer = await downloadLaunchFile(bodyFile.filePath);
|
||||
const type =
|
||||
String(bodyFile.fileType || "").toLowerCase() === "pdf"
|
||||
? "application/pdf"
|
||||
: "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
|
||||
return new File([buffer], bodyFile.fileName, { type });
|
||||
}
|
||||
|
||||
function buildSignedFileInfo(downloadData = {}) {
|
||||
const signedUrl = downloadData.downloadUrl || downloadData.url || "";
|
||||
const signedName =
|
||||
downloadData.fileName ||
|
||||
`${businessRecord.value?.contractName || contractName.value || "已签署合同"}.pdf`;
|
||||
return {
|
||||
signedFileName: signedName,
|
||||
signedFilePath: signedUrl || contractId.value || "",
|
||||
signedFileType: signedName.split(".").pop()?.toLowerCase() || "pdf",
|
||||
signedFileSize: downloadData.fileSize || downloadData.size || null,
|
||||
};
|
||||
}
|
||||
|
||||
async function writeBackLaunchSealFinished(status = 2000) {
|
||||
if (!businessRecord.value?.id) {
|
||||
return;
|
||||
}
|
||||
let signedFileInfo = {};
|
||||
try {
|
||||
const downloadRes = await downloadContract(contractId.value);
|
||||
if (isSuccessResponse(downloadRes) && getResponseResult(downloadRes)) {
|
||||
signedFileInfo = buildSignedFileInfo(getResponseResult(downloadRes));
|
||||
}
|
||||
} catch {}
|
||||
|
||||
const res = await finishLaunchSeal(businessRecord.value.id, {
|
||||
sealContractId: contractId.value,
|
||||
sealContractStatus: status,
|
||||
...signedFileInfo,
|
||||
});
|
||||
|
||||
if (isSuccessResponse(res) && getResponseResult(res)) {
|
||||
businessRecord.value = { ...businessRecord.value, ...getResponseResult(res) };
|
||||
emit("business-updated", getResponseResult(res));
|
||||
}
|
||||
}
|
||||
|
||||
function resetSealPlacementState() {
|
||||
sealItems.value = [];
|
||||
sealPlacedId = 0;
|
||||
@@ -994,6 +1072,12 @@ async function handleCreateContract() {
|
||||
imageHeight.value = Number(data.imageHeight) || 1122;
|
||||
contractStatus.value = 10;
|
||||
updateBusinessStatusByContractStatus(contractStatus.value);
|
||||
if (businessRecord.value?.id) {
|
||||
await saveLaunchSealInfo(businessRecord.value.id, {
|
||||
sealContractId: contractId.value,
|
||||
sealContractStatus: contractStatus.value,
|
||||
});
|
||||
}
|
||||
saveContractState();
|
||||
message.success("签署流程创建成功,请继续获取印章并拖拽到文档上");
|
||||
await nextTick();
|
||||
@@ -1005,6 +1089,30 @@ async function handleCreateContract() {
|
||||
}
|
||||
}
|
||||
|
||||
async function handleCreateContractFromBusinessFile() {
|
||||
if (!businessRecord.value) {
|
||||
return;
|
||||
}
|
||||
creating.value = true;
|
||||
try {
|
||||
const bodyUploadFile = await buildBusinessBodyUploadFile(businessRecord.value);
|
||||
if (!bodyUploadFile) {
|
||||
message.warning("当前合同没有已保存的正文文件,请先在合同发起弹窗中配置正文");
|
||||
return;
|
||||
}
|
||||
uploadFile.value = bodyUploadFile;
|
||||
if (!contractName.value) {
|
||||
contractName.value =
|
||||
businessRecord.value.contractName || bodyUploadFile.name.replace(/\.[^.]+$/, "");
|
||||
}
|
||||
await handleCreateContract();
|
||||
} catch {
|
||||
message.error("读取后端合同正文失败,请检查正文文件是否存在");
|
||||
} finally {
|
||||
creating.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const pdfScrollRef = ref(null);
|
||||
const displayWidth = ref(700);
|
||||
const displayHeight = ref(990);
|
||||
@@ -1424,6 +1532,10 @@ function handlePreSignCallback(data) {
|
||||
} else {
|
||||
message.success(`预盖章确认成功,签署人:${data.signer || "未知"}`);
|
||||
}
|
||||
|
||||
if (businessRecord.value?.id && data.contractStatus === 2000) {
|
||||
writeBackLaunchSealFinished(data.contractStatus);
|
||||
}
|
||||
}
|
||||
|
||||
function refreshQrCode() {
|
||||
@@ -1522,6 +1634,9 @@ async function handleCompleteContract() {
|
||||
|
||||
contractStatus.value = 2000;
|
||||
updateBusinessStatusByContractStatus(2000);
|
||||
if (businessRecord.value?.id) {
|
||||
await writeBackLaunchSealFinished(2000);
|
||||
}
|
||||
clearContractState();
|
||||
message.success("合约已完成");
|
||||
} catch {
|
||||
|
||||
Reference in New Issue
Block a user