feat(new tool): RMB Uppercase Converter

Fix #891. Taken from 6297c10cb0
This commit is contained in:
sharevb 2024-05-01 14:48:01 +02:00 committed by ShareVB
parent cb5b462e11
commit 6530d37b1a
5 changed files with 162 additions and 0 deletions

View file

@ -6,6 +6,7 @@ import { tool as asciiTextDrawer } from './ascii-text-drawer';
import { tool as textToUnicode } from './text-to-unicode'; import { tool as textToUnicode } from './text-to-unicode';
import { tool as safelinkDecoder } from './safelink-decoder'; 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 pdfSignatureChecker } from './pdf-signature-checker';
import { tool as numeronymGenerator } from './numeronym-generator'; import { tool as numeronymGenerator } from './numeronym-generator';
import { tool as macAddressGenerator } from './mac-address-generator'; import { tool as macAddressGenerator } from './mac-address-generator';
@ -107,6 +108,7 @@ export const toolsByCategory: ToolCategory[] = [
listConverter, listConverter,
tomlToJson, tomlToJson,
tomlToYaml, tomlToYaml,
rmbNumbers,
], ],
}, },
{ {

View file

@ -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'),
});

View file

@ -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: '整',
},
]);
});
});

View file

@ -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: '整' },
];
};

View file

@ -0,0 +1,44 @@
<script setup lang="ts">
import { useThemeVars } from 'naive-ui';
import { rmb } from './rmb-numbers.service';
const themeVars = useThemeVars();
const inputRmb = ref(23);
const outputRmb = computed(() => rmb(inputRmb.value)); ;
</script>
<template>
<div flex flex-col gap-2>
<c-card title="Lower Case Amount">
<n-input-number v-model:value="inputRmb" max="100000000000" min="0" placeholder="Enter the amount in lowercase (example: 1314.52)" :show-button="false" w-full />
</c-card>
<div my-16px divider />
<c-card title="Amount in Capital Letters" flex flex-col>
<div m-0 m-x-auto>
<span
v-for="(item, index) in outputRmb"
:key="index"
:class="item.type"
>
{{ item.value }}
</span>
</div>
</c-card>
</div>
</template>
<style lang="less" scoped>
.unit {
font-size: 1.4em;
color: v-bind('themeVars.successColor');
}
.number {
font-size: 2.4em;
}
.cut {
font-size: 1.4em;
color: v-bind('themeVars.errorColor');
}
</style>