Files
qhmes/jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/MesXslMixingSpec.data.ts

1409 lines
62 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { BasicColumn, FormSchema } from '/@/components/Table';
import { JVxeColumn, JVxeTypes } from '/@/components/jeecg/JVxeTable/types';
import { createLocalStorage } from '/@/utils/cache';
import { buildUUID } from '/@/utils/uuid';
const mixingMaterialColumnStorage = createLocalStorage();
/** 橡胶及配合剂明细列隐藏偏好 localStorage 键 */
export const MIXING_MATERIAL_COLUMN_CACHE_KEY = 'mes_xsl_mixing_spec_material_hidden_columns';
/** 橡胶及配合剂明细列宽偏好 localStorage 键 */
export const MIXING_MATERIAL_COLUMN_WIDTH_CACHE_KEY = 'mes_xsl_mixing_spec_material_column_widths_v2';
/** 行号列宽度(与 JVxeTable rowNumber 默认一致) */
export const MIXING_MATERIAL_ROW_NUMBER_WIDTH = 60;
/** 橡胶及配合剂明细列可缩小到的最小宽度 */
export const MIXING_MATERIAL_MIN_COLUMN_WIDTH = 40;
//update-begin---author:cursor ---date:20260601 for【XSLMES-20260601-A62】橡胶及配合剂明细默认展示种类列-----------
/** 默认隐藏的明细列(种类列默认展示,物料小类默认隐藏) */
export const MIXING_MATERIAL_DEFAULT_HIDDEN_COLUMN_KEYS = ['materialMajor', 'materialMinor'];
//update-end---author:cursor ---date:20260601 for【XSLMES-20260601-A62】橡胶及配合剂明细默认展示种类列-----------
/** 不允许隐藏的明细列 */
export const MIXING_MATERIAL_LOCKED_COLUMN_KEYS = ['mixerMaterialName'];
export interface MixingMaterialColumnSettingItem {
key: string;
title: string;
locked?: boolean;
}
export const columns: BasicColumn[] = [
{ title: '规格', align: 'center', dataIndex: 'specName', width: 160, fixed: 'left' },
{ title: '用途', align: 'center', dataIndex: 'purpose', width: 180 },
{ title: '机台', align: 'center', dataIndex: 'machineName', width: 120 },
{ title: '制作日期', align: 'center', dataIndex: 'makeDate', width: 120 },
{ title: '发行编号', align: 'center', dataIndex: 'issueNumber', width: 150 },
{ title: '段数', align: 'center', dataIndex: 'stageCount', width: 88 },
{ title: '纯混炼时间(秒)', align: 'center', dataIndex: 'pureMixSec', width: 130 },
{ title: '变更日期', align: 'center', dataIndex: 'changeDate', width: 120 },
{ title: '修改时间', align: 'center', dataIndex: 'updateTime', width: 165 },
];
export const searchFormSchema: FormSchema[] = [
{ label: '关键字', field: 'keyword', component: 'Input', colProps: { span: 8 }, componentProps: { placeholder: '规格/用途/发行编号/机台' } },
{ label: '制作日期起', field: 'makeDate_begin', component: 'DatePicker', colProps: { span: 8 }, componentProps: { valueFormat: 'YYYY-MM-DD' } },
{ label: '制作日期止', field: 'makeDate_end', component: 'DatePicker', colProps: { span: 8 }, componentProps: { valueFormat: 'YYYY-MM-DD' } },
];
export const mainSchema: FormSchema[] = [
{ label: '', field: 'id', component: 'Input', show: false },
{ label: '', field: 'machineId', component: 'Input', show: false },
{ label: '规格', field: 'specName', component: 'Input', required: true, colProps: { span: 8 } },
//update-begin---author:cursor ---date:20260522 for【XSLMES-20260522-A33】混炼示方基本信息字段优化-----------
{ label: '用途', field: 'purpose', component: 'Input', required: true, colProps: { span: 8 } },
{ label: '机台', field: 'machineName', component: 'Input', colProps: { span: 8 } },
{ label: '制作日期', field: 'makeDate', component: 'DatePicker', colProps: { span: 8 }, componentProps: { valueFormat: 'YYYY-MM-DD' } },
{ label: '发行编号', field: 'issueNumber', component: 'Input', required: true, colProps: { span: 8 } },
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A33】混炼示方基本信息字段优化-----------
{ label: '换算系数', field: 'convertFactor', component: 'InputNumber', colProps: { span: 8 }, componentProps: { precision: 2, style: { width: '100%' } } },
{ label: '填充体积', field: 'fillVolume', component: 'InputNumber', colProps: { span: 8 }, componentProps: { precision: 6, style: { width: '100%' } } },
{ label: '回收炭黑(秒)', field: 'recycleCarbonSec', component: 'InputNumber', colProps: { span: 8 }, componentProps: { precision: 0, style: { width: '100%' } } },
{ label: '母胶比重', field: 'motherRubberSg', component: 'InputNumber', colProps: { span: 8 }, componentProps: { precision: 6, style: { width: '100%' } } },
{ label: '终炼胶比重', field: 'finalRubberSg', component: 'InputNumber', colProps: { span: 8 }, componentProps: { precision: 6, style: { width: '100%' } } },
{ label: '适用工厂', field: 'applyFactory', component: 'Input', colProps: { span: 8 } },
{ label: '段数', field: 'stageCount', component: 'Input', colProps: { span: 8 }, componentProps: { placeholder: '如 2/3' } },
{ label: '纯混炼时间(秒)', field: 'pureMixSec', component: 'InputNumber', colProps: { span: 8 }, componentProps: { precision: 0, style: { width: '100%' } } },
{ label: '回收炭黑(KG)', field: 'recycleCarbonKg', component: 'InputNumber', colProps: { span: 8 }, componentProps: { precision: 6, style: { width: '100%' } } },
{ label: '自动小料打印设定', field: 'autoSmallPrintSetting', component: 'Input', colProps: { span: 8 } },
{ label: '设定车数', field: 'setTrainCount', component: 'InputNumber', colProps: { span: 8 }, componentProps: { precision: 0, style: { width: '100%' } } },
{ label: '侧壁水温', field: 'sideWallWaterTemp', component: 'InputNumber', colProps: { span: 8 }, componentProps: { precision: 6, style: { width: '100%' } } },
{ label: '超时排胶时间', field: 'overtimeDischargeSec', component: 'InputNumber', colProps: { span: 8 }, componentProps: { precision: 0, style: { width: '100%' } } },
{ label: '超温排胶时间', field: 'overtempDischargeSec', component: 'InputNumber', colProps: { span: 8 }, componentProps: { precision: 0, style: { width: '100%' } } },
{ label: '超温排胶温度', field: 'overtempDischargeTemp', component: 'InputNumber', colProps: { span: 8 }, componentProps: { precision: 6, style: { width: '100%' } } },
{ label: '卸料门水温', field: 'doorWaterTemp', component: 'InputNumber', colProps: { span: 8 }, componentProps: { precision: 6, style: { width: '100%' } } },
{ label: '转子水温', field: 'rotorWaterTemp', component: 'InputNumber', colProps: { span: 8 }, componentProps: { precision: 6, style: { width: '100%' } } },
{ label: '最高进料温度', field: 'maxFeedTemp', component: 'InputNumber', colProps: { span: 8 }, componentProps: { precision: 6, style: { width: '100%' } } },
{ label: '起草人', field: 'draftBy', component: 'Input', colProps: { span: 8 } },
{ label: '起草时间', field: 'draftTime', component: 'DatePicker', colProps: { span: 8 }, componentProps: { showTime: true, valueFormat: 'YYYY-MM-DD HH:mm:ss' } },
{ label: '校对人', field: 'proofreadBy', component: 'Input', colProps: { span: 8 } },
{ label: '校对时间', field: 'proofreadTime', component: 'DatePicker', colProps: { span: 8 }, componentProps: { showTime: true, valueFormat: 'YYYY-MM-DD HH:mm:ss' } },
{ label: '审核人', field: 'auditBy', component: 'Input', colProps: { span: 8 } },
{ label: '审核时间', field: 'auditTime', component: 'DatePicker', colProps: { span: 8 }, componentProps: { showTime: true, valueFormat: 'YYYY-MM-DD HH:mm:ss' } },
{ label: '批准人', field: 'approveBy', component: 'Input', colProps: { span: 8 } },
{ label: '批准时间', field: 'approveTime', component: 'DatePicker', colProps: { span: 8 }, componentProps: { showTime: true, valueFormat: 'YYYY-MM-DD HH:mm:ss' } },
{ label: '变更日期', field: 'changeDate', component: 'DatePicker', colProps: { span: 8 }, componentProps: { valueFormat: 'YYYY-MM-DD' } },
];
//update-begin---author:cursor ---date:20260522 for【XSLMES-20260522-A17】橡胶及配合剂明细列展示设置-----------
export const materialColumns: JVxeColumn[] = [
//update-begin---author:cursor ---date:20260525 for【XSLMES-20260525-A55】混炼示方明细关联密炼物料ID-----------
{ title: '密炼物料ID', key: 'mixerMaterialId', type: JVxeTypes.hidden },
//update-end---author:cursor ---date:20260525 for【XSLMES-20260525-A55】混炼示方明细关联密炼物料ID-----------
{ title: '物料大类', key: 'materialMajor', type: JVxeTypes.input, width: 100, minWidth: MIXING_MATERIAL_MIN_COLUMN_WIDTH },
//update-begin---author:cursor ---date:20260601 for【XSLMES-20260601-A62】种类列置于首列展示且只读-----------
{
title: '种类',
key: 'materialKind',
type: JVxeTypes.input,
width: 100,
minWidth: MIXING_MATERIAL_MIN_COLUMN_WIDTH,
disabled: true,
},
{ title: '物料小类', key: 'materialMinor', type: JVxeTypes.input, width: 120, minWidth: MIXING_MATERIAL_MIN_COLUMN_WIDTH },
//update-end---author:cursor ---date:20260601 for【XSLMES-20260601-A62】种类列置于首列展示且只读-----------
{
title: '密炼物料名称',
key: 'mixerMaterialName',
type: JVxeTypes.slot,
slotName: 'mixerMaterialNameSlot',
width: 160,
minWidth: MIXING_MATERIAL_MIN_COLUMN_WIDTH,
},
{ title: '密炼物料描述', key: 'mixerMaterialDesc', type: JVxeTypes.input, width: 220, minWidth: MIXING_MATERIAL_MIN_COLUMN_WIDTH },
{ title: '单重', key: 'unitWeight', type: JVxeTypes.inputNumber, width: 72, minWidth: MIXING_MATERIAL_MIN_COLUMN_WIDTH, align: 'center' },
//update-begin---author:cursor ---date:20260525 for【XSLMES-20260525-A41】累计列按种类分组合计只读展示-----------
{
title: '累计',
key: 'accumWeight',
type: JVxeTypes.inputNumber,
width: 72,
minWidth: MIXING_MATERIAL_MIN_COLUMN_WIDTH,
align: 'center',
disabled: true,
},
//update-end---author:cursor ---date:20260525 for【XSLMES-20260525-A41】累计列按种类分组合计只读展示-----------
{ title: '顺序', key: 'seqNo', type: JVxeTypes.inputNumber, width: 64, minWidth: MIXING_MATERIAL_MIN_COLUMN_WIDTH, align: 'center' },
];
/** 橡胶及配合剂明细列设置项 */
export function getMixingMaterialColumnSettingItems(): MixingMaterialColumnSettingItem[] {
return materialColumns.map((col) => ({
key: String(col.key),
title: String(col.title),
locked: MIXING_MATERIAL_LOCKED_COLUMN_KEYS.includes(String(col.key)),
}));
}
/** 读取已隐藏的橡胶及配合剂明细列 key */
export function loadMixingMaterialHiddenColumnKeys(): string[] {
const saved = mixingMaterialColumnStorage.get(MIXING_MATERIAL_COLUMN_CACHE_KEY);
const validKeys = new Set(getMixingMaterialColumnSettingItems().map((item) => item.key));
if (!Array.isArray(saved)) {
return MIXING_MATERIAL_DEFAULT_HIDDEN_COLUMN_KEYS.filter((key) => validKeys.has(key));
}
//update-begin---author:cursor ---date:20260601 for【XSLMES-20260601-A62】迁移列显示偏好原隐藏种类改为隐藏物料小类-----------
let keys = saved.filter(
(key) => typeof key === 'string' && !MIXING_MATERIAL_LOCKED_COLUMN_KEYS.includes(key) && validKeys.has(key),
);
if (keys.includes('materialKind') && !keys.includes('materialMinor')) {
keys = keys.filter((key) => key !== 'materialKind');
keys.push('materialMinor');
}
return keys;
//update-end---author:cursor ---date:20260601 for【XSLMES-20260601-A62】迁移列显示偏好原隐藏种类改为隐藏物料小类-----------
}
/** 保存已隐藏的橡胶及配合剂明细列 key */
export function saveMixingMaterialHiddenColumnKeys(hiddenKeys: string[]) {
const validKeys = hiddenKeys.filter((key) => !MIXING_MATERIAL_LOCKED_COLUMN_KEYS.includes(key));
if (validKeys.length) {
mixingMaterialColumnStorage.set(MIXING_MATERIAL_COLUMN_CACHE_KEY, validKeys);
return;
}
mixingMaterialColumnStorage.remove(MIXING_MATERIAL_COLUMN_CACHE_KEY);
}
/** 按隐藏配置过滤橡胶及配合剂明细列 */
export function applyMixingMaterialColumnVisibility(columns: JVxeColumn[], hiddenKeys: string[]): JVxeColumn[] {
if (!hiddenKeys?.length) {
return columns;
}
const hiddenSet = new Set(hiddenKeys);
return columns.filter((col) => !col.key || !hiddenSet.has(String(col.key)));
}
/** 读取已保存的橡胶及配合剂明细列宽 */
export function loadMixingMaterialColumnWidths(): Record<string, number> {
const saved = mixingMaterialColumnStorage.get(MIXING_MATERIAL_COLUMN_WIDTH_CACHE_KEY);
const validKeys = new Set(materialColumns.map((col) => String(col.key)));
if (!saved || typeof saved !== 'object') {
return {};
}
const result: Record<string, number> = {};
Object.entries(saved as Record<string, unknown>).forEach(([key, value]) => {
if (validKeys.has(key) && typeof value === 'number' && value > 0) {
result[key] = value;
}
});
return result;
}
/** 保存橡胶及配合剂明细列宽 */
export function saveMixingMaterialColumnWidths(widthMap: Record<string, number>) {
const validKeys = new Set(materialColumns.map((col) => String(col.key)));
const next: Record<string, number> = {};
Object.entries(widthMap || {}).forEach(([key, value]) => {
if (validKeys.has(key) && typeof value === 'number' && value > 0) {
next[key] = value;
}
});
if (Object.keys(next).length) {
mixingMaterialColumnStorage.set(MIXING_MATERIAL_COLUMN_WIDTH_CACHE_KEY, next);
return;
}
mixingMaterialColumnStorage.remove(MIXING_MATERIAL_COLUMN_WIDTH_CACHE_KEY);
}
/** 应用已保存列宽minWidth 保持最小值,允许拖拽缩小) */
export function applyMixingMaterialColumnWidths(columns: JVxeColumn[], widthMap: Record<string, number>): JVxeColumn[] {
return columns.map((col) => {
const savedWidth = widthMap[String(col.key)];
const nextCol = {
...col,
minWidth: MIXING_MATERIAL_MIN_COLUMN_WIDTH,
};
if (savedWidth) {
nextCol.width = savedWidth;
}
return nextCol;
});
}
/** 计算橡胶及配合剂明细表总宽度 */
export function calcMixingMaterialTableWidth(columns: JVxeColumn[], widthMap: Record<string, number> = {}) {
const dataWidth = columns.reduce((sum, col) => {
const key = String(col.key);
const width = widthMap[key] ?? Number(col.width) ?? 80;
return sum + width;
}, 0);
return MIXING_MATERIAL_ROW_NUMBER_WIDTH + dataWidth + 1;
}
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A17】橡胶及配合剂明细列展示设置-----------
//update-begin---author:cursor ---date:20260525 for【XSLMES-20260525-A41】橡胶及配合剂明细累计按种类分组合计-----------
/** 是否为有效明细行(参与种类分组) */
function isMixingMaterialDataRow(row: Recordable): boolean {
if (!row) {
return false;
}
return !!(row.mixerMaterialId || row.mixerMaterialName || row.materialKind || row.unitWeight != null && row.unitWeight !== '');
}
/** 规范化种类字段,用于连续行分组 */
function normalizeMixingMaterialKind(row: Recordable): string {
const kind = row?.materialKind;
return kind != null && String(kind).trim() !== '' ? String(kind).trim() : '';
}
/** 按种类连续分组,累计写入每组最后一行 */
export function fillMixingMaterialAccumWeight(rows: Recordable[] = []): Recordable[] {
if (!rows?.length) {
return rows;
}
let index = 0;
while (index < rows.length) {
const current = rows[index];
if (!isMixingMaterialDataRow(current)) {
current.accumWeight = null;
index++;
continue;
}
const kind = normalizeMixingMaterialKind(current);
let groupEnd = index;
let sum = 0;
while (groupEnd < rows.length) {
const row = rows[groupEnd];
if (!isMixingMaterialDataRow(row) || normalizeMixingMaterialKind(row) !== kind) {
break;
}
const weight = toMixingMaterialNumber(row.unitWeight);
if (weight != null) {
sum += weight;
}
groupEnd++;
}
for (let rowIndex = index; rowIndex < groupEnd; rowIndex++) {
rows[rowIndex].accumWeight =
rowIndex === groupEnd - 1 && sum !== 0 ? roundMixingMaterialNumber(sum) : null;
}
index = groupEnd;
}
return rows;
}
/** 安全解析明细数值,避免字符串拼接 */
function toMixingMaterialNumber(value: unknown): number | null {
if (value == null || value === '') {
return null;
}
const num = Number(value);
return Number.isFinite(num) ? num : null;
}
/** 混炼示方重量小数位(与后端 BigDecimal 精度一致) */
const MIXING_MATERIAL_WEIGHT_SCALE = 6;
//update-begin---author:cursor ---date:20260526 for【XSLMES-20260526-A60】换算系数/配方参数数值展示优化-----------
/** 换算系数展示小数位 */
export const MIXING_CONVERT_FACTOR_SCALE = 2;
/** 配方参数设定允许的小数精度 */
export const MIXING_RECIPE_PARAM_DECIMAL_SCALE = 6;
/** 配方参数设定:允许小数的字段 */
export const MIXING_RECIPE_PARAM_DECIMAL_FIELDS = [
'sideWallWaterTemp',
'overtempDischargeTemp',
'doorWaterTemp',
'rotorWaterTemp',
'maxFeedTemp',
] as const;
/** 换算系数展示:固定保留两位小数 */
export function formatMixingConvertFactorDisplay(value: unknown): string {
if (value === null || value === undefined || value === '') {
return '';
}
const num = Number(value);
if (Number.isNaN(num)) {
return String(value);
}
return num.toFixed(MIXING_CONVERT_FACTOR_SCALE);
}
/** 换算系数输入解析 */
export function parseMixingConvertFactorValue(value: unknown): number | null {
if (value === '' || value == null) {
return null;
}
const num = Number(String(value).replace(/,/g, '').trim());
if (Number.isNaN(num)) {
return null;
}
return Number(num.toFixed(MIXING_CONVERT_FACTOR_SCALE));
}
/** 配方参数展示:允许小数,无小数部分时仅展示整数 */
export function formatMixingRecipeParamDisplay(value: unknown, maxPrecision = MIXING_RECIPE_PARAM_DECIMAL_SCALE): string {
if (value === null || value === undefined || value === '') {
return '';
}
const num = Number(value);
if (Number.isNaN(num)) {
return String(value);
}
const rounded = Number(num.toFixed(maxPrecision));
if (Number.isInteger(rounded)) {
return String(rounded);
}
return String(rounded);
}
/** 配方参数输入解析 */
export function parseMixingRecipeParamValue(value: unknown): number | null {
if (value === '' || value == null) {
return null;
}
const text = String(value).replace(/,/g, '').trim();
if (!text) {
return null;
}
const num = Number(text);
if (Number.isNaN(num)) {
return null;
}
return Number(num.toFixed(MIXING_RECIPE_PARAM_DECIMAL_SCALE));
}
//update-end---author:cursor ---date:20260526 for【XSLMES-20260526-A60】换算系数/配方参数数值展示优化-----------
/** 重量四舍五入,消除浮点累加误差 */
function roundMixingMaterialNumber(value: number): number {
return Number(value.toFixed(MIXING_MATERIAL_WEIGHT_SCALE));
}
/** 格式化重量展示文本 */
function formatMixingMaterialWeight(value: unknown): string {
const num = toMixingMaterialNumber(value);
if (num == null) {
return '';
}
return String(roundMixingMaterialNumber(num));
}
//update-begin---author:cursor ---date:20260525 for【XSLMES-20260525-A43】换算系数联动明细单重实时计算-----------
/** 规范化换算系数,空值或非正数按 1 处理 */
export function normalizeMixingConvertFactor(factor: unknown): number {
const num = toMixingMaterialNumber(factor);
if (num == null || num <= 0) {
return 1;
}
return num;
}
/** 基准单重 × 换算系数 */
export function calcMixingMaterialConvertedWeight(base: unknown, factor: unknown): number | null {
const baseNum = toMixingMaterialNumber(base);
if (baseNum == null) {
return null;
}
return Number((baseNum * normalizeMixingConvertFactor(factor)).toFixed(MIXING_MATERIAL_WEIGHT_SCALE));
}
/** 从当前显示单重反推基准单重 */
export function syncMaterialBaseUnitWeightFromDisplay(row: Recordable, factor: unknown) {
if (!row) {
return;
}
const unit = toMixingMaterialNumber(row.unitWeight);
if (unit == null) {
row.baseUnitWeight = null;
return;
}
row.baseUnitWeight = Number((unit / normalizeMixingConvertFactor(factor)).toFixed(MIXING_MATERIAL_WEIGHT_SCALE));
}
/** 初始化明细行基准单重(编辑加载时由已保存单重反推) */
export function initMaterialBaseUnitWeight(row: Recordable, factor: unknown, force = false) {
if (!isMixingMaterialDataRow(row)) {
row.baseUnitWeight = null;
return;
}
if (!force && toMixingMaterialNumber(row.baseUnitWeight) != null) {
return;
}
syncMaterialBaseUnitWeightFromDisplay(row, factor);
}
/** 批量初始化基准单重 */
export function initMaterialBaseUnitWeights(rows: Recordable[] = [], factor: unknown, force = false) {
for (const row of rows) {
initMaterialBaseUnitWeight(row, factor, force);
}
return rows;
}
/** 按换算系数重算所有明细单重 */
export function applyConvertFactorToMaterialRows(
rows: Recordable[] = [],
factor: unknown,
prevFactor?: unknown,
): Recordable[] {
const nextFactor = normalizeMixingConvertFactor(factor);
const oldFactor = prevFactor != null ? normalizeMixingConvertFactor(prevFactor) : nextFactor;
for (const row of rows) {
if (!isMixingMaterialDataRow(row)) {
continue;
}
let base = toMixingMaterialNumber(row.baseUnitWeight);
if (base == null) {
const unit = toMixingMaterialNumber(row.unitWeight);
if (unit == null) {
continue;
}
base = roundMixingMaterialNumber(unit / oldFactor);
row.baseUnitWeight = base;
}
row.unitWeight = calcMixingMaterialConvertedWeight(base, nextFactor);
}
return rows;
}
//update-end---author:cursor ---date:20260525 for【XSLMES-20260525-A43】换算系数联动明细单重实时计算-----------
//update-begin---author:cursor ---date:20260525 for【XSLMES-20260525-A46】填充体积按单重/比重/机台有效体积自动计算-----------
/** 解析设备有效体积(支持纯数字或带单位字符串) */
export function parseMixingEffectiveVolume(raw: unknown): number | null {
if (raw == null || raw === '') {
return null;
}
const text = String(raw).trim();
if (!text) {
return null;
}
const direct = toMixingMaterialNumber(text);
if (direct != null && direct > 0) {
return direct;
}
const matched = text.match(/([0-9]+(?:\.[0-9]+)?)/);
if (!matched) {
return null;
}
const parsed = Number(matched[1]);
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
}
/** 按段数与比重字段选择本段计算用比重 */
export function resolveMixingSpecificGravity(form: Recordable = {}): number | null {
const motherSg = toMixingMaterialNumber(form.motherRubberSg);
const finalSg = toMixingMaterialNumber(form.finalRubberSg);
const stageCount = String(form.stageCount || '').trim();
const stageMatch = stageCount.match(/^(\d+)\/(\d+)$/);
const isFinalStage = stageMatch ? stageMatch[1] === stageMatch[2] : false;
if (isFinalStage && finalSg != null && finalSg > 0) {
return finalSg;
}
if (motherSg != null && motherSg > 0) {
return motherSg;
}
if (finalSg != null && finalSg > 0) {
return finalSg;
}
return null;
}
/**
* 填充体积(%) = 单重合计 ÷ 比重 ÷ 机台有效体积(L) × 100
* 单重合计已含换算系数,此处不再重复乘换算系数
*/
export function calcMixingFillVolume(totalWeight: unknown, specificGravity: unknown, effectiveVolume: unknown): number | null {
const weight = toMixingMaterialNumber(totalWeight);
const sg = toMixingMaterialNumber(specificGravity);
const volume = parseMixingEffectiveVolume(effectiveVolume);
if (weight == null || weight <= 0 || sg == null || sg <= 0 || volume == null || volume <= 0) {
return null;
}
const materialVolume = weight / sg;
return Number(((materialVolume / volume) * 100).toFixed(6));
}
//update-end---author:cursor ---date:20260525 for【XSLMES-20260525-A46】填充体积按单重/比重/机台有效体积自动计算-----------
/** 汇总有效明细行的单重合计 */
export function calcMixingMaterialUnitWeightTotal(rows: Recordable[] = []): number | null {
let sum = 0;
let hasAny = false;
for (const row of rows) {
if (!isMixingMaterialDataRow(row)) {
continue;
}
const weight = toMixingMaterialNumber(row.unitWeight);
if (weight != null) {
sum += weight;
hasAny = true;
}
}
return hasAny ? roundMixingMaterialNumber(sum) : null;
}
/** 汇总有效明细行的累计合计(与单重合计一致) */
export function calcMixingMaterialAccumWeightTotal(rows: Recordable[] = []): number | null {
return calcMixingMaterialUnitWeightTotal(rows);
}
export interface MixingMaterialFooterCell {
key: string;
width: number;
text: string;
align?: 'left' | 'center' | 'right';
isLabel?: boolean;
isTotal?: boolean;
}
/** 构建橡胶及配合剂明细底部合计行单元格(列宽与明细表同步) */
export function buildMixingMaterialFooterCells(
columns: JVxeColumn[],
widthMap: Record<string, number>,
totals: { unitWeight?: number | null; accumWeight?: number | null },
): MixingMaterialFooterCell[] {
const unitWeightIndex = columns.findIndex((col) => String(col.key) === 'unitWeight');
const formatTotal = (value: number | null | undefined) => formatMixingMaterialWeight(value);
return columns.map((col, index) => {
const key = String(col.key);
const width = widthMap[key] ?? Number(col.width) ?? 80;
if (key === 'unitWeight') {
return { key, width, text: formatTotal(totals.unitWeight), align: 'center', isTotal: true };
}
if (key === 'accumWeight') {
return { key, width, text: formatTotal(totals.accumWeight), align: 'center', isTotal: true };
}
const isLabelCol = unitWeightIndex > 0 && index === unitWeightIndex - 1;
return {
key,
width,
text: isLabelCol ? '合计' : '',
align: 'center',
isLabel: isLabelCol,
};
});
}
//update-end---author:cursor ---date:20260525 for【XSLMES-20260525-A41】橡胶及配合剂明细累计按种类分组合计-----------
//update-begin---author:cursor ---date:20260522 for【XSLMES-20260522-A20】明细表默认列宽对齐参考图-----------
/** 混合步骤/下密炼机明细列可缩小到的最小宽度 */
export const MIXING_STEP_MIN_COLUMN_WIDTH = 48;
export const stepColumns: JVxeColumn[] = [
//update-begin---author:cursor ---date:20260522 for【XSLMES-20260522-A35】混合步骤动作/组合列常显下拉倒三角-----------
{
title: '动作',
key: 'actionName',
type: JVxeTypes.slot,
slotName: 'actionNameSlot',
width: 120,
minWidth: MIXING_STEP_MIN_COLUMN_WIDTH,
},
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A35】混合步骤动作/组合列常显下拉倒三角-----------
{ title: '时间(")', key: 'actionSec', type: JVxeTypes.inputNumber, width: 80, minWidth: MIXING_STEP_MIN_COLUMN_WIDTH, align: 'center' },
{ title: '保护时间', key: 'protectSec', type: JVxeTypes.inputNumber, width: 80, minWidth: MIXING_STEP_MIN_COLUMN_WIDTH, align: 'center' },
{ title: '温度(℃)', key: 'tempC', type: JVxeTypes.inputNumber, width: 80, minWidth: MIXING_STEP_MIN_COLUMN_WIDTH, align: 'center' },
{ title: '功率(Kw)', key: 'powerKw', type: JVxeTypes.inputNumber, width: 80, minWidth: MIXING_STEP_MIN_COLUMN_WIDTH, align: 'center' },
{ title: '能量(Kwh)', key: 'energyKwh', type: JVxeTypes.inputNumber, width: 80, minWidth: MIXING_STEP_MIN_COLUMN_WIDTH, align: 'center' },
//update-begin---author:cursor ---date:20260522 for【XSLMES-20260522-A35】混合步骤动作/组合列常显下拉倒三角-----------
{
title: '组合',
key: 'comboMode',
type: JVxeTypes.slot,
slotName: 'comboModeSlot',
width: 72,
minWidth: MIXING_STEP_MIN_COLUMN_WIDTH,
align: 'center',
},
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A35】混合步骤动作/组合列常显下拉倒三角-----------
{ title: '转速(rpm)', key: 'speedRpm', type: JVxeTypes.inputNumber, width: 80, minWidth: MIXING_STEP_MIN_COLUMN_WIDTH, align: 'center' },
{ title: '压力(Mpa)', key: 'pressureMpa', type: JVxeTypes.inputNumber, width: 80, minWidth: MIXING_STEP_MIN_COLUMN_WIDTH, align: 'center' },
{ title: '栓(%)', key: 'boltPercent', type: JVxeTypes.inputNumber, width: 72, minWidth: MIXING_STEP_MIN_COLUMN_WIDTH, align: 'center' },
];
/** 混合步骤/下密炼机明细列宽偏好 localStorage 键(两表共用同步) */
export const MIXING_STEP_COLUMN_WIDTH_CACHE_KEY = 'mes_xsl_mixing_spec_step_column_widths_v1';
/** 读取已保存的混合步骤明细列宽 */
export function loadMixingStepColumnWidths(): Record<string, number> {
const saved = mixingMaterialColumnStorage.get(MIXING_STEP_COLUMN_WIDTH_CACHE_KEY);
const validKeys = new Set(stepColumns.map((col) => String(col.key)));
if (!saved || typeof saved !== 'object') {
return {};
}
const result: Record<string, number> = {};
Object.entries(saved as Record<string, unknown>).forEach(([key, value]) => {
if (validKeys.has(key) && typeof value === 'number' && value > 0) {
result[key] = value;
}
});
return result;
}
/** 保存混合步骤明细列宽 */
export function saveMixingStepColumnWidths(widthMap: Record<string, number>) {
const validKeys = new Set(stepColumns.map((col) => String(col.key)));
const next: Record<string, number> = {};
Object.entries(widthMap || {}).forEach(([key, value]) => {
if (validKeys.has(key) && typeof value === 'number' && value > 0) {
next[key] = value;
}
});
if (Object.keys(next).length) {
mixingMaterialColumnStorage.set(MIXING_STEP_COLUMN_WIDTH_CACHE_KEY, next);
return;
}
mixingMaterialColumnStorage.remove(MIXING_STEP_COLUMN_WIDTH_CACHE_KEY);
}
/** 应用已保存混合步骤列宽minWidth 保持最小值,允许拖拽缩小) */
export function applyMixingStepColumnWidths(columns: JVxeColumn[], widthMap: Record<string, number>): JVxeColumn[] {
return columns.map((col) => {
const savedWidth = widthMap[String(col.key)];
const nextCol = {
...col,
minWidth: MIXING_STEP_MIN_COLUMN_WIDTH,
};
if (savedWidth) {
nextCol.width = savedWidth;
}
return nextCol;
});
}
/** 计算混合步骤/下密炼机明细表总宽度 */
export function calcMixingStepTableWidth(columns: JVxeColumn[] = stepColumns, widthMap: Record<string, number> = {}) {
const dataWidth = columns.reduce((sum, col) => {
const key = String(col.key);
const width = widthMap[key] ?? Number(col.width) ?? Number(col.minWidth) ?? 80;
return sum + width;
}, 0);
return MIXING_MATERIAL_ROW_NUMBER_WIDTH + dataWidth + 1;
}
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A20】明细表默认列宽对齐参考图-----------
export const downStepColumns: JVxeColumn[] = [...stepColumns];
//update-begin---author:cursor ---date:20260522 for【XSLMES-20260522-A19】TCU温度条件表列宽可调且表头换行-----------
/** TCU 温度条件明细列宽偏好 localStorage 键 */
export const MIXING_TCU_COLUMN_WIDTH_CACHE_KEY = 'mes_xsl_mixing_spec_tcu_column_widths_v2';
/** TCU 温度条件明细列可缩小到的最小宽度 */
export const MIXING_TCU_MIN_COLUMN_WIDTH = 48;
export const tcuColumns: JVxeColumn[] = [
//update-begin---author:cursor ---date:20260522 for【XSLMES-20260522-A33】TCU区分固定上/下密炼机-----------
{ title: '区分', key: 'sectionType', type: JVxeTypes.select, dictCode: 'xslmes_mixing_tcu_section', disabled: true, width: 96, minWidth: MIXING_TCU_MIN_COLUMN_WIDTH, align: 'center' },
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A33】TCU区分固定上/下密炼机-----------
{ title: '前转子温度', key: 'frontRotorTemp', type: JVxeTypes.inputNumber, width: 76, minWidth: MIXING_TCU_MIN_COLUMN_WIDTH, align: 'center' },
{ title: '后转子温度', key: 'rearRotorTemp', type: JVxeTypes.inputNumber, width: 76, minWidth: MIXING_TCU_MIN_COLUMN_WIDTH, align: 'center' },
{ title: '前混炼室温度', key: 'frontChamberTemp', type: JVxeTypes.inputNumber, width: 76, minWidth: MIXING_TCU_MIN_COLUMN_WIDTH, align: 'center' },
{ title: '后混炼室温度', key: 'rearChamberTemp', type: JVxeTypes.inputNumber, width: 76, minWidth: MIXING_TCU_MIN_COLUMN_WIDTH, align: 'center' },
{ title: '上下顶栓温度', key: 'topPlugTemp', type: JVxeTypes.inputNumber, width: 76, minWidth: MIXING_TCU_MIN_COLUMN_WIDTH, align: 'center' },
{ title: '药品称量位置', key: 'drugWeighPos', type: JVxeTypes.select, dictCode: 'xslmes_mixing_drug_weigh_pos', width: 76, minWidth: MIXING_TCU_MIN_COLUMN_WIDTH, align: 'center' },
];
/** 读取已保存的 TCU 温度条件明细列宽 */
export function loadMixingTcuColumnWidths(): Record<string, number> {
const saved = mixingMaterialColumnStorage.get(MIXING_TCU_COLUMN_WIDTH_CACHE_KEY);
const validKeys = new Set(tcuColumns.map((col) => String(col.key)));
if (!saved || typeof saved !== 'object') {
return {};
}
const result: Record<string, number> = {};
Object.entries(saved as Record<string, unknown>).forEach(([key, value]) => {
if (validKeys.has(key) && typeof value === 'number' && value > 0) {
result[key] = value;
}
});
return result;
}
/** 保存 TCU 温度条件明细列宽 */
export function saveMixingTcuColumnWidths(widthMap: Record<string, number>) {
const validKeys = new Set(tcuColumns.map((col) => String(col.key)));
const next: Record<string, number> = {};
Object.entries(widthMap || {}).forEach(([key, value]) => {
if (validKeys.has(key) && typeof value === 'number' && value > 0) {
next[key] = value;
}
});
if (Object.keys(next).length) {
mixingMaterialColumnStorage.set(MIXING_TCU_COLUMN_WIDTH_CACHE_KEY, next);
return;
}
mixingMaterialColumnStorage.remove(MIXING_TCU_COLUMN_WIDTH_CACHE_KEY);
}
/** 应用已保存 TCU 列宽minWidth 保持最小值,允许拖拽缩小) */
export function applyMixingTcuColumnWidths(columns: JVxeColumn[], widthMap: Record<string, number>): JVxeColumn[] {
return columns.map((col) => {
const savedWidth = widthMap[String(col.key)];
const nextCol = {
...col,
minWidth: MIXING_TCU_MIN_COLUMN_WIDTH,
};
if (savedWidth) {
nextCol.width = savedWidth;
}
return nextCol;
});
}
/** 计算 TCU 温度条件明细表总宽度 */
export function calcMixingTcuTableWidth(columns: JVxeColumn[] = tcuColumns, widthMap: Record<string, number> = {}) {
const dataWidth = columns.reduce((sum, col) => {
const key = String(col.key);
const width = widthMap[key] ?? Number(col.width) ?? Number(col.minWidth) ?? 80;
return sum + width;
}, 0);
return MIXING_MATERIAL_ROW_NUMBER_WIDTH + dataWidth + 1;
}
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A19】TCU温度条件表列宽可调且表头换行-----------
//update-begin---author:cursor ---date:20260522 for【XSLMES-20260522-A26】TCU紧凑两行且胶料表向下扩展-----------
/** TCU 温度条件固定行数(上密炼机/下密炼机) */
export const DEFAULT_MIXING_TCU_ROW_COUNT = 2;
/** TCU 表头高度(列标题换行) */
export const MIXING_TCU_HEADER_HEIGHT = 48;
/** 计算 TCU 温度条件表格高度(仅展示固定两行) */
export function calcMixingTcuTableHeight(rowCount = DEFAULT_MIXING_TCU_ROW_COUNT) {
const rows = Math.max(1, rowCount);
return MIXING_TCU_HEADER_HEIGHT + rows * MIXING_VXE_MINI_ROW_HEIGHT + 2;
}
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A26】TCU紧凑两行且胶料表向下扩展-----------
//update-begin---author:cursor ---date:20260522 for【XSLMES-20260522-A22】明细表默认空行数-----------
/** 橡胶及配合剂明细默认行数 */
export const DEFAULT_MIXING_MATERIAL_ROW_COUNT = 20;
/** 混合步骤明细默认行数 */
export const DEFAULT_MIXING_STEP_ROW_COUNT = 30;
//update-begin---author:cursor ---date:20260522 for【XSLMES-20260522-A30】胶料/混合步骤列表默认展示高度-----------
/** 橡胶及配合剂明细列表默认展示行数(可视高度) */
export const MIXING_MATERIAL_VISIBLE_ROW_COUNT = 17;
/** 混合步骤明细列表默认展示行数(可视高度) */
export const MIXING_STEP_VISIBLE_ROW_COUNT = 15;
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A30】胶料/混合步骤列表默认展示高度-----------
//update-begin---author:cursor ---date:20260522 for【XSLMES-20260522-A27】下密炼机混炼条件默认行数改为4-----------
/** 下密炼机混炼条件明细默认行数 */
export const DEFAULT_MIXING_DOWN_STEP_ROW_COUNT = 4;
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A27】下密炼机混炼条件默认行数改为4-----------
/** vxe mini 表头高度 */
export const MIXING_VXE_MINI_HEADER_HEIGHT = 36;
/** vxe mini 行高 */
export const MIXING_VXE_MINI_ROW_HEIGHT = 32;
/** 橡胶及配合剂明细合计行高度(含边框) */
export const MIXING_MATERIAL_FOOTER_ROW_HEIGHT = MIXING_VXE_MINI_ROW_HEIGHT + 1;
//update-begin---author:cursor ---date:20260522 for【XSLMES-20260522-A29】胶料/混合步骤表格高度按行数完整展示-----------
/** 计算橡胶及配合剂明细表格展示高度 */
export function calcMixingMaterialTableHeight(rowCount = MIXING_MATERIAL_VISIBLE_ROW_COUNT) {
const rows = Math.max(1, rowCount);
return MIXING_VXE_MINI_HEADER_HEIGHT + rows * MIXING_VXE_MINI_ROW_HEIGHT + 4;
}
/** 计算混合步骤明细表格展示高度 */
export function calcMixingStepTableHeight(rowCount = MIXING_STEP_VISIBLE_ROW_COUNT) {
const rows = Math.max(1, rowCount);
return MIXING_VXE_MINI_HEADER_HEIGHT + rows * MIXING_VXE_MINI_ROW_HEIGHT + 4;
}
//update-begin---author:cursor ---date:20260522 for【XSLMES-20260522-A31】下密炼机4行表格高度微调-----------
/** 计算下密炼机混炼条件表格高度默认完整展示4行 */
export function calcMixingDownStepTableHeight(rowCount = DEFAULT_MIXING_DOWN_STEP_ROW_COUNT) {
const rows = Math.max(1, rowCount);
// 额外留白补偿边框与 vxe 内边距避免第4行底部被裁切
return MIXING_VXE_MINI_HEADER_HEIGHT + rows * MIXING_VXE_MINI_ROW_HEIGHT + 12;
}
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A31】下密炼机4行表格高度微调-----------
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A29】胶料/混合步骤表格高度按行数完整展示-----------
//update-begin---author:cursor ---date:20260522 for【XSLMES-20260522-A32】四明细表行高/展示行数设置持久化-----------
/** 混炼示方明细表标识 */
export type MixingDetailTableKey = 'material' | 'tcu' | 'step' | 'downStep';
/** 明细表行高与展示行数偏好 */
export interface MixingTableHeightPreference {
rowHeight: number;
visibleRowCount: number;
}
/** 明细表行高设置元数据 */
export interface MixingTableHeightSettingMeta {
key: MixingDetailTableKey;
label: string;
defaultRowHeight: number;
defaultVisibleRowCount: number;
minVisibleRowCount: number;
maxVisibleRowCount: number;
headerHeight: number;
extraPadding: number;
}
/** 明细表行高 localStorage 键 */
export const MIXING_TABLE_HEIGHT_PREF_CACHE_KEY = 'mes_xsl_mixing_spec_table_height_prefs_v1';
/** 行高可调范围px */
export const MIXING_TABLE_ROW_HEIGHT_MIN = 24;
export const MIXING_TABLE_ROW_HEIGHT_MAX = 56;
export const MIXING_TABLE_HEIGHT_SETTING_META: Record<MixingDetailTableKey, MixingTableHeightSettingMeta> = {
material: {
key: 'material',
label: '橡胶及配合剂',
defaultRowHeight: MIXING_VXE_MINI_ROW_HEIGHT,
defaultVisibleRowCount: MIXING_MATERIAL_VISIBLE_ROW_COUNT,
minVisibleRowCount: 5,
maxVisibleRowCount: 30,
headerHeight: MIXING_VXE_MINI_HEADER_HEIGHT,
extraPadding: 4,
},
tcu: {
key: 'tcu',
label: 'TCU温度条件',
defaultRowHeight: MIXING_VXE_MINI_ROW_HEIGHT,
defaultVisibleRowCount: DEFAULT_MIXING_TCU_ROW_COUNT,
minVisibleRowCount: 2,
maxVisibleRowCount: 6,
headerHeight: MIXING_TCU_HEADER_HEIGHT,
extraPadding: 2,
},
step: {
key: 'step',
label: '混合步骤',
defaultRowHeight: MIXING_VXE_MINI_ROW_HEIGHT,
defaultVisibleRowCount: MIXING_STEP_VISIBLE_ROW_COUNT,
minVisibleRowCount: 5,
maxVisibleRowCount: 40,
headerHeight: MIXING_VXE_MINI_HEADER_HEIGHT,
extraPadding: 4,
},
downStep: {
key: 'downStep',
label: '下密炼机混炼条件',
defaultRowHeight: MIXING_VXE_MINI_ROW_HEIGHT,
defaultVisibleRowCount: DEFAULT_MIXING_DOWN_STEP_ROW_COUNT,
minVisibleRowCount: 2,
maxVisibleRowCount: 12,
headerHeight: MIXING_VXE_MINI_HEADER_HEIGHT,
extraPadding: 12,
},
};
function clampMixingTableNumber(value: unknown, min: number, max: number, fallback: number) {
const num = Number(value);
if (Number.isNaN(num)) {
return fallback;
}
return Math.min(max, Math.max(min, Math.round(num)));
}
/** 获取明细表默认行高偏好 */
export function getMixingTableHeightDefault(tableKey: MixingDetailTableKey): MixingTableHeightPreference {
const meta = MIXING_TABLE_HEIGHT_SETTING_META[tableKey];
return {
rowHeight: meta.defaultRowHeight,
visibleRowCount: meta.defaultVisibleRowCount,
};
}
/** 规范化明细表行高偏好 */
export function normalizeMixingTableHeightPreference(
tableKey: MixingDetailTableKey,
preference?: Partial<MixingTableHeightPreference> | null,
): MixingTableHeightPreference {
const meta = MIXING_TABLE_HEIGHT_SETTING_META[tableKey];
const defaults = getMixingTableHeightDefault(tableKey);
return {
rowHeight: clampMixingTableNumber(
preference?.rowHeight,
MIXING_TABLE_ROW_HEIGHT_MIN,
MIXING_TABLE_ROW_HEIGHT_MAX,
defaults.rowHeight,
),
visibleRowCount: clampMixingTableNumber(
preference?.visibleRowCount,
meta.minVisibleRowCount,
meta.maxVisibleRowCount,
defaults.visibleRowCount,
),
};
}
/** 读取明细表行高偏好 */
export function loadMixingTableHeightPreference(tableKey: MixingDetailTableKey): MixingTableHeightPreference {
const saved = mixingMaterialColumnStorage.get(MIXING_TABLE_HEIGHT_PREF_CACHE_KEY);
const tableSaved =
saved && typeof saved === 'object' ? (saved as Record<string, Partial<MixingTableHeightPreference>>)[tableKey] : null;
return normalizeMixingTableHeightPreference(tableKey, tableSaved);
}
/** 保存明细表行高偏好 */
export function saveMixingTableHeightPreference(tableKey: MixingDetailTableKey, preference: MixingTableHeightPreference) {
const saved = mixingMaterialColumnStorage.get(MIXING_TABLE_HEIGHT_PREF_CACHE_KEY);
const next =
saved && typeof saved === 'object'
? { ...(saved as Record<string, MixingTableHeightPreference>) }
: ({} as Record<string, MixingTableHeightPreference>);
next[tableKey] = normalizeMixingTableHeightPreference(tableKey, preference);
mixingMaterialColumnStorage.set(MIXING_TABLE_HEIGHT_PREF_CACHE_KEY, next);
}
/** 计算明细表展示区域高度 */
export function calcMixingDetailTableViewportHeight(
tableKey: MixingDetailTableKey,
preference?: Partial<MixingTableHeightPreference>,
) {
const meta = MIXING_TABLE_HEIGHT_SETTING_META[tableKey];
const normalized = normalizeMixingTableHeightPreference(tableKey, preference);
return meta.headerHeight + normalized.visibleRowCount * normalized.rowHeight + meta.extraPadding;
}
/** 构建 JVxeTable rowConfig */
export function buildMixingTableRowConfig(preference: MixingTableHeightPreference) {
return {
isHover: true,
height: preference.rowHeight,
};
}
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A32】四明细表行高/展示行数设置持久化-----------
function createEmptyMixingRows(count: number): Recordable[] {
return Array.from({ length: count }, () => ({ id: buildUUID() }));
}
/** 创建橡胶及配合剂默认空行 */
export function createEmptyMaterialRows(count = DEFAULT_MIXING_MATERIAL_ROW_COUNT): Recordable[] {
return createEmptyMixingRows(count);
}
/** 创建混合步骤默认空行 */
export function createEmptyStepRows(count = DEFAULT_MIXING_STEP_ROW_COUNT): Recordable[] {
return createEmptyMixingRows(count);
}
/** 创建下密炼机混炼条件默认空行 */
export function createEmptyDownStepRows(count = DEFAULT_MIXING_DOWN_STEP_ROW_COUNT): Recordable[] {
return createEmptyMixingRows(count);
}
//update-begin---author:cursor ---date:20260522 for【XSLMES-20260522-A33】TCU默认两行及上密炼机药品称默认值-----------
/** 上密炼机 TCU 行默认药品称量位置(药品称) */
export const MIXING_TCU_UP_MIXER_DRUG_WEIGH_POS = 'drug_scale';
/** 构建 TCU 温度条件默认两行(上密炼机/下密炼机) */
export function buildDefaultMixingTcuRows(rows: Recordable[] = []): Recordable[] {
const up =
rows.find((r) => r.sectionType === 'up_mixer') ||
({ sectionType: 'up_mixer', drugWeighPos: MIXING_TCU_UP_MIXER_DRUG_WEIGH_POS } as Recordable);
if (up.sectionType === 'up_mixer' && !up.drugWeighPos) {
up.drugWeighPos = MIXING_TCU_UP_MIXER_DRUG_WEIGH_POS;
}
const down = rows.find((r) => r.sectionType === 'down_mixer') || ({ sectionType: 'down_mixer', drugWeighPos: undefined } as Recordable);
return [up, down];
}
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A33】TCU默认两行及上密炼机药品称默认值-----------
/** 确保明细行具备唯一 idJVxeTable 依赖 rowKey=id */
export function normalizeMixingDetailRows(rows: Recordable[] = []): Recordable[] {
return (rows || []).map((row) => ({ ...row, id: row?.id || buildUUID() }));
}
//update-begin---author:cursor ---date:20260522 for【XSLMES-20260522-A39】编辑页明细补齐默认空行与新增一致-----------
/** 编辑/详情加载时补齐明细默认空行(不少于 defaultCount已有数据行保留 */
export function ensureMixingDetailRows(rows: Recordable[] = [], defaultCount: number): Recordable[] {
const normalized = normalizeMixingDetailRows(rows);
if (normalized.length >= defaultCount) {
return normalized;
}
return [...normalized, ...createEmptyMixingRows(defaultCount - normalized.length)];
}
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A39】编辑页明细补齐默认空行与新增一致-----------
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A22】明细表默认空行数-----------
//update-begin---author:cursor ---date:20260525 for【XSLMES-20260525-A50】混炼示方密炼物料选料弹窗与种类解析-----------
/** 混炼示方选料弹窗:隐藏的小类 ID 偏好 localStorage 键 */
export const MIXING_MATERIAL_PICKER_HIDDEN_CATEGORY_CACHE_KEY = 'mes_xsl_mixing_spec_material_picker_hidden_categories';
export interface MixingMaterialPickerCategoryItem {
id: string;
name: string;
majorId: string;
majorName: string;
label: string;
}
const mixingMaterialPickerStorage = createLocalStorage();
export function loadMixingMaterialPickerHiddenCategoryIds(): string[] {
const raw = mixingMaterialPickerStorage.get(MIXING_MATERIAL_PICKER_HIDDEN_CATEGORY_CACHE_KEY);
return Array.isArray(raw) ? raw.map(String) : [];
}
export function saveMixingMaterialPickerHiddenCategoryIds(ids: string[]) {
mixingMaterialPickerStorage.set(MIXING_MATERIAL_PICKER_HIDDEN_CATEGORY_CACHE_KEY, ids || []);
}
//update-begin---author:cursor ---date:20260525 for【XSLMES-20260525-A50】选料弹窗小类树为空时重置隐藏配置-----------
/** 过滤无效隐藏项;若全部小类被隐藏则自动重置,避免左侧树只剩「全部小类」 */
export function sanitizeMixingMaterialPickerHiddenCategoryIds(allMinorIds: string[], hidden: string[]) {
const allSet = new Set((allMinorIds || []).map(String));
const filtered = (hidden || []).map(String).filter((id) => allSet.has(id));
if (allSet.size > 0 && filtered.length >= allSet.size) {
return [];
}
return filtered;
}
//update-end---author:cursor ---date:20260525 for【XSLMES-20260525-A50】选料弹窗小类树为空时重置隐藏配置-----------
//update-begin---author:cursor ---date:20260525 for【XSLMES-20260525-A52】混炼示方种类改读密炼物料种类配置-----------
export interface MixerMaterialKindLookup {
byRefId: Record<string, string>;
byRefCode: Record<string, string>;
//update-begin---author:cursor ---date:20260601 for【XSLMES-20260601-A62】种类查找表支持胶料类别名称-----------
byRefName?: Record<string, string>;
//update-end---author:cursor ---date:20260601 for【XSLMES-20260601-A62】种类查找表支持胶料类别名称-----------
rubberKindName?: string;
}
export const EMPTY_MIXER_MATERIAL_KIND_LOOKUP: MixerMaterialKindLookup = {
byRefId: {},
byRefCode: {},
byRefName: {},
rubberKindName: '胶料',
};
let mixingMaterialKindLookupCache: MixerMaterialKindLookup | null = null;
let mixingMaterialKindLookupPromise: Promise<MixerMaterialKindLookup> | null = null;
/** 加载密炼物料种类配置查找表(带内存缓存) */
export async function loadMixingMaterialKindLookup(forceReload = false): Promise<MixerMaterialKindLookup> {
if (!forceReload && mixingMaterialKindLookupCache) {
return mixingMaterialKindLookupCache;
}
if (!forceReload && mixingMaterialKindLookupPromise) {
return mixingMaterialKindLookupPromise;
}
mixingMaterialKindLookupPromise = (async () => {
try {
const { loadKindLookup } = await import('../mesXslMixerMaterialKindCfg/MesXslMixerMaterialKindCfg.api');
const raw = await loadKindLookup();
const lookup: MixerMaterialKindLookup = {
byRefId: raw?.byRefId || {},
byRefCode: raw?.byRefCode || {},
//update-begin---author:cursor ---date:20260601 for【XSLMES-20260601-A62】加载种类配置含类别名称映射-----------
byRefName: raw?.byRefName || {},
//update-end---author:cursor ---date:20260601 for【XSLMES-20260601-A62】加载种类配置含类别名称映射-----------
rubberKindName: raw?.rubberKindName || '胶料',
};
mixingMaterialKindLookupCache = lookup;
return lookup;
} catch {
return EMPTY_MIXER_MATERIAL_KIND_LOOKUP;
} finally {
mixingMaterialKindLookupPromise = null;
}
})();
return mixingMaterialKindLookupPromise;
}
/** 清除种类配置缓存(配置变更后可调用) */
export function clearMixingMaterialKindLookupCache() {
mixingMaterialKindLookupCache = null;
mixingMaterialKindLookupPromise = null;
}
function matchKindFromLookup(lookup: MixerMaterialKindLookup | null | undefined, key?: string) {
if (!lookup || key == null || String(key).trim() === '') {
return '';
}
const normalized = String(key).trim();
if (lookup.byRefId?.[normalized]) {
return lookup.byRefId[normalized];
}
if (lookup.byRefCode?.[normalized]) {
return lookup.byRefCode[normalized];
}
const lower = normalized.toLowerCase();
if (lookup.byRefCode?.[lower]) {
return lookup.byRefCode[lower];
}
for (const [code, kindName] of Object.entries(lookup.byRefCode || {})) {
if (code && normalized.includes(code)) {
return kindName;
}
}
return '';
}
/** 解析混炼示方明细种类:称量方式优先,其次分类 ID再按分类名称未命中返回空 */
export function resolveMixingMaterialKindFromLookup(
lookup: MixerMaterialKindLookup | null | undefined,
weighMode?: string,
minorCategoryId?: string,
minorCategoryName?: string,
) {
const fromWeighMode = matchKindFromLookup(lookup, weighMode);
if (fromWeighMode) {
return fromWeighMode;
}
const fromMinorId = matchKindFromLookup(lookup, minorCategoryId);
if (fromMinorId) {
return fromMinorId;
}
//update-begin---author:cursor ---date:20260601 for【XSLMES-20260601-A62】按胶料类别名称匹配种类配置-----------
if (minorCategoryName != null && String(minorCategoryName).trim() !== '') {
const refName = String(minorCategoryName).trim();
if (lookup?.byRefName?.[refName]) {
return lookup.byRefName[refName];
}
}
//update-end---author:cursor ---date:20260601 for【XSLMES-20260601-A62】按胶料类别名称匹配种类配置-----------
return '';
}
/** 胶料页签解析种类:配置匹配后可用 rubberKindName如「胶料」兜底 */
export function resolveMixingRubberKindForPicker(
lookup: MixerMaterialKindLookup | null | undefined,
weighMode?: string,
categoryId?: string,
categoryName?: string,
categoryCode?: string,
) {
let kind = resolveMixingMaterialKindFromLookup(lookup, weighMode, categoryId, categoryName);
if (!kind && categoryCode) {
kind = matchKindFromLookup(lookup, categoryCode);
}
if (!kind && lookup?.rubberKindName) {
kind = lookup.rubberKindName;
}
return kind;
}
/** @deprecated 保留兼容,请改用 resolveMixingMaterialKindFromLookup */
export function resolveMixingMaterialKindFromCategory(_isRubber?: unknown, minorName?: string) {
return minorName != null && String(minorName).trim() !== '' ? String(minorName).trim() : '';
}
/** @deprecated 保留兼容,请改用 resolveMixingMaterialKindFromLookup */
export function resolveMixingMaterialKindFromWeighMode(_weighMode?: string) {
return '';
}
/** 选料确认时种类:读密炼物料种类配置表 */
export function resolveMixingMaterialKindForPicker(
lookup: MixerMaterialKindLookup | null | undefined,
weighMode: string | undefined,
minorCategoryId?: string,
minorCategoryName?: string,
) {
return resolveMixingMaterialKindFromLookup(lookup, weighMode, minorCategoryId, minorCategoryName);
}
//update-end---author:cursor ---date:20260525 for【XSLMES-20260525-A52】混炼示方种类改读密炼物料种类配置-----------
//update-begin---author:cursor ---date:20260525 for【XSLMES-20260525-A50】选料弹窗自动/人工称量列与种类映射-----------
/** 与配合示方「自动/人工」列相同字典 */
export const MIXING_MATERIAL_PICKER_WEIGH_MODE_DICT = 'xslmes_formula_spec_weigh_mode';
/** 选料弹窗表格列(隐藏 ERP 编号,新增种类列与自动/人工称量) */
export const mixingMaterialPickerTableColumns: BasicColumn[] = [
{ title: '物料编码', align: 'center', width: 120, dataIndex: 'materialCode' },
{ title: '物料名称', align: 'center', width: 160, dataIndex: 'materialName' },
{ title: '自动/人工称量', align: 'center', width: 132, dataIndex: 'pickerWeighMode' },
{ title: '种类', align: 'center', width: 100, dataIndex: 'pickerMaterialKind' },
{ title: '物料大类', align: 'center', width: 120, dataIndex: 'majorCategoryId_dictText' },
{ title: '物料小类', align: 'center', width: 120, dataIndex: 'minorCategoryId_dictText' },
{ title: '物料描述', align: 'center', width: 180, ellipsis: true, dataIndex: 'materialDesc' },
];
//update-end---author:cursor ---date:20260525 for【XSLMES-20260525-A50】选料弹窗自动/人工称量列与种类映射-----------
/** 选择密炼物料后回填混炼示方橡胶及配合剂明细行 */
export function applyMixingMaterialFromSelection(row: Recordable, material: Recordable, materialKind: string) {
if (!row || !material) {
return;
}
//update-begin---author:cursor ---date:20260525 for【XSLMES-20260525-A55】选料回填密炼物料ID-----------
row.mixerMaterialId = material.id || material.mixerMaterialId || '';
//update-end---author:cursor ---date:20260525 for【XSLMES-20260525-A55】选料回填密炼物料ID-----------
row.mixerMaterialName = material.materialName || material.materialCode || '';
row.mixerMaterialDesc = material.materialDesc || material.materialName || material.materialCode || '';
row.materialMajor = material.majorCategoryId_dictText || '';
row.materialMinor = material.minorCategoryId_dictText || '';
//update-begin---author:cursor ---date:20260525 for【XSLMES-20260525-A53】种类仅读配置表移除小类名兜底-----------
row.materialKind = materialKind != null && String(materialKind).trim() !== '' ? String(materialKind).trim() : '';
//update-end---author:cursor ---date:20260525 for【XSLMES-20260525-A53】种类仅读配置表移除小类名兜底-----------
}
//update-end---author:cursor ---date:20260525 for【XSLMES-20260525-A50】混炼示方密炼物料选料弹窗与种类解析-----------
//update-begin---author:cursor ---date:20260601 for【XSLMES-20260601-A62】选料弹窗新增胶料页签查询胶料信息-----------
/** 选料弹窗「胶料」页签表格列(数据源为胶料信息 mes_material */
export const mixingRubberPickerTableColumns: BasicColumn[] = [
{ title: '胶料编码', align: 'center', width: 140, dataIndex: 'materialCode' },
{ title: '胶料名称', align: 'center', width: 180, dataIndex: 'materialName' },
//update-begin---author:cursor ---date:20260601 for【XSLMES-20260601-A62】胶料页签补齐自动/人工称量列-----------
{ title: '自动/人工称量', align: 'center', width: 132, dataIndex: 'pickerWeighMode' },
//update-end---author:cursor ---date:20260601 for【XSLMES-20260601-A62】胶料页签补齐自动/人工称量列-----------
{ title: '种类', align: 'center', width: 100, dataIndex: 'pickerMaterialKind' },
{ title: '胶料类别', align: 'center', width: 140, dataIndex: 'categoryId_dictText' },
{ title: '胶料别名', align: 'center', width: 140, dataIndex: 'aliasName' },
];
/** 选择胶料信息后回填混炼示方橡胶及配合剂明细行(复用 mixerMaterialId 存 mes_material.id */
export function applyMixingRubberFromSelection(row: Recordable, material: Recordable, materialKind: string) {
if (!row || !material) {
return;
}
row.mixerMaterialId = material.id || '';
row.mixerMaterialName = material.materialName || material.materialCode || '';
row.mixerMaterialDesc = material.materialName || material.materialCode || '';
row.materialMajor = '';
row.materialMinor = material.categoryId_dictText || '';
row.materialKind = materialKind != null && String(materialKind).trim() !== '' ? String(materialKind).trim() : '';
}
//update-end---author:cursor ---date:20260601 for【XSLMES-20260601-A62】选料弹窗新增胶料页签查询胶料信息-----------
//update-begin---author:cursor ---date:20260526 for【XSLMES-20260526-A58】参照历史混合步骤选择弹窗-----------
/** 混合步骤可复制的业务字段 */
export const MIXING_STEP_COPY_FIELD_KEYS = [
'sortNo',
'actionName',
'actionSec',
'protectSec',
'tempC',
'powerKw',
'energyKwh',
'comboMode',
'speedRpm',
'pressureMpa',
'boltPercent',
] as const;
/** 判断混合步骤行是否含有效业务数据 */
export function isMixingStepRowFilled(row: Recordable = {}): boolean {
return MIXING_STEP_COPY_FIELD_KEYS.some((key) => {
const value = row[key];
return value != null && value !== '';
});
}
/** 从历史混炼示方混合步骤克隆为新行(生成新 id剔除主表/审计字段) */
export function cloneMixingHistoryStepRows(sourceRows: Recordable[] = []): Recordable[] {
return (sourceRows || [])
.filter((row) => isMixingStepRowFilled(row))
.map((row, index) => {
const next: Recordable = { id: buildUUID() };
MIXING_STEP_COPY_FIELD_KEYS.forEach((key) => {
const value = row[key];
if (value != null && value !== '') {
next[key] = value;
}
});
if (next.sortNo == null) {
next.sortNo = index + 1;
}
return next;
});
}
/** 解析混炼示方配方状态(参照旧系统展示) */
export function resolveMixingSpecFormulaStatus(record: Recordable = {}): string {
if (Number(record.delFlag) === 1) {
return '作废';
}
if (record.approveTime) {
return '审批通过';
}
if (record.auditTime) {
return '审核通过';
}
if (record.proofreadTime) {
return '校对通过';
}
return '编制中';
}
/** 混炼示方是否允许编辑/删除与配合示方一致密炼PS校对后锁定 */
export function isMixingSpecEditable(record: Recordable = {}): boolean {
return !record?.proofreadBy && !record?.proofreadTime;
}
/** 参照历史混合步骤:混炼示方选择列表列 */
export const mixingSpecHistorySelectColumns: BasicColumn[] = [
{ title: '示方编号', align: 'center', dataIndex: 'specName', width: 160 },
{ title: '机台', align: 'center', dataIndex: 'machineName', width: 100 },
{ title: '发行编号', align: 'center', dataIndex: 'issueNumber', width: 150 },
{ title: '发行日期', align: 'center', dataIndex: 'makeDate', width: 120 },
{
title: '配方状态',
align: 'center',
dataIndex: 'formulaStatus',
width: 100,
customRender: ({ record }) => resolveMixingSpecFormulaStatus(record),
},
];
//update-end---author:cursor ---date:20260526 for【XSLMES-20260526-A58】参照历史混合步骤选择弹窗-----------
//update-begin---author:cursor ---date:20260526 for【XSLMES-20260526-A59】规格点击选择混炼示方及参照新增-----------
/** 参照新增时需剔除的主表字段 */
export const MIXING_SPEC_MAIN_STRIP_FIELDS = [
'id',
'createBy',
'createTime',
'updateBy',
'updateTime',
'draftBy',
'draftTime',
'proofreadBy',
'proofreadTime',
'auditBy',
'auditTime',
'approveBy',
'approveTime',
'changeDate',
'delFlag',
'tenantId',
'sysOrgCode',
] as const;
/** 参照新增时需剔除的明细字段 */
export const MIXING_SPEC_CHILD_STRIP_FIELDS = [
'id',
'mixingSpecId',
'createBy',
'createTime',
'updateBy',
'updateTime',
'tenantId',
] as const;
/** 参照选中示方新增:复制除 ID 及审计字段外的全部数据 */
export function cloneMixingSpecPageForReferenceAdd(source: Recordable = {}): Recordable {
const main: Recordable = { ...source };
MIXING_SPEC_MAIN_STRIP_FIELDS.forEach((key) => delete main[key]);
main.id = '';
const cloneChildList = (rows: Recordable[] = []) =>
normalizeMixingDetailRows(
(rows || []).map((row) => {
const next: Recordable = { ...row };
MIXING_SPEC_CHILD_STRIP_FIELDS.forEach((key) => delete next[key]);
delete next.baseUnitWeight;
return next;
}),
);
return {
...main,
materialList: cloneChildList(source.materialList),
stepList: cloneChildList(source.stepList),
downStepList: cloneChildList(source.downStepList),
tcuList: cloneChildList(source.tcuList),
};
}
//update-end---author:cursor ---date:20260526 for【XSLMES-20260526-A59】规格点击选择混炼示方及参照新增-----------