feat(mes): 添加胶料管理系统演示页面

- 创建 LegacyDemoPage 通用渲染组件用于演示页面
- 新增 14 个胶料管理相关演示页面包括车间生产统计、胶料盘点、库存查询等功能
- 配置页面数据源和过滤器,支持日期范围、物料、机台等多维度筛选
- 实现表格数据展示、状态提示和工具栏操作功能
- 添加页面说明文档描述各页面功能和业务逻辑
master
zangch@mesnac.com 4 days ago
parent b500da3a5b
commit 4abcbd17ea

@ -0,0 +1,8 @@
<template>
<LegacyDemoPage page-key="compound-check-balance" />
</template>
<script setup lang="ts" name="MLShowCompoundCheckBalance">
import LegacyDemoPage from './components/LegacyDemoPage.vue';
</script>

@ -0,0 +1,8 @@
<template>
<LegacyDemoPage page-key="compound-check-info-query" />
</template>
<script setup lang="ts" name="MLShowCompoundCheckInfoQuery">
import LegacyDemoPage from './components/LegacyDemoPage.vue';
</script>

@ -0,0 +1,8 @@
<template>
<LegacyDemoPage page-key="compound-check-order-maintain" />
</template>
<script setup lang="ts" name="MLShowCompoundCheckOrderMaintain">
import LegacyDemoPage from './components/LegacyDemoPage.vue';
</script>

@ -0,0 +1,8 @@
<template>
<LegacyDemoPage page-key="compound-dynamic-stock-check" />
</template>
<script setup lang="ts" name="MLShowCompoundDynamicStockCheck">
import LegacyDemoPage from './components/LegacyDemoPage.vue';
</script>

@ -0,0 +1,8 @@
<template>
<LegacyDemoPage page-key="compound-production-stat" />
</template>
<script setup lang="ts" name="MLShowCompoundProductionStat">
import LegacyDemoPage from './components/LegacyDemoPage.vue';
</script>

@ -0,0 +1,8 @@
<template>
<LegacyDemoPage page-key="compound-realtime-stock" />
</template>
<script setup lang="ts" name="MLShowCompoundRealtimeStock">
import LegacyDemoPage from './components/LegacyDemoPage.vue';
</script>

@ -0,0 +1,8 @@
<template>
<LegacyDemoPage page-key="final-compound-flow-info" />
</template>
<script setup lang="ts" name="MLShowFinalCompoundFlowInfo">
import LegacyDemoPage from './components/LegacyDemoPage.vue';
</script>

@ -0,0 +1,8 @@
<template>
<LegacyDemoPage page-key="final-compound-outbound-detail" />
</template>
<script setup lang="ts" name="MLShowFinalCompoundOutboundDetail">
import LegacyDemoPage from './components/LegacyDemoPage.vue';
</script>

@ -0,0 +1,8 @@
<template>
<LegacyDemoPage page-key="final-compound-unused-query" />
</template>
<script setup lang="ts" name="MLShowFinalCompoundUnusedQuery">
import LegacyDemoPage from './components/LegacyDemoPage.vue';
</script>

@ -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 吨数、合计值、明细机台记录。
- 逻辑:结构与日产量统计页一致,强调定额重量与实际重量对比。

@ -0,0 +1,8 @@
<template>
<LegacyDemoPage page-key="material-overdue-warning" />
</template>
<script setup lang="ts" name="MLShowMaterialOverdueWarning">
import LegacyDemoPage from './components/LegacyDemoPage.vue';
</script>

@ -0,0 +1,8 @@
<template>
<LegacyDemoPage page-key="mixer-hourly-output" />
</template>
<script setup lang="ts" name="MLShowMixerHourlyOutput">
import LegacyDemoPage from './components/LegacyDemoPage.vue';
</script>

@ -0,0 +1,8 @@
<template>
<LegacyDemoPage page-key="mixing-area-daily-output" />
</template>
<script setup lang="ts" name="MLShowMixingAreaDailyOutput">
import LegacyDemoPage from './components/LegacyDemoPage.vue';
</script>

@ -0,0 +1,8 @@
<template>
<LegacyDemoPage page-key="mixing-area-quota-weight-stat" />
</template>
<script setup lang="ts" name="MLShowMixingAreaQuotaWeightStat">
import LegacyDemoPage from './components/LegacyDemoPage.vue';
</script>

