123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361 |
- /**
- * @fileoverview 订单表单数字格式化工具函数
- * @description 提供统一的数字格式化功能,确保4位浮点型数据的精确处理
- */
- /**
- * 数字格式化配置选项
- * @typedef {Object} NumberFormatOptions
- * @property {number} minimumFractionDigits - 最小小数位数
- * @property {number} maximumFractionDigits - 最大小数位数
- * @property {boolean} useGrouping - 是否使用千分位分隔符
- */
- /**
- * 数字验证结果
- * @typedef {Object} NumberValidationResult
- * @property {boolean} isValid - 是否为有效数字
- * @property {number} value - 转换后的数字值
- * @property {string} error - 错误信息(如果有)
- */
- /**
- * 格式化配置常量
- * @description 定义不同类型数字的格式化规则
- * @readonly
- */
- export const NUMBER_FORMAT_CONFIG = Object.freeze({
- /** 4位浮点型数量格式化选项 */
- QUANTITY_FLOAT: {
- minimumFractionDigits: 0,
- maximumFractionDigits: 4,
- useGrouping: false
- },
- /** 整数数量格式化选项 */
- QUANTITY_INTEGER: {
- minimumFractionDigits: 0,
- maximumFractionDigits: 0,
- useGrouping: false
- },
- /** 金额格式化选项(2位小数) */
- AMOUNT: {
- minimumFractionDigits: 2,
- maximumFractionDigits: 2,
- useGrouping: true
- },
- /** 税率格式化选项(2位小数) */
- TAX_RATE: {
- minimumFractionDigits: 0,
- maximumFractionDigits: 2,
- useGrouping: false
- },
- /** 单价格式化选项(2位小数) */
- UNIT_PRICE: {
- minimumFractionDigits: 0,
- maximumFractionDigits: 2,
- useGrouping: false
- }
- })
- /**
- * 数字类型枚举
- * @description 定义支持的数字类型
- * @readonly
- */
- export const NUMBER_TYPES = Object.freeze({
- /** 4位浮点型数量 */
- QUANTITY_FLOAT: 'QUANTITY_FLOAT',
- /** 整数数量 */
- QUANTITY_INTEGER: 'QUANTITY_INTEGER',
- /** 金额 */
- AMOUNT: 'AMOUNT',
- /** 税率 */
- TAX_RATE: 'TAX_RATE',
- /** 单价 */
- UNIT_PRICE: 'UNIT_PRICE'
- })
- /**
- * 验证并转换数字值
- * @description 验证输入值是否为有效数字,并进行安全转换
- * @param {*} value - 待验证的值
- * @returns {NumberValidationResult} 验证结果
- */
- export function validateNumber(value) {
- // 处理null、undefined和空字符串
- if (value === null || value === undefined || value === '') {
- return {
- isValid: true,
- value: 0,
- error: null
- }
- }
- // 转换为数字
- const numValue = Number(value)
- // 检查是否为有效数字
- if (isNaN(numValue) || !isFinite(numValue)) {
- return {
- isValid: false,
- value: 0,
- error: '无效的数字格式'
- }
- }
- return {
- isValid: true,
- value: numValue,
- error: null
- }
- }
- /**
- * 格式化4位浮点型数字
- * @description 将数字格式化为4位小数的字符串,自动去除尾随零
- * @param {number|string|null|undefined} value - 数字值
- * @returns {string} 格式化后的数字字符串
- * @example
- * formatFloatNumber(123.45670000) // 返回 "123.4567"
- * formatFloatNumber(100.0000) // 返回 "100"
- * formatFloatNumber(null) // 返回 "0"
- */
- export function formatFloatNumber(value) {
- const validation = validateNumber(value)
- if (!validation.isValid) {
- return '0'
- }
- const numValue = validation.value
- // 使用4位小数精度格式化
- const formatted = numValue.toLocaleString('zh-CN', NUMBER_FORMAT_CONFIG.QUANTITY_FLOAT)
- // 去除尾随零和不必要的小数点
- return formatted.replace(/\.?0+$/, '')
- }
- /**
- * 格式化整数
- * @description 将数字格式化为整数字符串
- * @param {number|string|null|undefined} value - 数字值
- * @returns {string} 格式化后的整数字符串
- * @example
- * formatIntegerNumber(123.456) // 返回 "123"
- * formatIntegerNumber(null) // 返回 "0"
- */
- export function formatIntegerNumber(value) {
- const validation = validateNumber(value)
- if (!validation.isValid) {
- return '0'
- }
- console.log(value, Math.round(validation.value).toLocaleString('zh-CN', NUMBER_FORMAT_CONFIG.QUANTITY_INTEGER))
- return Math.round(validation.value).toLocaleString('zh-CN', NUMBER_FORMAT_CONFIG.QUANTITY_INTEGER)
- }
- /**
- * 格式化金额
- * @description 将数字格式化为金额字符串,保留2位小数并添加千分位分隔符
- * @param {number|string|null|undefined} value - 金额值
- * @param {boolean} [withSymbol=true] - 是否添加货币符号
- * @returns {string} 格式化后的金额字符串
- * @example
- * formatAmount(1234.567) // 返回 "¥1,234.57"
- * formatAmount(1234.567, false) // 返回 "1,234.57"
- * formatAmount(null) // 返回 "¥0.00"
- */
- export function formatAmount(value, withSymbol = true) {
- const validation = validateNumber(value)
- if (!validation.isValid) {
- return withSymbol ? '¥0.00' : '0.00'
- }
- const formatted = validation.value.toLocaleString('zh-CN', NUMBER_FORMAT_CONFIG.AMOUNT)
- return withSymbol ? `¥${formatted}` : formatted
- }
- /**
- * 格式化税率
- * @description 将数字格式化为税率字符串,最多保留2位小数
- * @param {number|string|null|undefined} value - 税率值(0-100)
- * @param {boolean} [withSymbol=true] - 是否添加百分号
- * @returns {string} 格式化后的税率字符串
- * @example
- * formatTaxRate(13.5) // 返回 "13.5%"
- * formatTaxRate(13.50, false) // 返回 "13.5"
- * formatTaxRate(null) // 返回 "0%"
- */
- export function formatTaxRate(value, withSymbol = true) {
- const validation = validateNumber(value)
- if (!validation.isValid) {
- return withSymbol ? '0%' : '0'
- }
- const formatted = validation.value.toLocaleString('zh-CN', NUMBER_FORMAT_CONFIG.TAX_RATE)
- return withSymbol ? `${formatted}%` : formatted
- }
- /**
- * 格式化单价
- * @description 将数字格式化为单价字符串,最多保留4位小数
- * @param {number|string|null|undefined} value - 单价值
- * @returns {string} 格式化后的单价字符串
- * @example
- * formatUnitPrice(123.45670000) // 返回 "123.4567"
- * formatUnitPrice(100.0000) // 返回 "100"
- * formatUnitPrice(null) // 返回 "0"
- */
- export function formatUnitPrice(value) {
- const validation = validateNumber(value)
- if (!validation.isValid) {
- return '0.00'
- }
- const formatted = validation.value.toLocaleString('zh-CN', {
- minimumFractionDigits: 2,
- maximumFractionDigits: 2,
- useGrouping: false
- })
- return formatted
- }
- /**
- * 通用数字格式化函数
- * @description 根据指定类型格式化数字
- * @param {number|string|null|undefined} value - 数字值
- * @param {keyof typeof NUMBER_TYPES} type - 数字类型
- * @param {Object} [options={}] - 额外选项
- * @param {boolean} [options.withSymbol] - 是否添加符号(适用于金额和税率)
- * @returns {string} 格式化后的数字字符串
- * @example
- * formatNumber(123.4567, NUMBER_TYPES.QUANTITY_FLOAT) // 返回 "123.4567"
- * formatNumber(1234.56, NUMBER_TYPES.AMOUNT) // 返回 "¥1,234.56"
- * formatNumber(13.5, NUMBER_TYPES.TAX_RATE) // 返回 "13.5%"
- */
- export function formatNumber(value, type, options = {}) {
- const { withSymbol = true } = options
- switch (type) {
- case NUMBER_TYPES.QUANTITY_FLOAT:
- return formatFloatNumber(value)
- case NUMBER_TYPES.QUANTITY_INTEGER:
- return formatIntegerNumber(value)
- case NUMBER_TYPES.AMOUNT:
- return formatAmount(value, withSymbol)
- case NUMBER_TYPES.TAX_RATE:
- return formatTaxRate(value, withSymbol)
- case NUMBER_TYPES.UNIT_PRICE:
- return formatUnitPrice(value)
- default:
- return formatFloatNumber(value)
- }
- }
- /**
- * 解析格式化的数字字符串
- * @description 将格式化的数字字符串解析为数字值
- * @param {string} formattedValue - 格式化的数字字符串
- * @returns {NumberValidationResult} 解析结果
- * @example
- * parseFormattedNumber("1,234.56") // 返回 { isValid: true, value: 1234.56, error: null }
- * parseFormattedNumber("¥1,234.56") // 返回 { isValid: true, value: 1234.56, error: null }
- * parseFormattedNumber("13.5%") // 返回 { isValid: true, value: 13.5, error: null }
- */
- export function parseFormattedNumber(formattedValue) {
- if (typeof formattedValue !== 'string') {
- return validateNumber(formattedValue)
- }
- // 移除货币符号、百分号、千分位分隔符等
- const cleanValue = formattedValue
- .replace(/[¥$€£%,\s]/g, '')
- .trim()
- return validateNumber(cleanValue)
- }
- /**
- * 精确计算两个数字的乘积
- * @description 避免浮点数计算精度问题
- * @param {number|string} num1 - 第一个数字
- * @param {number|string} num2 - 第二个数字
- * @returns {number} 计算结果
- * @example
- * preciseMultiply(0.1, 0.2) // 返回 0.02(而不是 0.020000000000000004)
- */
- export function preciseMultiply(num1, num2) {
- const validation1 = validateNumber(num1)
- const validation2 = validateNumber(num2)
- if (!validation1.isValid || !validation2.isValid) {
- return 0
- }
- const value1 = validation1.value
- const value2 = validation2.value
- // 获取小数位数
- const decimals1 = (value1.toString().split('.')[1] || '').length
- const decimals2 = (value2.toString().split('.')[1] || '').length
- const totalDecimals = decimals1 + decimals2
- // 转换为整数进行计算,然后除以相应的倍数
- const int1 = Math.round(value1 * Math.pow(10, decimals1))
- const int2 = Math.round(value2 * Math.pow(10, decimals2))
- return (int1 * int2) / Math.pow(10, totalDecimals)
- }
- /**
- * 精确计算两个数字的除法
- * @description 避免浮点数计算精度问题
- * @param {number|string} dividend - 被除数
- * @param {number|string} divisor - 除数
- * @returns {number} 计算结果
- * @example
- * preciseDivide(0.3, 0.1) // 返回 3(而不是 2.9999999999999996)
- */
- export function preciseDivide(dividend, divisor) {
- const validation1 = validateNumber(dividend)
- const validation2 = validateNumber(divisor)
- if (!validation1.isValid || !validation2.isValid || validation2.value === 0) {
- return 0
- }
- const value1 = validation1.value
- const value2 = validation2.value
- // 获取小数位数
- const decimals1 = (value1.toString().split('.')[1] || '').length
- const decimals2 = (value2.toString().split('.')[1] || '').length
- const maxDecimals = Math.max(decimals1, decimals2)
- // 转换为整数进行计算
- const int1 = Math.round(value1 * Math.pow(10, maxDecimals))
- const int2 = Math.round(value2 * Math.pow(10, maxDecimals))
- return int1 / int2
- }
- /**
- * 四舍五入到指定小数位数
- * @description 精确的四舍五入函数
- * @param {number|string} value - 数字值
- * @param {number} [decimals=4] - 小数位数
- * @returns {number} 四舍五入后的结果
- * @example
- * preciseRound(123.45678, 4) // 返回 123.4568
- * preciseRound(123.45678, 2) // 返回 123.46
- */
- export function preciseRound(value, decimals = 4) {
- const validation = validateNumber(value)
- if (!validation.isValid) {
- return 0
- }
- const multiplier = Math.pow(10, decimals)
- return Math.round(validation.value * multiplier) / multiplier
- }
|