feat:【antd】【erp 系统】sale/out 部分重构(form 基本重构完)
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
import type { ErpSaleOrderApi } from '#/api/erp/sale/order';
|
import type { ErpSaleOrderApi } from '#/api/erp/sale/order';
|
||||||
import type { ErpSaleOutApi } from '#/api/erp/sale/out';
|
import type { ErpSaleOutApi } from '#/api/erp/sale/out';
|
||||||
|
|
||||||
import { computed, nextTick, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
|
|
||||||
import { useVbenModal } from '@vben/common-ui';
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
import { $t } from '@vben/locales';
|
import { $t } from '@vben/locales';
|
||||||
@@ -110,7 +110,6 @@ const handleUpdateTotalPrice = (totalPrice: number) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/** 选择销售订单 */
|
/** 选择销售订单 */
|
||||||
// TODO @AI:不确定
|
|
||||||
const handleUpdateOrder = (order: ErpSaleOrderApi.SaleOrder) => {
|
const handleUpdateOrder = (order: ErpSaleOrderApi.SaleOrder) => {
|
||||||
formData.value = {
|
formData.value = {
|
||||||
...formData.value,
|
...formData.value,
|
||||||
@@ -132,8 +131,6 @@ const handleUpdateOrder = (order: ErpSaleOrderApi.SaleOrder) => {
|
|||||||
formData.value.items = order.items!.filter(
|
formData.value.items = order.items!.filter(
|
||||||
(item) => item.count && item.count > 0,
|
(item) => item.count && item.count > 0,
|
||||||
) as ErpSaleOutApi.SaleOutItem[];
|
) as ErpSaleOutApi.SaleOutItem[];
|
||||||
|
|
||||||
formApi.setValues(formData.value, false);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** 创建或更新销售出库 */
|
/** 创建或更新销售出库 */
|
||||||
@@ -184,64 +181,26 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
otherPrice: 0,
|
otherPrice: 0,
|
||||||
items: [],
|
items: [],
|
||||||
};
|
};
|
||||||
// await formApi.setValues(formData.value, false);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 加载数据
|
// 加载数据
|
||||||
const data = modalApi.getData<{ id?: number; type: string }>();
|
const data = modalApi.getData<{ id?: number; type: string }>();
|
||||||
if (!data) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
formType.value = data.type;
|
formType.value = data.type;
|
||||||
|
formApi.setDisabled(formType.value === 'detail');
|
||||||
formApi.updateSchema(useFormSchema(formType.value));
|
formApi.updateSchema(useFormSchema(formType.value));
|
||||||
|
if (!data || !data.id) {
|
||||||
if (!data.id) {
|
|
||||||
// 初始化空的表单数据
|
|
||||||
formData.value = {
|
|
||||||
id: undefined,
|
|
||||||
no: undefined,
|
|
||||||
accountId: undefined,
|
|
||||||
outTime: undefined,
|
|
||||||
remark: undefined,
|
|
||||||
fileUrl: undefined,
|
|
||||||
discountPercent: 0,
|
|
||||||
customerId: undefined,
|
|
||||||
discountPrice: 0,
|
|
||||||
totalPrice: 0,
|
|
||||||
otherPrice: 0,
|
|
||||||
items: [],
|
|
||||||
} as unknown as ErpSaleOutApi.SaleOut;
|
|
||||||
await nextTick();
|
|
||||||
const itemFormInstance = Array.isArray(itemFormRef.value)
|
|
||||||
? itemFormRef.value[0]
|
|
||||||
: itemFormRef.value;
|
|
||||||
if (itemFormInstance && typeof itemFormInstance.init === 'function') {
|
|
||||||
itemFormInstance.init([]);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
modalApi.lock();
|
modalApi.lock();
|
||||||
try {
|
try {
|
||||||
formData.value = await getSaleOut(data.id);
|
formData.value = await getSaleOut(data.id);
|
||||||
|
|
||||||
// 设置到 values
|
// 设置到 values
|
||||||
await formApi.setValues(formData.value, false);
|
await formApi.setValues(formData.value, false);
|
||||||
// 初始化子表单
|
|
||||||
await nextTick();
|
|
||||||
const itemFormInstance = Array.isArray(itemFormRef.value)
|
|
||||||
? itemFormRef.value[0]
|
|
||||||
: itemFormRef.value;
|
|
||||||
if (itemFormInstance && typeof itemFormInstance.init === 'function') {
|
|
||||||
itemFormInstance.init(formData.value.items || []);
|
|
||||||
}
|
|
||||||
} finally {
|
} finally {
|
||||||
modalApi.unlock();
|
modalApi.unlock();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
defineExpose({ modalApi });
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type {ErpSaleOutApi, SaleOutItem} from '#/api/erp/sale/out';
|
import type { ErpSaleOutApi } from '#/api/erp/sale/out';
|
||||||
|
|
||||||
import { nextTick, onMounted, ref, watch } from 'vue';
|
import { computed, nextTick, onMounted, ref, watch } from 'vue';
|
||||||
|
|
||||||
import { erpPriceMultiply } from '@vben/utils';
|
import {
|
||||||
|
erpCountInputFormatter,
|
||||||
|
erpPriceInputFormatter,
|
||||||
|
erpPriceMultiply,
|
||||||
|
} from '@vben/utils';
|
||||||
|
|
||||||
import {Input, InputNumber, Select} from 'ant-design-vue';
|
import { Input, InputNumber, Select } from 'ant-design-vue';
|
||||||
|
|
||||||
import {TableAction, useVbenVxeGrid} from '#/adapter/vxe-table';
|
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
import { getProductSimpleList } from '#/api/erp/product/product';
|
import { getProductSimpleList } from '#/api/erp/product/product';
|
||||||
import { getWarehouseStockCount } from '#/api/erp/stock/stock';
|
import { getWarehouseStockCount } from '#/api/erp/stock/stock';
|
||||||
import { getWarehouseSimpleList } from '#/api/erp/stock/warehouse';
|
import { getWarehouseSimpleList } from '#/api/erp/stock/warehouse';
|
||||||
@@ -36,25 +40,39 @@ const emit = defineEmits([
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const tableData = ref<ErpSaleOutApi.SaleOutItem[]>([]);
|
const tableData = ref<ErpSaleOutApi.SaleOutItem[]>([]);
|
||||||
const productOptions = ref<any[]>([]);
|
const productOptions = ref<any[]>([]); // 产品下拉选项
|
||||||
const warehouseOptions = ref<any[]>([]);
|
const warehouseOptions = ref<any[]>([]); // 仓库下拉选项
|
||||||
|
|
||||||
|
/** 获取表格合计数据 */
|
||||||
|
const summaries = computed(() => {
|
||||||
|
return {
|
||||||
|
count: tableData.value.reduce((sum, item) => sum + (item.count || 0), 0),
|
||||||
|
totalProductPrice: tableData.value.reduce(
|
||||||
|
(sum, item) => sum + (item.totalProductPrice || 0),
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
taxPrice: tableData.value.reduce(
|
||||||
|
(sum, item) => sum + (item.taxPrice || 0),
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
totalPrice: tableData.value.reduce(
|
||||||
|
(sum, item) => sum + (item.totalPrice || 0),
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
/** 表格配置 */
|
/** 表格配置 */
|
||||||
const [Grid, gridApi] = useVbenVxeGrid({
|
const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
gridOptions: {
|
gridOptions: {
|
||||||
editConfig: {
|
|
||||||
trigger: 'click',
|
|
||||||
mode: 'cell',
|
|
||||||
},
|
|
||||||
columns: useFormItemColumns(),
|
columns: useFormItemColumns(),
|
||||||
data: tableData.value,
|
data: tableData.value,
|
||||||
border: true,
|
|
||||||
showOverflow: true,
|
|
||||||
autoResize: true,
|
|
||||||
minHeight: 250,
|
minHeight: 250,
|
||||||
keepSource: true,
|
autoResize: true,
|
||||||
|
border: true,
|
||||||
rowConfig: {
|
rowConfig: {
|
||||||
keyField: 'id',
|
keyField: 'row_id',
|
||||||
|
isHover: true,
|
||||||
},
|
},
|
||||||
pagerConfig: {
|
pagerConfig: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
@@ -72,17 +90,17 @@ watch(
|
|||||||
if (!items) {
|
if (!items) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await nextTick();
|
items.forEach((item) => initRow(item));
|
||||||
tableData.value = [...items];
|
tableData.value = [...items];
|
||||||
await nextTick();
|
await nextTick(); // 特殊:保证 gridApi 已经初始化
|
||||||
gridApi.grid.reloadData(tableData.value);
|
await gridApi.grid.reloadData(tableData.value);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
immediate: true,
|
immediate: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
/** 计算 discountPrice、totalPrice 价格 */
|
/** 计算 discountPrice、otherPrice、totalPrice 价格 */
|
||||||
watch(
|
watch(
|
||||||
() => [tableData.value, props.discountPercent, props.otherPrice],
|
() => [tableData.value, props.discountPercent, props.otherPrice],
|
||||||
() => {
|
() => {
|
||||||
@@ -108,119 +126,6 @@ watch(
|
|||||||
{ deep: true },
|
{ deep: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
/** 初始化 */
|
|
||||||
onMounted(async () => {
|
|
||||||
productOptions.value = await getProductSimpleList();
|
|
||||||
warehouseOptions.value = await getWarehouseSimpleList();
|
|
||||||
});
|
|
||||||
|
|
||||||
function handlePriceChange(row: any) {
|
|
||||||
if (row.productPrice && row.count) {
|
|
||||||
row.totalProductPrice = erpPriceMultiply(row.productPrice, row.count) ?? 0;
|
|
||||||
row.taxPrice =
|
|
||||||
erpPriceMultiply(row.totalProductPrice, (row.taxPercent || 0) / 100) ?? 0;
|
|
||||||
row.totalPrice = row.totalProductPrice + row.taxPrice;
|
|
||||||
}
|
|
||||||
handleUpdateValue(row);
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleWarehouseChange = async (row: ErpSaleOutApi.SaleOutItem) => {
|
|
||||||
const warehouseId = row.warehouseId;
|
|
||||||
const stockCount = await getWarehouseStockCount({
|
|
||||||
productId: row.productId!,
|
|
||||||
warehouseId: warehouseId!,
|
|
||||||
});
|
|
||||||
row.stockCount = stockCount || 0;
|
|
||||||
handleUpdateValue(row);
|
|
||||||
};
|
|
||||||
|
|
||||||
function handleUpdateValue(row: any) {
|
|
||||||
const index = tableData.value.findIndex((item) => item.id === row.id);
|
|
||||||
if (index === -1) {
|
|
||||||
tableData.value.push(row);
|
|
||||||
} else {
|
|
||||||
tableData.value[index] = row;
|
|
||||||
}
|
|
||||||
emit('update:items', [...tableData.value]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const getSummaries = (): {
|
|
||||||
count: number;
|
|
||||||
productName: string;
|
|
||||||
taxPrice: number;
|
|
||||||
totalPrice: number;
|
|
||||||
totalProductPrice: number;
|
|
||||||
} => {
|
|
||||||
const count = tableData.value.reduce(
|
|
||||||
(sum, item) => sum + (item.count || 0),
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
const totalProductPrice = tableData.value.reduce(
|
|
||||||
(sum, item) => sum + (item.totalProductPrice || 0),
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
const taxPrice = tableData.value.reduce(
|
|
||||||
(sum, item) => sum + (item.taxPrice || 0),
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
const totalPrice = tableData.value.reduce(
|
|
||||||
(sum, item) => sum + (item.totalPrice || 0),
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
return {
|
|
||||||
productName: '合计',
|
|
||||||
count,
|
|
||||||
totalProductPrice,
|
|
||||||
taxPrice,
|
|
||||||
totalPrice,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const validate = async (): Promise<boolean> => {
|
|
||||||
try {
|
|
||||||
for (let i = 0; i < tableData.value.length; i++) {
|
|
||||||
const item = tableData.value[i];
|
|
||||||
if (item) {
|
|
||||||
if (!item.warehouseId) {
|
|
||||||
throw new Error(`第 ${i + 1} 行:仓库不能为空`);
|
|
||||||
}
|
|
||||||
if (!item.count || item.count <= 0) {
|
|
||||||
throw new Error(`第 ${i + 1} 行:产品数量不能为空`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('验证失败:', error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getData = (): ErpSaleOutApi.SaleOutItem[] => tableData.value;
|
|
||||||
const init = (items: ErpSaleOutApi.SaleOutItem[] | undefined): void => {
|
|
||||||
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;
|
|
||||||
})
|
|
||||||
: [];
|
|
||||||
// TODO @XuZhiqiang:使用 await 风格哈;
|
|
||||||
nextTick(() => {
|
|
||||||
gridApi.grid.reloadData(tableData.value);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/** 处理删除 */
|
/** 处理删除 */
|
||||||
function handleDelete(row: ErpSaleOutApi.SaleOutItem) {
|
function handleDelete(row: ErpSaleOutApi.SaleOutItem) {
|
||||||
const index = tableData.value.findIndex((item) => item.id === row.id);
|
const index = tableData.value.findIndex((item) => item.id === row.id);
|
||||||
@@ -231,10 +136,60 @@ function handleDelete(row: ErpSaleOutApi.SaleOutItem) {
|
|||||||
emit('update:items', [...tableData.value]);
|
emit('update:items', [...tableData.value]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 处理仓库变更 */
|
||||||
|
const handleWarehouseChange = async (row: ErpSaleOutApi.SaleOutItem) => {
|
||||||
|
const stockCount = await getWarehouseStockCount({
|
||||||
|
productId: row.productId!,
|
||||||
|
warehouseId: row.warehouseId!,
|
||||||
|
});
|
||||||
|
row.stockCount = stockCount || 0;
|
||||||
|
handleRowChange(row);
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 处理行数据变更 */
|
||||||
|
function handleRowChange(row: any) {
|
||||||
|
const index = tableData.value.findIndex((item) => item.id === row.id);
|
||||||
|
if (index === -1) {
|
||||||
|
tableData.value.push(row);
|
||||||
|
} else {
|
||||||
|
tableData.value[index] = row;
|
||||||
|
}
|
||||||
|
emit('update:items', [...tableData.value]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 初始化行数据 */
|
||||||
|
const initRow = (row: ErpSaleOutApi.SaleOutItem): void => {
|
||||||
|
if (row.productPrice && row.count) {
|
||||||
|
row.totalProductPrice = erpPriceMultiply(row.productPrice, row.count) ?? 0;
|
||||||
|
row.taxPrice =
|
||||||
|
erpPriceMultiply(row.totalProductPrice, (row.taxPercent || 0) / 100) ?? 0;
|
||||||
|
row.totalPrice = row.totalProductPrice + row.taxPrice;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 表单校验 */
|
||||||
|
function validate() {
|
||||||
|
for (let i = 0; i < tableData.value.length; i++) {
|
||||||
|
const item = tableData.value[i];
|
||||||
|
if (item) {
|
||||||
|
if (!item.warehouseId) {
|
||||||
|
throw new Error(`第 ${i + 1} 行:仓库不能为空`);
|
||||||
|
}
|
||||||
|
if (!item.count || item.count <= 0) {
|
||||||
|
throw new Error(`第 ${i + 1} 行:产品数量不能为空`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
validate,
|
validate,
|
||||||
getData,
|
});
|
||||||
init,
|
|
||||||
|
/** 初始化 */
|
||||||
|
onMounted(async () => {
|
||||||
|
productOptions.value = await getProductSimpleList();
|
||||||
|
warehouseOptions.value = await getWarehouseSimpleList();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -269,7 +224,7 @@ defineExpose({
|
|||||||
v-model:value="row.count"
|
v-model:value="row.count"
|
||||||
:min="0"
|
:min="0"
|
||||||
:precision="2"
|
:precision="2"
|
||||||
@change="handlePriceChange(row)"
|
@change="handleRowChange(row)"
|
||||||
/>
|
/>
|
||||||
<span v-else>{{ row.count || '-' }}</span>
|
<span v-else>{{ row.count || '-' }}</span>
|
||||||
</template>
|
</template>
|
||||||
@@ -279,7 +234,7 @@ defineExpose({
|
|||||||
v-model:value="row.productPrice"
|
v-model:value="row.productPrice"
|
||||||
:min="0"
|
:min="0"
|
||||||
:precision="2"
|
:precision="2"
|
||||||
@change="handlePriceChange(row)"
|
@change="handleRowChange(row)"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #remark="{ row }">
|
<template #remark="{ row }">
|
||||||
@@ -293,7 +248,7 @@ defineExpose({
|
|||||||
:min="0"
|
:min="0"
|
||||||
:max="100"
|
:max="100"
|
||||||
:precision="2"
|
:precision="2"
|
||||||
@change="handlePriceChange(row)"
|
@change="handleRowChange(row)"
|
||||||
/>
|
/>
|
||||||
<span v-else>{{ row.taxPercent || '-' }}</span>
|
<span v-else>{{ row.taxPercent || '-' }}</span>
|
||||||
</template>
|
</template>
|
||||||
@@ -319,10 +274,14 @@ defineExpose({
|
|||||||
<div class="text-muted-foreground flex justify-between text-sm">
|
<div class="text-muted-foreground flex justify-between text-sm">
|
||||||
<span class="text-foreground font-medium">合计:</span>
|
<span class="text-foreground font-medium">合计:</span>
|
||||||
<div class="flex space-x-4">
|
<div class="flex space-x-4">
|
||||||
<span>数量:{{ getSummaries().count }}</span>
|
<span>数量:{{ erpCountInputFormatter(summaries.count) }}</span>
|
||||||
<span>金额:{{ getSummaries().totalProductPrice }}</span>
|
<span>
|
||||||
<span>税额:{{ getSummaries().taxPrice }}</span>
|
金额:{{ erpPriceInputFormatter(summaries.totalProductPrice) }}
|
||||||
<span>税额合计:{{ getSummaries().totalPrice }}</span>
|
</span>
|
||||||
|
<span>税额:{{ erpPriceInputFormatter(summaries.taxPrice) }}</span>
|
||||||
|
<span>
|
||||||
|
税额合计:{{ erpPriceInputFormatter(summaries.totalPrice) }}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user