jinbi-utils 1.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/.babelrc +19 -0
  2. package/.cz-config.js +55 -0
  3. package/.dockerignore +3 -0
  4. package/.editorconfig +12 -0
  5. package/.eslintignore +8 -0
  6. package/.eslintrc.js +54 -0
  7. package/Dockerfile +3 -0
  8. package/README.md +160 -0
  9. package/api-extractor.json +15 -0
  10. package/commitlint.config.js +3 -0
  11. package/dist/index.esm.js +1277 -0
  12. package/dist/index.esm.min.js +15 -0
  13. package/dist/index.umd.js +1348 -0
  14. package/dist/index.umd.min.js +16 -0
  15. package/docs/assets/images/icons.png +0 -0
  16. package/docs/assets/images/icons@2x.png +0 -0
  17. package/docs/assets/images/widgets.png +0 -0
  18. package/docs/assets/images/widgets@2x.png +0 -0
  19. package/docs/assets/js/main.js +1 -0
  20. package/docs/assets/js/search.json +1 -0
  21. package/docs/globals.html +144 -0
  22. package/docs/index.html +147 -0
  23. package/docs/interfaces/file.compressimgqualitycallback.html +212 -0
  24. package/docs/interfaces/file.compressimgscalecallback.html +206 -0
  25. package/docs/interfaces/file.exportbyblobparams.html +294 -0
  26. package/docs/interfaces/file.filesizeobject.html +227 -0
  27. package/docs/interfaces/file.genexportbyblobparams.html +237 -0
  28. package/docs/modules/common.html +188 -0
  29. package/docs/modules/date.html +364 -0
  30. package/docs/modules/file.html +452 -0
  31. package/docs/modules/number.html +356 -0
  32. package/docs/modules/object.html +245 -0
  33. package/docs/modules/print.html +183 -0
  34. package/docs/modules/string.html +352 -0
  35. package/docs/modules/validate.html +389 -0
  36. package/jest.config.js +15 -0
  37. package/package.json +76 -0
  38. package/rollup.config.js +65 -0
  39. package/src/common/index.ts +323 -0
  40. package/src/constant/common.constant.ts +13 -0
  41. package/src/date/index.ts +143 -0
  42. package/src/file/index.ts +296 -0
  43. package/src/http/http.ts +79 -0
  44. package/src/http/httpEnums.ts +61 -0
  45. package/src/index.ts +10 -0
  46. package/src/number/index.ts +190 -0
  47. package/src/object/index.ts +54 -0
  48. package/src/print/index.ts +102 -0
  49. package/src/string/index.ts +111 -0
  50. package/src/validate/index.ts +78 -0
  51. package/src/wecom/wecom.ts +75 -0
  52. package/test/common/index.test.ts +19 -0
  53. package/test/date/index.test.ts +107 -0
  54. package/test/file/index.test.ts +104 -0
  55. package/test/number/index.test.ts +108 -0
  56. package/test/object/index.test.ts +20 -0
  57. package/test/string/index.test.ts +82 -0
  58. package/tsconfig.json +39 -0
  59. package/typedoc.json +9 -0
  60. package/types/common/index.d.ts +47 -0
  61. package/types/constant/common.constant.d.ts +12 -0
  62. package/types/date/index.d.ts +60 -0
  63. package/types/file/index.d.ts +96 -0
  64. package/types/http/http.d.ts +17 -0
  65. package/types/http/httpEnums.d.ts +53 -0
  66. package/types/index.d.ts +10 -0
  67. package/types/number/index.d.ts +62 -0
  68. package/types/object/index.d.ts +25 -0
  69. package/types/print/index.d.ts +11 -0
  70. package/types/string/index.d.ts +53 -0
  71. package/types/validate/index.d.ts +45 -0
  72. package/types/wecom/wecom.d.ts +3 -0
