/**
* 数字转换为中文数字
* @param {number|string} num 需要转换的数字
* @param {object} options 配置项
* @param {boolean} options.uppercase 是否使用大写中文数字(壹贰叁...),默认 false
* @returns {string} 中文数字字符串
* @example
* numberToChinese(0) // '零'
* numberToChinese(10) // '十'
* numberToChinese(12) // '十二'
* numberToChinese(123) // '一百二十三'
* numberToChinese(1001) // '一千零一'
* numberToChinese(10000) // '一万'
* numberToChinese(100010000) // '一亿零一万'
* numberToChinese(110) // '一百一十'
* numberToChinese(123, { uppercase: true }) // '壹佰贰拾叁'
*/
export function numberToChinese(num, options = {}) {
const { uppercase = false } = options
// 数字字符映射
const digits = uppercase
? ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖']
: ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九']
// 量级映射
const units = uppercase ? ['', '拾', '佰', '仟'] : ['', '十', '百', '千']
const bigUnits = ['', '万', '亿', '万亿']
const n = Math.floor(Number(num))
if (isNaN(n)) return ''
if (n === 0) return digits[0]
let neg = false
let absNum = n
if (absNum < 0) {
neg = true
absNum = Math.abs(absNum)
}
// 将数字按4位一段从低到高拆分
const segments = []
while (absNum > 0) {
segments.push(absNum % 10000)
absNum = Math.floor(absNum / 10000)
}
let result = ''
let needZero = false // 标记是否需要在下一段前补"零"(段间零)
for (let s = segments.length - 1; s >= 0; s--) {
const segment = segments[s]
if (segment === 0) {
needZero = true
continue
}
// 段间零:上一段为0或标记需要零
if (needZero || (s < segments.length - 1 && segment < 1000)) {
result += digits[0]
needZero = false
}
// 从高位到低位处理4位数
let hasNonZero = false
for (let i = 3; i >= 0; i--) {
const divisor = Math.pow(10, i)
const d = Math.floor(segment / divisor) % 10
if (d === 0) {
if (hasNonZero) {
// 中间零:前面有非零数字,后面可能还有非零数字
// 先标记,等遇到下一个非零数字时再输出"零"
needZero = true
}
} else {
if (needZero) {
result += digits[0]
needZero = false
}
result += digits[d] + units[i]
hasNonZero = true
}
}
// 添加大单位(万、亿)
result += bigUnits[s]
needZero = false
}
// 最高位"一十"省略为"十"
const prefix = uppercase ? '壹拾' : '一十'
if (result.startsWith(prefix)) {
result = result.slice(1)
}
return (neg ? '负' : '') + result
}