feat:【antd】【erp 系统】sale/order 部分代码优化(form 中量)

This commit is contained in:
YunaiV
2025-10-03 18:26:30 +08:00
parent 3902005ad4
commit 3f0c60be04
3 changed files with 58 additions and 53 deletions

View File

@@ -24,6 +24,7 @@ export namespace ErpSaleOrderApi {
depositPrice?: number; // 定金金额,单位:元
items?: SaleOrderItem[]; // 销售订单产品明细列表
}
export interface SaleOrderItem {
id?: number; // 订单项编号
orderId?: number; // 采购订单编号
@@ -66,6 +67,13 @@ export function getSaleOrder(id: number) {
);
}
/** 查询销售订单项列表 */
export function getSaleOrderItemListByOrderId(orderId: number) {
return requestClient.get<ErpSaleOrderApi.SaleOrderItem[]>(
`/erp/sale-order/item/list-by-order-id?orderId=${orderId}`,
);
}
/** 新增销售订单 */
export function createSaleOrder(data: ErpSaleOrderApi.SaleOrder) {
return requestClient.post('/erp/sale-order/create', data);

View File

@@ -1,7 +1,7 @@
<script lang="ts" setup>
import type { ErpSaleOrderApi } from '#/api/erp/sale/order';
import { computed, nextTick, ref } from 'vue';
import { computed, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
@@ -16,18 +16,18 @@ import {
import { $t } from '#/locales';
import { useFormSchema } from '../data';
import SaleOrderItemForm from './sale-order-item-form.vue';
import ItemForm from './item-form.vue';
const emit = defineEmits(['success']);
const formData = ref<ErpSaleOrderApi.SaleOrder>();
const formType = ref(''); // 表单类型:'create' | 'update' | 'detail'
const itemFormRef = ref<InstanceType<typeof SaleOrderItemForm>>();
const formType = ref(''); // 表单类型:'create' | 'edit' | 'detail'
const itemFormRef = ref<InstanceType<typeof ItemForm>>();
/* eslint-disable unicorn/no-nested-ternary */
const getTitle = computed(() =>
formType.value === 'create'
? $t('ui.actionTitle.create', ['销售订单'])
: formType.value === 'update'
: formType.value === 'edit'
? $t('ui.actionTitle.edit', ['销售订单'])
: '销售订单详情',
);
@@ -38,7 +38,7 @@ const [Form, formApi] = useVbenForm({
class: 'w-full',
},
labelWidth: 120,
// disabled: !['create', 'update'].includes(formType.value), // TODO @芋艿:这里晚点处理下;
// disabled: !['create', 'edit'].includes(formType.value), // TODO @芋艿:这里晚点处理下;
},
wrapperClass: 'grid-cols-3',
layout: 'vertical',
@@ -51,6 +51,7 @@ const [Form, formApi] = useVbenForm({
},
});
/** 更新商品项 */
const handleUpdateItems = (items: ErpSaleOrderApi.SaleOrderItem[]) => {
formData.value = modalApi.getData<ErpSaleOrderApi.SaleOrder>();
if (formData.value) {
@@ -58,6 +59,7 @@ const handleUpdateItems = (items: ErpSaleOrderApi.SaleOrderItem[]) => {
}
};
/** 更新优惠金额 */
const handleUpdateDiscountPrice = (discountPrice: number) => {
if (formData.value) {
formData.value.discountPrice = discountPrice;
@@ -67,6 +69,7 @@ const handleUpdateDiscountPrice = (discountPrice: number) => {
}
};
/** 更新总金额 */
const handleUpdateTotalPrice = (totalPrice: number) => {
if (formData.value) {
formData.value.totalPrice = totalPrice;
@@ -87,10 +90,7 @@ const [Modal, modalApi] = useVbenModal({
? itemFormRef.value[0]
: itemFormRef.value;
try {
const isValid = await itemFormInstance.validate();
if (!isValid) {
return;
}
itemFormInstance.validate();
} catch (error: any) {
message.error(error.message || '子表单验证失败');
return;
@@ -140,14 +140,14 @@ const [Modal, modalApi] = useVbenModal({
<Modal
v-bind="$attrs"
:title="getTitle"
class="w-1/2"
class="w-3/4"
:closable="true"
:mask-closable="true"
:show-confirm-button="formType !== 'detail'"
>
<Form class="mx-3">
<template #product="slotProps">
<SaleOrderItemForm
<ItemForm
v-bind="slotProps"
ref="itemFormRef"
class="w-full"

View File

@@ -1,7 +1,8 @@
<script lang="ts" setup>
import type { ErpProductApi } from '#/api/erp/product/product';
import type { ErpSaleOrderApi } from '#/api/erp/sale/order';
import { nextTick, onMounted, ref, watch } from 'vue';
import { onMounted, ref, watch } from 'vue';
import { erpPriceMultiply } from '@vben/utils';
@@ -13,6 +14,12 @@ import { getStockCount } from '#/api/erp/stock/stock';
import { useSaleOrderItemTableColumns } from '../data';
interface Props {
items?: ErpSaleOrderApi.SaleOrderItem[];
disabled?: boolean;
discountPercent?: number;
}
const props = withDefaults(defineProps<Props>(), {
items: () => [],
disabled: false,
@@ -25,14 +32,8 @@ const emit = defineEmits([
'update:total-price',
]);
interface Props {
items?: ErpSaleOrderApi.SaleOrderItem[];
disabled?: boolean;
discountPercent?: number;
}
const tableData = ref<ErpSaleOrderApi.SaleOrderItem[]>([]);
const productOptions = ref<any[]>([]);
const tableData = ref<ErpSaleOrderApi.SaleOrderItem[]>([]); //
const productOptions = ref<ErpProductApi.Product[]>([]); //
/** 表格配置 */
const [Grid, gridApi] = useVbenVxeGrid({
@@ -50,6 +51,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
keepSource: true,
rowConfig: {
keyField: 'row_id',
isHover: true,
},
pagerConfig: {
enabled: false,
@@ -67,9 +69,7 @@ watch(
if (!items) {
return;
}
await nextTick();
tableData.value = [...items];
await nextTick();
await gridApi.grid.reloadData(tableData.value);
},
{
@@ -93,8 +93,7 @@ watch(
? 0
: erpPriceMultiply(totalPrice, props.discountPercent / 100);
const finalTotalPrice = totalPrice - discountPrice!;
//
//
emit('update:discount-price', discountPrice);
emit('update:total-price', finalTotalPrice);
},
@@ -106,6 +105,7 @@ onMounted(async () => {
productOptions.value = await getProductSimpleList();
});
/** 处理新增 */
function handleAdd() {
const newRow = {
productId: undefined,
@@ -124,38 +124,39 @@ function handleAdd() {
};
tableData.value.push(newRow);
gridApi.grid.insertAt(newRow, -1);
//
emit('update:items', [...tableData.value]);
}
/** 处理删除 */
function handleDelete(row: ErpSaleOrderApi.SaleOrderItem) {
gridApi.grid.remove(row);
const index = tableData.value.findIndex((item) => item.id === row.id);
if (index !== -1) {
tableData.value.splice(index, 1);
}
//
emit('update:items', [...tableData.value]);
}
/** 处理产品变更 */
async function handleProductChange(productId: any, row: any) {
const product = productOptions.value.find((p) => p.id === productId);
if (!product) {
return;
}
const stockCount = await getStockCount(productId);
row.productId = productId;
row.productUnitId = product.unitId;
row.productBarCode = product.barCode;
row.productUnitName = product.unitName;
row.productName = product.name;
row.stockCount = stockCount || 0;
row.stockCount = (await getStockCount(productId)) || 0;
row.productPrice = product.salePrice || 0;
row.count = row.count || 1;
handlePriceChange(row);
}
/** 处理价格变更 */
function handlePriceChange(row: any) {
if (row.productPrice && row.count) {
row.totalProductPrice = erpPriceMultiply(row.productPrice, row.count) ?? 0;
@@ -166,6 +167,7 @@ function handlePriceChange(row: any) {
handleUpdateValue(row);
}
/** 更新行数据 */
function handleUpdateValue(row: any) {
const index = tableData.value.findIndex((item) => item.id === row.id);
if (index === -1) {
@@ -176,13 +178,14 @@ function handleUpdateValue(row: any) {
emit('update:items', [...tableData.value]);
}
const getSummaries = (): {
/** 获取表格合计数据 */
function getSummaries(): {
count: number;
productName: string;
taxPrice: number;
totalPrice: number;
totalProductPrice: number;
} => {
} {
return {
productName: '合计',
count: tableData.value.reduce((sum, item) => sum + (item.count || 0), 0),
@@ -199,30 +202,25 @@ const getSummaries = (): {
0,
),
};
};
}
const validate = async (): Promise<boolean> => {
try {
for (let i = 0; i < tableData.value.length; i++) {
const item = tableData.value[i];
if (item) {
if (!item.productId) {
throw new Error(`${i + 1} 行:产品不能为空`);
}
if (!item.count || item.count <= 0) {
throw new Error(`${i + 1} 行:产品数量不能为空`);
}
if (!item.productPrice || item.productPrice <= 0) {
throw new Error(`${i + 1} 行:产品单价不能为空`);
}
/** 表单校验 */
function validate() {
for (let i = 0; i < tableData.value.length; i++) {
const item = tableData.value[i];
if (item) {
if (!item.productId) {
throw new Error(`${i + 1} 行:产品不能为空`);
}
if (!item.count || item.count <= 0) {
throw new Error(`${i + 1} 行:产品数量不能为空`);
}
if (!item.productPrice || item.productPrice <= 0) {
throw new Error(`${i + 1} 行:产品单价不能为空`);
}
}
return true;
} catch (error) {
console.error('验证失败:', error);
throw error;
}
};
}
defineExpose({
validate,
@@ -244,7 +242,6 @@ defineExpose({
/>
<span v-else>{{ row.productName || '-' }}</span>
</template>
<template #count="{ row }">
<InputNumber
v-if="!disabled"