Files
bj_power/bj_power_wms_client/frontend/src/pages/Outbound.vue
T
SunYF dd078038e2 feat: 为多个页面添加操作帮助功能,替换旧版静态资源文件
1. 新增PageHelp通用帮助组件,实现页面左下角悬浮帮助按钮
2. 为WMS、MES、工位终端多个业务页面接入帮助系统
3. 替换项目中旧的打包静态资源文件,更新资源引用路径
4. 新增各模块的帮助文档配置数据
2026-08-28 17:23:44 +08:00

157 lines
5.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup>
import { computed, reactive, ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import request from '../utils/request'
import { getRealName } from '../utils/auth'
import PageHelp from '../components/PageHelp.vue'
import { helpOutbound } from '../help'
const queryForm = reactive({ orderNo: '' })
const operator = getRealName()
const targetDock = ref('')
const loading = ref(false)
const queried = ref(false)
const rows = ref([])
const pendingRows = computed(() => rows.value.filter(r => r.status !== '领料完结'))
function gapOf(row) {
return Math.max((row.totalQty || 0) - (row.outQty || 0), 0)
}
// 一键自动分配批次:合格区 Z02,batchNo 正序 FIFO 凑够缺口
async function autoAllocate(row) {
const gap = gapOf(row)
if (gap <= 0) return
const stock = await request.get('/stock/query', { params: { type: 'batch', materialCode: row.materialCode, page: 1, pageSize: 200 } })
const batches = (stock?.batch || [])
.filter(b => b.qualityStatus === '合格' && b.zoneCode === 'Z02')
.sort((a, b) => String(a.batchNo).localeCompare(String(b.batchNo)))
const plan = []
let remain = gap
for (const b of batches) {
if (remain <= 0) break
const avail = (b.quantity || 0) - (b.lockedQty || 0)
if (avail <= 0) continue
const take = Math.min(avail, remain)
plan.push({ batchNo: b.batchNo, qty: take, zoneCode: b.zoneCode })
remain -= take
}
if (!plan.length) {
ElMessage.warning(`物料 ${row.materialCode} 在合格区(Z02)无可用批次库存`)
return
}
if (remain > 0) {
ElMessage.error(`可用量不足:缺口 ${gap}Z02 仅可凑 ${gap - remain},还差 ${remain}`)
return
}
const lines = plan.map(p => `· 批次 ${p.batchNo} 出库 ${p.qty}`).join('\n')
await ElMessageBox.confirm(
`工单 ${row.orderNo} 物料 ${row.materialCode}\n缺口 ${gap},按 FIFO 分配如下:\n${lines}\n\n操作人:${operator}${targetDock.value ? `\n目标口:${targetDock.value}` : ''}`,
'确认自动分配出库',
{ type: 'warning', confirmButtonText: '确认出库', cancelButtonText: '取消' }
)
const outboundNos = []
let lastStatus = ''
for (const p of plan) {
const res = await request.post('/outbound/create', {
orderNo: row.orderNo,
materialCode: row.materialCode,
batchNo: p.batchNo,
qty: p.qty,
operator,
targetDock: targetDock.value.trim() || undefined
})
if (res?.outboundNo) outboundNos.push(res.outboundNo)
lastStatus = res?.status || lastStatus
}
const done = lastStatus === '领料完结'
ElMessage.success(`出库完成:出库单号 ${outboundNos.join('、')};该物料${done ? '已' : '未'}领料完结`)
await query()
}
function statusTag(s) {
return s === '领料完结' ? 'success' : s === '进行中' ? 'primary' : 'info'
}
async function query() {
if (!queryForm.orderNo.trim()) {
ElMessage.warning('请输入工单号')
return
}
loading.value = true
try {
const data = await request.get('/ledger/query', { params: { orderNo: queryForm.orderNo.trim(), page: 1, pageSize: 100 } })
rows.value = data?.list || []
queried.value = true
if (!rows.value.length) ElMessage.info('该工单暂无台账记录')
} finally {
loading.value = false
}
}
</script>
<template>
<div>
<el-card shadow="never" class="mb12">
<el-form inline label-width="90px" @submit.prevent>
<el-form-item label="工单号">
<el-input v-model="queryForm.orderNo" placeholder="输入工单号回车查询" clearable autofocus
style="width:260px" @keyup.enter="query" />
</el-form-item>
<el-form-item label="操作人">
<el-input :model-value="operator" disabled style="width:160px" />
</el-form-item>
<el-form-item label="目标工位">
<el-input v-model="targetDock" placeholder="可选,如 DOCK-A" clearable style="width:180px" />
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="loading" @click="query">查询台账</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card v-if="queried" shadow="never">
<template #header>
台账明细 {{ rows.length }} 待补 {{ pendingRows.length }}
</template>
<el-table :data="rows" border v-loading="loading">
<el-table-column prop="orderNo" label="工单号" min-width="140" />
<el-table-column prop="materialCode" label="物料编码" min-width="150" />
<el-table-column prop="materialName" label="物料名称" min-width="140">
<template #default="{ row }">{{ row.materialName || '-' }}</template>
</el-table-column>
<el-table-column prop="totalQty" label="需求总量" width="100" align="center" />
<el-table-column prop="outQty" label="已出库" width="90" align="center" />
<el-table-column label="缺口" width="90" align="center">
<template #default="{ row }">
<span :class="{ gap: gapOf(row) > 0 }">{{ gapOf(row) }}</span>
</template>
</el-table-column>
<el-table-column label="状态" width="110" align="center">
<template #default="{ row }">
<el-tag :type="statusTag(row.status)" size="small">{{ row.status }}</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="140" align="center">
<template #default="{ row }">
<el-button v-if="row.status !== '领料完结'" type="primary" size="small"
@click="autoAllocate(row)">一键分配出库</el-button>
<span v-else style="color:#67c23a">已完结</span>
</template>
</el-table-column>
</el-table>
</el-card>
<el-empty v-else description="请先按工单号查询物料台账" />
</div>
<PageHelp :data="helpOutbound" />
</template>
<style scoped>
.mb12 { margin-bottom: 12px; }
.gap { color: #f56c6c; font-weight: 700; }
</style>