docs(business): 生成业务契约文档并重构MES客户端代码

- 创建案例.md文档,包含业务场景清单、业务流程、案例和回归测试三份完整文档
- WMS库房管理系统涵盖入库、出库、库存、检验、盘点等24个业务场景
- MES产线控制系统包含工单、排产、BOM、备料、质检、追溯等23个业务场景
- 工位终端系统支持上线、报工、下线、领料、巡检等15个现场作业场景
- 主链路从合同到成品入库,异常链路处理不合格、数量不符、退料等情况
- 删除MES客户端中的ScanRecord相关代码,精简客户端结构
- 更新客户端初始化逻辑,移除扫描记录相关的依赖注入配置
This commit is contained in:
SunYF
2026-09-20 18:25:44 +08:00
parent 5f68e77d5f
commit 8e6e8e1d20
46 changed files with 1052 additions and 3954 deletions
+39 -13
View File
@@ -24,14 +24,22 @@ const loadingPrep = ref(false)
const queried = ref(false)
const rows = ref([])
onMounted(loadOrders)
async function loadOrders() {
const orders = ref([])
const ordersLoading = ref(false)
// 工单号来自 MES 实时查询:无输入默认拉「未完成优先」的前 20 条;输入时按工单号模糊远程搜索。
async function searchOrders(query) {
ordersLoading.value = true
try {
const data = await request.get('/orders', { params: { page: 1, pageSize: 200 } })
const data = await request.get('/orders', { params: { orderNo: query || '', limit: 20 } })
orders.value = Array.isArray(data) ? data : data?.list || []
} catch { orders.value = [] }
finally { ordersLoading.value = false }
}
const orders = ref([])
async function loadOrders() { await searchOrders('') }
function orderLabel(o) { return o.productName ? `${o.workOrderNo} ${o.productName}` : o.workOrderNo }
// 选中下拉项或手输兜底(allow-create)后直接查该工单台账
function onOrderPick(v) { if (v && String(v).trim()) queryPrep() }
onMounted(loadOrders)
const pendingRows = computed(() => rows.value.filter(r => r.status !== '领料完结'))
function gapOf(row) { return Math.max((row.totalQty || 0) - (row.outQty || 0), 0) }
@@ -326,7 +334,14 @@ async function submitGen() {
}
/* ===================== 出库记录(统一主表,全部 + 导出) ===================== */
const recFilters = reactive({ outboundType: '', status: '', materialCode: '', boxNo: '', ownershipType: '', dateRange: [] })
// 出库记录默认展示近 3 个月(与后端导出时间硬规则一致,保证「列表条数 = 导出条数」)
function recDefaultRange() {
const p = (n) => String(n).padStart(2, '0')
const fmt = (d) => `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}`
const end = new Date(); const start = new Date(); start.setMonth(start.getMonth() - 3)
return [fmt(start), fmt(end)]
}
const recFilters = reactive({ outboundType: '', status: '', materialCode: '', boxNo: '', ownershipType: '', dateRange: recDefaultRange() })
const recRows = ref([])
const recTotal = ref(0)
const loadingRec = ref(false)
@@ -346,8 +361,8 @@ async function loadRecords() {
page: recPage.current, pageSize: recPage.size
}
if (Array.isArray(recFilters.dateRange) && recFilters.dateRange.length === 2) {
params.startAt = new Date(recFilters.dateRange[0]).getTime()
params.endAt = new Date(recFilters.dateRange[1]).getTime() + 86399999
params.startDate = recFilters.dateRange[0]
params.endDate = recFilters.dateRange[1]
}
const data = await request.get('/outbound/query', { params })
recRows.value = (data?.list || []).map(r => ({ ...r, snCodes: snOf(r) }))
@@ -355,16 +370,20 @@ async function loadRecords() {
} finally { loadingRec.value = false }
}
function searchRec() { recPage.current = 1; loadRecords() }
function resetRec() { Object.assign(recFilters, { outboundType: '', status: '', materialCode: '', boxNo: '', dateRange: [] }); searchRec() }
function resetRec() { Object.assign(recFilters, { outboundType: '', status: '', materialCode: '', boxNo: '', dateRange: recDefaultRange() }); searchRec() }
function downloadCSV() {
// 时间窗口与列表保持一致:未选/已清空时回落近 3 个月,保证确认框条数 = 实际导出条数
const dr = (Array.isArray(recFilters.dateRange) && recFilters.dateRange.length === 2) ? recFilters.dateRange : recDefaultRange()
exportXlsxFetch('/api/outbound/export', {
outboundType: recFilters.outboundType,
status: recFilters.status,
materialCode: recFilters.materialCode.trim(),
boxNo: recFilters.boxNo.trim()
}, '出库管理.xlsx')
.then(() => ElMessage.success('导出成功'))
boxNo: recFilters.boxNo.trim(),
startDate: dr[0],
endDate: dr[1]
}, '出库管理.xlsx', { countUrl: '/outbound/query', name: '出库记录' })
.then((name) => { if (name) ElMessage.success('已导出:' + name) })
.catch(() => ElMessage.error('导出失败'))
}
@@ -387,8 +406,15 @@ onMounted(loadDicts)
<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="queryPrep" />
<el-select v-model="queryForm.orderNo" filterable remote allow-create default-first-option
clearable reserve-keyword :remote-method="searchOrders" :loading="ordersLoading"
placeholder="选择或输入工单号(默认未完成优先前20条,可模糊搜索)"
style="width:340px" @change="onOrderPick">
<el-option v-for="o in orders" :key="o.workOrderNo" :value="o.workOrderNo" :label="orderLabel(o)">
<span style="float:left">{{ o.workOrderNo }}</span>
<span style="float:right;color:#909399;font-size:12px">{{ o.productName }} {{ o.status }}</span>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="操作人">
<el-input :model-value="operator" disabled style="width:160px" />