优化混炼示方,新增种类配置

This commit is contained in:
geht
2026-05-25 19:44:14 +08:00
parent c85657d199
commit dc3f305303
34 changed files with 3892 additions and 104 deletions

View File

@@ -91,10 +91,27 @@ export const materialColumns: JVxeColumn[] = [
{ title: '物料大类', key: 'materialMajor', type: JVxeTypes.input, width: 100, minWidth: MIXING_MATERIAL_MIN_COLUMN_WIDTH },
{ title: '物料小类', key: 'materialMinor', type: JVxeTypes.input, width: 120, minWidth: MIXING_MATERIAL_MIN_COLUMN_WIDTH },
{ title: '种类', key: 'materialKind', type: JVxeTypes.input, width: 80, minWidth: MIXING_MATERIAL_MIN_COLUMN_WIDTH },
{ title: '密炼物料名称', key: 'mixerMaterialName', type: JVxeTypes.input, width: 160, minWidth: MIXING_MATERIAL_MIN_COLUMN_WIDTH },
{
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' },
{ title: '累计', key: 'accumWeight', 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' },
];
@@ -196,6 +213,280 @@ export function calcMixingMaterialTableWidth(columns: JVxeColumn[], widthMap: Re
}
//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.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;
/** 重量四舍五入,消除浮点累加误差 */
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;
@@ -412,6 +703,9 @@ 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) {
@@ -637,3 +931,99 @@ export function ensureMixingDetailRows(rows: Recordable[] = [], defaultCount: nu
}
//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】选料弹窗小类树为空时重置隐藏配置-----------
/** 解析混炼示方明细种类:小类勾选胶料则显示「胶料」,否则显示小类名 */
export function resolveMixingMaterialKindFromCategory(isRubber?: unknown, minorName?: string) {
if (isRubber === '1' || isRubber === 1 || isRubber === true) {
return '胶料';
}
return minorName != null && String(minorName).trim() !== '' ? String(minorName).trim() : '';
}
//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: 120, dataIndex: 'majorCategoryId_dictText' },
{ title: '物料小类', align: 'center', width: 120, dataIndex: 'minorCategoryId_dictText' },
{ title: '物料描述', align: 'center', width: 180, ellipsis: true, dataIndex: 'materialDesc' },
];
/** 配合示方称量方式 -> 混炼示方种类(与后端 resolveWeighModeMaterialKind 一致) */
export function resolveMixingMaterialKindFromWeighMode(weighMode?: string) {
if (weighMode == null || String(weighMode).trim() === '') {
return '';
}
const normalized = String(weighMode).trim();
const lower = normalized.toLowerCase();
if (lower.startsWith('auto') || normalized.includes('自动')) {
return '自动';
}
if (lower === 'manual' || normalized.includes('人工')) {
return '人工';
}
return '';
}
/** 选料确认时种类:称量方式优先,否则按小类胶料/小类名 */
export function resolveMixingMaterialKindForPicker(weighMode: string | undefined, isRubber?: unknown, minorName?: string) {
const fromWeighMode = resolveMixingMaterialKindFromWeighMode(weighMode);
if (fromWeighMode) {
return fromWeighMode;
}
return resolveMixingMaterialKindFromCategory(isRubber, minorName);
}
//update-end---author:cursor ---date:20260525 for【XSLMES-20260525-A50】选料弹窗自动/人工称量列与种类映射-----------
/** 选择密炼物料后回填混炼示方橡胶及配合剂明细行 */
export function applyMixingMaterialFromSelection(row: Recordable, material: Recordable, materialKind: string) {
if (!row || !material) {
return;
}
row.mixerMaterialName = material.materialName || material.materialCode || '';
row.mixerMaterialDesc = material.materialDesc || material.materialName || material.materialCode || '';
row.materialMajor = material.majorCategoryId_dictText || '';
row.materialMinor = material.minorCategoryId_dictText || '';
row.materialKind = materialKind || row.materialMinor || '';
}
//update-end---author:cursor ---date:20260525 for【XSLMES-20260525-A50】混炼示方密炼物料选料弹窗与种类解析-----------