From b3a2d29a3b9dad88429af4b946b58532ec940f44 Mon Sep 17 00:00:00 2001 From: nehc <934298133@qq.com> Date: Mon, 28 Jul 2025 14:03:25 +0800 Subject: [PATCH] =?UTF-8?q?feat(@vben/web-antd):=20erp-=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E9=87=87=E8=B4=AD=E8=AE=A2=E5=8D=95=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除采购订单明细项表单配置 - 调整采购订单表格列定义 - 优化采购订单删除操作,增加加载提示和错误处理 - 添加采购订单详情页面 - 重新计算采购订单项的价格字段 - 调整采购订单状态更新和删除的交互方式,增加二次确认 --- .../src/api/erp/purchase/order/index.ts | 1 + .../src/views/erp/purchase/order/data.ts | 126 ++---------------- .../src/views/erp/purchase/order/index.vue | 49 +++++-- .../order/modules/PurchaseOrderItemForm.vue | 30 +++-- .../views/erp/purchase/order/modules/form.vue | 11 +- 5 files changed, 70 insertions(+), 147 deletions(-) diff --git a/apps/web-antd/src/api/erp/purchase/order/index.ts b/apps/web-antd/src/api/erp/purchase/order/index.ts index 247dd61b..ae84c243 100644 --- a/apps/web-antd/src/api/erp/purchase/order/index.ts +++ b/apps/web-antd/src/api/erp/purchase/order/index.ts @@ -13,6 +13,7 @@ export namespace ErpPurchaseOrderApi { productUnitId?: number; // 产品单位编号 productUnitName?: string; // 产品单位名称 productPrice?: number; // 产品单价,单位:元 + totalProductPrice?: number; // 产品总价,单位:元 count?: number; // 数量 totalPrice?: number; // 总价,单位:元 taxPercent?: number; // 税率,百分比 diff --git a/apps/web-antd/src/views/erp/purchase/order/data.ts b/apps/web-antd/src/views/erp/purchase/order/data.ts index a623680c..0aa86f65 100644 --- a/apps/web-antd/src/views/erp/purchase/order/data.ts +++ b/apps/web-antd/src/views/erp/purchase/order/data.ts @@ -1,7 +1,7 @@ import type { VbenFormSchema } from '#/adapter/form'; import type { VxeTableGridOptions } from '#/adapter/vxe-table'; -import { erpCountInputFormatter, erpPriceInputFormatter } from '@vben/utils'; +import { erpPriceInputFormatter } from '@vben/utils'; import { z } from '#/adapter/form'; import { getAccountSimpleList } from '#/api/erp/finance/account'; @@ -13,6 +13,16 @@ import { DICT_TYPE, getDictOptions } from '#/utils'; /** 表单的配置项 */ export function useFormSchema(): VbenFormSchema[] { return [ + { + component: 'Input', + componentProps: { + style: { display: 'none' }, + }, + fieldName: 'id', + label: 'ID', + hideLabel: true, + formItemClass: 'hidden', + }, { component: 'Input', componentProps: { @@ -160,7 +170,7 @@ export function useFormSchema(): VbenFormSchema[] { /** 采购订单项表格列定义 */ export function usePurchaseOrderItemTableColumns(): VxeTableGridOptions['columns'] { return [ - { type: 'seq', title: '序号', minWidth: 50 }, + { type: 'seq', title: '序号', minWidth: 50, fixed: 'left' }, { field: 'productId', title: '产品名称', @@ -226,7 +236,7 @@ export function usePurchaseOrderItemTableColumns(): VxeTableGridOptions['columns }, { title: '操作', - width: 120, + width: 50, fixed: 'right', slots: { default: 'actions' }, }, @@ -415,113 +425,3 @@ export function useGridColumns(): VxeTableGridOptions['columns'] { }, ]; } - -/** 采购订单明细项表单配置 */ -export function useItemFormSchema( - onProductChange?: (productId: number) => void, -): VbenFormSchema[] { - return [ - { - component: 'ApiSelect', - componentProps: { - placeholder: '请选择产品', - allowClear: true, - showSearch: true, - api: getProductSimpleList, - fieldNames: { - label: 'name', - value: 'id', - }, - onChange: onProductChange, - }, - fieldName: 'productId', - label: '产品名称', - rules: 'required', - }, - { - component: 'InputNumber', - componentProps: { - placeholder: '请输入数量', - min: 1, - precision: 2, - formatter: erpCountInputFormatter, - style: { width: '100%' }, - }, - fieldName: 'count', - label: '数量', - rules: 'required', - }, - { - component: 'InputNumber', - componentProps: { - placeholder: '请输入单价', - min: 0, - precision: 2, - formatter: erpPriceInputFormatter, - style: { width: '100%' }, - }, - fieldName: 'productPrice', - label: '产品单价', - rules: 'required', - }, - { - component: 'InputNumber', - componentProps: { - placeholder: '金额', - precision: 2, - formatter: erpPriceInputFormatter, - disabled: true, - style: { width: '100%' }, - }, - fieldName: 'totalPrice', - label: '金额', - }, - { - component: 'InputNumber', - componentProps: { - placeholder: '请输入税率', - min: 0, - max: 100, - precision: 2, - style: { width: '100%' }, - }, - fieldName: 'taxPercent', - label: '税率(%)', - }, - { - component: 'InputNumber', - componentProps: { - placeholder: '税额', - precision: 2, - formatter: erpPriceInputFormatter, - disabled: true, - style: { width: '100%' }, - }, - fieldName: 'taxPrice', - label: '税额', - }, - { - component: 'InputNumber', - componentProps: { - placeholder: '税额合计', - precision: 2, - formatter: erpPriceInputFormatter, - disabled: true, - style: { width: '100%' }, - }, - fieldName: 'totalTaxPrice', - label: '税额合计', - }, - { - component: 'Textarea', - componentProps: { - placeholder: '请输入备注', - autoSize: { minRows: 2, maxRows: 4 }, - class: 'w-full', - }, - fieldName: 'remark', - label: '备注', - formItemClass: 'col-span-2', - }, - ]; -} diff --git a/apps/web-antd/src/views/erp/purchase/order/index.vue b/apps/web-antd/src/views/erp/purchase/order/index.vue index f491b0f4..bc9552c8 100644 --- a/apps/web-antd/src/views/erp/purchase/order/index.vue +++ b/apps/web-antd/src/views/erp/purchase/order/index.vue @@ -3,7 +3,6 @@ import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { ErpPurchaseOrderApi } from '#/api/erp/purchase/order'; import { ref } from 'vue'; -import { useRouter } from 'vue-router'; import { DocAlert, Page, useVbenModal } from '@vben/common-ui'; import { downloadFileFromBlobPart, isEmpty } from '@vben/utils'; @@ -12,6 +11,7 @@ import { message } from 'ant-design-vue'; import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; import { + deletePurchaseOrder, deletePurchaseOrderList, exportPurchaseOrder, getPurchaseOrderPage, @@ -35,8 +35,6 @@ function onRefresh() { gridApi.query(); } -const { push } = useRouter(); - const checkedIds = ref([]); function handleRowCheckboxChange({ records, @@ -48,7 +46,7 @@ function handleRowCheckboxChange({ /** 详情 */ function handleDetail(row: ErpPurchaseOrderApi.PurchaseOrder) { - push({ name: 'ErpPurchaseOrderDetail', params: { id: row.id } }); + formModalApi.setData({ type: 'detail', id: row.id }).open(); } /** 新增 */ @@ -62,8 +60,24 @@ function handleEdit(row: ErpPurchaseOrderApi.PurchaseOrder) { } /** 删除 */ -function handleDelete(row: ErpPurchaseOrderApi.PurchaseOrder) { - handleBatchDelete([row.id]); +async function handleDelete(row: ErpPurchaseOrderApi.PurchaseOrder) { + const hideLoading = message.loading({ + content: $t('ui.actionMessage.deleting'), + duration: 0, + key: 'action_process_msg', + }); + try { + if (row.id) await deletePurchaseOrder(row.id); + message.success({ + content: $t('ui.actionMessage.deleteSuccess'), + key: 'action_process_msg', + }); + onRefresh(); + } catch { + // 处理错误 + } finally { + hideLoading(); + } } /** 批量删除 */ @@ -195,7 +209,10 @@ const [Grid, gridApi] = useVbenVxeGrid({ disabled: isEmpty(checkedIds), icon: ACTION_ICON.DELETE, auth: ['erp:purchase-order:delete'], - onClick: handleBatchDelete, + popConfirm: { + title: `是否删除所选中数据?`, + confirm: handleBatchDelete, + }, }, ]" /> @@ -224,18 +241,26 @@ const [Grid, gridApi] = useVbenVxeGrid({ label: row.status === 10 ? '审批' : '反审批', type: 'link', auth: ['erp:purchase-order:update-status'], - onClick: handleUpdateStatus.bind( - null, - row, - row.status === 10 ? 20 : 10, - ), + popConfirm: { + title: `确认${row.status === 10 ? '审批' : '反审批'}${row.no}吗?`, + confirm: handleUpdateStatus.bind( + null, + row, + row.status === 10 ? 20 : 10, + ), + }, }, { label: $t('common.delete'), type: 'link', + danger: true, color: 'error', auth: ['erp:purchase-order:delete'], onClick: handleDelete.bind(null, row), + popConfirm: { + title: $t('ui.actionMessage.deleteConfirm', [row.no]), + confirm: handleDelete.bind(null, row), + }, }, ]" /> diff --git a/apps/web-antd/src/views/erp/purchase/order/modules/PurchaseOrderItemForm.vue b/apps/web-antd/src/views/erp/purchase/order/modules/PurchaseOrderItemForm.vue index 6ecde04c..03beaa53 100644 --- a/apps/web-antd/src/views/erp/purchase/order/modules/PurchaseOrderItemForm.vue +++ b/apps/web-antd/src/views/erp/purchase/order/modules/PurchaseOrderItemForm.vue @@ -58,16 +58,6 @@ const [Grid, gridApi] = useVbenVxeGrid({ enabled: false, }, }, - gridEvents: { - // editClosed: ({ row }) => { - // // 当单元格编辑完成时,同步更新tableData - // const index = tableData.value.findIndex((item) => item.id === row.id); - // if (index !== -1) { - // tableData.value[index] = { ...row }; - // emit('update:items', [...tableData.value]); - // } - // }, - }, }); /** 监听外部传入的列数据 */ @@ -118,7 +108,6 @@ onMounted(async () => { function handleAdd() { const newRow = { - id: tableData.value.length + 1, productId: null, productName: '', productUnitId: null, @@ -180,7 +169,6 @@ function handlePriceChange(row: any) { function handleUpdateValue(row: any) { const index = tableData.value.findIndex((item) => item.id === row.id); if (index === -1) { - row.id = tableData.value.length + 1; tableData.value.push(row); } else { tableData.value[index] = row; @@ -240,7 +228,23 @@ const getData = (): ErpPurchaseOrderApi.PurchaseOrderItem[] => tableData.value; const init = ( items: ErpPurchaseOrderApi.PurchaseOrderItem[] | undefined, ): void => { - tableData.value = items ? [...items] : []; + tableData.value = + items && items.length > 0 + ? items.map((item) => { + const newItem = { ...item }; + if (newItem.productPrice && newItem.count) { + newItem.totalProductPrice = + erpPriceMultiply(newItem.productPrice, newItem.count) ?? 0; + newItem.taxPrice = + erpPriceMultiply( + newItem.totalProductPrice, + (newItem.taxPercent || 0) / 100, + ) ?? 0; + newItem.totalPrice = newItem.totalProductPrice + newItem.taxPrice; + } + return newItem; + }) + : []; nextTick(() => { gridApi.grid.reloadData(tableData.value); }); diff --git a/apps/web-antd/src/views/erp/purchase/order/modules/form.vue b/apps/web-antd/src/views/erp/purchase/order/modules/form.vue index 9b605f24..d1399bfa 100644 --- a/apps/web-antd/src/views/erp/purchase/order/modules/form.vue +++ b/apps/web-antd/src/views/erp/purchase/order/modules/form.vue @@ -80,10 +80,8 @@ const [Modal, modalApi] = useVbenModal({ if (!valid) { return; } - // 验证子表单 - await nextTick(); // 确保组件已经挂载 + await nextTick(); - // 获取组件实例 - itemFormRef.value 是数组,需要访问第一个元素 const itemFormInstance = Array.isArray(itemFormRef.value) ? itemFormRef.value[0] : itemFormRef.value; @@ -114,12 +112,6 @@ const [Modal, modalApi] = useVbenModal({ const data = (await formApi.getValues()) as ErpPurchaseOrderApi.PurchaseOrder; data.items = formData.value?.items; - if (data.items) { - data.items = data.items.map((item) => { - const { ...itemWithoutId } = item; - return itemWithoutId; - }); - } // 将文件数组转换为字符串 if (data.fileUrl && Array.isArray(data.fileUrl)) { data.fileUrl = data.fileUrl.length > 0 ? data.fileUrl[0] : ''; @@ -199,6 +191,7 @@ defineExpose({ modalApi }); class="w-1/2" :closable="true" :mask-closable="true" + :show-confirm-button="formType !== 'detail'" >