diff --git a/src/tools/index.ts b/src/tools/index.ts index aa861c93..31bfd12c 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -6,6 +6,7 @@ import { tool as asciiTextDrawer } from './ascii-text-drawer'; import { tool as textToUnicode } from './text-to-unicode'; import { tool as safelinkDecoder } from './safelink-decoder'; +import { tool as rmbNumbers } from './rmb-numbers'; import { tool as pdfSignatureChecker } from './pdf-signature-checker'; import { tool as numeronymGenerator } from './numeronym-generator'; import { tool as macAddressGenerator } from './mac-address-generator'; @@ -107,6 +108,7 @@ export const toolsByCategory: ToolCategory[] = [ listConverter, tomlToJson, tomlToYaml, + rmbNumbers, ], }, { diff --git a/src/tools/rmb-numbers/index.ts b/src/tools/rmb-numbers/index.ts new file mode 100644 index 00000000..75189a94 --- /dev/null +++ b/src/tools/rmb-numbers/index.ts @@ -0,0 +1,12 @@ +import { CurrencyYen } from '@vicons/tabler'; +import { defineTool } from '../tool'; + +export const tool = defineTool({ + name: 'RMB Uppercase Converter', + path: '/rmb-numbers', + description: 'RMB/Renminbi Capitalization Conversion Tool', + keywords: ['rmb', 'renminbi', 'cny', 'number', 'uppercase', '人民币', '大写', '转换'], + component: () => import('./rmb-numbers.vue'), + icon: CurrencyYen, + createdAt: new Date('2024-04-29'), +}); diff --git a/src/tools/rmb-numbers/rmb-numbers.service.test.ts b/src/tools/rmb-numbers/rmb-numbers.service.test.ts new file mode 100644 index 00000000..d12158b2 --- /dev/null +++ b/src/tools/rmb-numbers/rmb-numbers.service.test.ts @@ -0,0 +1,37 @@ +import { describe, expect, it } from 'vitest'; +import { rmb } from './rmb-numbers.service'; + +describe('rmb-case-converter', () => { + it('rmb to convert rmb numbers to uppercase', async () => { + expect(rmb(123)).to.deep.equal([ + { + type: 'number', + value: '壹', + }, + { + type: 'unit', + value: '佰', + }, + { + type: 'number', + value: '贰', + }, + { + type: 'unit', + value: '拾', + }, + { + type: 'number', + value: '叁', + }, + { + type: 'unit', + value: '元', + }, + { + type: 'cut', + value: '整', + }, + ]); + }); +}); diff --git a/src/tools/rmb-numbers/rmb-numbers.service.ts b/src/tools/rmb-numbers/rmb-numbers.service.ts new file mode 100644 index 00000000..ef1cebb6 --- /dev/null +++ b/src/tools/rmb-numbers/rmb-numbers.service.ts @@ -0,0 +1,67 @@ +export { rmb }; + +const numbers = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖']; +const leftUnits = ['元', '拾', '佰', '仟', '万', '拾', '佰', '仟', '亿', '拾', '佰', '仟', '万', '拾', '佰', '仟', '万']; +const rightUnits = ['角', '分']; + +function rmb(value: number) { + if (Object.prototype.toString.call(value) === '[object Number]' && value >= 0.01) { + const fragment: any = []; + const [leftValues, rightValues] = String(value).split('.').map(part => part.split('').map(i => Number(i))); + + const leftValueLength = leftValues.length; // 整数部分位数 + const unit1 = leftValueLength - 1; // 元位 + const unit5 = leftValueLength - 5; // 万位 + const unit9 = leftValueLength - 9; // 亿位 + const unit13 = leftValueLength - 13; // 万亿位 + const unit17 = leftValueLength - 17; // 万万亿位 + const hasLeftValue = leftValueLength > 1 || leftValues[0] > 0; // 整数部分不为0 + const hasRightValue = rightValues && (rightValues[0] > 0 || rightValues[1] > 0); // 小数部分不为0 + const has678Value = leftValues[unit5 - 1] > 0 || leftValues[unit5 - 2] > 0 || leftValues[unit5 - 3] > 0; // 拾万、佰万或仟万位不为0 + const overflowIndex = leftValueLength - leftUnits.length; // 溢出位索引 + + let leftUnitIndex = 0; + for (let i = leftValueLength - 1; i >= 0; i--) { + if (leftValues[i] === 0 && (i === unit5 || i === unit9 || i === unit13 || i === unit17) && leftValues[i + 1] > 0) { + // 当前位为0,且当前位为万、亿、万亿、万万亿,且低一位不为0 + fragment.unshift({ type: 'number', value: numbers[leftValues[i]] }); + } + + if ((leftValues[i] > 0) || (i === unit1 && hasLeftValue) || (i === unit5 && has678Value) || i === unit9 || i === unit13 || i === unit17) { + // 元、万、亿、万亿、万万亿或当前位不为0 + fragment.unshift({ type: 'unit', value: leftUnits[leftUnitIndex] }); + } + + if (leftValues[i] > 0 || (leftValues[i + 1] > 0 && i !== unit5 && i !== unit9 && i !== unit13 && i !== unit17) || i <= overflowIndex) { + // 当前位不为0,或低一位不为0且当前位非万、亿、万亿、万万亿,或当前为溢出位 + fragment.unshift({ type: 'number', value: numbers[leftValues[i]] }); + } + + leftUnitIndex++; + } + + if (hasRightValue) { + // 角 + if (rightValues[0] > 0 || hasLeftValue) { // 角位不为0,或整数位不为0 + fragment.push({ type: 'number', value: numbers[rightValues[0]] }); + } + if (rightValues[0] > 0) { // 角位不为0 + fragment.push({ type: 'unit', value: rightUnits[0] }); + } + // 分 + if (rightValues[1] > 0) { + fragment.push({ type: 'number', value: numbers[rightValues[1]] }); + fragment.push({ type: 'unit', value: rightUnits[1] }); + } + } + else { // 没有小数位 + fragment.push({ type: 'cut', value: '整' }); + } + return fragment; + } + return [ + { type: 'number', value: '零' }, + { type: 'unit', value: '元' }, + { type: 'cut', value: '整' }, + ]; +}; diff --git a/src/tools/rmb-numbers/rmb-numbers.vue b/src/tools/rmb-numbers/rmb-numbers.vue new file mode 100644 index 00000000..40b5e9f2 --- /dev/null +++ b/src/tools/rmb-numbers/rmb-numbers.vue @@ -0,0 +1,44 @@ + + + + +