/** * 体积单位转换 * 支持的体积单位:pL, nL, uL (μL), mL, L */ class VolumeConverter { // 体积单位换算系数(以升L为基准) unitFactors = { 'L': 1, 'mL': 1e-3, // 毫升 'μL': 1e-6, // 微升 'uL': 1e-6, // 微升(别名) 'nL': 1e-9, // 纳升 'pL': 1e-12 // 皮升 }; // 支持的单位列表 supportedUnits = ['L', 'mL', 'μL', 'uL', 'nL', 'pL']; /** * 规范化单位(统一别名) */ normalizeUnit(unit) { const aliasMap = { 'ul': 'uL', 'Ul': 'uL', 'UL': 'uL', 'μl': 'μL', 'Μl': 'μL', 'ΜL': 'μL' }; // 转换为标准形式 let normalized = unit; // 处理μ符号的各种表示 if (unit.toLowerCase() === 'ul' || unit === 'μL' || unit === 'uL') { normalized = 'μL'; } // 其他别名处理 else if (aliasMap[unit]) { normalized = aliasMap[unit]; } return normalized; } /** * 验证单位是否支持 */ isSupportedUnit(unit) { const normalizedUnit = this.normalizeUnit(unit); return this.supportedUnits.includes(normalizedUnit); } /** * 获取所有支持的单位 */ getSupportedUnits() { return [...this.supportedUnits]; } /** * 体积单位转换(核心方法) * @param {number|string} value - 待转换的值 * @param {string} targetUnit - 目标单位 * @returns {number} 转换后的值 */ convert(value, targetUnit) { // 1. 解析输入值 let inputValue, inputUnit; if (typeof value === 'string') { const result = this.parseValue(value); inputValue = result.value; inputUnit = result.unit; } else if (typeof value === 'number') { inputValue = value; inputUnit = ''; // 无量纲 } else { throw new Error('输入值必须是数字或字符串'); } const normalizedTargetUnit = this.normalizeUnit(targetUnit); // 2. 验证目标单位 if (!this.isSupportedUnit(normalizedTargetUnit)) { throw new Error(`不支持的单位: ${targetUnit}。支持的单位: ${this.supportedUnits.join(', ')}`); } // 3. 检查是否需要转换 if (!inputUnit) { return inputValue; // 无量纲输入,直接返回 } if (inputUnit === normalizedTargetUnit) { return inputValue; // 相同单位,直接返回 } // 4. 执行转换 return this._convertValue(inputValue, inputUnit, normalizedTargetUnit); } /** * 解析带单位的数值字符串 */ parseValue(valueStr) { const str = valueStr.toString().trim(); // 支持科学计数法 const match = str.match(/^([+-]?\d*\.?\d+(?:[eE][+-]?\d+)?)\s*([a-zA-Zμ]+)$/); if (!match) { throw new Error(`格式错误: ${valueStr}。正确格式如: "10mL", "5.5μL", "1.23e-6L"`); } const value = parseFloat(match[1]); const rawUnit = match[2]; const unit = this.normalizeUnit(rawUnit); // 验证单位是否支持 if (!this.isSupportedUnit(unit)) { throw new Error(`不支持的单位: ${rawUnit}。支持的单位: ${this.supportedUnits.join(', ')}`); } return { value, unit }; } /** * 内部转换逻辑 */ _convertValue(value, fromUnit, toUnit) { // 统一处理μL的多种表示 const normalizedFromUnit = this.normalizeUnit(fromUnit); const normalizedToUnit = this.normalizeUnit(toUnit); // 获取换算系数 const fromFactor = this.unitFactors[normalizedFromUnit]; const toFactor = this.unitFactors[normalizedToUnit]; if (fromFactor === undefined || toFactor === undefined) { throw new Error(`单位换算系数未定义: ${fromUnit} 或 ${toUnit}`); } // 转换公式:value * (fromFactor / toFactor) return value * (fromFactor / toFactor); } /** * 批量转换 */ convertAll(values, targetUnit) { if (!Array.isArray(values)) { throw new Error('输入必须是数组'); } return values.map(value => this.convert(value, targetUnit)); } /** * 格式化输出 */ format(value, unit, precision = 6) { const numValue = typeof value === 'string' ? parseFloat(value) : value; if (isNaN(numValue)) { return 'NaN'; } // 根据数值大小选择合适的显示格式 const absValue = Math.abs(numValue); if (absValue === 0) { return `0 ${unit}`; } if (absValue < 0.001 || absValue >= 1000000) { return `${numValue.toExponential(precision)} ${unit}`; } // 确定小数位数 let decimalPlaces = precision; if (absValue < 1) { decimalPlaces = Math.max(precision, Math.ceil(-Math.log10(absValue)) + 2); } else if (absValue >= 100) { decimalPlaces = Math.max(0, precision - 2); } return `${numValue.toFixed(decimalPlaces)} ${unit}`; } /** * 获取单位换算关系 */ getConversionFactor(fromUnit, toUnit) { const normalizedFromUnit = this.normalizeUnit(fromUnit); const normalizedToUnit = this.normalizeUnit(toUnit); if (!this.isSupportedUnit(normalizedFromUnit) || !this.isSupportedUnit(normalizedToUnit)) { throw new Error('单位不支持'); } const fromFactor = this.unitFactors[normalizedFromUnit]; const toFactor = this.unitFactors[normalizedToUnit]; return fromFactor / toFactor; } /** * 创建快捷转换方法 */ createShortcutMethods() {`` const shortcuts = {}; this.supportedUnits.forEach(unit => { const methodName = `to${unit.replace('μ', 'u')}`; shortcuts[methodName] = (value) => this.convert(value, unit); }); return shortcuts; } } export const volumeConverter = new VolumeConverter();