feat(production): 新增排产支撑功能并移除手动报工页面

- 在main.go中更新排产物料需求注释说明,明确WMS与MES系统间交互方式
- 从前端help.js中移除手动报工(helpScan)相关帮助文档
- 更新工艺流程、绩效报表等页面帮助文档中的报工流程说明
- 在主布局中添加排产支撑菜单项并移除手动报工菜单项
- 新增ProducibleSupport.vue组件实现排产支撑页面功能
- 移除Scan.vue手动报工页面组件
- 更新相关帮助文档内容以匹配新的业务流程
This commit is contained in:
SunYF
2026-09-21 14:06:05 +08:00
parent 132c3ed6bd
commit 0e769f3813
105 changed files with 2313 additions and 5314 deletions
@@ -0,0 +1,127 @@
<script setup>
defineOptions({ name: 'ProducibleSupport' })
import { computed, reactive, ref, onMounted } from 'vue'
import request from '../utils/request'
import { fmtTime } from '../utils/format'
import PageHelp from '../components/PageHelp.vue'
import { helpProducible } from '../help'
// 排产支撑:逐成品可生产套数 + 整体还能满产多少天/支撑到哪天/最先告罄短板物料
const loading = ref(false)
const items = ref([])
const supportDays = ref(0)
const supportUntil = ref('')
const shortBoardMaterial = ref('')
const computedAt = ref(0)
// 成品分页(§A2:只算启用成品、按需分页,不再全量无分页)
const page = reactive({ current: 1, size: 20 })
const pagedItems = computed(() => {
const start = (page.current - 1) * page.size
return items.value.slice(start, start + page.size)
})
function onSizeChange(s) {
page.size = s
page.current = 1
}
async function load() {
loading.value = true
try {
const data = await request.get('/production/producible-support')
items.value = data?.items || []
supportDays.value = data?.supportDays || 0
supportUntil.value = data?.supportUntil || ''
shortBoardMaterial.value = data?.shortBoardMaterial || ''
computedAt.value = data?.computedAt || 0
page.current = 1
} catch {
// 拦截器已提示
} finally {
loading.value = false
}
}
onMounted(load)
</script>
<template>
<div>
<el-card shadow="never" class="mb12">
<el-alert type="info" :closable="false" show-icon>
<template #title>
<div style="font-size:12px;line-height:1.7">
本页基于日排产与库存可用量推算<b>可生产套数</b> = 按各成品 BOM 单台用量 ÷ WMS 可用量取最小
<b>支撑天数</b> = 用可用量对日排产做逐日消耗模拟直到某物料不足排产排满后 N+1 天起按近3天平均日需求外推封顶365天
若无排产计划或排产日全部满足短板物料栏显示无排产计划 / 充足
</div>
</template>
</el-alert>
<el-row :gutter="12" style="margin-top:12px">
<el-col :span="6">
<el-card shadow="never" class="stat">
<div class="stat-num" :class="supportDays > 0 ? 'ok' : 'warn'">{{ supportDays }}</div>
<div class="stat-label">还能满产</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card shadow="never" class="stat">
<div class="stat-num">{{ supportUntil || '-' }}</div>
<div class="stat-label">库存可支撑到</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card shadow="never" class="stat">
<div class="stat-num short" :title="shortBoardMaterial">{{ shortBoardMaterial || '充足' }}</div>
<div class="stat-label">最先告罄短板物料</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card shadow="never" class="stat">
<div class="stat-num">{{ computedAt ? fmtTime(computedAt) : '-' }}</div>
<div class="stat-label">计算时间</div>
</el-card>
</el-col>
</el-row>
</el-card>
<el-card shadow="never" header="各成品可生产套数(只算启用成品;按 BOM 单台用量 ÷ 库存可用量取最小,短板物料见说明列)" v-loading="loading">
<el-table :data="pagedItems" border size="small" empty-text="暂无成品未配置 BOM 或物料清单">
<el-table-column prop="productCode" label="成品编码" min-width="150" />
<el-table-column prop="productName" label="成品名称" min-width="150">
<template #default="{ row }">{{ row.productName || '-' }}</template>
</el-table-column>
<el-table-column label="可生产套数" width="120" align="center" sortable prop="producibleQty">
<template #default="{ row }">
<span :class="row.producibleQty > 0 ? 'ok' : 'warn'" class="qty">{{ row.producibleQty }}</span>
</template>
</el-table-column>
<el-table-column label="短板说明" min-width="320">
<template #default="{ row }">
<span class="muted">{{ row.shortText || '-' }}</span>
</template>
</el-table-column>
</el-table>
<div class="pager">
<el-pagination background layout="total, sizes, prev, pager, next" :total="items.length"
:current-page="page.current" :page-size="page.size" :page-sizes="[10, 20, 50, 100]"
@current-change="(p) => (page.current = p)" @size-change="onSizeChange" />
</div>
</el-card>
<PageHelp :data="helpProducible" page="ProducibleSupport" />
</div>
</template>
<style scoped>
.mb12 { margin-bottom: 12px; }
.pager { display: flex; justify-content: flex-end; margin-top: 12px; }
.stat { text-align: center; }
.stat-num { font-size: 26px; font-weight: 700; color: #303133; word-break: break-all; line-height: 1.3; }
.stat-num.short { font-size: 18px; color: #e6a23c; }
.stat-num.ok { color: #67c23a; }
.stat-num.warn { color: #e6a23c; }
.stat-label { font-size: 12px; color: #909399; margin-top: 6px; }
.qty { font-size: 18px; font-weight: 700; }
.muted { color: #909399; }
</style>