diff --git a/apps/web-antd/src/adapter/vxe-table.ts b/apps/web-antd/src/adapter/vxe-table.ts index 69f8555035ae08142cd8100dc4e241ad8e806032..e28a6ae1d4bd75aca875787f28d820ca88b3bda5 100644 --- a/apps/web-antd/src/adapter/vxe-table.ts +++ b/apps/web-antd/src/adapter/vxe-table.ts @@ -7,6 +7,7 @@ import { IconifyIcon } from '@vben/icons'; import { $te } from '@vben/locales'; import { AsyncComponents, + createRequiredValidation, setupVbenVxeTable, useVbenVxeGrid, } from '@vben/plugins/vxe-table'; @@ -354,7 +355,7 @@ setupVbenVxeTable({ useVbenForm, }); -export { useVbenVxeGrid }; +export { createRequiredValidation, useVbenVxeGrid }; const [VxeTable, VxeColumn, VxeToolbar] = AsyncComponents; export { VxeColumn, VxeTable, VxeToolbar }; diff --git a/apps/web-antd/src/api/erp/stock/in/index.ts b/apps/web-antd/src/api/erp/stock/in/index.ts index 119398bc382a1b08474ee1791937383da5d4a92d..8fe9bafd487869adaa21d748f0586d5fee1b352d 100644 --- a/apps/web-antd/src/api/erp/stock/in/index.ts +++ b/apps/web-antd/src/api/erp/stock/in/index.ts @@ -2,17 +2,38 @@ import type { PageParam, PageResult } from '@vben/request'; import { requestClient } from '#/api/request'; -namespace ErpStockInApi { +export namespace ErpStockInApi { /** 其它入库单信息 */ export interface StockIn { id?: number; // 入库编号 no: string; // 入库单号 supplierId: number; // 供应商编号 + supplierName?: string; // 供应商名称 inTime: Date; // 入库时间 totalCount: number; // 合计数量 totalPrice: number; // 合计金额,单位:元 status: number; // 状态 remark: string; // 备注 + fileUrl?: string; // 附件 + productNames?: string; // 产品信息 + creatorName?: string; // 创建人 + items?: StockInItem[]; // 入库产品清单 + } + + /** 其它入库单产品信息 */ + export interface StockInItem { + id?: number; // 编号 + warehouseId: number; // 仓库编号 + productId: number; // 产品编号 + productName?: string; // 产品名称 + productUnitId?: number; // 产品单位编号 + productUnitName?: string; // 产品单位名称 + productBarCode?: string; // 产品条码 + count: number; // 数量 + productPrice: number; // 产品单价 + totalPrice: number; // 总价 + stockCount?: number; // 库存数量 + remark?: string; // 备注 } /** 其它入库单分页查询参数 */ diff --git a/apps/web-antd/src/api/erp/stock/stock/index.ts b/apps/web-antd/src/api/erp/stock/stock/index.ts index 57d4ec22946e8bb2f723f8f9a95c5fd834cb0a0c..3ef12dff6ba07eb671b56af1aae4ac71f60286f9 100644 --- a/apps/web-antd/src/api/erp/stock/stock/index.ts +++ b/apps/web-antd/src/api/erp/stock/stock/index.ts @@ -2,7 +2,7 @@ import type { PageParam, PageResult } from '@vben/request'; import { requestClient } from '#/api/request'; -namespace ErpStockApi { +export namespace ErpStockApi { /** 产品库存信息 */ export interface Stock { id?: number; // 编号 @@ -54,9 +54,13 @@ export function getStockByProductAndWarehouse( /** * 获得产品库存数量 */ -export function getStockCount(productId: number) { +export function getStockCount(productId: number, warehouseId?: number) { + const params: any = { productId }; + if (warehouseId !== undefined) { + params.warehouseId = warehouseId; + } return requestClient.get('/erp/stock/get-count', { - params: { productId }, + params, }); } diff --git a/apps/web-antd/src/views/erp/stock/in/data.ts b/apps/web-antd/src/views/erp/stock/in/data.ts new file mode 100644 index 0000000000000000000000000000000000000000..68b667fc94835d327249d3a30acb0e0333e512b7 --- /dev/null +++ b/apps/web-antd/src/views/erp/stock/in/data.ts @@ -0,0 +1,297 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { createRequiredValidation } from '#/adapter/vxe-table'; +import { getSupplierSimpleList } from '#/api/erp/purchase/supplier'; +import { getSimpleUserList } from '#/api/system/user'; +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: { + placeholder: '系统自动生成', + disabled: true, + }, + fieldName: 'no', + label: '入库单号', + }, + { + component: 'ApiSelect', + componentProps: { + placeholder: '请选择供应商', + allowClear: true, + showSearch: true, + api: getSupplierSimpleList, + fieldNames: { + label: 'name', + value: 'id', + }, + }, + fieldName: 'supplierId', + label: '供应商', + }, + { + component: 'DatePicker', + componentProps: { + placeholder: '选择入库时间', + showTime: true, + format: 'YYYY-MM-DD HH:mm:ss', + valueFormat: 'x', + style: { width: '100%' }, + }, + fieldName: 'inTime', + label: '入库时间', + rules: 'required', + }, + { + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + autoSize: { minRows: 2, maxRows: 4 }, + class: 'w-full', + }, + fieldName: 'remark', + label: '备注', + formItemClass: 'col-span-3', + }, + { + component: 'FileUpload', + componentProps: { + maxNumber: 1, + maxSize: 10, + accept: [ + 'pdf', + 'doc', + 'docx', + 'xls', + 'xlsx', + 'txt', + 'jpg', + 'jpeg', + 'png', + ], + showDescription: true, + }, + fieldName: 'fileUrl', + label: '附件', + formItemClass: 'col-span-3', + }, + { + fieldName: 'product', + label: '产品清单', + component: 'Input', + formItemClass: 'col-span-3', + }, + ]; +} + +/** 入库产品清单表格列定义 */ +export function useStockInItemTableColumns( + isValidating?: any, +): VxeTableGridOptions['columns'] { + return [ + { type: 'seq', title: '序号', minWidth: 50, fixed: 'left' }, + { + field: 'warehouseId', + title: '仓库名称', + minWidth: 150, + slots: { default: 'warehouseId' }, + className: createRequiredValidation(isValidating, 'warehouseId'), + }, + { + field: 'productId', + title: '产品名称', + minWidth: 200, + slots: { default: 'productId' }, + className: createRequiredValidation(isValidating, 'productId'), + }, + { + field: 'stockCount', + title: '库存', + minWidth: 100, + }, + { + field: 'productBarCode', + title: '条码', + minWidth: 120, + }, + { + field: 'productUnitName', + title: '单位', + minWidth: 80, + }, + { + field: 'count', + title: '数量', + minWidth: 120, + slots: { default: 'count' }, + className: createRequiredValidation(isValidating, 'count'), + }, + { + field: 'productPrice', + title: '产品单价', + minWidth: 120, + slots: { default: 'productPrice' }, + }, + { + field: 'totalPrice', + title: '金额', + minWidth: 120, + formatter: 'formatAmount2', + }, + { + field: 'remark', + title: '备注', + minWidth: 150, + slots: { default: 'remark' }, + }, + { + title: '操作', + width: 50, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'no', + label: '入库单号', + component: 'Input', + componentProps: { + placeholder: '请输入入库单号', + allowClear: true, + }, + }, + { + fieldName: 'supplierId', + label: '供应商', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择供应商', + allowClear: true, + showSearch: true, + api: getSupplierSimpleList, + labelField: 'name', + valueField: 'id', + filterOption: false, + }, + }, + { + fieldName: 'inTime', + label: '入库时间', + component: 'RangePicker', + componentProps: { + placeholder: ['开始日期', '结束日期'], + showTime: true, + format: 'YYYY-MM-DD HH:mm:ss', + valueFormat: 'YYYY-MM-DD HH:mm:ss', + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + placeholder: '请选择状态', + allowClear: true, + options: getDictOptions(DICT_TYPE.ERP_AUDIT_STATUS, 'number'), + }, + }, + { + fieldName: 'remark', + label: '备注', + component: 'Input', + componentProps: { + placeholder: '请输入备注', + allowClear: true, + }, + }, + { + fieldName: 'creator', + label: '创建人', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择创建人', + allowClear: true, + showSearch: true, + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + filterOption: false, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + type: 'checkbox', + width: 50, + fixed: 'left', + }, + { + field: 'no', + title: '入库单号', + minWidth: 180, + }, + { + field: 'productNames', + title: '产品信息', + minWidth: 200, + showOverflow: 'tooltip', + }, + { + field: 'supplierName', + title: '供应商', + minWidth: 120, + }, + { + field: 'inTime', + title: '入库时间', + minWidth: 180, + cellRender: { + name: 'CellDateTime', + }, + }, + { + field: 'creatorName', + title: '创建人', + minWidth: 100, + }, + { + field: 'status', + title: '状态', + minWidth: 90, + fixed: 'right', + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.ERP_AUDIT_STATUS }, + }, + }, + { + title: '操作', + width: 300, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-antd/src/views/erp/stock/in/index.vue b/apps/web-antd/src/views/erp/stock/in/index.vue index ae06852cbda0ca6840eb69181a1e2662ed8b9465..55891fa957f955cf73939570012d7f1d8848a17e 100644 --- a/apps/web-antd/src/views/erp/stock/in/index.vue +++ b/apps/web-antd/src/views/erp/stock/in/index.vue @@ -1,34 +1,220 @@ diff --git a/apps/web-antd/src/views/erp/stock/in/modules/StockInItemForm.vue b/apps/web-antd/src/views/erp/stock/in/modules/StockInItemForm.vue new file mode 100644 index 0000000000000000000000000000000000000000..c1275a33cb25782746fb04eb470e5a09753766ec --- /dev/null +++ b/apps/web-antd/src/views/erp/stock/in/modules/StockInItemForm.vue @@ -0,0 +1,362 @@ + + + + + diff --git a/apps/web-antd/src/views/erp/stock/in/modules/form.vue b/apps/web-antd/src/views/erp/stock/in/modules/form.vue new file mode 100644 index 0000000000000000000000000000000000000000..fffca0978d272ec564a3177eed6ab07a93f5e74e --- /dev/null +++ b/apps/web-antd/src/views/erp/stock/in/modules/form.vue @@ -0,0 +1,202 @@ + + + diff --git a/apps/web-antd/src/views/erp/stock/record/data.ts b/apps/web-antd/src/views/erp/stock/record/data.ts new file mode 100644 index 0000000000000000000000000000000000000000..3f80549218e291e3696ebb3379d470cff958ba35 --- /dev/null +++ b/apps/web-antd/src/views/erp/stock/record/data.ts @@ -0,0 +1,146 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { getProductSimpleList } from '#/api/erp/product/product'; +import { getWarehouseSimpleList } from '#/api/erp/stock/warehouse'; +import { DICT_TYPE, getDictOptions } from '#/utils'; + +/** 搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'productId', + label: '产品', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择产品', + allowClear: true, + showSearch: true, + api: getProductSimpleList, + labelField: 'name', + valueField: 'id', + filterOption: false, + }, + }, + { + fieldName: 'warehouseId', + label: '仓库', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择仓库', + allowClear: true, + showSearch: true, + api: getWarehouseSimpleList, + labelField: 'name', + valueField: 'id', + filterOption: false, + }, + }, + { + fieldName: 'bizType', + label: '类型', + component: 'Select', + componentProps: { + placeholder: '请选择类型', + allowClear: true, + options: getDictOptions(DICT_TYPE.ERP_STOCK_RECORD_BIZ_TYPE, 'number'), + }, + }, + { + fieldName: 'bizNo', + label: '业务单号', + component: 'Input', + componentProps: { + placeholder: '请输入业务单号', + allowClear: true, + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + placeholder: ['开始日期', '结束日期'], + showTime: true, + format: 'YYYY-MM-DD HH:mm:ss', + valueFormat: 'YYYY-MM-DD HH:mm:ss', + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'productName', + title: '产品名称', + minWidth: 150, + }, + { + field: 'categoryName', + title: '产品分类', + width: 120, + }, + { + field: 'unitName', + title: '产品单位', + width: 100, + }, + { + field: 'warehouseName', + title: '仓库', + width: 120, + }, + { + field: 'bizType', + title: '类型', + width: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.ERP_STOCK_RECORD_BIZ_TYPE }, + }, + }, + { + field: 'bizNo', + title: '出入库单号', + width: 200, + showOverflow: 'tooltip', + }, + { + field: 'createTime', + title: '出入库日期', + width: 180, + cellRender: { + name: 'CellDateTime', + }, + }, + { + field: 'count', + title: '出入库数量', + width: 120, + cellRender: { + name: 'CellAmount', + props: { + digits: 2, + }, + }, + }, + { + field: 'totalCount', + title: '库存量', + width: 100, + cellRender: { + name: 'CellAmount', + props: { + digits: 2, + }, + }, + }, + { + field: 'creatorName', + title: '操作人', + width: 100, + }, + ]; +} \ No newline at end of file diff --git a/apps/web-antd/src/views/erp/stock/record/index.vue b/apps/web-antd/src/views/erp/stock/record/index.vue index 33dddb83345ef1653f46453e91cd848ac5e72aca..a05f403eb4bf8f74c715a99dfe0c3ca48be4543c 100644 --- a/apps/web-antd/src/views/erp/stock/record/index.vue +++ b/apps/web-antd/src/views/erp/stock/record/index.vue @@ -1,32 +1,78 @@ diff --git a/apps/web-antd/src/views/erp/stock/stock/data.ts b/apps/web-antd/src/views/erp/stock/stock/data.ts new file mode 100644 index 0000000000000000000000000000000000000000..4d0e52b7eea709811a452f3ea46345d7060b5125 --- /dev/null +++ b/apps/web-antd/src/views/erp/stock/stock/data.ts @@ -0,0 +1,76 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { getProductSimpleList } from '#/api/erp/product/product'; +import { getWarehouseSimpleList } from '#/api/erp/stock/warehouse'; + +/** 搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'productId', + label: '产品', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择产品', + allowClear: true, + showSearch: true, + api: getProductSimpleList, + labelField: 'name', + valueField: 'id', + filterOption: false, + }, + }, + { + fieldName: 'warehouseId', + label: '仓库', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择仓库', + allowClear: true, + showSearch: true, + api: getWarehouseSimpleList, + labelField: 'name', + valueField: 'id', + filterOption: false, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'productName', + title: '产品名称', + minWidth: 150, + }, + { + field: 'unitName', + title: '产品单位', + minWidth: 100, + }, + { + field: 'categoryName', + title: '产品分类', + minWidth: 120, + }, + { + field: 'count', + title: '库存量', + minWidth: 100, + cellRender: { + name: 'CellAmount', + props: { + digits: 2, + }, + }, + }, + { + field: 'warehouseName', + title: '仓库', + minWidth: 120, + }, + ]; +} diff --git a/apps/web-antd/src/views/erp/stock/stock/index.vue b/apps/web-antd/src/views/erp/stock/stock/index.vue index 04305f8858d6b86e2ac0ab42801beac3d3a5ec66..8cd51e59c5161024018944d4e4ae614f07309887 100644 --- a/apps/web-antd/src/views/erp/stock/stock/index.vue +++ b/apps/web-antd/src/views/erp/stock/stock/index.vue @@ -1,32 +1,78 @@ diff --git a/packages/effects/plugins/src/vxe-table/index.ts b/packages/effects/plugins/src/vxe-table/index.ts index 70f4cfa5c0b2391268f4bf17d4dd685963933e8e..8cc12bef799fb31586c085f9b1775a7f697ec72e 100644 --- a/packages/effects/plugins/src/vxe-table/index.ts +++ b/packages/effects/plugins/src/vxe-table/index.ts @@ -1,8 +1,9 @@ export { AsyncComponents, setupVbenVxeTable } from './init'; export type { VxeTableGridOptions } from './types'; export * from './use-vxe-grid'; - export { default as VbenVxeGrid } from './use-vxe-grid.vue'; + +export * from './validation'; export type { VxeGridListeners, VxeGridProps, diff --git a/packages/effects/plugins/src/vxe-table/style.css b/packages/effects/plugins/src/vxe-table/style.css index 5b47fa2cfce1d4a3b90fe97adb3e3475d29d8ce7..152fcf219e4cd512ae7c250c56d8b35e10df7512 100644 --- a/packages/effects/plugins/src/vxe-table/style.css +++ b/packages/effects/plugins/src/vxe-table/style.css @@ -115,3 +115,29 @@ .vxe-grid--layout-body-content-wrapper { overflow: hidden; } + +/* 必填字段错误样式 */ +.vxe-table .required-field-error::after { + position: absolute; + top: -1px; + right: -1px; + z-index: 10; + padding: 1px 4px; + font-size: 10px; + line-height: 1; + color: white; + content: '必填'; + background-color: #ff4d4f; + border-radius: 0 0 0 4px; +} + +/* 必填字段内的输入框样式 */ +.vxe-table .required-field-error .ant-select, +.vxe-table .required-field-error .ant-input-number, +.vxe-table .required-field-error .ant-input { + border-color: #ff4d4f !important; +} + +.vxe-table .required-field-error .ant-select .ant-select-selector { + border-color: #ff4d4f !important; +} diff --git a/packages/effects/plugins/src/vxe-table/validation.ts b/packages/effects/plugins/src/vxe-table/validation.ts new file mode 100644 index 0000000000000000000000000000000000000000..487f24107df5174632aef9e778d1e97acfc9156b --- /dev/null +++ b/packages/effects/plugins/src/vxe-table/validation.ts @@ -0,0 +1,61 @@ +/** + * 创建验证类名的工具函数 + * @param isValidating 验证状态 + * @param fieldName 字段名 + * @param validationRules 验证规则,可以是字符串或自定义函数 + * @returns 返回 className 函数 + */ +function createValidationClassName( + isValidating: any, + fieldName: string, + validationRules: ((row: any) => boolean) | string, +) { + return ({ row }: { row: any }) => { + if (!isValidating?.value) return ''; + + let isValid = true; + if (typeof validationRules === 'string') { + // 处理简单的验证规则 + if (validationRules === 'required') { + isValid = + fieldName === 'count' + ? row[fieldName] && row[fieldName] > 0 + : !!row[fieldName]; + } + } else if (typeof validationRules === 'function') { + // 处理自定义验证函数 + isValid = validationRules(row); + } + + return isValid ? '' : 'required-field-error'; + }; +} + +/** + * 创建必填字段验证 + * @param isValidating 验证状态 + * @param fieldName 字段名 + * @returns 返回 className 函数 + */ +function createRequiredValidation(isValidating: any, fieldName: string) { + return createValidationClassName(isValidating, fieldName, 'required'); +} + +/** + * 创建自定义验证 + * @param isValidating 验证状态 + * @param validationFn 自定义验证函数 + * @returns 返回 className 函数 + */ +function createCustomValidation( + isValidating: any, + validationFn: (row: any) => boolean, +) { + return createValidationClassName(isValidating, '', validationFn); +} + +export { + createCustomValidation, + createRequiredValidation, + createValidationClassName, +};