From 9d2446b6ff3a1b25afe76b5ba4ad1f245861faf5 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 6 Jun 2026 22:26:20 +0800 Subject: [PATCH] =?UTF-8?q?fix(crm):=20=E4=BF=AE=E5=A4=8D=E5=9B=9E?= =?UTF-8?q?=E6=AC=BE=E6=96=B0=E5=A2=9E=E5=BC=B9=E7=AA=97=E9=A2=84=E5=A1=AB?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E8=A2=AB=E6=B8=85=E7=A9=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将回款表单下游字段清空逻辑从 schema 依赖刷新移到用户选择事件 - 修复从回款计划新增回款时合同、期数、金额等预填数据被清空的问题 - 兼容回款管理、回款计划、客户/合同详情等新增回款入口 - 同步处理 web-antd、web-ele、web-antdv-next --- .../web-antd/src/views/crm/receivable/data.ts | 99 ++++++++++++------- .../src/views/crm/receivable/modules/form.vue | 70 +++++++++---- .../src/views/crm/receivable/data.ts | 99 ++++++++++++------- .../src/views/crm/receivable/modules/form.vue | 70 +++++++++---- apps/web-ele/src/views/crm/receivable/data.ts | 99 ++++++++++++------- .../src/views/crm/receivable/modules/form.vue | 70 +++++++++---- 6 files changed, 348 insertions(+), 159 deletions(-) diff --git a/apps/web-antd/src/views/crm/receivable/data.ts b/apps/web-antd/src/views/crm/receivable/data.ts index eacd97c34..b85771db3 100644 --- a/apps/web-antd/src/views/crm/receivable/data.ts +++ b/apps/web-antd/src/views/crm/receivable/data.ts @@ -57,12 +57,19 @@ export function useFormSchema(): VbenFormSchema[] { label: '客户名称', component: 'ApiSelect', rules: 'required', - componentProps: { + componentProps: (_values, form) => ({ api: getCustomerSimpleList, labelField: 'name', valueField: 'id', placeholder: '请选择客户', - }, + onChange: () => { + form.setFieldValue('contractId', undefined); + form.setFieldValue('planId', undefined); + form.setFieldValue('price', undefined); + form.setFieldValue('returnTime', undefined); + form.setFieldValue('returnType', undefined); + }, + }), dependencies: { triggerFields: ['id'], disabled: (values) => values.id, @@ -76,21 +83,33 @@ export function useFormSchema(): VbenFormSchema[] { dependencies: { triggerFields: ['customerId'], disabled: (values) => !values.customerId || values.id, - async componentProps(values) { - if (values.customerId) { - if (!values.id) { - // 特殊:只有在【新增】时,才清空合同编号 - values.contractId = undefined; - } - const contracts = await getContractSimpleList(values.customerId); + async componentProps(values, form) { + if (!values.customerId) { return { - options: contracts.map((item) => ({ - label: item.name, - value: item.id, - })), - placeholder: '请选择合同', - } as any; + options: [], + placeholder: '请选择客户', + }; } + const contracts = await getContractSimpleList(values.customerId); + return { + options: contracts.map((item) => ({ + label: item.name, + value: item.id, + })), + placeholder: '请选择合同', + onChange: (value: number) => { + form.setFieldValue('planId', undefined); + form.setFieldValue('returnTime', undefined); + form.setFieldValue('returnType', undefined); + const contract = contracts.find((item) => item.id === value); + form.setFieldValue( + 'price', + contract + ? contract.totalPrice - contract.totalReceivablePrice + : undefined, + ); + }, + } as any; }, }, }, @@ -101,28 +120,38 @@ export function useFormSchema(): VbenFormSchema[] { rules: 'required', dependencies: { triggerFields: ['contractId'], - disabled: (values) => !values.contractId, - async componentProps(values) { - if (values.contractId) { - values.planId = undefined; - const plans = await getReceivablePlanSimpleList( - values.customerId, - values.contractId, - ); + disabled: (values) => !values.contractId || values.id, + async componentProps(values, form) { + if (!values.contractId) { return { - options: plans.map((item) => ({ - label: item.period, - value: item.id, - })), - placeholder: '请选择回款期数', - onChange: async (value: any) => { - const plan = await getReceivablePlan(value); - values.returnTime = plan?.returnTime; - values.price = plan?.price; - values.returnType = plan?.returnType; - }, - } as any; + options: [], + placeholder: '请选择合同', + }; } + const plans = await getReceivablePlanSimpleList( + values.customerId, + values.contractId, + ); + return { + options: plans.map((item) => ({ + disabled: !!item.receivableId, + label: `第 ${item.period} 期`, + value: item.id, + })), + placeholder: '请选择回款期数', + onChange: async (value: any) => { + if (!value) { + form.setFieldValue('returnTime', undefined); + form.setFieldValue('price', undefined); + form.setFieldValue('returnType', undefined); + return; + } + const plan = await getReceivablePlan(value); + form.setFieldValue('returnTime', plan?.returnTime); + form.setFieldValue('price', plan?.price); + form.setFieldValue('returnType', plan?.returnType); + }, + } as any; }, }, }, diff --git a/apps/web-antd/src/views/crm/receivable/modules/form.vue b/apps/web-antd/src/views/crm/receivable/modules/form.vue index 07f240dd7..d04f2a9d8 100644 --- a/apps/web-antd/src/views/crm/receivable/modules/form.vue +++ b/apps/web-antd/src/views/crm/receivable/modules/form.vue @@ -7,6 +7,7 @@ import { useVbenForm, useVbenModal } from '@vben/common-ui'; import { message } from 'ant-design-vue'; +import { getContractSimpleList } from '#/api/crm/contract'; import { createReceivable, getReceivable, @@ -16,8 +17,20 @@ import { $t } from '#/locales'; import { useFormSchema } from '../data'; +type ReceivablePrefillData = Partial< + Pick< + CrmReceivableApi.Receivable, + 'contractId' | 'customerId' | 'price' | 'returnType' + > +> & { id?: number }; + +type ReceivableFormModalData = ReceivablePrefillData & { + plan?: ReceivablePrefillData; + receivable?: Pick; +}; + const emit = defineEmits(['success']); -const formData = ref(); +const formData = ref>(); const getTitle = computed(() => { return formData.value?.id ? $t('ui.actionTitle.edit', ['回款']) @@ -36,6 +49,32 @@ const [Form, formApi] = useVbenForm({ showDefaultActions: false, }); +/** 构建新增回款的预填表单 */ +async function buildCreateFormData( + plan: ReceivablePrefillData, +): Promise> { + const values: Partial = { + contractId: plan?.contractId, + customerId: plan?.customerId, + }; + // 从回款计划创建时,直接继承计划的期数、金额和回款方式 + if (plan?.id) { + values.planId = plan.id; + values.price = plan.price; + values.returnType = plan.returnType; + return values; + } + // 从客户/合同详情创建时,没有计划期数,按合同剩余应回款金额预填 + if (values.customerId && values.contractId) { + const contracts = await getContractSimpleList(values.customerId); + const contract = contracts.find((item) => item.id === values.contractId); + if (contract) { + values.price = contract.totalPrice - contract.totalReceivablePrice; + } + } + return values; +} + const [Modal, modalApi] = useVbenModal({ async onConfirm() { const { valid } = await formApi.validate(); @@ -63,31 +102,26 @@ const [Modal, modalApi] = useVbenModal({ return; } // 加载数据 - const data = modalApi.getData(); + formData.value = undefined; + await formApi.resetForm(); + const data = modalApi.getData() as null | ReceivableFormModalData; if (!data) { return; } - const { receivable, plan } = data; + const { receivable } = data; + const plan = + data.plan ?? (data.customerId || data.contractId ? data : undefined); modalApi.lock(); try { - if (receivable) { + if (receivable?.id) { formData.value = await getReceivable(receivable.id!); } else if (plan) { - formData.value = plan.id - ? { - planId: plan.id, - price: plan.price, - returnType: plan.returnType, - customerId: plan.customerId, - contractId: plan.contractId, - } - : ({ - customerId: plan.customerId, - contractId: plan.contractId, - } as any); + formData.value = await buildCreateFormData(plan); + } + if (formData.value) { + // 设置到 values + await formApi.setValues(formData.value as any); } - // 设置到 values - await formApi.setValues(formData.value as any); } finally { modalApi.unlock(); } diff --git a/apps/web-antdv-next/src/views/crm/receivable/data.ts b/apps/web-antdv-next/src/views/crm/receivable/data.ts index fffa32ca9..96895a784 100644 --- a/apps/web-antdv-next/src/views/crm/receivable/data.ts +++ b/apps/web-antdv-next/src/views/crm/receivable/data.ts @@ -57,12 +57,19 @@ export function useFormSchema(): VbenFormSchema[] { label: '客户名称', component: 'ApiSelect', rules: 'required', - componentProps: { + componentProps: (_values, form) => ({ api: getCustomerSimpleList, labelField: 'name', valueField: 'id', placeholder: '请选择客户', - }, + onChange: () => { + form.setFieldValue('contractId', undefined); + form.setFieldValue('planId', undefined); + form.setFieldValue('price', undefined); + form.setFieldValue('returnTime', undefined); + form.setFieldValue('returnType', undefined); + }, + }), dependencies: { triggerFields: ['id'], disabled: (values) => values.id, @@ -76,21 +83,33 @@ export function useFormSchema(): VbenFormSchema[] { dependencies: { triggerFields: ['customerId'], disabled: (values) => !values.customerId || values.id, - async componentProps(values) { - if (values.customerId) { - if (!values.id) { - // 特殊:只有在【新增】时,才清空合同编号 - values.contractId = undefined; - } - const contracts = await getContractSimpleList(values.customerId); + async componentProps(values, form) { + if (!values.customerId) { return { - options: contracts.map((item) => ({ - label: item.name, - value: item.id, - })), - placeholder: '请选择合同', - } as any; + options: [], + placeholder: '请选择客户', + }; } + const contracts = await getContractSimpleList(values.customerId); + return { + options: contracts.map((item) => ({ + label: item.name, + value: item.id, + })), + placeholder: '请选择合同', + onChange: (value: number) => { + form.setFieldValue('planId', undefined); + form.setFieldValue('returnTime', undefined); + form.setFieldValue('returnType', undefined); + const contract = contracts.find((item) => item.id === value); + form.setFieldValue( + 'price', + contract + ? contract.totalPrice - contract.totalReceivablePrice + : undefined, + ); + }, + } as any; }, }, }, @@ -101,28 +120,38 @@ export function useFormSchema(): VbenFormSchema[] { rules: 'required', dependencies: { triggerFields: ['contractId'], - disabled: (values) => !values.contractId, - async componentProps(values) { - if (values.contractId) { - values.planId = undefined; - const plans = await getReceivablePlanSimpleList( - values.customerId, - values.contractId, - ); + disabled: (values) => !values.contractId || values.id, + async componentProps(values, form) { + if (!values.contractId) { return { - options: plans.map((item) => ({ - label: item.period, - value: item.id, - })), - placeholder: '请选择回款期数', - onChange: async (value: any) => { - const plan = await getReceivablePlan(value); - values.returnTime = plan?.returnTime; - values.price = plan?.price; - values.returnType = plan?.returnType; - }, - } as any; + options: [], + placeholder: '请选择合同', + }; } + const plans = await getReceivablePlanSimpleList( + values.customerId, + values.contractId, + ); + return { + options: plans.map((item) => ({ + disabled: !!item.receivableId, + label: `第 ${item.period} 期`, + value: item.id, + })), + placeholder: '请选择回款期数', + onChange: async (value: any) => { + if (!value) { + form.setFieldValue('returnTime', undefined); + form.setFieldValue('price', undefined); + form.setFieldValue('returnType', undefined); + return; + } + const plan = await getReceivablePlan(value); + form.setFieldValue('returnTime', plan?.returnTime); + form.setFieldValue('price', plan?.price); + form.setFieldValue('returnType', plan?.returnType); + }, + } as any; }, }, }, diff --git a/apps/web-antdv-next/src/views/crm/receivable/modules/form.vue b/apps/web-antdv-next/src/views/crm/receivable/modules/form.vue index 17e32a27d..e1f2ad1a9 100644 --- a/apps/web-antdv-next/src/views/crm/receivable/modules/form.vue +++ b/apps/web-antdv-next/src/views/crm/receivable/modules/form.vue @@ -7,6 +7,7 @@ import { useVbenForm, useVbenModal } from '@vben/common-ui'; import { message } from 'antdv-next'; +import { getContractSimpleList } from '#/api/crm/contract'; import { createReceivable, getReceivable, @@ -16,8 +17,20 @@ import { $t } from '#/locales'; import { useFormSchema } from '../data'; +type ReceivablePrefillData = Partial< + Pick< + CrmReceivableApi.Receivable, + 'contractId' | 'customerId' | 'price' | 'returnType' + > +> & { id?: number }; + +type ReceivableFormModalData = ReceivablePrefillData & { + plan?: ReceivablePrefillData; + receivable?: Pick; +}; + const emit = defineEmits(['success']); -const formData = ref(); +const formData = ref>(); const getTitle = computed(() => { return formData.value?.id ? $t('ui.actionTitle.edit', ['回款']) @@ -36,6 +49,32 @@ const [Form, formApi] = useVbenForm({ showDefaultActions: false, }); +/** 构建新增回款的预填表单 */ +async function buildCreateFormData( + plan: ReceivablePrefillData, +): Promise> { + const values: Partial = { + contractId: plan?.contractId, + customerId: plan?.customerId, + }; + // 从回款计划创建时,直接继承计划的期数、金额和回款方式 + if (plan?.id) { + values.planId = plan.id; + values.price = plan.price; + values.returnType = plan.returnType; + return values; + } + // 从客户/合同详情创建时,没有计划期数,按合同剩余应回款金额预填 + if (values.customerId && values.contractId) { + const contracts = await getContractSimpleList(values.customerId); + const contract = contracts.find((item) => item.id === values.contractId); + if (contract) { + values.price = contract.totalPrice - contract.totalReceivablePrice; + } + } + return values; +} + const [Modal, modalApi] = useVbenModal({ async onConfirm() { const { valid } = await formApi.validate(); @@ -63,31 +102,26 @@ const [Modal, modalApi] = useVbenModal({ return; } // 加载数据 - const data = modalApi.getData(); + formData.value = undefined; + await formApi.resetForm(); + const data = modalApi.getData() as null | ReceivableFormModalData; if (!data) { return; } - const { receivable, plan } = data; + const { receivable } = data; + const plan = + data.plan ?? (data.customerId || data.contractId ? data : undefined); modalApi.lock(); try { - if (receivable) { + if (receivable?.id) { formData.value = await getReceivable(receivable.id!); } else if (plan) { - formData.value = plan.id - ? { - planId: plan.id, - price: plan.price, - returnType: plan.returnType, - customerId: plan.customerId, - contractId: plan.contractId, - } - : ({ - customerId: plan.customerId, - contractId: plan.contractId, - } as any); + formData.value = await buildCreateFormData(plan); + } + if (formData.value) { + // 设置到 values + await formApi.setValues(formData.value as any); } - // 设置到 values - await formApi.setValues(formData.value as any); } finally { modalApi.unlock(); } diff --git a/apps/web-ele/src/views/crm/receivable/data.ts b/apps/web-ele/src/views/crm/receivable/data.ts index d81d1474b..8717f2345 100644 --- a/apps/web-ele/src/views/crm/receivable/data.ts +++ b/apps/web-ele/src/views/crm/receivable/data.ts @@ -57,12 +57,19 @@ export function useFormSchema(): VbenFormSchema[] { label: '客户名称', component: 'ApiSelect', rules: 'required', - componentProps: { + componentProps: (_values, form) => ({ api: getCustomerSimpleList, labelField: 'name', valueField: 'id', placeholder: '请选择客户', - }, + onChange: () => { + form.setFieldValue('contractId', undefined); + form.setFieldValue('planId', undefined); + form.setFieldValue('price', undefined); + form.setFieldValue('returnTime', undefined); + form.setFieldValue('returnType', undefined); + }, + }), dependencies: { triggerFields: ['id'], disabled: (values) => values.id, @@ -76,21 +83,33 @@ export function useFormSchema(): VbenFormSchema[] { dependencies: { triggerFields: ['customerId'], disabled: (values) => !values.customerId || values.id, - async componentProps(values) { - if (values.customerId) { - if (!values.id) { - // 特殊:只有在【新增】时,才清空合同编号 - values.contractId = undefined; - } - const contracts = await getContractSimpleList(values.customerId); + async componentProps(values, form) { + if (!values.customerId) { return { - options: contracts.map((item) => ({ - label: item.name, - value: item.id, - })), - placeholder: '请选择合同', - } as any; + options: [], + placeholder: '请选择客户', + }; } + const contracts = await getContractSimpleList(values.customerId); + return { + options: contracts.map((item) => ({ + label: item.name, + value: item.id, + })), + placeholder: '请选择合同', + onChange: (value: number) => { + form.setFieldValue('planId', undefined); + form.setFieldValue('returnTime', undefined); + form.setFieldValue('returnType', undefined); + const contract = contracts.find((item) => item.id === value); + form.setFieldValue( + 'price', + contract + ? contract.totalPrice - contract.totalReceivablePrice + : undefined, + ); + }, + } as any; }, }, }, @@ -101,28 +120,38 @@ export function useFormSchema(): VbenFormSchema[] { rules: 'required', dependencies: { triggerFields: ['contractId'], - disabled: (values) => !values.contractId, - async componentProps(values) { - if (values.contractId) { - values.planId = undefined; - const plans = await getReceivablePlanSimpleList( - values.customerId, - values.contractId, - ); + disabled: (values) => !values.contractId || values.id, + async componentProps(values, form) { + if (!values.contractId) { return { - options: plans.map((item) => ({ - label: item.period, - value: item.id, - })), - placeholder: '请选择回款期数', - onChange: async (value: any) => { - const plan = await getReceivablePlan(value); - values.returnTime = plan?.returnTime; - values.price = plan?.price; - values.returnType = plan?.returnType; - }, - } as any; + options: [], + placeholder: '请选择合同', + }; } + const plans = await getReceivablePlanSimpleList( + values.customerId, + values.contractId, + ); + return { + options: plans.map((item) => ({ + disabled: !!item.receivableId, + label: `第 ${item.period} 期`, + value: item.id, + })), + placeholder: '请选择回款期数', + onChange: async (value: any) => { + if (!value) { + form.setFieldValue('returnTime', undefined); + form.setFieldValue('price', undefined); + form.setFieldValue('returnType', undefined); + return; + } + const plan = await getReceivablePlan(value); + form.setFieldValue('returnTime', plan?.returnTime); + form.setFieldValue('price', plan?.price); + form.setFieldValue('returnType', plan?.returnType); + }, + } as any; }, }, }, diff --git a/apps/web-ele/src/views/crm/receivable/modules/form.vue b/apps/web-ele/src/views/crm/receivable/modules/form.vue index ada8f56f9..5bea09fcb 100644 --- a/apps/web-ele/src/views/crm/receivable/modules/form.vue +++ b/apps/web-ele/src/views/crm/receivable/modules/form.vue @@ -7,6 +7,7 @@ import { useVbenForm, useVbenModal } from '@vben/common-ui'; import { ElMessage } from 'element-plus'; +import { getContractSimpleList } from '#/api/crm/contract'; import { createReceivable, getReceivable, @@ -16,8 +17,20 @@ import { $t } from '#/locales'; import { useFormSchema } from '../data'; +type ReceivablePrefillData = Partial< + Pick< + CrmReceivableApi.Receivable, + 'contractId' | 'customerId' | 'price' | 'returnType' + > +> & { id?: number }; + +type ReceivableFormModalData = ReceivablePrefillData & { + plan?: ReceivablePrefillData; + receivable?: Pick; +}; + const emit = defineEmits(['success']); -const formData = ref(); +const formData = ref>(); const getTitle = computed(() => { return formData.value?.id ? $t('ui.actionTitle.edit', ['回款']) @@ -36,6 +49,32 @@ const [Form, formApi] = useVbenForm({ showDefaultActions: false, }); +/** 构建新增回款的预填表单 */ +async function buildCreateFormData( + plan: ReceivablePrefillData, +): Promise> { + const values: Partial = { + contractId: plan?.contractId, + customerId: plan?.customerId, + }; + // 从回款计划创建时,直接继承计划的期数、金额和回款方式 + if (plan?.id) { + values.planId = plan.id; + values.price = plan.price; + values.returnType = plan.returnType; + return values; + } + // 从客户/合同详情创建时,没有计划期数,按合同剩余应回款金额预填 + if (values.customerId && values.contractId) { + const contracts = await getContractSimpleList(values.customerId); + const contract = contracts.find((item) => item.id === values.contractId); + if (contract) { + values.price = contract.totalPrice - contract.totalReceivablePrice; + } + } + return values; +} + const [Modal, modalApi] = useVbenModal({ async onConfirm() { const { valid } = await formApi.validate(); @@ -63,31 +102,26 @@ const [Modal, modalApi] = useVbenModal({ return; } // 加载数据 - const data = modalApi.getData(); + formData.value = undefined; + await formApi.resetForm(); + const data = modalApi.getData() as null | ReceivableFormModalData; if (!data) { return; } - const { receivable, plan } = data; + const { receivable } = data; + const plan = + data.plan ?? (data.customerId || data.contractId ? data : undefined); modalApi.lock(); try { - if (receivable) { + if (receivable?.id) { formData.value = await getReceivable(receivable.id!); } else if (plan) { - formData.value = plan.id - ? { - planId: plan.id, - price: plan.price, - returnType: plan.returnType, - customerId: plan.customerId, - contractId: plan.contractId, - } - : ({ - customerId: plan.customerId, - contractId: plan.contractId, - } as any); + formData.value = await buildCreateFormData(plan); + } + if (formData.value) { + // 设置到 values + await formApi.setValues(formData.value as any); } - // 设置到 values - await formApi.setValues(formData.value as any); } finally { modalApi.unlock(); }