gis-common 4.2.17 → 4.2.19

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.
@@ -150,142 +150,505 @@ class Cookie {
150
150
  }
151
151
  }
152
152
  }
153
- const CommUtils = {
153
+ const MathUtil = {
154
+ DEG2RAD: Math.PI / 180,
155
+ RAD2DEG: 180 / Math.PI,
156
+ randInt(low, high) {
157
+ return low + Math.floor(Math.random() * (high - low + 1));
158
+ },
159
+ randFloat(low, high) {
160
+ return low + Math.random() * (high - low);
161
+ },
154
162
  /**
155
- * 获取数据类型
163
+ * 角度转弧度
156
164
  *
157
- * @param data 待判断的数据
158
- * @returns 返回数据类型字符串
165
+ * @param {*} degrees
166
+ * @returns {*}
159
167
  */
160
- getDataType(data) {
161
- return Object.prototype.toString.call(data).slice(8, -1);
162
- },
163
- asArray(obj) {
164
- return this.isEmpty(obj) ? [] : Array.isArray(obj) ? obj : [obj];
165
- },
166
- asNumber(a) {
167
- return Number.isNaN(Number(a)) ? 0 : Number(a);
168
+ deg2Rad(degrees) {
169
+ return degrees * this.DEG2RAD;
168
170
  },
169
171
  /**
170
- * 将值转换为字符串
172
+ * 弧度转角度
171
173
  *
172
- * @param value 要转换的值
173
- * @returns 转换后的字符串,如果值为空,则返回空字符串
174
+ * @param {*} radians
175
+ * @returns {*}
174
176
  */
175
- asString(value) {
176
- if (this.isEmpty(value)) {
177
- return "";
178
- } else {
179
- switch (this.getDataType(value)) {
180
- case "Object":
181
- case "Array":
182
- return JSON.stringify(value);
183
- default:
184
- return value;
185
- }
186
- }
177
+ rad2Deg(radians) {
178
+ return radians * this.RAD2DEG;
179
+ },
180
+ round(value, n = 2) {
181
+ return Math.round(value * Math.pow(10, n)) / Math.pow(10, n);
187
182
  },
188
183
  /**
189
- * 判断传入的值是否为空
184
+ * 将数值限制在指定范围内
190
185
  *
191
- * @param value 待判断的值
192
- * @returns 返回布尔值,表示是否为空
186
+ * @param val 需要限制的数值
187
+ * @param min 最小值
188
+ * @param max 最大值
189
+ * @returns 返回限制后的数值
193
190
  */
194
- isEmpty(value) {
195
- if (value == null) {
196
- return true;
197
- }
198
- const type = this.getDataType(value);
199
- switch (type) {
200
- case "String":
201
- return value.trim() === "";
202
- case "Array":
203
- return !value.length;
204
- case "Object":
205
- return !Object.keys(value).length;
206
- case "Boolean":
207
- return !value;
208
- default:
209
- return false;
191
+ clamp(val, min, max) {
192
+ return Math.max(min, Math.min(max, val));
193
+ }
194
+ };
195
+ class Color {
196
+ constructor(r, g, b, a) {
197
+ __publicField(this, "_r");
198
+ __publicField(this, "_g");
199
+ __publicField(this, "_b");
200
+ __publicField(this, "_alpha");
201
+ this._validateColorChannel(r);
202
+ this._validateColorChannel(g);
203
+ this._validateColorChannel(b);
204
+ this._r = r;
205
+ this._g = g;
206
+ this._b = b;
207
+ this._alpha = MathUtil.clamp(a || 1, 0, 1);
208
+ }
209
+ _validateColorChannel(channel) {
210
+ if (channel < 0 || channel > 255) {
211
+ throw new Error("Color channel must be between 0 and 255.");
210
212
  }
211
- },
213
+ }
214
+ toString() {
215
+ return `rgba(${this._r}, ${this._g}, ${this._b}, ${this._alpha})`;
216
+ }
217
+ toJson() {
218
+ return { r: this._r, g: this._g, b: this._b, a: this._alpha };
219
+ }
220
+ get rgba() {
221
+ return `rgba(${this._r}, ${this._g}, ${this._b}, ${this._alpha})`;
222
+ }
223
+ get hex() {
224
+ return Color.rgb2hex(this._r, this._g, this._b, this._alpha);
225
+ }
226
+ setAlpha(a) {
227
+ this._alpha = MathUtil.clamp(a, 0, 1);
228
+ return this;
229
+ }
230
+ // 设置颜色的RGB值
231
+ setRgb(r, g, b) {
232
+ this._validateColorChannel(r);
233
+ this._validateColorChannel(g);
234
+ this._validateColorChannel(b);
235
+ this._r = r;
236
+ this._g = g;
237
+ this._b = b;
238
+ return this;
239
+ }
212
240
  /**
213
- * 将JSON对象转换为FormData对象
241
+ * 从RGBA字符串创建Color对象
214
242
  *
215
- * @param json 待转换的JSON对象,其属性值为字符串或Blob类型
216
- * @returns 转换后的FormData对象
243
+ * @param rgbaValue RGBA颜色值字符串,格式为"rgba(r,g,b,a)"或"rgb(r,g,b)"
244
+ * @returns 返回Color对象
245
+ * @throws 如果rgbaValue不是有效的RGBA颜色值,则抛出错误
217
246
  */
218
- json2form(json) {
219
- const formData = new FormData();
220
- if (this.isEmpty(json)) return formData;
221
- Object.keys(json).forEach((key) => {
222
- formData.append(key, json[key] instanceof Object ? JSON.stringify(json[key]) : json[key]);
223
- });
224
- return formData;
225
- },
247
+ static fromRgba(rgbaValue) {
248
+ const rgbaMatch = rgbaValue.match(/^rgba?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([\d.]+))?\s*\)$/);
249
+ if (!rgbaMatch) throw new Error("Invalid RGBA color value");
250
+ const r = parseInt(rgbaMatch[1], 10);
251
+ const g = parseInt(rgbaMatch[2], 10);
252
+ const b = parseInt(rgbaMatch[3], 10);
253
+ const a = rgbaMatch[5] ? parseFloat(rgbaMatch[5]) : 1;
254
+ return new Color(r, g, b, a);
255
+ }
226
256
  /**
227
- * 生成GUID
257
+ * 将十六进制颜色值转换为颜色对象
228
258
  *
229
- * @returns 返回一个由8个16进制数组成的GUID字符串
259
+ * @param hexValue 十六进制颜色值,可带或不带#前缀,支持3位和6位表示
260
+ * @returns 返回颜色对象
230
261
  */
231
- guid() {
232
- const S4 = function() {
233
- return ((1 + Math.random()) * 65536 | 0).toString(16).substring(1);
234
- };
235
- return S4() + S4() + S4() + S4() + S4() + S4() + S4() + S4();
236
- },
262
+ static fromHex(hexValue, a = 1) {
263
+ const rgxShort = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
264
+ const hex = hexValue.replace(rgxShort, (m, r2, g2, b2) => r2 + r2 + g2 + g2 + b2 + b2);
265
+ const rgx = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i;
266
+ const rgb = rgx.exec(hex);
267
+ if (!rgb) {
268
+ throw new Error("Invalid HEX color value");
269
+ }
270
+ const r = parseInt(rgb[1], 16);
271
+ const g = parseInt(rgb[2], 16);
272
+ const b = parseInt(rgb[3], 16);
273
+ return new Color(r, g, b, a);
274
+ }
237
275
  /**
238
- * 将参数进行解码并返回解码后的字符串
276
+ * 从 HSL 字符串创建颜色对象
239
277
  *
240
- * @param args 参数
241
- * @returns 解码后的字符串
278
+ * @param hsl HSL 字符串,格式为 hsl(h, s%, l%) 或 hsla(h, s%, l%, a)
279
+ * @returns 返回颜色对象,如果 hsl 字符串无效则返回 null
242
280
  */
243
- decodeDict(...args) {
244
- let res = "";
245
- if (args.length > 1) {
246
- const items = args.slice(1, args.length % 2 === 0 ? args.length - 1 : args.length);
247
- for (let i = 0; i < items.length; i = i + 2) {
248
- const item = items[i];
249
- if (args[0] === item) {
250
- res = items[i + 1];
251
- }
252
- }
253
- if (!res && args.length % 2 === 0) {
254
- res = args[args.length - 1];
255
- }
281
+ static fromHsl(hslValue) {
282
+ const hsl = /hsl\((\d+),\s*([\d.]+)%,\s*([\d.]+)%\)/g.exec(hslValue) || /hsla\((\d+),\s*([\d.]+)%,\s*([\d.]+)%,\s*([\d.]+)\)/g.exec(hslValue);
283
+ if (!hsl) {
284
+ throw new Error("Invalid HSL color value");
285
+ }
286
+ const h = parseInt(hsl[1], 10) / 360;
287
+ const s = parseInt(hsl[2], 10) / 100;
288
+ const l = parseInt(hsl[3], 10) / 100;
289
+ const a = hsl[4] ? parseFloat(hsl[4]) : 1;
290
+ function hue2rgb(p, q, t) {
291
+ if (t < 0) t += 1;
292
+ if (t > 1) t -= 1;
293
+ if (t < 1 / 6) return p + (q - p) * 6 * t;
294
+ if (t < 1 / 2) return q;
295
+ if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
296
+ return p;
297
+ }
298
+ let r, g, b;
299
+ if (s === 0) {
300
+ r = g = b = l;
256
301
  } else {
257
- res = args[0];
302
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
303
+ const p = 2 * l - q;
304
+ r = hue2rgb(p, q, h + 1 / 3);
305
+ g = hue2rgb(p, q, h);
306
+ b = hue2rgb(p, q, h - 1 / 3);
258
307
  }
259
- return res;
260
- },
308
+ return new Color(Math.round(r * 255), Math.round(g * 255), Math.round(b * 255), a);
309
+ }
261
310
  /**
262
- * 将一个或多个对象的所有可枚举属性复制到目标对象。
311
+ * 从字符串中创建颜色对象
263
312
  *
264
- * @param dest 目标对象,用于接收复制的属性。
265
- * @param args 一个或多个源对象,用于提供要复制的属性。
266
- * @returns 返回目标对象,包含所有复制的属性。
313
+ * @param str 字符串类型的颜色值,支持rgba、hex、hsl格式
314
+ * @returns 返回创建的颜色对象
315
+ * @throws 当颜色值无效时,抛出错误
267
316
  */
268
- extend(dest, ...args) {
269
- let i, j, len, src;
270
- for (j = 0, len = args.length; j < len; j++) {
271
- src = args[j];
272
- for (i in src) {
273
- dest[i] = src[i];
274
- }
317
+ static from(str) {
318
+ if (this.isRgb(str)) {
319
+ return this.fromRgba(str);
320
+ } else if (this.isHex(str)) {
321
+ return this.fromHex(str);
322
+ } else if (this.isHsl(str)) {
323
+ return this.fromHsl(str);
324
+ } else {
325
+ throw new Error("Invalid color value");
275
326
  }
276
- return dest;
277
- },
327
+ }
278
328
  /**
279
- * 将扁平化数组转换为树形结构数组
329
+ * 将RGB颜色值转换为十六进制颜色值
280
330
  *
281
- * @param data 扁平化数组
282
- * @param idPropertyName 数据中标识id的字段名,默认为'id'
283
- * @param parentIdPropertyName 数据中标识父节点id的字段名,默认为'parentId'
284
- * @param childrenPropertyName 树形结构中标识子节点的字段名,默认为'children'
285
- * @returns 转换后的树形结构数组
331
+ * @param r 红色分量值,取值范围0-255
332
+ * @param g 绿色分量值,取值范围0-255
333
+ * @param b 蓝色分量值,取值范围0-255
334
+ * @param a 可选参数,透明度分量值,取值范围0-1
335
+ * @returns 十六进制颜色值,格式为#RRGGBB或#RRGGBBAA
286
336
  */
287
- convertToTree2(data, idPropertyName = "id", parentIdPropertyName = "parentId", childrenPropertyName = "children") {
288
- const result = [];
337
+ static rgb2hex(r, g, b, a) {
338
+ var hex = "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
339
+ if (a !== void 0) {
340
+ const alpha = Math.round(a * 255).toString(16).padStart(2, "0");
341
+ return hex + alpha;
342
+ }
343
+ return hex;
344
+ }
345
+ static isHex(a) {
346
+ return /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(a);
347
+ }
348
+ static isRgb(a) {
349
+ return /^rgba?\s*\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*(,\s*[\d.]+)?\s*\)$/.test(a);
350
+ }
351
+ static isHsl(a) {
352
+ return /^(hsl|hsla)\(\d+,\s*[\d.]+%,\s*[\d.]+%(,\s*[\d.]+)?\)$/.test(a);
353
+ }
354
+ static isColor(a) {
355
+ return this.isHex(a) || this.isRgb(a) || this.isHsl(a);
356
+ }
357
+ static random() {
358
+ let r = Math.floor(Math.random() * 256);
359
+ let g = Math.floor(Math.random() * 256);
360
+ let b = Math.floor(Math.random() * 256);
361
+ let a = Math.random();
362
+ return new Color(r, g, b, a);
363
+ }
364
+ }
365
+ class CanvasDrawer {
366
+ constructor(el) {
367
+ __publicField(this, "context", null);
368
+ if (typeof el === "string") {
369
+ el = document.querySelector("#" + el);
370
+ if (!el) {
371
+ throw new Error("Element not found");
372
+ }
373
+ }
374
+ if (el instanceof HTMLElement) {
375
+ const canvas = el;
376
+ if (canvas.getContext) {
377
+ this.context = canvas.getContext("2d");
378
+ } else {
379
+ throw new Error("getContext is not available on this element");
380
+ }
381
+ } else {
382
+ throw new Error("Element is not an HTMLElement");
383
+ }
384
+ }
385
+ /**
386
+ * 绘制线条
387
+ *
388
+ * @param start 起始坐标点
389
+ * @param end 终止坐标点
390
+ * @param options 绘制选项,包括线条宽度和颜色
391
+ * @throws 当画布上下文不存在时抛出错误
392
+ */
393
+ drawLine({ x: startX, y: startY }, { x: endX, y: endY }, options = {}) {
394
+ if (!this.context) {
395
+ throw new Error("Canvas context is null or undefined");
396
+ }
397
+ this.context.beginPath();
398
+ const width = options.width || 1;
399
+ const color = options.color || "#000";
400
+ this.context.lineWidth = width;
401
+ this.context.strokeStyle = color;
402
+ this.context.moveTo(startX, startY);
403
+ this.context.lineTo(endX, endY);
404
+ this.context.stroke();
405
+ }
406
+ /**
407
+ * 绘制圆弧
408
+ *
409
+ * @param x 圆心x坐标
410
+ * @param y 圆心y坐标
411
+ * @param radius 半径
412
+ * @param startAngle 起始角度(度)
413
+ * @param endAngle 结束角度(度)
414
+ * @param anticlockwise 是否逆时针绘制
415
+ * @param isFill 是否填充
416
+ * @param bgColor 背景颜色
417
+ * @throws 当Canvas context为null或undefined时抛出错误
418
+ */
419
+ drawArc({ x, y }, radius, startAngle, endAngle, anticlockwise, isFill, bgColor) {
420
+ if (!this.context) {
421
+ throw new Error("Canvas context is null or undefined");
422
+ }
423
+ if (isFill) {
424
+ this.context.fillStyle = bgColor;
425
+ this.context.beginPath();
426
+ this.context.arc(x, y, radius, MathUtil.deg2Rad(startAngle), MathUtil.deg2Rad(endAngle), anticlockwise);
427
+ this.context.fill();
428
+ } else {
429
+ this.context.strokeStyle = bgColor;
430
+ this.context.beginPath();
431
+ this.context.arc(x, y, radius, MathUtil.deg2Rad(startAngle), MathUtil.deg2Rad(endAngle), anticlockwise);
432
+ this.context.stroke();
433
+ }
434
+ }
435
+ static createCanvas(width = 1, height = 1) {
436
+ const canvas = document.createElement("canvas");
437
+ if (width) {
438
+ canvas.width = width;
439
+ }
440
+ if (height) {
441
+ canvas.height = height;
442
+ }
443
+ return canvas;
444
+ }
445
+ }
446
+ class EventDispatcher {
447
+ constructor() {
448
+ __publicField(this, "_listeners");
449
+ __publicField(this, "_mutex", {});
450
+ __publicField(this, "_context");
451
+ }
452
+ addEventListener(type, listener, context, mutexStatus) {
453
+ if (this._listeners === void 0) this._listeners = {};
454
+ this._context = context;
455
+ const mutex = this._mutex;
456
+ const listeners = this._listeners;
457
+ if (listeners[type] === void 0) {
458
+ listeners[type] = [];
459
+ }
460
+ if (listeners[type].indexOf(listener) === -1) {
461
+ if (mutexStatus) {
462
+ mutex[type] = listener;
463
+ }
464
+ listeners[type].push(listener);
465
+ }
466
+ return this;
467
+ }
468
+ hasEventListener(type, listener) {
469
+ if (this._listeners === null || this._listeners === void 0) return false;
470
+ const listeners = this._listeners;
471
+ return listeners[type] !== void 0 && listeners[type].indexOf(listener) !== -1;
472
+ }
473
+ removeEventListener(type, listener) {
474
+ if (this._listeners === void 0) return;
475
+ const listeners = this._listeners;
476
+ const listenerArray = listeners[type];
477
+ if (this._mutex[type] === listener) {
478
+ this._mutex[type] = null;
479
+ }
480
+ if (listenerArray !== void 0) {
481
+ const index = listenerArray.map((d) => d.toString()).indexOf(listener.toString());
482
+ if (index !== -1) {
483
+ listenerArray.splice(index, 1);
484
+ }
485
+ }
486
+ }
487
+ dispatchEvent(event) {
488
+ if (this._listeners === void 0) return;
489
+ const listeners = this._listeners;
490
+ const listenerArray = listeners[event.type];
491
+ if (listenerArray !== void 0) {
492
+ event.target = this;
493
+ const array = listenerArray.slice(0);
494
+ if (this._mutex[event.type] !== void 0) {
495
+ const find = array.find((item) => item === this._mutex[event.type]);
496
+ if (find) {
497
+ find.call(this._context || this, event);
498
+ return;
499
+ }
500
+ }
501
+ for (let i = 0, l = array.length; i < l; i++) {
502
+ const item = array[i];
503
+ if (typeof item === "function") {
504
+ item.call(this._context || this, event);
505
+ }
506
+ }
507
+ }
508
+ }
509
+ removeAllListener() {
510
+ this._mutex = {};
511
+ for (const key in this._listeners) {
512
+ this._listeners[key] = [];
513
+ }
514
+ }
515
+ }
516
+ const Util = {
517
+ /**
518
+ * 获取数据类型
519
+ *
520
+ * @param data 待判断的数据
521
+ * @returns 返回数据类型字符串
522
+ */
523
+ getDataType(data) {
524
+ return Object.prototype.toString.call(data).slice(8, -1);
525
+ },
526
+ asArray(obj) {
527
+ return this.isEmpty(obj) ? [] : Array.isArray(obj) ? obj : [obj];
528
+ },
529
+ asNumber(a) {
530
+ return Number.isNaN(Number(a)) ? 0 : Number(a);
531
+ },
532
+ /**
533
+ * 将值转换为字符串
534
+ *
535
+ * @param value 要转换的值
536
+ * @returns 转换后的字符串,如果值为空,则返回空字符串
537
+ */
538
+ asString(value) {
539
+ if (this.isEmpty(value)) {
540
+ return "";
541
+ } else {
542
+ switch (this.getDataType(value)) {
543
+ case "Object":
544
+ case "Array":
545
+ return JSON.stringify(value);
546
+ default:
547
+ return value;
548
+ }
549
+ }
550
+ },
551
+ /**
552
+ * 判断传入的值是否为空
553
+ *
554
+ * @param value 待判断的值
555
+ * @returns 返回布尔值,表示是否为空
556
+ */
557
+ isEmpty(value) {
558
+ if (value == null) {
559
+ return true;
560
+ }
561
+ const type = this.getDataType(value);
562
+ switch (type) {
563
+ case "String":
564
+ return value.trim() === "";
565
+ case "Array":
566
+ return !value.length;
567
+ case "Object":
568
+ return !Object.keys(value).length;
569
+ case "Boolean":
570
+ return !value;
571
+ default:
572
+ return false;
573
+ }
574
+ },
575
+ /**
576
+ * 将JSON对象转换为FormData对象
577
+ *
578
+ * @param json 待转换的JSON对象,其属性值为字符串或Blob类型
579
+ * @returns 转换后的FormData对象
580
+ */
581
+ json2form(json) {
582
+ const formData = new FormData();
583
+ if (this.isEmpty(json)) return formData;
584
+ Object.keys(json).forEach((key) => {
585
+ formData.append(key, json[key] instanceof Object ? JSON.stringify(json[key]) : json[key]);
586
+ });
587
+ return formData;
588
+ },
589
+ /**
590
+ * 生成GUID
591
+ *
592
+ * @returns 返回一个由8个16进制数组成的GUID字符串
593
+ */
594
+ guid() {
595
+ const S4 = function() {
596
+ return ((1 + Math.random()) * 65536 | 0).toString(16).substring(1);
597
+ };
598
+ return S4() + S4() + S4() + S4() + S4() + S4() + S4() + S4();
599
+ },
600
+ /**
601
+ * 将参数进行解码并返回解码后的字符串
602
+ *
603
+ * @param args 参数
604
+ * @returns 解码后的字符串
605
+ */
606
+ decodeDict(...args) {
607
+ let res = "";
608
+ if (args.length > 1) {
609
+ const items = args.slice(1, args.length % 2 === 0 ? args.length - 1 : args.length);
610
+ for (let i = 0; i < items.length; i = i + 2) {
611
+ const item = items[i];
612
+ if (args[0] === item) {
613
+ res = items[i + 1];
614
+ }
615
+ }
616
+ if (!res && args.length % 2 === 0) {
617
+ res = args[args.length - 1];
618
+ }
619
+ } else {
620
+ res = args[0];
621
+ }
622
+ return res;
623
+ },
624
+ /**
625
+ * 将一个或多个对象的所有可枚举属性复制到目标对象。
626
+ *
627
+ * @param dest 目标对象,用于接收复制的属性。
628
+ * @param args 一个或多个源对象,用于提供要复制的属性。
629
+ * @returns 返回目标对象,包含所有复制的属性。
630
+ */
631
+ extend(dest, ...args) {
632
+ let i, j, len, src;
633
+ for (j = 0, len = args.length; j < len; j++) {
634
+ src = args[j];
635
+ for (i in src) {
636
+ dest[i] = src[i];
637
+ }
638
+ }
639
+ return dest;
640
+ },
641
+ /**
642
+ * 将扁平化数组转换为树形结构数组
643
+ *
644
+ * @param data 扁平化数组
645
+ * @param idPropertyName 数据中标识id的字段名,默认为'id'
646
+ * @param parentIdPropertyName 数据中标识父节点id的字段名,默认为'parentId'
647
+ * @param childrenPropertyName 树形结构中标识子节点的字段名,默认为'children'
648
+ * @returns 转换后的树形结构数组
649
+ */
650
+ convertToTree2(data, idPropertyName = "id", parentIdPropertyName = "parentId", childrenPropertyName = "children") {
651
+ const result = [];
289
652
  function buildChildren(item) {
290
653
  const children = data.filter((item2) => item2[parentIdPropertyName] === item[idPropertyName]).map((child) => {
291
654
  if (!result.some((r) => r[idPropertyName] === child[idPropertyName])) {
@@ -480,20 +843,153 @@ const CommUtils = {
480
843
  var numT = targetV.replace(/[^0-9]/gi, "");
481
844
  return numC < numT;
482
845
  }
483
- };
484
- const ObjectUtil = {
485
- deepClone(a) {
486
- return structuredClone(a);
487
- },
488
- isEqual(a, b) {
489
- return JSON.stringify(a) === JSON.stringify(b);
490
- },
491
- parse(str) {
492
- if (typeof str === "string" && str.startsWith("{") && str.endsWith("}")) return JSON.parse(str);
493
- if (CommUtils.isEmpty(str)) return {};
494
- if (CommUtils.isObject(str)) return str;
846
+ };
847
+ const ObjectUtil = {
848
+ deepClone(a) {
849
+ return structuredClone(a);
850
+ },
851
+ isEqual(a, b) {
852
+ return JSON.stringify(a) === JSON.stringify(b);
853
+ },
854
+ parse(str) {
855
+ if (typeof str === "string" && str.startsWith("{") && str.endsWith("}")) return JSON.parse(str);
856
+ if (Util.isEmpty(str)) return {};
857
+ if (Util.isObject(str)) return str;
858
+ }
859
+ };
860
+ class HashMap extends Map {
861
+ isEmpty() {
862
+ return this.size === 0;
863
+ }
864
+ _values() {
865
+ return Array.from(this.values());
866
+ }
867
+ _keys() {
868
+ return Array.from(this.keys());
869
+ }
870
+ _entries() {
871
+ return Array.from(this.entries());
872
+ }
873
+ /**
874
+ * 从键值对数组创建一个HashMap对象
875
+ *
876
+ * @param array 键值对数组,默认为空数组
877
+ * @returns 返回一个新的HashMap对象
878
+ */
879
+ static fromEntries(array = []) {
880
+ const hashMap = new HashMap();
881
+ array.forEach((element) => {
882
+ if (Array.isArray(element) && element.length === 2) {
883
+ hashMap.set(element[0], element[1]);
884
+ }
885
+ });
886
+ return hashMap;
887
+ }
888
+ /**
889
+ * 从JSON字符串创建HashMap实例
890
+ *
891
+ * @param str JSON字符串
892
+ * @returns HashMap实例
893
+ */
894
+ static fromJson(str) {
895
+ const json = ObjectUtil.parse(str);
896
+ return new HashMap(Object.entries(json));
897
+ }
898
+ }
899
+ class WebSocketClient extends EventDispatcher {
900
+ constructor(url = "ws://127.0.0.1:10088") {
901
+ super();
902
+ __publicField(this, "maxCheckTimes", 10);
903
+ __publicField(this, "url");
904
+ __publicField(this, "checkTimes", 0);
905
+ __publicField(this, "connectStatus", false);
906
+ __publicField(this, "client", null);
907
+ this.maxCheckTimes = 10;
908
+ this.url = url;
909
+ this.checkTimes = 0;
910
+ this.connect();
911
+ this.connCheckStatus(this.maxCheckTimes);
912
+ }
913
+ connect() {
914
+ this.disconnect();
915
+ if (this.url) {
916
+ try {
917
+ console.info("创建ws连接>>>" + this.url);
918
+ this.client = new WebSocket(this.url);
919
+ if (this.client) {
920
+ const self = this;
921
+ this.client.onopen = function(message) {
922
+ self.dispatchEvent({
923
+ type: EventType.WEB_SOCKET_CONNECT,
924
+ message
925
+ });
926
+ };
927
+ this.client.onmessage = function(message) {
928
+ self.connectStatus = true;
929
+ self.dispatchEvent({
930
+ type: EventType.WEB_SOCKET_MESSAGE,
931
+ message
932
+ });
933
+ };
934
+ this.client.onclose = function(message) {
935
+ self.dispatchEvent({
936
+ type: EventType.WEB_SOCKET_CLOSE,
937
+ message
938
+ });
939
+ };
940
+ if (this.checkTimes === this.maxCheckTimes) {
941
+ this.client.onerror = function(message) {
942
+ self.dispatchEvent({
943
+ type: EventType.WEB_SOCKET_ERROR,
944
+ message
945
+ });
946
+ };
947
+ }
948
+ }
949
+ } catch (ex) {
950
+ console.error("创建ws连接失败" + this.url + ":" + ex);
951
+ }
952
+ }
953
+ }
954
+ disconnect() {
955
+ if (this.client) {
956
+ try {
957
+ console.log("ws断开连接" + this.url);
958
+ this.client.close();
959
+ this.client = null;
960
+ } catch (ex) {
961
+ this.client = null;
962
+ }
963
+ }
964
+ }
965
+ connCheckStatus(times) {
966
+ if (this.checkTimes > times) return;
967
+ setTimeout(() => {
968
+ this.checkTimes++;
969
+ if (this.client && this.client.readyState !== 0 && this.client.readyState !== 1) {
970
+ this.connect();
971
+ }
972
+ this.connCheckStatus(times);
973
+ }, 2e3);
974
+ }
975
+ send(message) {
976
+ if (this.client && this.client.readyState === 1) {
977
+ this.client.send(message);
978
+ return true;
979
+ }
980
+ console.error(this.url + "消息发送失败:" + message);
981
+ return this;
495
982
  }
496
- };
983
+ heartbeat() {
984
+ setTimeout(() => {
985
+ if (this.client && this.client.readyState === 1) {
986
+ this.send("HeartBeat");
987
+ }
988
+ console.log("HeartBeat," + this.url);
989
+ setTimeout(this.heartbeat, 3e4);
990
+ }, 1e3);
991
+ }
992
+ }
497
993
  const ImageUtil = {
498
994
  emptyImageUrl: "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",
499
995
  /**
@@ -606,7 +1102,7 @@ const AjaxUtil = {
606
1102
  * @param callback - callback function when completed
607
1103
  */
608
1104
  jsonp(url, callback) {
609
- const name = "_jsonp_" + CommUtils.guid();
1105
+ const name = "_jsonp_" + Util.guid();
610
1106
  const head = document.getElementsByTagName("head")[0];
611
1107
  if (url.includes("?")) {
612
1108
  url += "&callback=" + name;
@@ -645,7 +1141,7 @@ const AjaxUtil = {
645
1141
  * );
646
1142
  */
647
1143
  get(url, options = {}, cb) {
648
- if (CommUtils.isFunction(options)) {
1144
+ if (Util.isFunction(options)) {
649
1145
  const t = cb;
650
1146
  cb = options;
651
1147
  options = t;
@@ -777,7 +1273,7 @@ const AjaxUtil = {
777
1273
  * );
778
1274
  */
779
1275
  getArrayBuffer(url, options, cb) {
780
- if (CommUtils.isFunction(options)) {
1276
+ if (Util.isFunction(options)) {
781
1277
  const t = cb;
782
1278
  cb = options;
783
1279
  options = t;
@@ -830,7 +1326,7 @@ const AjaxUtil = {
830
1326
  * );
831
1327
  */
832
1328
  getJSON(url, options, cb) {
833
- if (CommUtils.isFunction(options)) {
1329
+ if (Util.isFunction(options)) {
834
1330
  const t = cb;
835
1331
  cb = options;
836
1332
  options = t;
@@ -847,48 +1343,6 @@ const AjaxUtil = {
847
1343
  return this.get(url, options, callback);
848
1344
  }
849
1345
  };
850
- const MathUtils = {
851
- DEG2RAD: Math.PI / 180,
852
- RAD2DEG: 180 / Math.PI,
853
- randInt(low, high) {
854
- return low + Math.floor(Math.random() * (high - low + 1));
855
- },
856
- randFloat(low, high) {
857
- return low + Math.random() * (high - low);
858
- },
859
- /**
860
- * 角度转弧度
861
- *
862
- * @param {*} degrees
863
- * @returns {*}
864
- */
865
- deg2Rad(degrees) {
866
- return degrees * this.DEG2RAD;
867
- },
868
- /**
869
- * 弧度转角度
870
- *
871
- * @param {*} radians
872
- * @returns {*}
873
- */
874
- rad2Deg(radians) {
875
- return radians * this.RAD2DEG;
876
- },
877
- round(value, n = 2) {
878
- return Math.round(value * Math.pow(10, n)) / Math.pow(10, n);
879
- },
880
- /**
881
- * 将数值限制在指定范围内
882
- *
883
- * @param val 需要限制的数值
884
- * @param min 最小值
885
- * @param max 最大值
886
- * @returns 返回限制后的数值
887
- */
888
- clamp(val, min, max) {
889
- return Math.max(min, Math.min(max, val));
890
- }
891
- };
892
1346
  class GeoUtil {
893
1347
  /**
894
1348
  * 判断给定的经纬度是否合法
@@ -898,7 +1352,7 @@ class GeoUtil {
898
1352
  * @returns 如果经纬度合法,返回true;否则返回false
899
1353
  */
900
1354
  static isLnglat(lng, lat) {
901
- return !isNaN(lng) && !isNaN(lat) && !!(+lat > -90 && +lat < 90 && +lng > -180 && +lng < 180);
1355
+ return Util.isNumber(lng) && Util.isNumber(lat) && !!(+lat > -90 && +lat < 90 && +lng > -180 && +lng < 180);
902
1356
  }
903
1357
  /**
904
1358
  * 计算两哥平面坐标点间的距离
@@ -1090,15 +1544,15 @@ class GeoUtil {
1090
1544
  * @returns 返回计算后的新经纬度点,类型为{lat: number, lng: number}
1091
1545
  */
1092
1546
  static calcPointByBearAndDis(latlng, angle, distance) {
1093
- const sLat = MathUtils.deg2Rad(latlng.lat * 1);
1094
- const sLng = MathUtils.deg2Rad(latlng.lng * 1);
1547
+ const sLat = MathUtil.deg2Rad(latlng.lat * 1);
1548
+ const sLng = MathUtil.deg2Rad(latlng.lng * 1);
1095
1549
  const d = distance / this.R;
1096
- angle = MathUtils.deg2Rad(angle);
1550
+ angle = MathUtil.deg2Rad(angle);
1097
1551
  const lat = Math.asin(Math.sin(sLat) * Math.cos(d) + Math.cos(sLat) * Math.sin(d) * Math.cos(angle));
1098
1552
  const lon = sLng + Math.atan2(Math.sin(angle) * Math.sin(d) * Math.cos(sLat), Math.cos(d) - Math.sin(sLat) * Math.sin(lat));
1099
1553
  return {
1100
- lat: MathUtils.rad2Deg(lat),
1101
- lng: MathUtils.rad2Deg(lon)
1554
+ lat: MathUtil.rad2Deg(lat),
1555
+ lng: MathUtil.rad2Deg(lon)
1102
1556
  };
1103
1557
  }
1104
1558
  /**
@@ -1247,26 +1701,6 @@ const StringUtil = {
1247
1701
  return str;
1248
1702
  }
1249
1703
  },
1250
- /**
1251
- * 根据字符串数组和参数生成新的字符串
1252
- *
1253
- * @param strArray 字符串数组
1254
- * @param args 可变参数列表,支持 Object、Array 类型和任意其他类型,若为 null 或 undefined,则按类型默认转换为 '{}'、'[]' 或 ''
1255
- * @returns 返回生成的新字符串
1256
- */
1257
- tag(strArray, ...args) {
1258
- args = args.map((val) => {
1259
- switch (CommUtils.getDataType(val)) {
1260
- case "Object":
1261
- return val || "{}";
1262
- case "Array":
1263
- return val || "[]";
1264
- default:
1265
- return val || "";
1266
- }
1267
- });
1268
- return strArray.reduce((prev, next, index) => `${prev}${args[index - 1]}${next}`);
1269
- },
1270
1704
  /**
1271
1705
  * 计算字符串的字节长度
1272
1706
  *
@@ -1589,42 +2023,42 @@ const GeoJsonUtil = {
1589
2023
  const AssertUtil = {
1590
2024
  assertEmpty(...arg) {
1591
2025
  arg.forEach((a) => {
1592
- if (CommUtils.isEmpty(a)) {
2026
+ if (Util.isEmpty(a)) {
1593
2027
  throw Error(ErrorType.PARAMETER_ERROR_LACK + " -> " + a);
1594
2028
  }
1595
2029
  });
1596
2030
  },
1597
2031
  assertInteger(...arg) {
1598
2032
  arg.forEach((a) => {
1599
- if (!CommUtils.isInteger(a)) {
2033
+ if (!Util.isInteger(a)) {
1600
2034
  throw Error(ErrorType.PARAMETER_ERROR_INTEGER + " -> " + a);
1601
2035
  }
1602
2036
  });
1603
2037
  },
1604
2038
  assertNumber(...arg) {
1605
2039
  arg.forEach((a) => {
1606
- if (!CommUtils.isNumber(a)) {
2040
+ if (!Util.isNumber(a)) {
1607
2041
  throw Error(ErrorType.PARAMETER_ERROR_NUMBER + " -> " + a);
1608
2042
  }
1609
2043
  });
1610
2044
  },
1611
2045
  assertArray(...arg) {
1612
2046
  arg.forEach((a) => {
1613
- if (!CommUtils.isArray(a)) {
2047
+ if (!Util.isArray(a)) {
1614
2048
  throw Error(ErrorType.PARAMETER_ERROR_ARRAY + " -> " + a);
1615
2049
  }
1616
2050
  });
1617
2051
  },
1618
2052
  assertFunction(...arg) {
1619
2053
  arg.forEach((a) => {
1620
- if (!CommUtils.isFunction(a)) {
2054
+ if (!Util.isFunction(a)) {
1621
2055
  throw Error(ErrorType.PARAMETER_ERROR_FUNCTION + " -> " + a);
1622
2056
  }
1623
2057
  });
1624
2058
  },
1625
2059
  assertObject(...arg) {
1626
2060
  arg.forEach((a) => {
1627
- if (!CommUtils.isObject(a)) {
2061
+ if (!Util.isObject(a)) {
1628
2062
  throw Error(ErrorType.PARAMETER_ERROR_OBJECT + " -> " + a);
1629
2063
  }
1630
2064
  });
@@ -2328,7 +2762,7 @@ class CoordsUtil {
2328
2762
  let p, pp;
2329
2763
  for (let i = 0, len = arr.length; i < len; i++) {
2330
2764
  p = arr[i];
2331
- if (CommUtils.isNil(p)) {
2765
+ if (Util.isNil(p)) {
2332
2766
  result.push(null);
2333
2767
  continue;
2334
2768
  }
@@ -2512,955 +2946,492 @@ __publicField(DateUtil, "lastWeekDate", new Date((/* @__PURE__ */ new Date()).ge
2512
2946
  __publicField(DateUtil, "thisWeekDate", new Date((/* @__PURE__ */ new Date()).getFullYear(), (/* @__PURE__ */ new Date()).getMonth(), (/* @__PURE__ */ new Date()).getDate() + 1 - (/* @__PURE__ */ new Date()).getDay()));
2513
2947
  __publicField(DateUtil, "nextWeekDate", new Date((/* @__PURE__ */ new Date()).getFullYear(), (/* @__PURE__ */ new Date()).getMonth(), (/* @__PURE__ */ new Date()).getDate() + 1 + 7 - (/* @__PURE__ */ new Date()).getDay()));
2514
2948
  __publicField(DateUtil, "lastDayDate", new Date((/* @__PURE__ */ new Date()).getFullYear(), (/* @__PURE__ */ new Date()).getMonth(), (/* @__PURE__ */ new Date()).getDate() - 1));
2515
- __publicField(DateUtil, "thisDayDate", new Date((/* @__PURE__ */ new Date()).setHours(0, 0, 0, 0)));
2516
- __publicField(DateUtil, "nextDayDate", new Date((/* @__PURE__ */ new Date()).getFullYear(), (/* @__PURE__ */ new Date()).getMonth(), (/* @__PURE__ */ new Date()).getDate() + 1));
2517
- function trim(str) {
2518
- return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, "");
2519
- }
2520
- function splitWords(str) {
2521
- return trim(str).split(/\s+/);
2522
- }
2523
- const DomUtil = {
2524
- /**
2525
- * 获取元素的样式值
2526
- *
2527
- * @param el 元素对象
2528
- * @param style 样式属性名称
2529
- * @returns 元素的样式值,如果获取不到则返回 null
2530
- */
2531
- getStyle(el, style) {
2532
- var _a2;
2533
- let value = el.style[style];
2534
- if (!value || value === "auto") {
2535
- const css = (_a2 = document.defaultView) == null ? void 0 : _a2.getComputedStyle(el, null);
2536
- value = css ? css[style] : null;
2537
- if (value === "auto") value = null;
2538
- }
2539
- return value;
2540
- },
2541
- /**
2542
- * 创建一个HTML元素
2543
- *
2544
- * @param tagName 元素标签名
2545
- * @param className 元素类名
2546
- * @param container 父容器,若传入,则新创建的元素会被添加到该容器中
2547
- * @returns 返回新创建的HTML元素
2548
- */
2549
- create(tagName, className, container) {
2550
- const el = document.createElement(tagName);
2551
- el.className = className || "";
2552
- if (container) {
2553
- container.appendChild(el);
2554
- }
2555
- return el;
2556
- },
2557
- /**
2558
- * 从父节点中移除指定元素。
2559
- *
2560
- * @param el 要移除的元素对象,必须包含parentNode属性。
2561
- */
2562
- remove(el) {
2563
- const parent = el.parentNode;
2564
- if (parent) {
2565
- parent.removeChild(el);
2566
- }
2567
- },
2568
- /**
2569
- * 清空给定元素的子节点
2570
- *
2571
- * @param el 要清空子节点的元素,包含firstChild和removeChild属性
2572
- */
2573
- empty(el) {
2574
- while (el.firstChild) {
2575
- el.removeChild(el.firstChild);
2576
- }
2577
- },
2578
- /**
2579
- * 将元素移到父节点的最前面
2580
- *
2581
- * @param el 要移动的元素,需要包含 parentNode 属性
2582
- */
2583
- toFront(el) {
2584
- const parent = el.parentNode;
2585
- if (parent && parent.lastChild !== el) {
2586
- parent.appendChild(el);
2587
- }
2588
- },
2589
- /**
2590
- * 将元素移动到其父节点的最前面
2591
- *
2592
- * @param el 要移动的元素,需要包含parentNode属性
2593
- */
2594
- toBack(el) {
2595
- const parent = el.parentNode;
2596
- if (parent && parent.firstChild !== el) {
2597
- parent.insertBefore(el, parent.firstChild);
2598
- }
2599
- },
2600
- /**
2601
- * 获取元素的类名
2602
- *
2603
- * @param el 包含对应元素和类名的对象
2604
- * @param el.correspondingElement 对应的元素
2605
- * @param el.className 类名对象
2606
- * @param el.className.baseVal 类名字符串
2607
- * @returns 返回元素的类名字符串
2608
- */
2609
- getClass(el) {
2610
- const shadowElement = (el == null ? void 0 : el.host) || el;
2611
- return shadowElement.className.toString();
2612
- },
2613
- /**
2614
- * 判断元素是否包含指定类名
2615
- *
2616
- * @param el 元素对象,包含classList属性,classList属性包含contains方法
2617
- * @param name 要判断的类名
2618
- * @returns 返回一个布尔值,表示元素是否包含指定类名
2619
- */
2620
- hasClass(el, name) {
2621
- var _a2;
2622
- if ((_a2 = el.classList) == null ? void 0 : _a2.contains(name)) {
2623
- return true;
2624
- }
2625
- const className = this.getClass(el);
2626
- return className.length > 0 && new RegExp(`(^|\\s)${name}(\\s|$)`).test(className);
2627
- },
2628
- /**
2629
- * 给指定的 HTML 元素添加类名
2630
- *
2631
- * @param el 要添加类名的 HTML 元素
2632
- * @param name 要添加的类名,多个类名之间用空格分隔
2633
- */
2634
- addClass(el, name) {
2635
- if (el.classList !== void 0) {
2636
- const classes = splitWords(name);
2637
- for (let i = 0, len = classes.length; i < len; i++) {
2638
- el.classList.add(classes[i]);
2639
- }
2640
- } else if (!this.hasClass(el, name)) {
2641
- const className = this.getClass(el);
2642
- this.setClass(el, (className ? className + " " : "") + name);
2643
- }
2644
- },
2645
- /**
2646
- * 从元素中移除指定类名
2647
- *
2648
- * @param el 要移除类名的元素
2649
- * @param name 要移除的类名,多个类名用空格分隔
2650
- */
2651
- removeClass(el, name) {
2652
- if (el.classList !== void 0) {
2653
- const classes = splitWords(name);
2654
- classes.forEach((className) => el.classList.remove(className));
2655
- } else {
2656
- this.setClass(el, (" " + this.getClass(el) + " ").replace(" " + name + " ", " ").trim());
2657
- }
2658
- },
2659
- /**
2660
- * 设置元素的 CSS 类名
2661
- *
2662
- * @param el HTML 或 SVG 元素
2663
- * @param name 要设置的类名,多个类名之间用空格分隔
2664
- */
2665
- setClass(el, name) {
2666
- if ("classList" in el) {
2667
- el.classList.value = "";
2668
- name.split(" ").forEach((className) => el.classList.add(className));
2669
- }
2670
- },
2671
- /**
2672
- * 从字符串中解析XML文档,并返回根节点
2673
- *
2674
- * @param str 要解析的XML字符串
2675
- * @returns 解析后的XML文档的根节点
2676
- */
2677
- parseFromString(str) {
2678
- const parser = new DOMParser();
2679
- const doc = parser.parseFromString(str, "text/xml");
2680
- return doc.children[0];
2681
- }
2682
- };
2683
- const FileUtil = {
2949
+ __publicField(DateUtil, "thisDayDate", new Date((/* @__PURE__ */ new Date()).setHours(0, 0, 0, 0)));
2950
+ __publicField(DateUtil, "nextDayDate", new Date((/* @__PURE__ */ new Date()).getFullYear(), (/* @__PURE__ */ new Date()).getMonth(), (/* @__PURE__ */ new Date()).getDate() + 1));
2951
+ function trim(str) {
2952
+ return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, "");
2953
+ }
2954
+ function splitWords(str) {
2955
+ return trim(str).split(/\s+/);
2956
+ }
2957
+ const DomUtil = {
2684
2958
  /**
2685
- * 将Base64编码的字符串转换为Blob对象
2959
+ * 获取元素的样式值
2686
2960
  *
2687
- * @param data Base64编码的字符串
2688
- * @returns 转换后的Blob对象
2961
+ * @param el 元素对象
2962
+ * @param style 样式属性名称
2963
+ * @returns 元素的样式值,如果获取不到则返回 null
2689
2964
  */
2690
- convertBase64ToBlob(data) {
2691
- const mimeString = data.split(",")[0].split(":")[1].split(";")[0];
2692
- const byteCharacters = atob(data.split(",")[1]);
2693
- const byteNumbers = new Array(byteCharacters.length);
2694
- for (let i = 0; i < byteCharacters.length; i++) {
2695
- byteNumbers[i] = byteCharacters.charCodeAt(i);
2965
+ getStyle(el, style) {
2966
+ var _a2;
2967
+ let value = el.style[style];
2968
+ if (!value || value === "auto") {
2969
+ const css = (_a2 = document.defaultView) == null ? void 0 : _a2.getComputedStyle(el, null);
2970
+ value = css ? css[style] : null;
2971
+ if (value === "auto") value = null;
2696
2972
  }
2697
- const byteArray = new Uint8Array(byteNumbers);
2698
- const blob = new Blob([byteArray], { type: mimeString });
2699
- return blob;
2973
+ return value;
2700
2974
  },
2701
2975
  /**
2702
- * 将图片的URL转换为Base64编码
2703
- *
2704
- * @param url 图片的URL地址
2705
- * @param width 图片的宽度,默认为图片原始宽度
2706
- * @param height 图片的高度,默认为图片原始高度
2707
- * @returns 返回Promise对象,解析后得到包含Base64编码数据的对象
2708
- */
2709
- /**
2710
- * 将base64字符串转换为文件对象
2976
+ * 创建一个HTML元素
2711
2977
  *
2712
- * @param dataurl 包含base64字符串的数据URL
2713
- * @param filename 文件的名称
2714
- * @returns 返回文件对象
2978
+ * @param tagName 元素标签名
2979
+ * @param className 元素类名
2980
+ * @param container 父容器,若传入,则新创建的元素会被添加到该容器中
2981
+ * @returns 返回新创建的HTML元素
2715
2982
  */
2716
- convertBase64ToFile(dataurl, filename) {
2717
- const arr = dataurl.split(",");
2718
- const mimeMatch = arr[0].match(/:(.*?);/);
2719
- const mime = mimeMatch ? mimeMatch[1] : "image/png";
2720
- const bstr = atob(arr[1]);
2721
- const u8arr = new Uint8Array(bstr.length);
2722
- for (let i = 0; i < bstr.length; i++) {
2723
- u8arr[i] = bstr.charCodeAt(i);
2983
+ create(tagName, className, container) {
2984
+ const el = document.createElement(tagName);
2985
+ el.className = className || "";
2986
+ if (container) {
2987
+ container.appendChild(el);
2724
2988
  }
2725
- const file = new File([u8arr], filename, { type: mime });
2726
- return file;
2989
+ return el;
2727
2990
  },
2728
2991
  /**
2729
- * 从文件下载数据
2992
+ * 从父节点中移除指定元素。
2730
2993
  *
2731
- * @param data 要下载的数据,可以是字符串数组、BlobPart 或 MediaSource
2732
- * @param saveName 下载后文件的保存名称
2994
+ * @param el 要移除的元素对象,必须包含parentNode属性。
2733
2995
  */
2734
- downloadFromFile(data, saveName) {
2735
- if (typeof data == "object") {
2736
- if (data instanceof Blob) {
2737
- data = URL.createObjectURL(data);
2738
- } else {
2739
- const str = JSON.stringify(data);
2740
- const blob = new Blob([str], { type: "text/json" });
2741
- data = window.URL.createObjectURL(blob);
2742
- }
2743
- } else if (typeof data == "string" && data.indexOf("http") === -1) {
2744
- const blob = new Blob([data], { type: "text/json" });
2745
- data = window.URL.createObjectURL(blob);
2746
- }
2747
- var link = document.createElement("a");
2748
- link.href = data;
2749
- link.download = saveName || "";
2750
- link.click();
2751
- window.URL.revokeObjectURL(link.href);
2752
- }
2753
- };
2754
- class GlobalMsg {
2755
- static resetWarned() {
2756
- this.warned = {};
2757
- }
2758
- static changeVoice() {
2759
- this.isMute = !!Number(!this.isMute);
2760
- localStorage.setItem("mute", Number(this.isMute).toString());
2761
- }
2762
- static _call(method, message) {
2763
- if (!this.warned[message]) {
2764
- method(message);
2765
- if (method instanceof this.warning) {
2766
- this.msg("warning", message);
2767
- } else if (method instanceof this.info) {
2768
- this.msg("info", message);
2769
- } else if (method instanceof this.error) {
2770
- this.msg("error", message);
2771
- } else if (method instanceof this.success) {
2772
- this.msg("success", message);
2773
- }
2774
- this.warned[message] = true;
2996
+ remove(el) {
2997
+ const parent = el.parentNode;
2998
+ if (parent) {
2999
+ parent.removeChild(el);
2775
3000
  }
2776
- }
3001
+ },
2777
3002
  /**
2778
- * 播放消息提示音和文字朗读
3003
+ * 清空给定元素的子节点
2779
3004
  *
2780
- * @param type 消息类型
2781
- * @param message 消息内容
2782
- * @param options 配置选项,可选参数,包括语言、音量、语速和音高
2783
- * @returns 无返回值
3005
+ * @param el 要清空子节点的元素,包含firstChild和removeChild属性
2784
3006
  */
2785
- static msg(type, message, options = {}) {
2786
- Message({ type, message });
2787
- if (this.isMute) return;
2788
- const typename = CommUtils.decodeDict(type, "success", "恭喜:", "error", "发生错误:", "warning", "警告:", "info", "友情提示:") + ":";
2789
- this.speechSynthesisUtterance.text = typename + message;
2790
- this.speechSynthesisUtterance.lang = options.lang || "zh-CN";
2791
- this.speechSynthesisUtterance.volume = options.volume || 1;
2792
- this.speechSynthesisUtterance.rate = options.rate || 1;
2793
- this.speechSynthesisUtterance.pitch = options.pitch || 1;
2794
- this.speechSynthesis.speak(this.speechSynthesisUtterance);
2795
- }
2796
- static stop(e) {
2797
- this.speechSynthesisUtterance.text = e;
2798
- this.speechSynthesis.cancel();
2799
- }
2800
- static warning(message) {
2801
- if (process.env.NODE_ENV !== "development" && console !== void 0) {
2802
- console.warn(`Warning: ${message}`);
2803
- }
2804
- this.msg("warning", message);
2805
- }
2806
- static warningOnce(message) {
2807
- this._call(this.warning, message);
2808
- }
2809
- static info(message) {
2810
- if (process.env.NODE_ENV !== "development" && console !== void 0) {
2811
- console.info(`Info: ${message}`);
2812
- }
2813
- this.msg("info", message);
2814
- }
2815
- static infoOnce(message) {
2816
- this._call(this.info, message);
2817
- }
2818
- static error(message) {
2819
- if (process.env.NODE_ENV !== "development" && console !== void 0) {
2820
- console.error(`Error: ${message}`);
2821
- }
2822
- this.msg("error", message);
2823
- }
2824
- static errorOnce(message) {
2825
- this._call(this.error, message);
2826
- }
2827
- static success(message) {
2828
- if (process.env.NODE_ENV !== "development" && console !== void 0) {
2829
- console.log(`Success: ${message}`);
3007
+ empty(el) {
3008
+ while (el.firstChild) {
3009
+ el.removeChild(el.firstChild);
2830
3010
  }
2831
- this.msg("success", message);
2832
- }
2833
- static successOnce(message) {
2834
- this._call(this.success, message);
2835
- }
2836
- }
2837
- __publicField(GlobalMsg, "warned", {});
2838
- __publicField(GlobalMsg, "isMute", !!Number(localStorage.getItem("mute")) || false);
2839
- __publicField(GlobalMsg, "speechSynthesis", window.speechSynthesis);
2840
- __publicField(GlobalMsg, "speechSynthesisUtterance", new SpeechSynthesisUtterance());
2841
- const OptimizeUtil = {
3011
+ },
2842
3012
  /**
2843
- * 防抖函数,在指定的等待时间内,如果连续触发事件,则只在最后一次触发后执行函数。适用于像搜索输入框这种需要用户停止输入后才调用的场景
3013
+ * 将元素移到父节点的最前面
2844
3014
  *
2845
- * @param func 需要防抖的函数。
2846
- * @param wait 等待时间,单位毫秒。
2847
- * @param immediate 是否立即执行函数,默认为true。
2848
- * @returns 返回防抖后的函数。
3015
+ * @param el 要移动的元素,需要包含 parentNode 属性
2849
3016
  */
2850
- debounce(func, wait, immediate = true) {
2851
- let timeout = null;
2852
- let args;
2853
- let timestamp;
2854
- let result;
2855
- const later = () => {
2856
- const last = Date.now() - timestamp;
2857
- if (last < wait && last > 0) {
2858
- timeout = setTimeout(later, wait - last);
2859
- } else {
2860
- timeout = null;
2861
- if (!immediate) {
2862
- result = func.apply(this, args);
2863
- }
2864
- }
2865
- };
2866
- return (...args2) => {
2867
- timestamp = Date.now();
2868
- const callNow = immediate && !timeout;
2869
- if (!timeout) {
2870
- timeout = setTimeout(later, wait);
2871
- }
2872
- if (callNow) {
2873
- result = func.apply(this, args2);
2874
- if (!timeout) {
2875
- args2 = null;
2876
- }
2877
- }
2878
- return result;
2879
- };
3017
+ toFront(el) {
3018
+ const parent = el.parentNode;
3019
+ if (parent && parent.lastChild !== el) {
3020
+ parent.appendChild(el);
3021
+ }
2880
3022
  },
2881
3023
  /**
2882
- * 节流函数,适用于像滚动事件、窗口resize事件这种需要限制调用频率的场景
3024
+ * 将元素移动到其父节点的最前面
2883
3025
  *
2884
- * @param func 需要节流的函数
2885
- * @param wait 节流间隔,单位为毫秒
2886
- * @param type 节流类型,1表示时间戳方式,2表示定时器方式
2887
- * @returns 返回一个新的函数,该函数在节流控制下执行传入的函数
3026
+ * @param el 要移动的元素,需要包含parentNode属性
2888
3027
  */
2889
- throttle(func, wait, type = 1) {
2890
- let previous = 0;
2891
- let timeout = null;
2892
- return (...args) => {
2893
- if (type === 1) {
2894
- const now = Date.now();
2895
- if (now - previous >= wait) {
2896
- func.apply(this, args);
2897
- previous = now;
2898
- }
2899
- } else if (type === 2) {
2900
- if (!timeout) {
2901
- timeout = setTimeout(() => {
2902
- timeout = null;
2903
- func.apply(this, args);
2904
- }, wait);
2905
- }
2906
- }
2907
- };
3028
+ toBack(el) {
3029
+ const parent = el.parentNode;
3030
+ if (parent && parent.firstChild !== el) {
3031
+ parent.insertBefore(el, parent.firstChild);
3032
+ }
2908
3033
  },
2909
3034
  /**
2910
- * 缓存函数,将传入的函数fn的计算结果缓存,提高重复计算的效率
3035
+ * 获取元素的类名
2911
3036
  *
2912
- * @param fn 传入待缓存的函数
2913
- * @returns 返回缓存后的函数
2914
- */
2915
- memoize(fn) {
2916
- const cache = /* @__PURE__ */ new Map();
2917
- return (...args) => {
2918
- const argsString = JSON.stringify(args);
2919
- if (cache.has(argsString)) {
2920
- return cache.get(argsString);
2921
- } else {
2922
- const result = fn.apply(this, args);
2923
- cache.set(argsString, result);
2924
- return result;
2925
- }
2926
- };
3037
+ * @param el 包含对应元素和类名的对象
3038
+ * @param el.correspondingElement 对应的元素
3039
+ * @param el.className 类名对象
3040
+ * @param el.className.baseVal 类名字符串
3041
+ * @returns 返回元素的类名字符串
3042
+ */
3043
+ getClass(el) {
3044
+ const shadowElement = (el == null ? void 0 : el.host) || el;
3045
+ return shadowElement.className.toString();
2927
3046
  },
2928
3047
  /**
2929
- * 递归调用函数,以一定的频率和持续时间执行。
3048
+ * 判断元素是否包含指定类名
2930
3049
  *
2931
- * @param fun 要递归调用的函数。
2932
- * @param frequency 每次调用函数之间的时间间隔,单位为毫秒,默认为500毫秒。
2933
- * @param duration 函数递归调用的总时长,单位为毫秒,默认为5000毫秒。
3050
+ * @param el 元素对象,包含classList属性,classList属性包含contains方法
3051
+ * @param name 要判断的类名
3052
+ * @returns 返回一个布尔值,表示元素是否包含指定类名
2934
3053
  */
2935
- recurve(fun, frequency = 500, duration = 5e3) {
2936
- let timer = 0;
2937
- setTimeout(() => {
2938
- timer++;
2939
- if (timer < Math.floor(duration / frequency)) {
2940
- fun.call(this);
2941
- setTimeout(this.recurve.bind(this, fun, frequency, duration), frequency);
2942
- }
2943
- }, frequency);
3054
+ hasClass(el, name) {
3055
+ var _a2;
3056
+ if ((_a2 = el.classList) == null ? void 0 : _a2.contains(name)) {
3057
+ return true;
3058
+ }
3059
+ const className = this.getClass(el);
3060
+ return className.length > 0 && new RegExp(`(^|\\s)${name}(\\s|$)`).test(className);
2944
3061
  },
2945
3062
  /**
2946
- * 确保函数只被调用一次
3063
+ * 给指定的 HTML 元素添加类名
2947
3064
  *
2948
- * @param func 要被调用的函数
2949
- * @returns 返回一个新的函数,该函数在被首次调用时会执行传入的函数,之后再次调用将不再执行
3065
+ * @param el 要添加类名的 HTML 元素
3066
+ * @param name 要添加的类名,多个类名之间用空格分隔
2950
3067
  */
2951
- once(func) {
2952
- let called = false;
2953
- return function(...args) {
2954
- if (!called) {
2955
- called = true;
2956
- return func(...args);
3068
+ addClass(el, name) {
3069
+ if (el.classList !== void 0) {
3070
+ const classes = splitWords(name);
3071
+ for (let i = 0, len = classes.length; i < len; i++) {
3072
+ el.classList.add(classes[i]);
2957
3073
  }
2958
- };
2959
- }
2960
- };
2961
- const UrlUtil = {
3074
+ } else if (!this.hasClass(el, name)) {
3075
+ const className = this.getClass(el);
3076
+ this.setClass(el, (className ? className + " " : "") + name);
3077
+ }
3078
+ },
2962
3079
  /**
2963
- * 将json对象转换为查询字符串
3080
+ * 从元素中移除指定类名
2964
3081
  *
2965
- * @param json 待转换的json对象
2966
- * @returns 转换后的查询字符串
3082
+ * @param el 要移除类名的元素
3083
+ * @param name 要移除的类名,多个类名用空格分隔
2967
3084
  */
2968
- json2Query(json) {
2969
- var tempArr = [];
2970
- for (var i in json) {
2971
- if (json.hasOwnProperty(i)) {
2972
- var key = i;
2973
- var value = json[i];
2974
- tempArr.push(encodeURIComponent(key) + "=" + encodeURIComponent(value));
2975
- }
3085
+ removeClass(el, name) {
3086
+ if (el.classList !== void 0) {
3087
+ const classes = splitWords(name);
3088
+ classes.forEach((className) => el.classList.remove(className));
3089
+ } else {
3090
+ this.setClass(el, (" " + this.getClass(el) + " ").replace(" " + name + " ", " ").trim());
2976
3091
  }
2977
- var urlParamsStr = tempArr.join("&");
2978
- return urlParamsStr;
2979
3092
  },
2980
3093
  /**
2981
- * URL 中解析出查询参数和哈希参数,并以对象的形式返回
3094
+ * 设置元素的 CSS 类名
2982
3095
  *
2983
- * @param href 需要解析的 URL 链接,默认为当前窗口的 URL
2984
- * @param needDecode 是否需要解码参数值,默认为 true
2985
- * @returns 返回一个包含解析后参数的对象,其中键为参数名,值为参数值
3096
+ * @param el HTML SVG 元素
3097
+ * @param name 要设置的类名,多个类名之间用空格分隔
2986
3098
  */
2987
- query2Json(href = window.location.href, needDecode = true) {
2988
- const reg = /([^&=]+)=([\w\W]*?)(&|$|#)/g;
2989
- const { search, hash } = new URL(href);
2990
- const args = [search, hash];
2991
- let obj = {};
2992
- for (let i = 0; i < args.length; i++) {
2993
- const str = args[i];
2994
- if (str) {
2995
- const s = str.replace(/#|\//g, "");
2996
- const arr = s.split("?");
2997
- if (arr.length > 1) {
2998
- for (let j = 1; j < arr.length; j++) {
2999
- let res;
3000
- while (res = reg.exec(arr[j])) {
3001
- obj[res[1]] = needDecode ? decodeURIComponent(res[2]) : res[2];
3002
- }
3003
- }
3004
- }
3005
- }
3006
- }
3007
- return obj;
3008
- }
3009
- };
3010
- class Color {
3011
- constructor(r, g, b, a) {
3012
- __publicField(this, "_r");
3013
- __publicField(this, "_g");
3014
- __publicField(this, "_b");
3015
- __publicField(this, "_alpha");
3016
- this._validateColorChannel(r);
3017
- this._validateColorChannel(g);
3018
- this._validateColorChannel(b);
3019
- this._r = r;
3020
- this._g = g;
3021
- this._b = b;
3022
- this._alpha = MathUtils.clamp(a || 1, 0, 1);
3023
- }
3024
- _validateColorChannel(channel) {
3025
- if (channel < 0 || channel > 255) {
3026
- throw new Error("Color channel must be between 0 and 255.");
3099
+ setClass(el, name) {
3100
+ if ("classList" in el) {
3101
+ el.classList.value = "";
3102
+ name.split(" ").forEach((className) => el.classList.add(className));
3027
3103
  }
3028
- }
3029
- toString() {
3030
- return `rgba(${this._r}, ${this._g}, ${this._b}, ${this._alpha})`;
3031
- }
3032
- toJson() {
3033
- return { r: this._r, g: this._g, b: this._b, a: this._alpha };
3034
- }
3035
- get rgba() {
3036
- return `rgba(${this._r}, ${this._g}, ${this._b}, ${this._alpha})`;
3037
- }
3038
- get hex() {
3039
- return Color.rgb2hex(this._r, this._g, this._b, this._alpha);
3040
- }
3041
- setAlpha(a) {
3042
- this._alpha = MathUtils.clamp(a, 0, 1);
3043
- return this;
3044
- }
3045
- // 设置颜色的RGB值
3046
- setRgb(r, g, b) {
3047
- this._validateColorChannel(r);
3048
- this._validateColorChannel(g);
3049
- this._validateColorChannel(b);
3050
- this._r = r;
3051
- this._g = g;
3052
- this._b = b;
3053
- return this;
3054
- }
3104
+ },
3055
3105
  /**
3056
- * 从RGBA字符串创建Color对象
3106
+ * 从字符串中解析XML文档,并返回根节点
3057
3107
  *
3058
- * @param rgbaValue RGBA颜色值字符串,格式为"rgba(r,g,b,a)"或"rgb(r,g,b)"
3059
- * @returns 返回Color对象
3060
- * @throws 如果rgbaValue不是有效的RGBA颜色值,则抛出错误
3108
+ * @param str 要解析的XML字符串
3109
+ * @returns 解析后的XML文档的根节点
3061
3110
  */
3062
- static fromRgba(rgbaValue) {
3063
- const rgbaMatch = rgbaValue.match(/^rgba?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([\d.]+))?\s*\)$/);
3064
- if (!rgbaMatch) throw new Error("Invalid RGBA color value");
3065
- const r = parseInt(rgbaMatch[1], 10);
3066
- const g = parseInt(rgbaMatch[2], 10);
3067
- const b = parseInt(rgbaMatch[3], 10);
3068
- const a = rgbaMatch[5] ? parseFloat(rgbaMatch[5]) : 1;
3069
- return new Color(r, g, b, a);
3111
+ parseFromString(str) {
3112
+ const parser = new DOMParser();
3113
+ const doc = parser.parseFromString(str, "text/xml");
3114
+ return doc.children[0];
3070
3115
  }
3116
+ };
3117
+ const FileUtil = {
3071
3118
  /**
3072
- * 将十六进制颜色值转换为颜色对象
3119
+ * 将Base64编码的字符串转换为Blob对象
3073
3120
  *
3074
- * @param hexValue 十六进制颜色值,可带或不带#前缀,支持3位和6位表示
3075
- * @returns 返回颜色对象
3121
+ * @param data Base64编码的字符串
3122
+ * @returns 转换后的Blob对象
3076
3123
  */
3077
- static fromHex(hexValue, a = 1) {
3078
- const rgxShort = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
3079
- const hex = hexValue.replace(rgxShort, (m, r2, g2, b2) => r2 + r2 + g2 + g2 + b2 + b2);
3080
- const rgx = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i;
3081
- const rgb = rgx.exec(hex);
3082
- if (!rgb) {
3083
- throw new Error("Invalid HEX color value");
3124
+ convertBase64ToBlob(data) {
3125
+ const mimeString = data.split(",")[0].split(":")[1].split(";")[0];
3126
+ const byteCharacters = atob(data.split(",")[1]);
3127
+ const byteNumbers = new Array(byteCharacters.length);
3128
+ for (let i = 0; i < byteCharacters.length; i++) {
3129
+ byteNumbers[i] = byteCharacters.charCodeAt(i);
3084
3130
  }
3085
- const r = parseInt(rgb[1], 16);
3086
- const g = parseInt(rgb[2], 16);
3087
- const b = parseInt(rgb[3], 16);
3088
- return new Color(r, g, b, a);
3089
- }
3131
+ const byteArray = new Uint8Array(byteNumbers);
3132
+ const blob = new Blob([byteArray], { type: mimeString });
3133
+ return blob;
3134
+ },
3090
3135
  /**
3091
- * 从 HSL 字符串创建颜色对象
3136
+ * 将图片的URL转换为Base64编码
3092
3137
  *
3093
- * @param hsl HSL 字符串,格式为 hsl(h, s%, l%) 或 hsla(h, s%, l%, a)
3094
- * @returns 返回颜色对象,如果 hsl 字符串无效则返回 null
3138
+ * @param url 图片的URL地址
3139
+ * @param width 图片的宽度,默认为图片原始宽度
3140
+ * @param height 图片的高度,默认为图片原始高度
3141
+ * @returns 返回Promise对象,解析后得到包含Base64编码数据的对象
3095
3142
  */
3096
- static fromHsl(hslValue) {
3097
- const hsl = /hsl\((\d+),\s*([\d.]+)%,\s*([\d.]+)%\)/g.exec(hslValue) || /hsla\((\d+),\s*([\d.]+)%,\s*([\d.]+)%,\s*([\d.]+)\)/g.exec(hslValue);
3098
- if (!hsl) {
3099
- throw new Error("Invalid HSL color value");
3100
- }
3101
- const h = parseInt(hsl[1], 10) / 360;
3102
- const s = parseInt(hsl[2], 10) / 100;
3103
- const l = parseInt(hsl[3], 10) / 100;
3104
- const a = hsl[4] ? parseFloat(hsl[4]) : 1;
3105
- function hue2rgb(p, q, t) {
3106
- if (t < 0) t += 1;
3107
- if (t > 1) t -= 1;
3108
- if (t < 1 / 6) return p + (q - p) * 6 * t;
3109
- if (t < 1 / 2) return q;
3110
- if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
3111
- return p;
3112
- }
3113
- let r, g, b;
3114
- if (s === 0) {
3115
- r = g = b = l;
3116
- } else {
3117
- const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
3118
- const p = 2 * l - q;
3119
- r = hue2rgb(p, q, h + 1 / 3);
3120
- g = hue2rgb(p, q, h);
3121
- b = hue2rgb(p, q, h - 1 / 3);
3122
- }
3123
- return new Color(Math.round(r * 255), Math.round(g * 255), Math.round(b * 255), a);
3124
- }
3125
3143
  /**
3126
- * 从字符串中创建颜色对象
3144
+ * 将base64字符串转换为文件对象
3127
3145
  *
3128
- * @param str 字符串类型的颜色值,支持rgba、hex、hsl格式
3129
- * @returns 返回创建的颜色对象
3130
- * @throws 当颜色值无效时,抛出错误
3131
- */
3132
- static from(str) {
3133
- if (this.isRgb(str)) {
3134
- return this.fromRgba(str);
3135
- } else if (this.isHex(str)) {
3136
- return this.fromHex(str);
3137
- } else if (this.isHsl(str)) {
3138
- return this.fromHsl(str);
3139
- } else {
3140
- throw new Error("Invalid color value");
3146
+ * @param dataurl 包含base64字符串的数据URL
3147
+ * @param filename 文件的名称
3148
+ * @returns 返回文件对象
3149
+ */
3150
+ convertBase64ToFile(dataurl, filename) {
3151
+ const arr = dataurl.split(",");
3152
+ const mimeMatch = arr[0].match(/:(.*?);/);
3153
+ const mime = mimeMatch ? mimeMatch[1] : "image/png";
3154
+ const bstr = atob(arr[1]);
3155
+ const u8arr = new Uint8Array(bstr.length);
3156
+ for (let i = 0; i < bstr.length; i++) {
3157
+ u8arr[i] = bstr.charCodeAt(i);
3141
3158
  }
3142
- }
3159
+ const file = new File([u8arr], filename, { type: mime });
3160
+ return file;
3161
+ },
3143
3162
  /**
3144
- * 将RGB颜色值转换为十六进制颜色值
3163
+ * 从文件下载数据
3145
3164
  *
3146
- * @param r 红色分量值,取值范围0-255
3147
- * @param g 绿色分量值,取值范围0-255
3148
- * @param b 蓝色分量值,取值范围0-255
3149
- * @param a 可选参数,透明度分量值,取值范围0-1
3150
- * @returns 十六进制颜色值,格式为#RRGGBB或#RRGGBBAA
3165
+ * @param data 要下载的数据,可以是字符串数组、BlobPart 或 MediaSource
3166
+ * @param saveName 下载后文件的保存名称
3151
3167
  */
3152
- static rgb2hex(r, g, b, a) {
3153
- var hex = "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
3154
- if (a !== void 0) {
3155
- const alpha = Math.round(a * 255).toString(16).padStart(2, "0");
3156
- return hex + alpha;
3168
+ downloadFromFile(data, saveName) {
3169
+ if (typeof data == "object") {
3170
+ if (data instanceof Blob) {
3171
+ data = URL.createObjectURL(data);
3172
+ } else {
3173
+ const str = JSON.stringify(data);
3174
+ const blob = new Blob([str], { type: "text/json" });
3175
+ data = window.URL.createObjectURL(blob);
3176
+ }
3177
+ } else if (typeof data == "string" && data.indexOf("http") === -1) {
3178
+ const blob = new Blob([data], { type: "text/json" });
3179
+ data = window.URL.createObjectURL(blob);
3157
3180
  }
3158
- return hex;
3159
- }
3160
- static isHex(a) {
3161
- return /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(a);
3162
- }
3163
- static isRgb(a) {
3164
- return /^rgba?\s*\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*(,\s*[\d.]+)?\s*\)$/.test(a);
3165
- }
3166
- static isHsl(a) {
3167
- return /^(hsl|hsla)\(\d+,\s*[\d.]+%,\s*[\d.]+%(,\s*[\d.]+)?\)$/.test(a);
3181
+ var link = document.createElement("a");
3182
+ link.href = data;
3183
+ link.download = saveName || "";
3184
+ link.click();
3185
+ window.URL.revokeObjectURL(link.href);
3168
3186
  }
3169
- static isColor(a) {
3170
- return this.isHex(a) || this.isRgb(a) || this.isHsl(a);
3187
+ };
3188
+ class MessageUtil {
3189
+ static resetWarned() {
3190
+ this.warned = {};
3171
3191
  }
3172
- static random() {
3173
- let r = Math.floor(Math.random() * 256);
3174
- let g = Math.floor(Math.random() * 256);
3175
- let b = Math.floor(Math.random() * 256);
3176
- let a = Math.random();
3177
- return new Color(r, g, b, a);
3192
+ static changeVoice() {
3193
+ this.isMute = !!Number(!this.isMute);
3194
+ localStorage.setItem("mute", Number(this.isMute).toString());
3178
3195
  }
3179
- }
3180
- class CanvasDrawer {
3181
- constructor(el) {
3182
- __publicField(this, "context", null);
3183
- if (typeof el === "string") {
3184
- el = document.querySelector("#" + el);
3185
- if (!el) {
3186
- throw new Error("Element not found");
3187
- }
3188
- }
3189
- if (el instanceof HTMLElement) {
3190
- const canvas = el;
3191
- if (canvas.getContext) {
3192
- this.context = canvas.getContext("2d");
3193
- } else {
3194
- throw new Error("getContext is not available on this element");
3195
- }
3196
- } else {
3197
- throw new Error("Element is not an HTMLElement");
3196
+ static _call(method, message) {
3197
+ if (!this.warned[message]) {
3198
+ method(message);
3199
+ this.warned[message] = true;
3198
3200
  }
3199
3201
  }
3200
3202
  /**
3201
- * 绘制线条
3203
+ * 播放消息提示音和文字朗读
3202
3204
  *
3203
- * @param start 起始坐标点
3204
- * @param end 终止坐标点
3205
- * @param options 绘制选项,包括线条宽度和颜色
3206
- * @throws 当画布上下文不存在时抛出错误
3205
+ * @param type 消息类型
3206
+ * @param message 消息内容
3207
+ * @param options 配置选项,可选参数,包括语言、音量、语速和音高
3208
+ * @returns 无返回值
3207
3209
  */
3208
- drawLine({ x: startX, y: startY }, { x: endX, y: endY }, options = {}) {
3209
- if (!this.context) {
3210
- throw new Error("Canvas context is null or undefined");
3211
- }
3212
- this.context.beginPath();
3213
- const width = options.width || 1;
3214
- const color = options.color || "#000";
3215
- this.context.lineWidth = width;
3216
- this.context.strokeStyle = color;
3217
- this.context.moveTo(startX, startY);
3218
- this.context.lineTo(endX, endY);
3219
- this.context.stroke();
3210
+ static msg(type, message, options = {}) {
3211
+ Message({ type, message });
3212
+ if (this.isMute) return;
3213
+ const typename = Util.decodeDict(type, "success", "恭喜:", "error", "发生错误:", "warning", "警告:", "info", "友情提示:") + ":";
3214
+ this.speechSynthesisUtterance.text = typename + message;
3215
+ this.speechSynthesisUtterance.lang = options.lang || "zh-CN";
3216
+ this.speechSynthesisUtterance.volume = options.volume || 1;
3217
+ this.speechSynthesisUtterance.rate = options.rate || 1;
3218
+ this.speechSynthesisUtterance.pitch = options.pitch || 1;
3219
+ this.speechSynthesis.speak(this.speechSynthesisUtterance);
3220
3220
  }
3221
- /**
3222
- * 绘制圆弧
3223
- *
3224
- * @param x 圆心x坐标
3225
- * @param y 圆心y坐标
3226
- * @param radius 半径
3227
- * @param startAngle 起始角度(度)
3228
- * @param endAngle 结束角度(度)
3229
- * @param anticlockwise 是否逆时针绘制
3230
- * @param isFill 是否填充
3231
- * @param bgColor 背景颜色
3232
- * @throws 当Canvas context为null或undefined时抛出错误
3233
- */
3234
- drawArc({ x, y }, radius, startAngle, endAngle, anticlockwise, isFill, bgColor) {
3235
- if (!this.context) {
3236
- throw new Error("Canvas context is null or undefined");
3237
- }
3238
- if (isFill) {
3239
- this.context.fillStyle = bgColor;
3240
- this.context.beginPath();
3241
- this.context.arc(x, y, radius, MathUtils.deg2Rad(startAngle), MathUtils.deg2Rad(endAngle), anticlockwise);
3242
- this.context.fill();
3243
- } else {
3244
- this.context.strokeStyle = bgColor;
3245
- this.context.beginPath();
3246
- this.context.arc(x, y, radius, MathUtils.deg2Rad(startAngle), MathUtils.deg2Rad(endAngle), anticlockwise);
3247
- this.context.stroke();
3248
- }
3221
+ static stop(e) {
3222
+ this.speechSynthesisUtterance.text = e;
3223
+ this.speechSynthesis.cancel();
3249
3224
  }
3250
- static createCanvas(width = 1, height = 1) {
3251
- const canvas = document.createElement("canvas");
3252
- if (width) {
3253
- canvas.width = width;
3254
- }
3255
- if (height) {
3256
- canvas.height = height;
3225
+ static warning(message) {
3226
+ if (process.env.NODE_ENV === "development" && console !== void 0) {
3227
+ console.warn(`Warning: ${message}`);
3257
3228
  }
3258
- return canvas;
3229
+ this.msg("warning", message);
3259
3230
  }
3260
- }
3261
- class EventDispatcher {
3262
- constructor() {
3263
- __publicField(this, "_listeners");
3264
- __publicField(this, "_mutex", {});
3265
- __publicField(this, "_context");
3231
+ static warningOnce(message) {
3232
+ this._call(this.warning.bind(this), message);
3266
3233
  }
3267
- addEventListener(type, listener, context, mutexStatus) {
3268
- if (this._listeners === void 0) this._listeners = {};
3269
- this._context = context;
3270
- const mutex = this._mutex;
3271
- const listeners = this._listeners;
3272
- if (listeners[type] === void 0) {
3273
- listeners[type] = [];
3274
- }
3275
- if (listeners[type].indexOf(listener) === -1) {
3276
- if (mutexStatus) {
3277
- mutex[type] = listener;
3278
- }
3279
- listeners[type].push(listener);
3234
+ static info(message) {
3235
+ if (process.env.NODE_ENV === "development" && console !== void 0) {
3236
+ console.info(`Info: ${message}`);
3280
3237
  }
3281
- return this;
3238
+ this.msg("info", message);
3282
3239
  }
3283
- hasEventListener(type, listener) {
3284
- if (this._listeners === null || this._listeners === void 0) return false;
3285
- const listeners = this._listeners;
3286
- return listeners[type] !== void 0 && listeners[type].indexOf(listener) !== -1;
3240
+ static infoOnce(message) {
3241
+ this._call(this.info.bind(this), message);
3287
3242
  }
3288
- removeEventListener(type, listener) {
3289
- if (this._listeners === void 0) return;
3290
- const listeners = this._listeners;
3291
- const listenerArray = listeners[type];
3292
- if (this._mutex[type] === listener) {
3293
- this._mutex[type] = null;
3243
+ static error(message) {
3244
+ if (process.env.NODE_ENV === "development" && console !== void 0) {
3245
+ console.error(`Error: ${message}`);
3294
3246
  }
3295
- if (listenerArray !== void 0) {
3296
- const index = listenerArray.map((d) => d.toString()).indexOf(listener.toString());
3297
- if (index !== -1) {
3298
- listenerArray.splice(index, 1);
3299
- }
3247
+ this.msg("error", message);
3248
+ }
3249
+ static errorOnce(message) {
3250
+ this._call(this.error.bind(this), message);
3251
+ }
3252
+ static success(message) {
3253
+ if (process.env.NODE_ENV === "development" && console !== void 0) {
3254
+ console.log(`Success: ${message}`);
3300
3255
  }
3256
+ this.msg("success", message);
3301
3257
  }
3302
- dispatchEvent(event) {
3303
- if (this._listeners === void 0) return;
3304
- const listeners = this._listeners;
3305
- const listenerArray = listeners[event.type];
3306
- if (listenerArray !== void 0) {
3307
- event.target = this;
3308
- const array = listenerArray.slice(0);
3309
- if (this._mutex[event.type] !== void 0) {
3310
- const find = array.find((item) => item === this._mutex[event.type]);
3311
- if (find) {
3312
- find.call(this._context || this, event);
3313
- return;
3258
+ static successOnce(message) {
3259
+ this._call(this.success.bind(this), message);
3260
+ }
3261
+ }
3262
+ __publicField(MessageUtil, "warned", {});
3263
+ __publicField(MessageUtil, "isMute", !!Number(localStorage.getItem("mute")) || false);
3264
+ __publicField(MessageUtil, "speechSynthesis", window.speechSynthesis);
3265
+ __publicField(MessageUtil, "speechSynthesisUtterance", new SpeechSynthesisUtterance());
3266
+ const OptimizeUtil = {
3267
+ /**
3268
+ * 防抖函数,在指定的等待时间内,如果连续触发事件,则只在最后一次触发后执行函数。适用于像搜索输入框这种需要用户停止输入后才调用的场景
3269
+ *
3270
+ * @param func 需要防抖的函数。
3271
+ * @param wait 等待时间,单位毫秒。
3272
+ * @param immediate 是否立即执行函数,默认为true。
3273
+ * @returns 返回防抖后的函数。
3274
+ */
3275
+ debounce(func, wait, immediate = true) {
3276
+ let timeout = null;
3277
+ let args;
3278
+ let timestamp;
3279
+ let result;
3280
+ const later = () => {
3281
+ const last = Date.now() - timestamp;
3282
+ if (last < wait && last > 0) {
3283
+ timeout = setTimeout(later, wait - last);
3284
+ } else {
3285
+ timeout = null;
3286
+ if (!immediate) {
3287
+ result = func.apply(this, args);
3288
+ }
3289
+ }
3290
+ };
3291
+ return (...args2) => {
3292
+ timestamp = Date.now();
3293
+ const callNow = immediate && !timeout;
3294
+ if (!timeout) {
3295
+ timeout = setTimeout(later, wait);
3296
+ }
3297
+ if (callNow) {
3298
+ result = func.apply(this, args2);
3299
+ if (!timeout) {
3300
+ args2 = null;
3314
3301
  }
3315
3302
  }
3316
- for (let i = 0, l = array.length; i < l; i++) {
3317
- const item = array[i];
3318
- if (typeof item === "function") {
3319
- item.call(this._context || this, event);
3303
+ return result;
3304
+ };
3305
+ },
3306
+ /**
3307
+ * 节流函数,适用于像滚动事件、窗口resize事件这种需要限制调用频率的场景
3308
+ *
3309
+ * @param func 需要节流的函数
3310
+ * @param wait 节流间隔,单位为毫秒
3311
+ * @param type 节流类型,1表示时间戳方式,2表示定时器方式
3312
+ * @returns 返回一个新的函数,该函数在节流控制下执行传入的函数
3313
+ */
3314
+ throttle(func, wait, type = 1) {
3315
+ let previous = 0;
3316
+ let timeout = null;
3317
+ return (...args) => {
3318
+ if (type === 1) {
3319
+ const now = Date.now();
3320
+ if (now - previous >= wait) {
3321
+ func.apply(this, args);
3322
+ previous = now;
3323
+ }
3324
+ } else if (type === 2) {
3325
+ if (!timeout) {
3326
+ timeout = setTimeout(() => {
3327
+ timeout = null;
3328
+ func.apply(this, args);
3329
+ }, wait);
3320
3330
  }
3321
3331
  }
3322
- }
3323
- }
3324
- removeAllListener() {
3325
- this._mutex = {};
3326
- for (const key in this._listeners) {
3327
- this._listeners[key] = [];
3328
- }
3329
- }
3330
- }
3331
- class HashMap extends Map {
3332
- isEmpty() {
3333
- return this.size === 0;
3334
- }
3335
- _values() {
3336
- return Array.from(this.values());
3337
- }
3338
- _keys() {
3339
- return Array.from(this.keys());
3340
- }
3341
- _entries() {
3342
- return Array.from(this.entries());
3343
- }
3332
+ };
3333
+ },
3344
3334
  /**
3345
- * 从键值对数组创建一个HashMap对象
3335
+ * 缓存函数,将传入的函数fn的计算结果缓存,提高重复计算的效率
3346
3336
  *
3347
- * @param array 键值对数组,默认为空数组
3348
- * @returns 返回一个新的HashMap对象
3337
+ * @param fn 传入待缓存的函数
3338
+ * @returns 返回缓存后的函数
3349
3339
  */
3350
- static fromEntries(array = []) {
3351
- const hashMap = new HashMap();
3352
- array.forEach((element) => {
3353
- if (Array.isArray(element) && element.length === 2) {
3354
- hashMap.set(element[0], element[1]);
3340
+ memoize(fn) {
3341
+ const cache = /* @__PURE__ */ new Map();
3342
+ return (...args) => {
3343
+ const argsString = JSON.stringify(args);
3344
+ if (cache.has(argsString)) {
3345
+ return cache.get(argsString);
3346
+ } else {
3347
+ const result = fn.apply(this, args);
3348
+ cache.set(argsString, result);
3349
+ return result;
3355
3350
  }
3356
- });
3357
- return hashMap;
3358
- }
3351
+ };
3352
+ },
3359
3353
  /**
3360
- * 从JSON字符串创建HashMap实例
3354
+ * 递归调用函数,以一定的频率和持续时间执行。
3361
3355
  *
3362
- * @param str JSON字符串
3363
- * @returns HashMap实例
3356
+ * @param fun 要递归调用的函数。
3357
+ * @param frequency 每次调用函数之间的时间间隔,单位为毫秒,默认为500毫秒。
3358
+ * @param duration 函数递归调用的总时长,单位为毫秒,默认为5000毫秒。
3364
3359
  */
3365
- static fromJson(str) {
3366
- const json = ObjectUtil.parse(str);
3367
- return new HashMap(Object.entries(json));
3368
- }
3369
- }
3370
- class WebSocketClient extends EventDispatcher {
3371
- constructor(url = "ws://127.0.0.1:10088") {
3372
- super();
3373
- __publicField(this, "maxCheckTimes", 10);
3374
- __publicField(this, "url");
3375
- __publicField(this, "checkTimes", 0);
3376
- __publicField(this, "connectStatus", false);
3377
- __publicField(this, "client", null);
3378
- this.maxCheckTimes = 10;
3379
- this.url = url;
3380
- this.checkTimes = 0;
3381
- this.connect();
3382
- this.connCheckStatus(this.maxCheckTimes);
3383
- }
3384
- connect() {
3385
- this.disconnect();
3386
- if (this.url) {
3387
- try {
3388
- console.info("创建ws连接>>>" + this.url);
3389
- this.client = new WebSocket(this.url);
3390
- if (this.client) {
3391
- const self = this;
3392
- this.client.onopen = function(message) {
3393
- self.dispatchEvent({
3394
- type: EventType.WEB_SOCKET_CONNECT,
3395
- message
3396
- });
3397
- };
3398
- this.client.onmessage = function(message) {
3399
- self.connectStatus = true;
3400
- self.dispatchEvent({
3401
- type: EventType.WEB_SOCKET_MESSAGE,
3402
- message
3403
- });
3404
- };
3405
- this.client.onclose = function(message) {
3406
- self.dispatchEvent({
3407
- type: EventType.WEB_SOCKET_CLOSE,
3408
- message
3409
- });
3410
- };
3411
- if (this.checkTimes === this.maxCheckTimes) {
3412
- this.client.onerror = function(message) {
3413
- self.dispatchEvent({
3414
- type: EventType.WEB_SOCKET_ERROR,
3415
- message
3416
- });
3417
- };
3418
- }
3419
- }
3420
- } catch (ex) {
3421
- console.error("创建ws连接失败" + this.url + ":" + ex);
3360
+ recurve(fun, frequency = 500, duration = 5e3) {
3361
+ let timer = 0;
3362
+ setTimeout(() => {
3363
+ timer++;
3364
+ if (timer < Math.floor(duration / frequency)) {
3365
+ fun.call(this);
3366
+ setTimeout(this.recurve.bind(this, fun, frequency, duration), frequency);
3422
3367
  }
3423
- }
3424
- }
3425
- disconnect() {
3426
- if (this.client) {
3427
- try {
3428
- console.log("ws断开连接" + this.url);
3429
- this.client.close();
3430
- this.client = null;
3431
- } catch (ex) {
3432
- this.client = null;
3368
+ }, frequency);
3369
+ },
3370
+ /**
3371
+ * 确保函数只被调用一次
3372
+ *
3373
+ * @param func 要被调用的函数
3374
+ * @returns 返回一个新的函数,该函数在被首次调用时会执行传入的函数,之后再次调用将不再执行
3375
+ */
3376
+ once(func) {
3377
+ let called = false;
3378
+ return function(...args) {
3379
+ if (!called) {
3380
+ called = true;
3381
+ return func(...args);
3433
3382
  }
3434
- }
3383
+ };
3435
3384
  }
3436
- connCheckStatus(times) {
3437
- if (this.checkTimes > times) return;
3438
- setTimeout(() => {
3439
- this.checkTimes++;
3440
- if (this.client && this.client.readyState !== 0 && this.client.readyState !== 1) {
3441
- this.connect();
3385
+ };
3386
+ const UrlUtil = {
3387
+ /**
3388
+ * 将json对象转换为查询字符串
3389
+ *
3390
+ * @param json 待转换的json对象
3391
+ * @returns 转换后的查询字符串
3392
+ */
3393
+ json2Query(json) {
3394
+ var tempArr = [];
3395
+ for (var i in json) {
3396
+ if (json.hasOwnProperty(i)) {
3397
+ var key = i;
3398
+ var value = json[i];
3399
+ tempArr.push(encodeURIComponent(key) + "=" + encodeURIComponent(value));
3442
3400
  }
3443
- this.connCheckStatus(times);
3444
- }, 2e3);
3445
- }
3446
- send(message) {
3447
- if (this.client && this.client.readyState === 1) {
3448
- this.client.send(message);
3449
- return true;
3450
3401
  }
3451
- console.error(this.url + "消息发送失败:" + message);
3452
- return this;
3453
- }
3454
- heartbeat() {
3455
- setTimeout(() => {
3456
- if (this.client && this.client.readyState === 1) {
3457
- this.send("HeartBeat");
3402
+ var urlParamsStr = tempArr.join("&");
3403
+ return urlParamsStr;
3404
+ },
3405
+ /**
3406
+ * URL 中解析出查询参数和哈希参数,并以对象的形式返回
3407
+ *
3408
+ * @param href 需要解析的 URL 链接,默认为当前窗口的 URL
3409
+ * @param needDecode 是否需要解码参数值,默认为 true
3410
+ * @returns 返回一个包含解析后参数的对象,其中键为参数名,值为参数值
3411
+ */
3412
+ query2Json(href = window.location.href, needDecode = true) {
3413
+ const reg = /([^&=]+)=([\w\W]*?)(&|$|#)/g;
3414
+ const { search, hash } = new URL(href);
3415
+ const args = [search, hash];
3416
+ let obj = {};
3417
+ for (let i = 0; i < args.length; i++) {
3418
+ const str = args[i];
3419
+ if (str) {
3420
+ const s = str.replace(/#|\//g, "");
3421
+ const arr = s.split("?");
3422
+ if (arr.length > 1) {
3423
+ for (let j = 1; j < arr.length; j++) {
3424
+ let res;
3425
+ while (res = reg.exec(arr[j])) {
3426
+ obj[res[1]] = needDecode ? decodeURIComponent(res[2]) : res[2];
3427
+ }
3428
+ }
3429
+ }
3458
3430
  }
3459
- console.log("HeartBeat," + this.url);
3460
- setTimeout(this.heartbeat, 3e4);
3461
- }, 1e3);
3431
+ }
3432
+ return obj;
3462
3433
  }
3463
- }
3434
+ };
3464
3435
  const _MqttClient = class _MqttClient extends EventDispatcher {
3465
3436
  constructor(url = `ws://${window.document.domain}:20007/mqtt`, config = {}) {
3466
3437
  super();
@@ -3470,10 +3441,10 @@ const _MqttClient = class _MqttClient extends EventDispatcher {
3470
3441
  __publicField(this, "options");
3471
3442
  __publicField(this, "client");
3472
3443
  __publicField(this, "topics");
3473
- this.context = CommUtils.extend(_MqttClient.defaultContext, config);
3444
+ this.context = Util.extend(_MqttClient.defaultContext, config);
3474
3445
  this.options = {
3475
3446
  connectTimeout: this.context.MQTT_TIMEOUTM,
3476
- clientId: CommUtils.guid(),
3447
+ clientId: Util.guid(),
3477
3448
  username: this.context.MQTT_USERNAME,
3478
3449
  password: this.context.MQTT_PASSWORD,
3479
3450
  clean: true
@@ -3666,7 +3637,7 @@ const _Storage = class _Storage {
3666
3637
  }
3667
3638
  }
3668
3639
  };
3669
- __publicField(_Storage, "store", window.localStorage);
3640
+ __publicField(_Storage, "store", window.sessionStorage);
3670
3641
  __publicField(_Storage, "prefix", "");
3671
3642
  __publicField(_Storage, "_getPrefixedKey", function(key, options) {
3672
3643
  options = options || {};
@@ -3700,9 +3671,9 @@ export {
3700
3671
  ImageUtil,
3701
3672
  LayerType,
3702
3673
  LineSymbol,
3703
- MathUtils as MathUtil,
3674
+ MathUtil,
3704
3675
  MeasureMode,
3705
- GlobalMsg as MessageUtil,
3676
+ MessageUtil,
3706
3677
  MqttClient,
3707
3678
  ObjectState,
3708
3679
  ObjectUtil,
@@ -3710,6 +3681,6 @@ export {
3710
3681
  Storage,
3711
3682
  StringUtil,
3712
3683
  UrlUtil,
3713
- CommUtils as Util,
3684
+ Util,
3714
3685
  WebSocketClient
3715
3686
  };