Skip to content

单位系统

a-calc 提供了完整的单位系统,包括:

  • 单位计算:带单位的数值参与运算
  • 单位转换:不同单位之间的换算(分→元、米→千米等)
  • 单位位置格式化:控制单位在输出中的显示位置

单位计算

启用单位计算

使用 _unit: true 选项启用单位计算:

javascript
import { calc } from 'a-calc'

calc('100元 + 50元', { _unit: true })     // '150元'
calc('10kg + 5kg', { _unit: true })       // '15kg'
calc('50% + 30%', { _unit: true })        // '80%'

单位提取规则

a-calc 会自动从数值后面提取单位:

javascript
calc('100元', { _unit: true })            // '100元'
calc('99.9折', { _unit: true })           // '99.9折'
calc('15%', { _unit: true })              // '15%'
calc('3.14π', { _unit: true })            // '3.14π'

注意

单位必须紧跟在数字后面,不能有空格:

javascript
calc('100元', { _unit: true })   // ✓ 正确
calc('100 元', { _unit: true })  // ✗ 错误,单位会被忽略

复杂表达式

单位计算支持复杂的数学表达式:

javascript
calc('(100元 + 50元) * 2', { _unit: true })     // '300元'
calc('100元 * 3 - 50元', { _unit: true })       // '250元'
calc('1000元 / 4', { _unit: true })             // '250元'
calc('2 ** 10 字节', { _unit: true })           // '1024字节'

变量中的单位

变量值也可以包含单位:

javascript
calc('price + discount', {
  price: '100元',
  discount: '20元',
  _unit: true
})  // '120元'

calc('total * rate', {
  total: '1000元',
  rate: 0.8,
  _unit: true
})  // '800元'

移除单位

使用 !u!uh(无参数时)移除结果中的单位:

javascript
calc('100元 + 50元', { _unit: true })           // '150元'
calc('100元 + 50元 | !u', { _unit: true })      // '150'
calc('100元 + 50元 | !uh', { _unit: true })     // '150' (等效)

单位与格式化结合

单位计算可以与格式化选项一起使用:

javascript
// 保留小数
calc('100.5元 + 50.3元 | =2', { _unit: true })  // '150.80元'

// 千分位
calc('1000元 + 500元 | ,', { _unit: true })     // '1,500元'

// 组合
calc('10000元 * 1.5 | =2,', { _unit: true })    // '15,000.00元'

单位位置格式化

单位位置语法用于在格式化结果中添加货币符号或其他单位,并控制其显示位置。

语法概览

语法说明示例
!u:单位显示单位(位置可配置)100100元
!ua:单位单位在数字 (after)100100元
!ub:单位单位在数字 (before)100$100
!um:单位单位在中间 (middle)-100-$100
!uh:单位只转换,不显示单位100分1

!u:unit - 显示单位(推荐)

最简洁的单位语法,默认单位在数字后面:

javascript
calc('100 | !u:元')              // '100元'
calc('100.5 | =2 !u:元')         // '100.50元'

可通过 set_config 设置全局默认位置:

javascript
import { calc, set_config } from 'a-calc'

set_config({ _unit_default_position: 'before' })
calc('100 | !u:$')   // '$100'

set_config({ _unit_default_position: 'middle' })
calc('-100 | + !u:$') // '-$100'

set_config({ _unit_default_position: 'after' })
calc('100 | !u:元')   // '100元'

!ua:unit - 单位在数字后 (after)

javascript
calc('100 | !ua:元')             // '100元'
calc('-50 | + !ua:元')           // '-50元'
calc('1234.5 | =2, !ua:元')      // '1,234.50元'

!ub:unit - 单位在数字前 (before)

javascript
calc('100 | !ub:$')              // '$100'
calc('-50 | !ub:$')              // '$-50'
calc('100 | + !ub:$')            // '$+100'
calc('1234.5 | =2, !ub:$')       // '$1,234.50'

!um:unit - 单位在中间 (middle)

将货币符号放在正负号和数字之间,常用于金融数据展示:

javascript
calc('8 | + !um:$')              // '+$8'
calc('-6 | + !um:$')             // '-$6'

// 不同货币
calc('100 | + !um:¥')            // '+¥100'
calc('-50 | + !um:€')            // '-€50'

// 结合千分位和小数
calc('12345 | +, !um:$')         // '+$12,345'
calc('-12345.67 | +=2, !um:$')   // '-$12,345.67'

// 多字符单位
calc('100 | + !um:USD')          // '+USD100'

中间位置说明

当没有符号时,!um 效果等同于 !ub

javascript
calc('100 | !um:$')              // '$100' (无符号,同 !ub)
calc('100 | + !um:$')            // '+$100' (有符号,在中间)

!uh:unit - 只转换不显示

进行单位转换但不在结果中显示单位:

javascript
calc('100 | =2 !uh:元', {
  _unit_convert_out: { '元': { '分': 0.01 } }
})  // '1.00' (100分转换为1元,但不显示"元")

位置对比

javascript
const value = '-1234.56'

