// ─── 常量定义 ────────────────────────────────────── const MONEY_DIGITS = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖']; const MONEY_UNITS = ['', '拾', '佰', '仟']; const MONEY_BIG_UNITS = ['', '萬', '億']; // 分组单位:个、万、亿 const SIMPLE_DIGITS = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九']; const SIMPLE_UNITS = ['', '十', '百', '千']; const SIMPLE_BIG_UNITS = ['', '万', '亿']; // 中文 → 数字 映射 const CAP_MAP = { '零':0,'壹':1,'贰':2,'叁':3,'肆':4,'伍':5,'陆':6,'柒':7,'捌':8,'玖':9 }; const SIM_MAP = { '零':0,'一':1,'二':2,'三':3,'四':4,'五':5,'六':6,'七':7,'八':8,'九':9 }; const U_MAP = { '十':10,'拾':10,'百':100,'佰':100,'千':1000,'仟':1000 }; // ─── 金额大写(财务) ─────────────────────────────── function numberToMoney(numStr) { numStr = numStr.replace(/,/g, '').trim(); let [intPart, decPart] = numStr.includes('.') ? numStr.split('.') : [numStr, '']; // 处理整数部分前导零,保留纯数字形式 intPart = intPart.replace(/^0+(?=\d)/, '') || '0'; // 特殊情况:整数部分为0且有分数 if (intPart === '0') { if (decPart) { // 纯小数,如 0.53 decPart = decPart.slice(0, 2).padEnd(2, '0'); const jiao = parseInt(decPart[0] || 0); const fen = parseInt(decPart[1] || 0); let decStr = ''; if (jiao > 0) decStr += MONEY_DIGITS[jiao] + '角'; if (fen > 0) decStr += MONEY_DIGITS[fen] + '分'; return '零元' + (decStr || '整'); } return '零元整'; } // 每4位分组:从右往左 const groups = []; let tmp = intPart; while (tmp.length > 0) { groups.unshift(tmp.slice(-4)); tmp = tmp.slice(0, -4); } let res = ''; let lastWasZero = false; // 上一个分组是否全零 for (let gi = 0; gi < groups.length; gi++) { const group = groups[gi]; const bigUnit = MONEY_BIG_UNITS[groups.length - 1 - gi] || ''; // 正确的单位索引 if (group === '0000') { // 全零分组,标记需要补零(如果有后续非零分组) lastWasZero = true; continue; } let groupStr = ''; for (let i = 0; i < group.length; i++) { const digit = parseInt(group[i]); const posFromRight = group.length - 1 - i; // 0=个位,1=拾位,2=佰位,3=仟位 if (digit === 0) { // 只有当下一位非零时才补零 if (i < group.length - 1 && parseInt(group[i + 1]) !== 0) { groupStr += '零'; } } else { groupStr += MONEY_DIGITS[digit] + MONEY_UNITS[posFromRight]; } } // 清理连续零 groupStr = groupStr.replace(/零+/g, '零'); // 如果前一个分组是全零,且当前分组开头不是零,需要补零 if (lastWasZero && !groupStr.startsWith('零')) { groupStr = '零' + groupStr; } lastWasZero = false; // 添加分组单位 res += groupStr + bigUnit; } // 财务规范:保留"壹拾",不简化为"拾" // if (res.startsWith('壹拾')) res = res.slice(2); if (!res || res === '') res = '零'; // 处理小数部分 let decStr = ''; if (decPart) { decPart = decPart.slice(0, 2).padEnd(2, '0'); const jiao = parseInt(decPart[0] || 0); const fen = parseInt(decPart[1] || 0); if (jiao > 0) decStr += MONEY_DIGITS[jiao] + '角'; if (fen > 0) decStr += MONEY_DIGITS[fen] + '分'; if (jiao === 0 && fen === 0) decStr = '整'; } else { decStr = '整'; } return res + '元' + decStr; } // ─── 中文小写 ─────────────────────────────────────── function numberToSimple(numStr) { let [intPart, decPart] = numStr.includes('.') ? numStr.split('.') : [numStr, '']; // 处理整数部分前导零,保留纯数字形式 intPart = intPart.replace(/^0+(?=\d)/, '') || '0'; // 特殊情况:整数部分为0 if (intPart === '0') { if (decPart) { decPart = decPart.slice(0, 2).padEnd(2, '0'); const jiao = parseInt(decPart[0] || 0); const fen = parseInt(decPart[1] || 0); let decStr = ''; if (jiao > 0) decStr += SIMPLE_DIGITS[jiao] + '角'; if (fen > 0) decStr += SIMPLE_DIGITS[fen] + '分'; return '零元' + (decStr || ''); } return '零'; } if (intPart.length > 12) return '超出范围'; const groups = []; let tmp = intPart; while (tmp.length > 0) { groups.unshift(tmp.slice(-4)); tmp = tmp.slice(0, -4); } let res = ''; let lastWasZero = false; for (let gi = 0; gi < groups.length; gi++) { const group = groups[gi]; const bigUnit = SIMPLE_BIG_UNITS[groups.length - 1 - gi] || ''; if (group === '0000') { lastWasZero = true; continue; } let groupStr = ''; for (let i = 0; i < group.length; i++) { const digit = parseInt(group[i]); const pos = group.length - 1 - i; if (digit === 0) { if (i < group.length - 1 && parseInt(group[i + 1]) !== 0) { groupStr += '零'; } } else { groupStr += SIMPLE_DIGITS[digit] + SIMPLE_UNITS[pos]; } } groupStr = groupStr.replace(/零+/g, '零'); if (lastWasZero && !groupStr.startsWith('零')) { groupStr = '零' + groupStr; } lastWasZero = false; res += groupStr + bigUnit; } // 10-19 的"一十"改为"十" if (res.startsWith('一十')) { res = res.slice(1); } if (!res) res = '零'; let decStr = ''; if (decPart) { decPart = decPart.slice(0, 2).padEnd(2, '0'); const jiao = parseInt(decPart[0] || 0); const fen = parseInt(decPart[1] || 0); if (jiao > 0) decStr += SIMPLE_DIGITS[jiao] + '角'; if (fen > 0) decStr += SIMPLE_DIGITS[fen] + '分'; // 如果有小数部分,加上"元" return res + '元' + decStr; } return res; } // ─── 中文 → 阿拉伯数字 ─────────────────────────────── function chineseToArabic(str) { str = str.trim(); const useCapital = /[壹贰叁肆伍陆柒捌玖]/.test(str); // 去掉"元整"等无关字符 str = str.replace(/元|整|圆|角|分/g, ''); let total = 0; // 按 亿 分割 if (/[億亿]/.test(str)) { const idx = str.search(/[億亿]/); const yiPart = str.substring(0, idx); // 亿位 const rest = str.substring(idx + 1); // 剩余部分 total = parseChineseGroup(yiPart, useCapital) * 100000000; // 剩余部分再按万分割 if (/[萬万]/.test(rest)) { const idx2 = rest.search(/[萬万]/); const wanPart = rest.substring(0, idx2); const gePart = rest.substring(idx2 + 1); total += parseChineseGroup(wanPart, useCapital) * 10000; total += parseChineseGroup(gePart, useCapital); } else { total += parseChineseGroup(rest, useCapital); } return total; } // 按 万 分割 if (/[萬万]/.test(str)) { const idx = str.search(/[萬万]/); const wanPart = str.substring(0, idx); const gePart = str.substring(idx + 1); return parseChineseGroup(wanPart, useCapital) * 10000 + parseChineseGroup(gePart, useCapital); } return parseChineseGroup(str, useCapital); } function parseChineseGroup(s, useCapital) { const digitMap = useCapital ? CAP_MAP : SIM_MAP; let total = 0; // 最终结果 let current = 0; // 当前组内累积值 let digitBuf = ''; // 连续数字字符缓冲区,拼成完整多位数 for (let i = 0; i < s.length; i++) { const ch = s[i]; if (digitMap[ch] !== undefined) { // 数字字符:拼接到缓冲区(构建多位数,如 "一二三" → 123) digitBuf += digitMap[ch]; } else if (U_MAP[ch] !== undefined) { // 单位字符:将缓冲区数字转为整数后乘以单位 const unitVal = U_MAP[ch]; const num = parseInt(digitBuf || '0', 10); if (num === 0) { // 没有前置数字,如 "十"、"百" 开头(相当于 "一十"、"一百") current += unitVal; } else { // 有前置数字,如 "二十"、"三百" current += num * unitVal; } digitBuf = ''; } // 忽略其他字符(如 "零") } // 处理末尾没有单位的数字,如 "一百二十三" 的 "三" if (digitBuf !== '') { current += parseInt(digitBuf, 10); } return current; } // ─── UI 逻辑 ─────────────────────────────────────── let currentMode = 'money'; // 使用事件委托处理模式切换 document.querySelector('.mode-tabs').addEventListener('click', function(e) { if (e.target.classList.contains('mode-tab')) { const mode = e.target.dataset.mode; currentMode = mode; document.querySelectorAll('.mode-tab').forEach(t => t.classList.remove('active')); e.target.classList.add('active'); document.getElementById('panel-arabic-to-chinese').style.display = (mode === 'money' || mode === 'capital') ? 'block' : 'none'; document.getElementById('panel-chinese-to-arabic').style.display = mode === 'chinese' ? 'block' : 'none'; clearResults(); } }); function fillInput(val) { document.getElementById('inputNum').value = val; convertToChinese(); } function appendChar(ch) { const ta = document.getElementById('inputCN'); ta.value += ch; ta.focus(); // 触发自动转换 clearTimeout(window.inputTimer); window.inputTimer = setTimeout(convertToArabic, 600); } function showError(id, msg) { const el = document.getElementById(id); el.textContent = msg; el.classList.add('show'); } function hideError(id) { document.getElementById(id).classList.remove('show'); } function setResult(id, val) { const el = document.getElementById(id); el.textContent = val; el.classList.remove('empty'); } function clearResults() { ['resMoney', 'resSimple', 'resArabic'].forEach(id => { const el = document.getElementById(id); el.textContent = '等待转换…'; el.classList.add('empty'); }); hideError('errorNum'); hideError('errorCN'); } function isValidNum(s) { return /^-?\d+(\.\d+)?$/.test(s.trim()) && !isNaN(parseFloat(s)); } function convertToChinese() { hideError('errorNum'); const raw = document.getElementById('inputNum').value.replace(/,/g, '').trim(); if (!raw) { showError('errorNum', '请输入数字'); return; } if (!isValidNum(raw)) { showError('errorNum', '请输入有效的数字'); return; } const num = parseFloat(raw); if (Math.abs(num) > 999999999999) { showError('errorNum', '数字超出范围(最大 ±9999亿)'); return; } const sign = raw.startsWith('-') ? '负' : ''; const absStr = Math.abs(num).toString(); setResult('resMoney', sign + numberToMoney(absStr)); setResult('resSimple', sign + numberToSimple(absStr)); } function convertToArabic() { hideError('errorCN'); const raw = document.getElementById('inputCN').value.replace(/,/g, '').trim(); if (!raw) { showError('errorCN', '请输入中文数字'); return; } const result = chineseToArabic(raw); if (result === 0 && !/零|〇/.test(raw)) { showError('errorCN', '无法识别的中文数字'); return; } setResult('resArabic', result.toLocaleString('zh-CN')); } function copyText(elId, btn) { const text = document.getElementById(elId).textContent; if (!text || text.includes('等待转换')) return; navigator.clipboard.writeText(text).then(() => { const orig = btn.textContent; btn.textContent = '已复制 ✓'; btn.classList.add('copied'); setTimeout(() => { btn.textContent = orig; btn.classList.remove('copied'); }, 1500); }); } // 回车触发转换 document.getElementById('inputNum').addEventListener('keydown', e => { if (e.key === 'Enter') convertToChinese(); }); // 自动转换(输入后稍等) window.inputTimer = null; document.getElementById('inputNum').addEventListener('input', () => { clearTimeout(window.inputTimer); window.inputTimer = setTimeout(convertToChinese, 600); }); document.getElementById('inputCN').addEventListener('input', () => { clearTimeout(window.inputTimer); window.inputTimer = setTimeout(convertToArabic, 600); }); // 初始化默认空状态 ['resMoney', 'resSimple', 'resArabic'].forEach(id => { const el = document.getElementById(id); el.textContent = '等待转换…'; el.classList.add('empty'); });