1.1.1 合同变更申请上方承办部门、行业/大区去掉,添加业务方向。合同变更后的合同申请流程需要提示,合同激活关联项目排查测试问题。合同变更页面添加按钮能查看原合同信息。

dev
yinq 3 months ago
parent d7bc6c3d29
commit 54218f658f

@ -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
*/

@ -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;

@ -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;
/**
*
*/

@ -55,21 +55,21 @@
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="承办人" prop="undertakeBy">
<el-select v-model="form.undertakeBy" placeholder="请选择承办人" :disabled="isFormDisabled" filterable clearable style="width: 100%">
<el-option v-for="item in userInfoList" :key="item.userId" :label="item.nickName" :value="item.userId" />
<el-form-item label="业务方向" prop="businessDirection">
<el-select
v-model="form.businessDirection"
placeholder="请先选择合同,将自动带出原合同业务方向"
disabled
style="width: 100%"
>
<el-option v-for="dict in business_direction" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="行业/大区" prop="industryRegion">
<el-input v-model="form.industryRegion" placeholder="请输入行业/大区" :disabled="isFormDisabled" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="承办部门" prop="undertakeDeptId">
<el-select v-model="form.undertakeDeptId" placeholder="请选择承办部门" :disabled="isFormDisabled" filterable clearable style="width: 100%">
<el-option v-for="item in deptInfoList" :key="item.deptId" :label="item.deptName" :value="item.deptId" />
<el-form-item label="承办人" prop="undertakeBy">
<el-select v-model="form.undertakeBy" placeholder="请选择承办人" :disabled="isFormDisabled" filterable clearable style="width: 100%">
<el-option v-for="item in userInfoList" :key="item.userId" :label="item.nickName" :value="item.userId" />
</el-select>
</el-form-item>
</el-col>
@ -108,7 +108,17 @@
</el-col>
<el-col :span="12">
<el-form-item label="合同编号">
<el-input v-model="form.changeInfo.contractCode" placeholder="请输入合同编号" :disabled="isFormDisabled" />
<el-input v-model="form.changeInfo.contractCode" placeholder="请输入合同编号或点击生成" :disabled="isFormDisabled">
<template #append>
<el-button
type="primary"
@click="generateChangeContractCode"
:disabled="isFormDisabled || isChangeContractCodeGenerated"
>
生成合同编号
</el-button>
</template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="12">
@ -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<any[]>([]);
const userInfoList = ref<any[]>([]);
const paymentAccountList = ref<any[]>([]);
const printTemplateList = ref<any[]>([]);
const deptInfoList = ref<any[]>([]);
// /使
const changeMaterialList = computed(() => form.value.changeMaterialList || []);
const changePaymentMethodList = computed(() => form.value.changePaymentMethodList || []);
@ -656,6 +664,8 @@ const saleMaterialSelectRef = ref<InstanceType<typeof SaleMaterialSelect>>();
const selectedContractName = ref<string>('');
const contractSelectRef = ref<InstanceType<typeof ContractSelect>>();
/** 变更后合同编号是否已通过「生成」获取(与合同编辑页规则一致,生成一次后禁用按钮) */
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) {

@ -76,33 +76,47 @@
<span>{{ parseTime(scope.row.applyTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column label="承办部门" align="center" prop="undertakeDeptName" width="120" v-if="columns[14].visible" />
<el-table-column label="承办人" align="center" prop="undertakeByName" width="100" v-if="columns[15].visible" />
<el-table-column label="行业/大区" align="center" prop="industryRegion" width="100" v-if="columns[16].visible" />
<el-table-column label="印章法人单位" align="center" prop="sealLegalEntity" width="120" v-if="columns[17].visible" />
<el-table-column label="变更状态" align="center" prop="changeStatus" width="100" v-if="columns[18].visible">
<el-table-column label="业务方向" align="center" prop="businessDirection" width="120" v-if="columns[14].visible">
<template #default="scope">
<dict-tag :options="business_direction" :value="scope.row.businessDirection" />
</template>
</el-table-column>
<el-table-column label="承办部门" align="center" prop="undertakeDeptName" width="120" v-if="columns[15].visible" />
<el-table-column label="承办人" align="center" prop="undertakeByName" width="100" v-if="columns[16].visible" />
<el-table-column label="行业/大区" align="center" prop="industryRegion" width="100" v-if="columns[17].visible" />
<el-table-column label="印章法人单位" align="center" prop="sealLegalEntity" width="120" v-if="columns[18].visible" />
<el-table-column label="变更状态" align="center" prop="changeStatus" width="100" v-if="columns[19].visible">
<template #default="scope">
<dict-tag :options="contract_change_status" :value="scope.row.changeStatus" />
</template>
</el-table-column>
<el-table-column label="流程状态" align="center" prop="flowStatus" width="100" v-if="columns[19].visible" />
<el-table-column label="是否回写" align="center" prop="writeBackFlag" width="90" v-if="columns[20].visible">
<el-table-column label="流程状态" align="center" prop="flowStatus" width="100" v-if="columns[20].visible" />
<el-table-column label="是否回写" align="center" prop="writeBackFlag" width="90" v-if="columns[21].visible">
<template #default="scope">
<dict-tag :options="write_back_flag" :value="scope.row.writeBackFlag" />
</template>
</el-table-column>
<el-table-column label="回写时间" align="center" prop="writeBackTime" width="160" v-if="columns[21].visible">
<el-table-column label="回写时间" align="center" prop="writeBackTime" width="160" v-if="columns[22].visible">
<template #default="scope">
<span>{{ parseTime(scope.row.writeBackTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="remark" min-width="120" show-overflow-tooltip v-if="columns[22].visible" />
<el-table-column label="激活标识" align="center" prop="activeFlag" width="90" v-if="columns[23].visible" />
<el-table-column label="操作" align="center" fixed="right" width="160" class-name="small-padding fixed-width">
<el-table-column label="备注" align="center" prop="remark" min-width="120" show-overflow-tooltip v-if="columns[23].visible" />
<el-table-column label="激活标识" align="center" prop="activeFlag" width="90" v-if="columns[24].visible" />
<el-table-column label="操作" align="center" fixed="right" width="120" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="查看详情" placement="top" v-if="canViewDetail(scope.row)">
<el-button link type="info" icon="DocumentChecked" @click="handleView(scope.row)" v-hasPermi="['oa/erp:contractChange:query']"></el-button>
</el-tooltip>
<el-tooltip content="查看原合同" placement="top" v-if="canViewOriginalContract(scope.row)">
<el-button
link
type="success"
icon="Tickets"
@click="handleViewOriginalContract(scope.row)"
v-hasPermi="['oa/erp:contractInfo:edit']"
></el-button>
</el-tooltip>
<el-tooltip content="修改" placement="top" v-if="!canViewDetail(scope.row)">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['oa/erp:contractChange:edit']"></el-button>
</el-tooltip>
@ -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<any>(
proxy?.useDict('contract_change_status', 'contract_change_type', 'write_back_flag')
const { contract_change_status, contract_change_type, write_back_flag, business_direction } = toRefs<any>(
proxy?.useDict('contract_change_status', 'contract_change_type', 'write_back_flag', 'business_direction')
);
const contractChangeList = ref<ContractChangeVO[]>([]);
@ -163,16 +177,17 @@ const columns = ref<FieldOption[]>([
{ 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];

@ -1,5 +1,15 @@
<template>
<div class="p-2">
<!-- 内容变更生成的新合同与合同订单激活页提示一致便于经办人理解后续流程 -->
<el-alert
v-if="isFromContentChangeContract"
type="info"
:closable="false"
show-icon
style="margin-bottom: 12px"
>
本合同由合同内容变更生成在合同订单激活时将继承原合同关联项目并对原合同作停用处理请在本页完成合同维护与审批
</el-alert>
<el-card shadow="never" style="margin-top: 0">
<!-- <template #header>-->
<!-- <div style="text-align: left; font-weight: bold; font-size: 24px">合同{{ form.contractId ? ' - 修改' : ' - 新增' }}</div>-->
@ -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,

@ -164,6 +164,35 @@
<el-input v-model="form.contractName" placeholder="请输入合同名称" :disabled="true" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="客户合同编号" prop="customerContractCode">
<el-input
v-model="form.customerContractCode"
placeholder="请输入客户合同编号,若无填写‘-"
:disabled="isFormDisabled"
clearable
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="最终客户" prop="finalCustomerId">
<el-select
v-model="form.finalCustomerId"
placeholder="请选择最终客户"
:disabled="isFormDisabled"
filterable
clearable
style="width: 100%"
>
<el-option
v-for="item in customerInfoList"
:key="item.customerId"
:label="item.customerName"
:value="item.customerId"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="合同总价" prop="amount">
<el-input v-model="form.amount" placeholder="请输入合同总价" :disabled="true">
@ -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<any[]>([]);
const getCustomerInfoListSelect = async () => {
const res = await getCrmCustomerInfoList(null as any);
customerInfoList.value = res.data || [];
};
const paymentStageList = ref<PaymentStageVO[]>([]);
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;
}

Loading…
Cancel
Save