diff --git a/apps/web-antd/src/api/mes/qc/defectrecord/index.ts b/apps/web-antd/src/api/mes/qc/defectrecord/index.ts new file mode 100644 index 000000000..0d5182b15 --- /dev/null +++ b/apps/web-antd/src/api/mes/qc/defectrecord/index.ts @@ -0,0 +1,42 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MesQcDefectRecordApi { + /** MES 质检缺陷记录 */ + export interface DefectRecord { + id?: number; // 编号 + qcType?: number; // 检验类型 + qcId?: number; // 检验单 ID + lineId?: number; // 检验行 ID + name?: string; // 缺陷描述 + level?: number; // 缺陷等级 + quantity?: number; // 缺陷数量 + remark?: string; // 备注 + } +} + +/** 查询质检缺陷记录分页 */ +export function getDefectRecordPage( + params: PageParam & { lineId?: number; qcId?: number; qcType?: number; }, +) { + return requestClient.get>( + '/mes/qc/defect-record/page', + { params }, + ); +} + +/** 新增质检缺陷记录 */ +export function createDefectRecord(data: MesQcDefectRecordApi.DefectRecord) { + return requestClient.post('/mes/qc/defect-record/create', data); +} + +/** 修改质检缺陷记录 */ +export function updateDefectRecord(data: MesQcDefectRecordApi.DefectRecord) { + return requestClient.put('/mes/qc/defect-record/update', data); +} + +/** 删除质检缺陷记录 */ +export function deleteDefectRecord(id: number) { + return requestClient.delete(`/mes/qc/defect-record/delete?id=${id}`); +} diff --git a/apps/web-antd/src/api/mes/qc/indicatorresult/index.ts b/apps/web-antd/src/api/mes/qc/indicatorresult/index.ts new file mode 100644 index 000000000..dfa8a7f93 --- /dev/null +++ b/apps/web-antd/src/api/mes/qc/indicatorresult/index.ts @@ -0,0 +1,73 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MesQcIndicatorResultApi { + /** MES 检验结果明细 */ + export interface IndicatorResultDetail { + id?: number; // 编号 + resultId?: number; // 关联检验结果 ID + indicatorId?: number; // 检测指标 ID + value?: string; // 检测值(统一存为字符串) + valueNumber?: number; // UI 数值绑定(提交前转字符串) + remark?: string; // 备注 + // 关联查询字段(来自 indicator) + indicatorName?: string; // 检测指标名称 + valueType?: number; // 质检值类型 + valueSpecification?: string; // 值属性 + } + + /** MES 检验结果 */ + export interface IndicatorResult { + id?: number; // 编号 + code?: string; // 样品编号 + qcId?: number; // 关联质检单 ID + qcType?: number; // 质检类型 + itemId?: number; // 产品物料 ID + sn?: string; // 物资 SN + remark?: string; // 备注 + createTime?: Date; // 创建时间 + items?: IndicatorResultDetail[]; // 检验结果明细列表 + } +} + +/** 查询检验结果分页 */ +export function getIndicatorResultPage( + params: PageParam & { qcId?: number; qcType?: number }, +) { + return requestClient.get>( + '/mes/qc/indicator-result/page', + { params }, + ); +} + +/** 查询检验结果明细(含检测项模板):编辑传 id,新增不传 */ +export function getIndicatorResultDetail( + qcId: number, + qcType: number, + id?: number, +) { + return requestClient.get( + '/mes/qc/indicator-result/get-detail', + { params: { id, qcId, qcType } }, + ); +} + +/** 新增检验结果 */ +export function createIndicatorResult( + data: MesQcIndicatorResultApi.IndicatorResult, +) { + return requestClient.post('/mes/qc/indicator-result/create', data); +} + +/** 修改检验结果 */ +export function updateIndicatorResult( + data: MesQcIndicatorResultApi.IndicatorResult, +) { + return requestClient.put('/mes/qc/indicator-result/update', data); +} + +/** 删除检验结果 */ +export function deleteIndicatorResult(id: number) { + return requestClient.delete(`/mes/qc/indicator-result/delete?id=${id}`); +} diff --git a/apps/web-antd/src/api/mes/qc/iqc/index.ts b/apps/web-antd/src/api/mes/qc/iqc/index.ts new file mode 100644 index 000000000..cdb04cb6a --- /dev/null +++ b/apps/web-antd/src/api/mes/qc/iqc/index.ts @@ -0,0 +1,80 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MesQcIqcApi { + /** MES 来料检验单 */ + export interface Iqc { + id?: number; // 编号 + code?: string; // 检验单编号 + name?: string; // 检验单名称 + templateId?: number; // 检验模板 ID + sourceDocType?: number; // 来源单据类型 + sourceDocId?: number; // 来源单据 ID + sourceLineId?: number; // 来源单据行 ID + sourceDocCode?: string; // 来源单据编号(关联查询) + vendorId?: number; // 供应商 ID + vendorNickname?: string; // 供应商简称(关联查询) + vendorBatch?: string; // 供应商批次号 + itemId?: number; // 产品物料 ID + itemCode?: string; // 产品物料编码(关联查询) + itemName?: string; // 产品物料名称(关联查询) + itemSpecification?: string; // 规格型号(关联查询) + unitName?: string; // 单位名称(关联查询) + receivedQuantity?: number; // 本次接收数量 + checkQuantity?: number; // 本次检测数量 + qualifiedQuantity?: number; // 合格品数量 + unqualifiedQuantity?: number; // 不合格品数量 + criticalRate?: number; // 致命缺陷率(%) + majorRate?: number; // 严重缺陷率(%) + minorRate?: number; // 轻微缺陷率(%) + criticalQuantity?: number; // 致命缺陷数量 + majorQuantity?: number; // 严重缺陷数量 + minorQuantity?: number; // 轻微缺陷数量 + checkResult?: number; // 检测结果 + receiveDate?: number; // 来料日期 + inspectDate?: number; // 检测日期 + inspector?: string; // 检测人员(昵称) + inspectorUserId?: number; // 检测人员 ID + inspectorNickname?: string; // 检测人员昵称(关联查询) + status?: number; // 状态 + remark?: string; // 备注 + } +} + +/** 查询来料检验单分页 */ +export function getIqcPage(params: PageParam) { + return requestClient.get>('/mes/qc/iqc/page', { + params, + }); +} + +/** 查询来料检验单详情 */ +export function getIqc(id: number) { + return requestClient.get(`/mes/qc/iqc/get?id=${id}`); +} + +/** 新增来料检验单 */ +export function createIqc(data: MesQcIqcApi.Iqc) { + return requestClient.post('/mes/qc/iqc/create', data); +} + +/** 修改来料检验单 */ +export function updateIqc(data: MesQcIqcApi.Iqc) { + return requestClient.put('/mes/qc/iqc/update', data); +} + +/** 完成来料检验单 */ +export function finishIqc(id: number) { + return requestClient.put(`/mes/qc/iqc/finish?id=${id}`); +} + +/** 删除来料检验单 */ +export function deleteIqc(id: number) { + return requestClient.delete(`/mes/qc/iqc/delete?id=${id}`); +} + +/** 导出来料检验单 */ +export function exportIqc(params: any) { + return requestClient.download('/mes/qc/iqc/export-excel', { params }); +} diff --git a/apps/web-antd/src/api/mes/qc/iqc/line/index.ts b/apps/web-antd/src/api/mes/qc/iqc/line/index.ts new file mode 100644 index 000000000..dc37aa99b --- /dev/null +++ b/apps/web-antd/src/api/mes/qc/iqc/line/index.ts @@ -0,0 +1,41 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MesQcIqcLineApi { + /** MES 来料检验单行 */ + export interface IqcLine { + id?: number; // 编号 + iqcId?: number; // 来料检验单 ID + indicatorId?: number; // 检测指标 ID + indicatorCode?: string; // 检测指标编码(关联查询) + indicatorName?: string; // 检测指标名称(关联查询) + indicatorType?: number; // 检测指标类型(关联查询) + tool?: string; // 检测工具 + checkMethod?: string; // 检测方法 + standardValue?: number; // 标准值 + unitMeasureId?: number; // 计量单位 ID + unitMeasureName?: string; // 计量单位名称(关联查询) + maxThreshold?: number; // 误差上限 + minThreshold?: number; // 误差下限 + criticalQuantity?: number; // 致命缺陷数量 + majorQuantity?: number; // 严重缺陷数量 + minorQuantity?: number; // 轻微缺陷数量 + remark?: string; // 备注 + } +} + +/** 查询来料检验单行分页 */ +export function getIqcLinePage(params: PageParam & { iqcId?: number }) { + return requestClient.get>( + '/mes/qc/iqc/line/page', + { params }, + ); +} + +/** 查询来料检验单行详情 */ +export function getIqcLine(id: number) { + return requestClient.get( + `/mes/qc/iqc/line/get?id=${id}`, + ); +} diff --git a/apps/web-antd/src/router/routes/modules/mes.ts b/apps/web-antd/src/router/routes/modules/mes.ts new file mode 100644 index 000000000..05af60e29 --- /dev/null +++ b/apps/web-antd/src/router/routes/modules/mes.ts @@ -0,0 +1,46 @@ +import type { RouteRecordRaw } from 'vue-router'; + +const routes: RouteRecordRaw[] = [ + { + path: '/mes', + name: 'MesCenter', + meta: { + title: 'MES 制造执行', + icon: 'lucide:factory', + keepAlive: true, + hideInMenu: true, + }, + children: [ + { + path: 'wm/warehouse/location', + name: 'MesWmLocation', + meta: { + title: '库区设置', + activePath: '/mes/wm/warehouse', + }, + component: () => + import('#/views/mes/wm/warehouse/location/index.vue'), + }, + { + path: 'wm/warehouse/area', + name: 'MesWmArea', + meta: { + title: '库位设置', + activePath: '/mes/wm/warehouse', + }, + component: () => import('#/views/mes/wm/warehouse/area/index.vue'), + }, + { + path: 'wm/barcode/config', + name: 'MesWmBarcodeConfig', + meta: { + title: '条码配置', + activePath: '/mes/wm/barcode', + }, + component: () => import('#/views/mes/wm/barcode/config/index.vue'), + }, + ], + }, +]; + +export default routes; diff --git a/apps/web-antd/src/views/mes/wm/warehouse/area/data.ts b/apps/web-antd/src/views/mes/wm/warehouse/area/data.ts new file mode 100644 index 000000000..b18fb4981 --- /dev/null +++ b/apps/web-antd/src/views/mes/wm/warehouse/area/data.ts @@ -0,0 +1,339 @@ +import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { MesWmWarehouseAreaApi } from '#/api/mes/wm/warehouse/area'; + +import { h } from 'vue'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { Button } from 'ant-design-vue'; + +import { z } from '#/adapter/form'; +import { generateAutoCode } from '#/api/mes/md/autocode/record'; +import { getWarehouseSimpleList } from '#/api/mes/wm/warehouse'; +import { getWarehouseLocationSimpleList } from '#/api/mes/wm/warehouse/location'; +import { MesAutoCodeRuleCode } from '#/views/mes/utils/constants'; + +/** 表单类型 */ +export type FormType = 'create' | 'detail' | 'update'; + +/** 新增/修改的表单 */ +export function useFormSchema( + formType: FormType, + formApi?: VbenFormApi, +): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { triggerFields: [''], show: () => false }, + }, + { + fieldName: 'warehouseId', + label: '所属仓库', + component: 'ApiSelect', + componentProps: { + allowClear: true, + api: getWarehouseSimpleList, + labelField: 'name', + valueField: 'id', + placeholder: '请选择仓库', + // 仓库变更时清空所属库区 + onChange: () => formApi?.setFieldValue('locationId', undefined), + }, + }, + { + fieldName: 'locationId', + label: '所属库区', + component: 'ApiSelect', + dependencies: { + triggerFields: ['warehouseId'], + componentProps: (values) => ({ + allowClear: true, + api: () => + getWarehouseLocationSimpleList(values.warehouseId as number), + // 改变 warehouseId 时强制刷新选项 + params: { warehouseId: values.warehouseId }, + disabled: !values.warehouseId, + labelField: 'name', + valueField: 'id', + placeholder: '请选择库区', + }), + }, + rules: 'required', + }, + { + fieldName: 'code', + label: '库位编码', + component: 'Input', + componentProps: { + placeholder: '请输入库位编码', + }, + rules: 'required', + suffix: + formType === 'detail' + ? undefined + : () => + h( + Button, + { + type: 'default', + onClick: async () => { + try { + const code = await generateAutoCode( + MesAutoCodeRuleCode.WM_AREA_CODE, + ); + await formApi?.setFieldValue('code', code); + } catch (error) { + console.error(error); + } + }, + }, + { default: () => '自动生成' }, + ), + }, + { + fieldName: 'name', + label: '库位名称', + component: 'Input', + componentProps: { + placeholder: '请输入库位名称', + }, + rules: 'required', + }, + { + fieldName: 'area', + label: '面积(㎡)', + component: 'InputNumber', + componentProps: { + class: '!w-full', + min: 0, + precision: 2, + placeholder: '请输入面积', + }, + }, + { + fieldName: 'maxLoad', + label: '最大载重', + component: 'InputNumber', + componentProps: { + class: '!w-full', + min: 0, + precision: 2, + placeholder: '请输入最大载重', + }, + }, + { + fieldName: 'positionX', + label: '位置 X', + component: 'InputNumber', + componentProps: { + class: '!w-full', + min: 0, + placeholder: '请输入位置 X', + }, + }, + { + fieldName: 'positionY', + label: '位置 Y', + component: 'InputNumber', + componentProps: { + class: '!w-full', + min: 0, + placeholder: '请输入位置 Y', + }, + }, + { + fieldName: 'positionZ', + label: '位置 Z', + component: 'InputNumber', + componentProps: { + class: '!w-full', + min: 0, + placeholder: '请输入位置 Z', + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + placeholder: '请选择', + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + fieldName: 'frozen', + label: '是否冻结', + component: 'Switch', + componentProps: { + checkedChildren: '是', + unCheckedChildren: '否', + }, + rules: z.boolean().default(false), + }, + { + fieldName: 'allowItemMixing', + label: '允许物料混放', + component: 'Switch', + componentProps: { + checkedChildren: '是', + unCheckedChildren: '否', + }, + rules: z.boolean().default(true), + }, + { + fieldName: 'allowBatchMixing', + label: '允许批次混放', + component: 'Switch', + componentProps: { + checkedChildren: '是', + unCheckedChildren: '否', + }, + rules: z.boolean().default(true), + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + formItemClass: 'col-span-3', + componentProps: { + placeholder: '请输入备注', + rows: 3, + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'code', + label: '库位编码', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入库位编码', + }, + }, + { + fieldName: 'name', + label: '库位名称', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入库位名称', + }, + }, + { + fieldName: 'positionX', + label: '位置 X', + component: 'InputNumber', + componentProps: { + class: '!w-full', + min: 0, + placeholder: '请输入位置 X', + }, + }, + { + fieldName: 'positionY', + label: '位置 Y', + component: 'InputNumber', + componentProps: { + class: '!w-full', + min: 0, + placeholder: '请输入位置 Y', + }, + }, + { + fieldName: 'positionZ', + label: '位置 Z', + component: 'InputNumber', + componentProps: { + class: '!w-full', + min: 0, + placeholder: '请输入位置 Z', + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'code', + title: '库位编码', + minWidth: 140, + slots: { default: 'code' }, + }, + { + field: 'name', + title: '库位名称', + minWidth: 160, + }, + { + field: 'area', + title: '面积(㎡)', + width: 100, + }, + { + field: 'maxLoad', + title: '最大载重', + width: 100, + }, + { + field: 'positionX', + title: '位置 X', + width: 80, + }, + { + field: 'positionY', + title: '位置 Y', + width: 80, + }, + { + field: 'positionZ', + title: '位置 Z', + width: 80, + }, + { + field: 'status', + title: '状态', + width: 90, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'frozen', + title: '冻结', + width: 90, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, + }, + }, + { + field: 'remark', + title: '备注', + minWidth: 180, + }, + { + field: 'createTime', + title: '创建时间', + width: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 200, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-antd/src/views/mes/wm/warehouse/area/index.vue b/apps/web-antd/src/views/mes/wm/warehouse/area/index.vue new file mode 100644 index 000000000..f30a2211c --- /dev/null +++ b/apps/web-antd/src/views/mes/wm/warehouse/area/index.vue @@ -0,0 +1,219 @@ + + + diff --git a/apps/web-antd/src/views/mes/wm/warehouse/area/modules/form.vue b/apps/web-antd/src/views/mes/wm/warehouse/area/modules/form.vue new file mode 100644 index 000000000..19dc5a60d --- /dev/null +++ b/apps/web-antd/src/views/mes/wm/warehouse/area/modules/form.vue @@ -0,0 +1,149 @@ + + + diff --git a/apps/web-antd/src/views/mes/wm/warehouse/components/index.ts b/apps/web-antd/src/views/mes/wm/warehouse/components/index.ts new file mode 100644 index 000000000..d2981238b --- /dev/null +++ b/apps/web-antd/src/views/mes/wm/warehouse/components/index.ts @@ -0,0 +1,3 @@ +export { default as WmWarehouseAreaSelect } from './wm-warehouse-area-select.vue'; +export { default as WmWarehouseLocationSelect } from './wm-warehouse-location-select.vue'; +export { default as WmWarehouseSelect } from './wm-warehouse-select.vue'; diff --git a/apps/web-antd/src/views/mes/wm/warehouse/components/wm-warehouse-area-select.vue b/apps/web-antd/src/views/mes/wm/warehouse/components/wm-warehouse-area-select.vue new file mode 100644 index 000000000..1c482856a --- /dev/null +++ b/apps/web-antd/src/views/mes/wm/warehouse/components/wm-warehouse-area-select.vue @@ -0,0 +1,114 @@ + + + diff --git a/apps/web-antd/src/views/mes/wm/warehouse/components/wm-warehouse-location-select.vue b/apps/web-antd/src/views/mes/wm/warehouse/components/wm-warehouse-location-select.vue new file mode 100644 index 000000000..1852823eb --- /dev/null +++ b/apps/web-antd/src/views/mes/wm/warehouse/components/wm-warehouse-location-select.vue @@ -0,0 +1,113 @@ + + + diff --git a/apps/web-antd/src/views/mes/wm/warehouse/components/wm-warehouse-select.vue b/apps/web-antd/src/views/mes/wm/warehouse/components/wm-warehouse-select.vue new file mode 100644 index 000000000..e680c7978 --- /dev/null +++ b/apps/web-antd/src/views/mes/wm/warehouse/components/wm-warehouse-select.vue @@ -0,0 +1,110 @@ + + + diff --git a/apps/web-antd/src/views/mes/wm/warehouse/data.ts b/apps/web-antd/src/views/mes/wm/warehouse/data.ts new file mode 100644 index 000000000..045e21177 --- /dev/null +++ b/apps/web-antd/src/views/mes/wm/warehouse/data.ts @@ -0,0 +1,226 @@ +import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { MesWmWarehouseApi } from '#/api/mes/wm/warehouse'; +import type { SystemUserApi } from '#/api/system/user'; + +import { h } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; + +import { Button } from 'ant-design-vue'; + +import { z } from '#/adapter/form'; +import { generateAutoCode } from '#/api/mes/md/autocode/record'; +import { getSimpleUserList } from '#/api/system/user'; +import { MesAutoCodeRuleCode } from '#/views/mes/utils/constants'; + +/** 表单类型 */ +export type FormType = 'create' | 'detail' | 'update'; + +/** 关联数据 */ +let userList: SystemUserApi.User[] = []; +getSimpleUserList().then((data) => (userList = data)); + +/** 新增/修改的表单 */ +export function useFormSchema( + formType: FormType, + formApi?: VbenFormApi, +): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'code', + label: '仓库编码', + component: 'Input', + componentProps: { + placeholder: '请输入仓库编码', + }, + rules: 'required', + suffix: + formType === 'detail' + ? undefined + : () => + h( + Button, + { + type: 'default', + onClick: async () => { + try { + const code = await generateAutoCode( + MesAutoCodeRuleCode.WM_WAREHOUSE_CODE, + ); + await formApi?.setFieldValue('code', code); + } catch (error) { + console.error(error); + } + }, + }, + { default: () => '自动生成' }, + ), + }, + { + fieldName: 'name', + label: '仓库名称', + component: 'Input', + componentProps: { + placeholder: '请输入仓库名称', + }, + rules: 'required', + }, + { + fieldName: 'chargeUserId', + label: '负责人', + component: 'ApiSelect', + componentProps: { + allowClear: true, + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + placeholder: '请选择负责人', + }, + }, + { + fieldName: 'address', + label: '仓库地址', + component: 'Input', + componentProps: { + placeholder: '请输入仓库地址', + }, + }, + { + fieldName: 'area', + label: '面积(㎡)', + component: 'InputNumber', + componentProps: { + class: '!w-full', + min: 0, + precision: 2, + placeholder: '请输入面积', + }, + }, + { + fieldName: 'frozen', + label: '是否冻结', + component: 'Switch', + componentProps: { + checkedChildren: '是', + unCheckedChildren: '否', + }, + rules: z.boolean().default(false), + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + formItemClass: 'col-span-3', + componentProps: { + placeholder: '请输入备注', + rows: 3, + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'code', + label: '仓库编码', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入仓库编码', + }, + }, + { + fieldName: 'name', + label: '仓库名称', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入仓库名称', + }, + }, + { + fieldName: 'frozen', + label: '是否冻结', + component: 'Select', + componentProps: { + allowClear: true, + options: [ + { label: '是', value: true }, + { label: '否', value: false }, + ], + placeholder: '请选择', + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'code', + title: '仓库编码', + minWidth: 140, + slots: { default: 'code' }, + }, + { + field: 'name', + title: '仓库名称', + minWidth: 160, + }, + { + field: 'address', + title: '仓库地址', + minWidth: 180, + }, + { + field: 'area', + title: '面积(㎡)', + width: 110, + }, + { + field: 'chargeUserId', + title: '负责人', + width: 120, + formatter: ({ cellValue }) => + userList.find((user) => user.id === cellValue)?.nickname ?? '', + }, + { + field: 'frozen', + title: '冻结', + width: 90, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, + }, + }, + { + field: 'remark', + title: '备注', + minWidth: 180, + }, + { + field: 'createTime', + title: '创建时间', + width: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 220, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-antd/src/views/mes/wm/warehouse/index.vue b/apps/web-antd/src/views/mes/wm/warehouse/index.vue new file mode 100644 index 000000000..b96de2554 --- /dev/null +++ b/apps/web-antd/src/views/mes/wm/warehouse/index.vue @@ -0,0 +1,183 @@ + + + diff --git a/apps/web-antd/src/views/mes/wm/warehouse/location/data.ts b/apps/web-antd/src/views/mes/wm/warehouse/location/data.ts new file mode 100644 index 000000000..c75c0639c --- /dev/null +++ b/apps/web-antd/src/views/mes/wm/warehouse/location/data.ts @@ -0,0 +1,198 @@ +import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { MesWmWarehouseApi } from '#/api/mes/wm/warehouse'; +import type { MesWmWarehouseLocationApi } from '#/api/mes/wm/warehouse/location'; + +import { h } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; + +import { Button } from 'ant-design-vue'; + +import { z } from '#/adapter/form'; +import { generateAutoCode } from '#/api/mes/md/autocode/record'; +import { getWarehouseSimpleList } from '#/api/mes/wm/warehouse'; +import { MesAutoCodeRuleCode } from '#/views/mes/utils/constants'; + +/** 表单类型 */ +export type FormType = 'create' | 'detail' | 'update'; + +/** 关联数据 */ +let warehouseList: MesWmWarehouseApi.Warehouse[] = []; +getWarehouseSimpleList().then((data) => (warehouseList = data)); + +/** 新增/修改的表单 */ +export function useFormSchema( + formType: FormType, + formApi?: VbenFormApi, +): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { triggerFields: [''], show: () => false }, + }, + { + fieldName: 'code', + label: '库区编码', + component: 'Input', + componentProps: { + placeholder: '请输入库区编码', + }, + rules: 'required', + suffix: + formType === 'detail' + ? undefined + : () => + h( + Button, + { + type: 'default', + onClick: async () => { + try { + const code = await generateAutoCode( + MesAutoCodeRuleCode.WM_LOCATION_CODE, + ); + await formApi?.setFieldValue('code', code); + } catch (error) { + console.error(error); + } + }, + }, + { default: () => '自动生成' }, + ), + }, + { + fieldName: 'name', + label: '库区名称', + component: 'Input', + componentProps: { + placeholder: '请输入库区名称', + }, + rules: 'required', + }, + { + fieldName: 'warehouseId', + label: '所属仓库', + component: 'ApiSelect', + componentProps: { + allowClear: true, + api: getWarehouseSimpleList, + labelField: 'name', + valueField: 'id', + placeholder: '请选择仓库', + }, + rules: 'required', + }, + { + fieldName: 'area', + label: '面积(㎡)', + component: 'InputNumber', + componentProps: { + class: '!w-full', + min: 0, + precision: 2, + placeholder: '请输入面积', + }, + }, + { + fieldName: 'frozen', + label: '是否冻结', + component: 'Switch', + componentProps: { + checkedChildren: '是', + unCheckedChildren: '否', + }, + rules: z.boolean().default(false), + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + formItemClass: 'col-span-3', + componentProps: { + placeholder: '请输入备注', + rows: 3, + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'code', + label: '库区编码', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入库区编码', + }, + }, + { + fieldName: 'name', + label: '库区名称', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入库区名称', + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'code', + title: '库区编码', + minWidth: 140, + slots: { default: 'code' }, + }, + { + field: 'name', + title: '库区名称', + minWidth: 160, + }, + { + field: 'warehouseId', + title: '所属仓库', + minWidth: 140, + formatter: ({ cellValue }) => + warehouseList.find((w) => w.id === cellValue)?.name ?? '', + }, + { + field: 'area', + title: '面积(㎡)', + width: 110, + }, + { + field: 'frozen', + title: '冻结', + width: 90, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, + }, + }, + { + field: 'remark', + title: '备注', + minWidth: 180, + }, + { + field: 'createTime', + title: '创建时间', + width: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 240, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-antd/src/views/mes/wm/warehouse/location/index.vue b/apps/web-antd/src/views/mes/wm/warehouse/location/index.vue new file mode 100644 index 000000000..971284258 --- /dev/null +++ b/apps/web-antd/src/views/mes/wm/warehouse/location/index.vue @@ -0,0 +1,226 @@ + + + diff --git a/apps/web-antd/src/views/mes/wm/warehouse/location/modules/form.vue b/apps/web-antd/src/views/mes/wm/warehouse/location/modules/form.vue new file mode 100644 index 000000000..4d1daec3c --- /dev/null +++ b/apps/web-antd/src/views/mes/wm/warehouse/location/modules/form.vue @@ -0,0 +1,180 @@ + + + diff --git a/apps/web-antd/src/views/mes/wm/warehouse/modules/form.vue b/apps/web-antd/src/views/mes/wm/warehouse/modules/form.vue new file mode 100644 index 000000000..9de8bc39e --- /dev/null +++ b/apps/web-antd/src/views/mes/wm/warehouse/modules/form.vue @@ -0,0 +1,122 @@ + + + diff --git a/apps/web-ele/src/api/mes/qc/defectrecord/index.ts b/apps/web-ele/src/api/mes/qc/defectrecord/index.ts new file mode 100644 index 000000000..0d5182b15 --- /dev/null +++ b/apps/web-ele/src/api/mes/qc/defectrecord/index.ts @@ -0,0 +1,42 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MesQcDefectRecordApi { + /** MES 质检缺陷记录 */ + export interface DefectRecord { + id?: number; // 编号 + qcType?: number; // 检验类型 + qcId?: number; // 检验单 ID + lineId?: number; // 检验行 ID + name?: string; // 缺陷描述 + level?: number; // 缺陷等级 + quantity?: number; // 缺陷数量 + remark?: string; // 备注 + } +} + +/** 查询质检缺陷记录分页 */ +export function getDefectRecordPage( + params: PageParam & { lineId?: number; qcId?: number; qcType?: number; }, +) { + return requestClient.get>( + '/mes/qc/defect-record/page', + { params }, + ); +} + +/** 新增质检缺陷记录 */ +export function createDefectRecord(data: MesQcDefectRecordApi.DefectRecord) { + return requestClient.post('/mes/qc/defect-record/create', data); +} + +/** 修改质检缺陷记录 */ +export function updateDefectRecord(data: MesQcDefectRecordApi.DefectRecord) { + return requestClient.put('/mes/qc/defect-record/update', data); +} + +/** 删除质检缺陷记录 */ +export function deleteDefectRecord(id: number) { + return requestClient.delete(`/mes/qc/defect-record/delete?id=${id}`); +} diff --git a/apps/web-ele/src/api/mes/qc/indicatorresult/index.ts b/apps/web-ele/src/api/mes/qc/indicatorresult/index.ts new file mode 100644 index 000000000..dfa8a7f93 --- /dev/null +++ b/apps/web-ele/src/api/mes/qc/indicatorresult/index.ts @@ -0,0 +1,73 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MesQcIndicatorResultApi { + /** MES 检验结果明细 */ + export interface IndicatorResultDetail { + id?: number; // 编号 + resultId?: number; // 关联检验结果 ID + indicatorId?: number; // 检测指标 ID + value?: string; // 检测值(统一存为字符串) + valueNumber?: number; // UI 数值绑定(提交前转字符串) + remark?: string; // 备注 + // 关联查询字段(来自 indicator) + indicatorName?: string; // 检测指标名称 + valueType?: number; // 质检值类型 + valueSpecification?: string; // 值属性 + } + + /** MES 检验结果 */ + export interface IndicatorResult { + id?: number; // 编号 + code?: string; // 样品编号 + qcId?: number; // 关联质检单 ID + qcType?: number; // 质检类型 + itemId?: number; // 产品物料 ID + sn?: string; // 物资 SN + remark?: string; // 备注 + createTime?: Date; // 创建时间 + items?: IndicatorResultDetail[]; // 检验结果明细列表 + } +} + +/** 查询检验结果分页 */ +export function getIndicatorResultPage( + params: PageParam & { qcId?: number; qcType?: number }, +) { + return requestClient.get>( + '/mes/qc/indicator-result/page', + { params }, + ); +} + +/** 查询检验结果明细(含检测项模板):编辑传 id,新增不传 */ +export function getIndicatorResultDetail( + qcId: number, + qcType: number, + id?: number, +) { + return requestClient.get( + '/mes/qc/indicator-result/get-detail', + { params: { id, qcId, qcType } }, + ); +} + +/** 新增检验结果 */ +export function createIndicatorResult( + data: MesQcIndicatorResultApi.IndicatorResult, +) { + return requestClient.post('/mes/qc/indicator-result/create', data); +} + +/** 修改检验结果 */ +export function updateIndicatorResult( + data: MesQcIndicatorResultApi.IndicatorResult, +) { + return requestClient.put('/mes/qc/indicator-result/update', data); +} + +/** 删除检验结果 */ +export function deleteIndicatorResult(id: number) { + return requestClient.delete(`/mes/qc/indicator-result/delete?id=${id}`); +} diff --git a/apps/web-ele/src/api/mes/qc/iqc/index.ts b/apps/web-ele/src/api/mes/qc/iqc/index.ts new file mode 100644 index 000000000..cdb04cb6a --- /dev/null +++ b/apps/web-ele/src/api/mes/qc/iqc/index.ts @@ -0,0 +1,80 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MesQcIqcApi { + /** MES 来料检验单 */ + export interface Iqc { + id?: number; // 编号 + code?: string; // 检验单编号 + name?: string; // 检验单名称 + templateId?: number; // 检验模板 ID + sourceDocType?: number; // 来源单据类型 + sourceDocId?: number; // 来源单据 ID + sourceLineId?: number; // 来源单据行 ID + sourceDocCode?: string; // 来源单据编号(关联查询) + vendorId?: number; // 供应商 ID + vendorNickname?: string; // 供应商简称(关联查询) + vendorBatch?: string; // 供应商批次号 + itemId?: number; // 产品物料 ID + itemCode?: string; // 产品物料编码(关联查询) + itemName?: string; // 产品物料名称(关联查询) + itemSpecification?: string; // 规格型号(关联查询) + unitName?: string; // 单位名称(关联查询) + receivedQuantity?: number; // 本次接收数量 + checkQuantity?: number; // 本次检测数量 + qualifiedQuantity?: number; // 合格品数量 + unqualifiedQuantity?: number; // 不合格品数量 + criticalRate?: number; // 致命缺陷率(%) + majorRate?: number; // 严重缺陷率(%) + minorRate?: number; // 轻微缺陷率(%) + criticalQuantity?: number; // 致命缺陷数量 + majorQuantity?: number; // 严重缺陷数量 + minorQuantity?: number; // 轻微缺陷数量 + checkResult?: number; // 检测结果 + receiveDate?: number; // 来料日期 + inspectDate?: number; // 检测日期 + inspector?: string; // 检测人员(昵称) + inspectorUserId?: number; // 检测人员 ID + inspectorNickname?: string; // 检测人员昵称(关联查询) + status?: number; // 状态 + remark?: string; // 备注 + } +} + +/** 查询来料检验单分页 */ +export function getIqcPage(params: PageParam) { + return requestClient.get>('/mes/qc/iqc/page', { + params, + }); +} + +/** 查询来料检验单详情 */ +export function getIqc(id: number) { + return requestClient.get(`/mes/qc/iqc/get?id=${id}`); +} + +/** 新增来料检验单 */ +export function createIqc(data: MesQcIqcApi.Iqc) { + return requestClient.post('/mes/qc/iqc/create', data); +} + +/** 修改来料检验单 */ +export function updateIqc(data: MesQcIqcApi.Iqc) { + return requestClient.put('/mes/qc/iqc/update', data); +} + +/** 完成来料检验单 */ +export function finishIqc(id: number) { + return requestClient.put(`/mes/qc/iqc/finish?id=${id}`); +} + +/** 删除来料检验单 */ +export function deleteIqc(id: number) { + return requestClient.delete(`/mes/qc/iqc/delete?id=${id}`); +} + +/** 导出来料检验单 */ +export function exportIqc(params: any) { + return requestClient.download('/mes/qc/iqc/export-excel', { params }); +} diff --git a/apps/web-ele/src/api/mes/qc/iqc/line/index.ts b/apps/web-ele/src/api/mes/qc/iqc/line/index.ts new file mode 100644 index 000000000..dc37aa99b --- /dev/null +++ b/apps/web-ele/src/api/mes/qc/iqc/line/index.ts @@ -0,0 +1,41 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MesQcIqcLineApi { + /** MES 来料检验单行 */ + export interface IqcLine { + id?: number; // 编号 + iqcId?: number; // 来料检验单 ID + indicatorId?: number; // 检测指标 ID + indicatorCode?: string; // 检测指标编码(关联查询) + indicatorName?: string; // 检测指标名称(关联查询) + indicatorType?: number; // 检测指标类型(关联查询) + tool?: string; // 检测工具 + checkMethod?: string; // 检测方法 + standardValue?: number; // 标准值 + unitMeasureId?: number; // 计量单位 ID + unitMeasureName?: string; // 计量单位名称(关联查询) + maxThreshold?: number; // 误差上限 + minThreshold?: number; // 误差下限 + criticalQuantity?: number; // 致命缺陷数量 + majorQuantity?: number; // 严重缺陷数量 + minorQuantity?: number; // 轻微缺陷数量 + remark?: string; // 备注 + } +} + +/** 查询来料检验单行分页 */ +export function getIqcLinePage(params: PageParam & { iqcId?: number }) { + return requestClient.get>( + '/mes/qc/iqc/line/page', + { params }, + ); +} + +/** 查询来料检验单行详情 */ +export function getIqcLine(id: number) { + return requestClient.get( + `/mes/qc/iqc/line/get?id=${id}`, + ); +} diff --git a/apps/web-ele/src/router/routes/modules/mes.ts b/apps/web-ele/src/router/routes/modules/mes.ts new file mode 100644 index 000000000..05af60e29 --- /dev/null +++ b/apps/web-ele/src/router/routes/modules/mes.ts @@ -0,0 +1,46 @@ +import type { RouteRecordRaw } from 'vue-router'; + +const routes: RouteRecordRaw[] = [ + { + path: '/mes', + name: 'MesCenter', + meta: { + title: 'MES 制造执行', + icon: 'lucide:factory', + keepAlive: true, + hideInMenu: true, + }, + children: [ + { + path: 'wm/warehouse/location', + name: 'MesWmLocation', + meta: { + title: '库区设置', + activePath: '/mes/wm/warehouse', + }, + component: () => + import('#/views/mes/wm/warehouse/location/index.vue'), + }, + { + path: 'wm/warehouse/area', + name: 'MesWmArea', + meta: { + title: '库位设置', + activePath: '/mes/wm/warehouse', + }, + component: () => import('#/views/mes/wm/warehouse/area/index.vue'), + }, + { + path: 'wm/barcode/config', + name: 'MesWmBarcodeConfig', + meta: { + title: '条码配置', + activePath: '/mes/wm/barcode', + }, + component: () => import('#/views/mes/wm/barcode/config/index.vue'), + }, + ], + }, +]; + +export default routes; diff --git a/apps/web-ele/src/views/mes/wm/warehouse/area/data.ts b/apps/web-ele/src/views/mes/wm/warehouse/area/data.ts new file mode 100644 index 000000000..c6c676806 --- /dev/null +++ b/apps/web-ele/src/views/mes/wm/warehouse/area/data.ts @@ -0,0 +1,352 @@ +import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { MesWmWarehouseAreaApi } from '#/api/mes/wm/warehouse/area'; + +import { h } from 'vue'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { ElButton } from 'element-plus'; + +import { z } from '#/adapter/form'; +import { generateAutoCode } from '#/api/mes/md/autocode/record'; +import { getWarehouseSimpleList } from '#/api/mes/wm/warehouse'; +import { getWarehouseLocationSimpleList } from '#/api/mes/wm/warehouse/location'; +import { MesAutoCodeRuleCode } from '#/views/mes/utils/constants'; + +/** 表单类型 */ +export type FormType = 'create' | 'detail' | 'update'; + +/** 新增/修改的表单 */ +export function useFormSchema( + formType: FormType, + formApi?: VbenFormApi, +): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'warehouseId', + label: '所属仓库', + component: 'ApiSelect', + componentProps: { + api: getWarehouseSimpleList, + clearable: true, + labelField: 'name', + // 仓库变更时清空所属库区 + onChange: () => formApi?.setFieldValue('locationId', undefined), + placeholder: '请选择仓库', + valueField: 'id', + }, + }, + { + fieldName: 'locationId', + label: '所属库区', + component: 'ApiSelect', + dependencies: { + triggerFields: ['warehouseId'], + componentProps: (values) => ({ + api: () => + getWarehouseLocationSimpleList(values.warehouseId as number), + clearable: true, + disabled: !values.warehouseId, + labelField: 'name', + // 改变 warehouseId 时强制刷新选项 + params: { warehouseId: values.warehouseId }, + placeholder: '请选择库区', + valueField: 'id', + }), + }, + rules: 'required', + }, + { + fieldName: 'code', + label: '库位编码', + component: 'Input', + componentProps: { + placeholder: '请输入库位编码', + }, + rules: 'required', + suffix: + formType === 'detail' + ? undefined + : () => + h( + ElButton, + { + onClick: async () => { + try { + const code = await generateAutoCode( + MesAutoCodeRuleCode.WM_AREA_CODE, + ); + await formApi?.setFieldValue('code', code); + } catch (error) { + console.error(error); + } + }, + }, + { default: () => '自动生成' }, + ), + }, + { + fieldName: 'name', + label: '库位名称', + component: 'Input', + componentProps: { + placeholder: '请输入库位名称', + }, + rules: 'required', + }, + { + fieldName: 'area', + label: '面积(㎡)', + component: 'InputNumber', + componentProps: { + class: '!w-full', + controlsPosition: 'right', + min: 0, + placeholder: '请输入面积', + precision: 2, + }, + }, + { + fieldName: 'maxLoad', + label: '最大载重', + component: 'InputNumber', + componentProps: { + class: '!w-full', + controlsPosition: 'right', + min: 0, + placeholder: '请输入最大载重', + precision: 2, + }, + }, + { + fieldName: 'positionX', + label: '位置 X', + component: 'InputNumber', + componentProps: { + class: '!w-full', + controlsPosition: 'right', + min: 0, + placeholder: '请输入位置 X', + }, + }, + { + fieldName: 'positionY', + label: '位置 Y', + component: 'InputNumber', + componentProps: { + class: '!w-full', + controlsPosition: 'right', + min: 0, + placeholder: '请输入位置 Y', + }, + }, + { + fieldName: 'positionZ', + label: '位置 Z', + component: 'InputNumber', + componentProps: { + class: '!w-full', + controlsPosition: 'right', + min: 0, + placeholder: '请输入位置 Z', + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + placeholder: '请选择', + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + fieldName: 'frozen', + label: '是否冻结', + component: 'Switch', + componentProps: { + activeText: '是', + inactiveText: '否', + inlinePrompt: true, + }, + rules: z.boolean().default(false), + }, + { + fieldName: 'allowItemMixing', + label: '允许物料混放', + component: 'Switch', + componentProps: { + activeText: '是', + inactiveText: '否', + inlinePrompt: true, + }, + rules: z.boolean().default(true), + }, + { + fieldName: 'allowBatchMixing', + label: '允许批次混放', + component: 'Switch', + componentProps: { + activeText: '是', + inactiveText: '否', + inlinePrompt: true, + }, + rules: z.boolean().default(true), + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + formItemClass: 'col-span-3', + componentProps: { + placeholder: '请输入备注', + rows: 3, + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'code', + label: '库位编码', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入库位编码', + }, + }, + { + fieldName: 'name', + label: '库位名称', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入库位名称', + }, + }, + { + fieldName: 'positionX', + label: '位置 X', + component: 'InputNumber', + componentProps: { + class: '!w-full', + controlsPosition: 'right', + min: 0, + placeholder: '请输入位置 X', + }, + }, + { + fieldName: 'positionY', + label: '位置 Y', + component: 'InputNumber', + componentProps: { + class: '!w-full', + controlsPosition: 'right', + min: 0, + placeholder: '请输入位置 Y', + }, + }, + { + fieldName: 'positionZ', + label: '位置 Z', + component: 'InputNumber', + componentProps: { + class: '!w-full', + controlsPosition: 'right', + min: 0, + placeholder: '请输入位置 Z', + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'code', + title: '库位编码', + minWidth: 140, + slots: { default: 'code' }, + }, + { + field: 'name', + title: '库位名称', + minWidth: 160, + }, + { + field: 'area', + title: '面积(㎡)', + width: 100, + }, + { + field: 'maxLoad', + title: '最大载重', + width: 100, + }, + { + field: 'positionX', + title: '位置 X', + width: 80, + }, + { + field: 'positionY', + title: '位置 Y', + width: 80, + }, + { + field: 'positionZ', + title: '位置 Z', + width: 80, + }, + { + field: 'status', + title: '状态', + width: 90, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'frozen', + title: '冻结', + width: 90, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, + }, + }, + { + field: 'remark', + title: '备注', + minWidth: 180, + }, + { + field: 'createTime', + title: '创建时间', + width: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 200, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/mes/wm/warehouse/area/index.vue b/apps/web-ele/src/views/mes/wm/warehouse/area/index.vue new file mode 100644 index 000000000..b0f4a441c --- /dev/null +++ b/apps/web-ele/src/views/mes/wm/warehouse/area/index.vue @@ -0,0 +1,221 @@ + + + diff --git a/apps/web-ele/src/views/mes/wm/warehouse/area/modules/form.vue b/apps/web-ele/src/views/mes/wm/warehouse/area/modules/form.vue new file mode 100644 index 000000000..1d31e18bb --- /dev/null +++ b/apps/web-ele/src/views/mes/wm/warehouse/area/modules/form.vue @@ -0,0 +1,149 @@ + + + diff --git a/apps/web-ele/src/views/mes/wm/warehouse/components/index.ts b/apps/web-ele/src/views/mes/wm/warehouse/components/index.ts new file mode 100644 index 000000000..d2981238b --- /dev/null +++ b/apps/web-ele/src/views/mes/wm/warehouse/components/index.ts @@ -0,0 +1,3 @@ +export { default as WmWarehouseAreaSelect } from './wm-warehouse-area-select.vue'; +export { default as WmWarehouseLocationSelect } from './wm-warehouse-location-select.vue'; +export { default as WmWarehouseSelect } from './wm-warehouse-select.vue'; diff --git a/apps/web-ele/src/views/mes/wm/warehouse/components/wm-warehouse-area-select.vue b/apps/web-ele/src/views/mes/wm/warehouse/components/wm-warehouse-area-select.vue new file mode 100644 index 000000000..ba3c1a467 --- /dev/null +++ b/apps/web-ele/src/views/mes/wm/warehouse/components/wm-warehouse-area-select.vue @@ -0,0 +1,121 @@ + + + diff --git a/apps/web-ele/src/views/mes/wm/warehouse/components/wm-warehouse-location-select.vue b/apps/web-ele/src/views/mes/wm/warehouse/components/wm-warehouse-location-select.vue new file mode 100644 index 000000000..e9dca087e --- /dev/null +++ b/apps/web-ele/src/views/mes/wm/warehouse/components/wm-warehouse-location-select.vue @@ -0,0 +1,120 @@ + + + diff --git a/apps/web-ele/src/views/mes/wm/warehouse/components/wm-warehouse-select.vue b/apps/web-ele/src/views/mes/wm/warehouse/components/wm-warehouse-select.vue new file mode 100644 index 000000000..edeb295ec --- /dev/null +++ b/apps/web-ele/src/views/mes/wm/warehouse/components/wm-warehouse-select.vue @@ -0,0 +1,118 @@ + + + diff --git a/apps/web-ele/src/views/mes/wm/warehouse/data.ts b/apps/web-ele/src/views/mes/wm/warehouse/data.ts new file mode 100644 index 000000000..fafda94e7 --- /dev/null +++ b/apps/web-ele/src/views/mes/wm/warehouse/data.ts @@ -0,0 +1,227 @@ +import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { MesWmWarehouseApi } from '#/api/mes/wm/warehouse'; +import type { SystemUserApi } from '#/api/system/user'; + +import { h } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; + +import { ElButton } from 'element-plus'; + +import { z } from '#/adapter/form'; +import { generateAutoCode } from '#/api/mes/md/autocode/record'; +import { getSimpleUserList } from '#/api/system/user'; +import { MesAutoCodeRuleCode } from '#/views/mes/utils/constants'; + +/** 表单类型 */ +export type FormType = 'create' | 'detail' | 'update'; + +/** 关联数据 */ +let userList: SystemUserApi.User[] = []; +getSimpleUserList().then((data) => (userList = data)); + +/** 新增/修改的表单 */ +export function useFormSchema( + formType: FormType, + formApi?: VbenFormApi, +): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'code', + label: '仓库编码', + component: 'Input', + componentProps: { + placeholder: '请输入仓库编码', + }, + rules: 'required', + suffix: + formType === 'detail' + ? undefined + : () => + h( + ElButton, + { + onClick: async () => { + try { + const code = await generateAutoCode( + MesAutoCodeRuleCode.WM_WAREHOUSE_CODE, + ); + await formApi?.setFieldValue('code', code); + } catch (error) { + console.error(error); + } + }, + }, + { default: () => '自动生成' }, + ), + }, + { + fieldName: 'name', + label: '仓库名称', + component: 'Input', + componentProps: { + placeholder: '请输入仓库名称', + }, + rules: 'required', + }, + { + fieldName: 'chargeUserId', + label: '负责人', + component: 'ApiSelect', + componentProps: { + api: getSimpleUserList, + clearable: true, + labelField: 'nickname', + placeholder: '请选择负责人', + valueField: 'id', + }, + }, + { + fieldName: 'address', + label: '仓库地址', + component: 'Input', + componentProps: { + placeholder: '请输入仓库地址', + }, + }, + { + fieldName: 'area', + label: '面积(㎡)', + component: 'InputNumber', + componentProps: { + class: '!w-full', + controlsPosition: 'right', + min: 0, + placeholder: '请输入面积', + precision: 2, + }, + }, + { + fieldName: 'frozen', + label: '是否冻结', + component: 'Switch', + componentProps: { + activeText: '是', + inactiveText: '否', + inlinePrompt: true, + }, + rules: z.boolean().default(false), + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + formItemClass: 'col-span-3', + componentProps: { + placeholder: '请输入备注', + rows: 3, + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'code', + label: '仓库编码', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入仓库编码', + }, + }, + { + fieldName: 'name', + label: '仓库名称', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入仓库名称', + }, + }, + { + fieldName: 'frozen', + label: '是否冻结', + component: 'Select', + componentProps: { + clearable: true, + options: [ + { label: '是', value: true }, + { label: '否', value: false }, + ], + placeholder: '请选择', + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'code', + title: '仓库编码', + minWidth: 140, + slots: { default: 'code' }, + }, + { + field: 'name', + title: '仓库名称', + minWidth: 160, + }, + { + field: 'address', + title: '仓库地址', + minWidth: 180, + }, + { + field: 'area', + title: '面积(㎡)', + width: 110, + }, + { + field: 'chargeUserId', + title: '负责人', + width: 120, + formatter: ({ cellValue }) => + userList.find((user) => user.id === cellValue)?.nickname ?? '', + }, + { + field: 'frozen', + title: '冻结', + width: 90, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, + }, + }, + { + field: 'remark', + title: '备注', + minWidth: 180, + }, + { + field: 'createTime', + title: '创建时间', + width: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 240, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/mes/wm/warehouse/index.vue b/apps/web-ele/src/views/mes/wm/warehouse/index.vue new file mode 100644 index 000000000..92a9e4059 --- /dev/null +++ b/apps/web-ele/src/views/mes/wm/warehouse/index.vue @@ -0,0 +1,185 @@ + + + diff --git a/apps/web-ele/src/views/mes/wm/warehouse/location/data.ts b/apps/web-ele/src/views/mes/wm/warehouse/location/data.ts new file mode 100644 index 000000000..c3d00a1a3 --- /dev/null +++ b/apps/web-ele/src/views/mes/wm/warehouse/location/data.ts @@ -0,0 +1,202 @@ +import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { MesWmWarehouseApi } from '#/api/mes/wm/warehouse'; +import type { MesWmWarehouseLocationApi } from '#/api/mes/wm/warehouse/location'; + +import { h } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; + +import { ElButton } from 'element-plus'; + +import { z } from '#/adapter/form'; +import { generateAutoCode } from '#/api/mes/md/autocode/record'; +import { getWarehouseSimpleList } from '#/api/mes/wm/warehouse'; +import { MesAutoCodeRuleCode } from '#/views/mes/utils/constants'; + +/** 表单类型 */ +export type FormType = 'create' | 'detail' | 'update'; + +/** 关联数据 */ +let warehouseList: MesWmWarehouseApi.Warehouse[] = []; +getWarehouseSimpleList().then((data) => (warehouseList = data)); + +/** 新增/修改的表单 */ +export function useFormSchema( + formType: FormType, + formApi?: VbenFormApi, +): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'code', + label: '库区编码', + component: 'Input', + componentProps: { + placeholder: '请输入库区编码', + }, + rules: 'required', + suffix: + formType === 'detail' + ? undefined + : () => + h( + ElButton, + { + onClick: async () => { + try { + const code = await generateAutoCode( + MesAutoCodeRuleCode.WM_LOCATION_CODE, + ); + await formApi?.setFieldValue('code', code); + } catch (error) { + console.error(error); + } + }, + }, + { default: () => '自动生成' }, + ), + }, + { + fieldName: 'name', + label: '库区名称', + component: 'Input', + componentProps: { + placeholder: '请输入库区名称', + }, + rules: 'required', + }, + { + fieldName: 'warehouseId', + label: '所属仓库', + component: 'ApiSelect', + componentProps: { + api: getWarehouseSimpleList, + clearable: true, + labelField: 'name', + placeholder: '请选择仓库', + valueField: 'id', + }, + rules: 'required', + }, + { + fieldName: 'area', + label: '面积(㎡)', + component: 'InputNumber', + componentProps: { + class: '!w-full', + controlsPosition: 'right', + min: 0, + placeholder: '请输入面积', + precision: 2, + }, + }, + { + fieldName: 'frozen', + label: '是否冻结', + component: 'Switch', + componentProps: { + activeText: '是', + inactiveText: '否', + inlinePrompt: true, + }, + rules: z.boolean().default(false), + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + formItemClass: 'col-span-3', + componentProps: { + placeholder: '请输入备注', + rows: 3, + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'code', + label: '库区编码', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入库区编码', + }, + }, + { + fieldName: 'name', + label: '库区名称', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入库区名称', + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'code', + title: '库区编码', + minWidth: 140, + slots: { default: 'code' }, + }, + { + field: 'name', + title: '库区名称', + minWidth: 160, + }, + { + field: 'warehouseId', + title: '所属仓库', + minWidth: 140, + formatter: ({ cellValue }) => + warehouseList.find((w) => w.id === cellValue)?.name ?? '', + }, + { + field: 'area', + title: '面积(㎡)', + width: 110, + }, + { + field: 'frozen', + title: '冻结', + width: 90, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, + }, + }, + { + field: 'remark', + title: '备注', + minWidth: 180, + }, + { + field: 'createTime', + title: '创建时间', + width: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 240, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/mes/wm/warehouse/location/index.vue b/apps/web-ele/src/views/mes/wm/warehouse/location/index.vue new file mode 100644 index 000000000..87bad456e --- /dev/null +++ b/apps/web-ele/src/views/mes/wm/warehouse/location/index.vue @@ -0,0 +1,229 @@ + + + diff --git a/apps/web-ele/src/views/mes/wm/warehouse/location/modules/form.vue b/apps/web-ele/src/views/mes/wm/warehouse/location/modules/form.vue new file mode 100644 index 000000000..59ca8eb28 --- /dev/null +++ b/apps/web-ele/src/views/mes/wm/warehouse/location/modules/form.vue @@ -0,0 +1,192 @@ + + + diff --git a/apps/web-ele/src/views/mes/wm/warehouse/modules/form.vue b/apps/web-ele/src/views/mes/wm/warehouse/modules/form.vue new file mode 100644 index 000000000..c944e9498 --- /dev/null +++ b/apps/web-ele/src/views/mes/wm/warehouse/modules/form.vue @@ -0,0 +1,124 @@ + + +