@ -0,0 +1,8 @@
<template>
<LegacyDemoPage page-key="small-material-check" />
</template>
<script setup lang="ts" name="MLShowSmallMaterialCheck">
import LegacyDemoPage from './components/LegacyDemoPage.vue';
</script>

@ -0,0 +1,8 @@
<template>
<LegacyDemoPage page-key="small-material-consume" />
</template>
<script setup lang="ts" name="MLShowSmallMaterialConsume">
import LegacyDemoPage from './components/LegacyDemoPage.vue';
</script>

@ -0,0 +1,8 @@
<template>
<LegacyDemoPage page-key="small-material-detail-stat" />
</template>
<script setup lang="ts" name="MLShowSmallMaterialDetailStat">
import LegacyDemoPage from './components/LegacyDemoPage.vue';
</script>

@ -0,0 +1,8 @@
<template>
<LegacyDemoPage page-key="workshop-compound-check" />
</template>
<script setup lang="ts" name="MLShowWorkshopCompoundCheck">
import LegacyDemoPage from './components/LegacyDemoPage.vue';
</script>

@ -0,0 +1,8 @@
<template>
<LegacyDemoPage page-key="workshop-compound-consume" />
</template>
<script setup lang="ts" name="MLShowWorkshopCompoundConsume">
import LegacyDemoPage from './components/LegacyDemoPage.vue';
</script>

@ -0,0 +1,8 @@
<template>
<LegacyDemoPage page-key="workshop-compound-detail-stat" />
</template>
<script setup lang="ts" name="MLShowWorkshopCompoundDetailStat">
import LegacyDemoPage from './components/LegacyDemoPage.vue';
</script>

@ -0,0 +1,8 @@
<template>
<LegacyDemoPage page-key="workshop-compound-stat" />
</template>
<script setup lang="ts" name="MLShowWorkshopCompoundStat">
import LegacyDemoPage from './components/LegacyDemoPage.vue';
</script>

@ -0,0 +1,8 @@
<template>
<LegacyDemoPage page-key="workshop-production-stat" />
</template>
<script setup lang="ts" name="MLShowWorkshopProductionStat">
import LegacyDemoPage from './components/LegacyDemoPage.vue';
</script>

@ -0,0 +1,8 @@
<template>
<LegacyDemoPage page-key="workshop-stock-adjust" />
</template>
<script setup lang="ts" name="MLShowWorkshopStockAdjust">
import LegacyDemoPage from './components/LegacyDemoPage.vue';
</script>

