修改
This commit is contained in:
@@ -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) {
|
export function createLaunchDraft(data) {
|
||||||
return request({
|
return request({
|
||||||
url: "/contract/launch",
|
url: "/contract/launch",
|
||||||
|
|||||||
@@ -340,6 +340,11 @@
|
|||||||
{{ contractBodyHint }}
|
{{ contractBodyHint }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<a-space v-if="detailState.contractBodyFilePath" style="margin-top: 8px">
|
||||||
|
<a-button size="small" @click="downloadFile(detailState.contractBodyFilePath, detailState.contractBodyFileName || '合同正文.docx')">
|
||||||
|
下载
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="upload-block">
|
<div class="upload-block">
|
||||||
@@ -354,6 +359,18 @@
|
|||||||
>
|
>
|
||||||
<div class="other-upload-text">签订依据文件,对应法务 contractGistFile</div>
|
<div class="other-upload-text">签订依据文件,对应法务 contractGistFile</div>
|
||||||
</a-upload-dragger>
|
</a-upload-dragger>
|
||||||
|
<div v-if="detailState.contractGistFileList?.length" class="download-list">
|
||||||
|
<a-space wrap>
|
||||||
|
<a-button
|
||||||
|
v-for="item in detailState.contractGistFileList"
|
||||||
|
:key="item.uid || item.filePath"
|
||||||
|
size="small"
|
||||||
|
@click="downloadFile(item.filePath, item.name || item.fileName)"
|
||||||
|
>
|
||||||
|
下载 {{ item.name || item.fileName }}
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="upload-block">
|
<div class="upload-block">
|
||||||
@@ -368,6 +385,18 @@
|
|||||||
>
|
>
|
||||||
<div class="other-upload-text">其他附件(可上传多个文件)</div>
|
<div class="other-upload-text">其他附件(可上传多个文件)</div>
|
||||||
</a-upload-dragger>
|
</a-upload-dragger>
|
||||||
|
<div v-if="detailState.otherAttachmentList?.length" class="download-list">
|
||||||
|
<a-space wrap>
|
||||||
|
<a-button
|
||||||
|
v-for="item in detailState.otherAttachmentList"
|
||||||
|
:key="item.uid || item.filePath"
|
||||||
|
size="small"
|
||||||
|
@click="downloadFile(item.filePath, item.name || item.fileName)"
|
||||||
|
>
|
||||||
|
下载 {{ item.name || item.fileName }}
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a-form-item class="tight-item" label="主要内容">
|
<a-form-item class="tight-item" label="主要内容">
|
||||||
@@ -477,13 +506,13 @@
|
|||||||
<a-form-item class="tight-item" label="二级单位合同编码">
|
<a-form-item class="tight-item" label="二级单位合同编码">
|
||||||
<a-input
|
<a-input
|
||||||
v-model:value="detailState.selfCode"
|
v-model:value="detailState.selfCode"
|
||||||
placeholder="选填,对应法务 selfCode"
|
placeholder="选填:二级单位内部合同编码,对应法务 selfCode"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item class="tight-item" label="主合同编码">
|
<a-form-item class="tight-item" label="主合同编码">
|
||||||
<a-input
|
<a-input
|
||||||
v-model:value="detailState.mainContractCode"
|
v-model:value="detailState.mainContractCode"
|
||||||
placeholder="补录/变更合同时填写"
|
placeholder="选填:补录/变更时关联主合同编码,对应法务 mainContractCode"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item class="tight-item" label="固定期限日期类型">
|
<a-form-item class="tight-item" label="固定期限日期类型">
|
||||||
@@ -496,7 +525,7 @@
|
|||||||
<a-form-item class="tight-item" label="签约主体编码">
|
<a-form-item class="tight-item" label="签约主体编码">
|
||||||
<a-input
|
<a-input
|
||||||
v-model:value="detailState.signingSubjectCode"
|
v-model:value="detailState.signingSubjectCode"
|
||||||
placeholder="默认使用当前组织ID,多个用逗号分隔"
|
placeholder="起草人所在主体机构编码;当前默认取登录组织ID,多个用逗号分隔"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item class="tight-item" label="开票时间">
|
<a-form-item class="tight-item" label="开票时间">
|
||||||
@@ -707,7 +736,7 @@
|
|||||||
|
|
||||||
<a-collapse class="detail-collapse">
|
<a-collapse class="detail-collapse">
|
||||||
<a-collapse-panel
|
<a-collapse-panel
|
||||||
v-for="party in displayedParties"
|
v-for="party in counterpartyParties"
|
||||||
:key="party.roleKey"
|
:key="party.roleKey"
|
||||||
:header="`${party.displayName}详细字段维护`"
|
:header="`${party.displayName}详细字段维护`"
|
||||||
>
|
>
|
||||||
@@ -806,7 +835,12 @@ import { computed, ref } from "vue";
|
|||||||
import { message } from "ant-design-vue";
|
import { message } from "ant-design-vue";
|
||||||
import { PlusOutlined, SearchOutlined } from "@ant-design/icons-vue";
|
import { PlusOutlined, SearchOutlined } from "@ant-design/icons-vue";
|
||||||
import ContractTemplateFillDemoModal from "./ContractTemplateFillDemoModal.vue";
|
import ContractTemplateFillDemoModal from "./ContractTemplateFillDemoModal.vue";
|
||||||
import { queryPartnerOptions, uploadLaunchFile } from "@/api/launch.js";
|
import {
|
||||||
|
downloadLaunchFile,
|
||||||
|
getLaunchSelfProfile,
|
||||||
|
queryPartnerOptions,
|
||||||
|
uploadLaunchFile,
|
||||||
|
} from "@/api/launch.js";
|
||||||
import {
|
import {
|
||||||
contractSourceOptions,
|
contractSourceOptions,
|
||||||
contractTypeOptions,
|
contractTypeOptions,
|
||||||
@@ -911,7 +945,8 @@ const paymentMethodOptions = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const SELF_COMPANY_NAME = "浙港物流平台有限公司";
|
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) {
|
function getFileNameFromDetail(source, category) {
|
||||||
if (category === "body") {
|
if (category === "body") {
|
||||||
return source.contractBodyFileName || source.contractTextFileName || "";
|
return source.contractBodyFileName || source.contractTextFileName || "";
|
||||||
@@ -1021,6 +1090,12 @@ function copyPartyState(input) {
|
|||||||
const sourcePartyA = source.partyA || source.parties?.A || {};
|
const sourcePartyA = source.partyA || source.parties?.A || {};
|
||||||
const sourcePartyB = source.partyB || source.parties?.B || {};
|
const sourcePartyB = source.partyB || source.parties?.B || {};
|
||||||
const sourcePartyC = source.partyC || source.parties?.C || {};
|
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 = {
|
const state = {
|
||||||
isThreePartyContract: Boolean(source.isThreePartyContract),
|
isThreePartyContract: Boolean(source.isThreePartyContract),
|
||||||
partyA: {
|
partyA: {
|
||||||
@@ -1044,22 +1119,33 @@ function copyPartyState(input) {
|
|||||||
companyName: "",
|
companyName: "",
|
||||||
...sourcePartyC,
|
...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) {
|
if (listState) {
|
||||||
state.partyA = { ...state.partyA, ...listState.partyA };
|
state.partyA = { ...state.partyA, ...listState.partyA };
|
||||||
state.partyB = { ...state.partyB, ...listState.partyB };
|
state.counterpartyList = listState.counterpartyList.map((item) => ({ ...item }));
|
||||||
state.partyC = { ...state.partyC, ...listState.partyC };
|
state.partyB = { ...state.partyB, ...(state.counterpartyList[0] || {}) };
|
||||||
|
state.partyC = { ...state.partyC, ...(state.counterpartyList[1] || {}) };
|
||||||
state.isThreePartyContract = listState.isThreePartyContract;
|
state.isThreePartyContract = listState.isThreePartyContract;
|
||||||
|
} else {
|
||||||
|
state.partyB = { ...state.partyB, ...(state.counterpartyList[0] || {}) };
|
||||||
|
state.partyC = { ...state.partyC, ...(state.counterpartyList[1] || {}) };
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapLaunchPartyToFormParty(item = {}) {
|
function mapLaunchPartyToFormParty(item = {}) {
|
||||||
|
const roleIndex = COUNTERPARTY_ROLE_CODES.indexOf(item.roleCode || "B");
|
||||||
return {
|
return {
|
||||||
roleKey: item.roleCode || "B",
|
roleKey: item.roleCode || "B",
|
||||||
roleName: item.roleName || "",
|
roleName: item.roleName || "",
|
||||||
displayName:
|
displayName:
|
||||||
item.roleCode === "A" ? "我方" : item.roleCode === "C" ? "相对方2" : "相对方1",
|
item.roleCode === "A" ? "我方" : `相对方${roleIndex >= 0 ? roleIndex + 1 : 1}`,
|
||||||
partnerId: item.partnerId || null,
|
partnerId: item.partnerId || null,
|
||||||
customerSysid: item.partnerId || null,
|
customerSysid: item.partnerId || null,
|
||||||
oppositeId: item.oppositeId || "",
|
oppositeId: item.oppositeId || "",
|
||||||
@@ -1092,14 +1178,24 @@ function buildPartyStateFromList(list = []) {
|
|||||||
partyA: {},
|
partyA: {},
|
||||||
partyB: {},
|
partyB: {},
|
||||||
partyC: {},
|
partyC: {},
|
||||||
|
counterpartyList: [],
|
||||||
};
|
};
|
||||||
list.forEach((item) => {
|
list.forEach((item) => {
|
||||||
if (!item?.roleCode) return;
|
if (!item?.roleCode) return;
|
||||||
const targetKey =
|
if (item.roleCode === "A") {
|
||||||
item.roleCode === "A" ? "partyA" : item.roleCode === "C" ? "partyC" : "partyB";
|
result.partyA = mapLaunchPartyToFormParty(item);
|
||||||
result[targetKey] = 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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1114,8 +1210,12 @@ function getCompanyNatureByOppositeCharacter(value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createEmptyCounterparty(roleKey) {
|
function createEmptyCounterparty(roleKey) {
|
||||||
const blankState = copyPartyState({});
|
return {
|
||||||
return roleKey === "C" ? blankState.partyC : blankState.partyB;
|
roleKey,
|
||||||
|
roleName: "相对方",
|
||||||
|
displayName: `相对方${COUNTERPARTY_ROLE_CODES.indexOf(roleKey) + 1}`,
|
||||||
|
companyName: "",
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function isPartyFilled(party = {}) {
|
function isPartyFilled(party = {}) {
|
||||||
@@ -1129,35 +1229,37 @@ function isPartyFilled(party = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function buildCounterpartyRoleKeys(partyState) {
|
function buildCounterpartyRoleKeys(partyState) {
|
||||||
const state = copyPartyState(partyState);
|
const state = partyState || {};
|
||||||
const keys = [];
|
const sourceList = Array.isArray(state.counterpartyList)
|
||||||
if (isPartyFilled(state.partyB)) keys.push("B");
|
? state.counterpartyList
|
||||||
if (isPartyFilled(state.partyC)) keys.push("C");
|
: COUNTERPARTY_ROLE_CODES.map((roleKey) => state[`party${roleKey}`]).filter(Boolean);
|
||||||
return keys;
|
return sourceList
|
||||||
|
.filter((party) => isPartyFilled(party))
|
||||||
|
.map((party, index) => party.roleKey || COUNTERPARTY_ROLE_CODES[index])
|
||||||
|
.slice(0, MAX_COUNTERPARTY_COUNT);
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizePartyState(partyState) {
|
function normalizePartyState(partyState) {
|
||||||
const state = copyPartyState(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.partyA.companyName = state.partyA.companyName || SELF_COMPANY_NAME;
|
||||||
state.isThreePartyContract =
|
state.counterpartyList = (Array.isArray(state.counterpartyList) ? state.counterpartyList : [])
|
||||||
isPartyFilled(state.partyB) && isPartyFilled(state.partyC);
|
.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;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPartyByRoleKey(partyState, roleKey) {
|
function getPartyByRoleKey(partyState, roleKey) {
|
||||||
if (roleKey === "A") return partyState.partyA;
|
if (roleKey === "A") return partyState.partyA;
|
||||||
if (roleKey === "C") return partyState.partyC;
|
return (partyState.counterpartyList || []).find((item) => item.roleKey === roleKey);
|
||||||
return partyState.partyB;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 创建一条空的履行计划 */
|
/** 创建一条空的履行计划 */
|
||||||
@@ -1178,6 +1280,7 @@ function createPerformancePlan() {
|
|||||||
/** 创建空白的合同表单数据 */
|
/** 创建空白的合同表单数据 */
|
||||||
function createContractFormData() {
|
function createContractFormData() {
|
||||||
return {
|
return {
|
||||||
|
id: null,
|
||||||
contractName: "",
|
contractName: "",
|
||||||
contractCode: "",
|
contractCode: "",
|
||||||
contractType: "",
|
contractType: "",
|
||||||
@@ -1217,11 +1320,14 @@ function createContractFormData() {
|
|||||||
contractBodyArrayBuffer: null,
|
contractBodyArrayBuffer: null,
|
||||||
otherAttachmentList: [],
|
otherAttachmentList: [],
|
||||||
contractGistFileList: [],
|
contractGistFileList: [],
|
||||||
|
// 正文模板字段值统一放这里,后续新增模板字段时尽量只改 templateFieldList
|
||||||
|
templateFieldValues: {},
|
||||||
partyState: {
|
partyState: {
|
||||||
isThreePartyContract: false,
|
isThreePartyContract: false,
|
||||||
partyA: { companyName: SELF_COMPANY_NAME },
|
partyA: { companyName: SELF_COMPANY_NAME },
|
||||||
partyB: { companyName: "" },
|
partyB: { companyName: "" },
|
||||||
partyC: { companyName: "" },
|
partyC: { companyName: "" },
|
||||||
|
counterpartyList: [],
|
||||||
},
|
},
|
||||||
isInvolveHongKongMacaoTaiwan: false,
|
isInvolveHongKongMacaoTaiwan: false,
|
||||||
isPublicTender: false,
|
isPublicTender: false,
|
||||||
@@ -1257,6 +1363,18 @@ function copyContractFormData(data = {}) {
|
|||||||
Object.keys(form).forEach((key) => {
|
Object.keys(form).forEach((key) => {
|
||||||
if (key === "extraAmounts") {
|
if (key === "extraAmounts") {
|
||||||
form.extraAmounts = restoreExtraAmounts(source, form.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") {
|
} else if (key === "performancePlans") {
|
||||||
form.performancePlans = (source.performancePlans || []).map((item) => ({
|
form.performancePlans = (source.performancePlans || []).map((item) => ({
|
||||||
...createPerformancePlan(),
|
...createPerformancePlan(),
|
||||||
@@ -1290,6 +1408,17 @@ function copyContractFormData(data = {}) {
|
|||||||
} else if (key === "signingSubjectCode") {
|
} else if (key === "signingSubjectCode") {
|
||||||
form.signingSubjectCode =
|
form.signingSubjectCode =
|
||||||
source.signingSubjectCode ?? source.orgId ?? 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 {
|
} else {
|
||||||
form[key] = source[key] ?? form[key];
|
form[key] = source[key] ?? form[key];
|
||||||
}
|
}
|
||||||
@@ -1358,16 +1487,11 @@ const companyProfileMap = {
|
|||||||
const isViewMode = computed(() => modalMode.value === "view");
|
const isViewMode = computed(() => modalMode.value === "view");
|
||||||
|
|
||||||
const counterpartyParties = computed(() =>
|
const counterpartyParties = computed(() =>
|
||||||
counterpartyRoleKeys.value.map((roleKey) =>
|
(detailState.value.partyState.counterpartyList || []).filter((party) =>
|
||||||
getPartyByRoleKey(detailState.value.partyState, roleKey),
|
counterpartyRoleKeys.value.includes(party.roleKey),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
const displayedParties = computed(() => [
|
|
||||||
detailState.value.partyState.partyA,
|
|
||||||
...counterpartyParties.value,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const counterpartySummaryText = computed(() =>
|
const counterpartySummaryText = computed(() =>
|
||||||
counterpartyParties.value.length
|
counterpartyParties.value.length
|
||||||
? counterpartyParties.value
|
? counterpartyParties.value
|
||||||
@@ -1412,11 +1536,10 @@ function syncUploadLists() {
|
|||||||
|
|
||||||
function syncCounterpartyState() {
|
function syncCounterpartyState() {
|
||||||
detailState.value.partyState = normalizePartyState(detailState.value.partyState);
|
detailState.value.partyState = normalizePartyState(detailState.value.partyState);
|
||||||
counterpartyRoleKeys.value = buildCounterpartyRoleKeys(
|
counterpartyRoleKeys.value = buildCounterpartyRoleKeys(detailState.value.partyState);
|
||||||
detailState.value.partyState,
|
detailState.value.partyState.counterpartyList =
|
||||||
);
|
detailState.value.partyState.counterpartyList || [];
|
||||||
detailState.value.partyState.isThreePartyContract =
|
detailState.value.partyState.isThreePartyContract = counterpartyRoleKeys.value.length > 1;
|
||||||
counterpartyRoleKeys.value.length > 1;
|
|
||||||
refreshPartyDisplayMeta();
|
refreshPartyDisplayMeta();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1425,8 +1548,8 @@ function refreshPartyDisplayMeta() {
|
|||||||
detailState.value.partyState.partyA.roleName = "我方";
|
detailState.value.partyState.partyA.roleName = "我方";
|
||||||
detailState.value.partyState.partyA.displayName = "我方";
|
detailState.value.partyState.partyA.displayName = "我方";
|
||||||
|
|
||||||
counterpartyRoleKeys.value.forEach((roleKey, index) => {
|
(detailState.value.partyState.counterpartyList || []).forEach((party, index) => {
|
||||||
const party = getPartyByRoleKey(detailState.value.partyState, roleKey);
|
const roleKey = party.roleKey || COUNTERPARTY_ROLE_CODES[index];
|
||||||
party.roleKey = roleKey;
|
party.roleKey = roleKey;
|
||||||
party.roleName = "相对方";
|
party.roleName = "相对方";
|
||||||
party.displayName = `相对方${index + 1}`;
|
party.displayName = `相对方${index + 1}`;
|
||||||
@@ -1443,11 +1566,19 @@ function resetCurrentState() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function showSearchTip(title) {
|
function showSearchTip(title) {
|
||||||
|
if (title === "我方详情") {
|
||||||
|
message.info("我方信息当前按登录组织只读带出,后续接入真实组织主数据后自动完善。");
|
||||||
|
return;
|
||||||
|
}
|
||||||
loadPartnerOptions();
|
loadPartnerOptions();
|
||||||
message.info(`${title}数据已从相对方管理加载,可直接在下拉框搜索选择。`);
|
message.info(`${title}数据已从相对方管理加载,可直接在下拉框搜索选择。`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function showInvoiceTitles(party, roleName) {
|
function showInvoiceTitles(party, roleName) {
|
||||||
|
if (roleName === "我方") {
|
||||||
|
message.info("我方开票抬头当前不在发起页维护,后续建议从组织主数据自动带出。");
|
||||||
|
return;
|
||||||
|
}
|
||||||
const titles = party.invoiceTitleOptions || [];
|
const titles = party.invoiceTitleOptions || [];
|
||||||
message.info(
|
message.info(
|
||||||
titles.length
|
titles.length
|
||||||
@@ -1536,9 +1667,7 @@ function handleCompanySelect(roleKey, companyName, option = {}) {
|
|||||||
const party =
|
const party =
|
||||||
roleKey === "A"
|
roleKey === "A"
|
||||||
? detailState.value.partyState.partyA
|
? detailState.value.partyState.partyA
|
||||||
: roleKey === "B"
|
: getPartyByRoleKey(detailState.value.partyState, roleKey);
|
||||||
? detailState.value.partyState.partyB
|
|
||||||
: detailState.value.partyState.partyC;
|
|
||||||
if (!party) return;
|
if (!party) return;
|
||||||
|
|
||||||
const rawPartner =
|
const rawPartner =
|
||||||
@@ -1586,36 +1715,34 @@ function handleAddCounterparty() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextRoleKey = counterpartyRoleKeys.value.includes("B") ? "C" : "B";
|
const nextRoleKey = COUNTERPARTY_ROLE_CODES.find(
|
||||||
detailState.value.partyState[nextRoleKey === "C" ? "partyC" : "partyB"] =
|
(roleKey) => !counterpartyRoleKeys.value.includes(roleKey),
|
||||||
createEmptyCounterparty(nextRoleKey);
|
);
|
||||||
|
if (!nextRoleKey) return;
|
||||||
|
|
||||||
|
detailState.value.partyState.counterpartyList =
|
||||||
|
detailState.value.partyState.counterpartyList || [];
|
||||||
|
detailState.value.partyState.counterpartyList.push(
|
||||||
|
createEmptyCounterparty(nextRoleKey),
|
||||||
|
);
|
||||||
counterpartyRoleKeys.value = [...counterpartyRoleKeys.value, nextRoleKey];
|
counterpartyRoleKeys.value = [...counterpartyRoleKeys.value, nextRoleKey];
|
||||||
detailState.value.partyState.isThreePartyContract =
|
detailState.value.partyState.isThreePartyContract = counterpartyRoleKeys.value.length > 1;
|
||||||
counterpartyRoleKeys.value.length > 1;
|
|
||||||
refreshPartyDisplayMeta();
|
refreshPartyDisplayMeta();
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleRemoveCounterparty(roleKey) {
|
function handleRemoveCounterparty(roleKey) {
|
||||||
if (roleKey === "B" && counterpartyRoleKeys.value.includes("C")) {
|
detailState.value.partyState.counterpartyList =
|
||||||
detailState.value.partyState.partyB = {
|
(detailState.value.partyState.counterpartyList || [])
|
||||||
...createEmptyCounterparty("B"),
|
.filter((item) => item.roleKey !== roleKey)
|
||||||
...detailState.value.partyState.partyC,
|
.map((item, index) => ({
|
||||||
roleKey: "B",
|
...item,
|
||||||
roleName: "相对方",
|
roleKey: COUNTERPARTY_ROLE_CODES[index],
|
||||||
displayName: "相对方1",
|
displayName: `相对方${index + 1}`,
|
||||||
};
|
}));
|
||||||
detailState.value.partyState.partyC = createEmptyCounterparty("C");
|
counterpartyRoleKeys.value = detailState.value.partyState.counterpartyList.map(
|
||||||
counterpartyRoleKeys.value = ["B"];
|
(item) => item.roleKey,
|
||||||
} else {
|
);
|
||||||
detailState.value.partyState[roleKey === "C" ? "partyC" : "partyB"] =
|
detailState.value.partyState.isThreePartyContract = counterpartyRoleKeys.value.length > 1;
|
||||||
createEmptyCounterparty(roleKey);
|
|
||||||
counterpartyRoleKeys.value = counterpartyRoleKeys.value.filter(
|
|
||||||
(key) => key !== roleKey,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
detailState.value.partyState.isThreePartyContract =
|
|
||||||
counterpartyRoleKeys.value.length > 1;
|
|
||||||
refreshPartyDisplayMeta();
|
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() {
|
function openBodyConfigModal() {
|
||||||
bodyConfigModalRef.value?.showModal(
|
bodyConfigModalRef.value?.showModal(
|
||||||
{
|
{
|
||||||
@@ -1695,6 +1843,31 @@ function handleBodyConfigSave(payload = {}) {
|
|||||||
detailState.value = copyContractFormData(payload.detailState);
|
detailState.value = copyContractFormData(payload.detailState);
|
||||||
syncCounterpartyState();
|
syncCounterpartyState();
|
||||||
syncUploadLists();
|
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() {
|
function addPerformancePlan() {
|
||||||
@@ -1799,8 +1972,8 @@ function buildLaunchRecord() {
|
|||||||
|
|
||||||
counterpartyParties.value.forEach((party, index) => {
|
counterpartyParties.value.forEach((party, index) => {
|
||||||
partyList.push({
|
partyList.push({
|
||||||
roleCode: index === 0 ? "B" : "C",
|
roleCode: party.roleKey || COUNTERPARTY_ROLE_CODES[index],
|
||||||
roleName: index === 0 ? "乙方" : "丙方",
|
roleName: party.displayName || `相对方${index + 1}`,
|
||||||
partnerId: party.partnerId || party.customerSysid || null,
|
partnerId: party.partnerId || party.customerSysid || null,
|
||||||
oppositeId: party.oppositeId,
|
oppositeId: party.oppositeId,
|
||||||
companyName: party.companyName,
|
companyName: party.companyName,
|
||||||
@@ -1869,12 +2042,8 @@ function buildLaunchRecord() {
|
|||||||
currencyName: detailState.value.currencyName,
|
currencyName: detailState.value.currencyName,
|
||||||
contractPeriodType: detailState.value.contractPeriodType,
|
contractPeriodType: detailState.value.contractPeriodType,
|
||||||
periodExplain: detailState.value.periodExplain,
|
periodExplain: detailState.value.periodExplain,
|
||||||
effectiveStart: detailState.value.effectiveStart
|
effectiveStart: formatDateTimeValue(detailState.value.effectiveStart),
|
||||||
? `${detailState.value.effectiveStart} 00:00:00`
|
effectiveEnd: formatDateTimeValue(detailState.value.effectiveEnd, true),
|
||||||
: null,
|
|
||||||
effectiveEnd: detailState.value.effectiveEnd
|
|
||||||
? `${detailState.value.effectiveEnd} 23:59:59`
|
|
||||||
: null,
|
|
||||||
paymentMethod: detailState.value.paymentMethod,
|
paymentMethod: detailState.value.paymentMethod,
|
||||||
primaryContent: detailState.value.primaryContent,
|
primaryContent: detailState.value.primaryContent,
|
||||||
amountExplain: detailState.value.amountExplain,
|
amountExplain: detailState.value.amountExplain,
|
||||||
@@ -1909,6 +2078,9 @@ function buildLaunchRecord() {
|
|||||||
signDate: formatDateTimeValue(detailState.value.signDate),
|
signDate: formatDateTimeValue(detailState.value.signDate),
|
||||||
invoiceDate: formatDateTimeValue(detailState.value.invoiceDate),
|
invoiceDate: formatDateTimeValue(detailState.value.invoiceDate),
|
||||||
receiveDate: formatDateTimeValue(detailState.value.receiveDate),
|
receiveDate: formatDateTimeValue(detailState.value.receiveDate),
|
||||||
|
templateFieldValuesJson: JSON.stringify(
|
||||||
|
detailState.value.templateFieldValues || {},
|
||||||
|
),
|
||||||
remindDays: 3,
|
remindDays: 3,
|
||||||
signingSubjectCode: detailState.value.signingSubjectCode || "",
|
signingSubjectCode: detailState.value.signingSubjectCode || "",
|
||||||
extraAmounts: { ...detailState.value.extraAmounts },
|
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";
|
modalMode.value = mode || "create";
|
||||||
const source = context.detailState ||
|
const source = context.detailState ||
|
||||||
(Object.keys(context || {}).length ? context : createContractFormData());
|
(Object.keys(context || {}).length ? context : createContractFormData());
|
||||||
|
|
||||||
creationMode.value =
|
creationMode.value =
|
||||||
context.creationMode ||
|
context.creationMode ||
|
||||||
(source.contractBodyArrayBuffer ? "manual" : "template");
|
source.creationMode ||
|
||||||
templateKey.value = context.templateKey || "";
|
(source.contractBodyArrayBuffer || source.contractBodyFilePath ? "manual" : "template");
|
||||||
templateName.value = context.templateName || "";
|
templateKey.value = context.templateKey || source.templateId || "";
|
||||||
|
templateName.value = context.templateName || source.templateName || "";
|
||||||
detailState.value = copyContractFormData(source);
|
detailState.value = copyContractFormData(source);
|
||||||
|
const selfProfile = await getLaunchSelfProfile();
|
||||||
|
applySelfProfile(selfProfile || {});
|
||||||
syncCounterpartyState();
|
syncCounterpartyState();
|
||||||
syncUploadLists();
|
syncUploadLists();
|
||||||
loadPartnerOptions();
|
loadPartnerOptions();
|
||||||
|
|||||||
@@ -228,65 +228,25 @@ const templateFieldList = [
|
|||||||
{ value: 'contractName', label: '合同名称' },
|
{ value: 'contractName', label: '合同名称' },
|
||||||
{ value: 'contractCode', label: '合同编码' },
|
{ value: 'contractCode', label: '合同编码' },
|
||||||
{ value: 'contractType', label: '合同类型' },
|
{ value: 'contractType', label: '合同类型' },
|
||||||
{ value: 'contractTypeCode', label: '合同类型编码' },
|
|
||||||
{ value: 'contractAmountDisplay', label: '合同金额' },
|
{ value: 'contractAmountDisplay', label: '合同金额' },
|
||||||
{ value: 'contractAmount', label: '合同金额数字' },
|
|
||||||
{ value: 'effectiveStart', label: '有效期起' },
|
{ value: 'effectiveStart', label: '有效期起' },
|
||||||
{ value: 'effectiveEnd', label: '有效期止' },
|
{ value: 'effectiveEnd', label: '有效期止' },
|
||||||
{ value: 'effectivePeriod', label: '有效期区间' },
|
{ value: 'effectivePeriod', label: '有效期区间' },
|
||||||
{ value: 'contractPeriodTypeLabel', label: '合同期限类型' },
|
|
||||||
{ value: 'periodExplain', label: '期限说明' },
|
|
||||||
{ value: 'paymentMethod', 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: '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: 'partyAName', label: '甲方名称' },
|
||||||
{ value: 'partyBName', label: '乙方名称' },
|
{ value: 'partyBName', label: '乙方名称' },
|
||||||
{ value: 'partyCName', label: '丙方名称' },
|
{ value: 'partyCName', label: '丙方名称' },
|
||||||
{ value: 'partyAContactName', label: '甲方联系人' },
|
{ value: 'partyDName', label: '丁方名称' },
|
||||||
{ value: 'partyBContactName', label: '乙方联系人' },
|
{ value: 'partyEName', label: '戊方名称' },
|
||||||
{ value: 'partyCContactName', label: '丙方联系人' },
|
{ value: 'partyFName', 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: 'counterpartySummary', label: '合同方摘要' },
|
||||||
{ value: 'attachmentSummary', label: '附件情况' },
|
{ value: 'attachmentSummary', label: '附件情况' },
|
||||||
{ value: 'contractGistFileSummary', label: '签订依据附件' },
|
|
||||||
{ value: 'contractBodyFileName', label: '合同正文文件名' },
|
|
||||||
{ value: 'extraAmountSummary', label: '额外金额' },
|
|
||||||
{ value: 'performancePlanSummary', label: '履行计划' },
|
|
||||||
{ value: 'templateLabel', label: '模板名称' },
|
{ value: 'templateLabel', label: '模板名称' },
|
||||||
{ value: 'signDate', label: '签署日期' },
|
{ value: 'signDate', label: '签署日期' },
|
||||||
|
{ value: 'businessRemark', label: '业务备注' },
|
||||||
|
{ value: 'contractSubject', label: '合同标的说明' },
|
||||||
]
|
]
|
||||||
|
|
||||||
// ========== 格式化合同金额(如 368,000.00 元) ==========
|
// ========== 格式化合同金额(如 368,000.00 元) ==========
|
||||||
@@ -396,24 +356,51 @@ function buildPerformancePlanSummary(list = []) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ========== 合同方相关函数 ==========
|
// ========== 合同方相关函数 ==========
|
||||||
|
const COUNTERPARTY_ROLE_CODES = ['B', 'C', 'D', 'E', 'F']
|
||||||
|
|
||||||
/** 深拷贝合同方状态 */
|
/** 深拷贝合同方状态 */
|
||||||
function copyPartyState(input) {
|
function copyPartyState(input) {
|
||||||
const source = input || {}
|
const source = input || {}
|
||||||
const listState = buildPartyStateFromList(source.partyList)
|
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 {
|
return {
|
||||||
isThreePartyContract: Boolean(source.isThreePartyContract),
|
partyA: {
|
||||||
partyA: { roleKey: 'A', roleName: '甲方', companyName: '', ...(source.partyA || source.parties?.A || {}), ...(listState?.partyA || {}) },
|
roleKey: 'A',
|
||||||
partyB: { roleKey: 'B', roleName: '乙方', companyName: '', ...(source.partyB || source.parties?.B || {}), ...(listState?.partyB || {}) },
|
roleName: '我方',
|
||||||
partyC: { roleKey: 'C', roleName: '丙方', companyName: '', ...(source.partyC || source.parties?.C || {}), ...(listState?.partyC || {}) },
|
displayName: '我方',
|
||||||
...(listState ? { isThreePartyContract: listState.isThreePartyContract } : {}),
|
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 = {}) {
|
function mapLaunchPartyToFormParty(item = {}) {
|
||||||
|
const roleIndex = COUNTERPARTY_ROLE_CODES.indexOf(item.roleCode || 'B')
|
||||||
return {
|
return {
|
||||||
roleKey: item.roleCode || 'B',
|
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,
|
partnerId: item.partnerId || null,
|
||||||
oppositeId: item.oppositeId || '',
|
oppositeId: item.oppositeId || '',
|
||||||
companyName: item.companyName || '',
|
companyName: item.companyName || '',
|
||||||
@@ -437,21 +424,29 @@ function mapLaunchPartyToFormParty(item = {}) {
|
|||||||
|
|
||||||
function buildPartyStateFromList(list = []) {
|
function buildPartyStateFromList(list = []) {
|
||||||
if (!Array.isArray(list) || !list.length) return null
|
if (!Array.isArray(list) || !list.length) return null
|
||||||
const state = { partyA: {}, partyB: {}, partyC: {}, isThreePartyContract: false }
|
const state = { partyA: {}, counterpartyList: [], isThreePartyContract: false }
|
||||||
list.forEach((item) => {
|
list.forEach((item) => {
|
||||||
const key = item.roleCode === 'A' ? 'partyA' : item.roleCode === 'C' ? 'partyC' : 'partyB'
|
if (item.roleCode === 'A') {
|
||||||
state[key] = mapLaunchPartyToFormParty(item)
|
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
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 获取有效的合同方列表 */
|
/** 获取有效的合同方列表 */
|
||||||
function getPartyList(partyState) {
|
function getPartyList(partyState) {
|
||||||
const state = copyPartyState(partyState)
|
const state = copyPartyState(partyState)
|
||||||
const list = [state.partyA, state.partyB]
|
return [state.partyA, ...(state.counterpartyList || [])].filter(
|
||||||
if (state.isThreePartyContract) list.push(state.partyC)
|
(item) => item && String(item.companyName || '').trim() !== '',
|
||||||
return list
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 获取合同方摘要(如 "甲方:xxx;乙方:xxx") */
|
/** 获取合同方摘要(如 "甲方:xxx;乙方:xxx") */
|
||||||
@@ -527,6 +522,7 @@ function createPerformancePlan() {
|
|||||||
/** 创建空白的合同表单数据 */
|
/** 创建空白的合同表单数据 */
|
||||||
function createContractFormData() {
|
function createContractFormData() {
|
||||||
return {
|
return {
|
||||||
|
id: null,
|
||||||
// 合同基本信息
|
// 合同基本信息
|
||||||
contractName: '', // 合同名称
|
contractName: '', // 合同名称
|
||||||
contractCode: '', // 合同编码
|
contractCode: '', // 合同编码
|
||||||
@@ -568,13 +564,13 @@ function createContractFormData() {
|
|||||||
contractBodyArrayBuffer: null, // 合同正文文件二进制
|
contractBodyArrayBuffer: null, // 合同正文文件二进制
|
||||||
otherAttachmentList: [], // 其他附件列表
|
otherAttachmentList: [], // 其他附件列表
|
||||||
contractGistFileList: [], // 签订依据附件列表
|
contractGistFileList: [], // 签订依据附件列表
|
||||||
|
templateFieldValues: {}, // 模板字段值,统一按 {字段名: 值} 保存
|
||||||
|
|
||||||
// 合同方
|
// 合同方
|
||||||
partyState: {
|
partyState: {
|
||||||
isThreePartyContract: false,
|
isThreePartyContract: false,
|
||||||
partyA: { companyName: '' },
|
partyA: { companyName: '', roleKey: 'A', roleName: '我方', displayName: '我方' },
|
||||||
partyB: { companyName: '' },
|
counterpartyList: [],
|
||||||
partyC: { companyName: '' },
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// 合同属性
|
// 合同属性
|
||||||
@@ -647,6 +643,17 @@ function copyContractFormData(data = {}) {
|
|||||||
form.contractBodyFilePath = fileState.contractBodyFilePath
|
form.contractBodyFilePath = fileState.contractBodyFilePath
|
||||||
form.contractBodyFileType = fileState.contractBodyFileType
|
form.contractBodyFileType = fileState.contractBodyFileType
|
||||||
form.contractBodyFileSize = fileState.contractBodyFileSize
|
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
|
form.contractBodyArrayBuffer = source.contractBodyArrayBuffer
|
||||||
? (source.contractBodyArrayBuffer instanceof ArrayBuffer ? source.contractBodyArrayBuffer.slice(0) : null)
|
? (source.contractBodyArrayBuffer instanceof ArrayBuffer ? source.contractBodyArrayBuffer.slice(0) : null)
|
||||||
: null
|
: null
|
||||||
@@ -686,77 +693,45 @@ function copyContractFormData(data = {}) {
|
|||||||
/** 将合同表单数据转换为模板变量字典(用于 docxtemplater 渲染) */
|
/** 将合同表单数据转换为模板变量字典(用于 docxtemplater 渲染) */
|
||||||
function buildTemplatePreviewData(formData, templateLabel = '', creationMode = 'template') {
|
function buildTemplatePreviewData(formData, templateLabel = '', creationMode = 'template') {
|
||||||
const form = copyContractFormData(formData)
|
const form = copyContractFormData(formData)
|
||||||
const attachmentSummary = formatFileSummary(form.otherAttachmentList)
|
const partyA = getPartyList(form).find((item) => item.roleCode === 'A') || {}
|
||||||
const contractGistFileSummary = formatFileSummary(form.contractGistFileList)
|
const partyB = getPartyList(form).find((item) => item.roleCode === 'B') || {}
|
||||||
const partyA = form.partyState.partyA || {}
|
const partyC = getPartyList(form).find((item) => item.roleCode === 'C') || {}
|
||||||
const partyB = form.partyState.partyB || {}
|
const partyD = getPartyList(form).find((item) => item.roleCode === 'D') || {}
|
||||||
const partyC = form.partyState.partyC || {}
|
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 {
|
return {
|
||||||
contractName: form.contractName || '未填写',
|
contractName: pick('contractName', form.contractName || ''),
|
||||||
contractCode: form.contractCode || '未填写',
|
contractCode: pick('contractCode', form.contractCode || ''),
|
||||||
contractType: getOptionLabel(contractTypeOptions, form.contractType) || form.contractType || '未填写',
|
contractType: pick('contractType', form.contractType || ''),
|
||||||
contractTypeCode: form.contractTypeCode || '',
|
contractAmountDisplay: pick('contractAmountDisplay', formatContractAmount(form.contractAmount)),
|
||||||
contractAmountDisplay: formatContractAmount(form.contractAmount),
|
effectiveStart: pick('effectiveStart', form.effectiveStart || ''),
|
||||||
contractAmount: form.contractAmount ?? '',
|
effectiveEnd: pick('effectiveEnd', form.effectiveEnd || ''),
|
||||||
effectiveStart: form.effectiveStart || '--',
|
effectivePeriod: pick('effectivePeriod', [form.effectiveStart, form.effectiveEnd].filter(Boolean).join(' 至 ')),
|
||||||
effectiveEnd: form.effectiveEnd || '--',
|
paymentMethod: pick('paymentMethod', form.paymentMethod || ''),
|
||||||
effectivePeriod: form.contractPeriodType === '1'
|
primaryContent: pick('primaryContent', form.primaryContent || ''),
|
||||||
? form.periodExplain || '无固定期限'
|
amountExplain: pick('amountExplain', form.amountExplain || ''),
|
||||||
: `${form.effectiveStart || '--'} 至 ${form.effectiveEnd || '--'}`,
|
partyAName: pick('partyAName', partyA.companyName || ''),
|
||||||
contractPeriodTypeLabel: getContractPeriodTypeLabel(form.contractPeriodType),
|
partyBName: pick('partyBName', partyB.companyName || ''),
|
||||||
periodExplain: form.periodExplain || '',
|
partyCName: pick('partyCName', partyC.companyName || ''),
|
||||||
paymentMethod: form.paymentMethod || '未填写',
|
partyDName: pick('partyDName', partyD.companyName || ''),
|
||||||
paymentDirectionLabel: getOptionLabel(paymentDirectionOptions, form.paymentDirection),
|
partyEName: pick('partyEName', partyE.companyName || ''),
|
||||||
valuationModeLabel: getOptionLabel(valuationModeOptions, form.valuationMode),
|
partyFName: pick('partyFName', partyF.companyName || ''),
|
||||||
currencyNameLabel: getOptionLabel(currencyOptions, form.currencyName),
|
counterpartySummary: pick('counterpartySummary', getPartySummary(form)),
|
||||||
sealTypeLabel: getOptionLabel(sealTypeOptions, form.sealType),
|
attachmentSummary: pick('attachmentSummary', formatFileSummary(form.otherAttachmentList)),
|
||||||
textSourceLabel: getOptionLabel(textSourceOptions, form.textSource),
|
templateLabel: pick('templateLabel', templateLabel || (creationMode === 'manual' ? '手动创建' : '')),
|
||||||
isElectronLabel: getOptionLabel(isElectronOptions, form.isElectron),
|
signDate: pick('signDate', form.signDate || dayjs().format('YYYY-MM-DD')),
|
||||||
contractSourceLabel: getOptionLabel(contractSourceOptions, form.contractSource),
|
businessRemark: pick('businessRemark', ''),
|
||||||
dateTypeLabel: getOptionLabel(dateTypeOptions, form.dateType),
|
contractSubject: pick('contractSubject', ''),
|
||||||
mandateType: form.mandateType || '',
|
templateFieldValuesJson: JSON.stringify(form.templateFieldValues || {}),
|
||||||
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 || '',
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -871,9 +846,6 @@ const templateSelectOptions = computed(() =>
|
|||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO: 这里后续替换为“根据模板ID查询模板详情接口”
|
|
||||||
// TODO: 这里后续如果“已发布模板列表接口”不返回完整详情,再改成调用“模板详情接口”
|
|
||||||
//相当于详情接口返回了 映射 然后下面做匹配
|
|
||||||
const activeTemplate = computed(() =>
|
const activeTemplate = computed(() =>
|
||||||
templateKey.value ? getManifestDetailLocal(templateKey.value) : null,
|
templateKey.value ? getManifestDetailLocal(templateKey.value) : null,
|
||||||
);
|
);
|
||||||
@@ -884,7 +856,6 @@ const modalTitle = computed(() => "合同正文配置");
|
|||||||
|
|
||||||
const templateFieldLabelMap = computed(() => {
|
const templateFieldLabelMap = computed(() => {
|
||||||
const map = {};
|
const map = {};
|
||||||
//这个就是字典数据,包含所有定义的映射字段
|
|
||||||
templateFieldList.forEach((item) => {
|
templateFieldList.forEach((item) => {
|
||||||
map[item.value] = item.label;
|
map[item.value] = item.label;
|
||||||
});
|
});
|
||||||
@@ -892,39 +863,34 @@ const templateFieldLabelMap = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const launchFieldList = computed(() => {
|
const launchFieldList = computed(() => {
|
||||||
if (creationMode.value === "manual") {
|
if (creationMode.value === "manual") return [];
|
||||||
return [];
|
|
||||||
}
|
|
||||||
// 模板模式:从 variableMappings 中取出 bindField
|
|
||||||
const mappings = activeTemplate.value?.variableMappings || [];
|
const mappings = activeTemplate.value?.variableMappings || [];
|
||||||
const fieldKeys = [];
|
const keys = [];
|
||||||
|
|
||||||
mappings.forEach((item) => {
|
mappings.forEach((item) => {
|
||||||
if (!item.bindField) return;
|
const fieldKey = item.bindField || item.variableKey;
|
||||||
if (!fieldKeys.includes(item.bindField)) {
|
if (fieldKey && !keys.includes(fieldKey)) {
|
||||||
fieldKeys.push(item.bindField);
|
keys.push(fieldKey);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
return keys.map((key) => ({
|
||||||
return fieldKeys.map((key) => ({
|
|
||||||
key,
|
key,
|
||||||
label: templateFieldLabelMap.value[key] || key,
|
label: templateFieldLabelMap.value[key] || key,
|
||||||
readonly: Object.prototype.hasOwnProperty.call(templateVariables.value, key),
|
readonly: false,
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
function getLaunchFieldValue(fieldKey) {
|
function getLaunchFieldValue(fieldKey) {
|
||||||
if (Object.prototype.hasOwnProperty.call(templateVariables.value, fieldKey)) {
|
return contractDetailState.value.templateFieldValues?.[fieldKey] ?? "";
|
||||||
return templateVariables.value[fieldKey] ?? "";
|
|
||||||
}
|
|
||||||
return contractDetailState.value[fieldKey] ?? "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setLaunchFieldValue(fieldKey, value) {
|
function setLaunchFieldValue(fieldKey, value) {
|
||||||
if (Object.prototype.hasOwnProperty.call(templateVariables.value, fieldKey)) {
|
if (!contractDetailState.value.templateFieldValues) {
|
||||||
return;
|
contractDetailState.value.templateFieldValues = {};
|
||||||
|
}
|
||||||
|
contractDetailState.value.templateFieldValues[fieldKey] = value;
|
||||||
|
if (visible.value) {
|
||||||
|
schedulePreview();
|
||||||
}
|
}
|
||||||
contractDetailState.value[fieldKey] = value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const templateVariables = computed(() =>
|
const templateVariables = computed(() =>
|
||||||
@@ -936,22 +902,24 @@ const templateVariables = computed(() =>
|
|||||||
);
|
);
|
||||||
|
|
||||||
const hasPreviewSource = computed(() => {
|
const hasPreviewSource = computed(() => {
|
||||||
if (creationMode.value === "manual") {
|
return creationMode.value === "manual"
|
||||||
return !!contractDetailState.value.contractBodyArrayBuffer;
|
? !!(
|
||||||
}
|
contractDetailState.value.contractBodyArrayBuffer ||
|
||||||
return !!activeTemplate.value;
|
contractDetailState.value.contractBodyFilePath
|
||||||
|
)
|
||||||
|
: !!activeTemplate.value;
|
||||||
});
|
});
|
||||||
|
|
||||||
const previewDescription = computed(() =>
|
const previewDescription = computed(() =>
|
||||||
creationMode.value === "manual"
|
creationMode.value === "manual"
|
||||||
? "手动创建模式下,右侧直接渲染当前窗口上传的合同正文 Word 文件。"
|
? "手动创建模式下,右侧直接渲染当前上传的合同正文 Word 文件。"
|
||||||
: "模板创建模式下,系统会根据维护后的合同信息自动填充模板变量并生成合同正文。",
|
: "模板创建模式下,系统按模板变量映射填充正文。",
|
||||||
);
|
);
|
||||||
|
|
||||||
const emptyPreviewText = computed(() =>
|
const emptyPreviewText = computed(() =>
|
||||||
creationMode.value === "manual"
|
creationMode.value === "manual"
|
||||||
? "请先在当前窗口上传合同正文 Word 文件。"
|
? "请先上传合同正文 Word 文件。"
|
||||||
: "请选择模板并完善合同信息后再预览。",
|
: "请选择模板并完善模板变量后再预览。",
|
||||||
);
|
);
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
@@ -1268,6 +1236,10 @@ async function uploadBlobAsLaunchFile(blob, fileName) {
|
|||||||
contractDetailState.value.contractBodyFileSize = file.size;
|
contractDetailState.value.contractBodyFileSize = file.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildTemplateFieldValuesPayload() {
|
||||||
|
return JSON.stringify(contractDetailState.value.templateFieldValues || {});
|
||||||
|
}
|
||||||
|
|
||||||
async function ensureTemplateBodyFileUploaded() {
|
async function ensureTemplateBodyFileUploaded() {
|
||||||
if (creationMode.value !== "template") return;
|
if (creationMode.value !== "template") return;
|
||||||
const blob = previewBlob.value || (await buildFilledDocumentBlob());
|
const blob = previewBlob.value || (await buildFilledDocumentBlob());
|
||||||
@@ -1312,6 +1284,7 @@ async function handleSave() {
|
|||||||
templateKey: templateKey.value,
|
templateKey: templateKey.value,
|
||||||
templateName: activeTemplate.value?.templateName || "",
|
templateName: activeTemplate.value?.templateName || "",
|
||||||
detailState: copyContractFormData(contractDetailState.value),
|
detailState: copyContractFormData(contractDetailState.value),
|
||||||
|
templateFieldValuesJson: buildTemplateFieldValuesPayload(),
|
||||||
});
|
});
|
||||||
visible.value = false;
|
visible.value = false;
|
||||||
message.success("合同正文配置已保存");
|
message.success("合同正文配置已保存");
|
||||||
@@ -1342,13 +1315,14 @@ async function showModal(initialValues = {}, mode = "create") {
|
|||||||
creationMode.value = isCreate
|
creationMode.value = isCreate
|
||||||
? "template"
|
? "template"
|
||||||
: initialValues.creationMode ||
|
: initialValues.creationMode ||
|
||||||
|
initialValues.templateId ||
|
||||||
(initialValues.detailState?.contractBodyArrayBuffer ||
|
(initialValues.detailState?.contractBodyArrayBuffer ||
|
||||||
initialValues.contractBodyArrayBuffer
|
initialValues.contractBodyArrayBuffer
|
||||||
? "manual"
|
? "manual"
|
||||||
: "template");
|
: "template");
|
||||||
templateKey.value = isCreate
|
templateKey.value = isCreate
|
||||||
? publishedTemplates.value[0]?.id || ""
|
? publishedTemplates.value[0]?.id || ""
|
||||||
: initialValues.templateKey || "";
|
: initialValues.templateKey || initialValues.templateId || initialValues.detailState?.templateId || "";
|
||||||
contractDetailState.value = isCreate
|
contractDetailState.value = isCreate
|
||||||
? createContractFormData()
|
? createContractFormData()
|
||||||
: initialValues.detailState
|
: initialValues.detailState
|
||||||
|
|||||||
@@ -107,6 +107,7 @@
|
|||||||
<a @click="handleView(row)">查看</a>
|
<a @click="handleView(row)">查看</a>
|
||||||
<a v-if="canEdit(row)" @click="handleEdit(row)">编辑</a>
|
<a v-if="canEdit(row)" @click="handleEdit(row)">编辑</a>
|
||||||
<a v-if="canSubmit(row)" @click="handleSubmit(row)">提交法务</a>
|
<a v-if="canSubmit(row)" @click="handleSubmit(row)">提交法务</a>
|
||||||
|
<a v-if="canViewApprove(row)" @click="handleViewApprove(row)">审批记录</a>
|
||||||
<a v-if="canMockApprove(row)" @click="handleMockApprove(row)">测试通过</a>
|
<a v-if="canMockApprove(row)" @click="handleMockApprove(row)">测试通过</a>
|
||||||
<a v-if="canMockReject(row)" @click="handleMockReject(row)">测试驳回</a>
|
<a v-if="canMockReject(row)" @click="handleMockReject(row)">测试驳回</a>
|
||||||
<a v-if="canOnlineSign(row)" @click="handleOnlineSign(row)">
|
<a v-if="canOnlineSign(row)" @click="handleOnlineSign(row)">
|
||||||
@@ -162,6 +163,7 @@ import {
|
|||||||
mockCollectLaunch,
|
mockCollectLaunch,
|
||||||
mockRejectLaunch,
|
mockRejectLaunch,
|
||||||
finishLaunchSeal,
|
finishLaunchSeal,
|
||||||
|
queryApproveViewUrl,
|
||||||
queryLaunchList,
|
queryLaunchList,
|
||||||
submitLaunchRecord,
|
submitLaunchRecord,
|
||||||
updateLaunchDraft,
|
updateLaunchDraft,
|
||||||
@@ -307,6 +309,10 @@ function canArchive(row) {
|
|||||||
return row.status === "pending_archive";
|
return row.status === "pending_archive";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function canViewApprove(row) {
|
||||||
|
return ["reviewing", "pending_sign", "signing", "pending_collect", "pending_archive", "completed"].includes(row.status);
|
||||||
|
}
|
||||||
|
|
||||||
function selectChangeEvent({ records }) {
|
function selectChangeEvent({ records }) {
|
||||||
selectedRows.value = records;
|
selectedRows.value = records;
|
||||||
selectedRowKeys.value = records.map((item) => item.id);
|
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) {
|
function handleMockApprove(row) {
|
||||||
mockApproveLaunch(row.id, {}).then((res) => {
|
mockApproveLaunch(row.id, {}).then((res) => {
|
||||||
if (!isSuccessResponse(res)) {
|
if (!isSuccessResponse(res)) {
|
||||||
@@ -474,7 +503,10 @@ function handleModalSave(payload) {
|
|||||||
message.error(getResponseMessage(res, "保存合同失败"));
|
message.error(getResponseMessage(res, "保存合同失败"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
message.success(payload.mode === "edit" ? "合同修改成功" : "合同草稿保存成功");
|
message.success(
|
||||||
|
payload.successMessage ||
|
||||||
|
(payload.mode === "edit" ? "合同修改成功" : "合同草稿保存成功"),
|
||||||
|
);
|
||||||
getList();
|
getList();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -300,73 +300,33 @@ const templateSourceOptions = [
|
|||||||
|
|
||||||
// 可绑定业务字段列表
|
// 可绑定业务字段列表
|
||||||
const templateFieldList = [
|
const templateFieldList = [
|
||||||
{ key: 'contractName', label: '合同名称', category: '基本信息' },
|
{ value: 'contractName', label: '合同名称' },
|
||||||
{ key: 'contractCode', label: '合同编码', category: '基本信息' },
|
{ value: 'contractCode', label: '合同编码' },
|
||||||
{ key: 'contractType', label: '合同类型', category: '基本信息' },
|
{ value: 'contractType', label: '合同类型' },
|
||||||
{ key: 'contractAmountDisplay', label: '合同金额', category: '基本信息' },
|
{ value: 'contractAmountDisplay', label: '合同金额' },
|
||||||
{ key: 'effectiveStart', label: '有效期起', category: '基本信息' },
|
{ value: 'effectiveStart', label: '有效期起' },
|
||||||
{ key: 'effectiveEnd', label: '有效期止', category: '基本信息' },
|
{ value: 'effectiveEnd', label: '有效期止' },
|
||||||
{ key: 'effectivePeriod', label: '有效期区间', category: '基本信息' },
|
{ value: 'effectivePeriod', label: '有效期区间' },
|
||||||
{ key: 'paymentMethod', label: '付款方式', category: '基本信息' },
|
{ value: 'paymentMethod', label: '付款方式' },
|
||||||
{ key: 'primaryContent', label: '合同标的说明', category: '业务内容' },
|
{ value: 'primaryContent', label: '合同标的说明' },
|
||||||
{ key: 'amountExplain', label: '业务备注', category: '业务内容' },
|
{ value: 'amountExplain', label: '业务备注' },
|
||||||
{ key: 'partyAName', label: '甲方名称', category: '合同主体' },
|
{ value: 'partyAName', label: '甲方名称' },
|
||||||
{ key: 'partyBName', label: '乙方名称', category: '合同主体' },
|
{ value: 'partyBName', label: '乙方名称' },
|
||||||
{ key: 'partyCName', label: '丙方名称', category: '合同主体' },
|
{ value: 'partyCName', label: '丙方名称' },
|
||||||
{ key: 'counterpartySummary', label: '合同方摘要', category: '合同主体' },
|
{ value: 'counterpartySummary', label: '合同方摘要' },
|
||||||
{ key: 'attachmentSummary', label: '附件情况', category: '附件' },
|
{ value: 'contractSubject', label: '合同标的说明' },
|
||||||
{ key: 'templateLabel', label: '模板名称', category: '模板' },
|
{ value: 'businessRemark', label: '业务备注' },
|
||||||
{ key: 'signDate', label: '签署日期', category: '时间' },
|
{ value: 'attachmentSummary', label: '附件情况' },
|
||||||
|
{ value: 'templateLabel', label: '模板名称' },
|
||||||
|
{ value: 'signDate', label: '签署日期' },
|
||||||
]
|
]
|
||||||
|
|
||||||
// 业务字段下拉选项(由 templateFieldList 生成)
|
// 业务字段下拉选项(由 templateFieldList 生成)
|
||||||
const templateFieldOptions = templateFieldList.map((item) => ({
|
const templateFieldOptions = templateFieldList.map((item) => ({
|
||||||
label: `${item.label}(${item.key})`,
|
label: `${item.label}(${item.value})`,
|
||||||
value: item.key,
|
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 = []) {
|
function createSimpleVariableList(list = []) {
|
||||||
return list.map((item, index) => ({
|
return list.map((item, index) => ({
|
||||||
|
|||||||
Reference in New Issue
Block a user