fix(web-antdv-next): 同步 CRM 回款预填和 ERP 单据弹窗修复

pull/359/head
XuZhiqiang 2026-06-06 23:08:42 +08:00
parent 7bc60c481a
commit 4b77779e60
16 changed files with 162 additions and 97 deletions

View File

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

View File

@ -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<CrmReceivableApi.Receivable, 'id'>;
};
const emit = defineEmits(['success']);
const formData = ref<CrmReceivableApi.Receivable>();
const formData = ref<Partial<CrmReceivableApi.Receivable>>();
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<Partial<CrmReceivableApi.Receivable>> {
const values: Partial<CrmReceivableApi.Receivable> = {
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();
}

View File

@ -176,6 +176,7 @@ const [Modal, modalApi] = useVbenModal({
<Modal
:title="getTitle"
class="w-3/4"
:close-on-click-modal="false"
:show-confirm-button="formType !== 'detail'"
>
<Form class="mx-3">

View File

@ -26,7 +26,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
},
gridOptions: {
columns: usePurchaseInGridColumns(),
height: 'auto',
height: 520,
keepSource: true,
proxyConfig: {
ajax: {
@ -95,14 +95,12 @@ defineExpose({ open: openModal });
<template>
<Modal
class="!w-[50vw]"
v-model:open="open"
title="选择采购入库单"
@ok="handleOk"
width="80%"
@cancel.stop="open = false"
@ok.stop="handleOk"
>
<Grid
class="max-h-[600px]"
table-title="采购入库单列表(仅展示可付款的单据)"
/>
<Grid table-title="()" />
</Modal>
</template>

View File

@ -26,7 +26,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
},
gridOptions: {
columns: useSaleReturnGridColumns(),
height: 'auto',
height: 520,
keepSource: true,
proxyConfig: {
ajax: {
@ -99,14 +99,12 @@ defineExpose({ open: openModal });
<template>
<Modal
class="!w-[50vw]"
v-model:open="open"
title="选择采购退货单"
@ok="handleOk"
width="80%"
@cancel.stop="open = false"
@ok.stop="handleOk"
>
<Grid
class="max-h-[600px]"
table-title="采购退货单列表(仅展示需退款的单据)"
/>
<Grid table-title="退(退)" />
</Modal>
</template>

View File

@ -190,6 +190,7 @@ const [Modal, modalApi] = useVbenModal({
<Modal
:title="getTitle"
class="w-3/4"
:close-on-click-modal="false"
:show-confirm-button="formType !== 'detail'"
>
<Form class="mx-3">

View File

@ -26,7 +26,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
},
gridOptions: {
columns: useSaleOutGridColumns(),
height: 'auto',
height: 520,
keepSource: true,
proxyConfig: {
ajax: {
@ -91,14 +91,12 @@ defineExpose({ open: openModal });
<template>
<Modal
class="!w-[50vw]"
v-model:open="open"
title="选择销售出库单"
@ok="handleOk"
width="80%"
@cancel.stop="open = false"
@ok.stop="handleOk"
>
<Grid
class="max-h-[600px]"
table-title="销售出库单列表(仅展示可收款的单据)"
/>
<Grid table-title="()" />
</Modal>
</template>

View File

@ -26,7 +26,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
},
gridOptions: {
columns: useSaleReturnGridColumns(),
height: 'auto',
height: 520,
keepSource: true,
proxyConfig: {
ajax: {
@ -95,14 +95,12 @@ defineExpose({ open: openModal });
<template>
<Modal
class="!w-[50vw]"
v-model:open="open"
title="选择销售退货单"
@ok="handleOk"
width="80%"
@cancel.stop="open = false"
@ok.stop="handleOk"
>
<Grid
class="max-h-[600px]"
table-title="销售退货单列表(仅展示可退款的单据)"
/>
<Grid table-title="退(退)" />
</Modal>
</template>

View File

@ -206,6 +206,7 @@ const [Modal, modalApi] = useVbenModal({
<Modal
:title="getTitle"
class="w-3/4"
:close-on-click-modal="false"
:show-confirm-button="formType !== 'detail'"
>
<Form class="mx-3">

View File

@ -38,7 +38,7 @@ const [Grid] = useVbenVxeGrid({
},
gridOptions: {
columns: useOrderGridColumns(),
height: 'auto',
height: 520,
keepSource: true,
proxyConfig: {
ajax: {
@ -108,12 +108,13 @@ function handleOk() {
</template>
</Input>
<Modal
class="!w-[50vw]"
v-model:open="open"
title="选择关联订单"
@ok="handleOk"
width="80%"
@cancel.stop="open = false"
@ok.stop="handleOk"
>
<Grid class="max-h-[600px]" table-title="(退)" />
<Grid table-title="(退)" />
</Modal>
</div>
</template>

View File

@ -206,6 +206,7 @@ const [Modal, modalApi] = useVbenModal({
<Modal
:title="getTitle"
class="w-3/4"
:close-on-click-modal="false"
:show-confirm-button="formType !== 'detail'"
>
<Form class="mx-3">

View File

@ -38,7 +38,7 @@ const [Grid] = useVbenVxeGrid({
},
gridOptions: {
columns: useOrderGridColumns(),
height: 'auto',
height: 520,
keepSource: true,
proxyConfig: {
ajax: {
@ -108,12 +108,13 @@ function handleOk() {
</template>
</Input>
<Modal
class="!w-[50vw]"
v-model:open="open"
title="选择关联订单"
@ok="handleOk"
width="80%"
@cancel.stop="open = false"
@ok.stop="handleOk"
>
<Grid class="max-h-[600px]" table-title="(退)" />
<Grid table-title="(退)" />
</Modal>
</div>
</template>

View File

@ -201,6 +201,7 @@ const [Modal, modalApi] = useVbenModal({
<Modal
:title="getTitle"
class="w-3/4"
:close-on-click-modal="false"
:show-confirm-button="formType !== 'detail'"
>
<Form class="mx-3">

View File

@ -38,7 +38,7 @@ const [Grid] = useVbenVxeGrid({
},
gridOptions: {
columns: useOrderGridColumns(),
height: 'auto',
height: 520,
keepSource: true,
proxyConfig: {
ajax: {
@ -108,12 +108,13 @@ function handleOk() {
</template>
</Input>
<Modal
class="!w-[50vw]"
v-model:open="open"
title="选择关联订单"
@ok="handleOk"
width="80%"
@cancel.stop="open = false"
@ok.stop="handleOk"
>
<Grid class="max-h-[600px]" table-title="()" />
<Grid table-title="()" />
</Modal>
</div>
</template>

View File

@ -206,6 +206,7 @@ const [Modal, modalApi] = useVbenModal({
<Modal
:title="getTitle"
class="w-3/4"
:close-on-click-modal="false"
:show-confirm-button="formType !== 'detail'"
>
<Form class="mx-3">

View File

@ -38,7 +38,7 @@ const [Grid] = useVbenVxeGrid({
},
gridOptions: {
columns: useOrderGridColumns(),
height: 'auto',
height: 520,
keepSource: true,
proxyConfig: {
ajax: {
@ -108,12 +108,13 @@ function handleOk() {
</template>
</Input>
<Modal
class="!w-[50vw]"
v-model:open="open"
title="选择关联订单"
@ok="handleOk"
width="80%"
@cancel.stop="open = false"
@ok.stop="handleOk"
>
<Grid class="max-h-[600px]" table-title="(退)" />
<Grid table-title="(退)" />
</Modal>
</div>
</template>