|
|
@ -0,0 +1,223 @@ |
|
|
|
|
|
/** |
|
|
|
|
|
* 体积单位转换 |
|
|
|
|
|
* 支持的体积单位: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(); |