diff --git a/inventory-backend/app/models/__init__.py b/inventory-backend/app/models/__init__.py index f246b5a..ddb030d 100644 --- a/inventory-backend/app/models/__init__.py +++ b/inventory-backend/app/models/__init__.py @@ -15,5 +15,11 @@ except ImportError: # 4. 出库记录 (如果有,BuyService 用到了 TransOutbound) try: from app.models.outbound import TransOutbound, OutboundApproval +except ImportError: + pass + +# 5. 采购申请 +try: + from app.models.purchase import PurchaseRequest except ImportError: pass \ No newline at end of file diff --git a/inventory-web/src/views/outbound/Selection.vue b/inventory-web/src/views/outbound/Selection.vue index fcef389..f5e21c0 100644 --- a/inventory-web/src/views/outbound/Selection.vue +++ b/inventory-web/src/views/outbound/Selection.vue @@ -335,7 +335,7 @@ diff --git a/inventory-web/src/views/purchase/index.vue b/inventory-web/src/views/purchase/index.vue index 499f90a..a7b9cec 100644 --- a/inventory-web/src/views/purchase/index.vue +++ b/inventory-web/src/views/purchase/index.vue @@ -12,7 +12,7 @@ 已完成 刷新 - + 新建采购申请 @@ -70,13 +70,38 @@ - + + + + {{ item.name }} + {{ item.spec_model || '-' }} + + + {{ autoFillHint }} - + @@ -92,7 +117,7 @@ - + @@ -208,6 +233,7 @@ import { ref, computed, onMounted } from 'vue' import { Refresh, Plus } from '@element-plus/icons-vue' import { ElMessage, ElMessageBox } from 'element-plus' import { useUserStore } from '@/stores/user' +import { searchMaterialBase } from '@/api/inbound/buy' import { getPurchaseList, createPurchase, getPurchaseDetail, approvePurchase, getPurchaseApprovers, autoFillPurchase @@ -247,6 +273,11 @@ const autoFillHint = ref('') // 审批人 const approvers = ref([]) +// 物料搜索 +const materialOptions = ref([]) +const searchLoading = ref(false) +const materialBaseId = ref(null) + // 表单 const form = ref({ name: '', @@ -327,50 +358,82 @@ const openCreateDialog = () => { unit_price: undefined, total_price: undefined, approver_id: undefined, images: [] } - fileList.value = [] + materialBaseId.value = null + materialOptions.value = [] autoFillHint.value = '' + fileList.value = [] formDialogVisible.value = true } -// --- 自动补全名称/规格 --- -let autoFillTimer: any = null -const onNameChange = () => { - clearTimeout(autoFillTimer) - autoFillTimer = setTimeout(() => doAutoFill(), 500) -} -const onNameBlur = () => { - clearTimeout(autoFillTimer) - doAutoFill() -} -const doAutoFill = async () => { - const kw = form.value.name.trim() - if (!kw) { autoFillHint.value = ''; return } - try { - const res: any = await autoFillPurchase(kw) - if (res.data) { - if (!form.value.spec_model && res.data.spec_model) { - autoFillHint.value = `已自动补全规格:${res.data.spec_model}` - form.value.spec_model = res.data.spec_model - } else if (!form.value.name && res.data.name) { - autoFillHint.value = `已自动补全名称:${res.data.name}` - form.value.name = res.data.name - } - } - } catch (e) { /* ignore */ } +// --- 物料搜索 --- +let searchTimer: any = null +const handleSearchMaterialDebounced = (query: string) => { + clearTimeout(searchTimer) + searchTimer = setTimeout(() => handleSearchMaterial(query), 300) } -// --- 价格自动计算 --- -const onUnitPriceChange = (val: number | undefined) => { - if (val !== undefined && val > 0 && form.value.quantity > 0) { - const total = +(val * form.value.quantity).toFixed(2) - form.value.total_price = total +const handleSearchMaterial = async (query: string) => { + if (!query.trim()) { + materialOptions.value = [] + return + } + searchLoading.value = true + try { + const res: any = await searchMaterialBase(query, 1) + materialOptions.value = res.data || [] + } finally { + searchLoading.value = false } } -const onTotalPriceChange = (val: number | undefined) => { - if (val !== undefined && val > 0 && form.value.quantity > 0) { - const unit = +(val / form.value.quantity).toFixed(4) - form.value.unit_price = unit + +const onMaterialSelected = (id: number | null) => { + if (!id) { + materialBaseId.value = null + return } + const item = materialOptions.value.find(i => i.id === id) + if (item) { + form.value.name = item.name || item.material_name || '' + form.value.spec_model = item.spec_model || item.spec || '' + materialBaseId.value = id + autoFillHint.value = '' + } +} + +// --- 价格自动计算(需确认)--- +let priceConfirmTimer: any = null +const onUnitPriceChange = (val: number | undefined) => { + if (priceConfirmTimer) clearTimeout(priceConfirmTimer) + priceConfirmTimer = setTimeout(() => { + if (val !== undefined && val > 0 && form.value.quantity > 0 && !form.value.total_price) { + ElMessageBox.confirm( + `即将自动计算总价:${val} × ${form.value.quantity} = ${+(val * form.value.quantity).toFixed(2)},是否继续?`, + '自动计算确认', + { confirmButtonText: '确定', cancelButtonText: '取消', type: 'info' } + ).then(() => { + form.value.total_price = +(val * form.value.quantity).toFixed(2) + }).catch(() => {}) + } else if (val !== undefined && val > 0 && form.value.quantity > 0) { + form.value.total_price = +(val * form.value.quantity).toFixed(2) + } + }, 300) +} + +const onTotalPriceChange = (val: number | undefined) => { + if (priceConfirmTimer) clearTimeout(priceConfirmTimer) + priceConfirmTimer = setTimeout(() => { + if (val !== undefined && val > 0 && form.value.quantity > 0 && !form.value.unit_price) { + ElMessageBox.confirm( + `即将自动计算单价:${val} ÷ ${form.value.quantity} = ${+(val / form.value.quantity).toFixed(4)},是否继续?`, + '自动计算确认', + { confirmButtonText: '确定', cancelButtonText: '取消', type: 'info' } + ).then(() => { + form.value.unit_price = +(val / form.value.quantity).toFixed(4) + }).catch(() => {}) + } else if (val !== undefined && val > 0 && form.value.quantity > 0) { + form.value.unit_price = +(val / form.value.quantity).toFixed(4) + } + }, 300) } // --- 上传 --- @@ -421,9 +484,10 @@ const handleRemoveImage = async (uploadFile: any) => { // --- 提交 --- const submitForm = async () => { - if (!form.value.name.trim()) { ElMessage.warning('请填写采购物品名称'); return } + if (!form.value.name.trim()) { ElMessage.warning('请选择或填写采购物品'); return } if (!form.value.approver_id) { ElMessage.warning('请选择审批人'); return } if (!form.value.purchase_date) { ElMessage.warning('请选择采购日期'); return } + if (!form.value.images || form.value.images.length === 0) { ElMessage.warning('请上传至少一张图片'); return } submitLoading.value = true try {