From 38cadd7d572507011192dcff8eca4422de2ccf20 Mon Sep 17 00:00:00 2001 From: zhouxiaofeng <2946471396@qq.com> Date: Mon, 1 Jun 2026 14:39:18 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/launch.js | 7 + .../ContractPartyPlaceholderModal.vue | 370 +++++++++++++----- .../ContractTemplateFillDemoModal.vue | 298 +++++++------- src/views/contract/launch/index.vue | 34 +- .../ContractTemplateManageModal.vue | 82 +--- 5 files changed, 479 insertions(+), 312 deletions(-) diff --git a/src/api/launch.js b/src/api/launch.js index 316d69b..9ce50bc 100644 --- a/src/api/launch.js +++ b/src/api/launch.js @@ -23,6 +23,13 @@ export function getLaunchDetail(id) { }); } +export function getLaunchSelfProfile() { + return request({ + url: "/contract/launch/self-profile", + method: "get", + }).then((res) => (isSuccessResponse(res) ? getResult(res) || null : null)); +} + export function createLaunchDraft(data) { return request({ url: "/contract/launch", diff --git a/src/views/contract/launch/components/ContractPartyPlaceholderModal.vue b/src/views/contract/launch/components/ContractPartyPlaceholderModal.vue index fa16f6d..7151001 100644 --- a/src/views/contract/launch/components/ContractPartyPlaceholderModal.vue +++ b/src/views/contract/launch/components/ContractPartyPlaceholderModal.vue @@ -340,6 +340,11 @@ {{ contractBodyHint }} + + + 下载 + +
@@ -354,6 +359,18 @@ >
签订依据文件,对应法务 contractGistFile
+
+ + + 下载 {{ item.name || item.fileName }} + + +
@@ -368,6 +385,18 @@ >
其他附件(可上传多个文件)
+
+ + + 下载 {{ item.name || item.fileName }} + + +
@@ -477,13 +506,13 @@ @@ -496,7 +525,7 @@ @@ -707,7 +736,7 @@ @@ -806,7 +835,12 @@ import { computed, ref } from "vue"; import { message } from "ant-design-vue"; import { PlusOutlined, SearchOutlined } from "@ant-design/icons-vue"; import ContractTemplateFillDemoModal from "./ContractTemplateFillDemoModal.vue"; -import { queryPartnerOptions, uploadLaunchFile } from "@/api/launch.js"; +import { + downloadLaunchFile, + getLaunchSelfProfile, + queryPartnerOptions, + uploadLaunchFile, +} from "@/api/launch.js"; import { contractSourceOptions, contractTypeOptions, @@ -911,7 +945,8 @@ const paymentMethodOptions = [ ]; const SELF_COMPANY_NAME = "浙港物流平台有限公司"; -const MAX_COUNTERPARTY_COUNT = 2; +const MAX_COUNTERPARTY_COUNT = 5; +const COUNTERPARTY_ROLE_CODES = ["B", "C", "D", "E", "F"]; // ========== 工具函数 ========== @@ -933,6 +968,40 @@ function normalizeFileItem(item = {}) { }; } +function normalizeBooleanValue(value) { + if (value === true || value === false) return value; + if (value === 1 || value === "1" || value === "Y" || value === "true") return true; + if (value === 0 || value === "0" || value === "N" || value === "false") return false; + return false; +} + +function pickBooleanSource(source = {}, key) { + const mapping = { + isInvolveHongKongMacaoTaiwan: [ + source.isInvolveHongKongMacaoTaiwan, + source.isSgat, + ], + isPublicTender: [source.isPublicTender, source.isOpenBidding], + isForeignRelatedSubject: [ + source.isForeignRelatedSubject, + source.contractNature, + ], + isIncludedInCurrentYearBudget: [ + source.isIncludedInCurrentYearBudget, + source.isYearBudget, + ], + isShared: [source.isShared, source.isShare], + isMajorContract: [source.isMajorContract], + }; + const values = mapping[key] || []; + for (const value of values) { + if (value !== undefined && value !== null && value !== "") { + return value; + } + } + return undefined; +} + function getFileNameFromDetail(source, category) { if (category === "body") { return source.contractBodyFileName || source.contractTextFileName || ""; @@ -1021,6 +1090,12 @@ function copyPartyState(input) { const sourcePartyA = source.partyA || source.parties?.A || {}; const sourcePartyB = source.partyB || source.parties?.B || {}; const sourcePartyC = source.partyC || source.parties?.C || {}; + const sourceCounterpartyList = Array.isArray(source.counterpartyList) + ? source.counterpartyList + : [ + String(sourcePartyB.companyName || "").trim() ? { ...sourcePartyB, roleKey: "B" } : null, + String(sourcePartyC.companyName || "").trim() ? { ...sourcePartyC, roleKey: "C" } : null, + ].filter(Boolean); const state = { isThreePartyContract: Boolean(source.isThreePartyContract), partyA: { @@ -1044,22 +1119,33 @@ function copyPartyState(input) { companyName: "", ...sourcePartyC, }, + counterpartyList: sourceCounterpartyList.map((item, index) => ({ + ...createEmptyCounterparty(item.roleKey || COUNTERPARTY_ROLE_CODES[index]), + ...item, + roleKey: item.roleKey || COUNTERPARTY_ROLE_CODES[index], + displayName: `相对方${index + 1}`, + })), }; if (listState) { state.partyA = { ...state.partyA, ...listState.partyA }; - state.partyB = { ...state.partyB, ...listState.partyB }; - state.partyC = { ...state.partyC, ...listState.partyC }; + state.counterpartyList = listState.counterpartyList.map((item) => ({ ...item })); + state.partyB = { ...state.partyB, ...(state.counterpartyList[0] || {}) }; + state.partyC = { ...state.partyC, ...(state.counterpartyList[1] || {}) }; state.isThreePartyContract = listState.isThreePartyContract; + } else { + state.partyB = { ...state.partyB, ...(state.counterpartyList[0] || {}) }; + state.partyC = { ...state.partyC, ...(state.counterpartyList[1] || {}) }; } return state; } function mapLaunchPartyToFormParty(item = {}) { + const roleIndex = COUNTERPARTY_ROLE_CODES.indexOf(item.roleCode || "B"); return { roleKey: item.roleCode || "B", roleName: item.roleName || "", displayName: - item.roleCode === "A" ? "我方" : item.roleCode === "C" ? "相对方2" : "相对方1", + item.roleCode === "A" ? "我方" : `相对方${roleIndex >= 0 ? roleIndex + 1 : 1}`, partnerId: item.partnerId || null, customerSysid: item.partnerId || null, oppositeId: item.oppositeId || "", @@ -1092,14 +1178,24 @@ function buildPartyStateFromList(list = []) { partyA: {}, partyB: {}, partyC: {}, + counterpartyList: [], }; list.forEach((item) => { if (!item?.roleCode) return; - const targetKey = - item.roleCode === "A" ? "partyA" : item.roleCode === "C" ? "partyC" : "partyB"; - result[targetKey] = mapLaunchPartyToFormParty(item); + if (item.roleCode === "A") { + result.partyA = mapLaunchPartyToFormParty(item); + } else { + result.counterpartyList.push(mapLaunchPartyToFormParty(item)); + } }); - result.isThreePartyContract = Boolean(result.partyB?.companyName && result.partyC?.companyName); + result.counterpartyList = result.counterpartyList.map((item, index) => ({ + ...item, + roleKey: item.roleKey || COUNTERPARTY_ROLE_CODES[index], + displayName: `相对方${index + 1}`, + })); + result.partyB = result.counterpartyList[0] || {}; + result.partyC = result.counterpartyList[1] || {}; + result.isThreePartyContract = result.counterpartyList.length > 1; return result; } @@ -1114,8 +1210,12 @@ function getCompanyNatureByOppositeCharacter(value) { } function createEmptyCounterparty(roleKey) { - const blankState = copyPartyState({}); - return roleKey === "C" ? blankState.partyC : blankState.partyB; + return { + roleKey, + roleName: "相对方", + displayName: `相对方${COUNTERPARTY_ROLE_CODES.indexOf(roleKey) + 1}`, + companyName: "", + }; } function isPartyFilled(party = {}) { @@ -1129,35 +1229,37 @@ function isPartyFilled(party = {}) { } function buildCounterpartyRoleKeys(partyState) { - const state = copyPartyState(partyState); - const keys = []; - if (isPartyFilled(state.partyB)) keys.push("B"); - if (isPartyFilled(state.partyC)) keys.push("C"); - return keys; + const state = partyState || {}; + const sourceList = Array.isArray(state.counterpartyList) + ? state.counterpartyList + : COUNTERPARTY_ROLE_CODES.map((roleKey) => state[`party${roleKey}`]).filter(Boolean); + return sourceList + .filter((party) => isPartyFilled(party)) + .map((party, index) => party.roleKey || COUNTERPARTY_ROLE_CODES[index]) + .slice(0, MAX_COUNTERPARTY_COUNT); } function normalizePartyState(partyState) { const state = copyPartyState(partyState); - if (!isPartyFilled(state.partyB) && isPartyFilled(state.partyC)) { - state.partyB = { - ...createEmptyCounterparty("B"), - ...state.partyC, - roleKey: "B", - roleName: "相对方", - displayName: "相对方1", - }; - state.partyC = createEmptyCounterparty("C"); - } state.partyA.companyName = state.partyA.companyName || SELF_COMPANY_NAME; - state.isThreePartyContract = - isPartyFilled(state.partyB) && isPartyFilled(state.partyC); + state.counterpartyList = (Array.isArray(state.counterpartyList) ? state.counterpartyList : []) + .filter((party) => isPartyFilled(party)) + .slice(0, MAX_COUNTERPARTY_COUNT) + .map((party, index) => ({ + ...createEmptyCounterparty(party.roleKey || COUNTERPARTY_ROLE_CODES[index]), + ...party, + roleKey: party.roleKey || COUNTERPARTY_ROLE_CODES[index], + displayName: `相对方${index + 1}`, + })); + state.partyB = state.counterpartyList[0] || createEmptyCounterparty("B"); + state.partyC = state.counterpartyList[1] || createEmptyCounterparty("C"); + state.isThreePartyContract = state.counterpartyList.length > 1; return state; } function getPartyByRoleKey(partyState, roleKey) { if (roleKey === "A") return partyState.partyA; - if (roleKey === "C") return partyState.partyC; - return partyState.partyB; + return (partyState.counterpartyList || []).find((item) => item.roleKey === roleKey); } /** 创建一条空的履行计划 */ @@ -1178,6 +1280,7 @@ function createPerformancePlan() { /** 创建空白的合同表单数据 */ function createContractFormData() { return { + id: null, contractName: "", contractCode: "", contractType: "", @@ -1217,11 +1320,14 @@ function createContractFormData() { contractBodyArrayBuffer: null, otherAttachmentList: [], contractGistFileList: [], + // 正文模板字段值统一放这里,后续新增模板字段时尽量只改 templateFieldList + templateFieldValues: {}, partyState: { isThreePartyContract: false, partyA: { companyName: SELF_COMPANY_NAME }, partyB: { companyName: "" }, partyC: { companyName: "" }, + counterpartyList: [], }, isInvolveHongKongMacaoTaiwan: false, isPublicTender: false, @@ -1257,6 +1363,18 @@ function copyContractFormData(data = {}) { Object.keys(form).forEach((key) => { if (key === "extraAmounts") { form.extraAmounts = restoreExtraAmounts(source, form.extraAmounts); + } else if (key === "templateFieldValues") { + if (source.templateFieldValues && typeof source.templateFieldValues === "object") { + form.templateFieldValues = { ...source.templateFieldValues }; + } else if (source.templateFieldValuesJson) { + try { + form.templateFieldValues = JSON.parse(source.templateFieldValuesJson); + } catch (error) { + form.templateFieldValues = {}; + } + } else { + form.templateFieldValues = {}; + } } else if (key === "performancePlans") { form.performancePlans = (source.performancePlans || []).map((item) => ({ ...createPerformancePlan(), @@ -1290,6 +1408,17 @@ function copyContractFormData(data = {}) { } else if (key === "signingSubjectCode") { form.signingSubjectCode = source.signingSubjectCode ?? source.orgId ?? form.signingSubjectCode; + } else if ( + [ + "isInvolveHongKongMacaoTaiwan", + "isPublicTender", + "isForeignRelatedSubject", + "isIncludedInCurrentYearBudget", + "isShared", + "isMajorContract", + ].includes(key) + ) { + form[key] = normalizeBooleanValue(pickBooleanSource(source, key)); } else { form[key] = source[key] ?? form[key]; } @@ -1358,16 +1487,11 @@ const companyProfileMap = { const isViewMode = computed(() => modalMode.value === "view"); const counterpartyParties = computed(() => - counterpartyRoleKeys.value.map((roleKey) => - getPartyByRoleKey(detailState.value.partyState, roleKey), + (detailState.value.partyState.counterpartyList || []).filter((party) => + counterpartyRoleKeys.value.includes(party.roleKey), ), ); -const displayedParties = computed(() => [ - detailState.value.partyState.partyA, - ...counterpartyParties.value, -]); - const counterpartySummaryText = computed(() => counterpartyParties.value.length ? counterpartyParties.value @@ -1412,11 +1536,10 @@ function syncUploadLists() { function syncCounterpartyState() { detailState.value.partyState = normalizePartyState(detailState.value.partyState); - counterpartyRoleKeys.value = buildCounterpartyRoleKeys( - detailState.value.partyState, - ); - detailState.value.partyState.isThreePartyContract = - counterpartyRoleKeys.value.length > 1; + counterpartyRoleKeys.value = buildCounterpartyRoleKeys(detailState.value.partyState); + detailState.value.partyState.counterpartyList = + detailState.value.partyState.counterpartyList || []; + detailState.value.partyState.isThreePartyContract = counterpartyRoleKeys.value.length > 1; refreshPartyDisplayMeta(); } @@ -1425,8 +1548,8 @@ function refreshPartyDisplayMeta() { detailState.value.partyState.partyA.roleName = "我方"; detailState.value.partyState.partyA.displayName = "我方"; - counterpartyRoleKeys.value.forEach((roleKey, index) => { - const party = getPartyByRoleKey(detailState.value.partyState, roleKey); + (detailState.value.partyState.counterpartyList || []).forEach((party, index) => { + const roleKey = party.roleKey || COUNTERPARTY_ROLE_CODES[index]; party.roleKey = roleKey; party.roleName = "相对方"; party.displayName = `相对方${index + 1}`; @@ -1443,11 +1566,19 @@ function resetCurrentState() { } function showSearchTip(title) { + if (title === "我方详情") { + message.info("我方信息当前按登录组织只读带出,后续接入真实组织主数据后自动完善。"); + return; + } loadPartnerOptions(); message.info(`${title}数据已从相对方管理加载,可直接在下拉框搜索选择。`); } function showInvoiceTitles(party, roleName) { + if (roleName === "我方") { + message.info("我方开票抬头当前不在发起页维护,后续建议从组织主数据自动带出。"); + return; + } const titles = party.invoiceTitleOptions || []; message.info( titles.length @@ -1536,9 +1667,7 @@ function handleCompanySelect(roleKey, companyName, option = {}) { const party = roleKey === "A" ? detailState.value.partyState.partyA - : roleKey === "B" - ? detailState.value.partyState.partyB - : detailState.value.partyState.partyC; + : getPartyByRoleKey(detailState.value.partyState, roleKey); if (!party) return; const rawPartner = @@ -1586,36 +1715,34 @@ function handleAddCounterparty() { return; } - const nextRoleKey = counterpartyRoleKeys.value.includes("B") ? "C" : "B"; - detailState.value.partyState[nextRoleKey === "C" ? "partyC" : "partyB"] = - createEmptyCounterparty(nextRoleKey); + const nextRoleKey = COUNTERPARTY_ROLE_CODES.find( + (roleKey) => !counterpartyRoleKeys.value.includes(roleKey), + ); + if (!nextRoleKey) return; + + detailState.value.partyState.counterpartyList = + detailState.value.partyState.counterpartyList || []; + detailState.value.partyState.counterpartyList.push( + createEmptyCounterparty(nextRoleKey), + ); counterpartyRoleKeys.value = [...counterpartyRoleKeys.value, nextRoleKey]; - detailState.value.partyState.isThreePartyContract = - counterpartyRoleKeys.value.length > 1; + detailState.value.partyState.isThreePartyContract = counterpartyRoleKeys.value.length > 1; refreshPartyDisplayMeta(); } function handleRemoveCounterparty(roleKey) { - if (roleKey === "B" && counterpartyRoleKeys.value.includes("C")) { - detailState.value.partyState.partyB = { - ...createEmptyCounterparty("B"), - ...detailState.value.partyState.partyC, - roleKey: "B", - roleName: "相对方", - displayName: "相对方1", - }; - detailState.value.partyState.partyC = createEmptyCounterparty("C"); - counterpartyRoleKeys.value = ["B"]; - } else { - detailState.value.partyState[roleKey === "C" ? "partyC" : "partyB"] = - createEmptyCounterparty(roleKey); - counterpartyRoleKeys.value = counterpartyRoleKeys.value.filter( - (key) => key !== roleKey, - ); - } - - detailState.value.partyState.isThreePartyContract = - counterpartyRoleKeys.value.length > 1; + detailState.value.partyState.counterpartyList = + (detailState.value.partyState.counterpartyList || []) + .filter((item) => item.roleKey !== roleKey) + .map((item, index) => ({ + ...item, + roleKey: COUNTERPARTY_ROLE_CODES[index], + displayName: `相对方${index + 1}`, + })); + counterpartyRoleKeys.value = detailState.value.partyState.counterpartyList.map( + (item) => item.roleKey, + ); + detailState.value.partyState.isThreePartyContract = counterpartyRoleKeys.value.length > 1; refreshPartyDisplayMeta(); } @@ -1676,6 +1803,27 @@ function handleRemoveGistAttachment(file) { ); } +async function downloadFile(filePath, fileName = "附件") { + if (!filePath) { + message.warning("文件路径为空,无法下载"); + return; + } + try { + const arrayBuffer = await downloadLaunchFile(filePath); + const blob = new Blob([arrayBuffer]); + const url = window.URL.createObjectURL(blob); + const link = document.createElement("a"); + link.href = url; + link.download = fileName || "附件"; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + window.URL.revokeObjectURL(url); + } catch (error) { + message.error("文件下载失败"); + } +} + function openBodyConfigModal() { bodyConfigModalRef.value?.showModal( { @@ -1695,6 +1843,31 @@ function handleBodyConfigSave(payload = {}) { detailState.value = copyContractFormData(payload.detailState); syncCounterpartyState(); syncUploadLists(); + + if (modalMode.value === "edit" && detailState.value.id) { + const errorMessage = validateState(); + if (errorMessage) { + message.success("合同正文配置已保存到当前页面,请继续完善主信息后再保存合同草稿。"); + return; + } + + syncCounterpartyState(); + detailState.value.contractAmount = + detailState.value.contractAmount === null || + detailState.value.contractAmount === undefined || + detailState.value.contractAmount === "" + ? null + : Number(detailState.value.contractAmount); + + emit("save", { + mode: modalMode.value, + record: buildLaunchRecord(), + successMessage: "合同正文配置已保存", + }); + return; + } + + message.success("合同正文配置已保存到当前页面,请继续点击底部“保存”提交合同草稿。"); } function addPerformancePlan() { @@ -1799,8 +1972,8 @@ function buildLaunchRecord() { counterpartyParties.value.forEach((party, index) => { partyList.push({ - roleCode: index === 0 ? "B" : "C", - roleName: index === 0 ? "乙方" : "丙方", + roleCode: party.roleKey || COUNTERPARTY_ROLE_CODES[index], + roleName: party.displayName || `相对方${index + 1}`, partnerId: party.partnerId || party.customerSysid || null, oppositeId: party.oppositeId, companyName: party.companyName, @@ -1869,12 +2042,8 @@ function buildLaunchRecord() { currencyName: detailState.value.currencyName, contractPeriodType: detailState.value.contractPeriodType, periodExplain: detailState.value.periodExplain, - effectiveStart: detailState.value.effectiveStart - ? `${detailState.value.effectiveStart} 00:00:00` - : null, - effectiveEnd: detailState.value.effectiveEnd - ? `${detailState.value.effectiveEnd} 23:59:59` - : null, + effectiveStart: formatDateTimeValue(detailState.value.effectiveStart), + effectiveEnd: formatDateTimeValue(detailState.value.effectiveEnd, true), paymentMethod: detailState.value.paymentMethod, primaryContent: detailState.value.primaryContent, amountExplain: detailState.value.amountExplain, @@ -1909,6 +2078,9 @@ function buildLaunchRecord() { signDate: formatDateTimeValue(detailState.value.signDate), invoiceDate: formatDateTimeValue(detailState.value.invoiceDate), receiveDate: formatDateTimeValue(detailState.value.receiveDate), + templateFieldValuesJson: JSON.stringify( + detailState.value.templateFieldValues || {}, + ), remindDays: 3, signingSubjectCode: detailState.value.signingSubjectCode || "", extraAmounts: { ...detailState.value.extraAmounts }, @@ -1929,17 +2101,39 @@ function buildLaunchRecord() { }; } -function showModal(context = {}, mode = "create") { +function applySelfProfile(profile = {}) { + detailState.value.partyState.partyA = { + ...detailState.value.partyState.partyA, + companyName: profile.orgName || detailState.value.partyState.partyA.companyName, + contactPhone: profile.contactPhone || detailState.value.partyState.partyA.contactPhone, + contactEmail: profile.contactEmail || detailState.value.partyState.partyA.contactEmail, + contactAddress: profile.contactAddress || detailState.value.partyState.partyA.contactAddress, + registeredAddress: + profile.registeredAddress || detailState.value.partyState.partyA.registeredAddress, + bankName: profile.bankName || detailState.value.partyState.partyA.bankName, + bankAccount: profile.bankAccount || detailState.value.partyState.partyA.bankAccount, + legalRepresentative: + profile.legalRepresentative || detailState.value.partyState.partyA.legalRepresentative, + creditCode: profile.creditCode || detailState.value.partyState.partyA.creditCode, + invoiceTitleOptions: + profile.invoiceTitleOptions || detailState.value.partyState.partyA.invoiceTitleOptions || [], + }; +} + +async function showModal(context = {}, mode = "create") { modalMode.value = mode || "create"; const source = context.detailState || (Object.keys(context || {}).length ? context : createContractFormData()); creationMode.value = context.creationMode || - (source.contractBodyArrayBuffer ? "manual" : "template"); - templateKey.value = context.templateKey || ""; - templateName.value = context.templateName || ""; + source.creationMode || + (source.contractBodyArrayBuffer || source.contractBodyFilePath ? "manual" : "template"); + templateKey.value = context.templateKey || source.templateId || ""; + templateName.value = context.templateName || source.templateName || ""; detailState.value = copyContractFormData(source); + const selfProfile = await getLaunchSelfProfile(); + applySelfProfile(selfProfile || {}); syncCounterpartyState(); syncUploadLists(); loadPartnerOptions(); diff --git a/src/views/contract/launch/components/ContractTemplateFillDemoModal.vue b/src/views/contract/launch/components/ContractTemplateFillDemoModal.vue index d21de47..50af76c 100644 --- a/src/views/contract/launch/components/ContractTemplateFillDemoModal.vue +++ b/src/views/contract/launch/components/ContractTemplateFillDemoModal.vue @@ -228,65 +228,25 @@ 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: '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: 'partyDName', label: '丁方名称' }, + { value: 'partyEName', label: '戊方名称' }, + { value: 'partyFName', 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: '签署日期' }, + { value: 'businessRemark', label: '业务备注' }, + { value: 'contractSubject', label: '合同标的说明' }, ] // ========== 格式化合同金额(如 368,000.00 元) ========== @@ -396,24 +356,51 @@ function buildPerformancePlanSummary(list = []) { } // ========== 合同方相关函数 ========== +const COUNTERPARTY_ROLE_CODES = ['B', 'C', 'D', 'E', 'F'] /** 深拷贝合同方状态 */ function copyPartyState(input) { const source = input || {} const listState = buildPartyStateFromList(source.partyList) + const sourceCounterpartyList = Array.isArray(source.counterpartyList) + ? source.counterpartyList + : [ + source.partyB?.companyName ? { ...source.partyB, roleKey: 'B' } : null, + source.partyC?.companyName ? { ...source.partyC, roleKey: 'C' } : null, + ].filter(Boolean) return { - isThreePartyContract: Boolean(source.isThreePartyContract), - 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 } : {}), + partyA: { + roleKey: 'A', + roleName: '我方', + displayName: '我方', + companyName: '', + ...(source.partyA || source.parties?.A || {}), + ...(listState?.partyA || {}), + }, + counterpartyList: listState?.counterpartyList?.length + ? listState.counterpartyList.map((item) => ({ ...item })) + : sourceCounterpartyList.map((item, index) => ({ + roleKey: item.roleKey || COUNTERPARTY_ROLE_CODES[index], + roleName: item.roleName || '相对方', + displayName: `相对方${index + 1}`, + companyName: '', + ...item, + })), + isThreePartyContract: Boolean( + (listState?.counterpartyList || sourceCounterpartyList || []).length > 1, + ), } } function mapLaunchPartyToFormParty(item = {}) { + const roleIndex = COUNTERPARTY_ROLE_CODES.indexOf(item.roleCode || 'B') return { roleKey: item.roleCode || 'B', - roleName: item.roleCode === 'A' ? '甲方' : item.roleCode === 'C' ? '丙方' : '乙方', + roleName: item.roleCode === 'A' ? '我方' : item.roleName || '相对方', + displayName: + item.roleCode === 'A' + ? '我方' + : `相对方${roleIndex >= 0 ? roleIndex + 1 : 1}`, partnerId: item.partnerId || null, oppositeId: item.oppositeId || '', companyName: item.companyName || '', @@ -437,21 +424,29 @@ function mapLaunchPartyToFormParty(item = {}) { function buildPartyStateFromList(list = []) { if (!Array.isArray(list) || !list.length) return null - const state = { partyA: {}, partyB: {}, partyC: {}, isThreePartyContract: false } + const state = { partyA: {}, counterpartyList: [], isThreePartyContract: false } list.forEach((item) => { - const key = item.roleCode === 'A' ? 'partyA' : item.roleCode === 'C' ? 'partyC' : 'partyB' - state[key] = mapLaunchPartyToFormParty(item) + if (item.roleCode === 'A') { + state.partyA = mapLaunchPartyToFormParty(item) + } else { + state.counterpartyList.push(mapLaunchPartyToFormParty(item)) + } }) - state.isThreePartyContract = Boolean(state.partyB.companyName && state.partyC.companyName) + state.counterpartyList = state.counterpartyList.map((item, index) => ({ + ...item, + roleKey: item.roleKey || COUNTERPARTY_ROLE_CODES[index], + displayName: `相对方${index + 1}`, + })) + state.isThreePartyContract = state.counterpartyList.length > 1 return state } /** 获取有效的合同方列表 */ function getPartyList(partyState) { const state = copyPartyState(partyState) - const list = [state.partyA, state.partyB] - if (state.isThreePartyContract) list.push(state.partyC) - return list + return [state.partyA, ...(state.counterpartyList || [])].filter( + (item) => item && String(item.companyName || '').trim() !== '', + ) } /** 获取合同方摘要(如 "甲方:xxx;乙方:xxx") */ @@ -527,6 +522,7 @@ function createPerformancePlan() { /** 创建空白的合同表单数据 */ function createContractFormData() { return { + id: null, // 合同基本信息 contractName: '', // 合同名称 contractCode: '', // 合同编码 @@ -568,13 +564,13 @@ function createContractFormData() { contractBodyArrayBuffer: null, // 合同正文文件二进制 otherAttachmentList: [], // 其他附件列表 contractGistFileList: [], // 签订依据附件列表 + templateFieldValues: {}, // 模板字段值,统一按 {字段名: 值} 保存 // 合同方 partyState: { isThreePartyContract: false, - partyA: { companyName: '' }, - partyB: { companyName: '' }, - partyC: { companyName: '' }, + partyA: { companyName: '', roleKey: 'A', roleName: '我方', displayName: '我方' }, + counterpartyList: [], }, // 合同属性 @@ -647,6 +643,17 @@ function copyContractFormData(data = {}) { form.contractBodyFilePath = fileState.contractBodyFilePath form.contractBodyFileType = fileState.contractBodyFileType form.contractBodyFileSize = fileState.contractBodyFileSize + form.templateFieldValues = source.templateFieldValues && typeof source.templateFieldValues === 'object' + ? { ...source.templateFieldValues } + : source.templateFieldValuesJson + ? (() => { + try { + return JSON.parse(source.templateFieldValuesJson) + } catch (error) { + return {} + } + })() + : {} form.contractBodyArrayBuffer = source.contractBodyArrayBuffer ? (source.contractBodyArrayBuffer instanceof ArrayBuffer ? source.contractBodyArrayBuffer.slice(0) : null) : null @@ -686,77 +693,45 @@ function copyContractFormData(data = {}) { /** 将合同表单数据转换为模板变量字典(用于 docxtemplater 渲染) */ function buildTemplatePreviewData(formData, templateLabel = '', creationMode = 'template') { const form = copyContractFormData(formData) - 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 || {} + const partyA = getPartyList(form).find((item) => item.roleCode === 'A') || {} + const partyB = getPartyList(form).find((item) => item.roleCode === 'B') || {} + const partyC = getPartyList(form).find((item) => item.roleCode === 'C') || {} + const partyD = getPartyList(form).find((item) => item.roleCode === 'D') || {} + const partyE = getPartyList(form).find((item) => item.roleCode === 'E') || {} + const partyF = getPartyList(form).find((item) => item.roleCode === 'F') || {} + const templateValues = form.templateFieldValues || {} + + function pick(fieldKey, fallback = '') { + if (templateValues[fieldKey] !== undefined && templateValues[fieldKey] !== null && templateValues[fieldKey] !== '') { + return templateValues[fieldKey] + } + return fallback + } return { - contractName: form.contractName || '未填写', - contractCode: form.contractCode || '未填写', - 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.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: 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 || '', + contractName: pick('contractName', form.contractName || ''), + contractCode: pick('contractCode', form.contractCode || ''), + contractType: pick('contractType', form.contractType || ''), + contractAmountDisplay: pick('contractAmountDisplay', formatContractAmount(form.contractAmount)), + effectiveStart: pick('effectiveStart', form.effectiveStart || ''), + effectiveEnd: pick('effectiveEnd', form.effectiveEnd || ''), + effectivePeriod: pick('effectivePeriod', [form.effectiveStart, form.effectiveEnd].filter(Boolean).join(' 至 ')), + paymentMethod: pick('paymentMethod', form.paymentMethod || ''), + primaryContent: pick('primaryContent', form.primaryContent || ''), + amountExplain: pick('amountExplain', form.amountExplain || ''), + partyAName: pick('partyAName', partyA.companyName || ''), + partyBName: pick('partyBName', partyB.companyName || ''), + partyCName: pick('partyCName', partyC.companyName || ''), + partyDName: pick('partyDName', partyD.companyName || ''), + partyEName: pick('partyEName', partyE.companyName || ''), + partyFName: pick('partyFName', partyF.companyName || ''), + counterpartySummary: pick('counterpartySummary', getPartySummary(form)), + attachmentSummary: pick('attachmentSummary', formatFileSummary(form.otherAttachmentList)), + templateLabel: pick('templateLabel', templateLabel || (creationMode === 'manual' ? '手动创建' : '')), + signDate: pick('signDate', form.signDate || dayjs().format('YYYY-MM-DD')), + businessRemark: pick('businessRemark', ''), + contractSubject: pick('contractSubject', ''), + templateFieldValuesJson: JSON.stringify(form.templateFieldValues || {}), } } @@ -871,9 +846,6 @@ const templateSelectOptions = computed(() => })), ); -// TODO: 这里后续替换为“根据模板ID查询模板详情接口” -// TODO: 这里后续如果“已发布模板列表接口”不返回完整详情,再改成调用“模板详情接口” -//相当于详情接口返回了 映射 然后下面做匹配 const activeTemplate = computed(() => templateKey.value ? getManifestDetailLocal(templateKey.value) : null, ); @@ -884,7 +856,6 @@ const modalTitle = computed(() => "合同正文配置"); const templateFieldLabelMap = computed(() => { const map = {}; - //这个就是字典数据,包含所有定义的映射字段 templateFieldList.forEach((item) => { map[item.value] = item.label; }); @@ -892,39 +863,34 @@ const templateFieldLabelMap = computed(() => { }); const launchFieldList = computed(() => { - if (creationMode.value === "manual") { - return []; - } - // 模板模式:从 variableMappings 中取出 bindField + if (creationMode.value === "manual") return []; const mappings = activeTemplate.value?.variableMappings || []; - const fieldKeys = []; - + const keys = []; mappings.forEach((item) => { - if (!item.bindField) return; - if (!fieldKeys.includes(item.bindField)) { - fieldKeys.push(item.bindField); + const fieldKey = item.bindField || item.variableKey; + if (fieldKey && !keys.includes(fieldKey)) { + keys.push(fieldKey); } }); - - return fieldKeys.map((key) => ({ + return keys.map((key) => ({ key, label: templateFieldLabelMap.value[key] || key, - readonly: Object.prototype.hasOwnProperty.call(templateVariables.value, key), + readonly: false, })); }); function getLaunchFieldValue(fieldKey) { - if (Object.prototype.hasOwnProperty.call(templateVariables.value, fieldKey)) { - return templateVariables.value[fieldKey] ?? ""; - } - return contractDetailState.value[fieldKey] ?? ""; + return contractDetailState.value.templateFieldValues?.[fieldKey] ?? ""; } function setLaunchFieldValue(fieldKey, value) { - if (Object.prototype.hasOwnProperty.call(templateVariables.value, fieldKey)) { - return; + if (!contractDetailState.value.templateFieldValues) { + contractDetailState.value.templateFieldValues = {}; + } + contractDetailState.value.templateFieldValues[fieldKey] = value; + if (visible.value) { + schedulePreview(); } - contractDetailState.value[fieldKey] = value; } const templateVariables = computed(() => @@ -936,22 +902,24 @@ const templateVariables = computed(() => ); const hasPreviewSource = computed(() => { - if (creationMode.value === "manual") { - return !!contractDetailState.value.contractBodyArrayBuffer; - } - return !!activeTemplate.value; + return creationMode.value === "manual" + ? !!( + contractDetailState.value.contractBodyArrayBuffer || + contractDetailState.value.contractBodyFilePath + ) + : !!activeTemplate.value; }); const previewDescription = computed(() => creationMode.value === "manual" - ? "手动创建模式下,右侧直接渲染当前窗口上传的合同正文 Word 文件。" - : "模板创建模式下,系统会根据维护后的合同信息自动填充模板变量并生成合同正文。", + ? "手动创建模式下,右侧直接渲染当前上传的合同正文 Word 文件。" + : "模板创建模式下,系统按模板变量映射填充正文。", ); const emptyPreviewText = computed(() => creationMode.value === "manual" - ? "请先在当前窗口上传合同正文 Word 文件。" - : "请选择模板并完善合同信息后再预览。", + ? "请先上传合同正文 Word 文件。" + : "请选择模板并完善模板变量后再预览。", ); watch( @@ -1268,6 +1236,10 @@ async function uploadBlobAsLaunchFile(blob, fileName) { contractDetailState.value.contractBodyFileSize = file.size; } +function buildTemplateFieldValuesPayload() { + return JSON.stringify(contractDetailState.value.templateFieldValues || {}); +} + async function ensureTemplateBodyFileUploaded() { if (creationMode.value !== "template") return; const blob = previewBlob.value || (await buildFilledDocumentBlob()); @@ -1312,6 +1284,7 @@ async function handleSave() { templateKey: templateKey.value, templateName: activeTemplate.value?.templateName || "", detailState: copyContractFormData(contractDetailState.value), + templateFieldValuesJson: buildTemplateFieldValuesPayload(), }); visible.value = false; message.success("合同正文配置已保存"); @@ -1342,13 +1315,14 @@ async function showModal(initialValues = {}, mode = "create") { creationMode.value = isCreate ? "template" : initialValues.creationMode || + initialValues.templateId || (initialValues.detailState?.contractBodyArrayBuffer || initialValues.contractBodyArrayBuffer ? "manual" : "template"); templateKey.value = isCreate ? publishedTemplates.value[0]?.id || "" - : initialValues.templateKey || ""; + : initialValues.templateKey || initialValues.templateId || initialValues.detailState?.templateId || ""; contractDetailState.value = isCreate ? createContractFormData() : initialValues.detailState diff --git a/src/views/contract/launch/index.vue b/src/views/contract/launch/index.vue index c99b304..a69abfe 100644 --- a/src/views/contract/launch/index.vue +++ b/src/views/contract/launch/index.vue @@ -107,6 +107,7 @@ 查看 编辑 提交法务 + 审批记录 测试通过 测试驳回 @@ -162,6 +163,7 @@ import { mockCollectLaunch, mockRejectLaunch, finishLaunchSeal, + queryApproveViewUrl, queryLaunchList, submitLaunchRecord, updateLaunchDraft, @@ -307,6 +309,10 @@ function canArchive(row) { return row.status === "pending_archive"; } +function canViewApprove(row) { + return ["reviewing", "pending_sign", "signing", "pending_collect", "pending_archive", "completed"].includes(row.status); +} + function selectChangeEvent({ records }) { selectedRows.value = records; selectedRowKeys.value = records.map((item) => item.id); @@ -408,6 +414,29 @@ function handleSubmit(row) { }); } +function handleViewApprove(row) { + if (!row.lawContractId) { + message.warning("当前暂无法务合同ID,审批记录地址尚未生成"); + return; + } + + queryApproveViewUrl({ + contractId: String(row.lawContractId), + ivUser: row.initiator || "admin", + }).then((res) => { + if (!isSuccessResponse(res)) { + message.error(getResponseMessage(res, "获取审批记录查看地址失败")); + return; + } + const approveViewUrl = res?.result?.approveViewUrl; + if (!approveViewUrl) { + message.warning("审批记录地址为空"); + return; + } + window.open(approveViewUrl, "_blank"); + }); +} + function handleMockApprove(row) { mockApproveLaunch(row.id, {}).then((res) => { if (!isSuccessResponse(res)) { @@ -474,7 +503,10 @@ function handleModalSave(payload) { message.error(getResponseMessage(res, "保存合同失败")); return; } - message.success(payload.mode === "edit" ? "合同修改成功" : "合同草稿保存成功"); + message.success( + payload.successMessage || + (payload.mode === "edit" ? "合同修改成功" : "合同草稿保存成功"), + ); getList(); }); } diff --git a/src/views/contract/manifest/components/ContractTemplateManageModal.vue b/src/views/contract/manifest/components/ContractTemplateManageModal.vue index d3c3bd9..971a698 100644 --- a/src/views/contract/manifest/components/ContractTemplateManageModal.vue +++ b/src/views/contract/manifest/components/ContractTemplateManageModal.vue @@ -300,73 +300,33 @@ const templateSourceOptions = [ // 可绑定业务字段列表 const templateFieldList = [ - { key: 'contractName', label: '合同名称', category: '基本信息' }, - { key: 'contractCode', label: '合同编码', category: '基本信息' }, - { key: 'contractType', label: '合同类型', category: '基本信息' }, - { key: 'contractAmountDisplay', label: '合同金额', category: '基本信息' }, - { key: 'effectiveStart', label: '有效期起', category: '基本信息' }, - { key: 'effectiveEnd', label: '有效期止', category: '基本信息' }, - { key: 'effectivePeriod', label: '有效期区间', category: '基本信息' }, - { key: 'paymentMethod', label: '付款方式', category: '基本信息' }, - { key: 'primaryContent', label: '合同标的说明', category: '业务内容' }, - { key: 'amountExplain', label: '业务备注', category: '业务内容' }, - { key: 'partyAName', label: '甲方名称', category: '合同主体' }, - { key: 'partyBName', label: '乙方名称', category: '合同主体' }, - { key: 'partyCName', label: '丙方名称', category: '合同主体' }, - { key: 'counterpartySummary', label: '合同方摘要', category: '合同主体' }, - { key: 'attachmentSummary', label: '附件情况', category: '附件' }, - { key: 'templateLabel', label: '模板名称', category: '模板' }, - { key: 'signDate', label: '签署日期', category: '时间' }, + { value: 'contractName', label: '合同名称' }, + { value: 'contractCode', label: '合同编码' }, + { value: 'contractType', label: '合同类型' }, + { value: 'contractAmountDisplay', label: '合同金额' }, + { value: 'effectiveStart', label: '有效期起' }, + { value: 'effectiveEnd', label: '有效期止' }, + { value: 'effectivePeriod', label: '有效期区间' }, + { value: 'paymentMethod', label: '付款方式' }, + { value: 'primaryContent', label: '合同标的说明' }, + { value: 'amountExplain', label: '业务备注' }, + { value: 'partyAName', label: '甲方名称' }, + { value: 'partyBName', label: '乙方名称' }, + { value: 'partyCName', label: '丙方名称' }, + { value: 'counterpartySummary', label: '合同方摘要' }, + { value: 'contractSubject', label: '合同标的说明' }, + { value: 'businessRemark', label: '业务备注' }, + { value: 'attachmentSummary', label: '附件情况' }, + { value: 'templateLabel', label: '模板名称' }, + { value: 'signDate', label: '签署日期' }, ] // 业务字段下拉选项(由 templateFieldList 生成) const templateFieldOptions = templateFieldList.map((item) => ({ - label: `${item.label}(${item.key})`, - value: item.key, + label: `${item.label}(${item.value})`, + value: item.value, })) -// 根据字段 key 查找元数据 -function getTemplateFieldMeta(fieldKey) { - return templateFieldList.find((item) => item.key === fieldKey) || null -} - -// 获取字段的示例值 -function getTemplateFieldSampleValue(fieldKey) { - const map = { - contractName: '智慧园区设备采购合同', - contractCode: 'HT-2026-0008', - contractAmountDisplay: '368,000.00 元', - effectiveStart: '2026-05-10', - effectiveEnd: '2027-05-09', - effectivePeriod: '2026-05-10 至 2027-05-09', - paymentMethod: '银行转账(30%预付款,70%验收后支付)', - primaryContent: '园区门禁一体机、访客终端及部署实施服务', - amountExplain: '该示例用于演示模板变量填充效果。', - partyAName: '扬州宏富特种材料有限公司', - partyBName: '宁波港船务货运代理有限公司', - partyCName: '浙江临港数智科技有限公司', - counterpartySummary: '甲方:扬州宏富特种材料有限公司;乙方:宁波港船务货运代理有限公司', - attachmentSummary: '技术方案V1.pdf;补充协议草案.docx', - templateLabel: '采购合同模板', - signDate: '2026-05-14', - } - return map[fieldKey] || '' -} - -// 创建一条变量映射记录 -function createVariableMapping(input = {}) { - const meta = getTemplateFieldMeta(input.bindField || input.variableKey) - return { - id: input.id || `var_${Date.now()}_${Math.random().toString(16).slice(2, 8)}`, - variableKey: input.variableKey || '', - displayName: input.displayName || meta?.label || '', - bindField: input.bindField || '', - sampleValue: input.sampleValue || getTemplateFieldSampleValue(input.bindField) || '', - required: input.required ?? true, - description: input.description || meta?.category || '', - } -} - // 批量创建变量映射 function createSimpleVariableList(list = []) { return list.map((item, index) => ({