diff --git a/inventory-backend/app/services/bom_service.py b/inventory-backend/app/services/bom_service.py index 8bd7802..61a20a6 100644 --- a/inventory-backend/app/services/bom_service.py +++ b/inventory-backend/app/services/bom_service.py @@ -143,7 +143,7 @@ class BomService: @staticmethod def save_bom(data): - """保存 BOM (支持多版本)""" + """保存 BOM (支持多版本),新增跨版本内容查重""" bom_no = data.get('bom_no') version = data.get('version', 'V1.0') parent_id = data['parent_id'] @@ -157,6 +157,38 @@ class BomService: if child['child_id'] == parent_id: raise ValueError('父件与子件不能是同一物料') + # ===== 跨版本内容查重 ===== + # 将当前提交的 children 转换为可比较的集合 (child_id, dosage) + current_children_set = set() + for child in children: + # 用 (child_id, dosage) 元组表示,dosage 转为整数比较 + dosage_val = int(child.get('dosage', 0)) if child.get('dosage') else 0 + current_children_set.add((child['child_id'], dosage_val)) + + # 查询该 bom_no 下所有其他版本的子件配置 + existing_versions = db.session.query( + BomTable.version, + BomTable.child_id, + BomTable.dosage + ).filter( + BomTable.bom_no == bom_no, + BomTable.version != version # 排除当前正在保存的版本 + ).all() + + # 按版本分组,构建每个版本的子件集合 + version_children = {} + for ver, child_id, dosage in existing_versions: + if ver not in version_children: + version_children[ver] = set() + dosage_val = int(dosage) if dosage else 0 + version_children[ver].add((child_id, dosage_val)) + + # 比对每个版本 + for ver, existing_set in version_children.items(): + if current_children_set == existing_set: + raise ValueError(f'保存失败!当前子件配置与已有版本 {ver} 完全一致,请勿重复保存') + + # ===== 执行保存 ===== # 仅删除当前版本的旧记录 BomTable.query.filter_by(bom_no=bom_no, version=version).delete() diff --git a/inventory-web/src/views/bom/BomManage.vue b/inventory-web/src/views/bom/BomManage.vue index 4b44476..9a209e8 100644 --- a/inventory-web/src/views/bom/BomManage.vue +++ b/inventory-web/src/views/bom/BomManage.vue @@ -224,6 +224,7 @@ const isEditMode = ref(false) const isSaveAsMode = ref(false) // 任务1:标记是否为另存为模式 let originalVersion = '' // 保存原始版本号用于计算升级选项 let currentBomNo = '' // 保存当前操作的 BOM 编号用于计算版本避让 +let originalChildren: ChildRow[] = [] // 保存原始子件数据用于本地查重 const bomList = ref([]) const materialOptions = ref([]) @@ -441,6 +442,8 @@ const handleSaveAs = async (row: BomItem) => { // 保存原始版本号和 BOM 编号用于计算升级选项 originalVersion = form.version currentBomNo = row.bom_no // 保存当前操作的 BOM 编号 + // 保存原始子件数据用于本地查重 + originalChildren = JSON.parse(JSON.stringify(form.children)) // 默认选中次版本升级 form.versionUpgradeType = 'minor' form.version = versionOptions.value.minor @@ -524,6 +527,21 @@ const submitForm = async () => { return ElMessage.warning('子件列表中存在重复物料,请合并用量或删除重复项') } + // ===== 另存为本地查重:检查是否修改过 ===== + if (isSaveAsMode.value && originalChildren.length > 0) { + // 将当前 children 和原始 children 转换为 set 进行比较 + const currentSet = new Set(form.children.map(c => `${c.child_id}-${c.dosage}`)) + const originalSet = new Set(originalChildren.map(c => `${c.child_id}-${c.dosage}`)) + + // 比较两个集合是否完全相同 + const isIdentical = currentSet.size === originalSet.size && + [...currentSet].every(item => originalSet.has(item)) + + if (isIdentical) { + return ElMessage.warning('您未修改任何子件,与原版本内容一致,请修改后再保存') + } + } + const payload = { bom_no: fullBomNo.value, version: form.version, @@ -540,8 +558,11 @@ const submitForm = async () => { dialogVisible.value = false fetchBomList() } else { ElMessage.error(res.msg || '保存失败') } - } catch (e) { - // 错误已由全局拦截器统一处理 + } catch (e: any) { + // 确保后端返回的错误信息能正确展示 + if (e.response?.data?.msg) { + ElMessage.error(e.response.data.msg) + } } finally { saving.value = false } })