calc(`${value} | + !ua:$`)       // '-1234.56$'  (数字后)
calc(`${value} | + !ub:$`)       // '$-1234.56'  (数字前,符号在单位后)
calc(`${value} | + !um:$`)       // '-$1234.56'  (中间,符号在单位前)

@ 变量引用

使用 @ 前缀在单位语法中引用变量值:

javascript
// 基础引用
calc('100 | !ua:@unit', { unit: '元' })    // '100元'
calc('100 | !ub:@currency', { currency: '$' })  // '$100'

// 变量路径
calc('100 | !ua:@config.unit', {
  config: { unit: '元' }
})  // '100元'

// 结合转换
calc('100 | =2 !ua:@outUnit', {
  outUnit: '元',
  _unit_convert_out: { '元': { '分': 0.01 } }
})  // '1.00元'

// 同时指定输入输出单位
calc('100 | =2 !ua:@outUnit:@inUnit', {
  outUnit: '元',
  inUnit: '分',
  _unit_convert_out: { '元': { '分': 0.01, '厘': 0.001 } }
})  // '1.00元'

单位转换

单位转换用于将数值从一个单位换算到另一个单位(如分→元、米→千米)。

配置方式

_unit_convert_out(推荐)

输出单位为 key 配置转换规则:

javascript
calc('100 | =2 !ua:元', {
  _unit_convert_out: {
    '元': { '分': 0.01 }  // 分 × 0.01 = 元
  }
})  // '1.00元' (100分 → 1元)

_unit_convert_in

输入单位为 key 配置转换规则(会自动转换为 _unit_convert_out 格式):

javascript
calc('100 | =2 !ua:元', {
  _unit_convert_in: {
    '分': { '元': 0.01 }
  }
})  // '1.00元'

推荐使用 _unit_convert_out

_unit_convert_out 以输出单位为 key,查找时间复杂度为 O(1),性能更优。

语法方式

在格式字符串中直接指定转换:

javascript
// 只指定输出单位(自动查找输入单位)
calc('100 | =2 !ua:元', {
  _unit_convert_out: { '元': { '分': 0.01 } }
})  // '1.00元'

// 同时指定输出和输入单位
calc('100 | =2 !ua:元:分', {
  _unit_convert_out: { '元': { '分': 0.01, '厘': 0.001 } }
})  // '1.00元'

// 只转换不显示单位
calc('100 | =2 !uh:元', {
  _unit_convert_out: { '元': { '分': 0.01 } }
})  // '1.00'

转换规则格式

转换规则支持多种格式:

javascript
_unit_convert_out: {
  '元': {
    // 1. 数字:直接作为乘数
    '分': 0.01,  // value × 0.01

    // 2. 对象 mul:乘以指定值
    '角': { mul: 0.1 },  // value × 0.1

    // 3. 对象 div:除以指定值
    '厘': { div: 1000 },  // value ÷ 1000

    // 4. 对象组合:mul/div → plus/minus
    '特殊': {
      mul: 0.01,   // 先乘
      plus: 1      // 再加
    },

    // 5. 函数:完全自定义
    'CNY': (value, inputUnit, outputUnit) => value / 7.2,

    // 6. 对象 fn:自定义函数
    'USD': {
      fn: (value, inputUnit, outputUnit) => value / 7.2
    }
  }
}

对象规则执行顺序mul/divplus/minus

javascript
// 例:华氏度转摄氏度 C = (F - 32) × 5/9
'℃': {
  '℉': {
    minus: 32,        // 先减32
    mul: 5/9          // 这样写不对!
  }
}

// 正确写法:使用函数
'℃': {
  '℉': (f) => (f - 32) * 5 / 9
}

纯配置模式

不使用语法,完全通过配置项控制转换:

javascript
calc('100 | =2', {
  _unit_convert_out: { '元': { '分': 0.01 } },
  _unit_default_out: '元',      // 默认输出单位
  _unit_default_in: '分',    // 默认输入单位
  _unit_default_position: 'after'      // 单位位置
})  // '1.00元'

多输入单位选择

当一个输出单位对应多个输入单位时,使用 _unit_default_in 指定默认输入单位:

javascript
// 元可以从"分"或"厘"转换
calc('100 | =2 !ua:元', {
  _unit_convert_out: { '元': { '分': 0.01, '厘': 0.001 } },
  _unit_default_in: '分'  // 默认从"分"转换
})  // '1.00元' (100分 → 1元)

calc('100 | =3 !ua:元', {
  _unit_convert_out: { '元': { '分': 0.01, '厘': 0.001 } },
  _unit_default_in: '厘'  // 默认从"厘"转换
})  // '0.100元' (100厘 → 0.1元)

// 可以是数组,按顺序优先匹配
calc('100 | =2 !ua:元', {
  _unit_convert_out: { '元': { '分': 0.01, '厘': 0.001 } },
  _unit_default_in: ['分', '厘']  // 优先"分"
})  // '1.00元'

智能匹配优先级

