feat(@vben/web-antd): erp-优化上传附件及验证
- 修改订单时间格式为 timestamp - 限制文件上传数量为 1 - 调整表格列宽和显示逻辑 - 优化子表单验证和提交逻辑 - 修复文件上传组件的兼容性问题 - 优化产品清单的初始化和验证pull/181/head
parent
8a7239ce24
commit
f242f1c37d
|
@ -44,7 +44,7 @@ export function useFormSchema(): VbenFormSchema[] {
|
|||
placeholder: '选择订单时间',
|
||||
showTime: true,
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'x',
|
||||
style: { width: '100%' },
|
||||
},
|
||||
fieldName: 'orderTime',
|
||||
|
@ -65,7 +65,7 @@ export function useFormSchema(): VbenFormSchema[] {
|
|||
{
|
||||
component: 'FileUpload',
|
||||
componentProps: {
|
||||
maxNumber: 5,
|
||||
maxNumber: 1,
|
||||
maxSize: 10,
|
||||
accept: [
|
||||
'pdf',
|
||||
|
@ -340,17 +340,19 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
|
|||
{
|
||||
field: 'no',
|
||||
title: '订单单号',
|
||||
width: 260,
|
||||
width: 200,
|
||||
fixed: 'left',
|
||||
},
|
||||
{
|
||||
field: 'productNames',
|
||||
title: '产品信息',
|
||||
showOverflow: 'tooltip',
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
field: 'supplierName',
|
||||
title: '供应商',
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
field: 'orderTime',
|
||||
|
@ -361,40 +363,45 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
|
|||
{
|
||||
field: 'creatorName',
|
||||
title: '创建人',
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
field: 'totalCount',
|
||||
title: '总数量',
|
||||
formatter: 'formatNumber',
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
field: 'inCount',
|
||||
title: '入库数量',
|
||||
formatter: 'formatNumber',
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
field: 'returnCount',
|
||||
title: '退货数量',
|
||||
formatter: 'formatNumber',
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
field: 'totalProductPrice',
|
||||
title: '金额合计',
|
||||
formatter: 'formatNumber',
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
field: 'totalPrice',
|
||||
title: '含税金额',
|
||||
formatter: 'formatNumber',
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
field: 'depositPrice',
|
||||
title: '支付订金',
|
||||
formatter: 'formatNumber',
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
title: '状态',
|
||||
minWidth: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.ERP_AUDIT_STATUS },
|
||||
|
|
|
@ -58,6 +58,16 @@ 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]);
|
||||
// }
|
||||
// },
|
||||
},
|
||||
});
|
||||
|
||||
/** 监听外部传入的列数据 */
|
||||
|
@ -107,7 +117,25 @@ onMounted(async () => {
|
|||
});
|
||||
|
||||
function handleAdd() {
|
||||
gridApi.grid.insertAt(null, -1);
|
||||
const newRow = {
|
||||
id: tableData.value.length + 1,
|
||||
productId: null,
|
||||
productName: '',
|
||||
productUnitId: null,
|
||||
productUnitName: '',
|
||||
productBarCode: '',
|
||||
count: 1,
|
||||
productPrice: 0,
|
||||
totalProductPrice: 0,
|
||||
taxPercent: 0,
|
||||
taxPrice: 0,
|
||||
totalPrice: 0,
|
||||
stockCount: 0,
|
||||
remark: '',
|
||||
};
|
||||
tableData.value.push(newRow);
|
||||
gridApi.grid.insertAt(newRow, -1);
|
||||
emit('update:items', [...tableData.value]);
|
||||
}
|
||||
|
||||
function handleDelete(row: ErpPurchaseOrderApi.PurchaseOrderItem) {
|
||||
|
@ -185,20 +213,26 @@ const getSummaries = (): {
|
|||
};
|
||||
};
|
||||
|
||||
const validate = async (): Promise<void> => {
|
||||
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} 行:产品单价不能为空`);
|
||||
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} 行:产品单价不能为空`);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('验证失败:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -212,7 +246,11 @@ const init = (
|
|||
});
|
||||
};
|
||||
|
||||
defineExpose({ validate, getData, init });
|
||||
defineExpose({
|
||||
validate,
|
||||
getData,
|
||||
init,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
<script lang="ts" setup>
|
||||
import type { ErpPurchaseOrderApi } from '#/api/erp/purchase/order';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
import { computed, nextTick, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { formatDateTime } from '@vben/utils';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
|
@ -72,6 +71,9 @@ const handleUpdateTotalPrice = (totalPrice: number) => {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 创建或更新采购订单
|
||||
*/
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
async onConfirm() {
|
||||
const { valid } = await formApi.validate();
|
||||
|
@ -79,17 +81,49 @@ const [Modal, modalApi] = useVbenModal({
|
|||
return;
|
||||
}
|
||||
// 验证子表单
|
||||
if (itemFormRef.value && typeof itemFormRef.value.validate === 'function') {
|
||||
await itemFormRef.value.validate();
|
||||
await nextTick(); // 确保组件已经挂载
|
||||
|
||||
// 获取组件实例 - itemFormRef.value 是数组,需要访问第一个元素
|
||||
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) {
|
||||
message.error(error.message || '子表单验证失败');
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
message.error('子表单验证方法不存在');
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证产品清单不能为空
|
||||
if (!formData.value?.items || formData.value.items.length === 0) {
|
||||
message.error('产品清单不能为空,请至少添加一个产品');
|
||||
return;
|
||||
}
|
||||
|
||||
modalApi.lock();
|
||||
// 提交表单
|
||||
const data =
|
||||
(await formApi.getValues()) as ErpPurchaseOrderApi.PurchaseOrder;
|
||||
data.items =
|
||||
itemFormRef.value && typeof itemFormRef.value.getData === 'function'
|
||||
? itemFormRef.value.getData()
|
||||
: [];
|
||||
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] : '';
|
||||
}
|
||||
try {
|
||||
await (formType.value === 'create'
|
||||
? createPurchaseOrder(data)
|
||||
|
@ -109,18 +143,46 @@ const [Modal, modalApi] = useVbenModal({
|
|||
}
|
||||
// 加载数据
|
||||
const data = modalApi.getData<{ id?: number; type: string }>();
|
||||
if (!data || !data.id) {
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
formType.value = data.type;
|
||||
|
||||
if (!data.id) {
|
||||
// 初始化空的表单数据
|
||||
formData.value = { items: [] } as ErpPurchaseOrderApi.PurchaseOrder;
|
||||
await nextTick();
|
||||
const itemFormInstance = Array.isArray(itemFormRef.value)
|
||||
? itemFormRef.value[0]
|
||||
: itemFormRef.value;
|
||||
if (itemFormInstance && typeof itemFormInstance.init === 'function') {
|
||||
itemFormInstance.init([]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
modalApi.lock();
|
||||
try {
|
||||
formData.value = await getPurchaseOrder(data.id);
|
||||
formData.value.orderTime = formatDateTime(formData.value.orderTime);
|
||||
// 将字符串形式的文件URL转换为数组形式以适配FileUpload组件
|
||||
if (
|
||||
formData.value.fileUrl &&
|
||||
typeof formData.value.fileUrl === 'string'
|
||||
) {
|
||||
formData.value.fileUrl = formData.value.fileUrl
|
||||
? [formData.value.fileUrl]
|
||||
: [];
|
||||
}
|
||||
// 设置到 values
|
||||
await formApi.setValues(formData.value);
|
||||
// 初始化子表单
|
||||
itemFormRef.value?.init(formData.value.items || []);
|
||||
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 {
|
||||
modalApi.unlock();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue