diff --git a/README.md b/README.md index a71fd7c..95ba4c6 100644 --- a/README.md +++ b/README.md @@ -20,9 +20,8 @@ vue + vuex + vue router + TypeScript(支持 JavaScript) 模板 - [目录结构](#目录结构) - [风格建议](#风格建议) - [其他建议](#其他建议) - - [优化](#优化) - - [web 页面](#web%20页面) - - [工程](#工程) + - [配置和优化](#配置和优化) + - [优化](优化) - [部署](<#部署(nginx)>) - [备忘](#备忘) - [文档](#文档) @@ -33,8 +32,8 @@ vue + vuex + vue router + TypeScript(支持 JavaScript) 模板 ## 环境要求 -- `Node.js`: 建议 v12.13.1 -- `yarn`: 建议 v1.21.1 +- `Node.js`: v12 ([fibers](https://github.com/laverdet/node-fibers#supported-platforms) v4.0.2 不支持 Node v13) +- `yarn`: 最新 ### 建议开发环境 @@ -53,7 +52,7 @@ VSCode 插件 - `Vue Devtools`: 最新 -> 推荐工具: [`@vue/cli`](https://cli.vuejs.org/zh/guide), 全局安装时可使用 `vue ui` 命令启动图形化界面管理项目 +> 推荐工具: [`@vue/cli`](https://cli.vuejs.org/zh/guide)(最新), 全局安装时可使用 `vue ui` 命令启动图形化界面管理项目 > 推荐字体: [FiraCode](https://github.com/tonsky/FiraCode) @@ -74,6 +73,8 @@ yarn # 安装依赖 git config core.ignorecase false # 使git对文件名大小写敏感 ``` +- 安装后需要在 `yarn.lock` (或 `package-lock.json` ) 中, 指定**所有**依赖的 `mini-css-extract-plugin` 的版本为 `package.json` 对应版本然后再次安装(*为了与原插件版本一致, 更新了但没改版本, 导致再次安装的可能是稍旧的版本*, 建议直接下载并替换该依赖) + ### 开发环境(开发调试时使用) ```bash @@ -561,22 +562,17 @@ yarn vue-cli-service help # [命令] : 比如 yarn vue-cli-service help test:e2e // ... ``` -### 配置 & 优化 +### 配置和优化 -#### web 页面 +可通过 [vue.config.js](vue.config.js) (入口)文件配置工具链; `.env.*` 配置环境变量; 根目录下各配置文件配置相应工具 -请参照 [vue.config.js](vue.config.js) 文件中 _chainWebpack_ 的注释进行配置 +#### 优化 - 减小图片大小(比如背景图片等) - 对多个 js chunk 共同依赖的模块进行缓存/单独提取(cacheGroups) -- 视情况对 css 文件进行合并(比如按入口等, 不设置则按 chunk)【webpack 5 支持设置 css chunk 的 minSize/maxSize 啦】 +- 相同chunk下的基础样式或各个皮肤样式文件合并(比如css和scss) 或 其他合理的合并策略【webpack 5 支持设置 css chunk 的 minSize/maxSize 啦】 - [现代模式](https://cli.vuejs.org/zh/guide/browser-compatibility.html#现代模式) -#### 工程 - -- 需要在 `yarn.lock` (或 `package-lock.json` ) 中, 指定**所有** `mini-css-extract-plugin` 的版本为 `package.json` 对应版本 -- 相同chunk下的基础样式(非皮肤样式)文件合并(比如css和scss) - ## 部署(nginx) - chunk hash 长度: 修改 [webpack.optimize.SplitChunksPlugin](node_modules/webpack/lib/optimize/SplitChunksPlugin.js) diff --git a/package.json b/package.json index 55fe105..fba703b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vue-tpl", "author": "毛瑞 ", - "version": "1.2.2", + "version": "1.2.3", "private": false, "license": "MIT", "keywords": [ diff --git a/src/utils/numToCN.ts b/src/utils/numToCN.ts index fba43d5..ba2a0ec 100644 --- a/src/utils/numToCN.ts +++ b/src/utils/numToCN.ts @@ -1,104 +1,119 @@ /** 阿拉伯数字转中文数字(最高支持描述万万亿) * @param {string|number} number 阿拉伯数字 - * @param {number} unit 1: 不需要单位 2: 不需要单位并且保留开头的0 + * @param {number} describe 描述 默认:使用描述 1:不使用描述 2:不使用描述且保留开头的0 + * @param {number} cases 大小写 默认:中文小写 1:中文小写但是使用大写零 2:中文大写 * * @returns {string} 中文数字 */ -export default function numToCN(number: string | number, noUnit?: 1 | 2) { +export default function( + number: string | number, + describe?: 0 | 1 | 2, + cases?: 0 | 1 | 2 +) { if (!number && number !== 0) { return '' } + let NUM: string // 数字 + let UNIT: string // 单位 + let ZERO: string + let TRIM_BEFORE: RegExp // 去开头的0和单位 + if (cases === 2) { + ZERO = '零' + NUM = '零壹贰叁肆伍陆柒捌玖' + UNIT = '拾佰仟万拾佰仟亿拾佰仟万拾佰仟' + TRIM_BEFORE = /^零*[拾佰仟万亿]*零*/ + } else { + ZERO = cases ? '零' : '〇' + NUM = ZERO + '一二三四五六七八九' + UNIT = '十百千万十百千亿十百千万十百千' + TRIM_BEFORE = new RegExp(`^${ZERO}*[十百千万亿]*${ZERO}*`) + cases = 0 // 标识小写 + } + const GOLD = '亿' // 最大单位 + const SILVER = '万' // 溢出单位 一二万万亿 const POINT = '点' - const NUM = '零一二三四五六七八九' - const UNIT = '十百千万十百千亿十百千万十百千' const SIGN = { '+': '正', '-': '负' } const LEN_UNIT = 15 // UNIT.length - const ZERO = '零' // NUM[0] - const UNIT_MAX = '亿' // 最大单位 - const TRIPLE = '万' // 溢出单位 一二万万亿 - const TRIPLE_POS = 11 // 万的最后位置 十万亿 - const TRIM_BEFORE = /^零*[十百千万亿]*/ // 去开头的0和单位 + const SILVER_POS = 11 // 万的最后位置 十万亿 + return String(number).replace( /([+-])?(\d+)(\.\d+)?/g, - (match, sign: '+' | '-', integer: string, decimal: string) => { - let result = '' + (char, sign: '+' | '-', integer: string, unit: string) => { + let cn = '' let index // 小数部分(保留末尾0) - if (decimal) { - index = decimal.length + if (unit) { + index = unit.length while (--index) { - result = NUM[decimal[index] as any] + result + cn = NUM[unit[index] as any] + cn } - result = POINT + result + cn = POINT + cn } - let char - let unit let pos - let indexUnit = -1 // [-1, LEN_UNIT) + number = -1 // [-1, LEN_UNIT) index = integer.length // 整数部分 while (index--) { char = NUM[integer[index] as any] - if (!noUnit) { - if (indexUnit < LEN_UNIT) { + if (!describe) { + if (number < LEN_UNIT) { // 未超过最大可描述数值 - unit = UNIT[indexUnit++] || '' + unit = UNIT[number++] || '' if (ZERO === char) { - char = result[0] - switch (char) { - case (pos = UNIT.indexOf(char)) >= 0 && char: + if ((pos = UNIT.indexOf((char = cn[0]))) >= 0) { + if (number > SILVER_POS && char === GOLD) { + // 万亿 + char = unit + } else if (pos < UNIT.indexOf(unit)) { // 替换上大的单位 - if (indexUnit > TRIPLE_POS && char === UNIT_MAX) { - // 万亿 - char = unit - } else if ((pos as any) < UNIT.indexOf(unit)) { - char = unit - result = result.substring(1) - } else { - char = '' - } - break - - default: - // ZERO POINT falsy ... - char = NUM.indexOf(char) > 0 ? unit + ZERO : unit + char = unit + cn = cn.substring(1) + } else { + char = '' + } + } else { + // POINT falsy ... + char = NUM.indexOf(char) > 0 ? unit + ZERO : unit } } else if (unit) { - ;(pos = UNIT.indexOf(result[0])) >= 0 && + ;(pos = UNIT.indexOf(cn[0])) >= 0 && pos < UNIT.indexOf(unit) && - (result = result.substring(1)) + (cn = cn.substring(1)) char += unit } - } else if (indexUnit === LEN_UNIT) { - indexUnit++ // 使只执行一次 - result = TRIPLE + result + } else if (number === LEN_UNIT) { + number++ // 使只执行一次 + cn = SILVER + cn } } - result = char + result + cn = char + cn } - char = result[0] // 兼职首字 - if (!char || char === POINT) { - // 补零 0/0.1 - result = ZERO + result - } else if (char === NUM[1] && result[1] === UNIT[0]) { - // 一十* => 十* - result = result.substring(1) - } else if (noUnit !== 2 && (char === ZERO || UNIT.indexOf(char) >= 0)) { - // 去零及单位开头的 - ZERO === - (result = - result.replace( - TRIM_BEFORE, - noUnit || indexUnit > LEN_UNIT ? '' : ZERO - ) || ZERO) && (sign = 0 as any) + if (describe !== 2) { + char = cn[0] // 兼职首字 + if (!char || char === POINT) { + cn = ZERO + cn // 补0 0/0.1 + } else { + if (char === ZERO || UNIT.indexOf(char) >= 0) { + // 去0及单位开头的 + unit = describe || number > LEN_UNIT ? '' : ZERO + cn = cn.replace(TRIM_BEFORE, unit) || ZERO + unit && (unit = cn[1]) && unit !== POINT && (cn = cn.substring(1)) + } + // 0 及 一十* => 十* + ZERO === cn + ? (sign = 0 as any) + : cases || + (char === NUM[1] && cn[1] === UNIT[0] && (cn = cn.substring(1))) + } } + // 加正负号 - return (SIGN[sign] || '') + result + return (SIGN[sign] || '') + cn } ) } diff --git a/tests/unit/utils/numToCN.spec.ts b/tests/unit/utils/numToCN.spec.ts index 3a05f33..52cf856 100644 --- a/tests/unit/utils/numToCN.spec.ts +++ b/tests/unit/utils/numToCN.spec.ts @@ -5,16 +5,16 @@ describe('@/utils/numToCN: 阿拉伯数字和中文转换', () => { it('numToCN: 阿拉伯数字转中文数字', () => { expect(numToCN('')).toBe('') - expect(numToCN(0)).toBe('零') - expect(numToCN('00')).toBe('零') - expect(numToCN('+000000')).toBe('零') - expect(numToCN('-0000000000')).toBe('零') - expect(numToCN('00000000000000000')).toBe('零') + expect(numToCN(0)).toBe('〇') + expect(numToCN('00')).toBe('〇') + expect(numToCN('+000000')).toBe('〇') + expect(numToCN('-0000000000')).toBe('〇') + expect(numToCN('00000000000000000')).toBe('〇') - expect(numToCN(-0.1)).toBe('负零点一') - expect(numToCN(0.01)).toBe('零点零一') - expect(numToCN('0.01230')).toBe('零点零一二三零') - expect(numToCN('-00000.01200')).toBe('负零点零一二零零') + expect(numToCN(-0.1)).toBe('负〇点一') + expect(numToCN(0.01)).toBe('〇点〇一') + expect(numToCN('0.01230')).toBe('〇点〇一二三〇') + expect(numToCN('-00000.01200')).toBe('负〇点〇一二〇〇') expect(numToCN(2)).toBe('二') expect(numToCN('+2')).toBe('正二') @@ -31,44 +31,66 @@ describe('@/utils/numToCN: 阿拉伯数字和中文转换', () => { expect(numToCN(100)).toBe('一百') expect(numToCN(120)).toBe('一百二十') - expect(numToCN(103)).toBe('一百零三') + expect(numToCN(103)).toBe('一百〇三') expect(numToCN(1000)).toBe('一千') - expect(numToCN(1004)).toBe('一千零四') - expect(numToCN(1030)).toBe('一千零三十') + expect(numToCN(1004)).toBe('一千〇四') + expect(numToCN(1030)).toBe('一千〇三十') expect(numToCN(1200)).toBe('一千二百') expect(numToCN(10000)).toBe('一万') - expect(numToCN(10005)).toBe('一万零五') - expect(numToCN(10040)).toBe('一万零四十') - expect(numToCN(10045)).toBe('一万零四十五') - expect(numToCN(10300)).toBe('一万零三百') - expect(numToCN(10305)).toBe('一万零三百零五') - expect(numToCN(10340)).toBe('一万零三百四十') - expect(numToCN(10345)).toBe('一万零三百四十五') + expect(numToCN(10005)).toBe('一万〇五') + expect(numToCN(10040)).toBe('一万〇四十') + expect(numToCN(10045)).toBe('一万〇四十五') + expect(numToCN(10300)).toBe('一万〇三百') + expect(numToCN(10305)).toBe('一万〇三百〇五') + expect(numToCN(10340)).toBe('一万〇三百四十') + expect(numToCN(10345)).toBe('一万〇三百四十五') expect(numToCN(12000)).toBe('一万二千') - expect(numToCN(12005)).toBe('一万二千零五') - expect(numToCN(12040)).toBe('一万二千零四十') - expect(numToCN(12045)).toBe('一万二千零四十五') + expect(numToCN(12005)).toBe('一万二千〇五') + expect(numToCN(12040)).toBe('一万二千〇四十') + expect(numToCN(12045)).toBe('一万二千〇四十五') expect(numToCN(12300)).toBe('一万二千三百') - expect(numToCN(12305)).toBe('一万二千三百零五') + expect(numToCN(12305)).toBe('一万二千三百〇五') expect(numToCN(12340)).toBe('一万二千三百四十') expect(numToCN(12345)).toBe('一万二千三百四十五') - expect(numToCN(100006)).toBe('十万零六') - expect(numToCN(1004067)).toBe('一百万零四千零六十七') - expect(numToCN(1014067)).toBe('一百零一万四千零六十七') + expect(numToCN(100006)).toBe('十万〇六') + expect(numToCN(120406)).toBe('十二万〇四百〇六') + expect(numToCN(1004067)).toBe('一百万〇四千〇六十七') + expect(numToCN(1014067)).toBe('一百〇一万四千〇六十七') expect(numToCN(123456789000000)).toBe( '一百二十三万四千五百六十七亿八千九百万' ) expect(numToCN('012345678909876543210')).toBe( - '一二三四万五千六百七十八万九千零九十八亿七千六百五十四万三千二百一十' + '一二三四万五千六百七十八万九千〇九十八亿七千六百五十四万三千二百一十' ) - expect(numToCN('012345.67890', 1)).toBe('一二三四五点六七八九零') - expect(numToCN('012345.67890', 2)).toBe('零一二三四五点六七八九零') - expect(numToCN('-012345678909876543210', 1)).toBe( - '负一二三四五六七八九零九八七六五四三二一零' + expect(numToCN('012345.67890', 1)).toBe('一二三四五点六七八九〇') + // expect(numToCN('.7890', 2)).toBe('点七八九〇') + expect(numToCN('0123456.7890', 2)).toBe('〇一二三四五六点七八九〇') + expect(numToCN('-012345678909876543210.1', 1)).toBe( + '负一二三四五六七八九〇九八七六五四三二一〇点一' + ) + + expect(numToCN('012345.67890', 0, 1)).toBe('一万二千三百四十五点六七八九零') + expect(numToCN('0123456.7890', 0, 2)).toBe( + '壹拾贰万叁仟肆佰伍拾陆点柒捌玖零' + ) + expect(numToCN('-012345678909876543210.1', 0, 2)).toBe( + '负壹贰叁肆万伍仟陆佰柒拾捌万玖仟零玖拾捌亿柒仟陆佰伍拾肆万叁仟贰佰壹拾点壹' + ) + + expect(numToCN('012345.67890', 1, 1)).toBe('一二三四五点六七八九零') + expect(numToCN('0123456.7890', 1, 2)).toBe('壹贰叁肆伍陆点柒捌玖零') + expect(numToCN('-012345678909876543210.1', 1, 2)).toBe( + '负壹贰叁肆伍陆柒捌玖零玖捌柒陆伍肆叁贰壹零点壹' + ) + + expect(numToCN('012345.67890', 2, 1)).toBe('零一二三四五点六七八九零') + expect(numToCN('0123456.7890', 2, 2)).toBe('零壹贰叁肆伍陆点柒捌玖零') + expect(numToCN('-012345678909876543210.1', 2, 2)).toBe( + '负零壹贰叁肆伍陆柒捌玖零玖捌柒陆伍肆叁贰壹零点壹' ) }) })