feat: P0-4 按库存优化+切割准备清单(缺料告警)
This commit is contained in:
@@ -1,18 +1,51 @@
|
||||
<template>
|
||||
<el-card>
|
||||
<div class="toolbar">
|
||||
<div class="toolbar no-print">
|
||||
<el-select v-model="contractId" filterable placeholder="选择合同" style="width:300px">
|
||||
<el-option v-for="c in contracts" :key="c.id" :label="`${c.contract_no} ${c.customer_name || ''}`" :value="c.id" />
|
||||
</el-select>
|
||||
<span>锯口</span>
|
||||
<el-input-number v-model="kerf" :min="0" :max="50" size="small" controls-position="right" style="width:90px" />
|
||||
<el-divider direction="vertical" />
|
||||
<el-checkbox v-model="useStock">使用仓库库存原料</el-checkbox>
|
||||
<template v-if="!useStock">
|
||||
<span>标准原料长度</span>
|
||||
<el-input-number v-model="stockLength" :min="1000" :max="12000" :step="500" size="small"
|
||||
controls-position="right" style="width:110px" />
|
||||
</template>
|
||||
<el-button type="primary" :loading="loading" :disabled="!contractId" @click="run">生成下料方案</el-button>
|
||||
<el-button v-if="result" type="success" plain @click="print">打印方案</el-button>
|
||||
</div>
|
||||
|
||||
<template v-if="result">
|
||||
<el-alert type="success" :closable="false" show-icon
|
||||
:title="`合同 ${contractNo} · ${result.order_count} 个订单 · 利用率 ${(result.stats.utilization * 100).toFixed(1)}% · 用料 ${result.stats.stock_bars_used} 根`"
|
||||
<el-alert type="success" :closable="false" show-icon class="no-print"
|
||||
:title="`合同 ${contractNo} · ${result.order_count} 个订单 · 利用率 ${(result.stats.utilization * 100).toFixed(1)}% · 用料 ${result.stats.stock_bars_used} 根${result.use_stock ? ' · 按仓库库存' : ''}`"
|
||||
style="margin-bottom:12px" />
|
||||
|
||||
<!-- 切割准备/领料清单 -->
|
||||
<h4 class="sec-title">切割准备(领料单)<span id="print-title" v-if="false"></span></h4>
|
||||
<el-table :data="result.preparation" border size="small">
|
||||
<el-table-column label="原料" min-width="160">
|
||||
<template #default="{ row }">{{ row.stock_label }}({{ row.stock_length }}mm)</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="bars" label="领料根数" width="100" />
|
||||
<el-table-column prop="pieces" label="切割件数" width="100" />
|
||||
<el-table-column label="合计余料(mm)" width="130">
|
||||
<template #default="{ row }">{{ row.waste }}(均 {{ Math.round(row.waste / row.bars) }})</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 缺料清单(按库存模式) -->
|
||||
<template v-if="result.use_stock && result.shortage?.length">
|
||||
<h4 class="sec-title" style="color:#e6a23c">缺料清单(需求超出库存,需补库)</h4>
|
||||
<el-table :data="result.shortage" border size="small" :row-style="{ background: '#fdf6ec' }">
|
||||
<el-table-column prop="label" label="缺料用途" min-width="140" />
|
||||
<el-table-column prop="length" label="单件长度(mm)" width="120" />
|
||||
<el-table-column prop="qty" label="缺口(件)" width="100" />
|
||||
</el-table>
|
||||
</template>
|
||||
|
||||
<h4 class="sec-title">切割方案明细</h4>
|
||||
<el-table :data="result.plans" border size="small">
|
||||
<el-table-column label="原料" width="180">
|
||||
<template #default="{ row }">{{ row.stock_label }}({{ row.stock_length }}mm)</template>
|
||||
@@ -25,8 +58,11 @@
|
||||
</el-table-column>
|
||||
<el-table-column prop="waste" label="余料mm" width="90" />
|
||||
</el-table>
|
||||
<el-alert v-if="result.unplaced.length" type="warning" :closable="false"
|
||||
:title="`未排入 ${result.unplaced.length} 件(库存不足)`" style="margin-top:10px" />
|
||||
|
||||
<template v-if="!result.use_stock && result.unplaced.length">
|
||||
<el-alert type="warning" :closable="false" class="no-print"
|
||||
:title="`未排入 ${result.unplaced.length} 件`" style="margin-top:10px" />
|
||||
</template>
|
||||
</template>
|
||||
</el-card>
|
||||
</template>
|
||||
@@ -40,19 +76,26 @@ const contracts = ref<any[]>([])
|
||||
const contractId = ref<number | undefined>()
|
||||
const contractNo = ref('')
|
||||
const kerf = ref(5)
|
||||
const useStock = ref(false)
|
||||
const stockLength = ref(6000)
|
||||
const loading = ref(false)
|
||||
const result = ref<any>(null)
|
||||
|
||||
async function run() {
|
||||
loading.value = true
|
||||
try {
|
||||
const d = await api<any>('post', '/api/cut_solution', { contract_id: contractId.value, kerf: kerf.value })
|
||||
const d = await api<any>('post', '/api/cut_solution', {
|
||||
contract_id: contractId.value, kerf: kerf.value,
|
||||
use_stock: useStock.value, stock_length: stockLength.value,
|
||||
})
|
||||
result.value = d
|
||||
contractNo.value = d.contract_no
|
||||
ElMessage.success(`已生成:利用率 ${(d.stats.utilization * 100).toFixed(1)}%`)
|
||||
} finally { loading.value = false }
|
||||
}
|
||||
|
||||
function print() { window.print() }
|
||||
|
||||
onMounted(async () => {
|
||||
const d = await api<any>('get', '/api/contracts?page=1&page_size=100')
|
||||
contracts.value = d.contracts?.data ?? []
|
||||
@@ -60,5 +103,12 @@ onMounted(async () => {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.toolbar { display: flex; gap: 10px; align-items: center; margin-bottom: 12px; }
|
||||
.toolbar { display: flex; gap: 10px; align-items: center; margin-bottom: 12px; flex-wrap: wrap; }
|
||||
.sec-title { margin: 16px 0 8px; color: #303133; }
|
||||
@media print {
|
||||
.no-print { display: none !important; }
|
||||
body * { visibility: hidden; }
|
||||
.el-card, .el-card * { visibility: visible; }
|
||||
.el-card { position: absolute; left: 0; top: 0; width: 100%; box-shadow: none; }
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user