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 @@
+
+
+
+
+ {{ btn.label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ opt.label }}
+
+
+ {{ opt.label }}
+
+
+
+
+ 查询
+ 重置
+
+
+
+
{{ config.statusText }}
+
+
+
+
+
+ {{ table.title }}
+
+
+
+
+
+ {{ formatCell(row, child) }}
+
+
+
+ {{ formatCell(row, col) }}
+
+
+
+ {{ table.note }}
+
+
+
+
+
+
+
+
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[];
+}