feat:【antd】【erp 系统】sale/out 部分重构(form 部分重构)
This commit is contained in:
@@ -42,7 +42,7 @@ const [Form, formApi] = useVbenForm({
|
||||
},
|
||||
wrapperClass: 'grid-cols-3',
|
||||
layout: 'vertical',
|
||||
schema: useFormSchema(),
|
||||
schema: useFormSchema(formType.value),
|
||||
showDefaultActions: false,
|
||||
handleValuesChange: (values, changedFields) => {
|
||||
// 目的:同步到 item-form 组件,触发整体的价格计算
|
||||
@@ -95,7 +95,6 @@ const [Modal, modalApi] = useVbenModal({
|
||||
modalApi.lock();
|
||||
// 提交表单
|
||||
const data = (await formApi.getValues()) as ErpSaleOrderApi.SaleOrder;
|
||||
data.items = formData.value?.items;
|
||||
try {
|
||||
await (formType.value === 'create'
|
||||
? createSaleOrder(data)
|
||||
|
||||
@@ -255,6 +255,10 @@ onMounted(async () => {
|
||||
/>
|
||||
<span v-else>{{ row.productPrice || '-' }}</span>
|
||||
</template>
|
||||
<template #remark="{ row }">
|
||||
<Input v-if="!disabled" v-model:value="row.remark" class="w-full" />
|
||||
<span v-else>{{ row.remark || '-' }}</span>
|
||||
</template>
|
||||
<template #taxPercent="{ row }">
|
||||
<InputNumber
|
||||
v-if="!disabled"
|
||||
@@ -266,10 +270,6 @@ onMounted(async () => {
|
||||
/>
|
||||
<span v-else>{{ row.taxPercent || '-' }}</span>
|
||||
</template>
|
||||
<template #remark="{ row }">
|
||||
<Input v-if="!disabled" v-model:value="row.remark" class="w-full" />
|
||||
<span v-else>{{ row.remark || '-' }}</span>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
v-if="!disabled"
|
||||
|
||||
@@ -33,6 +33,30 @@ export function useFormSchema(formType: string): VbenFormSchema[] {
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'outTime',
|
||||
label: '出库时间',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
disabled: formType === 'detail',
|
||||
placeholder: '选择出库时间',
|
||||
showTime: true,
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'x',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'orderNo',
|
||||
label: '关联订单',
|
||||
component: 'Input',
|
||||
formItemClass: 'col-span-1',
|
||||
rules: 'required',
|
||||
componentProps: {
|
||||
placeholder: '请选择关联订单',
|
||||
disabled: formType === 'detail',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'customerId',
|
||||
label: '客户',
|
||||
@@ -51,29 +75,20 @@ export function useFormSchema(formType: string): VbenFormSchema[] {
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'orderNo',
|
||||
label: '关联订单',
|
||||
component: 'Input',
|
||||
formItemClass: 'col-span-1',
|
||||
rules: 'required',
|
||||
fieldName: 'saleUserId',
|
||||
label: '销售人员',
|
||||
component: 'ApiSelect',
|
||||
componentProps: {
|
||||
placeholder: '请选择关联订单',
|
||||
disabled: formType === 'detail',
|
||||
placeholder: '请选择销售人员',
|
||||
allowClear: true,
|
||||
showSearch: true,
|
||||
api: getSimpleUserList,
|
||||
fieldNames: {
|
||||
label: 'nickname',
|
||||
value: 'id',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'outTime',
|
||||
label: '出库时间',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
disabled: formType === 'detail',
|
||||
placeholder: '选择出库时间',
|
||||
showTime: true,
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'x',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
@@ -128,7 +143,7 @@ export function useFormSchema(formType: string): VbenFormSchema[] {
|
||||
},
|
||||
{
|
||||
fieldName: 'discountPrice',
|
||||
label: '付款优惠',
|
||||
label: '收款优惠',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
placeholder: '付款优惠',
|
||||
@@ -182,6 +197,7 @@ export function useFormSchema(formType: string): VbenFormSchema[] {
|
||||
componentProps: {
|
||||
precision: 2,
|
||||
min: 0,
|
||||
disabled: true
|
||||
},
|
||||
rules: z.number().min(0).optional(),
|
||||
},
|
||||
@@ -220,14 +236,10 @@ export function useFormItemColumns(): VxeTableGridOptions['columns'] {
|
||||
minWidth: 80,
|
||||
},
|
||||
{
|
||||
field: 'totalCount',
|
||||
title: '原数量',
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
field: 'outCount',
|
||||
title: '已出库数量',
|
||||
minWidth: 120,
|
||||
field: 'remark',
|
||||
title: '备注',
|
||||
minWidth: 150,
|
||||
slots: { default: 'remark' },
|
||||
},
|
||||
{
|
||||
field: 'count',
|
||||
@@ -254,7 +266,8 @@ export function useFormItemColumns(): VxeTableGridOptions['columns'] {
|
||||
fixed: 'right',
|
||||
field: 'taxPercent',
|
||||
title: '税率(%)',
|
||||
minWidth: 100,
|
||||
minWidth: 105,
|
||||
slots: { default: 'taxPercent' },
|
||||
},
|
||||
{
|
||||
fixed: 'right',
|
||||
@@ -270,6 +283,12 @@ export function useFormItemColumns(): VxeTableGridOptions['columns'] {
|
||||
minWidth: 120,
|
||||
formatter: 'formatAmount2',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 50,
|
||||
fixed: 'right',
|
||||
slots: { default: 'actions' },
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { ErpSaleOutApi } from '#/api/erp/sale/out';
|
||||
import { computed, nextTick, ref, watch } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
@@ -45,11 +46,14 @@ const formData = ref<
|
||||
const formType = ref(''); // 表单类型:'create' | 'edit' | 'detail'
|
||||
const itemFormRef = ref<InstanceType<typeof ItemForm>>();
|
||||
|
||||
const getTitle = computed(() => {
|
||||
if (formType.value === 'create') return '添加销售出库';
|
||||
if (formType.value === 'update') return '编辑销售出库';
|
||||
return '销售出库详情';
|
||||
});
|
||||
/* eslint-disable unicorn/no-nested-ternary */
|
||||
const getTitle = computed(() =>
|
||||
formType.value === 'create'
|
||||
? $t('ui.actionTitle.create', ['销售出库'])
|
||||
: formType.value === 'edit'
|
||||
? $t('ui.actionTitle.edit', ['销售出库'])
|
||||
: '销售出库详情',
|
||||
);
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
@@ -68,7 +72,7 @@ const [Form, formApi] = useVbenForm({
|
||||
formData.value.totalPrice =
|
||||
(formData.value.discountedPrice || 0) +
|
||||
(formData.value.otherPrice || 0);
|
||||
|
||||
// TODO @芋艿:是否需要,去掉?
|
||||
formApi.setValues(formData.value, false);
|
||||
}
|
||||
},
|
||||
@@ -79,11 +83,13 @@ const handleUpdateItems = async (items: ErpSaleOutApi.SaleOutItem[]) => {
|
||||
if (formData.value) {
|
||||
const data = await formApi.getValues();
|
||||
formData.value = { ...data, items };
|
||||
// TODO @芋艿:是否需要,去掉?
|
||||
await formApi.setValues(formData.value, false);
|
||||
}
|
||||
};
|
||||
|
||||
/** 选择销售订单 */
|
||||
// TODO @AI:不确定
|
||||
const handleUpdateOrder = (order: ErpSaleOrderApi.SaleOrder) => {
|
||||
formData.value = {
|
||||
...formData.value,
|
||||
@@ -109,72 +115,61 @@ const handleUpdateOrder = (order: ErpSaleOrderApi.SaleOrder) => {
|
||||
formApi.setValues(formData.value, false);
|
||||
};
|
||||
|
||||
watch(
|
||||
() => formData.value.items!,
|
||||
(newItems: ErpSaleOutApi.SaleOutItem[]) => {
|
||||
if (newItems && newItems.length > 0) {
|
||||
// 计算每个产品的总价、税额和总价
|
||||
newItems.forEach((item) => {
|
||||
item.totalProductPrice = (item.productPrice || 0) * (item.count || 0);
|
||||
item.taxPrice =
|
||||
(item.totalProductPrice || 0) * ((item.taxPercent || 0) / 100);
|
||||
item.totalPrice = (item.totalProductPrice || 0) + (item.taxPrice || 0);
|
||||
});
|
||||
// 计算总价
|
||||
formData.value.totalPrice = newItems.reduce((sum, item) => {
|
||||
return sum + (item.totalProductPrice || 0) + (item.taxPrice || 0);
|
||||
}, 0);
|
||||
} else {
|
||||
formData.value.totalPrice = 0;
|
||||
}
|
||||
// 优惠金额
|
||||
formData.value.discountPrice =
|
||||
((formData.value.totalPrice || 0) *
|
||||
(formData.value.discountPercent || 0)) /
|
||||
100;
|
||||
// 优惠后价格
|
||||
formData.value.discountedPrice =
|
||||
formData.value.totalPrice - formData.value.discountPrice;
|
||||
// TODO @AI:不确定
|
||||
// watch(
|
||||
// () => formData.value.items!,
|
||||
// (newItems: ErpSaleOutApi.SaleOutItem[]) => {
|
||||
// if (newItems && newItems.length > 0) {
|
||||
// // 计算每个产品的总价、税额和总价
|
||||
// newItems.forEach((item) => {
|
||||
// item.totalProductPrice = (item.productPrice || 0) * (item.count || 0);
|
||||
// item.taxPrice =
|
||||
// (item.totalProductPrice || 0) * ((item.taxPercent || 0) / 100);
|
||||
// item.totalPrice = (item.totalProductPrice || 0) + (item.taxPrice || 0);
|
||||
// });
|
||||
// // 计算总价
|
||||
// formData.value.totalPrice = newItems.reduce((sum, item) => {
|
||||
// return sum + (item.totalProductPrice || 0) + (item.taxPrice || 0);
|
||||
// }, 0);
|
||||
// } else {
|
||||
// formData.value.totalPrice = 0;
|
||||
// }
|
||||
// // 优惠金额
|
||||
// formData.value.discountPrice =
|
||||
// ((formData.value.totalPrice || 0) *
|
||||
// (formData.value.discountPercent || 0)) /
|
||||
// 100;
|
||||
// // 优惠后价格
|
||||
// formData.value.discountedPrice =
|
||||
// formData.value.totalPrice - formData.value.discountPrice;
|
||||
//
|
||||
// // 计算最终价格(包含其他费用)
|
||||
// formData.value.totalPrice =
|
||||
// formData.value.discountedPrice + (formData.value.otherPrice || 0);
|
||||
// formApi.setValues(formData.value, false);
|
||||
// },
|
||||
// { immediate: true },
|
||||
// );
|
||||
|
||||
// 计算最终价格(包含其他费用)
|
||||
formData.value.totalPrice =
|
||||
formData.value.discountedPrice + (formData.value.otherPrice || 0);
|
||||
formApi.setValues(formData.value, false);
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
/**
|
||||
* 创建或更新销售出库
|
||||
*/
|
||||
/** 创建或更新销售出库 */
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
async onConfirm() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
await nextTick();
|
||||
|
||||
const itemFormInstance = Array.isArray(itemFormRef.value)
|
||||
? itemFormRef.value[0]
|
||||
: itemFormRef.value;
|
||||
if (itemFormInstance && typeof itemFormInstance.validate === 'function') {
|
||||
try {
|
||||
const isValid = await itemFormInstance.validate();
|
||||
if (!isValid) {
|
||||
message.error('子表单验证失败');
|
||||
return;
|
||||
}
|
||||
} catch (error: any) {
|
||||
message.error(error.message || '子表单验证失败');
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
message.error('子表单验证方法不存在');
|
||||
try {
|
||||
itemFormInstance.validate();
|
||||
} catch (error: any) {
|
||||
message.error(error.message || '子表单验证失败');
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证产品清单不能为空
|
||||
// TODO @芋艿:需要校验么?
|
||||
if (!formData.value?.items || formData.value.items.length === 0) {
|
||||
message.error('产品清单不能为空,请至少添加一个产品');
|
||||
return;
|
||||
@@ -195,7 +190,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
// 关闭并提示
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
message.success(formType.value === 'create' ? '新增成功' : '更新成功');
|
||||
message.success($t('ui.actionMessage.operationSuccess'));
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
@@ -278,27 +273,21 @@ defineExpose({ modalApi });
|
||||
|
||||
<template>
|
||||
<Modal
|
||||
v-bind="$attrs"
|
||||
:title="getTitle"
|
||||
class="w-2/3"
|
||||
:closable="true"
|
||||
:mask-closable="true"
|
||||
class="w-3/4"
|
||||
:show-confirm-button="formType !== 'detail'"
|
||||
>
|
||||
<Form class="mx-3">
|
||||
<template #items="slotProps">
|
||||
<template #items>
|
||||
<ItemForm
|
||||
v-bind="slotProps"
|
||||
ref="itemFormRef"
|
||||
class="w-full"
|
||||
:items="formData?.items ?? []"
|
||||
:disabled="formType === 'detail'"
|
||||
@update:items="handleUpdateItems"
|
||||
/>
|
||||
</template>
|
||||
<template #orderNo="slotProps">
|
||||
<template #orderNo>
|
||||
<SelectSaleOrderForm
|
||||
v-bind="slotProps"
|
||||
:order-no="formData?.orderNo"
|
||||
@update:order="handleUpdateOrder"
|
||||
/>
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<script lang="ts" setup>
|
||||
import type { ErpSaleOutApi } from '#/api/erp/sale/out';
|
||||
import type {ErpSaleOutApi, SaleOutItem} from '#/api/erp/sale/out';
|
||||
|
||||
import { nextTick, onMounted, ref, watch } from 'vue';
|
||||
|
||||
import { erpPriceMultiply } from '@vben/utils';
|
||||
|
||||
import { InputNumber, Select } from 'ant-design-vue';
|
||||
import {Input, InputNumber, Select} from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import {TableAction, useVbenVxeGrid} from '#/adapter/vxe-table';
|
||||
import { getProductSimpleList } from '#/api/erp/product/product';
|
||||
import { getWarehouseStockCount } from '#/api/erp/stock/stock';
|
||||
import { getWarehouseSimpleList } from '#/api/erp/stock/warehouse';
|
||||
@@ -186,6 +186,16 @@ const init = (items: ErpSaleOutApi.SaleOutItem[] | undefined): void => {
|
||||
});
|
||||
};
|
||||
|
||||
/** 处理删除 */
|
||||
function handleDelete(row: ErpSaleOutApi.SaleOutItem) {
|
||||
const index = tableData.value.findIndex((item) => item.id === row.id);
|
||||
if (index !== -1) {
|
||||
tableData.value.splice(index, 1);
|
||||
}
|
||||
// 通知父组件更新
|
||||
emit('update:items', [...tableData.value]);
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
validate,
|
||||
getData,
|
||||
@@ -218,7 +228,6 @@ defineExpose({
|
||||
show-search
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #count="{ row }">
|
||||
<InputNumber
|
||||
v-if="!disabled"
|
||||
@@ -238,6 +247,37 @@ defineExpose({
|
||||
@change="handlePriceChange(row)"
|
||||
/>
|
||||
</template>
|
||||
<template #remark="{ row }">
|
||||
<Input v-if="!disabled" v-model:value="row.remark" class="w-full" />
|
||||
<span v-else>{{ row.remark || '-' }}</span>
|
||||
</template>
|
||||
<template #taxPercent="{ row }">
|
||||
<InputNumber
|
||||
v-if="!disabled"
|
||||
v-model:value="row.taxPercent"
|
||||
:min="0"
|
||||
:max="100"
|
||||
:precision="2"
|
||||
@change="handlePriceChange(row)"
|
||||
/>
|
||||
<span v-else>{{ row.taxPercent || '-' }}</span>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
v-if="!disabled"
|
||||
:actions="[
|
||||
{
|
||||
label: '删除',
|
||||
type: 'link',
|
||||
danger: true,
|
||||
popConfirm: {
|
||||
title: '确认删除该产品吗?',
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #bottom>
|
||||
<div class="border-border bg-muted mt-2 rounded border p-2">
|
||||
|
||||
Reference in New Issue
Block a user