js将字符串格式化为字符串形式的数字(允许负数)

xd422 / 2024-06-13 / 原文

本人在制作金额输入框时,由于数字数字框的右侧按钮难处理,所以使用文本输入框接收金额数字,这个函数是用来格式化与校验金额输入.

代码还不够优雅,望指点

/**
 * 将字符串格式为一个合规的字符串数字
 * @param {string} value 字符串大概率是数字字符串
 * @param {boolean} negative 是否允许负数
 * @return {string} "999999999.99"
 */
export function strFormatStrNum(value, negative = false) {
  // 初始检测最容易出错的值
  if (negative) {
    // 初始输入有可能输入0-或者符号需要初始化
    if (value === '0-' || value === '-') {
      // 归零 注意js中有负数零存在
      return '-0'
    }
  }
  // -9代表数字上限为亿位数,-8代表数字上限为千万位数,-2代表数字上限为十位数,想要加大上限就加深负数
  const accuracy_upper_limit = -9
  // 2代表数字只保留小数点后两位
  const accuracy_lower_limit = 2
  // 需要返回的是负数
  const if_negative = negative && value.charAt(0) === '-'

  // 声明切割值保存的数组
  let parts = []
  /*
   * 将value切割成指定格式的字符串数组
   * 有可能输入的值不是字符串形式所以需要toString
   * e也会被判断为数字所以需要删除e
   * 0b判断为二进制数字删除b
   * 0o判断为八进制数字删除o
   * 0x判断为十六进制数字删除x
   * 默认清除负数所以也要删除-
   * split('.')切割为数组
   */
  parts = value
    .toString()
    .replace(/e/g, '')
    .replace(/b/g, '')
    .replace(/o/g, '')
    .replace(/x/g, '')
    .replace(/-/g, '')
    .split('.')

  // 内部函数定义数值初始化
  function strInit() {
    if (if_negative) {
      // -0 === 0 返回 true
      return '-0'
    } else {
      return '0'
    }
  }

  // 进行上限切割后数字化转化
  const int = Number(parts[0].slice(accuracy_upper_limit))

  // 获取整形部分
  function getStrInt() {
    // 需要返回负数的话,需要加回负号
    return if_negative ? '-' + String(int) : String(int)
  }

  // 是否有小数,小数数组长度为2,如果不需要小数那就只对整数判断
  if (parts.length === 1 || accuracy_lower_limit === 0) {
    // 整数情况
    // 数字化的结果有可能是非数值
    if (Number.isNaN(int)) {
      // 初始化
      return strInit()
    } else {
      // 数字化后不是NaN转回字符串
      return getStrInt()
    }
  } else {
    // 有小数情况
    // 切割出指定位数小数
    const decimal = parts[1].slice(0, accuracy_lower_limit)
    // 小数单独进行数字化
    // const double = Number(decimal)
    const regex = /^\d+$/
    // 判断是否有一边数字为NaN
    // !Number.isNaN(int) && !Number.isNaN(double)
    if (!Number.isNaN(int) && regex.test(decimal)) {
      // 两边的数字都不为NaN
      // 右边不使用double是要考虑到.01的这种情况,.01会转为1
      // 2024-06-13修改,由于之前没有考虑到小数后输入字母现在加上正则,regex只允许0-9
      return getStrInt() + '.' + decimal
    } else if (!Number.isNaN(int)) {
      // 只有整数部分是正常的话,不加.会导致用户无法输入小数
      // 2024-06-13修改,之前的三元表达式由于字符串连接 (+) 运算符的优先级高于比较运算符导致没有正确执行,现在加上括号提权
      return getStrInt() + (accuracy_lower_limit > 0 ? '.0' : '')
    } else {
      // 初始化
      return strInit()
    }
  }
}

2024-06-13修复小数部分格式化与代码
旧代码

  } else {
    // 有小数情况
    // 切割出指定位数小数
    const decimal = parts[1].slice(0, accuracy_lower_limit)
    // 小数单独进行数字化
    const double = Number(decimal)
    // 判断是否有一边数字为NaN
    if (!Number.isNaN(int) && !Number.isNaN(double)) {
      // 两边的数字都不为NaN
      // 右边不使用double是要考虑到.01的这种情况,.01会转为1
      return getStrInt() + '.' + decimal
    } else if (!Number.isNaN(int)) {
      // 只有整数部分是正常的话,不加.会导致用户无法输入小数
      return getStrInt() + accuracy_lower_limit > 0 ? '.0' : ''
    } else {
      // 初始化
      return strInit()
    }
  }

新代码

  } else {
    // 有小数情况
    // 切割出指定位数小数
    const decimal = parts[1].slice(0, accuracy_lower_limit)
    // 小数单独进行数字化
    // const double = Number(decimal)
    const regex = /^\d+$/
    // 判断是否有一边数字为NaN
    // !Number.isNaN(int) && !Number.isNaN(double)
    if (!Number.isNaN(int) && regex.test(decimal)) {
      // 两边的数字都不为NaN
      // 右边不使用double是要考虑到.01的这种情况,.01会转为1
      // 2024-06-13修改,由于之前没有考虑到小数后输入字母现在加上正则,regex只允许0-9
      return getStrInt() + '.' + decimal
    } else if (!Number.isNaN(int)) {
      // 只有整数部分是正常的话,不加.会导致用户无法输入小数
      // 2024-06-13修改,之前的三元表达式由于字符串连接 (+) 运算符的优先级高于比较运算符导致没有正确执行,现在加上括号提权
      return getStrInt() + (accuracy_lower_limit > 0 ? '.0' : '')
    } else {
      // 初始化
      return strInit()
    }
  }