fix: BOM草稿模块缺陷修复(事务回滚 + 外键约束 + 前端状态清理)
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
from flask import Blueprint, request, jsonify, current_app
|
||||
from sqlalchemy import or_
|
||||
from app.services.bom_service import BomService, _cache_delete
|
||||
from app.services.bom_draft_service import BomDraftService
|
||||
from app.models.base import MaterialBase
|
||||
from app.models.bom import BomTable
|
||||
from app.extensions import db
|
||||
@ -420,3 +421,61 @@ def get_cascade_inventory():
|
||||
except Exception as e:
|
||||
current_app.logger.error(f'级联库存计算失败: {str(e)}')
|
||||
return jsonify({'code': 500, 'msg': '内部服务器错误'}), 500
|
||||
|
||||
|
||||
# ==================== BOM 草稿接口 ====================
|
||||
|
||||
@bom_bp.route('/draft/save', methods=['POST'])
|
||||
@jwt_required()
|
||||
def save_draft():
|
||||
"""暂存草稿"""
|
||||
data = request.get_json()
|
||||
bom_no = data.get('bom_no')
|
||||
version = data.get('version', 'V1.0')
|
||||
parent_id = data.get('parent_id')
|
||||
children = data.get('children', [])
|
||||
|
||||
if not bom_no:
|
||||
return jsonify({'code': 400, 'msg': 'bom_no 不能为空'}), 400
|
||||
if not parent_id:
|
||||
return jsonify({'code': 400, 'msg': 'parent_id 不能为空'}), 400
|
||||
|
||||
bom_draft_no = BomDraftService.save_draft(bom_no, version, parent_id, children)
|
||||
return jsonify({'code': 200, 'msg': '草稿暂存成功', 'data': {'bom_no': bom_draft_no}})
|
||||
|
||||
|
||||
@bom_bp.route('/draft/detail', methods=['GET'])
|
||||
@jwt_required()
|
||||
def get_draft_detail():
|
||||
"""读取草稿详情"""
|
||||
bom_no = request.args.get('bom_no')
|
||||
version = request.args.get('version', 'V1.0')
|
||||
|
||||
if not bom_no:
|
||||
return jsonify({'code': 400, 'msg': 'bom_no 不能为空'}), 400
|
||||
|
||||
draft = BomDraftService.get_draft_detail(bom_no, version)
|
||||
|
||||
# 【核心修改】:查不到草稿是正常现象,返回 HTTP 200 即可
|
||||
if draft is None:
|
||||
return jsonify({'code': 200, 'msg': '无草稿', 'data': None}), 200
|
||||
|
||||
return jsonify({'code': 200, 'msg': '查询成功', 'data': draft})
|
||||
|
||||
|
||||
@bom_bp.route('/draft/publish', methods=['POST'])
|
||||
@jwt_required()
|
||||
def publish_draft():
|
||||
"""发布草稿为正式 BOM"""
|
||||
data = request.get_json()
|
||||
bom_no = data.get('bom_no')
|
||||
version = data.get('version', 'V1.0')
|
||||
|
||||
if not bom_no:
|
||||
return jsonify({'code': 400, 'msg': 'bom_no 不能为空'}), 400
|
||||
|
||||
try:
|
||||
bom_draft_no = BomDraftService.publish_draft(bom_no, version)
|
||||
return jsonify({'code': 200, 'msg': 'BOM 发布成功', 'data': {'bom_no': bom_draft_no}})
|
||||
except ValueError as e:
|
||||
return jsonify({'code': 400, 'msg': str(e)}), 400
|
||||
|
||||
@ -5,8 +5,18 @@ class BomTable(db.Model):
|
||||
__tablename__ = 'bom_table'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
parent_id = db.Column(db.Integer, db.ForeignKey('material_base.id'), nullable=False, index=True) # ★ 父子件关联高频列
|
||||
child_id = db.Column(db.Integer, db.ForeignKey('material_base.id'), nullable=False, index=True) # ★ 子件过滤高频列
|
||||
parent_id = db.Column(
|
||||
db.Integer,
|
||||
db.ForeignKey('material_base.id', ondelete='SET NULL'),
|
||||
nullable=False,
|
||||
index=True
|
||||
)
|
||||
child_id = db.Column(
|
||||
db.Integer,
|
||||
db.ForeignKey('material_base.id', ondelete='SET NULL'),
|
||||
nullable=False,
|
||||
index=True
|
||||
)
|
||||
|
||||
bom_no = db.Column(db.String(100), nullable=False, index=True, comment='BOM编号') # ★ Redis 缓存 Key + 列表查询核心列
|
||||
version = db.Column(db.String(50), nullable=False, default='V1.0', index=True, comment='版本') # ★ 配合 bom_no 做唯一性约束
|
||||
@ -24,5 +34,15 @@ class BomTable(db.Model):
|
||||
)
|
||||
|
||||
# relationships
|
||||
parent = db.relationship('MaterialBase', foreign_keys=[parent_id], backref='bom_parents')
|
||||
child = db.relationship('MaterialBase', foreign_keys=[child_id], backref='bom_children')
|
||||
parent = db.relationship(
|
||||
'MaterialBase',
|
||||
foreign_keys=[parent_id],
|
||||
backref='bom_parents',
|
||||
passive_deletes=True
|
||||
)
|
||||
child = db.relationship(
|
||||
'MaterialBase',
|
||||
foreign_keys=[child_id],
|
||||
backref='bom_children',
|
||||
passive_deletes=True
|
||||
)
|
||||
38
inventory-backend/app/models/bom_draft.py
Normal file
38
inventory-backend/app/models/bom_draft.py
Normal file
@ -0,0 +1,38 @@
|
||||
from app.extensions import db
|
||||
|
||||
|
||||
class BomDraftTable(db.Model):
|
||||
__tablename__ = 'bom_draft_table'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
bom_no = db.Column(db.String(100), nullable=False, index=True, comment='BOM编号')
|
||||
version = db.Column(db.String(50), nullable=False, default='V1.0', index=True, comment='版本')
|
||||
parent_id = db.Column(
|
||||
db.Integer,
|
||||
db.ForeignKey('material_base.id', ondelete='SET NULL'),
|
||||
nullable=True,
|
||||
comment='父件物料ID'
|
||||
)
|
||||
child_id = db.Column(
|
||||
db.Integer,
|
||||
db.ForeignKey('material_base.id', ondelete='SET NULL'),
|
||||
nullable=True,
|
||||
comment='子件物料ID'
|
||||
)
|
||||
dosage = db.Column(db.Numeric(19, 4), comment='个数')
|
||||
loss_rate = db.Column(db.Numeric(5, 2), default=0, nullable=True, comment='损耗率%')
|
||||
remark = db.Column(db.Text, comment='备注')
|
||||
updated_at = db.Column(db.DateTime, default=db.func.now(), onupdate=db.func.now(), comment='更新时间')
|
||||
|
||||
parent = db.relationship(
|
||||
'MaterialBase',
|
||||
foreign_keys=[parent_id],
|
||||
backref='bom_draft_parents',
|
||||
passive_deletes=True
|
||||
)
|
||||
child = db.relationship(
|
||||
'MaterialBase',
|
||||
foreign_keys=[child_id],
|
||||
backref='bom_draft_children',
|
||||
passive_deletes=True
|
||||
)
|
||||
145
inventory-backend/app/services/bom_draft_service.py
Normal file
145
inventory-backend/app/services/bom_draft_service.py
Normal file
@ -0,0 +1,145 @@
|
||||
from app.extensions import db
|
||||
from app.models.bom_draft import BomDraftTable
|
||||
from app.models.base import MaterialBase
|
||||
from app.services.bom_service import BomService
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BomDraftService:
|
||||
|
||||
@staticmethod
|
||||
def save_draft(bom_no, version, parent_id, children):
|
||||
try:
|
||||
# 1. 删除旧草稿
|
||||
old = BomDraftTable.query.filter_by(bom_no=bom_no, version=version).all()
|
||||
for rec in old:
|
||||
db.session.delete(rec)
|
||||
db.session.flush()
|
||||
|
||||
# 2. 如果没有任何子件,必须插入一条只包含 parent_id 的占位头数据
|
||||
if not children:
|
||||
dummy_draft = BomDraftTable(
|
||||
bom_no=bom_no, version=version, parent_id=parent_id,
|
||||
child_id=None, dosage=0, loss_rate=0, remark=''
|
||||
)
|
||||
db.session.add(dummy_draft)
|
||||
else:
|
||||
# 正常批量插入新草稿行
|
||||
for child in children:
|
||||
draft = BomDraftTable(
|
||||
bom_no=bom_no, version=version, parent_id=parent_id,
|
||||
child_id=child.get('child_id'),
|
||||
dosage=child.get('dosage', 0),
|
||||
loss_rate=child.get('loss_rate', 0),
|
||||
remark=child.get('remark', '')
|
||||
)
|
||||
db.session.add(draft)
|
||||
|
||||
db.session.commit()
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
logger.error(f"[BomDraft] save_draft 失败 bom_no={bom_no}: {e}")
|
||||
raise
|
||||
return bom_no
|
||||
|
||||
@staticmethod
|
||||
def get_draft_detail(bom_no, version):
|
||||
rows = db.session.query(
|
||||
BomDraftTable,
|
||||
MaterialBase.name.label('child_name'),
|
||||
MaterialBase.spec_model.label('child_spec')
|
||||
).outerjoin(
|
||||
MaterialBase, BomDraftTable.child_id == MaterialBase.id
|
||||
).filter(
|
||||
BomDraftTable.bom_no == bom_no,
|
||||
BomDraftTable.version == version
|
||||
).all()
|
||||
|
||||
if not rows:
|
||||
return None
|
||||
|
||||
first = rows[0].BomDraftTable
|
||||
parent_id = first.parent_id
|
||||
parent_material = MaterialBase.query.get(parent_id) if parent_id else None
|
||||
|
||||
children = []
|
||||
for draft, child_name, child_spec in rows:
|
||||
# 过滤掉保存 BOM 头时插入的占位空行
|
||||
if draft.child_id is not None:
|
||||
children.append({
|
||||
'child_id': draft.child_id,
|
||||
'child_name': child_name or '',
|
||||
'child_spec': child_spec or '',
|
||||
'dosage': float(draft.dosage) if draft.dosage else 0.0,
|
||||
'loss_rate': float(draft.loss_rate) if draft.loss_rate else 0.0,
|
||||
'remark': draft.remark or '',
|
||||
})
|
||||
|
||||
return {
|
||||
'bom_no': bom_no,
|
||||
'version': first.version,
|
||||
'parent_id': parent_id,
|
||||
'parent_name': parent_material.name if parent_material else '',
|
||||
'parent_spec': parent_material.spec_model if parent_material else '',
|
||||
'children': children,
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def publish_draft(bom_no, version):
|
||||
"""
|
||||
发布草稿为正式 BOM:
|
||||
1. 获取草稿数据
|
||||
2. 强校验(父件不为空、子件列表非空、所有子件 ID>0、用量>0)
|
||||
3. 调用 BomService.save_bom 写入正式 bom_table
|
||||
4. 清空草稿数据
|
||||
"""
|
||||
try:
|
||||
# 步骤 1
|
||||
draft = BomDraftService.get_draft_detail(bom_no, version)
|
||||
if not draft:
|
||||
raise ValueError('草稿不存在')
|
||||
|
||||
# 步骤 2:强校验
|
||||
if not draft.get('parent_id'):
|
||||
raise ValueError('发布失败:父件不能为空')
|
||||
|
||||
children = draft.get('children', [])
|
||||
if not children:
|
||||
raise ValueError('发布失败:子件列表不能为空')
|
||||
|
||||
for child in children:
|
||||
if not child.get('child_id') or child['child_id'] <= 0:
|
||||
raise ValueError('发布失败:子件ID必须大于0')
|
||||
dosage = child.get('dosage')
|
||||
if not dosage or dosage <= 0:
|
||||
raise ValueError('发布失败:子件用量必须大于0')
|
||||
|
||||
# 步骤 3:复用正式 BOM 的写入逻辑(跨版本查重 + 缓存清理均在 save_bom 内完成)
|
||||
publish_data = {
|
||||
'bom_no': bom_no,
|
||||
'version': version,
|
||||
'parent_id': draft['parent_id'],
|
||||
'children': [
|
||||
{
|
||||
'child_id': child['child_id'],
|
||||
'dosage': child['dosage'],
|
||||
'remark': child.get('remark', ''),
|
||||
}
|
||||
for child in children
|
||||
],
|
||||
}
|
||||
BomService.save_bom(publish_data)
|
||||
|
||||
# 步骤 4:清空草稿数据
|
||||
old_rows = BomDraftTable.query.filter_by(bom_no=bom_no, version=version).all()
|
||||
for rec in old_rows:
|
||||
db.session.delete(rec)
|
||||
db.session.commit()
|
||||
logger.info(f"[BomDraft] publish_draft bom_no={bom_no} version={version} -> 已发布并清空草稿")
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
logger.error(f"[BomDraft] publish_draft 失败 bom_no={bom_no}: {e}")
|
||||
raise
|
||||
return bom_no
|
||||
@ -431,27 +431,31 @@ class BomService:
|
||||
|
||||
@staticmethod
|
||||
def create_or_update_bom(parent_id, child_list, bom_no=None, version='V1.0'):
|
||||
if not bom_no:
|
||||
existing = BomTable.query.filter_by(parent_id=parent_id).first()
|
||||
bom_no = existing.bom_no if existing else BomService.generate_bom_no()
|
||||
try:
|
||||
if not bom_no:
|
||||
existing = BomTable.query.filter_by(parent_id=parent_id).first()
|
||||
bom_no = existing.bom_no if existing else BomService.generate_bom_no()
|
||||
|
||||
# 改为对象级删除以触发审计事件
|
||||
old_records = BomTable.query.filter_by(bom_no=bom_no, version=version).all()
|
||||
for rec in old_records:
|
||||
db.session.delete(rec)
|
||||
# 改为对象级删除以触发审计事件
|
||||
old_records = BomTable.query.filter_by(bom_no=bom_no, version=version).all()
|
||||
for rec in old_records:
|
||||
db.session.delete(rec)
|
||||
|
||||
for item in child_list:
|
||||
bom = BomTable(
|
||||
bom_no=bom_no, version=version, parent_id=parent_id,
|
||||
child_id=item['child_id'], dosage=item.get('dosage', 0), remark=item.get('remark', '')
|
||||
)
|
||||
db.session.add(bom)
|
||||
db.session.commit()
|
||||
|
||||
# ===== 写入后立刻清除缓存(Cache Invalidation) =====
|
||||
_cache_delete(bom_no, version)
|
||||
logger.info(f"[BOM Cache] create_or_update_bom → 缓存已失效 bom_no={bom_no} version={version}")
|
||||
for item in child_list:
|
||||
bom = BomTable(
|
||||
bom_no=bom_no, version=version, parent_id=parent_id,
|
||||
child_id=item['child_id'], dosage=item.get('dosage', 0), remark=item.get('remark', '')
|
||||
)
|
||||
db.session.add(bom)
|
||||
db.session.commit()
|
||||
|
||||
# ===== 写入后立刻清除缓存(Cache Invalidation) =====
|
||||
_cache_delete(bom_no, version)
|
||||
logger.info(f"[BOM Cache] create_or_update_bom → 缓存已失效 bom_no={bom_no} version={version}")
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
logger.error(f"[BOM] create_or_update_bom 失败 bom_no={bom_no}: {e}")
|
||||
raise
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
|
||||
@ -42,3 +42,22 @@ export function deleteBom(bomNo: string, version: string) {
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// BOM 草稿相关接口
|
||||
// ==========================================
|
||||
|
||||
// 1. 暂存草稿
|
||||
export function saveDraft(data: any) {
|
||||
return request({ url: '/v1/bom/draft/save', method: 'post', data })
|
||||
}
|
||||
|
||||
// 2. 读取草稿详情
|
||||
export function getDraftDetail(params: { bom_no: string; version?: string }) {
|
||||
return request({ url: '/v1/bom/draft/detail', method: 'get', params })
|
||||
}
|
||||
|
||||
// 3. 发布草稿
|
||||
export function publishDraft(data: { bom_no: string; version: string }) {
|
||||
return request({ url: '/v1/bom/draft/publish', method: 'post', data })
|
||||
}
|
||||
|
||||
@ -223,7 +223,25 @@
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">{{ isReadOnlyMode ? '关闭' : '取消' }}</el-button>
|
||||
<el-button v-if="!isReadOnlyMode" type="primary" :loading="saving" @click="submitForm">保存</el-button>
|
||||
|
||||
<el-button
|
||||
v-if="!isReadOnlyMode"
|
||||
type="warning"
|
||||
plain
|
||||
:loading="saving"
|
||||
@click="saveDraftData"
|
||||
>
|
||||
暂存草稿
|
||||
</el-button>
|
||||
|
||||
<el-button
|
||||
v-if="!isReadOnlyMode"
|
||||
type="primary"
|
||||
:loading="saving"
|
||||
@click="submitForm"
|
||||
>
|
||||
保 存
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
@ -236,7 +254,7 @@ import { useRoute } from 'vue-router'
|
||||
import { ElMessage, ElMessageBox, FormInstance, FormRules } from 'element-plus'
|
||||
import { Plus, Search, EditPen } from '@element-plus/icons-vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { getBomList, getBomDetail, saveBom, deleteBom } from '@/api/bom'
|
||||
import { getBomList, getBomDetail, saveBom, deleteBom, getDraftDetail, saveDraft, publishDraft } from '@/api/bom'
|
||||
import { searchMaterialBase } from '@/api/inbound/buy'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
|
||||
@ -265,6 +283,22 @@ interface ChildRow {
|
||||
remark: string
|
||||
}
|
||||
|
||||
const originalDraftHash = ref('')
|
||||
|
||||
const getDraftHash = () => {
|
||||
const children = form.children.map(c => ({
|
||||
child_id: c.child_id,
|
||||
dosage: Number(c.dosage) || 0,
|
||||
remark: c.remark || ''
|
||||
}))
|
||||
return JSON.stringify({
|
||||
bom_no: form.bom_no,
|
||||
version: form.version,
|
||||
parent_id: form.parent_id,
|
||||
children
|
||||
})
|
||||
}
|
||||
|
||||
const userStore = useUserStore()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
@ -277,6 +311,8 @@ const isReadOnlyMode = ref(false)
|
||||
let originalVersion = ''
|
||||
let currentBomNo = ''
|
||||
let originalChildren: ChildRow[] = []
|
||||
let pendingDraftBomNo = ''
|
||||
let pendingDraftVersion = ''
|
||||
|
||||
const bomGroups = ref([]) // 分组结构: [{category, count, items[]}]
|
||||
const activeCategories = ref([]) // 默认全部展开
|
||||
@ -493,13 +529,152 @@ const onChildChange = (val: number | null, index: number) => {
|
||||
}
|
||||
}
|
||||
|
||||
const handleCreate = () => {
|
||||
resetForm()
|
||||
dialogTitle.value = '新建 BOM'
|
||||
isEditMode.value = false
|
||||
isSaveAsMode.value = false
|
||||
isReadOnlyMode.value = false
|
||||
dialogVisible.value = true
|
||||
const getOrGenerateTempBomNo = (): string => {
|
||||
let storedBomNo = localStorage.getItem('pending_bom_draft_no')
|
||||
if (storedBomNo) {
|
||||
return storedBomNo
|
||||
}
|
||||
const ts = new Date().toISOString().replace(/[-T:.Z]/g, '').slice(0, 14)
|
||||
const uid = Math.random().toString(36).slice(2, 10)
|
||||
const newBomNo = `DRAFT-TEMP-${ts}-${uid}`
|
||||
localStorage.setItem('pending_bom_draft_no', newBomNo)
|
||||
return newBomNo
|
||||
}
|
||||
|
||||
const handleCreate = async () => {
|
||||
const tempBomNo = getOrGenerateTempBomNo()
|
||||
pendingDraftBomNo = tempBomNo
|
||||
pendingDraftVersion = 'V1.0'
|
||||
|
||||
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) {}
|
||||
|
||||
resetForm()
|
||||
dialogTitle.value = '新建 BOM'
|
||||
isEditMode.value = false
|
||||
isSaveAsMode.value = false
|
||||
isReadOnlyMode.value = false
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const saveDraftData = async () => {
|
||||
const draftBomNo = form.bom_no || getOrGenerateTempBomNo()
|
||||
const currentHash = getDraftHash()
|
||||
|
||||
// 场景 A:之前已经存过/恢复过草稿,且没有任何改动
|
||||
if (originalDraftHash.value && currentHash === originalDraftHash.value) {
|
||||
return ElMessage.warning('草稿数据无变动,无需重复暂存')
|
||||
}
|
||||
|
||||
// 场景 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)
|
||||
}
|
||||
|
||||
const executeSaveDraftRequest = async (targetBomNo: string) => {
|
||||
const draftVersion = form.version || 'V1.0'
|
||||
const children = form.children
|
||||
.filter(child => child.child_id !== null || child.dosage > 0)
|
||||
.map(child => ({
|
||||
child_id: child.child_id,
|
||||
dosage: child.dosage,
|
||||
remark: child.remark || ''
|
||||
}))
|
||||
|
||||
saving.value = true
|
||||
try {
|
||||
const res = await saveDraft({
|
||||
bom_no: targetBomNo,
|
||||
version: draftVersion,
|
||||
parent_id: form.parent_id,
|
||||
children
|
||||
})
|
||||
if (res.code === 200) {
|
||||
ElMessage.success('草稿暂存成功')
|
||||
form.bom_no = targetBomNo
|
||||
localStorage.setItem('pending_bom_draft_no', targetBomNo)
|
||||
originalDraftHash.value = getDraftHash()
|
||||
} else {
|
||||
ElMessage.error(res.msg || '暂存失败')
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage.error('暂存异常')
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const restoreDraftToForm = (draftData: any) => {
|
||||
form.children = (draftData.children || []).map((child: any, idx: number) => ({
|
||||
rowKey: Date.now() + idx,
|
||||
child_id: child.child_id,
|
||||
material_name: child.child_name || '',
|
||||
material_spec: child.child_spec || '',
|
||||
dosage: child.dosage,
|
||||
remark: child.remark || ''
|
||||
}))
|
||||
|
||||
if (draftData.parent_id) {
|
||||
form.parent_id = draftData.parent_id
|
||||
parentNameInput.value = draftData.parent_name || ''
|
||||
}
|
||||
|
||||
form.bom_no = draftData.bom_no || ''
|
||||
form.version = draftData.version || 'V1.0'
|
||||
form.remark = draftData.remark || ''
|
||||
pendingDraftBomNo = draftData.bom_no || pendingDraftBomNo
|
||||
pendingDraftVersion = draftData.version || pendingDraftVersion
|
||||
|
||||
originalDraftHash.value = getDraftHash()
|
||||
}
|
||||
|
||||
const handleView = async (row: BomItem) => {
|
||||
@ -616,6 +791,9 @@ const resetForm = () => {
|
||||
currentBomNo = ''
|
||||
childSearchKeyword.value = ''
|
||||
parentNameInput.value = ''
|
||||
originalDraftHash.value = ''
|
||||
pendingDraftBomNo = ''
|
||||
pendingDraftVersion = ''
|
||||
if (formRef.value) formRef.value.resetFields()
|
||||
}
|
||||
|
||||
@ -662,10 +840,19 @@ const submitForm = async () => {
|
||||
|
||||
saving.value = true
|
||||
try {
|
||||
const res = await saveBom(payload)
|
||||
if (res.code === 200) { ElMessage.success('保存成功'); dialogVisible.value = false; fetchBomList() }
|
||||
else ElMessage.error(res.msg || '保存失败')
|
||||
} finally { saving.value = false }
|
||||
const res = await publishDraft({ bom_no: pureBomNo.value, version: form.version })
|
||||
if (res.code === 200) {
|
||||
ElMessage.success('发布成功')
|
||||
localStorage.removeItem('pending_bom_draft_no')
|
||||
originalDraftHash.value = ''
|
||||
dialogVisible.value = false
|
||||
fetchBomList()
|
||||
} else {
|
||||
ElMessage.error(res.msg || '发布失败')
|
||||
}
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user