前端开发常用的金额数字处理

JavaScript因为存在计算的精度问题,所以直接计算就可能会导致各种各样的bug,为了解决这个问题,就要使用BigNumber.js这个库。

BigNumber.js是一个用于任意精度计算的js库。可以在 官方文档 的console中测试使用。也可以通过 npm install bignumber.js --save 来安装。
然后 import BigNumber from ‘bignumber.js’ 来引入使用。他的大概原理是将所有数字当做字符串,重新实现了计算逻辑。缺点是性能比原生的差很多。

import BigNumber from 'bignumber.js'

// 获取小数位数
const getDecimalsLength = number => {
  const matched = String(number || 0).match(/\.(\d+)$/)
  return matched ? matched[1].length : 0
}

/**
 * 格式化数字,每隔三位添加逗号
 * @param {number} num 数值
 * @returns {string} 格式化后的数值字符串
 */
 export function parseNum(num) {
    const numberString = (num || 0).toString()
    // 在转换一些以万为单位的金额时,小数点位数会超过3位,小数点中的数字暂不解析
    const decimalPointIndex = numberString.indexOf('.')
    let intString = decimalPointIndex > -1 ? numberString.slice(0, decimalPointIndex) : numberString
    let decimalString = decimalPointIndex > -1 ? numberString.slice(decimalPointIndex) : ''
    let reg1 = new RegExp('(\\d)(?=(?:\\d{3})+($|\\.))','g')
    return intString.replace(reg1, "$1,") + decimalString
}

/**
 * 保留两位小数
 * @param {number|string} number 保留两位小数
 * @param {string} [mode=floor] 舍入模式,floor 是向下取整,round 是四舍五入,ceil 是向上进一位
 */
export const keep2Decimals = (number, mode = 'floor', decimals = 2) => {
  const times = Math.pow(10, decimals)
  const oneHundredTimes = new BigNumber(number).times(times)
  // https://mikemcl.github.io/bignumber.js/#constructor-properties
  const roundModes = {
    ceil: BigNumber.ROUND_CEIL, // 向正无限大方向舍入,始终不会减少计算值
    floor: BigNumber.ROUND_FLOOR, // 向负无限大方向舍入,始终不会增加计算值
    round: BigNumber.ROUND_HALF_UP, // 四舍五入
  }
  const roundMode = roundModes[mode]
  if (!Object.prototype.hasOwnProperty.call(roundModes, mode)) {
    throw new Error(`暂不支持 ${mode} 舍入模式`)
  }
  const integer = oneHundredTimes.integerValue(roundMode)
  const newNumber = new BigNumber(integer).dividedBy(times)
  return newNumber.toNumber()
}

/** 将元为单位的金额转为以万为单位 */
export function yuan2wan(price, options = { digits : 6, parseNumber : false, mode : 'floor'}) {
  let bigNumber = new BigNumber(price)
  let { digits, parseNumber, mode } = options
  if (digits === 4) {
    bigNumber = new BigNumber(bigNumber.integerValue(BigNumber.ROUND_FLOOR))
  } else if (digits === 6 && getDecimalsLength(bigNumber.toString()) > 2) {
    bigNumber = new BigNumber(keep2Decimals(bigNumber,mode))
  }
  const result = bigNumber.dividedBy(1e4).toNumber()
  if (!parseNumber) {
    return result
  }
  return parseNum(result)
}

/** 将万为单位的金额转为以元为单位 */
export function wan2yuan(price, parseNumber = false) {
  const bigNumber = new BigNumber(price)
  const result = bigNumber.times(1e4).toNumber()
  return parseNumber ? parseNum(result) : result
}

/** 将元为单位的金额转为以分为单位 */
export function yuan2fen(price, parseNumber = false) {
  const bigNumber = new BigNumber(price)
  const result = bigNumber.times(1e2).toNumber()
  return parseNumber ? parseNum(result) : result
}

/** 将分为单位的金额转为以元为单位 */
export function fen2yuan(price, parseNumber = false) {
  const bigNumber = new BigNumber(price)
  const result = bigNumber.dividedBy(1e2).toNumber()
  return parseNumber ? parseNum(result) : result
}

/**
 * 计算两位的差额,可设置保留小数点位数
 * @param {number} firstNumber 第一个数字
 * @param {number} lastNumber 第二个数字
 * @param {number} decimals 保留的小数点位数,默认为0
 * @returns {number} 结果
 */
export function getDifference(firstNumber, lastNumber, decimals = 0) {
  return Number(new BigNumber(firstNumber || 0)
        .minus(lastNumber || 0)
        .toFixed(decimals))
}

