diff --git a/src/views/mes/MLShow/CompoundCheckBalance.vue b/src/views/mes/MLShow/CompoundCheckBalance.vue new file mode 100644 index 0000000..1270348 --- /dev/null +++ b/src/views/mes/MLShow/CompoundCheckBalance.vue @@ -0,0 +1,8 @@ + + + + diff --git a/src/views/mes/MLShow/CompoundCheckInfoQuery.vue b/src/views/mes/MLShow/CompoundCheckInfoQuery.vue new file mode 100644 index 0000000..bc7d266 --- /dev/null +++ b/src/views/mes/MLShow/CompoundCheckInfoQuery.vue @@ -0,0 +1,8 @@ + + + + diff --git a/src/views/mes/MLShow/CompoundCheckOrderMaintain.vue b/src/views/mes/MLShow/CompoundCheckOrderMaintain.vue new file mode 100644 index 0000000..ec07f00 --- /dev/null +++ b/src/views/mes/MLShow/CompoundCheckOrderMaintain.vue @@ -0,0 +1,8 @@ + + + + diff --git a/src/views/mes/MLShow/CompoundDynamicStockCheck.vue b/src/views/mes/MLShow/CompoundDynamicStockCheck.vue new file mode 100644 index 0000000..3c9c3b8 --- /dev/null +++ b/src/views/mes/MLShow/CompoundDynamicStockCheck.vue @@ -0,0 +1,8 @@ + + + + diff --git a/src/views/mes/MLShow/CompoundProductionStat.vue b/src/views/mes/MLShow/CompoundProductionStat.vue new file mode 100644 index 0000000..2fd29e1 --- /dev/null +++ b/src/views/mes/MLShow/CompoundProductionStat.vue @@ -0,0 +1,8 @@ + + + + diff --git a/src/views/mes/MLShow/CompoundRealtimeStock.vue b/src/views/mes/MLShow/CompoundRealtimeStock.vue new file mode 100644 index 0000000..cbfa780 --- /dev/null +++ b/src/views/mes/MLShow/CompoundRealtimeStock.vue @@ -0,0 +1,8 @@ + + + + diff --git a/src/views/mes/MLShow/FinalCompoundFlowInfo.vue b/src/views/mes/MLShow/FinalCompoundFlowInfo.vue new file mode 100644 index 0000000..baa5d1f --- /dev/null +++ b/src/views/mes/MLShow/FinalCompoundFlowInfo.vue @@ -0,0 +1,8 @@ + + + + diff --git a/src/views/mes/MLShow/FinalCompoundOutboundDetail.vue b/src/views/mes/MLShow/FinalCompoundOutboundDetail.vue new file mode 100644 index 0000000..3d6cc7f --- /dev/null +++ b/src/views/mes/MLShow/FinalCompoundOutboundDetail.vue @@ -0,0 +1,8 @@ + + + + diff --git a/src/views/mes/MLShow/FinalCompoundUnusedQuery.vue b/src/views/mes/MLShow/FinalCompoundUnusedQuery.vue new file mode 100644 index 0000000..0879817 --- /dev/null +++ b/src/views/mes/MLShow/FinalCompoundUnusedQuery.vue @@ -0,0 +1,8 @@ + + + + diff --git a/src/views/mes/MLShow/MLShow页面说明.md b/src/views/mes/MLShow/MLShow页面说明.md new file mode 100644 index 0000000..fcec248 --- /dev/null +++ b/src/views/mes/MLShow/MLShow页面说明.md @@ -0,0 +1,117 @@ +# MLShow 页面说明 + +## 说明 +- 本目录页面为纯演示页,全部使用假数据。 +- 页面形态已重构为“单功能报表页”,不再渲染 `MES系统 / 胶料管理 / ...` 这类导航头。 +- 数据优先复用 `../mixTrace/show/data` 衍生数据(由 `data/source.ts` 汇总),再按页面字段扩展。 +- 页面配置集中在 `data/pageConfigs.ts`,通用渲染组件为 `components/LegacyDemoPage.vue`。 + +## 车间生产统计 +- Vue 文件:`WorkshopProductionStat.vue` +- 字段:物料名称、料名、投入重量、配方标重。 +- 逻辑:按日期/物料筛选,展示车间生产投入与配方目标对比。 + +## 车间胶料统计 +- Vue 文件:`WorkshopCompoundStat.vue` +- 字段:车间、物料名称、生产/消耗/出库(车数+重量)。 +- 逻辑:按日期/班组筛选,分组统计胶料流转总览。 + +## 小料消耗 +- Vue 文件:`SmallMaterialConsume.vue` +- 字段:单据号、业务日期、机台、班次班组、生产物料、投入物料、定额重量、实际重量。 +- 逻辑:按业务日期/机台/物料筛选,模拟小料消耗台账查询。 + +## 小料明细统计 +- Vue 文件:`SmallMaterialDetailStat.vue` +- 字段:生产日期、小料名称、早/中/夜(前存、计划、实际、消耗、结存)。 +- 逻辑:按日期区间统计三班库存变化,带“最新统计日期”提示。 + +## 小料盘点 +- Vue 文件:`SmallMaterialCheck.vue` +- 字段:小料名称、账面数量、盘点数量、盘点日期、班次。 +- 逻辑:按盘点日期/班次筛选,模拟盘点差异核对。 + +## 车间胶料消耗 +- Vue 文件:`WorkshopCompoundConsume.vue` +- 字段:生产日期、机台、班次班组、生产/投入物料、定额重量、称量重量、损耗量。 +- 逻辑:按生产条件筛选并计算损耗量。 + +## 车间胶料明细统计 +- Vue 文件:`WorkshopCompoundDetailStat.vue` +- 字段:日期、胶料类型、物料名称、上期结存/生产/消耗/结存(车数+重量)。 +- 逻辑:支持胶料类型复选筛选,展示期初期末变化。 + +## 车间胶料盘点 +- Vue 文件:`WorkshopCompoundCheck.vue` +- 字段:胶料名称、账面结存、盘点数量、差异、盘点日期。 +- 逻辑:按盘点日期/班次筛选,模拟盘点快照。 + +## 车间胶料盘存修正 +- Vue 文件:`WorkshopStockAdjust.vue` +- 字段:盘存日期、物料名称、盘存数量/重量、修正后数量/重量。 +- 逻辑:按日期和物料查询盘存修正结果。 + +## 物料超期预警 +- Vue 文件:`MaterialOverdueWarning.vue` +- 字段:生产日期、机台、起止车次、实际重量、生产时间、超期时长、条码、出库标志。 +- 逻辑:按时间区间筛选,支持“不显示已出库”开关过滤。 + +## 胶料盘点信息查询 +- Vue 文件:`CompoundCheckInfoQuery.vue` +- 字段:盘点单号、胶料条码、起始/结束车次、配方重量、实际重量、确认时间、扫描人。 +- 逻辑:按扫描时间+盘点单号组合查询,模拟盘点追溯。 + +## 胶料实时库存 +- Vue 文件:`CompoundRealtimeStock.vue` +- 字段:物料编码、物料名称、生产重量、剩余重量。 +- 逻辑:按物料筛选,展示实时库存列表。 + +## 密炼机台每小时产量查询 +- Vue 文件:`MixerHourlyOutput.vue` +- 字段:机台、总车数、时间段、时间段车数、胶料名称、胶料车数。 +- 逻辑:按时间区间/机台筛选,按小时粒度展示产量。 + +## 胶料动态库存(盘点) +- Vue 文件:`CompoundDynamicStockCheck.vue` +- 字段:料名结存、母胶A结存、母胶B结存。 +- 逻辑:以动态盘点视角展示多层级库存关系。 + +## 胶料盘点结存 +- Vue 文件:`CompoundCheckBalance.vue` +- 字段:单号、盘点单条码明细、确认时间。 +- 逻辑:保留“查询/结存/导出”操作,列表可为空用于维护场景演示。 + +## 炼胶工区日产量信息统计 +- Vue 文件:`MixingAreaDailyOutput.vue` +- 字段:ML1-ML8 车数/吨数、合计值、明细机台记录。 +- 逻辑:三段报表(车数汇总、吨数汇总、明细)模拟大屏统计。 + +## 胶料盘点单维护 +- Vue 文件:`CompoundCheckOrderMaintain.vue` +- 字段:盘点单号、记录人、记录时间、备注。 +- 逻辑:模拟维护页查询结果,保留添加/删除/导出动作。 + +## 胶料生产统计 +- Vue 文件:`CompoundProductionStat.vue` +- 字段:日期、ML1-ML8 车数、ML1-ML8 吨数、合计车、合计吨。 +- 逻辑:包含“平均”汇总行,模拟日度生产统计口径。 + +## 终炼胶出库明细表 +- Vue 文件:`FinalCompoundOutboundDetail.vue` +- 字段:操作时间、条码、生产日期、机台、班组、物料、起止车次、实际车数/重量、出库人员、质检标志。 +- 逻辑:按时间+机台+物料+条码筛选,模拟出库追踪明细。 + +## 终炼胶流转信息表 +- Vue 文件:`FinalCompoundFlowInfo.vue` +- 字段:生产信息、锁定解锁信息、出库信息、后工序扫描信息等多表块。 +- 逻辑:单号驱动的多区块联查,模拟完整流转链路。 + +## 终炼胶领用未使用查询 +- Vue 文件:`FinalCompoundUnusedQuery.vue` +- 字段:领用时间、领用标志、领用人、部件使用标志、条码、计划日期、机台、物料、起止车次、生产时间。 +- 逻辑:按领用时间区间筛选“已领用未使用”条码。 + +## 炼胶工区日产量信息定额重量统计 +- Vue 文件:`MixingAreaQuotaWeightStat.vue` +- 字段:ML1-ML8 车数、ML1-ML8 吨数、合计值、明细机台记录。 +- 逻辑:结构与日产量统计页一致,强调定额重量与实际重量对比。 diff --git a/src/views/mes/MLShow/MaterialOverdueWarning.vue b/src/views/mes/MLShow/MaterialOverdueWarning.vue new file mode 100644 index 0000000..6c3ce16 --- /dev/null +++ b/src/views/mes/MLShow/MaterialOverdueWarning.vue @@ -0,0 +1,8 @@ + + + + diff --git a/src/views/mes/MLShow/MixerHourlyOutput.vue b/src/views/mes/MLShow/MixerHourlyOutput.vue new file mode 100644 index 0000000..63ed2cc --- /dev/null +++ b/src/views/mes/MLShow/MixerHourlyOutput.vue @@ -0,0 +1,8 @@ + + + + diff --git a/src/views/mes/MLShow/MixingAreaDailyOutput.vue b/src/views/mes/MLShow/MixingAreaDailyOutput.vue new file mode 100644 index 0000000..301ed53 --- /dev/null +++ b/src/views/mes/MLShow/MixingAreaDailyOutput.vue @@ -0,0 +1,8 @@ + + + + diff --git a/src/views/mes/MLShow/MixingAreaQuotaWeightStat.vue b/src/views/mes/MLShow/MixingAreaQuotaWeightStat.vue new file mode 100644 index 0000000..b2e9eb4 --- /dev/null +++ b/src/views/mes/MLShow/MixingAreaQuotaWeightStat.vue @@ -0,0 +1,8 @@ + + + + diff --git a/src/views/mes/MLShow/SmallMaterialCheck.vue b/src/views/mes/MLShow/SmallMaterialCheck.vue new file mode 100644 index 0000000..a9db252 --- /dev/null +++ b/src/views/mes/MLShow/SmallMaterialCheck.vue @@ -0,0 +1,8 @@ + + + + diff --git a/src/views/mes/MLShow/SmallMaterialConsume.vue b/src/views/mes/MLShow/SmallMaterialConsume.vue new file mode 100644 index 0000000..13d1934 --- /dev/null +++ b/src/views/mes/MLShow/SmallMaterialConsume.vue @@ -0,0 +1,8 @@ + + + + diff --git a/src/views/mes/MLShow/SmallMaterialDetailStat.vue b/src/views/mes/MLShow/SmallMaterialDetailStat.vue new file mode 100644 index 0000000..e1860e0 --- /dev/null +++ b/src/views/mes/MLShow/SmallMaterialDetailStat.vue @@ -0,0 +1,8 @@ + + + + diff --git a/src/views/mes/MLShow/WorkshopCompoundCheck.vue b/src/views/mes/MLShow/WorkshopCompoundCheck.vue new file mode 100644 index 0000000..d20def8 --- /dev/null +++ b/src/views/mes/MLShow/WorkshopCompoundCheck.vue @@ -0,0 +1,8 @@ + + + + diff --git a/src/views/mes/MLShow/WorkshopCompoundConsume.vue b/src/views/mes/MLShow/WorkshopCompoundConsume.vue new file mode 100644 index 0000000..32f609f --- /dev/null +++ b/src/views/mes/MLShow/WorkshopCompoundConsume.vue @@ -0,0 +1,8 @@ + + + + diff --git a/src/views/mes/MLShow/WorkshopCompoundDetailStat.vue b/src/views/mes/MLShow/WorkshopCompoundDetailStat.vue new file mode 100644 index 0000000..bab1c0a --- /dev/null +++ b/src/views/mes/MLShow/WorkshopCompoundDetailStat.vue @@ -0,0 +1,8 @@ + + + + diff --git a/src/views/mes/MLShow/WorkshopCompoundStat.vue b/src/views/mes/MLShow/WorkshopCompoundStat.vue new file mode 100644 index 0000000..271dbf9 --- /dev/null +++ b/src/views/mes/MLShow/WorkshopCompoundStat.vue @@ -0,0 +1,8 @@ + + + + diff --git a/src/views/mes/MLShow/WorkshopProductionStat.vue b/src/views/mes/MLShow/WorkshopProductionStat.vue new file mode 100644 index 0000000..a0f8323 --- /dev/null +++ b/src/views/mes/MLShow/WorkshopProductionStat.vue @@ -0,0 +1,8 @@ + + + + diff --git a/src/views/mes/MLShow/WorkshopStockAdjust.vue b/src/views/mes/MLShow/WorkshopStockAdjust.vue new file mode 100644 index 0000000..f692295 --- /dev/null +++ b/src/views/mes/MLShow/WorkshopStockAdjust.vue @@ -0,0 +1,8 @@ + + + + diff --git a/src/views/mes/MLShow/components/LegacyDemoPage.vue b/src/views/mes/MLShow/components/LegacyDemoPage.vue new file mode 100644 index 0000000..2bdc210 --- /dev/null +++ b/src/views/mes/MLShow/components/LegacyDemoPage.vue @@ -0,0 +1,314 @@ + + + + + diff --git a/src/views/mes/MLShow/data/pageConfigs.ts b/src/views/mes/MLShow/data/pageConfigs.ts new file mode 100644 index 0000000..318351c --- /dev/null +++ b/src/views/mes/MLShow/data/pageConfigs.ts @@ -0,0 +1,218 @@ +import type { DemoColumn, DemoPageConfig, PageFilter } from '../types'; +import { baseRows, categoryPool, date, datetime, machinePool, materialPool, mlMachinePool, n, shiftPool, teamPool } from './source'; + +const selectOptions = (list: string[]) => list.map(item => ({ label: item, value: item })); +const defaultToolbar = ['页面设置', '打印预览', '导出'].map(label => ({ label })); + +const withStartEnd = >(rows: T[]): Array => + rows.map(item => { + const [startNo, endNo] = String(item.startEndNo || '1-2').split('-'); + return { ...item, startNo, endNo }; + }); + +const dateFilters = (start = '2026-01-01', end = '2026-01-01', targets = ['prodDate']): PageFilter[] => [ + { key: 'startDate', label: '开始日期', type: 'date', defaultValue: start, targets }, + { key: 'endDate', label: '结束日期', type: 'date', defaultValue: end, targets } +]; + +const groupedCarsWeight = (label: string, prefix: string): DemoColumn => ({ + label, + align: 'center', + children: [ + { label: '车数', prop: `${prefix}Cars`, minWidth: 90, align: 'right' }, + { label: '重量', prop: `${prefix}Weight`, minWidth: 100, align: 'right', format: 'fixed1' } + ] +}); + +const shiftGroup = (label: string, prefix: string): DemoColumn => ({ + label, + align: 'center', + children: [ + { label: '前存', prop: `${prefix}Prev`, minWidth: 80, align: 'right' }, + { label: '计划生产', prop: `${prefix}Plan`, minWidth: 90, align: 'right' }, + { label: '实际生产', prop: `${prefix}Actual`, minWidth: 90, align: 'right' }, + { label: '消耗数量', prop: `${prefix}Consume`, minWidth: 90, align: 'right' }, + { label: '实际结存', prop: `${prefix}Stock`, minWidth: 90, align: 'right' } + ] +}); + +const mlMachines = [...mlMachinePool]; +const summaryRows = ['2026-01-01', '2026-01-02'].map((d, idx) => { + const dayRows = baseRows.filter(row => row.prodDate === d).slice(0, 48); + const sourceRows = dayRows.length ? dayRows : baseRows.slice(idx * 24, idx * 24 + 48).map(item => ({ ...item, prodDate: d })); + const row: Record = { date: d }; + mlMachines.forEach((m, i) => { + const data = sourceRows.filter((item, j) => mlMachines[j % mlMachines.length] === m || String(item.machine).toUpperCase() === m); + row[`${m}Cars`] = data.reduce((s, x) => s + (Number(x.cars) || 0), 0); + row[`${m}Tons`] = n(data.reduce((s, x) => s + (Number(x.produceWeight) || 0), 0) / 1000, 3); + if (!row[`${m}Cars`]) { + row[`${m}Cars`] = 60 + i * 2 + idx; + row[`${m}Tons`] = n((60 + i * 2 + idx) * 0.42, 3); + } + }); + row.sumCars = mlMachines.reduce((s, m) => s + Number(row[`${m}Cars`]), 0); + row.sumTons = n(mlMachines.reduce((s, m) => s + Number(row[`${m}Tons`]), 0), 3); + return row; +}); + +const productionAvgRow = (() => { + const avg: Record = { date: '平均', _rowType: 'summary' }; + mlMachines.forEach(m => { + avg[`${m}Cars`] = n((Number(summaryRows[0][`${m}Cars`]) + Number(summaryRows[1][`${m}Cars`])) / 2, 1); + avg[`${m}Tons`] = n((Number(summaryRows[0][`${m}Tons`]) + Number(summaryRows[1][`${m}Tons`])) / 2, 2); + }); + avg.sumCars = ''; + avg.sumTons = ''; + return avg; +})(); + +const pageList = [ + ['workshop-production-stat', 'WorkshopProductionStat', '车间生产统计', '01车间生产统计.PNG'], + ['workshop-compound-stat', 'WorkshopCompoundStat', '车间胶料统计', '02车间胶料统计.PNG'], + ['small-material-consume', 'SmallMaterialConsume', '小料消耗', '03小料消耗.PNG'], + ['small-material-detail-stat', 'SmallMaterialDetailStat', '小料明细统计', '04小料明细统计.PNG'], + ['small-material-check', 'SmallMaterialCheck', '小料盘点', '05小料盘点.PNG'], + ['workshop-compound-consume', 'WorkshopCompoundConsume', '车间胶料消耗', '06车间胶料消耗.PNG'], + ['workshop-compound-detail-stat', 'WorkshopCompoundDetailStat', '车间胶料明细统计', '07.PNG'], + ['workshop-compound-check', 'WorkshopCompoundCheck', '车间胶料盘点', '08.PNG'], + ['workshop-stock-adjust', 'WorkshopStockAdjust', '车间胶料盘存修正', '09.PNG'], + ['material-overdue-warning', 'MaterialOverdueWarning', '物料超期预警', '10.PNG'], + ['compound-check-info-query', 'CompoundCheckInfoQuery', '胶料盘点信息查询', '11.PNG'], + ['compound-realtime-stock', 'CompoundRealtimeStock', '胶料实时库存', '12.PNG'], + ['mixer-hourly-output', 'MixerHourlyOutput', '密炼机台每小时产量查询', '13.PNG'], + ['compound-dynamic-stock-check', 'CompoundDynamicStockCheck', '胶料动态库存(盘点)', '14.PNG'], + ['compound-check-balance', 'CompoundCheckBalance', '胶料盘点结存', '15.PNG'], + ['mixing-area-daily-output', 'MixingAreaDailyOutput', '炼胶工区日产量信息统计', '16.PNG'], + ['compound-check-order-maintain', 'CompoundCheckOrderMaintain', '胶料盘点单维护', '17.PNG'], + ['compound-production-stat', 'CompoundProductionStat', '胶料生产统计', '18.PNG'], + ['final-compound-outbound-detail', 'FinalCompoundOutboundDetail', '终炼胶出库明细表', '19.PNG'], + ['final-compound-flow-info', 'FinalCompoundFlowInfo', '终炼胶流转信息表', '20.PNG'], + ['final-compound-unused-query', 'FinalCompoundUnusedQuery', '终炼胶领用未使用查询', '21.PNG'], + ['mixing-area-quota-weight-stat', 'MixingAreaQuotaWeightStat', '炼胶工区日产量信息定额重量统计', '22.PNG'] +] as const; + +export const pageMetas = pageList.map(([key, file, title, photo]) => ({ key, file, title, photo })); + +const configMap: Record = { + 'workshop-production-stat': { + key: 'workshop-production-stat', title: '车间生产统计', loadingMs: 680, + toolbarActions: [...defaultToolbar, { label: '保存为常用参数组', type: 'primary' }], + filters: [...dateFilters('2026-01-01', '2026-01-04'), { key: 'materialName', label: '物料名称', type: 'select', options: selectOptions(materialPool.slice(0, 24)), targets: ['materialName', 'formulaName'] }], + tables: [{ key: 'main', showIndex: true, columns: [{ label: '物料名称', prop: 'materialName', minWidth: 140 }, { label: '料名', prop: 'formulaName', minWidth: 140 }, { label: '投入重量', prop: 'produceWeight', minWidth: 120, align: 'right', format: 'fixed1' }, { label: '配方标重', prop: 'setWeight', minWidth: 120, align: 'right', format: 'fixed1' }], rows: baseRows.slice(0, 38) }] + }, + 'workshop-compound-stat': { + key: 'workshop-compound-stat', title: '车间胶料统计', loadingMs: 700, toolbarActions: defaultToolbar, + filters: [...dateFilters(), { key: 'team', label: '班组', type: 'select', options: selectOptions(teamPool), targets: ['team'] }], + tables: [{ key: 'main', showIndex: true, columns: [{ label: '车间', prop: 'workshop', minWidth: 90 }, { label: '物料名称', prop: 'materialName', minWidth: 130 }, groupedCarsWeight('生产', 'produce'), groupedCarsWeight('消耗', 'consume'), groupedCarsWeight('出库', 'outbound')], rows: baseRows.slice(0, 34).map(item => ({ ...item, produceCars: item.cars + 2, produceWeight: item.produceWeight + 2800, consumeCars: item.consumeCars + 1, consumeWeight: item.consumeWeight + 2100, outboundCars: item.cars, outboundWeight: item.produceWeight - 500 })) }] + }, + 'small-material-consume': { + key: 'small-material-consume', title: '小料消耗', loadingMs: 720, toolbarActions: [...defaultToolbar, { label: 'PDF' }], + filters: [{ key: 'businessDate', label: '业务日期', type: 'date', defaultValue: '2026-01-01', targets: ['businessDate'] }, { key: 'businessDateEnd', label: '业务日期', type: 'date', defaultValue: '2026-01-01', targets: ['businessDate'] }, { key: 'machine', label: '机台', type: 'select', options: selectOptions(machinePool), targets: ['machine'] }, { key: 'materialName', label: '物料名称', type: 'select', options: selectOptions(materialPool.slice(0, 24)), targets: ['materialName', 'inputMaterial'] }], + tables: [{ key: 'main', showIndex: true, columns: [{ label: '单据号', prop: 'orderNo', minWidth: 120 }, { label: '业务日期', prop: 'businessDate', minWidth: 110 }, { label: '生产机台', prop: 'machine', minWidth: 90 }, { label: '班次', prop: 'shift', minWidth: 70, align: 'center' }, { label: '班组', prop: 'team', minWidth: 70, align: 'center' }, { label: '生产物料', prop: 'materialName', minWidth: 130 }, { label: '投入物料', prop: 'inputMaterial', minWidth: 130 }, { label: '定额重量', prop: 'setWeight', minWidth: 100, align: 'right', format: 'fixed1' }, { label: '实际重量', prop: 'consumeWeight', minWidth: 100, align: 'right', format: 'fixed1' }], rows: baseRows.slice(0, 42) }] + }, + 'small-material-detail-stat': { + key: 'small-material-detail-stat', title: '小料明细统计', statusText: '最新统计日期:2026-02-24', loadingMs: 730, toolbarActions: defaultToolbar, + filters: [...dateFilters('2026-01-01', '2026-01-04')], + tables: [{ key: 'main', showIndex: true, columns: [{ label: '生产日期', prop: 'prodDate', minWidth: 120 }, { label: '小料名称', prop: 'materialName', minWidth: 140 }, { label: '早', align: 'center', children: [{ label: '前存', prop: 'morningPrev', minWidth: 80, align: 'right' }, { label: '计划生产', prop: 'morningPlan', minWidth: 90, align: 'right' }, { label: '实际生产', prop: 'morningActual', minWidth: 90, align: 'right' }, { label: '消耗数量', prop: 'morningConsume', minWidth: 90, align: 'right' }, { label: '实际结存', prop: 'morningStock', minWidth: 90, align: 'right' }] }, { label: '中', align: 'center', children: [{ label: '前存', prop: 'noonPrev', minWidth: 80, align: 'right' }, { label: '计划生产', prop: 'noonPlan', minWidth: 90, align: 'right' }, { label: '实际生产', prop: 'noonActual', minWidth: 90, align: 'right' }, { label: '消耗数量', prop: 'noonConsume', minWidth: 90, align: 'right' }, { label: '实际结存', prop: 'noonStock', minWidth: 90, align: 'right' }] }, shiftGroup('夜', 'night')], rows: baseRows.slice(0, 36).map((item, idx) => ({ ...item, morningPrev: Math.max(0, item.stockQty - 4), morningPlan: item.planCars, morningActual: item.cars, morningConsume: item.consumeCars, morningStock: Math.max(0, item.stockQty - item.consumeCars), noonPrev: Math.max(0, item.stockQty - 6), noonPlan: Math.max(item.planCars - 1, 0), noonActual: Math.max(item.cars - 1, 0), noonConsume: Math.max(item.consumeCars - 1, 0), noonStock: Math.max(0, item.stockQty - item.consumeCars - 2), nightPrev: Math.max(0, item.stockQty - 8), nightPlan: Math.max(item.planCars - 1, 0), nightActual: Math.max(item.cars - 1, 0), nightConsume: Math.max(item.consumeCars - 1, 0), nightStock: Math.max(0, item.stockQty - item.consumeCars - 3), prodDate: date(idx) })) }] + }, + 'small-material-check': { + key: 'small-material-check', title: '小料盘点', statusText: '最新盘点日期:2012-06-04', loadingMs: 660, toolbarActions: defaultToolbar, + filters: [{ key: 'checkDate', label: '盘点日期', type: 'date', defaultValue: '2012-06-04', targets: ['checkDate'] }, { key: 'shift', label: '班次', type: 'select', options: selectOptions(shiftPool), targets: ['shift'] }], + tables: [{ key: 'main', showIndex: true, columns: [{ label: '小料名称', prop: 'materialName', minWidth: 140 }, { label: '账面数量', prop: 'stockQty', minWidth: 100, align: 'right' }, { label: '盘点数量', prop: 'checkQty', minWidth: 100, align: 'right' }, { label: '盘点日期', prop: 'checkDate', minWidth: 110 }, { label: '班次', prop: 'shift', minWidth: 70, align: 'center' }], rows: baseRows.slice(0, 38).map(item => ({ ...item, checkQty: item.stockQty, checkDate: '2012-06-04' })) }] + }, + 'workshop-compound-consume': { + key: 'workshop-compound-consume', title: '车间胶料消耗', loadingMs: 670, toolbarActions: [...defaultToolbar, { label: 'PDF' }], + filters: [...dateFilters(), { key: 'shift', label: '班次', type: 'select', options: selectOptions(shiftPool), targets: ['shift'] }, { key: 'team', label: '班组', type: 'select', options: selectOptions(teamPool), targets: ['team'] }, { key: 'materialName', label: '物料名称', type: 'select', options: selectOptions(materialPool.slice(0, 24)), targets: ['materialName', 'inputMaterial'] }, { key: 'machine', label: '机台', type: 'select', options: selectOptions(machinePool), targets: ['machine'] }], + tables: [{ key: 'main', showIndex: true, columns: [{ label: '生产日期', prop: 'prodDate', minWidth: 110 }, { label: '生产机台', prop: 'machine', minWidth: 90 }, { label: '班次', prop: 'shift', minWidth: 70, align: 'center' }, { label: '班组', prop: 'team', minWidth: 70, align: 'center' }, { label: '生产物料', prop: 'materialName', minWidth: 130 }, { label: '投入物料', prop: 'inputMaterial', minWidth: 130 }, { label: '定额重量', prop: 'setWeight', minWidth: 90, align: 'right', format: 'fixed1' }, { label: '称量重量', prop: 'actualWeight', minWidth: 90, align: 'right', format: 'fixed1' }, { label: '损耗量', prop: 'lossWeight', minWidth: 90, align: 'right', format: 'fixed1' }], rows: baseRows.slice(0, 40).map(item => ({ ...item, lossWeight: n(item.actualWeight - item.setWeight, 1) })) }] + }, + 'workshop-compound-detail-stat': { + key: 'workshop-compound-detail-stat', title: '车间胶料明细统计', statusText: '上次统计日期为:2026-02-24', loadingMs: 680, toolbarActions: [...defaultToolbar, { label: '分页导出' }], + filters: [...dateFilters('2026-01-01', '2026-01-07'), { key: 'category', label: '胶料类型', type: 'checkbox', options: selectOptions(categoryPool), targets: ['category'] }], + tables: [{ key: 'main', showIndex: true, columns: [{ label: '日期', prop: 'prodDate', minWidth: 100 }, { label: '胶料类型', prop: 'category', minWidth: 100 }, { label: '物料名称', prop: 'materialName', minWidth: 130 }, groupedCarsWeight('上期结存', 'prev'), groupedCarsWeight('生产', 'produce'), groupedCarsWeight('消耗', 'consume'), groupedCarsWeight('结存', 'balance')], rows: baseRows.slice(0, 42).map(item => ({ ...item, prevCars: item.cars + 4, prevWeight: n(item.produceWeight + 800, 1), produceCars: item.cars, consumeCars: item.consumeCars, balanceCars: Math.max(0, item.cars - item.consumeCars + 2), balanceWeight: n(item.produceWeight - item.consumeWeight + 500, 1) })) }] + }, + 'workshop-compound-check': { + key: 'workshop-compound-check', title: '车间胶料盘点', statusText: '最新盘点日期:2018-07-24', loadingMs: 670, toolbarActions: defaultToolbar, + filters: [{ key: 'checkDate', label: '盘点日期', type: 'date', defaultValue: '2018-06-24', targets: ['checkDate'] }, { key: 'shift', label: '班次', type: 'select', options: selectOptions(shiftPool), targets: ['shift'] }], + tables: [{ key: 'main', showIndex: true, columns: [{ label: '胶料名称', prop: 'materialName', minWidth: 130 }, { label: '账面结存(车)', prop: 'bookCars', minWidth: 110, align: 'right' }, { label: '盘点数量(车)', prop: 'checkCars', minWidth: 110, align: 'right' }, { label: '差异(车)', prop: 'diffCars', minWidth: 90, align: 'right' }, { label: '盘点日期', prop: 'checkDate', minWidth: 110 }], rows: baseRows.slice(0, 40).map(item => ({ ...item, bookCars: item.cars, checkCars: Math.max(0, item.cars + (item.seq % 3) - 1), diffCars: (item.seq % 3) - 1, checkDate: '2018-06-24' })) }] + }, + 'workshop-stock-adjust': { + key: 'workshop-stock-adjust', title: '车间胶料盘存修正', loadingMs: 630, toolbarActions: [{ label: '查询', type: 'primary' }, { label: '导出' }], + filters: [{ key: 'checkDate', label: '盘存日期', type: 'date', defaultValue: '2026-01-01', targets: ['checkDate'] }, { key: 'materialName', label: '物料名称', type: 'select', options: selectOptions(materialPool.slice(0, 24)), targets: ['materialName'] }], + tables: [{ key: 'main', showIndex: true, columns: [{ label: '盘存日期', prop: 'checkDate', minWidth: 120 }, { label: '物料名称', prop: 'materialName', minWidth: 140 }, { label: '盘存数量', prop: 'stockQty', minWidth: 100, align: 'right' }, { label: '盘存重量', prop: 'stockWeight', minWidth: 120, align: 'right', format: 'fixed1' }, { label: '修正后数量', prop: 'adjustQty', minWidth: 100, align: 'right' }, { label: '修正后重量', prop: 'adjustWeight', minWidth: 120, align: 'right', format: 'fixed1' }], rows: baseRows.slice(0, 32).map(item => ({ ...item, checkDate: item.prodDate, adjustQty: Math.max(0, item.stockQty + (item.seq % 2)), adjustWeight: n(item.stockWeight + (item.seq % 3) * 60, 1) })) }] + }, + 'material-overdue-warning': { + key: 'material-overdue-warning', title: '物料超期预警', loadingMs: 680, toolbarActions: defaultToolbar, + filters: [...dateFilters(), { key: 'hideOutbound', label: '不显示已出库', type: 'switch', defaultValue: true }], + tables: [{ key: 'main', showIndex: true, columns: [{ label: '生产日期', prop: 'prodDate', minWidth: 110 }, { label: '机台', prop: 'machine', minWidth: 90 }, { label: '起止车次', prop: 'startEndNo', minWidth: 100, align: 'center' }, { label: '实际重量', prop: 'actualWeight', minWidth: 100, align: 'right', format: 'fixed3' }, { label: '生产时间', prop: 'operTime', minWidth: 160 }, { label: '超期时长(h)', prop: 'overdueHours', minWidth: 110, align: 'right' }, { label: '条码', prop: 'barcode', minWidth: 170 }, { label: '出库标志', prop: 'outFlag', minWidth: 100, align: 'center' }], rows: baseRows.slice(0, 56).map(item => ({ ...item, outFlag: item.seq % 4 === 0 ? '已出库' : '未出库' })) }] + }, + 'compound-check-info-query': { + key: 'compound-check-info-query', title: '胶料盘点信息查询', loadingMs: 720, toolbarActions: defaultToolbar, + filters: [{ key: 'scanStart', label: '扫描时间', type: 'datetime', defaultValue: '2026-01-01 00:00:00', targets: ['scanDate'] }, { key: 'scanEnd', label: '扫描时间', type: 'datetime', defaultValue: '2026-01-31 00:00:00', targets: ['scanDate'] }, { key: 'checkNo', label: '盘点单号', type: 'input', targets: ['checkNo'] }], + tables: [{ key: 'main', showIndex: true, columns: [{ label: '盘点单号', prop: 'checkNo', minWidth: 150 }, { label: '胶料条码', prop: 'barcode', minWidth: 170 }, { label: '起始车次', prop: 'startNo', minWidth: 90, align: 'right' }, { label: '结束车次', prop: 'endNo', minWidth: 90, align: 'right' }, { label: '配方重量', prop: 'setWeight', minWidth: 100, align: 'right', format: 'fixed3' }, { label: '实际重量', prop: 'actualWeight', minWidth: 100, align: 'right', format: 'fixed3' }, { label: '确认时间', prop: 'confirmTime', minWidth: 160 }, { label: '扫描人', prop: 'scanUser', minWidth: 100 }], rows: withStartEnd(baseRows.slice(0, 48)).map(item => ({ ...item, checkNo: `PD${item.orderNo}`, confirmTime: datetime(item.seq + 12) })) }] + }, + 'compound-realtime-stock': { + key: 'compound-realtime-stock', title: '胶料实时库存', loadingMs: 560, toolbarActions: defaultToolbar, + filters: [{ key: 'materialName', label: '物料名称', type: 'select', options: selectOptions(materialPool.slice(0, 24)), targets: ['materialName'] }], + tables: [{ key: 'main', showIndex: true, columns: [{ label: '物料编码', prop: 'materialCode', minWidth: 140 }, { label: '物料名称', prop: 'materialName', minWidth: 130 }, { label: '生产重量', prop: 'produceWeight', minWidth: 110, align: 'right', format: 'fixed1' }, { label: '剩余重量', prop: 'leftWeight', minWidth: 110, align: 'right', format: 'fixed1' }], rows: baseRows.slice(0, 58).map(item => ({ materialCode: `${400000000000 + item.seq * 73}`, materialName: item.materialName, produceWeight: n(item.produceWeight * 12, 3), leftWeight: n(item.produceWeight * ((item.seq % 6) + 1), 3) })) }] + }, + 'mixer-hourly-output': { + key: 'mixer-hourly-output', title: '密炼机台每小时产量查询', loadingMs: 710, toolbarActions: defaultToolbar, + filters: [{ key: 'startDatetime', label: '生产开始时间', type: 'datetime', defaultValue: '2026-01-01 10:00:00', targets: ['timeSeg'] }, { key: 'endDatetime', label: '生产结束时间', type: 'datetime', defaultValue: '2026-01-02 10:00:00', targets: ['timeSeg'] }, { key: 'machine', label: '机台', type: 'select', options: selectOptions(machinePool), targets: ['machine'] }], + tables: [{ key: 'main', columns: [{ label: '机台', prop: 'machine', minWidth: 90 }, { label: '总车数', prop: 'totalCars', minWidth: 90, align: 'right' }, { label: '时间段', prop: 'timeSeg', minWidth: 120 }, { label: '时间段车数', prop: 'cars', minWidth: 100, align: 'right' }, { label: '胶料名称', prop: 'materialName', minWidth: 130 }, { label: '胶料车数', prop: 'materialCars', minWidth: 100, align: 'right' }], rows: Array.from({ length: 36 }).map((_, index) => ({ machine: machinePool[index % machinePool.length], totalCars: 440 + (index % 7) * 6, timeSeg: `${date(index)} ${String(index % 24).padStart(2, '0')}`, cars: (index % 22) + 4, materialName: materialPool[index % materialPool.length], materialCars: (index % 22) + 1 })) }] + }, + 'compound-dynamic-stock-check': { + key: 'compound-dynamic-stock-check', title: '胶料动态库存(盘点)', loadingMs: 560, toolbarActions: defaultToolbar, + filters: [], + tables: [{ key: 'main', title: '胶料动态库存(盘点)', columns: [{ label: '料名', prop: 'baseName', minWidth: 120 }, { label: '结存', prop: 'baseStock', minWidth: 90, align: 'right' }, { label: '母胶', prop: 'motherNameA', minWidth: 140 }, { label: '结存', prop: 'motherStockA', minWidth: 90, align: 'right' }, { label: '母胶', prop: 'motherNameB', minWidth: 140 }, { label: '结存', prop: 'motherStockB', minWidth: 90, align: 'right' }], rows: baseRows.slice(0, 30).map((item, index) => ({ baseName: item.materialName, baseStock: Math.max(0, 260 - index * 4), motherNameA: materialPool[(index + 8) % materialPool.length], motherStockA: Math.max(0, 65 - (index % 26)), motherNameB: materialPool[(index + 13) % materialPool.length], motherStockB: Math.max(0, 88 - (index % 31)) })) }] + }, + 'compound-check-balance': { + key: 'compound-check-balance', title: '胶料盘点结存', loadingMs: 500, toolbarActions: [{ label: '查询', type: 'primary' }, { label: '结存', type: 'success' }, { label: '导出' }], + filters: [{ key: 'checkDate', label: '盘点日期', type: 'date', defaultValue: '2026-01-01', targets: ['checkDate'] }], + tables: [{ key: 'main', columns: [{ label: '单号', prop: 'sheetNo', minWidth: 220 }, { label: '盘点单条码明细', prop: 'sheetDetail', minWidth: 280 }, { label: '确认时间', prop: 'confirmTime', minWidth: 180 }], rows: [] }] + }, + 'mixing-area-daily-output': { + key: 'mixing-area-daily-output', title: '炼胶工区日产量信息统计', loadingMs: 780, toolbarActions: defaultToolbar, + filters: [{ key: 'planDate', label: '计划日期', type: 'date', defaultValue: '2026-01-01', targets: ['date'] }], + tables: [{ key: 'summaryCars', title: '2026年1月1日炼胶工区产量信息统计表(单位:车)', columns: [{ label: '日期', prop: 'date', minWidth: 120 }, ...mlMachines.map(item => ({ label: `${item}车`, prop: `${item}Cars`, minWidth: 82, align: 'right' as const })), { label: '合计/车', prop: 'sumCars', minWidth: 100, align: 'right' }], rows: summaryRows }, { key: 'summaryWeight', title: '2026年1月1日炼胶工区产量信息统计表(单位:吨)', columns: [{ label: '日期', prop: 'date', minWidth: 120 }, ...mlMachines.map(item => ({ label: `${item}吨`, prop: `${item}Tons`, minWidth: 92, align: 'right' as const, format: 'fixed3' as const })), { label: '合计/吨', prop: 'sumTons', minWidth: 100, align: 'right', format: 'fixed3' }], rows: summaryRows }, { key: 'detail', columns: [{ label: '日期', prop: 'date', minWidth: 120 }, { label: '班组', prop: 'team', minWidth: 80, align: 'center' }, { label: '机台', prop: 'machine', minWidth: 90 }, { label: '胶料类型', prop: 'category', minWidth: 100 }, { label: '实际数量(车)', prop: 'cars', minWidth: 120, align: 'right' }, { label: '实际重量(吨)', prop: 'ton', minWidth: 120, align: 'right', format: 'fixed3' }], rows: baseRows.slice(0, 26).map(item => ({ date: summaryRows[0].date, team: item.team, machine: item.machine, category: item.category, cars: item.cars * 40, ton: n(item.actualWeight / 5, 3) })) }] + }, + 'compound-check-order-maintain': { + key: 'compound-check-order-maintain', title: '胶料盘点单维护', loadingMs: 560, toolbarActions: [{ label: '查询', type: 'primary' }, { label: '添加', type: 'success' }, { label: '删除', type: 'danger' }, { label: '导出' }], + filters: [{ key: 'startDate', label: '开始日期', type: 'date', defaultValue: '2026-01-01', targets: ['recordTime'] }], + tables: [{ key: 'main', columns: [{ label: '盘点单号', prop: 'sheetNo', minWidth: 180 }, { label: '记录人', prop: 'recorder', minWidth: 120 }, { label: '记录时间', prop: 'recordTime', minWidth: 180 }, { label: '备注', prop: 'remark', minWidth: 220 }], rows: baseRows.slice(0, 6).map(item => ({ sheetNo: `PDWH${item.orderNo}`, recorder: item.operator, recordTime: datetime(item.seq), remark: item.seq % 2 === 0 ? '已确认' : '待确认' })) }] + }, + 'compound-production-stat': { + key: 'compound-production-stat', title: '胶料生产统计', loadingMs: 680, toolbarActions: defaultToolbar, + filters: [{ key: 'planStartDate', label: '计划开始日期', type: 'date', defaultValue: '2026-01-01', targets: ['date'] }, { key: 'planEndDate', label: '计划结束日期', type: 'date', defaultValue: '2026-01-02', targets: ['date'] }], + tables: [{ key: 'main', showIndex: true, columns: [{ label: '日期', prop: 'date', minWidth: 100 }, ...mlMachines.map(item => ({ label: `${item}车`, prop: `${item}Cars`, minWidth: 82, align: 'right' as const })), ...mlMachines.map(item => ({ label: `${item}吨`, prop: `${item}Tons`, minWidth: 92, align: 'right' as const, format: 'fixed3' as const })), { label: '合计/车', prop: 'sumCars', minWidth: 100, align: 'right' }, { label: '合计/吨', prop: 'sumTons', minWidth: 100, align: 'right', format: 'fixed3' }], rows: [...summaryRows, productionAvgRow] }] + }, + 'final-compound-outbound-detail': { + key: 'final-compound-outbound-detail', title: '终炼胶出库明细表', loadingMs: 730, toolbarActions: defaultToolbar, + filters: [{ key: 'operStart', label: '操作时间', type: 'datetime', defaultValue: '2026-01-01 10:00:00', targets: ['operTime'] }, { key: 'operEnd', label: '操作时间', type: 'datetime', defaultValue: '2026-01-10 10:00:00', targets: ['operTime'] }, { key: 'machine', label: '机台名称', type: 'select', options: selectOptions(machinePool), targets: ['machine'] }, { key: 'materialName', label: '物料名称', type: 'select', options: selectOptions(materialPool.slice(0, 24)), targets: ['materialName'] }, { key: 'barcode', label: '条码号', type: 'input', targets: ['barcode'] }], + tables: [{ key: 'main', showIndex: true, columns: [{ label: '操作时间', prop: 'operTime', minWidth: 160 }, { label: '条码号', prop: 'barcode', minWidth: 170 }, { label: '生产日期', prop: 'planDate', minWidth: 110 }, { label: '生产机台', prop: 'machine', minWidth: 90 }, { label: '班组', prop: 'team', minWidth: 70, align: 'center' }, { label: '物料名称', prop: 'materialName', minWidth: 110 }, { label: '起始车次', prop: 'startNo', minWidth: 90, align: 'right' }, { label: '终止车次', prop: 'endNo', minWidth: 90, align: 'right' }, { label: '实际车数', prop: 'cars', minWidth: 90, align: 'right' }, { label: '实际重量', prop: 'actualWeight', minWidth: 100, align: 'right', format: 'fixed3' }, { label: '出库人员', prop: 'dispatchUser', minWidth: 120 }, { label: '质检标志', prop: 'checkFlag', minWidth: 90, align: 'center' }], rows: withStartEnd(baseRows.slice(0, 52)) }] + }, + 'final-compound-flow-info': { + key: 'final-compound-flow-info', title: '终炼胶流转信息表', loadingMs: 820, toolbarActions: defaultToolbar, + filters: [{ key: 'flowCardNo', label: '流转卡号', type: 'input', defaultValue: '260101J83N03300091', targets: ['flowCardNo', 'barcode'] }], + tables: [{ key: 'production', title: '胶料生产信息', columns: [{ label: '条码号', prop: 'barcode', minWidth: 160 }, { label: '计划日期', prop: 'planDate', minWidth: 110 }, { label: '生产机台', prop: 'machine', minWidth: 90 }, { label: '班次', prop: 'shift', minWidth: 70, align: 'center' }, { label: '班组', prop: 'team', minWidth: 70, align: 'center' }, { label: '物料名称', prop: 'materialName', minWidth: 120 }, { label: '起止车次', prop: 'startEndNo', minWidth: 90, align: 'center' }, { label: '实际重量', prop: 'actualWeight', minWidth: 100, align: 'right', format: 'fixed3' }, { label: '生产时间', prop: 'operTime', minWidth: 160 }, { label: '出库标志', prop: 'outFlag', minWidth: 90, align: 'center' }, { label: '质检标志', prop: 'checkFlag', minWidth: 90, align: 'center' }], rows: baseRows.slice(0, 1).map(item => ({ ...item, outFlag: '已出库' })) }, { key: 'lockInfo', title: '卡片锁定解锁信息', columns: [{ label: '流转卡号', prop: 'flowCardNo', minWidth: 160 }, { label: '操作内容', prop: 'action', minWidth: 120 }, { label: '操作时间', prop: 'actionTime', minWidth: 160 }, { label: '操作原因', prop: 'reason', minWidth: 220 }], rows: [{ flowCardNo: '', action: '', actionTime: '', reason: '' }] }, { key: 'outInfo', title: '终炼胶出库信息', columns: [{ label: '流转卡号', prop: 'flowCardNo', minWidth: 170 }, { label: '操作时间', prop: 'operTime', minWidth: 160 }, { label: '出库日期', prop: 'outDate', minWidth: 110 }, { label: '生产机台', prop: 'machine', minWidth: 90 }, { label: '物料名称', prop: 'materialName', minWidth: 120 }, { label: '实际重量', prop: 'actualWeight', minWidth: 100, align: 'right', format: 'fixed3' }, { label: '实际车数', prop: 'cars', minWidth: 90, align: 'right' }, { label: '出库标志', prop: 'outFlag', minWidth: 90, align: 'center' }], rows: baseRows.slice(0, 1).map(item => ({ ...item, flowCardNo: item.scanCode, outDate: date(3), outFlag: '已出库' })) }, { key: 'handInfo', title: '手持及后工序扫描信息', columns: [{ label: '流转卡号', prop: 'flowCardNo', minWidth: 170 }, { label: '扫描结果', prop: 'scanResult', minWidth: 140 }, { label: '扫描时间', prop: 'scanTime', minWidth: 160 }, { label: '扫描机台', prop: 'scanMachine', minWidth: 100 }, { label: '扫描人', prop: 'scanUser', minWidth: 90 }, { label: '上传时间', prop: 'uploadTime', minWidth: 160 }], rows: baseRows.slice(0, 1).map(item => ({ flowCardNo: item.scanCode, scanResult: '领料成功', scanTime: datetime(6), scanMachine: item.machine, scanUser: item.dispatchUser, uploadTime: datetime(7) })) }] + }, + 'final-compound-unused-query': { + key: 'final-compound-unused-query', title: '终炼胶领用未使用查询', loadingMs: 720, toolbarActions: defaultToolbar, + filters: [{ key: 'useStart', label: '领用日期', type: 'datetime', defaultValue: '2026-01-01 10:00:00', targets: ['operTime'] }, { key: 'useEnd', label: '领用日期', type: 'datetime', defaultValue: '2026-01-05 10:00:00', targets: ['operTime'] }], + tables: [{ key: 'main', showIndex: true, columns: [{ label: '领用时间', prop: 'operTime', minWidth: 160 }, { label: '领用标志', prop: 'outScanFlag', minWidth: 100, align: 'center' }, { label: '领用人', prop: 'shelfText', minWidth: 110 }, { label: '部件使用标志', prop: 'semiUsedFlag', minWidth: 120, align: 'center' }, { label: '条码号', prop: 'barcode', minWidth: 170 }, { label: '计划日期', prop: 'planDate', minWidth: 110 }, { label: '生产机台', prop: 'machine', minWidth: 90 }, { label: '物料名称', prop: 'materialName', minWidth: 120 }, { label: '起止车次', prop: 'startEndNo', minWidth: 100, align: 'center' }, { label: '生产时间', prop: 'prodDateTime', minWidth: 160 }], rows: baseRows.slice(0, 36).map(item => ({ ...item, outScanFlag: '1已领用', semiUsedFlag: '未使用', prodDateTime: datetime(item.seq + 20) })) }] + }, + 'mixing-area-quota-weight-stat': { + key: 'mixing-area-quota-weight-stat', title: '炼胶工区日产量信息定额重量统计', loadingMs: 780, toolbarActions: defaultToolbar, + filters: [{ key: 'planDate', label: '计划日期', type: 'date', defaultValue: '2026-01-01', targets: ['date'] }], + tables: [{ key: 'summaryCars', title: '2026年1月1日炼胶工区产量信息定额重量统计表(单位:车)', columns: [{ label: '日期', prop: 'date', minWidth: 120 }, ...mlMachines.map(item => ({ label: `${item}车`, prop: `${item}Cars`, minWidth: 82, align: 'right' as const })), { label: '合计/车', prop: 'sumCars', minWidth: 100, align: 'right' }], rows: summaryRows }, { key: 'summaryWeight', title: '2026年1月1日炼胶工区产量信息定额重量统计表(单位:吨)', columns: [{ label: '日期', prop: 'date', minWidth: 120 }, ...mlMachines.map(item => ({ label: `${item}吨`, prop: `${item}Tons`, minWidth: 92, align: 'right' as const, format: 'fixed3' as const })), { label: '合计/吨', prop: 'sumTons', minWidth: 100, align: 'right', format: 'fixed3' }], rows: summaryRows }, { key: 'detail', columns: [{ label: '日期', prop: 'date', minWidth: 120 }, { label: '班组', prop: 'team', minWidth: 80, align: 'center' }, { label: '机台', prop: 'machine', minWidth: 90 }, { label: '胶料类型', prop: 'category', minWidth: 100 }, { label: '实际数量(车)', prop: 'cars', minWidth: 120, align: 'right' }, { label: '实际重量(吨)', prop: 'ton', minWidth: 120, align: 'right', format: 'fixed3' }], rows: baseRows.slice(0, 26).map(item => ({ date: summaryRows[0].date, team: item.team, machine: item.machine, category: item.category, cars: item.cars * 40, ton: n(item.actualWeight / 5, 3) })) }] + } +}; + +export const pageConfigMap: Record = configMap; +export const getPageConfig = (key: string): DemoPageConfig => + pageConfigMap[key] || { + key, + title: '未配置页面', + toolbarActions: defaultToolbar, + filters: [], + tables: [{ key: 'main', columns: [{ label: '提示', prop: 'message', minWidth: 320 }], rows: [{ message: `未找到页面配置:${key}` }] }] + }; diff --git a/src/views/mes/MLShow/data/source.ts b/src/views/mes/MLShow/data/source.ts new file mode 100644 index 0000000..520edc4 --- /dev/null +++ b/src/views/mes/MLShow/data/source.ts @@ -0,0 +1,250 @@ +import forwardData from '../../mixTrace/show/data/forward.json'; +import backward1Data from '../../mixTrace/show/data/backward1.json'; +import backward2Data from '../../mixTrace/show/data/backward2.json'; +import tire1Data from '../../mixTrace/show/data/tire1.json'; +import tire2Data from '../../mixTrace/show/data/tire2.json'; + +type AnyRecord = Record; +type TraceSeed = { + machine: string; + materialName: string; + setWeight: number; + actualWeight: number; + prodDate: string; + planDate: string; + operTime: string; + barcode: string; + scanCode: string; + shift: string; + team: string; + startEndNo: string; +}; + +const forward = forwardData as AnyRecord; +const backward1 = backward1Data as AnyRecord; +const backward2 = backward2Data as AnyRecord; +const tire1 = tire1Data as AnyRecord; +const tire2 = tire2Data as AnyRecord; + +const toList = (value: unknown): AnyRecord[] => (Array.isArray(value) ? (value as AnyRecord[]) : []); +const toText = (value: unknown): string => (value == null ? '' : String(value).trim()); +const toNumber = (value: unknown, fallback = 0): number => { + const number = Number(value); + return Number.isFinite(number) ? number : fallback; +}; +const safe = (list: T[], fallback: T[]): T[] => (list.length ? list : fallback); +const uniq = (list: string[]) => Array.from(new Set(list.filter(Boolean))); + +export const mlMachinePool = ['ML1', 'ML2', 'ML3', 'ML4', 'ML5', 'ML6', 'ML7', 'ML8']; +const machineAliasMap = new Map(); + +const normalizeShift = (value: unknown, index: number): string => { + const text = toText(value); + if (text.includes('早')) return '早'; + if (text.includes('中')) return '中'; + if (text.includes('夜')) return '夜'; + return ['早', '中', '夜'][index % 3]; +}; + +const normalizeTeam = (value: unknown, index: number): string => { + const text = toText(value); + if (text.includes('甲')) return '甲'; + if (text.includes('乙')) return '乙'; + if (text.includes('丙')) return '丙'; + return ['甲', '乙', '丙'][index % 3]; +}; + +const normalizeMachine = (value: unknown, index: number): string => { + const raw = toText(value).toUpperCase(); + if (!raw) return mlMachinePool[index % mlMachinePool.length]; + + const mlMatch = raw.match(/ML\s*(\d+)/); + if (mlMatch?.[1]) { + const machineNo = toNumber(mlMatch[1], 1); + return `ML${((machineNo - 1) % mlMachinePool.length) + 1}`; + } + + const digitMatch = raw.match(/(\d+)(?!.*\d)/); + if (digitMatch?.[1]) { + const machineNo = toNumber(digitMatch[1], 1); + if (machineNo > 0) { + return `ML${((machineNo - 1) % mlMachinePool.length) + 1}`; + } + } + + if (!machineAliasMap.has(raw)) { + machineAliasMap.set(raw, mlMachinePool[machineAliasMap.size % mlMachinePool.length]); + } + return machineAliasMap.get(raw) as string; +}; + +const dateFromText = (value: string, fallback: string): string => { + const match = value.match(/\d{4}-\d{2}-\d{2}/); + return match ? match[0] : fallback; +}; + +const datetimeFromText = (value: string, fallbackDate: string, index: number): string => { + if (/\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}/.test(value)) return value; + const hour = String((index * 3) % 24).padStart(2, '0'); + const minute = String((index * 7) % 60).padStart(2, '0'); + const second = String((index * 11) % 60).padStart(2, '0'); + return `${fallbackDate} ${hour}:${minute}:${second}`; +}; + +const toStartEndNo = (trainNoValue: string, index: number): string => { + const trainNo = trainNoValue.replace(/\s/g, ''); + if (trainNo.includes('-')) return trainNo; + if (trainNo) return `${trainNo}-${trainNo}`; + return `${(index % 120) + 1}-${(index % 120) + 2}`; +}; + +const createSeed = (production: AnyRecord, weighing: AnyRecord | null, index: number): TraceSeed => { + const dateFallback = `2026-01-${String(1 + (index % 28)).padStart(2, '0')}`; + const prodDate = dateFromText(toText(weighing?.planDate || production.productionDate), dateFallback); + const startEndNo = toStartEndNo(toText(weighing?.trainNo || production.trainNo), index); + const setWeight = toNumber(weighing?.setWeight ?? production.setWeight, 420 + index * 6); + const actualWeight = toNumber(weighing?.actualWeight ?? production.actualWeight ?? production.weight, setWeight - 1.2); + + return { + machine: normalizeMachine(weighing?.machine || production.machine, index), + materialName: toText(weighing?.materialCode || weighing?.materialName || production.materialName) || `胶料${index + 1}`, + setWeight, + actualWeight, + prodDate, + planDate: prodDate, + operTime: datetimeFromText(toText(weighing?.weighTime || production.productionDate), prodDate, index), + barcode: toText(weighing?.carBarcode || production.barcode), + scanCode: toText(production.barcode || weighing?.carBarcode), + shift: normalizeShift(production.shift, index), + team: normalizeTeam(production.team, index), + startEndNo + }; +}; + +const detailList = Object.values(forward.detailMap || {}) as AnyRecord[]; +const backwardDetailList = Object.values(backward2.detailMap || {}) as AnyRecord[]; + +const productionPool = [ + ...detailList.map(item => item.productionInfo).filter(Boolean), + ...backwardDetailList.map(item => item.production).filter(Boolean), + ...(tire2.productionInfo ? [tire2.productionInfo] : []) +] as AnyRecord[]; +const weighingPool = [...detailList, ...backwardDetailList].flatMap(item => toList(item.weighingData)); +const curingPool = toList(backward1.curingData); +const shelfPool = toList(backward1.shelfData); +const semiPool = toList(backward1.semiCardData); +const tireInfoMaterialPool = toList(tire1.tireInfoList) + .filter(item => toText(item.label).includes('物料')) + .map(item => toText(item.value)); + +const seedRows = safe( + [ + ...detailList.flatMap((detail, detailIndex) => { + const production = (detail.productionInfo || {}) as AnyRecord; + const weighingData = toList(detail.weighingData); + if (weighingData.length) { + return weighingData.map((weighing, weighingIndex) => createSeed(production, weighing, detailIndex * 10 + weighingIndex)); + } + return [createSeed(production, null, detailIndex)]; + }), + ...backwardDetailList.flatMap((detail, detailIndex) => { + const production = (detail.production || {}) as AnyRecord; + const weighingData = toList(detail.weighingData); + if (weighingData.length) { + return weighingData.map((weighing, weighingIndex) => createSeed(production, weighing, 200 + detailIndex * 10 + weighingIndex)); + } + return [createSeed(production, null, 200 + detailIndex)]; + }), + ...(tire2.productionInfo ? [createSeed(tire2.productionInfo as AnyRecord, null, 400)] : []) + ], + [createSeed({}, null, 0)] +); + +export const materialPool = safe( + uniq([ + ...seedRows.map(item => item.materialName), + ...productionPool.map(item => toText(item.materialName)), + ...weighingPool.map(item => toText(item.materialCode || item.materialName)), + ...curingPool.map(item => toText(item.materialSpec)), + ...tireInfoMaterialPool + ]), + ['KB110-F', 'ER408', 'TZ807-F', 'BF416', 'BN701-F'] +); + +export const machinePool = safe( + uniq([ + ...seedRows.map(item => item.machine), + ...shelfPool.map(item => normalizeMachine(item.machineNo, 0)), + ...semiPool.map(item => normalizeMachine(item.machineNo, 0)) + ]), + mlMachinePool +); + +export const shiftPool = safe(uniq(seedRows.map(item => item.shift)), ['早', '中', '夜']); +export const teamPool = safe(uniq(seedRows.map(item => item.team)), ['甲', '乙', '丙']); +export const categoryPool = ['塑炼胶', '母炼胶', '终炼胶']; +export const dispatcherPool = ['压出4手持', '压出3手持', '内衬层手持', '钢丝手持']; + +export const pick = (list: T[], index: number): T => list[index % list.length]; +export const n = (value: number, digit = 1): number => Number(value.toFixed(digit)); +export const date = (offset = 0): string => `2026-01-${String(1 + (offset % 28)).padStart(2, '0')}`; +export const datetime = (offset = 0): string => { + const d = date(offset); + const h = String((offset * 3) % 24).padStart(2, '0'); + const m = String((offset * 7) % 60).padStart(2, '0'); + const s = String((offset * 11) % 60).padStart(2, '0'); + return `${d} ${h}:${m}:${s}`; +}; + +export const baseRows = Array.from({ length: 96 }).map((_, index) => { + const seed = seedRows[index % seedRows.length]; + const materialName = seed.materialName || pick(materialPool, index); + const setWeight = Number(seed.setWeight || pick(weighingPool, index)?.setWeight || 420 + index * 6); + const actualWeight = Number(seed.actualWeight || pick(weighingPool, index)?.actualWeight || setWeight - 1.2); + const machine = seed.machine || pick(machinePool, index); + const cars = (index % 6) + 1; + const prodDate = seed.prodDate || date(index); + + return { + seq: index + 1, + orderNo: `${prodDate.replaceAll('-', '')}${String(1000 + index).slice(-4)}`, + businessDate: prodDate, + prodDate, + scanDate: datetime(index), + machine, + workshop: machine.startsWith('ML') ? '鐐艰兌' : '鎴愬瀷', + shift: seed.shift || pick(shiftPool, index), + team: seed.team || pick(teamPool, index), + materialName, + formulaName: `${materialName}-${String((index % 9) + 1).padStart(2, '0')}`, + inputMaterial: pick(materialPool, index + 3), + category: pick(categoryPool, index), + setWeight: n(setWeight, 3), + actualWeight: n(actualWeight, 3), + planCars: cars + (index % 2), + cars, + consumeCars: Math.max(cars - 1, 0), + consumeWeight: n(actualWeight * Math.max(cars - 1, 1), 1), + produceWeight: n(actualWeight * cars, 1), + stockQty: Math.max(0, 580 - index * 3), + stockWeight: n(Math.max(0, 108000 - index * 1025), 1), + scanCode: seed.scanCode || `${String(251200 + (index % 700)).padStart(6, '0')}J${String(83 + (index % 9)).padStart(2, '0')}N${String(33000000 + index).padStart(8, '0')}`, + barcode: seed.barcode || `${String(260100 + (index % 30)).padStart(6, '0')}N${String(73000000 + index).padStart(8, '0')}`, + startEndNo: seed.startEndNo || `${(index % 120) + 1}-${(index % 120) + 2}`, + operTime: seed.operTime || datetime(index), + planDate: seed.planDate || date(index > 2 ? index - 2 : 0), + overdueHours: Math.max(1, 48 - (index % 47)), + scanUser: `操作员${(index % 9) + 1}`, + operator: `记录员${(index % 6) + 1}`, + dispatchUser: pick(dispatcherPool, index), + checkFlag: index % 11 === 0 ? '寰呭妫€' : '鍚堟牸', + modifyFlag: index % 9 === 0 ? 1 : 0, + handFlag: index % 8 === 0 ? 1 : 0, + shelfText: pick(dispatcherPool, index), + prodType: pick(categoryPool, index), + status: index % 12 === 0 ? '未结存' : '正常', + quarterType: index % 2 === 0 ? 'A' : 'B', + mismatch: n((actualWeight - setWeight) * cars, 3) + }; +}); + diff --git a/src/views/mes/MLShow/index.vue b/src/views/mes/MLShow/index.vue new file mode 100644 index 0000000..1925fe6 --- /dev/null +++ b/src/views/mes/MLShow/index.vue @@ -0,0 +1,7 @@ + + + diff --git a/src/views/mes/MLShow/photo/01车间生产统计.PNG b/src/views/mes/MLShow/photo/01车间生产统计.PNG new file mode 100644 index 0000000..e7c62f8 Binary files /dev/null and b/src/views/mes/MLShow/photo/01车间生产统计.PNG differ diff --git a/src/views/mes/MLShow/photo/02车间胶料统计.PNG b/src/views/mes/MLShow/photo/02车间胶料统计.PNG new file mode 100644 index 0000000..dfd9d5f Binary files /dev/null and b/src/views/mes/MLShow/photo/02车间胶料统计.PNG differ diff --git a/src/views/mes/MLShow/photo/03小料消耗.PNG b/src/views/mes/MLShow/photo/03小料消耗.PNG new file mode 100644 index 0000000..e2c4f76 Binary files /dev/null and b/src/views/mes/MLShow/photo/03小料消耗.PNG differ diff --git a/src/views/mes/MLShow/photo/04小料明细统计.PNG b/src/views/mes/MLShow/photo/04小料明细统计.PNG new file mode 100644 index 0000000..3eed256 Binary files /dev/null and b/src/views/mes/MLShow/photo/04小料明细统计.PNG differ diff --git a/src/views/mes/MLShow/photo/05小料盘点.PNG b/src/views/mes/MLShow/photo/05小料盘点.PNG new file mode 100644 index 0000000..7ccb29d Binary files /dev/null and b/src/views/mes/MLShow/photo/05小料盘点.PNG differ diff --git a/src/views/mes/MLShow/photo/06车间胶料消耗.PNG b/src/views/mes/MLShow/photo/06车间胶料消耗.PNG new file mode 100644 index 0000000..59db348 Binary files /dev/null and b/src/views/mes/MLShow/photo/06车间胶料消耗.PNG differ diff --git a/src/views/mes/MLShow/photo/07.PNG b/src/views/mes/MLShow/photo/07.PNG new file mode 100644 index 0000000..684289e Binary files /dev/null and b/src/views/mes/MLShow/photo/07.PNG differ diff --git a/src/views/mes/MLShow/photo/08.PNG b/src/views/mes/MLShow/photo/08.PNG new file mode 100644 index 0000000..8dffe64 Binary files /dev/null and b/src/views/mes/MLShow/photo/08.PNG differ diff --git a/src/views/mes/MLShow/photo/09.PNG b/src/views/mes/MLShow/photo/09.PNG new file mode 100644 index 0000000..ec6b526 Binary files /dev/null and b/src/views/mes/MLShow/photo/09.PNG differ diff --git a/src/views/mes/MLShow/photo/10.PNG b/src/views/mes/MLShow/photo/10.PNG new file mode 100644 index 0000000..d531eb7 Binary files /dev/null and b/src/views/mes/MLShow/photo/10.PNG differ diff --git a/src/views/mes/MLShow/photo/11.PNG b/src/views/mes/MLShow/photo/11.PNG new file mode 100644 index 0000000..f8fff7b Binary files /dev/null and b/src/views/mes/MLShow/photo/11.PNG differ diff --git a/src/views/mes/MLShow/photo/12.PNG b/src/views/mes/MLShow/photo/12.PNG new file mode 100644 index 0000000..86197ac Binary files /dev/null and b/src/views/mes/MLShow/photo/12.PNG differ diff --git a/src/views/mes/MLShow/photo/13.PNG b/src/views/mes/MLShow/photo/13.PNG new file mode 100644 index 0000000..6611919 Binary files /dev/null and b/src/views/mes/MLShow/photo/13.PNG differ diff --git a/src/views/mes/MLShow/photo/14.PNG b/src/views/mes/MLShow/photo/14.PNG new file mode 100644 index 0000000..382e9a1 Binary files /dev/null and b/src/views/mes/MLShow/photo/14.PNG differ diff --git a/src/views/mes/MLShow/photo/15.PNG b/src/views/mes/MLShow/photo/15.PNG new file mode 100644 index 0000000..2de7774 Binary files /dev/null and b/src/views/mes/MLShow/photo/15.PNG differ diff --git a/src/views/mes/MLShow/photo/16.PNG b/src/views/mes/MLShow/photo/16.PNG new file mode 100644 index 0000000..2def09a Binary files /dev/null and b/src/views/mes/MLShow/photo/16.PNG differ diff --git a/src/views/mes/MLShow/photo/17.PNG b/src/views/mes/MLShow/photo/17.PNG new file mode 100644 index 0000000..2f0976a Binary files /dev/null and b/src/views/mes/MLShow/photo/17.PNG differ diff --git a/src/views/mes/MLShow/photo/18.PNG b/src/views/mes/MLShow/photo/18.PNG new file mode 100644 index 0000000..e8c1886 Binary files /dev/null and b/src/views/mes/MLShow/photo/18.PNG differ diff --git a/src/views/mes/MLShow/photo/19.PNG b/src/views/mes/MLShow/photo/19.PNG new file mode 100644 index 0000000..1c29649 Binary files /dev/null and b/src/views/mes/MLShow/photo/19.PNG differ diff --git a/src/views/mes/MLShow/photo/20.PNG b/src/views/mes/MLShow/photo/20.PNG new file mode 100644 index 0000000..4047fba Binary files /dev/null and b/src/views/mes/MLShow/photo/20.PNG differ diff --git a/src/views/mes/MLShow/photo/21.PNG b/src/views/mes/MLShow/photo/21.PNG new file mode 100644 index 0000000..e4cbdb0 Binary files /dev/null and b/src/views/mes/MLShow/photo/21.PNG differ diff --git a/src/views/mes/MLShow/photo/22.PNG b/src/views/mes/MLShow/photo/22.PNG new file mode 100644 index 0000000..fb38cdb Binary files /dev/null and b/src/views/mes/MLShow/photo/22.PNG differ diff --git a/src/views/mes/MLShow/types.ts b/src/views/mes/MLShow/types.ts new file mode 100644 index 0000000..6ffa44c --- /dev/null +++ b/src/views/mes/MLShow/types.ts @@ -0,0 +1,64 @@ +export type FilterType = + | 'input' + | 'number' + | 'select' + | 'date' + | 'datetime' + | 'daterange' + | 'checkbox' + | 'radio' + | 'switch'; + +export interface FilterOption { + label: string; + value: string | number | boolean; +} + +export interface PageFilter { + key: string; + label: string; + type: FilterType; + placeholder?: string; + width?: number; + targets?: string[]; + options?: FilterOption[]; + defaultValue?: any; +} + +export type ColumnFormat = 'fixed0' | 'fixed1' | 'fixed2' | 'fixed3' | 'percent'; + +export interface DemoColumn { + label: string; + prop?: string; + minWidth?: number; + width?: number; + align?: 'left' | 'center' | 'right'; + format?: ColumnFormat; + children?: DemoColumn[]; +} + +export interface DemoTableSection { + key: string; + title?: string; + note?: string; + columns: DemoColumn[]; + rows: Record[]; + maxHeight?: number; + showIndex?: boolean; +} + +export interface ToolbarAction { + label: string; + type?: '' | 'primary' | 'success' | 'warning' | 'danger' | 'info'; +} + +export interface DemoPageConfig { + key: string; + title: string; + statusText?: string; + tips?: string[]; + loadingMs?: number; + toolbarActions: ToolbarAction[]; + filters: PageFilter[]; + tables: DemoTableSection[]; +}