/**
|
* 解析原始记录模板中的检验值批注。
|
* 兼容旧格式“检验值”,并支持:检验值,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;
|
}
|