@ -0,0 +1,314 @@
<template>
<div class="legacy-page">
<div v-if="config.toolbarActions.length" class="legacy-toolbar">
<el-button
v-for="btn in config.toolbarActions"
:key="btn.label"
:type="btn.type || 'default'"
size="small"
class="toolbar-btn"
@click="handleMockAction(btn.label)"
>
{{ btn.label }}
</el-button>
</div>
<el-form v-if="config.filters.length" :model="queryModel" inline class="query-form" label-width="96px">
<el-form-item v-for="item in config.filters" :key="item.key" :label="item.label">
<el-input
v-if="item.type === 'input'"
v-model="queryModel[item.key]"
clearable
:placeholder="item.placeholder || `请输入${item.label}`"
:style="{ width: `${item.width || 180}px` }"
@keyup.enter="handleQuery"
/>
<el-input-number
v-else-if="item.type === 'number'"
v-model="queryModel[item.key]"
controls-position="right"
:style="{ width: `${item.width || 180}px` }"
/>
<el-select
v-else-if="item.type === 'select'"
v-model="queryModel[item.key]"
clearable
filterable
:placeholder="item.placeholder || `请选择${item.label}`"
:style="{ width: `${item.width || 180}px` }"
>
<el-option v-for="opt in item.options || []" :key="String(opt.value)" :label="opt.label" :value="opt.value" />
</el-select>
<el-date-picker
v-else-if="item.type === 'date'"
v-model="queryModel[item.key]"
type="date"
value-format="YYYY-MM-DD"
:placeholder="item.placeholder || '请选择日期'"
:style="{ width: `${item.width || 180}px` }"
/>
<el-date-picker
v-else-if="item.type === 'datetime'"
v-model="queryModel[item.key]"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
:placeholder="item.placeholder || '请选择时间'"
:style="{ width: `${item.width || 220}px` }"
/>
<el-radio-group v-else-if="item.type === 'radio'" v-model="queryModel[item.key]">
<el-radio v-for="opt in item.options || []" :key="String(opt.value)" :label="opt.value">{{ opt.label }}</el-radio>
</el-radio-group>
<el-checkbox-group v-else-if="item.type === 'checkbox'" v-model="queryModel[item.key]">
<el-checkbox v-for="opt in item.options || []" :key="String(opt.value)" :label="opt.value">{{ opt.label }}</el-checkbox>
</el-checkbox-group>
<el-switch v-else-if="item.type === 'switch'" v-model="queryModel[item.key]" inline-prompt active-text="" inactive-text="" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleQuery"></el-button>
<el-button @click="handleReset"></el-button>
</el-form-item>
</el-form>
<div v-if="config.statusText" class="status-text">{{ config.statusText }}</div>
<el-skeleton v-if="loading" :rows="8" animated class="skeleton-wrap" />
<div v-else class="table-wrap">
<section v-for="table in config.tables" :key="table.key" class="table-section">
<div v-if="table.title" class="section-title">{{ table.title }}</div>
<el-table
:data="tableDataMap[table.key] || []"
border
stripe
:max-height="table.maxHeight || 520"
:row-class-name="getRowClassName"
class="legacy-table"
>
<el-table-column v-if="table.showIndex" type="index" label="序号" width="56" align="center" />
<template v-for="col in table.columns" :key="`${table.key}-${col.label}-${col.prop || 'group'}`">
<el-table-column
v-if="col.children"
:label="col.label"
:min-width="col.minWidth"
:width="col.width"
:align="col.align || 'center'"
>
<el-table-column
v-for="child in col.children"
:key="`${table.key}-${col.label}-${child.label}-${child.prop}`"
:prop="child.prop"
:label="child.label"
:min-width="child.minWidth"
:width="child.width"
:align="child.align || 'center'"
>
<template #default="{ row }">{{ formatCell(row, child) }}</template>
</el-table-column>
</el-table-column>
<el-table-column
v-else
:prop="col.prop"
:label="col.label"
:min-width="col.minWidth"
:width="col.width"
:align="col.align || 'center'"
>
<template #default="{ row }">{{ formatCell(row, col) }}</template>
</el-table-column>
</template>
</el-table>
<div v-if="table.note" class="table-note">{{ table.note }}</div>
</section>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, onMounted, ref } from 'vue';
import { ElMessage } from 'element-plus';
import { getPageConfig } from '../data/pageConfigs';
import type { DemoColumn } from '../types';
const props = defineProps<{ pageKey: string }>();
const config = computed(() => getPageConfig(props.pageKey));
const loading = ref(true);
const queryModel = ref<Record<string, any>>({});
const tableDataMap = ref<Record<string, Record<string, any>[]>>({});
const initQuery = () => {
const model: Record<string, any> = {};
config.value.filters.forEach(item => {
if (item.defaultValue !== undefined) {
model[item.key] = Array.isArray(item.defaultValue) ? [...item.defaultValue] : item.defaultValue;
return;
}
if (item.type === 'checkbox') model[item.key] = [];
else if (item.type === 'switch') model[item.key] = false;
else model[item.key] = '';
});
queryModel.value = model;
};
const normalize = (value: any) => String(value ?? '').toLowerCase();
const isEmpty = (value: any) => value === undefined || value === null || value === '' || (Array.isArray(value) && value.length === 0);
const matchRow = (row: Record<string, any>) =>
config.value.filters.every(filter => {
const value = queryModel.value[filter.key];
if (isEmpty(value)) return true;
if (filter.type === 'switch') return value ? normalize(row.outFlag) !== '已出库' : true;
const targets = filter.targets?.length ? filter.targets : [filter.key];
const values = targets.map(target => row[target]);
if ((filter.type === 'checkbox' || filter.type === 'radio') && Array.isArray(value)) return values.some(item => value.includes(item));
if ((filter.key.toLowerCase().includes('start') || filter.key.toLowerCase().includes('begin')) && (filter.type === 'date' || filter.type === 'datetime')) {
return values.some(item => normalize(item) >= normalize(value));
}
if ((filter.key.toLowerCase().includes('end') || filter.key.toLowerCase().includes('finish')) && (filter.type === 'date' || filter.type === 'datetime')) {
return values.some(item => normalize(item) <= normalize(value));
}
return values.some(item => normalize(item).includes(normalize(value)));
});
const loadData = (slow = true) => {
loading.value = true;
const timer = slow ? config.value.loadingMs || 650 : 120;
setTimeout(() => {
const map: Record<string, Record<string, any>[]> = {};
config.value.tables.forEach(table => {
map[table.key] = table.rows.filter(row => matchRow(row));
});
tableDataMap.value = map;
loading.value = false;
}, timer);
};
const handleQuery = () => loadData(false);
const handleReset = () => {
initQuery();
loadData(false);
};
const handleMockAction = (name: string) => {
if (name.includes('导出')) ElMessage.success(`已模拟执行:${name}`);
else if (name.includes('打印')) ElMessage.success('已模拟打印预览');
else ElMessage.info(`已触发:${name}`);
};
const formatCell = (row: Record<string, any>, column: DemoColumn) => {
const val = column.prop ? row[column.prop] : '';
if (val === undefined || val === null || val === '') return '';
if (!column.format) return val;
const num = Number(val);
if (Number.isNaN(num)) return val;
if (column.format === 'fixed0') return num.toFixed(0);
if (column.format === 'fixed1') return num.toFixed(1);
if (column.format === 'fixed2') return num.toFixed(2);
if (column.format === 'fixed3') return num.toFixed(3);
if (column.format === 'percent') return `${(num * 100).toFixed(2)}%`;
return val;
};
const getRowClassName = ({ row }: { row: Record<string, any> }) => {
if (row._rowType === 'summary' || String(row.date || '').includes('平均')) return 'summary-row';
return '';
};
onMounted(() => {
initQuery();
loadData(true);
});
</script>
<style scoped lang="scss">
.legacy-page {
padding: 12px;
background: #eef2f8;
min-height: calc(100vh - 90px);
}
.legacy-toolbar {
background: #f2f5fb;
border: 1px solid #c6d4ea;
padding: 8px;
margin-bottom: 8px;
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.toolbar-btn {
border-radius: 2px;
}
.query-form {
background: #fff;
border: 1px solid #c6d4ea;
padding: 12px 12px 2px;
}
.status-text {
color: #f5222d;
font-weight: 700;
margin-top: 8px;
margin-bottom: 8px;
}
.skeleton-wrap {
padding: 14px;
margin-top: 8px;
background: #fff;
border: 1px solid #c6d4ea;
}
.table-wrap {
margin-top: 8px;
display: grid;
gap: 10px;
}
.table-section {
border: 1px solid #b9cdeb;
background: #fff;
padding: 6px;
}
.section-title {
padding: 8px 10px;
margin-bottom: 8px;
background: #0dae55;
color: #111;
font-size: 18px;
line-height: 1.2;
font-weight: 700;
}
.table-note {
color: #6b778c;
margin-top: 6px;
font-size: 12px;
}
.legacy-table {
:deep(.el-table__header th) {
background: linear-gradient(180deg, #d4e5f7, #bdd8f3);
color: #1f2d3d;
font-weight: 700;
border-bottom-color: #9ec1e8;
}
:deep(.el-table td),
:deep(.el-table th) {
border-color: #a8c8ee;
}
:deep(.el-table .summary-row td) {
font-weight: 700;
color: #1f2d3d;
background: #e7f1ff;
}
}
</style>

@ -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 = <T extends Record<string, any>>(rows: T[]): Array<T & { startNo: string; endNo: string }> =>
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<string, any> = { 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<string, any> = { 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<string, DemoPageConfig> = {
'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<string, DemoPageConfig> = 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}` }] }]
};

@ -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<string, any>;
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 = <T>(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<string, string>();
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 = <T>(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)
};
});

@ -0,0 +1,7 @@
<template>
<LegacyDemoPage page-key="workshop-production-stat" />
</template>
<script setup lang="ts" name="MLShowIndex">
import LegacyDemoPage from './components/LegacyDemoPage.vue';
</script>

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

@ -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<string, any>[];
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[];
}
Loading…
Cancel
Save