BOM 配方管理:禁止编辑原数据,引入另存为(深拷贝+清 ID)+ 只读查看模式(点击编号进只读弹窗)

This commit is contained in:
DXC
2026-06-04 14:44:29 +08:00
parent 90eed24441
commit 2977acbae7

View File

@ -35,7 +35,7 @@
<el-table :data="group.items" border style="width: 100%">
<el-table-column v-if="hasColumnPermission('bom_no')" prop="bom_no" label="BOM编号" min-width="180" sortable>
<template #default="{ row }">
<span style="cursor: pointer; color: #409EFF;" @click="handleEdit(row)">{{ row.bom_no }}</span>
<span style="cursor: pointer; color: #409EFF;" @click="handleView(row)">{{ row.bom_no }}</span>
</template>
</el-table-column>
<el-table-column v-if="hasColumnPermission('parent_name')" prop="parent_name" label="父件名称" min-width="150" show-overflow-tooltip />
@ -51,9 +51,8 @@
</template>
</el-table-column>
<el-table-column v-if="hasColumnPermission('child_count')" prop="child_count" label="子件数" width="80" align="center" />
<el-table-column v-if="userStore.hasPermission('bom_manage:operation')" label="操作" width="250" align="center" fixed="right">
<el-table-column v-if="userStore.hasPermission('bom_manage:operation')" label="操作" width="200" align="center" fixed="right">
<template #default="{ row }">
<el-button type="primary" link @click="handleEdit(row)">编辑</el-button>
<el-button type="success" link @click="handleSaveAs(row)">另存为</el-button>
<el-button type="danger" link @click="handleDelete(row)">删除</el-button>
</template>
@ -80,7 +79,7 @@
:remote-method="(q: string) => handleRemoteSearch(q, 'parent')"
:loading="selectLoading"
style="width: 100%"
:disabled="isEditMode"
:disabled="isReadOnlyMode || isEditMode"
class="beautified-select"
popper-class="bom-loadmore-popper parent-popper"
@visible-change="(visible: boolean) => handleVisibleChange(visible, 'parent')"
@ -99,7 +98,7 @@
</el-option>
</el-select>
<el-link
v-if="form.parent_id"
v-if="form.parent_id && !isReadOnlyMode"
type="primary"
:underline="false"
style="margin-left: 12px; font-size: 13px;"
@ -114,7 +113,7 @@
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="是否启用" prop="is_enabled" v-if="hasFormFieldPermission('is_enabled')">
<el-switch v-model="form.is_enabled" active-text="启用" inactive-text="禁用" :disabled="!userStore.hasPermission('bom_manage:operation')" />
<el-switch v-model="form.is_enabled" active-text="启用" inactive-text="禁用" :disabled="isReadOnlyMode || !userStore.hasPermission('bom_manage:operation')" />
</el-form-item>
</el-col>
<el-col :span="16"></el-col>
@ -131,19 +130,19 @@
</el-col>
<el-col :span="6">
<el-form-item label="备注" v-if="hasFormFieldPermission('remark')">
<el-input v-model="form.remark" placeholder="备注信息可选" />
<el-input v-model="form.remark" placeholder="备注信息可选" :disabled="isReadOnlyMode" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="版本号" prop="version" v-if="hasFormFieldPermission('version')">
<template v-if="isSaveAsMode">
<el-radio-group v-model="form.versionUpgradeType" @change="onVersionUpgradeTypeChange">
<el-radio-group v-model="form.versionUpgradeType" :disabled="isReadOnlyMode" @change="onVersionUpgradeTypeChange">
<el-radio-button label="minor">升级次版本 ({{ versionOptions.minor }})</el-radio-button>
<el-radio-button label="major">升级主版本 ({{ versionOptions.major }})</el-radio-button>
</el-radio-group>
</template>
<template v-else>
<el-input v-model="form.version" placeholder=": V1.0" />
<el-input v-model="form.version" placeholder=": V1.0" :disabled="isReadOnlyMode" />
</template>
</el-form-item>
</el-col>
@ -160,6 +159,7 @@
clearable
style="width: 300px; margin-bottom: 10px;"
:prefix-icon="Search"
:disabled="isReadOnlyMode"
/>
<el-table :data="filteredChildren" border style="width: 100%; margin-bottom: 15px" max-height="300">
@ -178,6 +178,7 @@
:loading="selectLoading"
:loading-text="`正在加载第 ${childQueryParams.page} 页...`"
:popper-class="`bom-loadmore-popper child-popper-${row.rowKey}`"
:disabled="isReadOnlyMode"
@visible-change="(visible: boolean) => handleVisibleChange(visible, 'child', row.rowKey)"
>
<el-option
@ -192,7 +193,7 @@
</div>
</el-option>
</el-select>
<el-tooltip content="前往修改基础信息" placement="top" v-if="row.child_id">
<el-tooltip content="前往修改基础信息" placement="top" v-if="row.child_id && !isReadOnlyMode">
<el-button
type="primary"
link
@ -207,32 +208,32 @@
<el-table-column label="用量" width="140" v-if="hasFormFieldPermission('dosage')">
<template #default="{ row }">
<el-input-number v-model="row.dosage" :min="0" :precision="0" style="width: 100%" :controls="false" />
<el-input-number v-model="row.dosage" :min="0" :precision="0" style="width: 100%" :controls="false" :disabled="isReadOnlyMode" />
</template>
</el-table-column>
<el-table-column label="备注" width="150" v-if="hasFormFieldPermission('remark')">
<template #default="{ row }">
<el-input v-model="row.remark" placeholder="备注" />
<el-input v-model="row.remark" placeholder="备注" :disabled="isReadOnlyMode" />
</template>
</el-table-column>
<el-table-column label="操作" width="60" align="center" v-if="userStore.hasPermission('bom_manage:operation')">
<template #default="{ row }">
<el-button type="danger" link @click="removeChild(row.rowKey)">删</el-button>
<el-button v-if="!isReadOnlyMode" type="danger" link @click="removeChild(row.rowKey)">删</el-button>
</template>
</el-table-column>
</el-table>
<div style="margin-top: 10px; text-align: center;" v-if="hasFormFieldPermission('child_id')">
<div style="margin-top: 10px; text-align: center;" v-if="hasFormFieldPermission('child_id') && !isReadOnlyMode">
<el-button type="primary" plain :icon="Plus" @click="addChild" style="width: 100%">添加一行子件</el-button>
</div>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" :loading="saving" @click="submitForm">保存</el-button>
<el-button @click="dialogVisible = false">{{ isReadOnlyMode ? '关闭' : '取消' }}</el-button>
<el-button v-if="!isReadOnlyMode" type="primary" :loading="saving" @click="submitForm">保存</el-button>
</span>
</template>
</el-dialog>
@ -280,6 +281,7 @@ const dialogVisible = ref(false)
const saving = ref(false)
const isEditMode = ref(false)
const isSaveAsMode = ref(false)
const isReadOnlyMode = ref(false)
let originalVersion = ''
let currentBomNo = ''
let originalChildren: ChildRow[] = []
@ -674,27 +676,80 @@ const handleCreate = () => {
dialogTitle.value = '新建 BOM'
isEditMode.value = false
isSaveAsMode.value = false
isReadOnlyMode.value = false
dialogVisible.value = true
}
const handleEdit = async (row: BomItem) => {
const handleView = async (row: BomItem) => {
await loadDetail(row.bom_no, row.version)
dialogTitle.value = '编辑 BOM'
isEditMode.value = true
dialogTitle.value = '查看 BOM'
isEditMode.value = false
isSaveAsMode.value = false
isReadOnlyMode.value = true
dialogVisible.value = true
}
const handleSaveAs = async (row: BomItem) => {
await loadDetail(row.bom_no, row.version)
dialogTitle.value = '另存为新版/变体'
isEditMode.value = true
isSaveAsMode.value = true
originalVersion = form.version
// 1. 重置 form 基础状态
resetForm()
// 2. 获取源 BOM 详情(不通过 loadDetail显式走"深拷贝+ ID"路径)
const res = await getBomDetail(row.bom_no, row.version)
if (res.code !== 200) return
const raw = JSON.parse(JSON.stringify(res.data))
// 3. ★ 核心:显式深拷贝 + 清除所有主键 ID防止后端误判为更新操作
if ('id' in raw) delete raw.id
if ('bom_id' in raw) delete raw.bom_id
if (Array.isArray(raw.children)) {
raw.children.forEach((c: any) => {
if ('id' in c) delete c.id
if ('bom_id' in c) delete c.bom_id
})
}
// 4. 把"已清除 ID 的纯净数据"写入 form保留子件下拉回显 + 父件下拉回显)
form.children = raw.children.map((child: any, idx: number) => ({
rowKey: idx,
child_id: child.child_id,
dosage: child.dosage,
remark: child.remark || ''
}))
form.children.forEach((child, idx) => {
initChildDropdownState(idx)
if (child.child_id) {
const state = childDropdownStates.value.get(idx)!
state.options = [{
id: raw.children[idx].child_id,
name: raw.children[idx].child_name || '未知物料',
spec: raw.children[idx].child_spec || ''
}]
state.hasMore = false
}
})
if (raw.parent_id) {
form.parent_id = raw.parent_id
parentOptions.value = [{
id: raw.parent_id,
name: raw.parent_name || '未知产品',
spec: raw.parent_spec || ''
}]
}
form.bom_no = (raw.parent_spec || row.bom_no).split('/')[0].trim()
form.remark = raw.remark || ''
// 5. 设置"另存为"模式特有状态(版本升级单选 + 子件变更检测)
originalVersion = raw.version || ''
currentBomNo = row.bom_no
originalChildren = JSON.parse(JSON.stringify(form.children))
form.versionUpgradeType = 'minor'
form.version = versionOptions.value.minor
// 6. 弹窗状态机:标题"新增 BOM"父件可改启用版本升级单选
dialogTitle.value = '新增 BOM'
isEditMode.value = false
isSaveAsMode.value = true
isReadOnlyMode.value = false
dialogVisible.value = true
}
@ -768,6 +823,7 @@ const resetForm = () => {
form.is_enabled = true
form.children = []
isSaveAsMode.value = false
isReadOnlyMode.value = false
originalVersion = ''
currentBomNo = ''
childSearchKeyword.value = ''
@ -864,9 +920,9 @@ onMounted(() => {
}
if (existingBom) {
// ★ 情况 A已经有BOM了直接打开编辑(查看)弹窗
// ★ 情况 A已经有BOM了直接打开只读查看弹窗
ElMessage.success('检测到该物料已有 BOM已自动为您打开');
handleEdit(existingBom);
handleView(existingBom);
} else {
// ★ 情况 B还没建过BOM打开新建并注入父件
handleCreate();