@zcrkey/js-utils 0.0.4 → 0.0.6

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.
@@ -10,7 +10,7 @@ export default class CrUtil {
10
10
  * @param value
11
11
  * @returns boolean
12
12
  */
13
- static isObject<T = any>(value: T): value is T extends Array<any> ? never : T;
13
+ static isObject<T = any>(value: T): value is T extends object ? (T extends any[] ? never : T) : never;
14
14
  /**
15
15
  * 判断是否为空对象
16
16
  * @param value
@@ -28,31 +28,37 @@ export default class CrUtil {
28
28
  * @param value
29
29
  * @returns boolean
30
30
  */
31
- static isDate(value: any): boolean;
31
+ static isDate(value: unknown): boolean;
32
32
  /**
33
33
  * 判断是否为字符串
34
34
  * @param value
35
35
  * @returns boolean
36
36
  */
37
- static isString(value: any): value is string;
37
+ static isString(value: unknown): value is string;
38
38
  /**
39
39
  * 判断是否为数字
40
40
  * @param value
41
41
  * @returns boolean
42
42
  */
43
- static isNumber(value: any): value is number;
43
+ static isNumber(value: unknown): value is number;
44
44
  /**
45
45
  * 判断是否为文件 File
46
46
  * @param value
47
47
  * @returns boolean
48
48
  */
49
- static isFile(value: any): value is File;
49
+ static isFile(value: unknown): value is File;
50
50
  /**
51
51
  * 判断是否为 Boolean
52
52
  * @param value
53
53
  * @returns boolean
54
54
  */
55
- static isBoolean(value: any): value is boolean;
55
+ static isBoolean(value: unknown): value is boolean;
56
+ /**
57
+ * 判断是否为 Function
58
+ * @param value
59
+ * @returns boolean
60
+ */
61
+ static isFunction(value: unknown): value is (...args: any[]) => any;
56
62
  /**
57
63
  * 去掉字符串前后所有空格
58
64
  * @param str
@@ -65,9 +71,16 @@ export default class CrUtil {
65
71
  * @returns number
66
72
  */
67
73
  static getArrayDimension(arr: any[]): number;
74
+ /**
75
+ * 深拷贝
76
+ * @param value
77
+ * @returns
78
+ */
79
+ static cloneDeep<T>(value: T): T;
68
80
  /**
69
81
  * 深拷贝
70
82
  * @param target
83
+ * @deprecated 即将移除,使用 {@link CrUtil.cloneDeep} 替代
71
84
  * @returns
72
85
  */
73
86
  static deepCopy<T = any>(target: T): T;
@@ -78,6 +91,7 @@ export default class CrUtil {
78
91
  * @param settings.idField 默认值 id
79
92
  * @param settings.pidField 默认值 parentId
80
93
  * @param settings.childrenField 默认值 children
94
+ * @deprecated 即将移除,使用 {@link CrObjUtil.listToTree} 替代
81
95
  * @returns
82
96
  */
83
97
  static listToTreeData(listData: any[], settings?: {
@@ -94,6 +108,7 @@ export default class CrUtil {
94
108
  * @param settings.idField 默认值 id
95
109
  * @param settings.pidField 默认值 parentId
96
110
  * @param settings.childrenField 默认值 children
111
+ * @deprecated 即将移除,使用 {@link CrObjUtil.treeToList} 替代
97
112
  * @returns
98
113
  */
99
114
  static treeDataToListData(treeData: any[], pid?: string | number, settings?: {
@@ -110,6 +125,7 @@ export default class CrUtil {
110
125
  * @param settings.valueField 默认值 value
111
126
  * @param settings.idField 默认值 id
112
127
  * @param settings.pidField 默认值 parentId
128
+ * @deprecated 即将移除,使用 {@link CrObjUtil.getFlatParentDatas} 替代
113
129
  * @returns
114
130
  */
115
131
  static getParentNodes<T, R extends Record<string, any>>(listData: R[], value: number | string, settings?: {
@@ -135,6 +151,19 @@ export default class CrUtil {
135
151
  static paramsParse(str: string, settings?: {
136
152
  ignoreQueryPrefix: boolean;
137
153
  }): {};
154
+ /**
155
+ * 获取 URL 参数
156
+ * @param {*} url
157
+ * @returns
158
+ * @example
159
+ * "https://example.com?foo=bar&baz=qux" => {"query":{"foo":"bar","baz":"qux"},"hash":{},"all":{"foo":"bar","baz":"qux"}}
160
+ * "https://example.com/page?foo=1#/?a=1&b=2" => {"query":{"foo":"1"},"hash":{"/":"","a":"1","b":"2"},"all":{"foo":"1","/":"","a":"1","b":"2"}},
161
+ */
162
+ static getQueryParams(url: string): {
163
+ query: Record<string, any>;
164
+ hash: Record<string, any>;
165
+ all: Record<string, any>;
166
+ };
138
167
  /**
139
168
  * 版本号比较
140
169
  * @param v1
@@ -171,4 +200,10 @@ export default class CrUtil {
171
200
  * @returns
172
201
  */
173
202
  static compareDataDiff(data1: any, data2: any): Record<string, "added" | "removed" | "modified">;
203
+ /**
204
+ * 格式化为千分位
205
+ * @param value
206
+ * @returns 12345.6789 => 12,345.6789
207
+ */
208
+ static fmtThousands(value: number | string | undefined): string;
174
209
  }
@@ -0,0 +1,572 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
+
29
+ // src/util.ts
30
+ var util_exports = {};
31
+ __export(util_exports, {
32
+ default: () => CrUtil
33
+ });
34
+ module.exports = __toCommonJS(util_exports);
35
+ var import_lodash = require("lodash");
36
+ var import_qs = __toESM(require("qs"));
37
+ var CrUtil = class {
38
+ /**
39
+ * 判断是否为数组
40
+ * @param value
41
+ * @returns boolean
42
+ */
43
+ static isArray(value) {
44
+ return Array.isArray(value);
45
+ }
46
+ /**
47
+ * 判断是否为对象
48
+ * @param value
49
+ * @returns boolean
50
+ */
51
+ static isObject(value) {
52
+ return typeof value === "object" && value !== null && !CrUtil.isArray(value);
53
+ }
54
+ /**
55
+ * 判断是否为空对象
56
+ * @param value
57
+ * @returns boolean
58
+ */
59
+ static isEmptyObject(value) {
60
+ return Object.keys(value).length === 0 && value.constructor === Object;
61
+ }
62
+ /**
63
+ * 判断是否对象的属性是否全部为空
64
+ * @param value
65
+ * @returns boolean
66
+ */
67
+ static isObjectPropertiesAllEmpty(value) {
68
+ return Object.keys(value).every((key) => {
69
+ const _value = value[key];
70
+ return _value === void 0 || _value === null || _value === "" || _value === 0 && typeof _value === "number" || isNaN(_value);
71
+ });
72
+ }
73
+ /**
74
+ * 判断是否为日期
75
+ * @param value
76
+ * @returns boolean
77
+ */
78
+ static isDate(value) {
79
+ return Object.prototype.toString.call(value) === "[object Date]";
80
+ }
81
+ /**
82
+ * 判断是否为字符串
83
+ * @param value
84
+ * @returns boolean
85
+ */
86
+ static isString(value) {
87
+ return Object.prototype.toString.call(value) === "[object String]";
88
+ }
89
+ /**
90
+ * 判断是否为数字
91
+ * @param value
92
+ * @returns boolean
93
+ */
94
+ static isNumber(value) {
95
+ return typeof value === "number" && !isNaN(value);
96
+ }
97
+ /**
98
+ * 判断是否为文件 File
99
+ * @param value
100
+ * @returns boolean
101
+ */
102
+ static isFile(value) {
103
+ return Object.prototype.toString.call(value) === "[object File]";
104
+ }
105
+ /**
106
+ * 判断是否为 Boolean
107
+ * @param value
108
+ * @returns boolean
109
+ */
110
+ static isBoolean(value) {
111
+ return Object.prototype.toString.call(value) === "[object Boolean]";
112
+ }
113
+ /**
114
+ * 判断是否为 Function
115
+ * @param value
116
+ * @returns boolean
117
+ */
118
+ static isFunction(value) {
119
+ return typeof value === "function";
120
+ }
121
+ /**
122
+ * 去掉字符串前后所有空格
123
+ * @param str
124
+ * @returns string
125
+ */
126
+ static trim(str) {
127
+ return (str + "").replace(/(^[\s\n\t]+|[\s\n\t]+$)/g, "");
128
+ }
129
+ /**
130
+ * 获取数组为几维数组
131
+ * @param arr
132
+ * @returns number
133
+ */
134
+ static getArrayDimension(arr) {
135
+ if (!CrUtil.isArray(arr))
136
+ return 0;
137
+ let maxDimension = 0;
138
+ for (let i = 0; i < arr.length; i++) {
139
+ const dimension = CrUtil.getArrayDimension(arr[i]);
140
+ maxDimension = Math.max(maxDimension, dimension);
141
+ }
142
+ return maxDimension + 1;
143
+ }
144
+ /**
145
+ * 深拷贝
146
+ * @param value
147
+ * @returns
148
+ */
149
+ static cloneDeep(value) {
150
+ return (0, import_lodash.cloneDeep)(value);
151
+ }
152
+ /**
153
+ * 深拷贝
154
+ * @param target
155
+ * @deprecated 即将移除,使用 {@link CrUtil.cloneDeep} 替代
156
+ * @returns
157
+ */
158
+ static deepCopy(target) {
159
+ let _target;
160
+ if (CrUtil.isArray(target)) {
161
+ _target = [];
162
+ for (let i = 0, len = target.length; i < len; i++) {
163
+ if (CrUtil.isArray(target[i]) || CrUtil.isObject(target[i])) {
164
+ _target.push(CrUtil.deepCopy(target[i]));
165
+ } else {
166
+ _target.push(target[i]);
167
+ }
168
+ }
169
+ } else if (CrUtil.isObject(target)) {
170
+ _target = {};
171
+ for (let i in target) {
172
+ if (CrUtil.isArray(target[i]) || CrUtil.isObject(target[i])) {
173
+ _target[i] = CrUtil.deepCopy(target[i]);
174
+ } else {
175
+ _target[i] = target[i];
176
+ }
177
+ }
178
+ } else {
179
+ _target = target;
180
+ }
181
+ return _target;
182
+ }
183
+ /**
184
+ * 列表数据转树型数据
185
+ * @param listData
186
+ * @param settings
187
+ * @param settings.idField 默认值 id
188
+ * @param settings.pidField 默认值 parentId
189
+ * @param settings.childrenField 默认值 children
190
+ * @deprecated 即将移除,使用 {@link CrObjUtil.listToTree} 替代
191
+ * @returns
192
+ */
193
+ static listToTreeData(listData, settings) {
194
+ const options = Object.assign(
195
+ {
196
+ idField: "id",
197
+ pidField: "parentId",
198
+ childrenField: "children",
199
+ isDeepCopy: false,
200
+ getData: (item) => {
201
+ return item;
202
+ }
203
+ },
204
+ settings
205
+ );
206
+ const { idField, pidField, childrenField, isDeepCopy, getData } = options;
207
+ if (isDeepCopy) {
208
+ listData = CrUtil.deepCopy(listData);
209
+ }
210
+ const pidMap = /* @__PURE__ */ new Map();
211
+ const idMap = /* @__PURE__ */ new Map();
212
+ for (const item of listData) {
213
+ const pid = item[pidField];
214
+ const id = item[idField];
215
+ if (!pidMap.has(pid)) {
216
+ pidMap.set(pid, []);
217
+ }
218
+ pidMap.get(pid).push(item);
219
+ idMap.set(id, item);
220
+ }
221
+ const buildTree = (pid) => {
222
+ const children = pidMap.get(pid) || [];
223
+ return children.map((item) => {
224
+ const rawChildren = buildTree(item[idField]);
225
+ const node = {
226
+ ...getData(item)
227
+ };
228
+ if (rawChildren.length > 0) {
229
+ node[childrenField] = rawChildren;
230
+ }
231
+ return node;
232
+ });
233
+ };
234
+ const rootPids = /* @__PURE__ */ new Set();
235
+ for (const pid of pidMap.keys()) {
236
+ if (pid === void 0 || pid === null || pid === 0 || !idMap.has(pid)) {
237
+ rootPids.add(pid);
238
+ }
239
+ }
240
+ const tree = [];
241
+ for (const pid of rootPids) {
242
+ tree.push(...buildTree(pid));
243
+ }
244
+ return tree;
245
+ }
246
+ /**
247
+ * 树型数据转列表数据
248
+ * @param treeData
249
+ * @param settings
250
+ * @param settings.idField 默认值 id
251
+ * @param settings.pidField 默认值 parentId
252
+ * @param settings.childrenField 默认值 children
253
+ * @deprecated 即将移除,使用 {@link CrObjUtil.treeToList} 替代
254
+ * @returns
255
+ */
256
+ static treeDataToListData(treeData, pid = 0, settings) {
257
+ let options = Object.assign(
258
+ {
259
+ idField: "id",
260
+ pidField: "parentId",
261
+ childrenField: "children",
262
+ isDeepCopy: false
263
+ },
264
+ settings
265
+ );
266
+ let listData = [];
267
+ for (let i = 0; i < treeData.length; i++) {
268
+ const node = treeData[i];
269
+ const item = { ...node };
270
+ item[options.pidField] = pid;
271
+ delete item[options.childrenField];
272
+ listData.push(item);
273
+ if (node[options.childrenField]) {
274
+ const childrenList = this.treeDataToListData(
275
+ node[options.childrenField],
276
+ item[options.idField],
277
+ settings
278
+ );
279
+ listData.push(...childrenList);
280
+ }
281
+ }
282
+ if (options.isDeepCopy) {
283
+ listData = CrUtil.deepCopy(listData);
284
+ }
285
+ return listData;
286
+ }
287
+ /**
288
+ * 获取所有父级数据(包含自身)
289
+ * @param listData
290
+ * @param value
291
+ * @param settings
292
+ * @param settings.valueField 默认值 value
293
+ * @param settings.idField 默认值 id
294
+ * @param settings.pidField 默认值 parentId
295
+ * @deprecated 即将移除,使用 {@link CrObjUtil.getFlatParentDatas} 替代
296
+ * @returns
297
+ */
298
+ static getParentNodes(listData, value, settings) {
299
+ const options = Object.assign(
300
+ {
301
+ valueField: "value",
302
+ idField: "id",
303
+ pidField: "parentId",
304
+ getData: (item) => {
305
+ return item;
306
+ }
307
+ },
308
+ settings
309
+ );
310
+ if (!(listData && listData.length > 0) || !value) {
311
+ return [];
312
+ }
313
+ const idField = options.idField;
314
+ const pidField = options.pidField;
315
+ const valueField = options.valueField;
316
+ const nodes = [];
317
+ let node = listData.find(
318
+ (item) => item[valueField] == value
319
+ );
320
+ if (node) {
321
+ nodes.push(options.getData(node));
322
+ while (node && node[pidField]) {
323
+ const parentOption = listData.find(
324
+ // eslint-disable-next-line @typescript-eslint/no-loop-func
325
+ (item) => item[idField] == node[pidField]
326
+ );
327
+ if (parentOption) {
328
+ nodes.push(options.getData(parentOption));
329
+ }
330
+ node = parentOption;
331
+ }
332
+ }
333
+ nodes.reverse();
334
+ return nodes;
335
+ }
336
+ /**
337
+ * 参数序列化
338
+ * @param params {a:'1',b:{},c:[]}
339
+ * @returns
340
+ */
341
+ static paramsSerializer(params, settings) {
342
+ const options = Object.assign(
343
+ {
344
+ isFilterNonNull: false
345
+ },
346
+ settings
347
+ );
348
+ if (options.isFilterNonNull) {
349
+ Object.keys(params).forEach((key) => {
350
+ if (params[key] === null || params[key] === void 0 || params[key] === "") {
351
+ delete params[key];
352
+ }
353
+ });
354
+ }
355
+ return import_qs.default.stringify(params);
356
+ }
357
+ /**
358
+ * 参数解析
359
+ * @param {*} str
360
+ * @param {*} settings
361
+ * @returns
362
+ */
363
+ static paramsParse(str, settings) {
364
+ let obj = {};
365
+ let option = Object.assign(
366
+ {
367
+ ignoreQueryPrefix: true
368
+ },
369
+ settings
370
+ );
371
+ if (str) {
372
+ obj = import_qs.default.parse(str, option);
373
+ }
374
+ return obj;
375
+ }
376
+ /**
377
+ * 获取 URL 参数
378
+ * @param {*} url
379
+ * @returns
380
+ * @example
381
+ * "https://example.com?foo=bar&baz=qux" => {"query":{"foo":"bar","baz":"qux"},"hash":{},"all":{"foo":"bar","baz":"qux"}}
382
+ * "https://example.com/page?foo=1#/?a=1&b=2" => {"query":{"foo":"1"},"hash":{"/":"","a":"1","b":"2"},"all":{"foo":"1","/":"","a":"1","b":"2"}},
383
+ */
384
+ static getQueryParams(url) {
385
+ const query = {};
386
+ const hash = {};
387
+ const all = {};
388
+ const parse = (queryString) => {
389
+ if (!queryString) {
390
+ return {};
391
+ }
392
+ try {
393
+ return import_qs.default.parse(queryString);
394
+ } catch {
395
+ const result = {};
396
+ if (!queryString)
397
+ return result;
398
+ queryString.split("&").forEach((pair) => {
399
+ if (!pair)
400
+ return;
401
+ const [key, val] = pair.split("=");
402
+ if (key)
403
+ result[decodeURIComponent(key)] = decodeURIComponent(val || "");
404
+ });
405
+ return result;
406
+ }
407
+ };
408
+ try {
409
+ const [base, hashPart] = url.split("#");
410
+ const queryPart = base.split("?")[1];
411
+ Object.assign(query, parse(queryPart));
412
+ if (hashPart) {
413
+ for (const part of hashPart.split("?")) {
414
+ Object.assign(hash, parse(part));
415
+ }
416
+ }
417
+ Object.assign(all, query, hash);
418
+ } catch (error) {
419
+ console.error("getQueryParams:", error);
420
+ }
421
+ return { query, hash, all };
422
+ }
423
+ /**
424
+ * 版本号比较
425
+ * @param v1
426
+ * @param v2
427
+ * @returns number
428
+ * @description v1大于v2(1)、v1小于v2(-1)、v1等于v2(0)
429
+ */
430
+ static compareVersion(v1, v2) {
431
+ let v1Arr = CrUtil.trim(v1).split(".");
432
+ let v2Arr = CrUtil.trim(v2).split(".");
433
+ const len = Math.max(v1.length, v2.length);
434
+ while (v1Arr.length < len) {
435
+ v1Arr.push("0");
436
+ }
437
+ while (v2Arr.length < len) {
438
+ v2Arr.push("0");
439
+ }
440
+ for (let i = 0; i < len; i++) {
441
+ const num1 = parseInt(v1Arr[i]);
442
+ const num2 = parseInt(v2Arr[i]);
443
+ if (num1 > num2) {
444
+ return 1;
445
+ } else if (num1 < num2) {
446
+ return -1;
447
+ }
448
+ }
449
+ return 0;
450
+ }
451
+ /**
452
+ * 根据 ids 获取相对应的数据名称
453
+ * @param data
454
+ * @param ids
455
+ * @param settings
456
+ * @returns string
457
+ */
458
+ static getDataNameByIds(data, ids, settings) {
459
+ const options = Object.assign(
460
+ {
461
+ sep: "、",
462
+ idField: "id",
463
+ nameField: "name"
464
+ },
465
+ settings
466
+ );
467
+ let str = "";
468
+ if (ids && ids.length > 0) {
469
+ str = ids.map((id) => {
470
+ let item = data.find((item2) => item2[options.idField] == id);
471
+ if (item) {
472
+ return item[options.nameField];
473
+ } else {
474
+ return "";
475
+ }
476
+ }).join(options.sep);
477
+ }
478
+ return str;
479
+ }
480
+ /**
481
+ * 追加html标签属性值
482
+ * @param htmlStr
483
+ * @param tagName
484
+ * @param attrName
485
+ * @param newAttrValue
486
+ * @returns string
487
+ */
488
+ static appendHtmlTagAttr(htmlStr, tagName, attrName, newAttrValue) {
489
+ let regex = new RegExp(`(<${tagName}\\s+)([^>]*?)([^>]*>)`, "g");
490
+ let replacedHtml = htmlStr.replace(regex, function(match, p1, p2, p3) {
491
+ let _regex = new RegExp(`${attrName}="(.*?)"`, "g");
492
+ if (_regex.exec(match)) {
493
+ let __regex = new RegExp(
494
+ `(<${tagName}\\s+)([^>]*?)${attrName}="(.*)"([^>]*>)`,
495
+ "g"
496
+ );
497
+ return match.replace(__regex, function(_match, _p1, _p2, _p3, _p4) {
498
+ return _p1 + _p2 + `${attrName}="${_p3} ${newAttrValue}"` + _p4;
499
+ });
500
+ } else {
501
+ return p1 + `${attrName}="${newAttrValue}" ` + p3;
502
+ }
503
+ });
504
+ replacedHtml = replacedHtml.replace(
505
+ new RegExp(`<${tagName}>`, "g"),
506
+ `<${tagName} ${attrName}="${newAttrValue}">`
507
+ );
508
+ return replacedHtml;
509
+ }
510
+ /**
511
+ * 比较数据差异
512
+ * @param data1
513
+ * @param data2
514
+ * @returns
515
+ */
516
+ static compareDataDiff(data1, data2) {
517
+ const diffFieldSet = {};
518
+ const compare = (path, o1, o2) => {
519
+ if (CrUtil.isArray(o1) && CrUtil.isArray(o2)) {
520
+ const lengthDiff = o1.length - o2.length;
521
+ if (lengthDiff !== 0) {
522
+ diffFieldSet[path] = "modified";
523
+ if (lengthDiff > 0) {
524
+ for (let i = o2.length; i < o1.length; i++) {
525
+ diffFieldSet[`${path}[${i}]`] = "removed";
526
+ }
527
+ } else {
528
+ for (let i = o1.length; i < o2.length; i++) {
529
+ diffFieldSet[`${path}[${i}]`] = "added";
530
+ }
531
+ }
532
+ } else {
533
+ for (let i = 0; i < o1.length; i++) {
534
+ compare(`${path}[${i}]`, o1[i], o2[i]);
535
+ }
536
+ }
537
+ } else if (CrUtil.isObject(o1) && CrUtil.isObject(o2)) {
538
+ for (let key in o1) {
539
+ const _path = path ? `${path}.${key}` : key;
540
+ if (Object.prototype.hasOwnProperty.call(o1, key) && !(key in o2)) {
541
+ diffFieldSet[_path] = "removed";
542
+ } else if (Object.prototype.hasOwnProperty.call(o2, key)) {
543
+ compare(_path, o1[key], o2[key]);
544
+ }
545
+ }
546
+ for (let key in o2) {
547
+ const _path = path ? `${path}.${key}` : key;
548
+ if (Object.prototype.hasOwnProperty.call(o2, key) && !(key in o1)) {
549
+ diffFieldSet[_path] = "added";
550
+ }
551
+ }
552
+ } else if (o1 !== o2) {
553
+ diffFieldSet[path] = "modified";
554
+ }
555
+ };
556
+ compare("", data1, data2);
557
+ return diffFieldSet;
558
+ }
559
+ /**
560
+ * 格式化为千分位
561
+ * @param value
562
+ * @returns 12345.6789 => 12,345.6789
563
+ */
564
+ static fmtThousands(value) {
565
+ if (value == void 0 || value == null || isNaN(Number(value))) {
566
+ return String(value);
567
+ }
568
+ const [intPart, decimalPart] = String(value).split(".");
569
+ const formattedInt = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
570
+ return decimalPart ? `${formattedInt}.${decimalPart}` : formattedInt;
571
+ }
572
+ };