当只指定输出单位时,a-calc 按以下顺序匹配输入单位:

  1. 语法显式指定(最高优先级)

    javascript
    calc('100 | !ua:元:厘', { ... })  // 使用"厘"
  2. _unit_default_in 配置

    javascript
    calc('100 | !ua:元', {
      _unit_default_in: '分'
    })  // 使用"分"
  3. 单一输入单位(自动使用)

    javascript
    calc('100 | !ua:元', {
      _unit_convert_out: { '元': { '分': 0.01 } }  // 只有"分"
    })  // 自动使用"分"
  4. 多个输入单位且无法确定:不进行转换

单位位置映射

可以为不同的单位预设不同的显示位置。

_unit_convert_out 中配置

使用 _position 字段:

javascript
calc('100 | =2 !u:元', {
  _unit_convert_out: {
    '元': {
      '分': 0.01,
      _position: 'after'  // 元在数字后
    },
    '$': {
      '¢': 0.01,
      _position: 'before'  // $ 在数字前
    }
  }
})  // '1.00元'

calc('100 | =2 !u:$', {
  _unit_convert_out: {
    '$': {
      '¢': 0.01,
      _position: 'before'
    }
  }
})  // '$1.00'

输入单位也可以有位置配置:

javascript
calc('100 | !u:分', {
  _unit_convert_out: {
    '元': {
      '分': {
        mul: 0.01,
        _position: 'after'  // 分的位置配置
      }
    }
  }
})  // '100分'

使用 _unit_position_map

直接配置单位到位置的映射(优先级高于 _unit_convert_out 中的配置):

javascript
calc('100 | !u:$', {
  _unit_position_map: {
    '$': 'before',
    '€': 'middle',
    '元': 'after'
  }
})  // '$100'

calc('-100 | + !u:€', {
  _unit_position_map: { '€': 'middle' }
})  // '-€100'

位置优先级

从低到高:

  1. _unit_default_position(全局默认)
  2. _unit_convert_out 中的 _position
  3. _unit_position_map
  4. 显式语法 !ua: / !ub: / !um:(最高)
javascript
calc('100 | !ua:$', {
  _unit_position_map: { '$': 'before' }  // 被 !ua 覆盖
})  // '100$' (显式 !ua 优先)

单位与千分位关联

可以为不同的单位配置不同的千分位格式,详见 千分位格式化 - 单位关联

javascript
calc('1234567.89 | , !ua:€', {
  _unit_thousands_map: { '€': 'eu' }
})  // '1.234.567,89€' (欧洲格式)

calc('1234567.89 | , !ua:$', {
  _unit_convert_out: { '$': { _thousands: 'en' } }
})  // '1,234,567.89$' (英文/国际格式)

配置项速查

配置项类型说明
_unitboolean启用单位计算模式
_unit_convert_outobject单位转换映射(输出单位为 key)
_unit_convert_inobject单位转换映射(输入单位为 key)
_unit_default_outstring | string[]默认输出单位
_unit_default_instring | string[]默认输入单位
_unit_default_position'after' | 'before' | 'middle'全局单位位置
_unit_position_mapobject单位到位置的映射
_unit_thousands_mapobject单位到千分位预设的映射
格式语法说明
!u移除单位(无参数时)
!u:unit显示单位(位置可配置)
!ua:unit单位在数字后
!ub:unit单位在数字前
!um:unit单位在中间
!uh:unit只转换不显示单位
!u:output:input指定输入输出单位
!u:@var使用变量值作为单位

完整示例

多币种金融系统

javascript
const currencyOptions = {
  _unit_convert_out: {
    '元': {
      '分': 0.01,
      _position: 'after',
      _thousands: 'wan'  // 万进制千分位
    },
    '$': {
      '¢': 0.01,
      _position: 'before',
      _thousands: 'en'
    },
    '€': {
      'cent': 0.01,
      _position: 'before',
      _thousands: 'eu'
    }
  }
}

calc('12345678 | =2, !u:元', currencyOptions)   // '1234,5678.00元'
calc('12345678 | =2, !u:$', currencyOptions)    // '$123,456.78'
calc('12345678 | =2, !u:€', currencyOptions)    // '€123.456,78'

长度单位换算

javascript
const lengthOptions = {
  _unit_convert_out: {
    '千米': {
      '米': { div: 1000 },
      '厘米': { div: 100000 }
    },
    '米': {
      '厘米': { div: 100 },
      '毫米': { div: 1000 }
    }
  }
}

calc('5000 | =2 !ua:千米:米', lengthOptions)    // '5.00千米'
calc('150 | =2 !ua:米:厘米', lengthOptions)     // '1.50米'

温度转换

javascript
const tempOptions = {
  _unit_convert_out: {
    '℃': {
      '℉': (f) => (f - 32) * 5 / 9,
      'K': (k) => k - 273.15
    },
    '℉': {
      '℃': (c) => c * 9 / 5 + 32
    }
  }
}

calc('100 | =1 !ua:℃:℉', tempOptions)   // '37.7℃' (100℉ → 37.7℃)
calc('0 | =1 !ua:℃:K', tempOptions)     // '-273.1℃' (0K → -273.15℃)

基于 MIT 许可发布