/**
 * 金额大小写转换
 * @export
 * @param {any} m 
 * @returns  {string} 转换后的大写金额
 */
export function NumberToChinese (m) {
var num = Number(m);
  if(!/^\d*(\.\d*)?$/.test(num))throw(new Error(-1, "Number is wrong!"));     
  var AA = new Array("零","壹","贰","叁","肆","伍","陆","柒","捌","玖");     
  var BB = new Array("","拾","佰","仟","萬","億","圆","");     
  var CC = new Array("角", "分", "厘");     
  var a = (""+ num).replace(/(^0*)/g, "").split("."), k = 0, re = "";     
  for(var i=a[0].length-1; i>=0; i--){     
    switch(k){     
      case 0 : re = BB[7] + re; break;     
      case 4 : if(!new RegExp("0{4}\\d{"+ (a[0].length-i-1) +"}$").test(a[0]))     
              re = BB[4] + re; break;     
      case 8 : re = BB[5] + re; BB[7] = BB[5]; k = 0; break;     
    }     
    if(k%4 == 2 && a[0].charAt(i)=="0" && a[0].charAt(i+2) != "0") re = AA[0] + re;     
    if(a[0].charAt(i) != 0) re = AA[a[0].charAt(i)] + BB[k%4] + re; k++;     
  }     
  if(a.length>1){     
    re += BB[6];     
    for(var i=0; i<a[1].length; i++){     
      re += AA[a[1].charAt(i)] + CC[i];     
      if(i==2) break;     
    }   
    if(a[1].charAt(0)=="0" && a[1].charAt(1)=="0"){  
      re+="元整";  
    }    
  }else{  
    re+="元整";  
  }     
  return re;   
}

/**
 * 保留n位小数  不够失望添加0占位
 * @param {number} value 数值 默认2位
 * @returns {string} 格式化后的数值字符串
 */

export const formatnumber = (value, num = 2) => {
  let a
  let b
  let c
  let i
  a = value.toString()
  b = a.indexOf('.')
  c = a.length
  if (num === 0) {
    if (b !== -1) {
      a = a.substring(0, b)
    }
  } else {
    // 如果没有小数点
    // eslint-disable-next-line no-lonely-if
    if (b === -1) {
      a = `${a}.`
      for (i = 1; i <= num; i++) {
        a = `${a}0`
      }
    } else {
      // 有小数点,超出位数自动截取,否则补0
      a = a.substring(0, b + num + 1)
      for (i = c; i <= b + num; i++) {
        a = `${a}0`
      }
    }
  }
  return a
}

// 汉字金额
export const getChineseCharacterAmount = num => {
  if (!num) return ''
  if (isNaN(Number(num))) {
    return ''
  }
  const sitToUnit = a => {
    // eslint-disable-next-line no-magic-numbers
    if (a > 10) {
      // eslint-disable-next-line no-magic-numbers
      a = a - 8
      return (sitToUnit(a))
    }
    const zh = '分角圆拾佰仟万拾佰仟亿'
    return zh.charAt(a)
  }
  let minus = ''
  let mstr = ''
  const text = num.toString()
  const zh = '零壹贰叁肆伍陆柒捌玖'
  if (text.indexOf('-') === 0) {
    num = text.replace('-', '')
    minus = '负'
  }
  let moneyNum = Number(num)
  // eslint-disable-next-line no-magic-numbers
  let money = Math.round(moneyNum * 100).toString(10)
  const len = money.length
  for (let i = 0; i < len; i++) {
    mstr = mstr + zh.charAt(parseInt(money.charAt(i))) + sitToUnit(len - i - 1)
  }
  mstr = mstr.replace('零分', '')
  mstr = mstr.replace('零角', '零')
  let yy = 0
  // eslint-disable-next-line no-constant-condition
  while (true) {
    let { length } = mstr
    mstr = mstr.replace('零圆', '圆')
    mstr = mstr.replace('零万', '万')
    mstr = mstr.replace('零亿', '亿')
    mstr = mstr.replace('零仟', '零')
    mstr = mstr.replace('零佰', '零')
    mstr = mstr.replace('零零', '零')
    mstr = mstr.replace('零拾', '零')
    mstr = mstr.replace('亿万', '亿零')
    mstr = mstr.replace('万仟', '万零')
    mstr = mstr.replace('仟佰', '仟零')
    yy = mstr.length
    if (yy === length) {
      break
    }
  }
  yy = mstr.length
  if (mstr.charAt(yy - 1) === '零') {
    mstr = mstr.substring(0, yy - 1)
  }
  yy = mstr.length
  if (mstr.charAt(yy - 1) === '圆') {
    mstr = `${mstr}整`
  }
  return minus + mstr
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值