(no commit message provided)

Co-authored-by: aider (openai/DeepSeek-V3.2-Thinking) <aider@aider.chat>
This commit is contained in:
dxc
2026-02-09 15:19:19 +08:00
parent 40abb53721
commit b61072eea0
4 changed files with 173 additions and 8 deletions

View File

@ -1,5 +1,6 @@
from flask import Blueprint, request, jsonify, current_app
from app.services.bom_service import BomService
from app.models.base import MaterialBase
from flask_jwt_extended import jwt_required
bom_bp = Blueprint('bom', __name__)
@ -35,3 +36,19 @@ def save_bom():
except Exception as e:
current_app.logger.error(f'保存BOM失败: {str(e)}')
return jsonify({'code': 500, 'msg': '内部服务器错误'}), 500
@bom_bp.route('/base/list', methods=['GET'])
@jwt_required()
def get_material_base_list():
"""获取所有基础物料列表,用于前端下拉框"""
try:
materials = MaterialBase.query.filter_by(is_enabled=True).order_by(MaterialBase.id.desc()).all()
data = [item.to_dict() for item in materials]
return jsonify({
'code': 200,
'msg': 'success',
'data': data
})
except Exception as e:
current_app.logger.error(f'获取基础物料列表失败: {str(e)}')
return jsonify({'code': 500, 'msg': '内部服务器错误'}), 500

View File

@ -9,7 +9,7 @@ class BomService:
def create_or_update_bom(parent_id, child_list):
"""
保存/更新父件的BOM子件关系
child_list: [{"child_id": int, "dosage": float, "remark": str}, ...]
child_list: [{"child_id": int, "dosage": float, "loss_rate": float, "remark": str}, ...]
"""
# 删除该父件原有的BOM记录
BomTable.query.filter_by(parent_id=parent_id).delete()
@ -19,6 +19,7 @@ class BomService:
parent_id=parent_id,
child_id=item['child_id'],
dosage=item.get('dosage', 0),
loss_rate=item.get('loss_rate', 0),
remark=item.get('remark', '')
)
db.session.add(bom)

View File

@ -28,4 +28,22 @@ export function printStocktakeReport(data: any) {
method: 'post',
data
})
}
}
// 保存 BOM 结构
export function saveBom(data: { parent_id: number; children: any[] }) {
return request({
url: '/v1/bom',
method: 'post',
data
})
}
// 获取基础物料列表
export function getMaterialBaseList(params?: any) {
return request({
url: '/v1/bom/base/list',
method: 'get',
params
})
}

View File

@ -113,13 +113,68 @@
</template>
</el-dialog>
<!-- BOM 编辑弹窗 -->
<el-dialog v-model="bomDialogVisible" title="创建/编辑 BOM" width="800px">
<el-form :model="bomForm" label-width="120px">
<el-form-item label="父件 (成品)">
<el-select v-model="bomForm.parent_id" placeholder="请选择" filterable style="width:100%">
<el-option
v-for="item in materialBaseOptions"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
<div style="font-weight: bold; margin-bottom: 10px;">子件列表</div>
<el-table :data="bomForm.children" border style="width:100%; margin-top:10px">
<el-table-column label="子件物料" width="300">
<template #default="{ row, $index }">
<el-select v-model="row.child_id" placeholder="请选择" filterable style="width:100%">
<el-option
v-for="item in materialBaseOptions"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</template>
</el-table-column>
<el-table-column label="用量" width="150">
<template #default="{ row }">
<el-input-number v-model="row.dosage" :min="0" :precision="4" style="width:100%" />
</template>
</el-table-column>
<el-table-column label="损耗率%" width="150">
<template #default="{ row }">
<el-input-number v-model="row.loss_rate" :min="0" :max="100" :precision="2" style="width:100%" />
</template>
</el-table-column>
<el-table-column label="操作" width="80">
<template #default="{ $index }">
<el-button type="danger" size="small" @click="removeChildRow($index)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div style="margin-top:10px">
<el-button type="primary" @click="addChildRow">添加子件</el-button>
</div>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="bomDialogVisible = false">取消</el-button>
<el-button type="primary" @click="saveBom">保存 BOM</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { Printer, Search, Upload, Plus } from '@element-plus/icons-vue'
import { getAllStock, printSelectionList } from '@/api/inbound/stock'
import { getAllStock, printSelectionList, getMaterialBaseList, saveBom as saveBomApi } from '@/api/inbound/stock'
import { ElMessage, ElMessageBox } from 'element-plus'
// --- 类型定义 ---
@ -153,6 +208,14 @@ const allStockData = ref<DisplayItem[]>([])
// 当前选中的行
const selectedItems = ref<DisplayItem[]>([])
// BOM 相关
const bomDialogVisible = ref(false)
const materialBaseOptions = ref<any[]>([])
const bomForm = ref({
parent_id: null as number | null,
children: [] as any[]
})
// --- 计算属性:前端模糊搜索过滤 ---
const filteredTableData = computed(() => {
const keyword = searchKeyword.value.trim().toLowerCase()
@ -237,15 +300,81 @@ const confirmPrint = async () => {
}
}
// 5. BOM 操作占位函数
// 5. BOM 操作
const handleImportBom = () => {
// TODO: 打开上传文件的 Dialog 或者跳转页面
ElMessage.info('点击了导入BOM请实现具体逻辑')
}
const handleCreateBom = () => {
// TODO: 打开新建 BOM 的表单
ElMessage.info('点击了创建BOM请实现具体逻辑')
const handleCreateBom = async () => {
bomDialogVisible.value = true
if (materialBaseOptions.value.length === 0) {
try {
const res = await getMaterialBaseList({})
if (res.code === 200) {
materialBaseOptions.value = res.data
} else {
ElMessage.error('获取物料列表失败')
}
} catch (err) {
ElMessage.error('网络错误,无法获取物料列表')
}
}
}
const addChildRow = () => {
bomForm.value.children.push({
child_id: null,
dosage: 0,
loss_rate: 0,
remark: ''
})
}
const removeChildRow = (index: number) => {
bomForm.value.children.splice(index, 1)
}
const saveBom = async () => {
if (!bomForm.value.parent_id) {
ElMessage.warning('请选择父件')
return
}
if (bomForm.value.children.length === 0) {
ElMessage.warning('请至少添加一个子件')
return
}
for (const child of bomForm.value.children) {
if (!child.child_id) {
ElMessage.warning('请为每个子件选择物料')
return
}
}
const payload = {
parent_id: bomForm.value.parent_id,
children: bomForm.value.children.map(c => ({
child_id: c.child_id,
dosage: c.dosage,
loss_rate: c.loss_rate,
remark: c.remark || ''
}))
}
try {
const res = await saveBomApi(payload)
if (res.code === 200) {
ElMessage.success('BOM保存成功')
bomDialogVisible.value = false
// 清空表单
bomForm.value = {
parent_id: null,
children: []
}
} else {
ElMessage.error(res.msg || '保存失败')
}
} catch (err) {
ElMessage.error('网络错误')
}
}
// 辅助函数:高亮关键词 (可选)
@ -299,4 +428,4 @@ onMounted(() => {
width: 100%;
max-width: 400px;
}
</style>
</style>