|
|
@@ -19,7 +19,12 @@
|
|
|
</el-col>
|
|
|
<el-col :span="6">
|
|
|
<el-form-item label="合同名称" prop="contractName">
|
|
|
- <el-input v-model="form.contractName" placeholder="可不填,系统将自动生成" maxlength="100" show-word-limit />
|
|
|
+ <el-input
|
|
|
+ v-model="form.contractName"
|
|
|
+ placeholder="可不填,自动生成:供方简称-需方简称(合同编号)"
|
|
|
+ maxlength="100"
|
|
|
+ show-word-limit
|
|
|
+ />
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :span="6">
|
|
|
@@ -600,6 +605,7 @@ import ContactSelector from '@/components/ContactSelector.vue';
|
|
|
import QuoteSelector from '@/components/QuoteSelector.vue';
|
|
|
import UserSelector from '@/components/UserSelector.vue';
|
|
|
import ApprovalDialog from './components/ApprovalDialog.vue';
|
|
|
+import { omitEmptyExceptPartyFields } from './partyFields';
|
|
|
import { getContactByCustomerId } from '@/api/crm/customer';
|
|
|
import type { CustomerVO, ContactVO } from '@/api/crm/customer/types';
|
|
|
import { getDefaultInvoiceInfoByCustomerId } from '@/api/crm/invoiceInfo';
|
|
|
@@ -1324,11 +1330,29 @@ const updateContractNoBySupplier = (newSupplierCode: string | undefined) => {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+const CONTRACT_NO_STD_RE = /^([A-Z]+)\/X-JT(\d{8})(\d{3})$/;
|
|
|
+
|
|
|
+/** 签订日变更时:标准编号只换日期段,保留序号;并同步名称括号内编号 */
|
|
|
+const syncContractNoDateFromSignDate = (signDateVal: string | null) => {
|
|
|
+ if (!signDateVal || !form.value.contractNo) return;
|
|
|
+ const m = String(form.value.contractNo).match(CONTRACT_NO_STD_RE);
|
|
|
+ if (!m) return;
|
|
|
+
|
|
|
+ const dateStr = signDateVal.replace(/-/g, '');
|
|
|
+ if (dateStr.length !== 8) return;
|
|
|
+ if (m[2] === dateStr) return;
|
|
|
+
|
|
|
+ const nextNo = `${m[1]}/X-JT${dateStr}${m[3]}`;
|
|
|
+ form.value.contractNo = nextNo;
|
|
|
+
|
|
|
+ if (form.value.contractName) {
|
|
|
+ form.value.contractName = form.value.contractName.replace(/[((][^))]*[))]$/, `(${nextNo})`);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
/**
|
|
|
* 更新合同名称中的供方简称
|
|
|
- * 合同名称格式:
|
|
|
- * - 销售合同、框架合同:供方简称-客户简称(合同编号)
|
|
|
- * - 采购合同:客户简称-供方简称(合同编号)
|
|
|
+ * 统一格式:{供方简称}-{需方简称}({合同编号})
|
|
|
*/
|
|
|
const updateContractNameBySupplier = (newSupplierShortName: string | undefined) => {
|
|
|
if (!form.value.contractName || !newSupplierShortName) {
|
|
|
@@ -1336,7 +1360,6 @@ const updateContractNameBySupplier = (newSupplierShortName: string | undefined)
|
|
|
}
|
|
|
|
|
|
const contractName = form.value.contractName;
|
|
|
- const contractType = form.value.contractType;
|
|
|
|
|
|
// 提取合同编号(在括号中,支持中文和英文括号)
|
|
|
const contractNoMatch = contractName.match(/[((](.+?)[))]$/);
|
|
|
@@ -1348,78 +1371,21 @@ const updateContractNameBySupplier = (newSupplierShortName: string | undefined)
|
|
|
// 常见的供方简称列表(用于识别和替换)
|
|
|
const commonSupplierNames = ['山峡', 'SX'];
|
|
|
|
|
|
- // 根据合同类型解析和替换
|
|
|
- if (contractType === 'sale' || contractType === 'framework') {
|
|
|
- // 销售/框架合同格式:供方简称-客户简称(合同编号)
|
|
|
- // 例如:山峡-客户A(SX/X-JT20250926002)
|
|
|
- const parts = nameBody.split('-').map((p) => p.trim());
|
|
|
- if (parts.length >= 2) {
|
|
|
- // 替换第一个部分(供方简称)
|
|
|
- parts[0] = newSupplierShortName;
|
|
|
- const newNameBody = parts.join('-');
|
|
|
- form.value.contractName = contractNo ? `${newNameBody}(${contractNo})` : newNameBody;
|
|
|
- } else if (parts.length === 1) {
|
|
|
- // 如果只有一个部分,判断是否是供方简称
|
|
|
- const isSupplierName = commonSupplierNames.some((name) => parts[0].includes(name));
|
|
|
- if (isSupplierName) {
|
|
|
- // 是供方简称,替换它
|
|
|
- form.value.contractName = contractNo ? `${newSupplierShortName}(${contractNo})` : newSupplierShortName;
|
|
|
- } else {
|
|
|
- // 可能是客户简称,保持原样,在前面添加供方简称
|
|
|
- form.value.contractName = contractNo ? `${newSupplierShortName}-${parts[0]}(${contractNo})` : `${newSupplierShortName}-${parts[0]}`;
|
|
|
- }
|
|
|
- }
|
|
|
- } else if (contractType === 'purchase') {
|
|
|
- // 采购合同格式:客户简称-供方简称(合同编号)
|
|
|
- // 例如:客户A-山峡(SX/X-JT20250926002)
|
|
|
- const parts = nameBody.split('-').map((p) => p.trim());
|
|
|
- if (parts.length >= 2) {
|
|
|
- // 替换最后一个部分(供方简称)
|
|
|
- parts[parts.length - 1] = newSupplierShortName;
|
|
|
- const newNameBody = parts.join('-');
|
|
|
- form.value.contractName = contractNo ? `${newNameBody}(${contractNo})` : newNameBody;
|
|
|
- } else if (parts.length === 1) {
|
|
|
- // 如果只有一个部分,判断是否是供方简称
|
|
|
- const isSupplierName = commonSupplierNames.some((name) => parts[0].includes(name));
|
|
|
- if (isSupplierName) {
|
|
|
- // 是供方简称,替换它
|
|
|
- form.value.contractName = contractNo ? `${newSupplierShortName}(${contractNo})` : newSupplierShortName;
|
|
|
- } else {
|
|
|
- // 可能是客户简称,保持原样,在后面添加供方简称
|
|
|
- form.value.contractName = contractNo ? `${parts[0]}-${newSupplierShortName}(${contractNo})` : `${parts[0]}-${newSupplierShortName}`;
|
|
|
- }
|
|
|
- }
|
|
|
- } else {
|
|
|
- // 其他类型,尝试替换所有可能的供方简称
|
|
|
- let updatedName = contractName;
|
|
|
- let replaced = false;
|
|
|
-
|
|
|
- for (const oldName of commonSupplierNames) {
|
|
|
- if (updatedName.includes(oldName)) {
|
|
|
- // 使用正则替换,保持格式
|
|
|
- updatedName = updatedName.replace(new RegExp(oldName, 'g'), newSupplierShortName);
|
|
|
- replaced = true;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 如果没有找到已知的供方简称,尝试在名称开头或结尾查找
|
|
|
- if (!replaced && nameBody) {
|
|
|
- // 检查是否以常见供方简称开头或结尾
|
|
|
- for (const oldName of commonSupplierNames) {
|
|
|
- if (nameBody.startsWith(oldName)) {
|
|
|
- updatedName = contractName.replace(oldName, newSupplierShortName);
|
|
|
- replaced = true;
|
|
|
- break;
|
|
|
- } else if (nameBody.endsWith(oldName)) {
|
|
|
- updatedName = contractName.replace(oldName, newSupplierShortName);
|
|
|
- replaced = true;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
+ // 统一格式:供方简称-需方简称(合同编号)
|
|
|
+ const parts = nameBody.split('-').map((p) => p.trim());
|
|
|
+ if (parts.length >= 2) {
|
|
|
+ parts[0] = newSupplierShortName;
|
|
|
+ const newNameBody = parts.join('-');
|
|
|
+ form.value.contractName = contractNo ? `${newNameBody}(${contractNo})` : newNameBody;
|
|
|
+ } else if (parts.length === 1) {
|
|
|
+ const isSupplierName = commonSupplierNames.some((name) => parts[0].includes(name));
|
|
|
+ if (isSupplierName) {
|
|
|
+ form.value.contractName = contractNo ? `${newSupplierShortName}(${contractNo})` : newSupplierShortName;
|
|
|
+ } else {
|
|
|
+ form.value.contractName = contractNo
|
|
|
+ ? `${newSupplierShortName}-${parts[0]}(${contractNo})`
|
|
|
+ : `${newSupplierShortName}-${parts[0]}`;
|
|
|
}
|
|
|
-
|
|
|
- form.value.contractName = updatedName;
|
|
|
}
|
|
|
|
|
|
// 如果合同编号已更新,同步更新合同名称中的合同编号
|
|
|
@@ -1430,10 +1396,8 @@ const updateContractNameBySupplier = (newSupplierShortName: string | undefined)
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
- * 更新合同名称中的客户简称
|
|
|
- * 合同名称格式:
|
|
|
- * - 销售合同、框架合同:供方简称-客户简称(合同编号)
|
|
|
- * - 采购合同:客户简称-供方简称(合同编号)
|
|
|
+ * 更新合同名称中的客户简称(需方)
|
|
|
+ * 统一格式:{供方简称}-{需方简称}({合同编号})
|
|
|
*/
|
|
|
const updateContractNameByCustomer = (newCustomerShortName: string | undefined) => {
|
|
|
if (!form.value.contractName || !newCustomerShortName) {
|
|
|
@@ -1441,7 +1405,6 @@ const updateContractNameByCustomer = (newCustomerShortName: string | undefined)
|
|
|
}
|
|
|
|
|
|
const contractName = form.value.contractName;
|
|
|
- const contractType = form.value.contractType;
|
|
|
|
|
|
// 提取合同编号(在括号中,支持中文和英文括号)
|
|
|
const contractNoMatch = contractName.match(/[((](.+?)[))]$/);
|
|
|
@@ -1453,92 +1416,21 @@ const updateContractNameByCustomer = (newCustomerShortName: string | undefined)
|
|
|
// 常见的供方简称列表(用于识别,避免误替换)
|
|
|
const commonSupplierNames = ['山峡', 'SX'];
|
|
|
|
|
|
- // 根据合同类型解析和替换
|
|
|
- if (contractType === 'sale' || contractType === 'framework') {
|
|
|
- // 销售/框架合同格式:供方简称-客户简称(合同编号)
|
|
|
- // 例如:山峡-客户A(SX/X-JT20250926002)
|
|
|
- const parts = nameBody.split('-').map((p) => p.trim());
|
|
|
- if (parts.length >= 2) {
|
|
|
- // 替换第二个部分(客户简称)
|
|
|
- parts[1] = newCustomerShortName;
|
|
|
- const newNameBody = parts.join('-');
|
|
|
- form.value.contractName = contractNo ? `${newNameBody}(${contractNo})` : newNameBody;
|
|
|
- } else if (parts.length === 1) {
|
|
|
- // 如果只有一个部分,判断是否是供方简称
|
|
|
- const isSupplierName = commonSupplierNames.some((name) => parts[0].includes(name));
|
|
|
- if (isSupplierName) {
|
|
|
- // 是供方简称,在后面添加客户简称
|
|
|
- form.value.contractName = contractNo ? `${parts[0]}-${newCustomerShortName}(${contractNo})` : `${parts[0]}-${newCustomerShortName}`;
|
|
|
- } else {
|
|
|
- // 可能是客户简称,替换它,但需要添加供方简称
|
|
|
- // 这里保持原样,只替换客户简称部分(如果当前没有供方简称,则只更新客户简称)
|
|
|
- form.value.contractName = contractNo ? `${newCustomerShortName}(${contractNo})` : newCustomerShortName;
|
|
|
- }
|
|
|
- }
|
|
|
- } else if (contractType === 'purchase') {
|
|
|
- // 采购合同格式:客户简称-供方简称(合同编号)
|
|
|
- // 例如:客户A-山峡(SX/X-JT20250926002)
|
|
|
- const parts = nameBody.split('-').map((p) => p.trim());
|
|
|
- if (parts.length >= 2) {
|
|
|
- // 替换第一个部分(客户简称)
|
|
|
- parts[0] = newCustomerShortName;
|
|
|
- const newNameBody = parts.join('-');
|
|
|
- form.value.contractName = contractNo ? `${newNameBody}(${contractNo})` : newNameBody;
|
|
|
- } else if (parts.length === 1) {
|
|
|
- // 如果只有一个部分,判断是否是供方简称
|
|
|
- const isSupplierName = commonSupplierNames.some((name) => parts[0].includes(name));
|
|
|
- if (isSupplierName) {
|
|
|
- // 是供方简称,在前面添加客户简称
|
|
|
- form.value.contractName = contractNo ? `${newCustomerShortName}-${parts[0]}(${contractNo})` : `${newCustomerShortName}-${parts[0]}`;
|
|
|
- } else {
|
|
|
- // 可能是客户简称,替换它
|
|
|
- form.value.contractName = contractNo ? `${newCustomerShortName}(${contractNo})` : newCustomerShortName;
|
|
|
- }
|
|
|
- }
|
|
|
- } else {
|
|
|
- // 其他类型,尝试智能替换客户简称
|
|
|
- // 策略:如果名称中包含已知的供方简称,则替换另一个部分;否则尝试替换非供方简称的部分
|
|
|
- let updatedName = contractName;
|
|
|
- let replaced = false;
|
|
|
-
|
|
|
- // 先检查是否包含供方简称
|
|
|
- let supplierNameInBody = '';
|
|
|
- for (const supplierName of commonSupplierNames) {
|
|
|
- if (nameBody.includes(supplierName)) {
|
|
|
- supplierNameInBody = supplierName;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (supplierNameInBody) {
|
|
|
- // 如果包含供方简称,替换另一个部分
|
|
|
- const parts = nameBody.split('-').map((p) => p.trim());
|
|
|
- if (parts.length >= 2) {
|
|
|
- // 找到供方简称的位置并替换另一个部分
|
|
|
- const supplierIndex = parts.findIndex((p) => p.includes(supplierNameInBody));
|
|
|
- const customerIndex = supplierIndex === 0 ? 1 : 0;
|
|
|
- parts[customerIndex] = newCustomerShortName;
|
|
|
- const newNameBody = parts.join('-');
|
|
|
- updatedName = contractNo ? `${newNameBody}(${contractNo})` : newNameBody;
|
|
|
- replaced = true;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 如果没有替换成功,尝试直接替换非供方简称的部分
|
|
|
- if (!replaced && nameBody) {
|
|
|
- const parts = nameBody.split('-').map((p) => p.trim());
|
|
|
- if (parts.length >= 2) {
|
|
|
- // 替换第一个部分(假设是客户简称)
|
|
|
- parts[0] = newCustomerShortName;
|
|
|
- const newNameBody = parts.join('-');
|
|
|
- updatedName = contractNo ? `${newNameBody}(${contractNo})` : newNameBody;
|
|
|
- } else if (parts.length === 1 && !commonSupplierNames.some((name) => parts[0].includes(name))) {
|
|
|
- // 如果只有一个部分且不是供方简称,则替换它
|
|
|
- updatedName = contractNo ? `${newCustomerShortName}(${contractNo})` : newCustomerShortName;
|
|
|
- }
|
|
|
+ // 统一格式:供方简称-需方简称(合同编号)
|
|
|
+ const parts = nameBody.split('-').map((p) => p.trim());
|
|
|
+ if (parts.length >= 2) {
|
|
|
+ parts[1] = newCustomerShortName;
|
|
|
+ const newNameBody = parts.join('-');
|
|
|
+ form.value.contractName = contractNo ? `${newNameBody}(${contractNo})` : newNameBody;
|
|
|
+ } else if (parts.length === 1) {
|
|
|
+ const isSupplierName = commonSupplierNames.some((name) => parts[0].includes(name));
|
|
|
+ if (isSupplierName) {
|
|
|
+ form.value.contractName = contractNo
|
|
|
+ ? `${parts[0]}-${newCustomerShortName}(${contractNo})`
|
|
|
+ : `${parts[0]}-${newCustomerShortName}`;
|
|
|
+ } else {
|
|
|
+ form.value.contractName = contractNo ? `${newCustomerShortName}(${contractNo})` : newCustomerShortName;
|
|
|
}
|
|
|
-
|
|
|
- form.value.contractName = updatedName;
|
|
|
}
|
|
|
|
|
|
// 如果合同编号已更新,同步更新合同名称中的合同编号
|
|
|
@@ -1679,6 +1571,7 @@ const handleSignDateChange = (val: string | null) => {
|
|
|
form.value.startDate = '';
|
|
|
form.value.endDate = '';
|
|
|
}
|
|
|
+ syncContractNoDateFromSignDate(val);
|
|
|
// 触发合同期限字段的验证
|
|
|
nextTick(() => {
|
|
|
formRef.value?.validateField('startDate');
|
|
|
@@ -2184,12 +2077,8 @@ const handleSaveDraft = async () => {
|
|
|
data.attachmentFiles = JSON.stringify(attachmentsWithCategory);
|
|
|
}
|
|
|
|
|
|
- // 清理未填写的字段,避免发送 undefined 到后端
|
|
|
- Object.keys(data).forEach((key) => {
|
|
|
- if (data[key] === undefined || data[key] === '') {
|
|
|
- delete data[key];
|
|
|
- }
|
|
|
- });
|
|
|
+ // 清理未填写的字段;供/需方空字符串保留,以便清空落库
|
|
|
+ omitEmptyExceptPartyFields(data);
|
|
|
|
|
|
// 联系人字段映射(前端contactsName/contactsPhone映射回后端contactName/contactMobile)
|
|
|
if (data.contactsName) {
|
|
|
@@ -2366,12 +2255,8 @@ const handleSubmit = async () => {
|
|
|
data.attachmentFiles = JSON.stringify(attachmentsWithCategory);
|
|
|
}
|
|
|
|
|
|
- // 清理未填写的字段,避免发送 undefined 到后端
|
|
|
- Object.keys(data).forEach((key) => {
|
|
|
- if (data[key] === undefined || data[key] === '') {
|
|
|
- delete data[key];
|
|
|
- }
|
|
|
- });
|
|
|
+ // 清理未填写的字段;供/需方空字符串保留,以便清空落库
|
|
|
+ omitEmptyExceptPartyFields(data);
|
|
|
|
|
|
// 联系人字段映射(前端contactsName/contactsPhone映射回后端contactName/contactMobile)
|
|
|
if (data.contactsName) {
|