/**
|
|
* 浓度单位转换 (同一类别内转换)
|
|
*/
|
|
|
|
class ConcentrationConverter {
|
|
// 单位换算系数
|
|
unitFactors = {
|
|
// 质量单位(以g为基准)
|
|
'g': 1,
|
|
'mg': 1e-3,
|
|
'μg': 1e-6,
|
|
'ug': 1e-6, // μg的别名
|
|
'ng': 1e-9,
|
|
'pg': 1e-12,
|
|
'fg': 1e-15,
|
|
|
|
// 体积单位(以L为基准)
|
|
'L': 1,
|
|
'mL': 1e-3,
|
|
'μL': 1e-6,
|
|
'uL': 1e-6, // μL的别名
|
|
'nL': 1e-9,
|
|
'pL': 1e-12,
|
|
'fL': 1e-15,
|
|
|
|
// 摩尔单位(以mol为基准)
|
|
'mol': 1,
|
|
'mmol': 1e-3,
|
|
'μmol': 1e-6,
|
|
'umol': 1e-6, // μmol的别名
|
|
'nmol': 1e-9,
|
|
'pmol': 1e-12,
|
|
'fmol': 1e-15,
|
|
|
|
// 活性单位(以U为基准)
|
|
'U': 1,
|
|
'IU': 1, // 假设1 IU = 1 U
|
|
'mU': 1e-3,
|
|
'mIU': 1e-3,
|
|
};
|
|
|
|
// 支持的浓度单位(按类别分组)
|
|
unitCategories = {
|
|
'mass': ['mg/mL', 'μg/mL', 'ug/mL', 'ng/mL', 'pg/mL', 'fg/mL', 'g/L', 'mg/L', 'μg/L'],
|
|
'mole': ['mol/mL', 'mol/L', 'mmol/L', 'μmol/L', 'umol/L', 'nmol/L', 'pmol/L', 'mmol/mL', 'μmol/mL'],
|
|
'activity': ['U/mL', 'IU/mL', 'U/L', 'IU/L'],
|
|
'percent': ['%']
|
|
};
|
|
|
|
/**
|
|
* 规范化单位(统一别名)
|
|
*/
|
|
normalizeUnit(unit) {
|
|
const aliasMap = {
|
|
'ug/mL': 'μg/mL',
|
|
'umol/L': 'μmol/L',
|
|
'uL': 'μL',
|
|
'ul': 'μL',
|
|
'IU/mL': 'U/mL',
|
|
'IU/L': 'U/L'
|
|
};
|
|
return aliasMap[unit] || unit;
|
|
}
|
|
|
|
/**
|
|
* 获取单位类别
|
|
*/
|
|
getUnitCategory(unit) {
|
|
const normalizedUnit = this.normalizeUnit(unit);
|
|
|
|
for (const [category, units] of Object.entries(this.unitCategories)) {
|
|
if (units.includes(normalizedUnit)) {
|
|
return category;
|
|
}
|
|
}
|
|
|
|
throw new Error(`不支持的浓度单位: ${unit}`);
|
|
}
|
|
|
|
/**
|
|
* 检查两个单位是否属于同一类别
|
|
*/
|
|
isSameCategory(unit1, unit2) {
|
|
try {
|
|
const cat1 = this.getUnitCategory(unit1);
|
|
const cat2 = this.getUnitCategory(unit2);
|
|
return cat1 === cat2;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 浓度单位转换(核心方法)
|
|
* @param {number|string} value - 待转换的值
|
|
* @param {string} targetUnit - 目标单位
|
|
* @returns {number} 转换后的值
|
|
*/
|
|
convert(value, targetUnit) {
|
|
// 1. 解析输入值
|
|
let inputValue, inputUnit;
|
|
|
|
if (typeof value === 'string') {
|
|
const match = value.toString().trim().match(/^([\d.]+(?:[eE][+-]?\d+)?)(?:\s*([a-zA-Zμ/%]+))?$/);
|
|
if (!match) {
|
|
throw new Error(`输入格式错误: ${value}`);
|
|
}
|
|
inputValue = parseFloat(match[1]);
|
|
inputUnit = match[2] ? this.normalizeUnit(match[2]) : '';
|
|
} else if (typeof value === 'number') {
|
|
inputValue = value;
|
|
inputUnit = ''; // 无量纲
|
|
} else {
|
|
throw new Error('输入值必须是数字或字符串');
|
|
}
|
|
|
|
const normalizedTargetUnit = this.normalizeUnit(targetUnit);
|
|
|
|
// 2. 检查是否需要转换
|
|
if (!inputUnit) {
|
|
return inputValue; // 无量纲输入,直接返回
|
|
}
|
|
|
|
if (inputUnit === normalizedTargetUnit) {
|
|
return inputValue; // 相同单位,直接返回
|
|
}
|
|
|
|
// 3. 检查是否为%单位
|
|
if (inputUnit === '%' || normalizedTargetUnit === '%') {
|
|
// %单位是单独的系列,不进行转换
|
|
throw new Error(`单位类别不匹配: ${inputUnit} 不能转换为 ${targetUnit}`);
|
|
}
|
|
|
|
// 4. 验证单位类别
|
|
if (!this.isSameCategory(inputUnit, normalizedTargetUnit)) {
|
|
throw new Error(`单位类别不匹配: ${inputUnit} 不能转换为 ${targetUnit}`);
|
|
}
|
|
|
|
// 5. 执行转换
|
|
return this._convertValue(inputValue, inputUnit, normalizedTargetUnit);
|
|
}
|
|
|
|
/**
|
|
* 内部转换逻辑
|
|
*/
|
|
_convertValue(value, fromUnit, toUnit) {
|
|
// 解析浓度单位:分子/分母
|
|
const [fromNum, fromDen] = fromUnit.split('/');
|
|
const [toNum, toDen] = toUnit.split('/');
|
|
|
|
// 获取各部分的换算系数
|
|
const numFactor = this.unitFactors[fromNum] / this.unitFactors[toNum];
|
|
const denFactor = this.unitFactors[toDen] / this.unitFactors[fromDen];
|
|
|
|
// 计算总转换系数
|
|
return value * numFactor * denFactor;
|
|
}
|
|
|
|
/**
|
|
* 批量转换
|
|
*/
|
|
convertAll(values, targetUnit) {
|
|
if (!Array.isArray(values)) {
|
|
throw new Error('输入必须是数组');
|
|
}
|
|
|
|
return values.map(value => this.convert(value, targetUnit));
|
|
}
|
|
|
|
/**
|
|
* 验证单位是否支持
|
|
*/
|
|
isSupportedUnit(unit) {
|
|
try {
|
|
this.getUnitCategory(unit);
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
export const convertConcentration = new ConcentrationConverter();
|