2 Commits

Author SHA1 Message Date
dxc
6cf5a25d77 V3.44 2026-06-10 12:16:00 +08:00
DXC
6686747e57 BomManage: saveDraftData 简化为全局唯一草稿静默覆盖模式 2026-06-10 12:11:53 +08:00
2 changed files with 122 additions and 102 deletions

View File

@ -239,7 +239,7 @@ const handleLogout = () => {
<footer v-if="!isLoginPage" class="app-footer"> <footer v-if="!isLoginPage" class="app-footer">
<span class="version-tag"> <span class="version-tag">
<el-icon style="vertical-align: middle; margin-right: 4px"><InfoFilled /></el-icon> <el-icon style="vertical-align: middle; margin-right: 4px"><InfoFilled /></el-icon>
当前版本:V3.43 当前版本:V3.44
</span> </span>
</footer> </footer>

View File

@ -542,34 +542,16 @@ const getOrGenerateTempBomNo = (): string => {
} }
const handleCreate = async () => { const handleCreate = async () => {
const tempBomNo = getOrGenerateTempBomNo() // 1. 全局拦截
pendingDraftBomNo = tempBomNo const isIntercepted = await checkAndInterceptGlobalDraft()
pendingDraftVersion = 'V1.0' if (isIntercepted) return
try {
const res = await getDraftDetail({ bom_no: pendingDraftBomNo, version: pendingDraftVersion })
if (res.code === 200 && res.data) {
const confirm = await ElMessageBox.confirm(
'检测到未发布的草稿,是否恢复继续编辑?',
'草稿恢复',
{ confirmButtonText: '恢复草稿', cancelButtonText: '放弃草稿', type: 'info' }
).catch(() => null)
if (confirm) {
restoreDraftToForm(res.data)
dialogTitle.value = '新建 BOM'
isEditMode.value = false
isSaveAsMode.value = false
isReadOnlyMode.value = false
dialogVisible.value = true
return
} else {
resetForm()
}
}
} catch (e) {}
// 2. 正常的纯新建逻辑
resetForm() resetForm()
const tempBomNo = getOrGenerateTempBomNo()
form.bom_no = tempBomNo
originalDraftHash.value = getDraftHash()
dialogTitle.value = '新建 BOM' dialogTitle.value = '新建 BOM'
isEditMode.value = false isEditMode.value = false
isSaveAsMode.value = false isSaveAsMode.value = false
@ -578,45 +560,16 @@ const handleCreate = async () => {
} }
const saveDraftData = async () => { const saveDraftData = async () => {
// 获取当前正在编辑的单号
const draftBomNo = form.bom_no || getOrGenerateTempBomNo() const draftBomNo = form.bom_no || getOrGenerateTempBomNo()
const currentHash = getDraftHash() const currentHash = getDraftHash()
// 场景 A之前已经存过/恢复过草稿,且没有任何改动 // 场景 A防呆拦截。如果数据没有任何改动,直接提示并阻断多余的网络请求
if (originalDraftHash.value && currentHash === originalDraftHash.value) { if (originalDraftHash.value && currentHash === originalDraftHash.value) {
return ElMessage.warning('草稿数据无变动,无需重复暂存') return ElMessage.warning('草稿数据无变动,无需重复暂存')
} }
// 场景 B已经存过草稿,且发生了改动 -> 询问覆盖还是新建 // 场景 B执行保存。在"全局唯一草稿"架构下不再询问直接实时同步最新状态到数据库
if (originalDraftHash.value && currentHash !== originalDraftHash.value) {
try {
const action = await ElMessageBox.confirm(
'检测到草稿内容已发生变动,请选择保存方式:',
'草稿变动提示',
{
confirmButtonText: '直接覆盖',
cancelButtonText: '另存为新草稿',
distinguishCancelAndClose: true,
type: 'warning'
}
)
if (action === 'confirm') {
// 选择覆盖原草稿
await executeSaveDraftRequest(draftBomNo)
}
} catch (action) {
if (action === 'cancel') {
// 选择另存为新草稿:生成新单号并保存
const ts = new Date().toISOString().replace(/[-T:.Z]/g, '').slice(0, 14)
const uid = Math.random().toString(36).slice(2, 6)
const newTempNo = `DRAFT-TEMP-${ts}-${uid}`
form.bom_no = newTempNo
await executeSaveDraftRequest(newTempNo)
}
}
return
}
// 场景 C第一次暂存
await executeSaveDraftRequest(draftBomNo) await executeSaveDraftRequest(draftBomNo)
} }
@ -642,6 +595,7 @@ const executeSaveDraftRequest = async (targetBomNo: string) => {
ElMessage.success('草稿暂存成功') ElMessage.success('草稿暂存成功')
form.bom_no = targetBomNo form.bom_no = targetBomNo
localStorage.setItem('pending_bom_draft_no', targetBomNo) localStorage.setItem('pending_bom_draft_no', targetBomNo)
localStorage.setItem('pending_bom_draft_version', draftVersion)
originalDraftHash.value = getDraftHash() originalDraftHash.value = getDraftHash()
} else { } else {
ElMessage.error(res.msg || '暂存失败') ElMessage.error(res.msg || '暂存失败')
@ -677,6 +631,71 @@ const restoreDraftToForm = (draftData: any) => {
originalDraftHash.value = getDraftHash() originalDraftHash.value = getDraftHash()
} }
const checkUserDraft = async (bomNo: string, version: string) => {
try {
const res = await getDraftDetail({ bom_no: bomNo, version: version })
if (res.code === 200 && res.data) {
const confirm = await ElMessageBox.confirm(
`检测到未发布的草稿数据,是否恢复继续编辑?`,
'草稿恢复',
{ confirmButtonText: '恢复草稿', cancelButtonText: '放弃草稿', type: 'info' }
).catch(() => 'cancel')
if (confirm === 'confirm') {
return res.data
}
}
} catch (e) {
// 网络异常或查不到均视为无草稿
}
return null
}
const checkAndInterceptGlobalDraft = async (): Promise<boolean> => {
const pendingNo = localStorage.getItem('pending_bom_draft_no')
const pendingVer = localStorage.getItem('pending_bom_draft_version') || 'V1.0'
if (pendingNo) {
try {
const res = await getDraftDetail({ bom_no: pendingNo, version: pendingVer })
if (res.code === 200 && res.data) {
const action = await ElMessageBox.confirm(
`系统检测到您有一个未发布的草稿 [${pendingNo}]。\n\n为防止数据混乱同一时间只允许保留一份草稿。\n• 点击【恢复草稿】继续编辑\n• 点击【放弃旧草稿】彻底销毁它并开启新任务`,
'全局草稿拦截',
{
confirmButtonText: '恢复草稿',
cancelButtonText: '放弃旧草稿',
distinguishCancelAndClose: true,
type: 'warning'
}
).catch(action => action)
if (action === 'confirm') {
resetForm()
restoreDraftToForm(res.data)
originalDraftHash.value = getDraftHash()
dialogTitle.value = '继续编辑草稿'
isEditMode.value = false
isSaveAsMode.value = false
isReadOnlyMode.value = false
dialogVisible.value = true
return true
} else if (action === 'cancel') {
localStorage.removeItem('pending_bom_draft_no')
localStorage.removeItem('pending_bom_draft_version')
return false
} else {
return true
}
}
} catch (e) {
localStorage.removeItem('pending_bom_draft_no')
localStorage.removeItem('pending_bom_draft_version')
}
}
return false
}
const handleView = async (row: BomItem) => { const handleView = async (row: BomItem) => {
await loadDetail(row.bom_no, row.version) await loadDetail(row.bom_no, row.version)
dialogTitle.value = '查看 BOM' dialogTitle.value = '查看 BOM'
@ -686,54 +705,54 @@ const handleView = async (row: BomItem) => {
dialogVisible.value = true dialogVisible.value = true
} }
const handleSaveAs = async (row: BomItem) => { const handleSaveAs = async (row: any) => {
// 1. 重置 form 基础状态 // 1. 全局拦截
resetForm() const isIntercepted = await checkAndInterceptGlobalDraft()
if (isIntercepted) return
// 2. 获取源 BOM 详情(不通过 loadDetail显式走"深拷贝+ ID"路径) // 2. 正常的纯另存为逻辑
const res = await getBomDetail(row.bom_no, row.version) resetForm()
if (res.code !== 200) return const res = await getBomDetail(row.bom_no, row.version)
const raw = JSON.parse(JSON.stringify(res.data)) if (res.code !== 200) return
const raw = JSON.parse(JSON.stringify(res.data))
// 3. ★ 核心:显式深拷贝 + 清除所有主键 ID防止后端误判为更新操作 if ('id' in raw) delete raw.id
if ('id' in raw) delete raw.id if ('bom_id' in raw) delete raw.bom_id
if ('bom_id' in raw) delete raw.bom_id if (Array.isArray(raw.children)) {
if (Array.isArray(raw.children)) { raw.children.forEach((c: any) => {
raw.children.forEach((c: any) => { if ('id' in c) delete c.id
if ('id' in c) delete c.id if ('bom_id' in c) delete c.bom_id
if ('bom_id' in c) delete c.bom_id })
}) }
}
// 4. 把"已清除 ID 的纯净数据"写入 form form.children = raw.children.map((child: any, idx: number) => ({
form.children = raw.children.map((child: any, idx: number) => ({ rowKey: idx,
rowKey: idx, child_id: child.child_id,
child_id: child.child_id, material_name: child.child_name || '未知物料',
material_name: child.child_name || '未知物料', material_spec: child.child_spec || '',
material_spec: child.child_spec || '', dosage: child.dosage,
dosage: child.dosage, remark: child.remark || ''
remark: child.remark || '' }))
})) if (raw.parent_id) {
if (raw.parent_id) { form.parent_id = raw.parent_id
form.parent_id = raw.parent_id parentNameInput.value = raw.parent_name || '未知产品'
parentNameInput.value = raw.parent_name || '未知产品' }
}
form.bom_no = (raw.parent_spec || row.bom_no).split('/')[0].trim()
form.remark = raw.remark || ''
// 5. 设置"另存为"模式特有状态(版本升级单选 + 子件变更检测) form.bom_no = (raw.parent_spec || row.bom_no).split('/')[0].trim()
originalVersion = raw.version || '' form.remark = raw.remark || ''
currentBomNo = row.bom_no form.versionUpgradeType = 'minor'
originalChildren = JSON.parse(JSON.stringify(form.children)) form.version = versionOptions.value?.minor || 'V1.1'
form.versionUpgradeType = 'minor'
form.version = versionOptions.value.minor
// 6. 弹窗状态机:标题"新增 BOM"父件可改启用版本升级单选 originalVersion = raw.version || ''
dialogTitle.value = '新增 BOM' currentBomNo = row.bom_no
isEditMode.value = false originalChildren = JSON.parse(JSON.stringify(form.children))
isSaveAsMode.value = true originalDraftHash.value = getDraftHash()
isReadOnlyMode.value = false
dialogVisible.value = true dialogTitle.value = '新增 BOM (版本升级)'
isEditMode.value = false
isSaveAsMode.value = true
isReadOnlyMode.value = false
dialogVisible.value = true
} }
const loadDetail = async (bomNo: string, version: string) => { const loadDetail = async (bomNo: string, version: string) => {
@ -844,6 +863,7 @@ const submitForm = async () => {
if (res.code === 200) { if (res.code === 200) {
ElMessage.success('发布成功') ElMessage.success('发布成功')
localStorage.removeItem('pending_bom_draft_no') localStorage.removeItem('pending_bom_draft_no')
localStorage.removeItem('pending_bom_draft_version')
originalDraftHash.value = '' originalDraftHash.value = ''
dialogVisible.value = false dialogVisible.value = false
fetchBomList() fetchBomList()