diff --git a/src/api/oa/erp/contractChange/types.ts b/src/api/oa/erp/contractChange/types.ts
index 0b68221..18ed451 100644
--- a/src/api/oa/erp/contractChange/types.ts
+++ b/src/api/oa/erp/contractChange/types.ts
@@ -99,6 +99,11 @@ export interface ContractChangeVO {
*/
industryRegion: string;
+ /**
+ * 业务方向(字典,取自原合同)
+ */
+ businessDirection?: string;
+
/**
* 印章法人单位(1公章 2法人章 3合同章 4销售合同章)
*/
@@ -257,6 +262,11 @@ export interface ContractChangeForm extends BaseEntity {
*/
industryRegion?: string;
+ /**
+ * 业务方向(字典,取自原合同)
+ */
+ businessDirection?: string;
+
/**
* 印章法人单位(1公章 2法人章 3合同章 4销售合同章)
*/
@@ -306,6 +316,7 @@ export interface ContractChangeSaveForm {
undertakeDeptId?: string | number;
undertakeBy?: number;
industryRegion?: string;
+ businessDirection?: string;
sealLegalEntity?: string;
changeStatus?: string;
flowStatus?: string;
@@ -420,6 +431,11 @@ export interface ContractChangeQuery extends PageQuery {
*/
industryRegion?: string;
+ /**
+ * 业务方向
+ */
+ businessDirection?: string;
+
/**
* 印章法人单位(1公章 2法人章 3合同章 4销售合同章)
*/
diff --git a/src/api/oa/erp/contractInfo/types.ts b/src/api/oa/erp/contractInfo/types.ts
index 774182e..1363aed 100644
--- a/src/api/oa/erp/contractInfo/types.ts
+++ b/src/api/oa/erp/contractInfo/types.ts
@@ -54,6 +54,16 @@ export interface ContractInfoVO {
*/
totalPrice: number;
+ /**
+ * 最终客户(关联客户ID)
+ */
+ finalCustomerId?: string | number;
+
+ /**
+ * 最终客户名称(列表/详情关联)
+ */
+ finalCustomerName?: string;
+
/**
* 甲方公司
*/
@@ -441,6 +451,12 @@ export interface ContractInfoForm extends BaseEntity {
* 合同付款方式列表
*/
contractPaymentMethodList?: any[];
+
+ /**
+ * 内容变更来源的原合同ID(由内容变更生成新合同时返回,用于前端提示)
+ */
+ originalContractId?: string | number;
+
flowCode?: any;
variables?: any;
bizExt?: any;
diff --git a/src/api/oa/erp/projectInfo/types.ts b/src/api/oa/erp/projectInfo/types.ts
index 2b5fbf1..c508e10 100644
--- a/src/api/oa/erp/projectInfo/types.ts
+++ b/src/api/oa/erp/projectInfo/types.ts
@@ -161,6 +161,16 @@ export interface ProjectInfoVO {
* 合同名称
*/
contractName?: string;
+
+ /**
+ * 客户合同编号(来自合同表)
+ */
+ customerContractCode?: string;
+
+ /**
+ * 最终客户ID(来自合同表)
+ */
+ finalCustomerId?: string | number;
}
export interface ProjectInfoForm extends BaseEntity {
@@ -337,6 +347,16 @@ export interface ProjectInfoForm extends BaseEntity {
*/
contractName?: string;
+ /**
+ * 客户合同编号(合同订单激活写入合同表)
+ */
+ customerContractCode?: string;
+
+ /**
+ * 最终客户ID(合同订单激活写入合同表)
+ */
+ finalCustomerId?: string | number;
+
/**
* 合同跟进人名称
*/
diff --git a/src/views/oa/erp/contractChange/edit.vue b/src/views/oa/erp/contractChange/edit.vue
index ff53909..47403a1 100644
--- a/src/views/oa/erp/contractChange/edit.vue
+++ b/src/views/oa/erp/contractChange/edit.vue
@@ -55,21 +55,21 @@
-
-
-
+
+
+
-
-
-
-
-
-
-
-
+
+
+
@@ -108,7 +108,17 @@
-
+
+
+
+ 生成合同编号
+
+
+
@@ -476,7 +486,6 @@ import type { PaymentStageVO } from '@/api/oa/base/paymentStage/types';
import { getCrmCustomerInfoList } from '@/api/oa/crm/customerInfo';
import { getCrmPaymentAccountList } from '@/api/oa/crm/paymentAccount';
import { getUserList } from '@/api/system/user';
-import { allListDept } from '@/api/system/dept';
import { getInfo } from '@/api/login';
import { getBasePrintTemplateList } from '@/api/oa/base/printTemplate';
import ApprovalButton from '@/components/Process/approvalButton.vue';
@@ -485,7 +494,8 @@ import SubmitVerify from '@/components/Process/submitVerify.vue';
import FileUpload from '@/components/FileUpload/index.vue';
import SaleMaterialSelect from '@/components/SaleMaterialSelect/index.vue';
import ContractSelect from '@/components/ContractSelect/index.vue';
-import { FlowCodeEnum } from '@/enums/OAEnum';
+import { FlowCodeEnum, CodeRuleEnum } from '@/enums/OAEnum';
+import { getRuleGenerateCode } from '@/api/system/codeRule';
const route = useRoute();
const router = useRouter();
@@ -528,8 +538,6 @@ const customerInfoList = ref([]);
const userInfoList = ref([]);
const paymentAccountList = ref([]);
const printTemplateList = ref([]);
-const deptInfoList = ref([]);
-
// 变更后物料/付款方式列表(仅内容变更时使用)
const changeMaterialList = computed(() => form.value.changeMaterialList || []);
const changePaymentMethodList = computed(() => form.value.changePaymentMethodList || []);
@@ -656,6 +664,8 @@ const saleMaterialSelectRef = ref>();
const selectedContractName = ref('');
const contractSelectRef = ref>();
+/** 变更后合同编号是否已通过「生成」获取(与合同编辑页规则一致,生成一次后禁用按钮) */
+const isChangeContractCodeGenerated = ref(false);
const isEdit = computed(() => !!(routeParams.value.id && (routeParams.value.type === 'update' || routeParams.value.type === 'view' || routeParams.value.type === 'approval')));
const isFormDisabled = computed(() => routeParams.value.type === 'view' || routeParams.value.type === 'approval');
@@ -693,9 +703,8 @@ const form = ref<
changeMaterialList: undefined,
changePaymentMethodList: undefined,
changeCode: undefined,
- undertakeDeptId: undefined,
undertakeBy: undefined,
- industryRegion: undefined,
+ businessDirection: undefined,
sealLegalEntity: undefined
});
@@ -744,6 +753,7 @@ async function onContractChange(contractId: string | number) {
form.value.contractName = c.contractName;
form.value.originalCustomerName = cAny.oneCustomerName || cAny.twoCustomerName;
form.value.originalContractAmount = c.totalPrice as any;
+ form.value.businessDirection = (c as any).businessDirection;
if (form.value.changeType === '1') {
form.value.changeContractCode = c.contractCode;
form.value.changeContractName = c.contractName;
@@ -760,6 +770,23 @@ async function onContractChange(contractId: string | number) {
changePaymentId: undefined,
contractChangeId: undefined
}));
+ // 重新带出的是原合同编号,允许再次点「生成」换新编号
+ isChangeContractCodeGenerated.value = false;
+ }
+}
+
+/** 生成变更后合同编号(编码规则与合同编辑页相同) */
+async function generateChangeContractCode() {
+ if (isChangeContractCodeGenerated.value || !form.value.changeInfo) return;
+ try {
+ const params = { codeRuleCode: CodeRuleEnum.CONTRACT } as any;
+ const res = await getRuleGenerateCode(params);
+ form.value.changeInfo.contractCode = res.msg;
+ isChangeContractCodeGenerated.value = true;
+ proxy?.$modal.msgSuccess('合同编号生成成功');
+ } catch (error) {
+ console.error('生成合同编号失败:', error);
+ proxy?.$modal.msgError('生成合同编号失败');
}
}
@@ -840,12 +867,19 @@ async function loadDetail() {
form.value.changeInfo = d.changeInfo ? { ...d.changeInfo } : undefined;
form.value.changeMaterialList = (d.changeMaterialList || []).map((x: any) => ({ ...x }));
form.value.changePaymentMethodList = (d.changePaymentMethodList || []).map((x: any) => ({ ...x }));
- form.value.undertakeDeptId = m.undertakeDeptId;
form.value.undertakeBy = m.undertakeBy;
- form.value.industryRegion = m.industryRegion;
+ form.value.businessDirection = m.businessDirection;
form.value.sealLegalEntity = m.sealLegalEntity;
form.value.newContractId = m.newContractId;
selectedContractName.value = m.contractName || '';
+ // 变更后编号已与原合同编号不同(含已点生成或手改),则不再允许重复点生成
+ if (form.value.changeType === '1' && form.value.changeInfo?.contractCode != null && form.value.contractCode != null) {
+ const newCode = String(form.value.changeInfo.contractCode).trim();
+ const oldCode = String(form.value.contractCode).trim();
+ isChangeContractCodeGenerated.value = newCode !== '' && newCode !== oldCode;
+ } else {
+ isChangeContractCodeGenerated.value = false;
+ }
}
function loadSelectOptions() {
@@ -855,7 +889,6 @@ function loadSelectOptions() {
getUserList({} as any).then((res) => (userInfoList.value = res.data || []));
getCrmPaymentAccountList({}).then((res) => (paymentAccountList.value = res.data || []));
getBasePrintTemplateList({ templateType: '1' }).then((res) => (printTemplateList.value = res.data || []));
- allListDept({} as any).then((res) => (deptInfoList.value = res.data || []));
}
// ----- 变更后物料:增删改 -----
@@ -1033,9 +1066,8 @@ function buildPayload(changeStatus: '1' | '2'): ContractChangeSaveForm {
changeContractName: f.changeContractName,
customerName: f.customerName,
changeContractAmount: f.changeContractAmount,
- undertakeDeptId: f.undertakeDeptId,
undertakeBy: f.undertakeBy,
- industryRegion: f.industryRegion,
+ businessDirection: f.businessDirection,
sealLegalEntity: f.sealLegalEntity
};
if (f.changeType === '1') {
@@ -1147,12 +1179,11 @@ onMounted(async () => {
} else {
form.value.changeType = '1';
form.value.flowStatus = 'draft';
- // 新增时默认承办部门、承办人为当前登录用户
+ // 新增时默认承办人为当前登录用户
try {
const infoRes = await getInfo();
if (infoRes?.data?.user) {
const user = infoRes.data.user as any;
- if (user.deptId != null) form.value.undertakeDeptId = user.deptId;
if (user.userId != null) form.value.undertakeBy = user.userId;
}
} catch (e) {
diff --git a/src/views/oa/erp/contractChange/index.vue b/src/views/oa/erp/contractChange/index.vue
index 5323994..49ca2b5 100644
--- a/src/views/oa/erp/contractChange/index.vue
+++ b/src/views/oa/erp/contractChange/index.vue
@@ -76,33 +76,47 @@
{{ parseTime(scope.row.applyTime, '{y}-{m}-{d} {h}:{i}:{s}') }}
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
+
{{ parseTime(scope.row.writeBackTime, '{y}-{m}-{d} {h}:{i}:{s}') }}
-
-
-
+
+
+
+
+
+
@@ -122,8 +136,8 @@ import { ContractChangeVO, ContractChangeQuery } from '@/api/oa/erp/contractChan
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const route = useRoute();
const router = useRouter();
-const { contract_change_status, contract_change_type, write_back_flag } = toRefs(
- proxy?.useDict('contract_change_status', 'contract_change_type', 'write_back_flag')
+const { contract_change_status, contract_change_type, write_back_flag, business_direction } = toRefs(
+ proxy?.useDict('contract_change_status', 'contract_change_type', 'write_back_flag', 'business_direction')
);
const contractChangeList = ref([]);
@@ -163,16 +177,17 @@ const columns = ref([
{ key: 11, label: '变更后合同金额', visible: false },
{ key: 12, label: '变更原因', visible: false },
{ key: 13, label: '申请时间', visible: true },
- { key: 14, label: '承办部门', visible: true },
- { key: 15, label: '承办人', visible: true },
- { key: 16, label: '行业/大区', visible: false },
- { key: 17, label: '印章法人单位', visible: false },
- { key: 18, label: '变更状态', visible: true },
- { key: 19, label: '流程状态', visible: false },
- { key: 20, label: '是否回写', visible: true },
- { key: 21, label: '回写时间', visible: false },
- { key: 22, label: '备注', visible: false },
- { key: 23, label: '激活标识', visible: false }
+ { key: 14, label: '业务方向', visible: true },
+ { key: 15, label: '承办部门', visible: false },
+ { key: 16, label: '承办人', visible: true },
+ { key: 17, label: '行业/大区', visible: false },
+ { key: 18, label: '印章法人单位', visible: false },
+ { key: 19, label: '变更状态', visible: true },
+ { key: 20, label: '流程状态', visible: false },
+ { key: 21, label: '是否回写', visible: false },
+ { key: 22, label: '回写时间', visible: false },
+ { key: 23, label: '备注', visible: false },
+ { key: 24, label: '激活标识', visible: false }
]);
/** 查询合同变更列表 */
@@ -218,6 +233,30 @@ const canViewDetail = (row: ContractChangeVO) => {
return status != null && Number(status) !== 1;
};
+/** 变更状态为 3 时可查看原合同(合同编辑页-只读) */
+const canViewOriginalContract = (row: ContractChangeVO) => {
+ const status = row?.changeStatus;
+ const hasContractId = row?.contractId != null && String(row.contractId).trim() !== '';
+ return hasContractId && status != null && Number(status) === 3;
+};
+
+/** 查看原合同:跳转合同信息编辑页(查看模式) */
+const handleViewOriginalContract = (row: ContractChangeVO) => {
+ const id = row?.contractId;
+ if (id == null || String(id).trim() === '') {
+ proxy?.$modal.msgWarning('未找到原合同ID');
+ return;
+ }
+ proxy?.$tab.closePage(route);
+ router.push({
+ path: '/contract/contractInfo/edit',
+ query: {
+ id: String(id),
+ type: 'view'
+ }
+ });
+};
+
/** 查看详情:使用菜单路由打开 */
const handleView = (row?: ContractChangeVO) => {
const _contractChangeId = row?.contractChangeId || ids.value[0];
diff --git a/src/views/oa/erp/contractInfo/edit.vue b/src/views/oa/erp/contractInfo/edit.vue
index 7d1894b..560e5e0 100644
--- a/src/views/oa/erp/contractInfo/edit.vue
+++ b/src/views/oa/erp/contractInfo/edit.vue
@@ -1,5 +1,15 @@
+
+
+ 本合同由合同内容变更生成;在合同订单激活时将继承原合同关联项目,并对原合同作停用处理。请在本页完成合同维护与审批。
+
@@ -995,7 +1005,8 @@ const initFormData: ContractInfoFormEx = {
warrantyPeriodDescription: undefined,
deliveryLocation: undefined,
shipMethod: undefined,
- deliveryStart: undefined
+ deliveryStart: undefined,
+ originalContractId: undefined
} as any;
const data = reactive<{ form: ContractInfoFormEx; rules: any }>({
@@ -1028,6 +1039,12 @@ const ossIdString = computed({
const { form, rules } = toRefs(data);
+/** 是否为由「合同内容变更」生成的新合同(后端 originalContractId 非空) */
+const isFromContentChangeContract = computed(() => {
+ const id = (form.value as any).originalContractId;
+ return id != null && id !== '';
+});
+
// 监听contractFlag变化
watch(
() => form.value.contractFlag,
diff --git a/src/views/oa/erp/contractInfo/orderActivate.vue b/src/views/oa/erp/contractInfo/orderActivate.vue
index 2fe742c..f0b206f 100644
--- a/src/views/oa/erp/contractInfo/orderActivate.vue
+++ b/src/views/oa/erp/contractInfo/orderActivate.vue
@@ -164,6 +164,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
@@ -423,6 +452,7 @@ import type { FormInstance } from 'element-plus';
import { getErpProjectPlanStageList, listErpProjectPlanStage } from '@/api/oa/erp/erpProjectPlanStage';
import { ErpProjectPlanStageForm, ErpProjectPlanStageQuery } from '@/api/oa/erp/erpProjectPlanStage/types';
import { listContractInfo, getContractInfo } from '@/api/oa/erp/contractInfo';
+import { getCrmCustomerInfoList } from '@/api/oa/crm/customerInfo';
import { getBasePaymentStageList } from '@/api/oa/base/paymentStage';
import { PaymentStageVO } from '@/api/oa/base/paymentStage/types';
import { getInfo } from '@/api/login';
@@ -729,6 +759,13 @@ const getUserList = async () => {
userList.value = res.rows;
};
+/** 客户下拉(最终客户) */
+const customerInfoList = ref([]);
+const getCustomerInfoListSelect = async () => {
+ const res = await getCrmCustomerInfoList(null as any);
+ customerInfoList.value = res.data || [];
+};
+
const paymentStageList = ref([]);
const getPaymentStageList = async () => {
try {
@@ -760,6 +797,8 @@ type ProjectInfoFormEx = ProjectInfoForm & {
bizExt?: any;
contractCode?: string;
contractName?: string;
+ customerContractCode?: string;
+ finalCustomerId?: string | number;
planStageList?: ErpProjectPlanStageForm[];
projectContractsList?: any[];
};
@@ -786,6 +825,8 @@ const initFormData: ProjectInfoFormEx = {
contractId: undefined,
contractCode: undefined,
contractName: undefined,
+ customerContractCode: undefined,
+ finalCustomerId: undefined,
remark: undefined,
ossId: undefined,
activeFlag: '1',
@@ -802,6 +843,7 @@ const data = reactive<{ form: ProjectInfoFormEx; rules: any }>({
businessDirection: [{ required: true, message: '业务方向不能为空', trigger: 'change' }],
projectCategory: [{ required: true, message: '订单类别不能为空', trigger: 'change' }],
projectTypeId: [{ required: true, message: '订单类型不能为空', trigger: 'change' }],
+ customerContractCode: [{ required: true, message: '客户合同编号不能为空', trigger: 'blur' }],
deptId: [{ required: true, message: '部门不能为空', trigger: 'change' }],
peopleId: [{ required: true, message: '抄送人员不能为空', trigger: 'change' }],
ossId: [{ required: true, message: '请上传终版合同', trigger: 'change' }]
@@ -868,6 +910,10 @@ function normalizePeopleId(value: string | string[] | number | undefined): strin
const submitForm = (status: string, mode: boolean) => {
projectInfoFormRef.value?.validate(async (valid: boolean) => {
if (!valid) return;
+ if (!projectList.value?.length) {
+ proxy?.$modal.msgWarning('合同订单需新建项目或者关联已有项目!');
+ return;
+ }
const isDraft = status === SUBMIT_STATUS_DRAFT;
buttonLoading.value = true;
try {
@@ -918,7 +964,7 @@ const submitForm = (status: string, mode: boolean) => {
// 数据加载:合同信息、关联项目、项目阶段计划
// ---------------------------------------------------------------------------
const loadSelectOptions = async () => {
- await Promise.all([getUserList(), getDeptInfoListSelect(), getPaymentStageList()]);
+ await Promise.all([getUserList(), getDeptInfoListSelect(), getPaymentStageList(), getCustomerInfoListSelect()]);
};
/** 根据路由 contractId 加载合同信息并回填表单;有项目则加载主项目+阶段+关联项目,无则从合同带出 */
@@ -951,6 +997,8 @@ const loadContractInfo = async () => {
form.value.deptId = contractData.contractDeptId;
form.value.contractCode = contractData.contractCode;
form.value.contractName = contractData.contractName;
+ form.value.customerContractCode = contractData.customerContractCode;
+ form.value.finalCustomerId = contractData.finalCustomerId;
form.value.amount = contractData.totalPrice;
// 根据部门ID查找部门信息,获取部门负责人和分管副总
@@ -1000,7 +1048,11 @@ const loadContractInfo = async () => {
projectListLoading.value = true;
try {
const projRes = await getErpProjectContractsList({ contractId: originalId } as any);
- projectList.value = (projRes.data || []).map((item: any) => ({ ...(item as any) }));
+ // 原合同下的关联均为已有项目,项目来源固定为「0 选择已有项目」
+ projectList.value = (projRes.data || []).map((item: any) => ({
+ ...item,
+ projectSource: PROJECT_SOURCE_EXIST
+ }));
} finally {
projectListLoading.value = false;
}