Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | 37x 35x 35x 35x 35x 37x 3x 41x 3x 25x 25x 25x 25x 4x 4x 25x 25x 16x 16x 16x 3x | /**
* 四舍五入(HALF_UP,银行/保险佣金口径)。
*
* 不能直接用 `Math.round(value * factor)`:一方面 1.005 * 100 = 100.49999999999999
* 会被舍成 1.00;另一方面 `Math.round(-100.5) === -100` 是向零取整,与 HALF_UP
* 「远离零进位」的语义相反,负的调整项(otherAmount)会差一分钱。
* 这里先取绝对值、用指数记数法移位('1.005e2' 精确等于 100.5)再按符号还原。
*/
export function roundHalfUp(value: number, decimals = 2): number {
if (!Number.isFinite(value)) return 0
const factor = 10 ** decimals
const abs = Math.abs(value)
// 移位用字符串指数,避开二进制浮点误差;超大数退化为乘法
const shifted = Number(`${abs}e${decimals}`)
const rounded = Number.isFinite(shifted) ? Math.round(shifted) : Math.round(abs * factor)
return (value < 0 ? -rounded : rounded) / factor
}
const HKD = 'HKD'
/**
* 币种归一:Intl.NumberFormat 对空串或非 3 位字母会抛 RangeError,
* 后端 currency 缺失时会把整页渲染打断,故兜底到 HKD。
*/
function normalizeCurrency(currency: string | null | undefined): string {
return typeof currency === 'string' && /^[A-Za-z]{3}$/.test(currency.trim())
? currency.trim().toUpperCase()
: HKD
}
// Intl.NumberFormat 构造是慢路径,列表页每行要格式化 2~3 次,按 币种:小数位 缓存
const formatterCache = new Map<string, Intl.NumberFormat>()
function getFormatter(currency: string, decimals: number): Intl.NumberFormat {
const code = normalizeCurrency(currency)
const key = `${code}:${decimals}`
let formatter = formatterCache.get(key)
if (!formatter) {
formatter = new Intl.NumberFormat('zh-HK', {
style: 'currency',
currency: code,
minimumFractionDigits: decimals,
maximumFractionDigits: decimals
})
formatterCache.set(key, formatter)
}
return formatter
}
export function formatMoney(value: number, currency: string, decimals = 2): string {
return getFormatter(currency, decimals).format(roundHalfUp(value, decimals))
}
// 明细金额保留 4 位 HALF_UP;HKD 汇总展示 2 位
export function formatDetailMoney(value: number, currency: string): string {
const code = normalizeCurrency(currency)
const decimals = code === HKD ? 2 : 4
return formatMoney(value, code, decimals)
}
export function formatHkd(value: number): string {
return formatMoney(value, HKD, 2)
}
|