华西海圻ELN前端工程
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

222 lines
5.7 KiB

  1. /**
  2. * 体积单位转换
  3. * 支持的体积单位pL, nL, uL (μL), mL, L
  4. */
  5. class VolumeConverter {
  6. // 体积单位换算系数(以升L为基准)
  7. unitFactors = {
  8. 'L': 1,
  9. 'mL': 1e-3, // 毫升
  10. 'μL': 1e-6, // 微升
  11. 'uL': 1e-6, // 微升(别名)
  12. 'nL': 1e-9, // 纳升
  13. 'pL': 1e-12 // 皮升
  14. };
  15. // 支持的单位列表
  16. supportedUnits = ['L', 'mL', 'μL', 'uL', 'nL', 'pL'];
  17. /**
  18. * 规范化单位统一别名
  19. */
  20. normalizeUnit(unit) {
  21. const aliasMap = {
  22. 'ul': 'uL',
  23. 'Ul': 'uL',
  24. 'UL': 'uL',
  25. 'μl': 'μL',
  26. 'Μl': 'μL',
  27. 'ΜL': 'μL'
  28. };
  29. // 转换为标准形式
  30. let normalized = unit;
  31. // 处理μ符号的各种表示
  32. if (unit.toLowerCase() === 'ul' || unit === 'μL' || unit === 'uL') {
  33. normalized = 'μL';
  34. }
  35. // 其他别名处理
  36. else if (aliasMap[unit]) {
  37. normalized = aliasMap[unit];
  38. }
  39. return normalized;
  40. }
  41. /**
  42. * 验证单位是否支持
  43. */
  44. isSupportedUnit(unit) {
  45. const normalizedUnit = this.normalizeUnit(unit);
  46. return this.supportedUnits.includes(normalizedUnit);
  47. }
  48. /**
  49. * 获取所有支持的单位
  50. */
  51. getSupportedUnits() {
  52. return [...this.supportedUnits];
  53. }
  54. /**
  55. * 体积单位转换核心方法
  56. * @param {number|string} value - 待转换的值
  57. * @param {string} targetUnit - 目标单位
  58. * @returns {number} 转换后的值
  59. */
  60. convert(value, targetUnit) {
  61. // 1. 解析输入值
  62. let inputValue, inputUnit;
  63. if (typeof value === 'string') {
  64. const result = this.parseValue(value);
  65. inputValue = result.value;
  66. inputUnit = result.unit;
  67. } else if (typeof value === 'number') {
  68. inputValue = value;
  69. inputUnit = ''; // 无量纲
  70. } else {
  71. throw new Error('输入值必须是数字或字符串');
  72. }
  73. const normalizedTargetUnit = this.normalizeUnit(targetUnit);
  74. // 2. 验证目标单位
  75. if (!this.isSupportedUnit(normalizedTargetUnit)) {
  76. throw new Error(`不支持的单位: ${targetUnit}。支持的单位: ${this.supportedUnits.join(', ')}`);
  77. }
  78. // 3. 检查是否需要转换
  79. if (!inputUnit) {
  80. return inputValue; // 无量纲输入,直接返回
  81. }
  82. if (inputUnit === normalizedTargetUnit) {
  83. return inputValue; // 相同单位,直接返回
  84. }
  85. // 4. 执行转换
  86. return this._convertValue(inputValue, inputUnit, normalizedTargetUnit);
  87. }
  88. /**
  89. * 解析带单位的数值字符串
  90. */
  91. parseValue(valueStr) {
  92. const str = valueStr.toString().trim();
  93. // 支持科学计数法
  94. const match = str.match(/^([+-]?\d*\.?\d+(?:[eE][+-]?\d+)?)\s*([a-zA-Zμ]+)$/);
  95. if (!match) {
  96. throw new Error(`格式错误: ${valueStr}。正确格式如: "10mL", "5.5μL", "1.23e-6L"`);
  97. }
  98. const value = parseFloat(match[1]);
  99. const rawUnit = match[2];
  100. const unit = this.normalizeUnit(rawUnit);
  101. // 验证单位是否支持
  102. if (!this.isSupportedUnit(unit)) {
  103. throw new Error(`不支持的单位: ${rawUnit}。支持的单位: ${this.supportedUnits.join(', ')}`);
  104. }
  105. return { value, unit };
  106. }
  107. /**
  108. * 内部转换逻辑
  109. */
  110. _convertValue(value, fromUnit, toUnit) {
  111. // 统一处理μL的多种表示
  112. const normalizedFromUnit = this.normalizeUnit(fromUnit);
  113. const normalizedToUnit = this.normalizeUnit(toUnit);
  114. // 获取换算系数
  115. const fromFactor = this.unitFactors[normalizedFromUnit];
  116. const toFactor = this.unitFactors[normalizedToUnit];
  117. if (fromFactor === undefined || toFactor === undefined) {
  118. throw new Error(`单位换算系数未定义: ${fromUnit}${toUnit}`);
  119. }
  120. // 转换公式:value * (fromFactor / toFactor)
  121. return value * (fromFactor / toFactor);
  122. }
  123. /**
  124. * 批量转换
  125. */
  126. convertAll(values, targetUnit) {
  127. if (!Array.isArray(values)) {
  128. throw new Error('输入必须是数组');
  129. }
  130. return values.map(value => this.convert(value, targetUnit));
  131. }
  132. /**
  133. * 格式化输出
  134. */
  135. format(value, unit, precision = 6) {
  136. const numValue = typeof value === 'string' ? parseFloat(value) : value;
  137. if (isNaN(numValue)) {
  138. return 'NaN';
  139. }
  140. // 根据数值大小选择合适的显示格式
  141. const absValue = Math.abs(numValue);
  142. if (absValue === 0) {
  143. return `0 ${unit}`;
  144. }
  145. if (absValue < 0.001 || absValue >= 1000000) {
  146. return `${numValue.toExponential(precision)} ${unit}`;
  147. }
  148. // 确定小数位数
  149. let decimalPlaces = precision;
  150. if (absValue < 1) {
  151. decimalPlaces = Math.max(precision, Math.ceil(-Math.log10(absValue)) + 2);
  152. } else if (absValue >= 100) {
  153. decimalPlaces = Math.max(0, precision - 2);
  154. }
  155. return `${numValue.toFixed(decimalPlaces)} ${unit}`;
  156. }
  157. /**
  158. * 获取单位换算关系
  159. */
  160. getConversionFactor(fromUnit, toUnit) {
  161. const normalizedFromUnit = this.normalizeUnit(fromUnit);
  162. const normalizedToUnit = this.normalizeUnit(toUnit);
  163. if (!this.isSupportedUnit(normalizedFromUnit) || !this.isSupportedUnit(normalizedToUnit)) {
  164. throw new Error('单位不支持');
  165. }
  166. const fromFactor = this.unitFactors[normalizedFromUnit];
  167. const toFactor = this.unitFactors[normalizedToUnit];
  168. return fromFactor / toFactor;
  169. }
  170. /**
  171. * 创建快捷转换方法
  172. */
  173. createShortcutMethods() {``
  174. const shortcuts = {};
  175. this.supportedUnits.forEach(unit => {
  176. const methodName = `to${unit.replace('μ', 'u')}`;
  177. shortcuts[methodName] = (value) => this.convert(value, unit);
  178. });
  179. return shortcuts;
  180. }
  181. }
  182. export const volumeConverter = new VolumeConverter();