前几天,在公司项目中遇到一个给金额添加千分位的需求,于是一直想着有空来整理一下,一直没抽出时间,今天有空,整理一下。
2023.09.11更新:前端有自带的方法,我是看到这篇博客的时候才知道的:
js处理金额显示格式(最简单)_js 金额格式_@前端小菜的博客-CSDN博客
整理如下:
function moneyFormats(value) { return Number(value).toLocaleString('zh', { style: 'currency', currency: 'CNY', minimumFractionDigits: 2 }); } moneyFormats('555555.445') // '¥555,555.45' moneyFormats('88888888888888888888888888888') '¥88,888,888,888,888,900,000,000,000,000.00' /* * Number.toLocaleString() 第二个参数: * style: 格式化时使用的样式(默认"decimal") * 👉 "decimal"表示纯数字格式, "currency"表示货币格式, "percent"表示百分比格式 * currency: 在货币格式化中使用的货币符号(没有默认值,如果样式是"currency",必须提供货币属性) * 👉 "USD" 表示美元, "EUR" 表示欧元, "CNY" 表示人民币 * minimumFractionDigits: 使用的小数位数的最小数目 * 👉 可能的值是从0到20,默认为普通的数字和百分比格式为0 */
这个方法有个小问题,Number(value)转为数字格式的时候,会有有效位数的问题。
不过换个角度想想,好像也没这么大金额的,如果确认金额不带小数的话,可以这样(当然带小数转BigInt就会报错了):
function moneyFormats(value) { return BigInt(value).toLocaleString('zh', { style: 'currency', currency: 'CNY', minimumFractionDigits: 2 }); } // moneyFormats('555555.445') // 报错 Uncaught SyntaxError: Cannot convert 555555.445 to a BigInt moneyFormats('88888888888888888888888888888') // '¥88,888,888,888,888,888,888,888,888,888.00'
那么,可以灵活处理一下,反正小数部分也是照搬下来(四舍五入一下),所以只用处理整数
(上班中午休息的时候手写的,简单测试了下没发现问题,如果有问题再说)
const fractionalCount = 3 // 保留x位小数 function moneyFormats(num) { if (isNaN(num)) { return '-' } // 将 数字/字符串类型的数字 转换为 字符串,并按小数点拆分为整数部分和小数部分 let parts = (num + "").split(".") if (parts.length > 2) { return "-" // 不是数字 } // 整数部分 let integerPart = BigInt(parts[0]).toLocaleString('zh', { style: 'currency', currency: 'CNY', minimumFractionDigits: 0 }) // 小数部分(考虑精度问题就稍微麻烦一点,核心思路就是按要保留小数位数截断,然后多取1位判断是否需要进位) let fractionalPart = "" if (fractionalCount > 0) { if (parts.length > 1) { let fLen = parts[1].length if (fLen > fractionalCount) { // 需要四舍五入 if (parts[1][fractionalCount] >= 5) { // 需要进位 let fractionalBigInt = BigInt(parts[1].substring(0, fractionalCount)) fractionalPart = (fractionalBigInt + 1n).toString() } else { // 不需要进位 fractionalPart = parts[1].substring(0, fractionalCount) } } else { // 需要在后面补 0 let zeroCount = fractionalCount - fLen fractionalPart = parts[1] + new Array(zeroCount).fill('0').join('') } } else { // 小数部分全是 0 fractionalPart = parts[1] + new Array(fractionalCount).fill('0').join('') } fractionalPart = '.' + fractionalPart } // console.log('fractionalPart', fractionalPart) return integerPart + fractionalPart } moneyFormats('888888888888888888888888888888888888888888.444444444444444444445') // '¥888,888,888,888,888,888,888,888,888,888,888,888,888,888.444' moneyFormats('00000111111.4') // '¥111,111.400' moneyFormats('00000111111.95') // '¥111,111.950'
在网上简单搜了一下,主要有几种方式,比较常规的代码判断这里就不再重复了,主要是想整理一下正则的处理方案。
项目中用到了echarts,感觉应该echarts中应该会有相关的功能。大胆猜想,如果echarts也使用了正则处理这个逻辑,那\d肯定会出现在正则中,同时每三位添加一个逗号,那正则中很有可能出现\d{3}之类的部分。果不其然,一搜,发现真有。
这两个就是从echarts中摘出来的代码:
o.addCommas=function(t){return isNaN(t)?"-":(t=(t+"").split("."),t[0].replace(/(\d{1,3})(?=(?:\d{3})+(?!\d))/g,"$1,")+(t.length>1?"."+t[1]:""))} function tu(t){return isNaN(t)?"-":(t=(t+"").split("."))[0].replace(/(\d{1,3})(?=(?:\d{3})+(?!\d))/g,"$1,")+(1<t.length?"."+t[1]:"")}
分析了一下,其实这两个代码功能是一样的,只是可能用的打包工具有些差异,所以最终压缩后的代码不完全一样。
我把上面这个函数美化了一下,加了点注释,代码逻辑是等价的
function addCommas(num) { if (isNaN(num)) { return '-' } // 将 数字/字符串类型的数字 转换为 字符串,并按小数点拆分为整数部分和小数部分 let parts = (num + "").split(".") // 整数部分 从右往左匹配 let integerPart = parts[0].replace(/(\d{1,3})(?=(?:\d{3})+(?!\d))/g, "$1,") // 小数部分 let fractionalPart = parts.length > 1 ? "." + parts[1] : "" return integerPart + fractionalPart }
这样就一目了然了,如果想炫技的话,可以写到一行:
function addCommas(num) { return isNaN(num) ? "-" : (num = (num + "").split("."))[0].replace(/(\d{1,3})(?=(?:\d{3})+(?!\d))/g, "$1,") + (num.length > 1 ? "." + num[1] : "") }
上面这种更适合严谨一点的需求,因为他同时带一些校验,比如非数字字符串等等。
如果能够确保入参是数字,数字字符串或者是null,undefined,那么可以简单一些,像这样:
function addCommas(val) { if (typeof val === 'undefined' || val === null) { return '' } return (val + "").replace(/(\d)(?=(\d{3})+\.)/g, '$1,'); } // console.log(addCommas('')) // '' // console.log(addCommas('0')) // '0' // console.log(addCommas(null)) // '' // console.log(addCommas(undefined)) // ''
再进一步,如果是前端展示金额的话,可以像这样:(注意toFixed会四舍五入,这里要按照实际业务来决定是四舍五入还是向下取整)
2023.09.11备注:前端 toFixed() 并不是四舍五入,也不是向下舍入,所以涉及到金额显示,请谨慎使用!
参考这篇博客:https://www.only4.work/blog/?id=519
function priceFormat(val) { val = Number(val) if (val) { return "¥" + val.toFixed(2).replace(/(\d)(?=(\d{3})+\.)/g, '$1,'); } else { return '¥0.00' } }
到这里整理的就差不多了,希望能够帮到大家。
本文由张小弟之家原创,转载请注明出处。
本站文章除注明转载/出处外,均为原创,若要转载请务必注明出处。转载后请将转载链接通过邮件告知我站,谢谢合作。本站邮箱:admin@only4.work
尊重他人劳动成果,共创和谐网络环境。点击版权声明查看本站相关条款。