diff --git a/apps/web-antd/src/views/crm/business/data.ts b/apps/web-antd/src/views/crm/business/data.ts index f2e5fa0e5..0cfd32130 100644 --- a/apps/web-antd/src/views/crm/business/data.ts +++ b/apps/web-antd/src/views/crm/business/data.ts @@ -90,6 +90,12 @@ export function useFormSchema(): VbenFormSchema[] { valueFormat: 'x', }, }, + { + fieldName: 'product', + label: '产品清单', + component: 'Input', + formItemClass: 'col-span-3', + }, { fieldName: 'totalProductPrice', label: '产品总金额', diff --git a/apps/web-antd/src/views/crm/business/modules/form.vue b/apps/web-antd/src/views/crm/business/modules/form.vue index 8d3b0d553..3a25fb663 100644 --- a/apps/web-antd/src/views/crm/business/modules/form.vue +++ b/apps/web-antd/src/views/crm/business/modules/form.vue @@ -13,7 +13,10 @@ import { getBusiness, updateBusiness, } from '#/api/crm/business'; +import { BizTypeEnum } from '#/api/crm/permission'; import { $t } from '#/locales'; +import { erpPriceMultiply } from '#/utils'; +import { ProductEditTable } from '#/views/crm/product'; import { useFormSchema } from '../data'; @@ -25,15 +28,37 @@ const getTitle = computed(() => { : $t('ui.actionTitle.create', ['商机']); }); +function handleUpdateProducts(products: any) { + formData.value = modalApi.getData(); + formData.value!.products = products; + if (formData.value) { + const totalProductPrice = + formData.value.products?.reduce( + (prev, curr) => prev + curr.totalPrice, + 0, + ) ?? 0; + const discountPercent = formData.value.discountPercent; + const discountPrice = + discountPercent === null + ? 0 + : erpPriceMultiply(totalProductPrice, discountPercent / 100); + const totalPrice = totalProductPrice - (discountPrice ?? 0); + formData.value!.totalProductPrice = totalProductPrice; + formData.value!.totalPrice = totalPrice; + formApi.setValues(formData.value!); + } +} + const [Form, formApi] = useVbenForm({ commonConfig: { componentProps: { class: 'w-full', }, - formItemClass: 'col-span-2', labelWidth: 120, }, - layout: 'horizontal', + // 一共3列 + wrapperClass: 'grid-cols-3', + layout: 'vertical', schema: useFormSchema(), showDefaultActions: false, }); @@ -47,6 +72,7 @@ const [Modal, modalApi] = useVbenModal({ modalApi.lock(); // 提交表单 const data = (await formApi.getValues()) as CrmBusinessApi.Business; + data.products = formData.value?.products; try { await (formData.value?.id ? updateBusiness(data) : createBusiness(data)); // 关闭并提示 @@ -80,7 +106,17 @@ const [Modal, modalApi] = useVbenModal({ diff --git a/apps/web-antd/src/views/crm/contract/data.ts b/apps/web-antd/src/views/crm/contract/data.ts index 79118dd3f..2c20e7a71 100644 --- a/apps/web-antd/src/views/crm/contract/data.ts +++ b/apps/web-antd/src/views/crm/contract/data.ts @@ -1,22 +1,32 @@ import type { VbenFormSchema } from '#/adapter/form'; import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import { z } from '#/adapter/form'; import { getSimpleBusinessList } from '#/api/crm/business'; import { getSimpleContactList } from '#/api/crm/contact'; import { getCustomerSimpleList } from '#/api/crm/customer'; -import { floatToFixed2 } from '#/utils'; +import { getSimpleUserList } from '#/api/system/user'; +import { erpPriceMultiply, floatToFixed2 } from '#/utils'; import { DICT_TYPE } from '#/utils/dict'; /** 新增/修改的表单 */ export function useFormSchema(): VbenFormSchema[] { return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, { fieldName: 'no', label: '合同编号', component: 'Input', - rules: 'required', componentProps: { - placeholder: '请输入合同编号', + placeholder: '保存时自动生成', + disabled: () => true, }, }, { @@ -28,9 +38,22 @@ export function useFormSchema(): VbenFormSchema[] { placeholder: '请输入合同名称', }, }, + { + fieldName: 'ownerUserId', + label: '负责人', + component: 'ApiSelect', + componentProps: { + api: () => getSimpleUserList(), + fieldNames: { + label: 'nickname', + value: 'id', + }, + }, + rules: 'required', + }, { fieldName: 'customerId', - label: '客户', + label: '客户名称', component: 'ApiSelect', rules: 'required', componentProps: { @@ -42,9 +65,8 @@ export function useFormSchema(): VbenFormSchema[] { }, { fieldName: 'businessId', - label: '商机', + label: '商机名称', component: 'ApiSelect', - rules: 'required', componentProps: { api: getSimpleBusinessList, labelField: 'name', @@ -52,49 +74,53 @@ export function useFormSchema(): VbenFormSchema[] { placeholder: '请选择商机', }, }, - { - fieldName: 'totalPrice', - label: '合同金额', - component: 'InputNumber', - rules: 'required', - componentProps: { - placeholder: '请输入合同金额', - min: 0, - precision: 2, - }, - }, { fieldName: 'orderDate', - label: '下单时间', + label: '下单日期', component: 'DatePicker', rules: 'required', componentProps: { - placeholder: '请选择下单时间', + showTime: false, + format: 'YYYY-MM-DD', + valueFormat: 'x', }, }, { fieldName: 'startTime', label: '合同开始时间', component: 'DatePicker', - rules: 'required', componentProps: { - placeholder: '请选择合同开始时间', + showTime: false, + format: 'YYYY-MM-DD', + valueFormat: 'x', }, }, { fieldName: 'endTime', label: '合同结束时间', component: 'DatePicker', - rules: 'required', componentProps: { - placeholder: '请选择合同结束时间', + showTime: false, + format: 'YYYY-MM-DD', + valueFormat: 'x', + }, + }, + { + fieldName: 'signUserId', + label: '公司签约人', + component: 'ApiSelect', + componentProps: { + api: () => getSimpleUserList(), + fieldNames: { + label: 'nickname', + value: 'id', + }, }, }, { fieldName: 'signContactId', label: '客户签约人', component: 'ApiSelect', - rules: 'required', componentProps: { api: getSimpleContactList, labelField: 'name', @@ -111,6 +137,50 @@ export function useFormSchema(): VbenFormSchema[] { rows: 4, }, }, + { + fieldName: 'product', + label: '产品清单', + component: 'Input', + formItemClass: 'col-span-3', + }, + { + fieldName: 'totalProductPrice', + label: '产品总金额', + component: 'InputNumber', + componentProps: { + min: 0, + }, + }, + { + fieldName: 'discountPercent', + label: '整单折扣(%)', + component: 'InputNumber', + rules: z.number().min(0).max(100).default(0), + componentProps: { + min: 0, + precision: 2, + }, + }, + { + fieldName: 'totalPrice', + label: '折扣后金额', + component: 'InputNumber', + dependencies: { + triggerFields: ['totalProductPrice', 'discountPercent'], + disabled: () => true, + trigger(values, form) { + const discountPrice = + erpPriceMultiply( + values.totalProductPrice, + values.discountPercent / 100, + ) ?? 0; + form.setFieldValue( + 'totalPrice', + values.totalProductPrice - discountPrice, + ); + }, + }, + }, ]; } diff --git a/apps/web-antd/src/views/crm/contract/modules/form.vue b/apps/web-antd/src/views/crm/contract/modules/form.vue index 5d2b0b78a..b96f22c02 100644 --- a/apps/web-antd/src/views/crm/contract/modules/form.vue +++ b/apps/web-antd/src/views/crm/contract/modules/form.vue @@ -12,7 +12,10 @@ import { getContract, updateContract, } from '#/api/crm/contract'; +import { BizTypeEnum } from '#/api/crm/permission'; import { $t } from '#/locales'; +import { erpPriceMultiply } from '#/utils'; +import { ProductEditTable } from '#/views/crm/product'; import { useFormSchema } from '../data'; @@ -24,15 +27,37 @@ const getTitle = computed(() => { : $t('ui.actionTitle.create', ['合同']); }); +function handleUpdateProducts(products: any) { + formData.value = modalApi.getData(); + formData.value!.products = products; + if (formData.value) { + const totalProductPrice = + formData.value.products?.reduce( + (prev, curr) => prev + curr.totalPrice, + 0, + ) ?? 0; + const discountPercent = formData.value.discountPercent; + const discountPrice = + discountPercent === null + ? 0 + : erpPriceMultiply(totalProductPrice, discountPercent / 100); + const totalPrice = totalProductPrice - (discountPrice ?? 0); + formData.value!.totalProductPrice = totalProductPrice; + formData.value!.totalPrice = totalPrice; + formApi.setValues(formData.value!); + } +} + const [Form, formApi] = useVbenForm({ commonConfig: { componentProps: { class: 'w-full', }, + labelWidth: 120, }, - // 一共2列 - wrapperClass: 'grid-cols-2', - layout: 'horizontal', + // 一共3列 + wrapperClass: 'grid-cols-3', + layout: 'vertical', schema: useFormSchema(), showDefaultActions: false, }); @@ -46,6 +71,7 @@ const [Modal, modalApi] = useVbenModal({ modalApi.lock(); // 提交表单 const data = (await formApi.getValues()) as CrmContractApi.Contract; + data.products = formData.value?.products; try { await (formData.value?.id ? updateContract(data) : createContract(data)); // 关闭并提示 @@ -79,7 +105,17 @@ const [Modal, modalApi] = useVbenModal({ diff --git a/apps/web-antd/src/views/crm/product/data.ts b/apps/web-antd/src/views/crm/product/data.ts index 348afb372..fa56e96b6 100644 --- a/apps/web-antd/src/views/crm/product/data.ts +++ b/apps/web-antd/src/views/crm/product/data.ts @@ -174,3 +174,60 @@ export function useGridColumns(): VxeTableGridOptions['columns'] { }, ]; } + +/** 代码生成表格列定义 */ +export function useProductEditTableColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'seq', title: '序号', minWidth: 50 }, + { + field: 'productId', + title: '产品名称', + minWidth: 100, + slots: { default: 'productId' }, + }, + { + field: 'productNo', + title: '条码', + minWidth: 150, + }, + { + field: 'productUnit', + title: '单位', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_PRODUCT_UNIT }, + }, + }, + { + field: 'productPrice', + title: '价格(元)', + minWidth: 100, + formatter: 'formatNumber', + }, + { + field: 'sellingPrice', + title: '售价(元)', + minWidth: 100, + slots: { default: 'sellingPrice' }, + }, + { + field: 'count', + title: '数量', + minWidth: 100, + slots: { default: 'count' }, + }, + { + field: 'totalPrice', + title: '合计', + minWidth: 100, + formatter: 'formatNumber', + }, + { + title: '操作', + width: 80, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-antd/src/views/crm/product/index.ts b/apps/web-antd/src/views/crm/product/index.ts index 09e7043b1..a9038b467 100644 --- a/apps/web-antd/src/views/crm/product/index.ts +++ b/apps/web-antd/src/views/crm/product/index.ts @@ -15,3 +15,7 @@ export const ProductDetails = defineAsyncComponent( export const ProductDetailsList = defineAsyncComponent( () => import('./modules/detail-list.vue'), ); + +export const ProductEditTable = defineAsyncComponent( + () => import('./modules/product-table.vue'), +); diff --git a/apps/web-antd/src/views/crm/product/modules/product-table.vue b/apps/web-antd/src/views/crm/product/modules/product-table.vue new file mode 100644 index 000000000..6ef206355 --- /dev/null +++ b/apps/web-antd/src/views/crm/product/modules/product-table.vue @@ -0,0 +1,183 @@ + + +