@@ -0,0 +1,296 @@
1
+ /**
2
+ * 文件处理相关
3
+ * @packageDocumentation
4
+ * @module File
5
+ * @preferred
6
+ */
7
+
8
+ import { isEmpty } from '../common';
9
+ import { ceil } from '../number';
10
+
11
+ export type FileSizeUnit = 'B' |'KB' | 'MB' | 'GB' | 'TB';
12
+
13
+ export interface FileSizeObject {
14
+ size: number;
15
+ unit: FileSizeUnit;
16
+ }
17
+ /**
18
+ * 计算文件大小
19
+ #### 使用说明
20
+ ```
21
+ calcFileSize(100) 返回 { size: 100, unit: 'B' }
22
+ calcFileSize(1024) 返回 { size: 1, unit: 'KB' }
23
+ calcFileSize(1024, 'KB') 返回 { size: 1, unit: 'MB' }
24
+ calcFileSize(1126.4, 'mb') 返回 { size: 1.1, unit: 'GB' }
25
+ calcFileSize(Math.pow(1024, 2), 'kb') 返回 { size: 1, unit: 'GB' }
26
+ ```
27
+ * @param size 单位 k
28
+ * @returns number
29
+ */
30
+ export function calcFileSize(size: number, unit: FileSizeUnit = 'B'): FileSizeObject {
31
+ const unitList: Array<FileSizeUnit> = ['B', 'KB', 'MB', 'GB', 'TB'];
32
+
33
+ let currentUnitIndex = unitList.indexOf(unit.toUpperCase() as FileSizeUnit);
34
+
35
+ currentUnitIndex = currentUnitIndex === -1 ? 0 : currentUnitIndex;
36
+
37
+ // todo 单位换算。
38
+ while (size >= 1024 && currentUnitIndex < unitList.length) {
39
+ size = size / 1024;
40
+ currentUnitIndex += 1;
41
+ }
42
+ return {
43
+ size,
44
+ unit: unitList[currentUnitIndex],
45
+ };
46
+ }
47
+
48
+ /**
49
+ * 文件大小转换
50
+ #### 使用说明
51
+ ```
52
+ fileSizeFormat('') 返回 -
53
+ fileSizeFormat('1024') 返回 1KB
54
+ fileSizeFormat('1024', 'KB') 返回 1MB
55
+ fileSizeFormat('2645', 'B') 返回 ceil(2645 / 1024, 2)MB
56
+ ```
57
+ * @param {string} str 字符串 单位k
58
+ */
59
+ export function fileSizeFormat(str: string | number, unit: FileSizeUnit = 'B', defaultValue = '-') {
60
+ if (isEmpty(str)) {
61
+ return defaultValue;
62
+ }
63
+ const o = calcFileSize(parseFloat(str.toString()), unit);
64
+ return `${ceil(o.size, 2)}${o.unit}`;
65
+ }
66
+
67
+ /**
68
+ * base64转换为file
69
+ * dataurl base64图片
70
+ * */
71
+ export function dataURLtoBlob(dataurl: string): Blob {
72
+ const mime = (dataurl.match(/:(.*?);/) as any)[1];
73
+ const bytes = window.atob(dataurl.split(',')[1]); // 去掉url的头,并转换为byte
74
+ // 处理异常,将ascii码小于0的转换为大于0
75
+ const ab = new ArrayBuffer(bytes.length);
76
+ const ia = new Uint8Array(ab);
77
+ for (let i = 0; i < bytes.length; i += 1) {
78
+ ia[i] = bytes.charCodeAt(i);
79
+ }
80
+ return new Blob([ab], { type: mime });
81
+ }
82
+
83
+ export function blobToDataURL(blob: Blob): any {
84
+ return new Promise((resolve, reject) => {
85
+ const reader = new FileReader();
86
+ reader.onload = (e) => {
87
+ if (!e || !e.target) {
88
+ reject(e);
89
+ } else {
90
+ resolve(e.target.result as string);
91
+ }
92
+ };
93
+ reader.onerror = (err) => {
94
+ reject(err);
95
+ };
96
+ reader.readAsDataURL(blob);
97
+ });
98
+ }
99
+
100
+ export interface CompressImgScaleCallback {
101
+ (w: number, h: number): number;
102
+ }
103
+ export interface CompressImgQualityCallback {
104
+ (fileSize: number, scale: number, w: number, h: number): number;
105
+ }
106
+
107
+ /**
108
+ * 压缩图片方法
109
+ * @param file 图片
110
+ * @param scaleCallback 宽高 压缩规则
111
+ * @param qualityCallback 质量 压缩规则
112
+ */
113
+ export function compressImg(
114
+ file: File,
115
+ scaleCallback?: CompressImgScaleCallback,
116
+ qualityCallback?: CompressImgQualityCallback,
117
+ ): Promise<Blob> {
118
+ return new Promise((resolve, reject) => {
119
+ const fileSize = parseFloat((file.size / 1024 / 1024).toString());
120
+ const read = new FileReader();
121
+ read.onload = (e: ProgressEvent<FileReader>) => {
122
+ const img = new Image();
123
+ img.onload = () => {
124
+ // 读取图片宽高
125
+ const w = img.width;
126
+ const h = img.height;
127
+ // 生成canvas
128
+ const canvas = document.createElement('canvas');
129
+ const ctx = canvas.getContext('2d');
130
+
131
+ // 处理函数返回的不是 number & NaN 类型的情况
132
+ let scale = scaleCallback ? scaleCallback(w, h) : 1;
133
+ if (Number.isNaN(scale) || typeof scale !== 'number') {
134
+ scale = 1;
135
+ }
136
+ let quality = qualityCallback ? qualityCallback(fileSize, scale, w, h) : 1;
137
+ if (Number.isNaN(quality) || typeof quality !== 'number') {
138
+ quality = 1;
139
+ }
140
+
141
+ // 图片 缩放规则
142
+ if (ctx) {
143
+ const scaleW = parseInt((w * scale).toString(), 10);
144
+ const scaleH = parseInt((h * scale).toString(), 10);
145
+ // 创建属性节点
146
+ canvas.setAttribute('width', scaleW.toString());
147
+ canvas.setAttribute('height', scaleH.toString());
148
+ ctx.drawImage(img, 0, 0, scaleW, scaleH);
149
+ }
150
+ // 图片质量 压缩
151
+ const base64 = canvas.toDataURL(file.type, quality);
152
+
153
+ resolve(dataURLtoBlob(base64));
154
+ };
155
+ img.onerror = (evt: Event | string) => {
156
+ reject(evt);
157
+ };
158
+
159
+ if (e.target) {
160
+ img.src = e.target.result as string;
161
+ }
162
+ };
163
+
164
+ read.onerror = (e: ProgressEvent<FileReader>) => {
165
+ reject(e);
166
+ };
167
+ read.readAsDataURL(file);
168
+ });
169
+ }
170
+
171
+ export type Method =
172
+ | 'get' | 'GET'
173
+ | 'delete' | 'DELETE'
174
+ | 'head' | 'HEAD'
175
+ | 'options' | 'OPTIONS'
176
+ | 'post' | 'POST'
177
+ | 'put' | 'PUT'
178
+ | 'patch' | 'PATCH'
179
+ | 'link' | 'LINK'
180
+ | 'unlink' | 'UNLINK'
181
+
182
+ export interface ExportByBlobParams {
183
+ /**
184
+ * 请求方式
185
+ */
186
+ method?: Method;
187
+ /**
188
+ * 接口地址
189
+ */
190
+ url: string;
191
+ /**
192
+ * post接口参数
193
+ */
194
+ data?: any;
195
+ /**
196
+ * get接口参数
197
+ */
198
+ params?: any;
199
+ /**
200
+ * 导出的文件名称
201
+ */
202
+ filename?: string;
203
+ }
204
+
205
+
206
+ export interface GenExportByBlobParams {
207
+ /**
208
+ * axios 请求函数
209
+ */
210
+ axiosRequest: any;
211
+ /**
212
+ * 不使用 withCredentials 域名
213
+ */
214
+ notWithCredentials?: string[];
215
+ }
216
+ /**
217
+ * 生成导出函数
218
+ */
219
+ export function genExportByBlob({
220
+ axiosRequest,
221
+ notWithCredentials = [],
222
+ }) {
223
+ return function exportByBlob(config: ExportByBlobParams) {
224
+ let {
225
+ filename,
226
+ } = config;
227
+ const {
228
+ url,
229
+ data = {},
230
+ params = {},
231
+ method = 'post',
232
+ } = config;
233
+ return new Promise((resolve, reject) => {
234
+ axiosRequest({
235
+ method,
236
+ url,
237
+ data,
238
+ params,
239
+ responseType: 'blob',
240
+ config: {
241
+ withCredentials: notWithCredentials.some((str) => {
242
+ return !url.includes(str);
243
+ }),
244
+ },
245
+ // eslint-disable-next-line consistent-return
246
+ }).then(async (res) => {
247
+ const response = res?.data;
248
+ if (response.type === 'application/json') {
249
+ const reader = new FileReader();
250
+ reader.readAsText(response, 'utf-8');
251
+ reader.onload = () => {
252
+ let result: any = {};
253
+ try {
254
+ result = JSON.parse(reader.result as string);
255
+ } catch (e) {
256
+ result = response;
257
+ }
258
+ // Message.error((result && result.message) || '导出失败');
259
+ reject(result);
260
+ };
261
+ } else {
262
+ try {
263
+ const { headers } = res; // 下载后文件名
264
+ const contentDisposition = headers['content-disposition'];
265
+ const responseFilename = decodeURIComponent(contentDisposition.split(';')[1].split('filename=')[1]);
266
+ // 有传文件名称&不带后缀 使用服务端的文件拓展名
267
+ if (filename && !filename.includes('.')) {
268
+ const splitFileName = responseFilename.split('.');
269
+ const suffix = splitFileName[splitFileName.length - 1];
270
+ filename += `.${suffix}`;
271
+ } else if (!filename) {
272
+ filename = responseFilename;
273
+ }
274
+ } catch (e) {
275
+ // 默认名称
276
+ if (!filename) {
277
+ filename = 'download.xlsx';
278
+ }
279
+ }
280
+ const blob = new Blob([res.data]);
281
+ const downloadElement = document.createElement('a');
282
+ const href = await blobToDataURL(blob); // 创建下载的链接
283
+ downloadElement.href = href;
284
+ downloadElement.download = filename;
285
+ document.body.appendChild(downloadElement);
286
+ downloadElement.click(); // 点击下载
287
+ document.body.removeChild(downloadElement); // 下载完成移除元素
288
+ // window.URL.revokeObjectURL(href);
289
+ resolve(res);
290
+ }
291
+ }).catch((e: Error) => {
292
+ reject(e);
293
+ });
294
+ });
295
+ };
296
+ }
@@ -0,0 +1,79 @@
1
+ import axios, {AxiosInstance, AxiosResponse} from "axios";
2
+ import { httpEnum } from './httpEnums';
3
+ import {getDeviceType, getFromType, getToken, removeToken} from "../common/index";
4
+ interface IHttp {
5
+ cacheType: 'localStorage' | 'sessionStorage' | 'both';
6
+ currentMode: string; // local | dev | test | prod 等自定义mode
7
+ errorCb?: (error: any) => void;
8
+ getTokenCb?: () => void;
9
+ tokenKey?: 'token';
10
+ }
11
+ const request = axios.create({
12
+ timeout: 15000,
13
+ headers: {
14
+ // 'Authorization': getToken(),
15
+ "Content-Type": "application/json",
16
+ },
17
+ });
18
+ // const requestArr = [request];
19
+
20
+ /**
21
+ * @param cacheType localStorage | sessionStorage | both
22
+ * @param currentMode local | dev | test | prod 等自定义mode
23
+ * @param errorCb 错误回调
24
+ * @param getTokenCb 获取token的回调
25
+ * @param tokenKey
26
+ * */
27
+ export const http = ({cacheType, currentMode, errorCb, getTokenCb, tokenKey}: IHttp):AxiosInstance => {
28
+ request.interceptors.request.use(
29
+ async (config: any) => {
30
+ const token = getToken(cacheType, tokenKey);
31
+ const deviceType = getDeviceType();
32
+ config.headers.Authorization = token;
33
+ config.headers.token = token;
34
+ config.headers.FrontType = getFromType(deviceType);
35
+ return config;
36
+ },
37
+ (error) => {
38
+ // do something with request error
39
+ return Promise.reject(error);
40
+ }
41
+ );
42
+
43
+ // response interceptor
44
+ request.interceptors.response.use(
45
+ async (response: AxiosResponse) => {
46
+ const res = response.data;
47
+ const statusCode = res.code || response.status;
48
+ // blob文件流
49
+ if (res instanceof Blob) {
50
+ return res;
51
+ }
52
+ // 成功
53
+ if (statusCode === httpEnum.CODES.Success) {
54
+ return res;
55
+ }
56
+ // token失效
57
+ if (statusCode === httpEnum.CODES.UnAuthorized) {
58
+ if (currentMode !== "local" && currentMode !== "dev" && currentMode !== 'development') {
59
+ removeToken(cacheType, tokenKey);
60
+ if (getTokenCb && typeof getTokenCb === "function") {
61
+ getTokenCb();
62
+ }
63
+ }
64
+ return Promise.reject(new Error("token过期!"));
65
+ }
66
+ const errMsg = res.message || res.msg;
67
+ return Promise.reject(new Error(errMsg || "Error"));
68
+ },
69
+ async (error) => {
70
+ if (errorCb && typeof errorCb === "function") {
71
+ errorCb(error);
72
+ }
73
+ return Promise.reject(error);
74
+ }
75
+
76
+ );
77
+ return request
78
+ }
79
+
@@ -0,0 +1,61 @@
1
+ export const httpEnum = {
2
+ // http响应状态码
3
+ HTTP_STATUS: {
4
+ // 1xx,临时响应
5
+ TEMP_RESPOND: {
6
+ Continue: 100, // 服务器通知浏览器之前一切正常,请客户端继续请求,如果请求结束,可忽略
7
+ SwitchingProtocal: 101 // 针对请求头的Upgrade返回的信息。表明服务器正在切换到指定的协议
8
+ },
9
+ // 2xx,成功
10
+ SUCCESS: {
11
+ Ok: 200, // 请求成功
12
+ Created: 201, // 常用于POST,PUT 请求,表明请求已经成功,并新建了一个资源。并在响应体中返回路径
13
+ Accepted: 202, // 请求已经接收到,但没有响应,稍后也不会返回一个异步请求结果。 该状态码适用于等待其他进程处理或者批处理的场景
14
+ NoAuthoritativeInformation: 203, // 表明响应返回的元信息(meta-infomation)和最初的服务器不同,而是从本地或者第三方获取的
15
+ NoContent: 204, // 请求没有数据返回,但是头信息有用。用户代理(浏览器)会更新缓存的头信息
16
+ ResetContent: 205, // 告诉用户代理(浏览器)重置发送该请求的文档
17
+ ParticalContent: 206 // 客户端使用Range请求头时,返回该状态码
18
+ },
19
+ // 3xx,重定向
20
+ REDIRECT: {
21
+ MultipleChoice: 300, // 返回多个响应,需要浏览器或者用户选择
22
+ MovedPermanently: 301, // 请求资源的URL被永久的改变,新的URL会在响应的Location中给出。浏览器到新的URL重新请求资源,因为有些客户端会把请求方式method改成GET。所以该状态码建议GET和HEAD方法中使用。搜索引擎会更新地址到资源的链接(SEO中‘link-judge’被发送到新的URL)
23
+ Found: 302, // 请求资源的URL被暂时修改到Location提供的URL。未来可能还会有新的修改。览器会根据新的URL重新请求资源。有些客户端会把方法method改为GET,建议在GET和HEAD方法中使用。搜索引擎不会更改URL到资源的。
24
+ SeeOther: 303, // 服务通过返回的响应数据指导客户端通过GET方法去另一个URL获取资源。通常用于POST或者PUT的请求返回结果,重定向到信息提示页面或者进度展示页面。重定向页面的方法是GET方法。
25
+ NotModified: 304, // 资源未变更。服务器根据请求头判断,需要资源未修改,只返回响应头;否则将资源一起返回。
26
+ TemporaryRedirect: 307, // 临时重定向。基本和302相同。唯一的区别是这个状态码严格禁止浏览器到新URL请求资源时修改原来的请求方式和请求体。
27
+ PermanentRedirect: 308 // 永久重定向。基本和301相同。但是严格禁止修改请求方式和请求体。
28
+ },
29
+ // 4xx,请求错误
30
+ REQUEST_ERROR: {
31
+ BadRequest: 400, // 请求语法有问题,服务器无法识别。
32
+ UnAuthorized: 401, // 客户端未授权该请求。缺乏有效的身份认证凭证,一般可能是未登陆。登陆后一般都解决问题。
33
+ Forbidden: 403, // 服务器拒绝响应。权限不足。
34
+ NotFound: 404, // URL无效或者URL有效但是没有资源。
35
+ MethodNotAllowed: 405, // 请求方式Method不允许。但是GET和HEAD属于强制方式,不能返回这个状态码。
36
+ NotAccepted: 406, // 资源类型不符合服务器要求。
37
+ ProxyAuthorizationRequired: 407, // 需要代理授权。
38
+ RequestTimeout: 408, // 服务器将不再使用的连接关闭。响应头会有Connection: close。
39
+ UpgradeRequired: 426, // 告诉客户端需要升级通信协议。
40
+ TokenUnAuthorized: 499, // 告诉客户端需要升级通信协议。
41
+ },
42
+ // 5xx,服务器错误
43
+ SERVER_ERROR: {
44
+ InternalServerError: 500, // 服务器内部错误,未捕获。
45
+ BadGateway: 502, // 服务器作为网关使用时,收到上游服务器返回的无效响应。
46
+ ServiceUnavailable: 503, // 无法服务。一般发生在因维护而停机或者服务过载。一般还会伴随着返回一个响应头Retry-After: 说明恢复服务的估计时间。
47
+ GateTimeout: 504, // 网关超时。服务器作为网关或者代理,不能及时从上游服务器获取响应返回给客户端。
48
+ HttpVersionNotSupported: 505 // 发出的请求http版本服务器不支持。如果请求通过http2发送,服务器不支持http2.0,就会返回该状态码。
49
+ }
50
+ },
51
+ // http状态码
52
+ CODES: {
53
+ Success: '00000',
54
+ UnAuthorized: '00099',
55
+ UnAuthorizedPhone: '11001', //未授权用户手机号
56
+ UnAuthorizedWecom: '11000', //调用企微接口异常
57
+ UnAuthorizedHavePhone: '11002', //查到手机号但是也报错
58
+ },
59
+ // http状态码白名单,在具体业务中处理
60
+ ERR_CODE_WHITE_LIST: []
61
+ }
package/src/index.ts ADDED
@@ -0,0 +1,10 @@
1
+ export * from './common';
2
+ export * from './date';
3
+ export * from './file';
4
+ export * from './object';
5
+ export * from './print';
6
+ export * from './validate';
7
+ export * from './number';
8
+ export * from './string';
9
+ export * from './http/httpEnums'
10
+ export * from './http/http'
@@ -0,0 +1,190 @@
1
+ /**
2
+ * number处理相关
3
+ * @packageDocumentation
4
+ * @module Number
5
+ * @preferred
6
+ */
7
+
8
+ import { isEmpty } from '../common';
9
+ /**
10
+ * 千分位格式化
11
+ #### 使用说明
12
+ ```
13
+ toThousands(1) => 1
14
+ toThousands(12345) => 12,345
15
+ toThousands('12323.12') 返回 '12,323.12'
16
+ ```
17
+ */
18
+ export function toThousands(num: number | string, defaultValue = '-'): string {
19
+ if (isEmpty(num)) {
20
+ return defaultValue;
21
+ }
22
+ let val = num.toString();
23
+
24
+ if (!val.includes('.')) {
25
+ val += '.';
26
+ }
27
+ return val.replace(/\d(?=(\d{3})+\.)/g, $0 => `${$0},`).replace(/\.$/, '');
28
+ }
29
+
30
+ /**
31
+ * 阿拉伯数组转中文数字
32
+ #### 使用说明
33
+ ```
34
+ convertCurrency(0) 返回 '壹拾贰元壹角贰分'
35
+ convertCurrency(1234567809) 返回 '壹拾贰亿叁仟肆佰伍拾陆万柒仟捌佰零玖元整'
36
+ ```
37
+ * @param {number} money 金额
38
+ */
39
+ export function convertCurrency(money = '') {
40
+ const maxNum = 999999999999999.9999; // 最大处理的数字
41
+
42
+ if (isEmpty(money)) {
43
+ return '';
44
+ }
45
+ if (Number(money) >= maxNum) {
46
+ // 超出最大处理数字
47
+ return money;
48
+ }
49
+
50
+ const cnNums = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];
51
+ const cnIntRadice = ['', '拾', '佰', '仟'];
52
+ const cnIntUnits = ['', '万', '亿', '兆']; // 对应整数部分扩展单位
53
+ const cnDecUnits = ['角', '分', '毫', '厘']; // 对应小数部分单位
54
+ const cnInteger = '整'; // 整数金额时后面跟的字符
55
+ const cnIntLast = '元'; // 整型完以后的单位
56
+ let integerNum; // 金额整数部分
57
+ let decimalNum; // 金额小数部分
58
+ let chineseStr = ''; // 输出的中文金额字符串
59
+ let parts; // 分离金额后用的数组,预定义
60
+ money = parseFloat(money).toString();
61
+
62
+ if (money === '0') {
63
+ chineseStr = cnNums[0] + cnIntLast + cnInteger;
64
+ return chineseStr;
65
+ }
66
+ // 转换为字符串
67
+ money = money.toString();
68
+ if (money.indexOf('.') === -1) {
69
+ integerNum = money;
70
+ decimalNum = '';
71
+ } else {
72
+ parts = money.split('.');
73
+ integerNum = parts[0];
74
+ decimalNum = parts[1].substr(0, 4);
75
+ }
76
+ // 获取整型部分转换
77
+ if (parseInt(integerNum, 10) > 0) {
78
+ let zeroCount = 0;
79
+ const intLen = integerNum.length;
80
+ for (let i = 0; i < intLen; i += 1) {
81
+ const n = integerNum.substr(i, 1);
82
+ const p = intLen - i - 1;
83
+ const q = p / 4;
84
+ const m = p % 4;
85
+ if (n === '0') {
86
+ zeroCount += 1;
87
+ } else {
88
+ if (zeroCount > 0) {
89
+ chineseStr += cnNums[0];
90
+ }
91
+ // 归零
92
+ zeroCount = 0;
93
+ chineseStr += cnNums[parseInt(n, 10)] + cnIntRadice[m];
94
+ }
95
+ if (m === 0 && zeroCount < 4) {
96
+ chineseStr += cnIntUnits[q];
97
+ }
98
+ }
99
+ chineseStr += cnIntLast;
100
+ }
101
+ // 小数部分
102
+ if (decimalNum !== '') {
103
+ const decLen = decimalNum.length;
104
+ for (let i = 0; i < decLen; i += 1) {
105
+ const dn = decimalNum.substr(i, 1);
106
+ if (dn !== '0') {
107
+ chineseStr += cnNums[Number(dn)] + cnDecUnits[i];
108
+ }
109
+ }
110
+ }
111
+ if (chineseStr === '') {
112
+ chineseStr += cnNums[0] + cnIntLast + cnInteger;
113
+ } else if (decimalNum === '') {
114
+ chineseStr += cnInteger;
115
+ }
116
+ return chineseStr;
117
+ }
118
+
119
+ /**
120
+ * 格式化小数位
121
+ #### 使用说明
122
+ ```
123
+ formatFloat('1.2345') 返回 '1.23'
124
+ formatFloat('1.2345', 3) 返回 '1.235'
125
+ formatFloat('1', 3) 返回 '1.000'
126
+ ```
127
+ * @param {*} val 小数
128
+ * @param {number} [pos=2] 保留的小数位
129
+ */
130
+ export function formatFloat(val: string | number, pos = 2): string {
131
+ let f = parseFloat(String(val));
132
+ if (Number.isNaN(f)) {
133
+ return '';
134
+ }
135
+ f = Math.round(Number(val) * Math.pow(10, pos)) / Math.pow(10, pos); // pow 幂
136
+ let s = f.toString();
137
+ let rs = s.indexOf('.');
138
+ if (rs < 0) {
139
+ rs = s.length;
140
+ s += '.';
141
+ }
142
+ while (s.length <= rs + pos) {
143
+ s += '0';
144
+ }
145
+ return s;
146
+ }
147
+
148
+ /**
149
+ * 向上取整
150
+ #### 使用说明
151
+ ```
152
+ ceil('1.23') 返回 2
153
+ ceil('1.234', 2) 返回 1.24
154
+ ceil('1.230', 2) 返回 1.23
155
+ ```
156
+ * @param {*} val
157
+ * @param {number} [pos=2] 保留的小数位
158
+ */
159
+ export function ceil(val: string | number, pos = 0) {
160
+ if (isEmpty(val)) {
161
+ return val;
162
+ }
163
+
164
+ const swell = Math.pow(10, pos);
165
+ const swellVal = Number(val) * swell;
166
+ const newVal = Math.ceil(swellVal) / swell;
167
+
168
+ return newVal;
169
+ }
170
+
171
+ /**
172
+ * 向下取整
173
+ #### 使用说明
174
+ ```
175
+ floor('1.23') 返回 1
176
+ floor('1.234', 2) 返回 1.23
177
+ floor('1.230', 2) 返回 1.23
178
+ ```
179
+ * @param {*} val
180
+ * @param {number} [pos=2] 保留的小数位
181
+ */
182
+ export function floor(val: string | number, pos = 0) {
183
+ if (isEmpty(val)) {
184
+ return val;
185
+ }
186
+
187
+ const swell = Math.pow(10, pos);
188
+ const swellVal = Number(val) * swell;
189
+ return Math.floor(swellVal) / swell;
190
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * object处理相关
3
+ * @packageDocumentation
4
+ * @module Object
5
+ * @preferred
6
+ */
7
+
8
+ /**
9
+ * 比较两者的值是否相等
10
+ #### 使用说明
11
+ ```
12
+ deepEqual({a: 10}, {a: 10}) 返回 true
13
+ deepEqual({a: 10}, {a: 10, b: 20}) 返回 false
14
+ ```
15
+ * @param {*} x 用来比较的值
16
+ * @param {*} y 另一个用来比较的值
17
+ */
18
+ export function deepEqual(x, y) {
19
+ const ok = Object.keys,
20
+ tx = typeof x,
21
+ ty = typeof y;
22
+ return x && y && tx === 'object' && tx === ty
23
+ ? ok(x).length === ok(y).length && ok(x).every(key => deepEqual(x[key], y[key]))
24
+ : x === y;
25
+ }
26
+ function getNodePath(node, nodeValue, nodeKey, childrenKey, path: any[] = []) {
27
+ path.push(node);
28
+ if (node[nodeKey] === nodeValue) {
29
+ throw 'GOT IT!';
30
+ }
31
+ if (node[childrenKey] && node[childrenKey].length > 0) {
32
+ for (let i = 0; i < node[childrenKey].length; i++) {
33
+ getNodePath(node[childrenKey][i], nodeValue, nodeKey, childrenKey, path);
34
+ }
35
+ }
36
+ path.pop();
37
+ }
38
+ /**
39
+ * 查找树结构节点路径
40
+ * @param {*} nodeValue 节点值
41
+ * @param {string} nodeKey 节点键
42
+ * @param {Array} treeArray 树数据
43
+ * @param {string} children 子集键
44
+ */
45
+ export function findNodePath(nodeValue, nodeKey, treeArray, childrenKey) {
46
+ const path: any[] = [];
47
+ try {
48
+ for (let i = 0; i < treeArray.length; i++) {
49
+ getNodePath(treeArray[i], nodeValue, nodeKey, childrenKey, path);
50
+ }
51
+ } catch (e) {
52
+ return path;
53
+ }
54
+ }