/** * 解析原始记录模板中的检验值批注。 * 兼容旧格式“检验值”,并支持:检验值,2、检验值,2,科学计数法。 */ export function parseInspectionValueAnnotation(annotation) { if (typeof annotation !== 'string' || !annotation.includes('检验值')) return null; // 批注允许中文逗号、英文逗号以及“和”连接格式参数。 const parts = annotation .replace(/,/g, ',') .replace(/\s+/g, '') .replace(/和/g, ',') .split(',') .map(item => item.trim()) .filter(Boolean); const decimalIndex = parts.findIndex((item, index) => index > 0 && /^\d+$/.test(item)); const decimalPlaces = decimalIndex < 0 ? null : Number(parts[decimalIndex]); if (decimalPlaces != null && (!Number.isInteger(decimalPlaces) || decimalPlaces < 0 || decimalPlaces > 15)) { return { error: '小数位数必须是 0 到 15 的整数' }; } const scientificIndex = parts.findIndex((item, index) => index > 0 && item === '科学计数法'); const displayMode = scientificIndex >= 0 ? 'SCIENTIFIC' : 'NORMAL'; const roundingNames = { '四舍五入': 'HALF_UP', '四舍六入': 'HALF_UP_6', '向上舍入': 'CEILING', '向下舍入': 'FLOOR', '截断': 'DOWN' }; const roundingPart = parts.slice(2).find(item => Object.prototype.hasOwnProperty.call(roundingNames, item)); const unsupported = parts.slice(1).filter((item, index) => item && index + 1 !== decimalIndex && item !== '科学计数法' && !roundingNames[item] ); if (unsupported.length || parts.length > 4) { return { error: '检验值批注格式应为:检验值,2,四舍五入 或 检验值,2,科学计数法,四舍五入' }; } return { decimalPlaces, displayMode, // 参数顺序决定处理顺序:科学计数法在小数位前表示先转科学计数法。 scientificFirst: displayMode === 'SCIENTIFIC' && scientificIndex < decimalIndex, roundingMode: roundingPart ? roundingNames[roundingPart] : 'HALF_UP' }; } export function formatInspectionValue(value, config) { if (!config || config.error || config.decimalPlaces == null || value === '' || value == null) return value; const number = Number(value); if (!Number.isFinite(number)) return value; // 科学计数法在批注中排在小数位数前时,先转科学计数法再保留尾数位数。 if (config.displayMode === 'SCIENTIFIC' && config.scientificFirst) { const scientific = number.toExponential(config.decimalPlaces); return scientific.replace('e', 'E'); } const factor = 10 ** config.decimalPlaces; let rounded; if (config.roundingMode === 'CEILING') rounded = Math.ceil(number * factor) / factor; else if (config.roundingMode === 'FLOOR') rounded = Math.floor(number * factor) / factor; else if (config.roundingMode === 'DOWN') rounded = (number < 0 ? Math.ceil(number * factor) : Math.floor(number * factor)) / factor; else if (config.roundingMode === 'HALF_UP_6') { const absolute = Math.abs(number) * factor; const base = Math.floor(absolute); const fraction = absolute - base; rounded = (number < 0 ? -1 : 1) * ((fraction >= 0.6 ? base + 1 : base) / factor); } else rounded = Math.round((number + (number >= 0 ? Number.EPSILON : -Number.EPSILON)) * factor) / factor; const fixed = rounded.toFixed(config.decimalPlaces); if (config.displayMode === 'SCIENTIFIC') return Number(fixed).toExponential(config.decimalPlaces).replace('e', 'E'); return fixed; } export function validateInspectionValueAnnotation(annotation) { const config = parseInspectionValueAnnotation(annotation); return config && config.error ? config.error : null; }