From 9cc52f128c2803ab4feeea610bcb8fb3b0fa4f1e Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 21 May 2026 08:45:48 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=88wms=EF=BC=89:=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=20md=20item=20=E7=9A=84=E8=BF=81=E7=A7=BB=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=EF=BC=88=E7=BB=A7=E7=BB=AD=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../item/components/md-item-select-dialog.vue | 21 ++++++++++++------- .../mes/md/item/components/md-item-select.vue | 16 ++++++++------ apps/web-antd/src/views/mes/md/item/index.vue | 2 +- .../src/views/mes/md/item/modules/form.vue | 11 +++++----- .../item/modules/item-batch-config-form.vue | 8 +++++-- .../mes/md/item/modules/product-bom-form.vue | 16 +++++++++----- .../md/item/modules/product-media-list.vue | 20 ++++++++++++------ .../type/components/md-item-type-select.vue | 8 +++---- .../type/components/md-item-type-tree.vue | 12 +++++------ .../src/views/mes/md/item/type/index.vue | 3 ++- .../item/components/md-item-select-dialog.vue | 21 ++++++++++++------- .../mes/md/item/components/md-item-select.vue | 16 ++++++++------ apps/web-ele/src/views/mes/md/item/index.vue | 2 +- .../src/views/mes/md/item/modules/form.vue | 11 +++++----- .../item/modules/item-batch-config-form.vue | 8 +++++-- .../mes/md/item/modules/product-bom-form.vue | 16 +++++++++----- .../md/item/modules/product-media-list.vue | 20 ++++++++++++------ .../type/components/md-item-type-select.vue | 8 +++---- .../type/components/md-item-type-tree.vue | 12 +++++------ .../src/views/mes/md/item/type/index.vue | 3 ++- 20 files changed, 148 insertions(+), 86 deletions(-) diff --git a/apps/web-antd/src/views/mes/md/item/components/md-item-select-dialog.vue b/apps/web-antd/src/views/mes/md/item/components/md-item-select-dialog.vue index 4f36d9260..2e6a07a9a 100644 --- a/apps/web-antd/src/views/mes/md/item/components/md-item-select-dialog.vue +++ b/apps/web-antd/src/views/mes/md/item/components/md-item-select-dialog.vue @@ -21,13 +21,13 @@ const emit = defineEmits<{ selected: [rows: MesMdItemApi.Item[]]; }>(); -const open = ref(false); -const multiple = ref(true); -const syncingSingleSelection = ref(false); -const selectedRows = ref([]); -const selectedItemTypeId = ref(); -const preSelectedIds = ref([]); -const typeTreeRef = ref>(); +const open = ref(false); // 弹窗是否打开 +const multiple = ref(true); // 是否多选 +const syncingSingleSelection = ref(false); // 是否同步单选勾选状态 +const selectedRows = ref([]); // 已选物料列表 +const selectedItemTypeId = ref(); // 当前筛选分类编号 +const preSelectedIds = ref([]); // 预选物料编号列表 +const typeTreeRef = ref>(); // 物料分类树 /** 单选模式下同步 VXE 勾选状态,避免跨页残留多选 */ async function syncSingleSelection(row?: MesMdItemApi.Item) { @@ -63,6 +63,7 @@ async function handleCheckboxChange({ selectedRows.value = records; } +/** 处理全选变化 */ function handleCheckboxAll({ records }: { records: MesMdItemApi.Item[] }) { if (syncingSingleSelection.value) { return; @@ -70,11 +71,13 @@ function handleCheckboxAll({ records }: { records: MesMdItemApi.Item[] }) { selectedRows.value = records; } +/** 按分类筛选物料 */ function handleItemTypeNodeClick(row: MesMdItemTypeApi.ItemType | undefined) { selectedItemTypeId.value = row?.id; gridApi.query(); } +/** 回显预选物料 */ function applyPreSelection() { if (preSelectedIds.value.length === 0) { return; @@ -131,6 +134,7 @@ const [Grid, gridApi] = useVbenVxeGrid({ }, }); +/** 重置查询和选择状态 */ async function resetQueryState() { selectedItemTypeId.value = undefined; selectedRows.value = []; @@ -139,6 +143,7 @@ async function resetQueryState() { await gridApi.formApi.resetForm(); } +/** 打开物料选择弹窗 */ async function openModal(selectedIds?: number[], options?: { multiple?: boolean }) { open.value = true; multiple.value = options?.multiple ?? true; @@ -150,11 +155,13 @@ async function openModal(selectedIds?: number[], options?: { multiple?: boolean applyPreSelection(); } +/** 关闭物料选择弹窗 */ async function closeModal() { open.value = false; await resetQueryState(); } +/** 确认选择物料 */ function handleConfirm() { if (selectedRows.value.length === 0) { message.warning(multiple.value ? '请至少选择一条数据' : '请选择一条数据'); diff --git a/apps/web-antd/src/views/mes/md/item/components/md-item-select.vue b/apps/web-antd/src/views/mes/md/item/components/md-item-select.vue index dee1e1370..006277683 100644 --- a/apps/web-antd/src/views/mes/md/item/components/md-item-select.vue +++ b/apps/web-antd/src/views/mes/md/item/components/md-item-select.vue @@ -31,16 +31,17 @@ const emit = defineEmits<{ change: [item: MesMdItemApi.Item | undefined]; 'update:modelValue': [value: number | undefined]; }>(); -const attrs = useAttrs(); -const dialogRef = ref>(); -const hovering = ref(false); -const selectedItem = ref(); +const attrs = useAttrs(); // 透传属性 +const dialogRef = ref>(); // 物料选择弹窗 +const hovering = ref(false); // 是否悬停 +const selectedItem = ref(); // 当前选中物料 -const displayLabel = computed(() => selectedItem.value?.name ?? ''); -const showClear = computed( +const displayLabel = computed(() => selectedItem.value?.name ?? ''); // 选择器展示名称 +const showClear = computed( // 是否显示清空图标 () => props.allowClear && !props.disabled && hovering.value && props.modelValue != null, ); +/** 根据物料编号回显选择器 */ async function resolveItemById(id: number | undefined) { if (id == null) { selectedItem.value = undefined; @@ -64,12 +65,14 @@ watch( { immediate: true }, ); +/** 清空已选物料 */ function clearSelected() { selectedItem.value = undefined; emit('update:modelValue', undefined); emit('change', undefined); } +/** 打开物料选择弹窗 */ function handleClick(event: MouseEvent) { if (props.disabled) { return; @@ -84,6 +87,7 @@ function handleClick(event: MouseEvent) { dialogRef.value?.open(selectedIds, { multiple: false }); } +/** 回填选中的物料 */ function handleSelected(rows: MesMdItemApi.Item[]) { const item = rows[0]; if (!item) { diff --git a/apps/web-antd/src/views/mes/md/item/index.vue b/apps/web-antd/src/views/mes/md/item/index.vue index 3184639df..c1e995f25 100644 --- a/apps/web-antd/src/views/mes/md/item/index.vue +++ b/apps/web-antd/src/views/mes/md/item/index.vue @@ -29,7 +29,7 @@ import ImportForm from './modules/import-form.vue'; defineOptions({ name: 'MesMdItem' }); -const selectedItemTypeId = ref(); +const selectedItemTypeId = ref(); // 当前选中的物料分类编号 const [FormModal, formModalApi] = useVbenModal({ connectedComponent: Form, diff --git a/apps/web-antd/src/views/mes/md/item/modules/form.vue b/apps/web-antd/src/views/mes/md/item/modules/form.vue index 81072ed59..193a15b05 100644 --- a/apps/web-antd/src/views/mes/md/item/modules/form.vue +++ b/apps/web-antd/src/views/mes/md/item/modules/form.vue @@ -24,12 +24,12 @@ type FormMode = 'create' | 'detail' | 'update'; defineOptions({ name: 'MesMdItemForm' }); const emit = defineEmits(['success']); -const formMode = ref('create'); -const subTabsName = ref('bom'); +const formMode = ref('create'); // 表单模式 +const subTabsName = ref('bom'); // 当前子表页签 const formData = ref(); -const barcodeDetailRef = ref>(); +const barcodeDetailRef = ref>(); // 条码详情弹窗 -const isDetail = computed(() => formMode.value === 'detail'); +const isDetail = computed(() => formMode.value === 'detail'); // 是否查看模式 const getTitle = computed(() => { const titles: Record = { create: '新增物料/产品', @@ -38,7 +38,7 @@ const getTitle = computed(() => { }; return titles[formMode.value]; }); -const currentItemOrProduct = computed(() => formData.value?.itemOrProduct || ''); +const currentItemOrProduct = computed(() => formData.value?.itemOrProduct || ''); // 当前物料/产品标识 const [Form, formApi] = useVbenForm({ commonConfig: { @@ -57,6 +57,7 @@ const [Form, formApi] = useVbenForm({ /** 表单 schema 需要 formApi 引用,所以通过 setState 设置 schema */ formApi.setState({ schema: useFormSchema(formApi) }); +/** 查看物料条码 */ function handleBarcode() { if (!formData.value?.id) { return; diff --git a/apps/web-antd/src/views/mes/md/item/modules/item-batch-config-form.vue b/apps/web-antd/src/views/mes/md/item/modules/item-batch-config-form.vue index d41aa7ce6..387e6111d 100644 --- a/apps/web-antd/src/views/mes/md/item/modules/item-batch-config-form.vue +++ b/apps/web-antd/src/views/mes/md/item/modules/item-batch-config-form.vue @@ -25,10 +25,11 @@ const props = withDefaults( }, ); -const isReadOnly = computed(() => props.formType === 'detail'); -const loading = ref(false); +const isReadOnly = computed(() => props.formType === 'detail'); // 是否只读 +const loading = ref(false); // 批次属性加载/保存中 const formData = ref(buildDefaultData()); +/** 构建批次属性默认值 */ function buildDefaultData(): MesMdItemBatchConfigApi.BatchConfig { return { itemId: props.itemId, @@ -49,6 +50,7 @@ function buildDefaultData(): MesMdItemBatchConfigApi.BatchConfig { }; } +/** 加载批次属性配置 */ async function loadData() { loading.value = true; try { @@ -59,12 +61,14 @@ async function loadData() { } } +/** 判断是否已选择批次属性 */ function hasAnySelected() { return Object.entries(formData.value).some( ([key, value]) => key.endsWith('Flag') && value === true, ); } +/** 保存批次属性配置 */ async function handleSave() { if (!hasAnySelected()) { message.warning('至少选择一个批次属性'); diff --git a/apps/web-antd/src/views/mes/md/item/modules/product-bom-form.vue b/apps/web-antd/src/views/mes/md/item/modules/product-bom-form.vue index f4ce385ac..12819fd04 100644 --- a/apps/web-antd/src/views/mes/md/item/modules/product-bom-form.vue +++ b/apps/web-antd/src/views/mes/md/item/modules/product-bom-form.vue @@ -32,12 +32,12 @@ const props = withDefaults( }, ); -const isReadOnly = computed(() => props.formType === 'detail'); -const formOpen = ref(false); -const formLoading = ref(false); +const isReadOnly = computed(() => props.formType === 'detail'); // 是否只读 +const formOpen = ref(false); // BOM 表单是否打开 +const formLoading = ref(false); // BOM 表单提交中 const formData = ref(); -const list = ref([]); -const itemSelectRef = ref>(); +const list = ref([]); // 产品 BOM 列表 +const itemSelectRef = ref>(); // 物料选择弹窗 const [Form, formApi] = useVbenForm({ commonConfig: { @@ -114,6 +114,7 @@ const [Grid, gridApi] = useVbenVxeGrid({ } as VxeTableGridOptions, }); +/** 加载产品 BOM 列表 */ async function getList() { gridApi.setLoading(true); try { @@ -124,10 +125,12 @@ async function getList() { } } +/** 打开 BOM 物料选择弹窗 */ function handleAdd() { itemSelectRef.value?.open(undefined, { multiple: true }); } +/** 添加选中的 BOM 物料 */ async function handleItemSelected(rows: MesMdItemApi.Item[]) { if (rows.length === 0) { return; @@ -143,6 +146,7 @@ async function handleItemSelected(rows: MesMdItemApi.Item[]) { await getList(); } +/** 打开 BOM 编辑弹窗 */ async function openForm(row: MesMdProductBomApi.ProductBom) { formOpen.value = true; formData.value = row; @@ -153,6 +157,7 @@ async function openForm(row: MesMdProductBomApi.ProductBom) { }); } +/** 提交 BOM 表单 */ async function submitForm() { const { valid } = await formApi.validate(); if (!valid) { @@ -170,6 +175,7 @@ async function submitForm() { } } +/** 删除 BOM 物料 */ async function handleDelete(id: number) { await deleteProductBom(id); message.success($t('ui.actionMessage.deleteSuccess', ['BOM'])); diff --git a/apps/web-antd/src/views/mes/md/item/modules/product-media-list.vue b/apps/web-antd/src/views/mes/md/item/modules/product-media-list.vue index c9d0ba3a9..e61c61772 100644 --- a/apps/web-antd/src/views/mes/md/item/modules/product-media-list.vue +++ b/apps/web-antd/src/views/mes/md/item/modules/product-media-list.vue @@ -38,13 +38,13 @@ const props = withDefaults( }, ); -const isReadOnly = computed(() => props.formType === 'detail'); -const title = computed(() => props.kind); -const loading = ref(false); -const formOpen = ref(false); -const formLoading = ref(false); +const isReadOnly = computed(() => props.formType === 'detail'); // 是否只读 +const title = computed(() => props.kind); // 当前媒体类型标题 +const loading = ref(false); // SIP/SOP 列表加载中 +const formOpen = ref(false); // SIP/SOP 表单是否打开 +const formLoading = ref(false); // SIP/SOP 表单提交中 const formData = ref(); -const list = ref([]); +const list = ref([]); // SIP/SOP 列表 const [Form, formApi] = useVbenForm({ commonConfig: { @@ -99,24 +99,29 @@ const [Form, formApi] = useVbenForm({ showDefaultActions: false, }); +/** 获取当前媒体类型的列表接口 */ function getListApi() { return props.kind === 'SIP' ? getProductSipListByItemId : getProductSopListByItemId; } +/** 获取当前媒体类型的新增接口 */ function createApi() { return props.kind === 'SIP' ? createProductSip : createProductSop; } +/** 获取当前媒体类型的修改接口 */ function updateApi() { return props.kind === 'SIP' ? updateProductSip : updateProductSop; } +/** 获取当前媒体类型的删除接口 */ function deleteApi() { return props.kind === 'SIP' ? deleteProductSip : deleteProductSop; } +/** 加载 SIP/SOP 列表 */ async function getList() { loading.value = true; try { @@ -126,6 +131,7 @@ async function getList() { } } +/** 打开 SIP/SOP 表单 */ async function openForm(row?: MediaItem) { formOpen.value = true; formData.value = row; @@ -137,6 +143,7 @@ async function openForm(row?: MediaItem) { }); } +/** 提交 SIP/SOP 表单 */ async function submitForm() { const { valid } = await formApi.validate(); if (!valid) { @@ -154,6 +161,7 @@ async function submitForm() { } } +/** 删除 SIP/SOP */ async function handleDelete(id: number) { await deleteApi()(id); message.success('删除成功'); diff --git a/apps/web-antd/src/views/mes/md/item/type/components/md-item-type-select.vue b/apps/web-antd/src/views/mes/md/item/type/components/md-item-type-select.vue index 939225e0f..d3e6527e1 100644 --- a/apps/web-antd/src/views/mes/md/item/type/components/md-item-type-select.vue +++ b/apps/web-antd/src/views/mes/md/item/type/components/md-item-type-select.vue @@ -36,11 +36,11 @@ type ItemTypeNode = MesMdItemTypeApi.ItemType & { disabled?: boolean; }; -const allList = ref([]); -const itemTypeTree = ref([]); -const selectedItem = ref(); +const allList = ref([]); // 物料分类列表 +const itemTypeTree = ref([]); // 物料分类树 +const selectedItem = ref(); // 当前选中分类 -const selectValue = computed({ +const selectValue = computed({ // 选择器绑定值 get: () => props.modelValue, set: (value: number | undefined) => { emit('update:modelValue', value); diff --git a/apps/web-antd/src/views/mes/md/item/type/components/md-item-type-tree.vue b/apps/web-antd/src/views/mes/md/item/type/components/md-item-type-tree.vue index 79efa3b2e..eeb7d926b 100644 --- a/apps/web-antd/src/views/mes/md/item/type/components/md-item-type-tree.vue +++ b/apps/web-antd/src/views/mes/md/item/type/components/md-item-type-tree.vue @@ -25,12 +25,12 @@ const emit = defineEmits<{ nodeClick: [itemType: MesMdItemTypeApi.ItemType | undefined]; }>(); -const loading = ref(false); -const filterText = ref(''); -const currentNodeId = ref(); -const selectedKeys = ref([]); -const itemTypeList = ref([]); -const itemTypeTree = ref([]); +const loading = ref(false); // 分类树加载中 +const filterText = ref(''); // 分类搜索关键字 +const currentNodeId = ref(); // 当前选中分类编号 +const selectedKeys = ref([]); // 当前选中节点 key 列表 +const itemTypeList = ref([]); // 物料分类列表 +const itemTypeTree = ref([]); // 物料分类树 /** 加载分类树 */ async function loadTree() { diff --git a/apps/web-antd/src/views/mes/md/item/type/index.vue b/apps/web-antd/src/views/mes/md/item/type/index.vue index 6b2906f1a..7a2e8d44c 100644 --- a/apps/web-antd/src/views/mes/md/item/type/index.vue +++ b/apps/web-antd/src/views/mes/md/item/type/index.vue @@ -22,8 +22,9 @@ const [FormModal, formModalApi] = useVbenModal({ destroyOnClose: true, }); +const isExpanded = ref(true); // 树形表格是否展开 + /** 切换树形展开/收缩状态 */ -const isExpanded = ref(true); function handleExpand() { isExpanded.value = !isExpanded.value; gridApi.grid.setAllTreeExpand(isExpanded.value); diff --git a/apps/web-ele/src/views/mes/md/item/components/md-item-select-dialog.vue b/apps/web-ele/src/views/mes/md/item/components/md-item-select-dialog.vue index 12c9a7eb1..a811d5121 100644 --- a/apps/web-ele/src/views/mes/md/item/components/md-item-select-dialog.vue +++ b/apps/web-ele/src/views/mes/md/item/components/md-item-select-dialog.vue @@ -21,13 +21,13 @@ const emit = defineEmits<{ selected: [rows: MesMdItemApi.Item[]]; }>(); -const open = ref(false); -const multiple = ref(true); -const syncingSingleSelection = ref(false); -const selectedRows = ref([]); -const selectedItemTypeId = ref(); -const preSelectedIds = ref([]); -const typeTreeRef = ref>(); +const open = ref(false); // 弹窗是否打开 +const multiple = ref(true); // 是否多选 +const syncingSingleSelection = ref(false); // 是否同步单选勾选状态 +const selectedRows = ref([]); // 已选物料列表 +const selectedItemTypeId = ref(); // 当前筛选分类编号 +const preSelectedIds = ref([]); // 预选物料编号列表 +const typeTreeRef = ref>(); // 物料分类树 /** 单选模式下同步 VXE 勾选状态,避免跨页残留多选 */ async function syncSingleSelection(row?: MesMdItemApi.Item) { @@ -63,6 +63,7 @@ async function handleCheckboxChange({ selectedRows.value = records; } +/** 处理全选变化 */ function handleCheckboxAll({ records }: { records: MesMdItemApi.Item[] }) { if (syncingSingleSelection.value) { return; @@ -70,11 +71,13 @@ function handleCheckboxAll({ records }: { records: MesMdItemApi.Item[] }) { selectedRows.value = records; } +/** 按分类筛选物料 */ function handleItemTypeNodeClick(row: MesMdItemTypeApi.ItemType | undefined) { selectedItemTypeId.value = row?.id; gridApi.query(); } +/** 回显预选物料 */ function applyPreSelection() { if (preSelectedIds.value.length === 0) { return; @@ -131,6 +134,7 @@ const [Grid, gridApi] = useVbenVxeGrid({ }, }); +/** 重置查询和选择状态 */ async function resetQueryState() { selectedItemTypeId.value = undefined; selectedRows.value = []; @@ -139,6 +143,7 @@ async function resetQueryState() { await gridApi.formApi.resetForm(); } +/** 打开物料选择弹窗 */ async function openModal(selectedIds?: number[], options?: { multiple?: boolean }) { open.value = true; multiple.value = options?.multiple ?? true; @@ -150,11 +155,13 @@ async function openModal(selectedIds?: number[], options?: { multiple?: boolean applyPreSelection(); } +/** 关闭物料选择弹窗 */ async function closeModal() { open.value = false; await resetQueryState(); } +/** 确认选择物料 */ function handleConfirm() { if (selectedRows.value.length === 0) { ElMessage.warning(multiple.value ? '请至少选择一条数据' : '请选择一条数据'); diff --git a/apps/web-ele/src/views/mes/md/item/components/md-item-select.vue b/apps/web-ele/src/views/mes/md/item/components/md-item-select.vue index b3079762a..eb4e48560 100644 --- a/apps/web-ele/src/views/mes/md/item/components/md-item-select.vue +++ b/apps/web-ele/src/views/mes/md/item/components/md-item-select.vue @@ -31,16 +31,17 @@ const emit = defineEmits<{ change: [item: MesMdItemApi.Item | undefined]; 'update:modelValue': [value: number | undefined]; }>(); -const attrs = useAttrs(); -const dialogRef = ref>(); -const hovering = ref(false); -const selectedItem = ref(); +const attrs = useAttrs(); // 透传属性 +const dialogRef = ref>(); // 物料选择弹窗 +const hovering = ref(false); // 是否悬停 +const selectedItem = ref(); // 当前选中物料 -const displayLabel = computed(() => selectedItem.value?.name ?? ''); -const showClear = computed( +const displayLabel = computed(() => selectedItem.value?.name ?? ''); // 选择器展示名称 +const showClear = computed( // 是否显示清空图标 () => props.clearable && !props.disabled && hovering.value && props.modelValue != null, ); +/** 根据物料编号回显选择器 */ async function resolveItemById(id: number | undefined) { if (id == null) { selectedItem.value = undefined; @@ -64,12 +65,14 @@ watch( { immediate: true }, ); +/** 清空已选物料 */ function clearSelected() { selectedItem.value = undefined; emit('update:modelValue', undefined); emit('change', undefined); } +/** 打开物料选择弹窗 */ function handleClick(event: MouseEvent) { if (props.disabled) { return; @@ -84,6 +87,7 @@ function handleClick(event: MouseEvent) { dialogRef.value?.open(selectedIds, { multiple: false }); } +/** 回填选中的物料 */ function handleSelected(rows: MesMdItemApi.Item[]) { const item = rows[0]; if (!item) { diff --git a/apps/web-ele/src/views/mes/md/item/index.vue b/apps/web-ele/src/views/mes/md/item/index.vue index da06eeab9..c6768d390 100644 --- a/apps/web-ele/src/views/mes/md/item/index.vue +++ b/apps/web-ele/src/views/mes/md/item/index.vue @@ -35,7 +35,7 @@ import ImportForm from './modules/import-form.vue'; defineOptions({ name: 'MesMdItem' }); -const selectedItemTypeId = ref(); +const selectedItemTypeId = ref(); // 当前选中的物料分类编号 const [FormModal, formModalApi] = useVbenModal({ connectedComponent: Form, diff --git a/apps/web-ele/src/views/mes/md/item/modules/form.vue b/apps/web-ele/src/views/mes/md/item/modules/form.vue index b22a02e92..5066ece1a 100644 --- a/apps/web-ele/src/views/mes/md/item/modules/form.vue +++ b/apps/web-ele/src/views/mes/md/item/modules/form.vue @@ -24,12 +24,12 @@ type FormMode = 'create' | 'detail' | 'update'; defineOptions({ name: 'MesMdItemForm' }); const emit = defineEmits(['success']); -const formMode = ref('create'); -const subTabsName = ref('bom'); +const formMode = ref('create'); // 表单模式 +const subTabsName = ref('bom'); // 当前子表页签 const formData = ref(); -const barcodeDetailRef = ref>(); +const barcodeDetailRef = ref>(); // 条码详情弹窗 -const isDetail = computed(() => formMode.value === 'detail'); +const isDetail = computed(() => formMode.value === 'detail'); // 是否查看模式 const getTitle = computed(() => { const titles: Record = { create: '新增物料/产品', @@ -38,7 +38,7 @@ const getTitle = computed(() => { }; return titles[formMode.value]; }); -const currentItemOrProduct = computed(() => formData.value?.itemOrProduct || ''); +const currentItemOrProduct = computed(() => formData.value?.itemOrProduct || ''); // 当前物料/产品标识 const [Form, formApi] = useVbenForm({ commonConfig: { @@ -57,6 +57,7 @@ const [Form, formApi] = useVbenForm({ /** 表单 schema 需要 formApi 引用,所以通过 setState 设置 schema */ formApi.setState({ schema: useFormSchema(formApi) }); +/** 查看物料条码 */ function handleBarcode() { if (!formData.value?.id) { return; diff --git a/apps/web-ele/src/views/mes/md/item/modules/item-batch-config-form.vue b/apps/web-ele/src/views/mes/md/item/modules/item-batch-config-form.vue index dbbcc7539..15c6afd9c 100644 --- a/apps/web-ele/src/views/mes/md/item/modules/item-batch-config-form.vue +++ b/apps/web-ele/src/views/mes/md/item/modules/item-batch-config-form.vue @@ -25,10 +25,11 @@ const props = withDefaults( }, ); -const isReadOnly = computed(() => props.formType === 'detail'); -const loading = ref(false); +const isReadOnly = computed(() => props.formType === 'detail'); // 是否只读 +const loading = ref(false); // 批次属性加载/保存中 const formData = ref(buildDefaultData()); +/** 构建批次属性默认值 */ function buildDefaultData(): MesMdItemBatchConfigApi.BatchConfig { return { itemId: props.itemId, @@ -49,6 +50,7 @@ function buildDefaultData(): MesMdItemBatchConfigApi.BatchConfig { }; } +/** 加载批次属性配置 */ async function loadData() { loading.value = true; try { @@ -59,12 +61,14 @@ async function loadData() { } } +/** 判断是否已选择批次属性 */ function hasAnySelected() { return Object.entries(formData.value).some( ([key, value]) => key.endsWith('Flag') && value === true, ); } +/** 保存批次属性配置 */ async function handleSave() { if (!hasAnySelected()) { ElMessage.warning('至少选择一个批次属性'); diff --git a/apps/web-ele/src/views/mes/md/item/modules/product-bom-form.vue b/apps/web-ele/src/views/mes/md/item/modules/product-bom-form.vue index 3f1ff71be..852909887 100644 --- a/apps/web-ele/src/views/mes/md/item/modules/product-bom-form.vue +++ b/apps/web-ele/src/views/mes/md/item/modules/product-bom-form.vue @@ -32,12 +32,12 @@ const props = withDefaults( }, ); -const isReadOnly = computed(() => props.formType === 'detail'); -const formOpen = ref(false); -const formLoading = ref(false); +const isReadOnly = computed(() => props.formType === 'detail'); // 是否只读 +const formOpen = ref(false); // BOM 表单是否打开 +const formLoading = ref(false); // BOM 表单提交中 const formData = ref(); -const list = ref([]); -const itemSelectRef = ref>(); +const list = ref([]); // 产品 BOM 列表 +const itemSelectRef = ref>(); // 物料选择弹窗 const [Form, formApi] = useVbenForm({ commonConfig: { @@ -120,6 +120,7 @@ const [Grid, gridApi] = useVbenVxeGrid({ } as VxeTableGridOptions, }); +/** 加载产品 BOM 列表 */ async function getList() { gridApi.setLoading(true); try { @@ -130,10 +131,12 @@ async function getList() { } } +/** 打开 BOM 物料选择弹窗 */ function handleAdd() { itemSelectRef.value?.open(undefined, { multiple: true }); } +/** 添加选中的 BOM 物料 */ async function handleItemSelected(rows: MesMdItemApi.Item[]) { if (rows.length === 0) { return; @@ -149,6 +152,7 @@ async function handleItemSelected(rows: MesMdItemApi.Item[]) { await getList(); } +/** 打开 BOM 编辑弹窗 */ async function openForm(row: MesMdProductBomApi.ProductBom) { formOpen.value = true; formData.value = row; @@ -159,6 +163,7 @@ async function openForm(row: MesMdProductBomApi.ProductBom) { }); } +/** 提交 BOM 表单 */ async function submitForm() { const { valid } = await formApi.validate(); if (!valid) { @@ -176,6 +181,7 @@ async function submitForm() { } } +/** 删除 BOM 物料 */ async function handleDelete(id: number) { await deleteProductBom(id); ElMessage.success($t('ui.actionMessage.deleteSuccess', ['BOM'])); diff --git a/apps/web-ele/src/views/mes/md/item/modules/product-media-list.vue b/apps/web-ele/src/views/mes/md/item/modules/product-media-list.vue index a0ad8eef1..cf6dc1191 100644 --- a/apps/web-ele/src/views/mes/md/item/modules/product-media-list.vue +++ b/apps/web-ele/src/views/mes/md/item/modules/product-media-list.vue @@ -46,13 +46,13 @@ const props = withDefaults( }, ); -const isReadOnly = computed(() => props.formType === 'detail'); -const title = computed(() => props.kind); -const loading = ref(false); -const formOpen = ref(false); -const formLoading = ref(false); +const isReadOnly = computed(() => props.formType === 'detail'); // 是否只读 +const title = computed(() => props.kind); // 当前媒体类型标题 +const loading = ref(false); // SIP/SOP 列表加载中 +const formOpen = ref(false); // SIP/SOP 表单是否打开 +const formLoading = ref(false); // SIP/SOP 表单提交中 const formData = ref(); -const list = ref([]); +const list = ref([]); // SIP/SOP 列表 const [Form, formApi] = useVbenForm({ commonConfig: { @@ -112,24 +112,29 @@ const [Form, formApi] = useVbenForm({ showDefaultActions: false, }); +/** 获取当前媒体类型的列表接口 */ function getListApi() { return props.kind === 'SIP' ? getProductSipListByItemId : getProductSopListByItemId; } +/** 获取当前媒体类型的新增接口 */ function createApi() { return props.kind === 'SIP' ? createProductSip : createProductSop; } +/** 获取当前媒体类型的修改接口 */ function updateApi() { return props.kind === 'SIP' ? updateProductSip : updateProductSop; } +/** 获取当前媒体类型的删除接口 */ function deleteApi() { return props.kind === 'SIP' ? deleteProductSip : deleteProductSop; } +/** 加载 SIP/SOP 列表 */ async function getList() { loading.value = true; try { @@ -139,6 +144,7 @@ async function getList() { } } +/** 打开 SIP/SOP 表单 */ async function openForm(row?: MediaItem) { formOpen.value = true; formData.value = row; @@ -150,6 +156,7 @@ async function openForm(row?: MediaItem) { }); } +/** 提交 SIP/SOP 表单 */ async function submitForm() { const { valid } = await formApi.validate(); if (!valid) { @@ -167,6 +174,7 @@ async function submitForm() { } } +/** 删除 SIP/SOP */ async function handleDelete(id: number) { await deleteApi()(id); ElMessage.success('删除成功'); diff --git a/apps/web-ele/src/views/mes/md/item/type/components/md-item-type-select.vue b/apps/web-ele/src/views/mes/md/item/type/components/md-item-type-select.vue index 6616dd2f3..d8c0c2d4b 100644 --- a/apps/web-ele/src/views/mes/md/item/type/components/md-item-type-select.vue +++ b/apps/web-ele/src/views/mes/md/item/type/components/md-item-type-select.vue @@ -36,11 +36,11 @@ type ItemTypeNode = MesMdItemTypeApi.ItemType & { disabled?: boolean; }; -const allList = ref([]); -const itemTypeTree = ref([]); -const selectedItem = ref(); +const allList = ref([]); // 物料分类列表 +const itemTypeTree = ref([]); // 物料分类树 +const selectedItem = ref(); // 当前选中分类 -const selectValue = computed({ +const selectValue = computed({ // 选择器绑定值 get: () => props.modelValue, set: (value: number | undefined) => { emit('update:modelValue', value); diff --git a/apps/web-ele/src/views/mes/md/item/type/components/md-item-type-tree.vue b/apps/web-ele/src/views/mes/md/item/type/components/md-item-type-tree.vue index afeba53bb..ee8e12804 100644 --- a/apps/web-ele/src/views/mes/md/item/type/components/md-item-type-tree.vue +++ b/apps/web-ele/src/views/mes/md/item/type/components/md-item-type-tree.vue @@ -25,12 +25,12 @@ const emit = defineEmits<{ nodeClick: [itemType: MesMdItemTypeApi.ItemType | undefined]; }>(); -const loading = ref(false); -const filterText = ref(''); -const currentNodeId = ref(); -const itemTypeList = ref([]); -const itemTypeTree = ref([]); -const treeRef = ref>(); +const loading = ref(false); // 分类树加载中 +const filterText = ref(''); // 分类搜索关键字 +const currentNodeId = ref(); // 当前选中分类编号 +const itemTypeList = ref([]); // 物料分类列表 +const itemTypeTree = ref([]); // 物料分类树 +const treeRef = ref>(); // 分类树实例 /** 加载分类树 */ async function loadTree() { diff --git a/apps/web-ele/src/views/mes/md/item/type/index.vue b/apps/web-ele/src/views/mes/md/item/type/index.vue index e334a5eec..525c8354f 100644 --- a/apps/web-ele/src/views/mes/md/item/type/index.vue +++ b/apps/web-ele/src/views/mes/md/item/type/index.vue @@ -22,8 +22,9 @@ const [FormModal, formModalApi] = useVbenModal({ destroyOnClose: true, }); +const isExpanded = ref(true); // 树形表格是否展开 + /** 切换树形展开/收缩状态 */ -const isExpanded = ref(true); function handleExpand() { isExpanded.value = !isExpanded.value; gridApi.grid.setAllTreeExpand(isExpanded.value);