Skip to content

实现 数字转中文大写 工具

分类:general | 标签:大写、人民币、金额 数字、人民币金额转中文大写,数字转英文

2.1 功能说明

数字、人民币金额转中文大写,数字转英文

2.1.1 使用指南

功能说明

将阿拉伯数字转换为中文大写数字、人民币金额大写(财务票据常用)、英文读数,以及公历日期转中文大写。

使用场景

发票、合同、支票填写,财务与行政办公,日期的中文书写。

2.2 代码实现

2.2.1 组件结构

本工具是一个基于 Vue 3 <script setup> 语法的单文件组件(SFC),统一包裹在 ToolPageShell 组件内,由它提供页面标题、工具 ID、分类与「使用指南」插槽等通用外壳;核心业务逻辑(响应式状态、计算属性、事件处理函数)全部写在 <script setup> 中,输入/输出通过 el-inputel-button 等 Element Plus 组件与用户交互,所有数据均在浏览器本地处理,不会上传服务器。

2.2.2 核心逻辑概览

<script setup> 中定义的主要函数/方法:

  • clearErr()
  • fail()
  • parseNum()
  • sectionToChinese()
  • intToChinese()
  • doCn()
  • doRmb()
  • enUnder1000()
  • doEn()
  • numToZh()
  • doDate()
  • copy()

2.2.3 关键实现代码

以下是该工具的完整 <script setup> 实现(核心代码)。模板(<template>)与样式(<style>)已省略。

vue
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
import ToolPageShell from '@/components/ToolPageShell.vue'

const D = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖']
const err = ref('')
const cnInput = ref(''), cnOut = ref('')
const rmbInput = ref(''), rmbOut = ref('')
const enInput = ref(''), enOut = ref('')
const dateInput = ref(''), dateOut = ref('')
const tab = ref('cn')

function clearErr() { err.value = '' }
function fail(msg) { err.value = msg; return false }

// 严格校验:必须是合法数字,且不得为 NaN/Infinity,区间受限
function parseNum(s, max) {
  if (!s || typeof s !== 'string') return fail('请输入数字')
  const t = s.trim().replace(/,/g, '')
  if (!/^-?\d+(\.\d+)?$/.test(t)) return fail('输入不是合法数字')
  const n = Number(t)
  if (!isFinite(n)) return fail('数字超出可处理范围')
  if (max !== undefined && Math.abs(n) > max) return fail(`数字绝对值不能超过 ${max}`)
  return n
}

function sectionToChinese(s) {
  const I = ['', '拾', '佰', '仟']
  let str = '', started = false, zeroPending = false
  const arr = [Math.floor(s / 1000) % 10, Math.floor(s / 100) % 10, Math.floor(s / 10) % 10, s % 10]
  for (let i = 0; i < 4; i++) {
    const d = arr[i]
    if (d === 0) { if (started) zeroPending = true; continue }
    if (zeroPending) { str += '零'; zeroPending = false }
    str += D[d] + I[3 - i]
    started = true
  }
  return str
}

function intToChinese(num) {
  if (num === 0) return '零'
  const G = ['', '万', '亿', '兆']
  let rv = '', zero = false, n = num, g = 0
  while (n > 0 && g < 4) {
    const s = n % 10000
    n = Math.floor(n / 10000)
    if (s !== 0) {
      let sec = sectionToChinese(s) + G[g]
      if (zero && rv !== '') sec += '零'
      rv = sec + rv
      zero = false
    } else {
      zero = true
    }
    g++
  }
  return rv
}

function doCn() {
  cnOut.value = ''
  const n = parseNum(cnInput.value, 1e16)
  if (n === false) return
  const neg = n < 0
  cnOut.value = (neg ? '负' : '') + intToChinese(Math.round(Math.abs(n)))
}

function doRmb() {
  rmbOut.value = ''
  const n = parseNum(rmbInput.value, 1e14)
  if (n === false) return
  const neg = n < 0
  const abs = Math.abs(n)
  const intPart = Math.floor(abs)
  let dec = Math.round((abs - intPart) * 100)
  let ip = intPart, jiao = 0, fen = 0
  if (dec === 100) { ip = intPart + 1; dec = 0 }
  else { jiao = Math.floor(dec / 10); fen = dec % 10 }
  let str = ip > 0 ? intToChinese(ip) + '元' : ''
  if (jiao === 0 && fen === 0) {
    str += ip > 0 ? '整' : '零元整'
  } else {
    if (ip > 0 && jiao === 0 && fen > 0) str += '零'
    if (jiao > 0) str += D[jiao] + '角'
    if (fen > 0) str += D[fen] + '分'
  }
  rmbOut.value = (neg ? '负' : '') + str
}

const EN = ['', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten', 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen']
const TENS = ['', '', 'twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety']
function enUnder1000(n) {
  if (n < 20) return EN[n]
  if (n < 100) return TENS[Math.floor(n / 10)] + (n % 10 ? '-' + EN[n % 10] : '')
  return EN[Math.floor(n / 100)] + ' hundred' + (n % 100 ? ' ' + enUnder1000(n % 100) : '')
}
function doEn() {
  enOut.value = ''
  const n = parseNum(enInput.value, 1e15)
  if (n === false) return
  const neg = n < 0
  const abs = Math.round(Math.abs(n))
  const intPart = Math.floor(abs)
  const decPart = Math.round((abs - intPart) * 1e9) // up to 9 decimals
  const scales = ['', 'thousand', 'million', 'billion', 'trillion']
  let groups = []
  let t = intPart
  while (t > 0) { groups.push(t % 1000); t = Math.floor(t / 1000) }
  let words = []
  for (let i = groups.length - 1; i >= 0; i--) {
    if (groups[i] === 0) continue
    let w = enUnder1000(groups[i])
    if (scales[i]) w += ' ' + scales[i]
    words.push(w)
  }
  let str = words.length ? words.join(' ') : 'zero'
  if (decPart > 0) {
    str += ' point ' + String(decPart).padStart(9, '0').replace(/0+$/, '').split('').map(d => EN[Number(d)]).join(' ')
  }
  enOut.value = (neg ? 'negative ' : '') + str
}

const ZH = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九']
function numToZh(n) { return String(n).split('').map(c => ZH[Number(c)]).join('') }
function doDate() {
  dateOut.value = ''
  const m = dateInput.value.trim().match(/^(\d{1,4})-(\d{1,2})-(\d{1,2})$/)
  if (!m) return fail('日期格式应为 yyyy-MM-dd,如 2025-08-10')
  const y = +m[1], mo = +m[2], d = +m[3]
  if (mo < 1 || mo > 12 || d < 1 || d > 31) return fail('月份或日期超出范围')
  const moZh = mo <= 10 ? ZH[mo] + '月' : (mo === 11 ? '十一月' : '十二月')
  const dZh = d <= 10 ? ZH[d] + '日' : d < 20 ? '十' + ZH[d - 10] + '日' : d === 20 ? '二十日' : d < 30 ? '二十' + ZH[d - 20] + '日' : d === 30 ? '三十日' : '三十' + ZH[d - 30] + '日'
  dateOut.value = numToZh(y) + '年' + moZh + dZh
}

function copy(v) {
  navigator.clipboard?.writeText(v).then(() => ElMessage.success('已复制'))
}

2.3 效果截图

数字转中文大写 效果截图

工具访问地址:https://www.i91tools.com/tools/number-to-chinese