Files
KCGL/inventory-web/src/views/outbound/Selection.vue

254 lines
8.5 KiB
Vue
Raw Normal View History

2026-02-06 10:16:37 +08:00
<template>
<div class="app-container">
<el-card shadow="always" class="no-print-content">
2026-02-06 10:16:37 +08:00
<template #header>
<div class="card-header">
<div class="header-left">
<span class="title">出库拣货车</span>
<span class="subtitle">(请添加需要出库的物品)</span>
2026-02-06 10:16:37 +08:00
</div>
<div>
<el-button type="primary" :icon="Plus" @click="openManualSelect">
手动添加库存
</el-button>
<el-button type="warning" :icon="List" @click="openBomSelect">
按 BOM 套餐添加
</el-button>
<el-divider direction="vertical" />
<el-button type="success" :icon="Printer" :disabled="selectedItems.length === 0" @click="handlePreview">
生成预览 & 打印
2026-02-06 10:16:37 +08:00
</el-button>
</div>
</div>
</template>
<el-alert
v-if="selectedItems.length === 0"
title="清单为空,请点击右上角按钮添加物品"
type="info"
center
show-icon
style="margin-bottom: 20px"
2026-02-06 10:16:37 +08:00
/>
<el-table
v-else
:data="selectedItems"
border
style="width: 100%"
row-key="uniqueKey"
>
<el-table-column type="index" label="序号" width="50" align="center" />
<el-table-column prop="typeLabel" label="类型" width="80" />
<el-table-column prop="name" label="名称" />
<el-table-column prop="standard" label="规格" />
<el-table-column prop="available_quantity" label="当前库存" width="100" />
<el-table-column label="本次出库" width="180" align="center">
<template #default="{ row }">
<el-input-number
v-model="row.export_quantity"
:min="1"
:max="row.available_quantity"
size="small"
/>
</template>
</el-table-column>
<el-table-column label="操作" width="80" align="center">
<template #default="{ $index }">
<el-button type="danger" link @click="removeRow($index)">移除</el-button>
</template>
</el-table-column>
</el-table>
2026-02-06 10:16:37 +08:00
</el-card>
<el-dialog v-model="manualDialogVisible" title="选择库存物品" width="80%" top="5vh">
<div class="filter-container">
<el-input v-model="searchKeyword" placeholder="搜索名称/规格" style="width: 300px" @input="filterStock" />
</div>
<el-table
:data="filteredStockData"
height="500"
border
@selection-change="handleStockSelection"
>
<el-table-column type="selection" width="55" />
2026-02-06 10:16:37 +08:00
<el-table-column prop="name" label="名称" />
<el-table-column prop="standard" label="规格" />
<el-table-column prop="available_quantity" label="可用库存" />
<el-table-column label="本次出库" width="150">
<template #default="{ row }">
<el-input-number
v-model="row.export_quantity"
:min="1"
:max="row.available_quantity"
size="small"
style="width: 100%"
/>
</template>
</el-table-column>
</el-table>
<template #footer>
<el-button @click="manualDialogVisible = false">取消</el-button>
<el-button type="primary" @click="confirmManualAdd">确认添加</el-button>
</template>
2026-02-06 10:16:37 +08:00
</el-dialog>
<el-dialog v-model="bomSelectVisible" title="选择 BOM 套餐" width="600px">
<el-form label-width="80px">
<el-form-item label="选择产品">
<el-select v-model="selectedBomNo" filterable placeholder="选择要出库的产品BOM" style="width: 100%">
<el-option
v-for="b in bomOptions"
:key="b.bom_no"
:label="`${b.parent_name} (${b.version})`"
:value="b.bom_no"
/>
</el-select>
</el-form-item>
<el-form-item label="生产套数">
<el-input-number v-model="bomSets" :min="1" label="套" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="bomSelectVisible = false">取消</el-button>
<el-button type="primary" @click="confirmBomAdd">一键添加</el-button>
</template>
</el-dialog>
<el-dialog v-model="previewVisible" title="打印预览" width="800px">
<el-button @click="window.print()">确认打印</el-button>
</el-dialog>
2026-02-06 10:16:37 +08:00
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { Plus, List, Printer } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import { getAllStock } from '@/api/inbound/stock'
import { getBomList, getBomDetail } from '@/api/bom'
2026-02-06 10:16:37 +08:00
// 购物车数据
const selectedItems = ref<any[]>([])
2026-02-06 10:16:37 +08:00
// 手动选择相关
const manualDialogVisible = ref(false)
const allStockData = ref<any[]>([])
const filteredStockData = ref<any[]>([])
const searchKeyword = ref('')
const tempSelection = ref<any[]>([])
2026-02-06 10:16:37 +08:00
// BOM 相关
const bomSelectVisible = ref(false)
const bomOptions = ref<any[]>([])
const selectedBomNo = ref('')
const bomSets = ref(1)
const previewVisible = ref(false)
// --- 方法 ---
// 1. 打开手动选择
const openManualSelect = async () => {
manualDialogVisible.value = true
if (allStockData.value.length === 0) {
const res:any = await getAllStock()
// 简单处理数据,实际应复用之前的 normalizeItem 逻辑
const list = res.materials ? [...res.materials, ...res.products, ...res.semis] : []
allStockData.value = list.map((i:any) => ({
...i,
name: i.name || i.material_name || i.product_name,
standard: i.standard || i.spec_model,
uniqueKey: i.id + '_' + i.type,
export_quantity: 1
}))
filteredStockData.value = allStockData.value
}
}
const filterStock = () => {
const kw = searchKeyword.value.toLowerCase()
filteredStockData.value = allStockData.value.filter(i =>
(i.name && i.name.toLowerCase().includes(kw)) || (i.standard && i.standard.toLowerCase().includes(kw))
)
}
const handleStockSelection = (val: any[]) => { tempSelection.value = val }
const confirmManualAdd = () => {
const newItems = tempSelection.value.filter(item =>
!selectedItems.value.find(existing => existing.uniqueKey === item.uniqueKey)
)
selectedItems.value.push(...newItems)
manualDialogVisible.value = false
tempSelection.value = []
ElMessage.success(`添加了 ${newItems.length} 项`)
}
// 2. 打开 BOM 选择
const openBomSelect = async () => {
bomSelectVisible.value = true
const res = await getBomList()
bomOptions.value = res.data || []
}
const confirmBomAdd = async () => {
if(!selectedBomNo.value) return;
const detailRes = await getBomDetail(selectedBomNo.value)
const bomRows = detailRes.data || []
if (allStockData.value.length === 0) {
// 确保有库存数据用于匹配
await openManualSelect()
manualDialogVisible.value = false // 仅加载数据,不显示弹窗
}
let addedCount = 0;
bomRows.forEach((bomItem:any) => {
const needQty = bomItem.dosage * bomSets.value
// 简单匹配逻辑:通过名称或ID匹配
const stockCandidate = allStockData.value.find(s =>
(s.base_id == bomItem.child_id)
)
if (stockCandidate) {
const existing = selectedItems.value.find(e => e.uniqueKey === stockCandidate.uniqueKey)
if (existing) {
existing.export_quantity += needQty
} else {
const newItem = JSON.parse(JSON.stringify(stockCandidate))
newItem.export_quantity = needQty
selectedItems.value.push(newItem)
}
addedCount++
}
2026-02-06 10:16:37 +08:00
})
if(addedCount > 0) {
ElMessage.success(`已添加 BOM 相关物料`)
bomSelectVisible.value = false
} else {
ElMessage.warning('未在库存中找到匹配的物料')
}
}
const removeRow = (index: number) => {
selectedItems.value.splice(index, 1)
}
2026-02-06 10:16:37 +08:00
const handlePreview = () => {
previewVisible.value = true
setTimeout(() => window.print(), 500)
}
2026-02-06 10:16:37 +08:00
</script>
<style scoped>
.card-header { display: flex; justify-content: space-between; align-items: center; }
.header-left .title { font-size: 18px; font-weight: bold; margin-right: 10px; }
.header-left .subtitle { font-size: 12px; color: #909399; }
.filter-container { margin-bottom: 10px; }
</style>