feat(web-admin): add RMB uppercase converter for OA money field
Pure function with node:test coverage for zero, jiao/fen, and range limits. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
98074bf201
commit
e6d7eecdc6
2 files changed
+123
No files matched your search
@@ -0,0 +1,38 @@
|
||||
// web-admin/src/utils/rmb-upper.test.ts
|
||||
import { describe, it } from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { toRmbUpper } from './rmb-upper'
|
||||
|
||||
describe('toRmbUpper', () => {
|
||||
it('zero', () => {
|
||||
assert.equal(toRmbUpper(0), '零元整')
|
||||
})
|
||||
it('integer', () => {
|
||||
assert.equal(toRmbUpper(1), '壹元整')
|
||||
assert.equal(toRmbUpper(10), '壹拾元整')
|
||||
assert.equal(toRmbUpper(100), '壹佰元整')
|
||||
assert.equal(toRmbUpper(1234), '壹仟贰佰叁拾肆元整')
|
||||
})
|
||||
it('jiao fen', () => {
|
||||
assert.equal(toRmbUpper(1.23), '壹元贰角叁分')
|
||||
assert.equal(toRmbUpper(0.5), '伍角')
|
||||
assert.equal(toRmbUpper(0.05), '伍分')
|
||||
assert.equal(toRmbUpper(1.2), '壹元贰角')
|
||||
assert.equal(toRmbUpper(1.03), '壹元零叁分')
|
||||
})
|
||||
it('nullish and invalid', () => {
|
||||
assert.equal(toRmbUpper(null), '')
|
||||
assert.equal(toRmbUpper(undefined), '')
|
||||
assert.equal(toRmbUpper(Number.NaN), '')
|
||||
assert.equal(toRmbUpper(-1), '')
|
||||
})
|
||||
it('too large', () => {
|
||||
assert.equal(toRmbUpper(1e12), '金额过大')
|
||||
})
|
||||
it('near max has yuan jiao fen', () => {
|
||||
const s = toRmbUpper(999999999999.99)
|
||||
assert.ok(s.includes('元'))
|
||||
assert.ok(s.includes('角') || s.includes('分'))
|
||||
assert.notEqual(s, '金额过大')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,85 @@
|
||||
export function toRmbUpper(value: number | null | undefined): string {
|
||||
if (value == null || !Number.isFinite(value) || value < 0) {
|
||||
return ''
|
||||
}
|
||||
|
||||
if (value > 999999999999.99) {
|
||||
return '金额过大'
|
||||
}
|
||||
|
||||
const fen = Math.round(value * 100)
|
||||
if (fen === 0) {
|
||||
return '零元整'
|
||||
}
|
||||
|
||||
const digits = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖']
|
||||
const units = ['', '拾', '佰', '仟']
|
||||
|
||||
function sectionToChinese(n: number): string {
|
||||
if (n === 0) return ''
|
||||
let result = ''
|
||||
let zeroPending = false
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const digit = Math.floor(n / 10 ** (3 - i)) % 10
|
||||
if (digit === 0) {
|
||||
if (result !== '') {
|
||||
zeroPending = true
|
||||
}
|
||||
} else {
|
||||
if (zeroPending) {
|
||||
result += '零'
|
||||
zeroPending = false
|
||||
}
|
||||
result += digits[digit] + units[3 - i]
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
const yuan = Math.floor(fen / 100)
|
||||
const jiao = Math.floor((fen % 100) / 10)
|
||||
const fenDigit = fen % 10
|
||||
|
||||
let result = ''
|
||||
|
||||
if (yuan > 0) {
|
||||
const yi = Math.floor(yuan / 100000000)
|
||||
const wan = Math.floor((yuan % 100000000) / 10000)
|
||||
const rest = yuan % 10000
|
||||
|
||||
if (yi > 0) {
|
||||
result += sectionToChinese(yi) + '亿'
|
||||
}
|
||||
if (wan > 0) {
|
||||
if (yi > 0 && wan < 1000) {
|
||||
result += '零'
|
||||
}
|
||||
result += sectionToChinese(wan) + '万'
|
||||
} else if (yi > 0 && rest > 0) {
|
||||
result += '零'
|
||||
}
|
||||
if (rest > 0) {
|
||||
if ((yi > 0 || wan > 0) && rest < 1000) {
|
||||
result += '零'
|
||||
}
|
||||
result += sectionToChinese(rest)
|
||||
}
|
||||
result += '元'
|
||||
}
|
||||
|
||||
if (jiao > 0) {
|
||||
result += digits[jiao] + '角'
|
||||
} else if (fenDigit > 0 && yuan > 0) {
|
||||
result += '零'
|
||||
}
|
||||
|
||||
if (fenDigit > 0) {
|
||||
result += digits[fenDigit] + '分'
|
||||
}
|
||||
|
||||
if (jiao === 0 && fenDigit === 0) {
|
||||
result += '整'
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
Reference in new issue
Block a user