gis-common 4.2.18 → 4.2.20

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,141 +150,528 @@ 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
+ var ColorName = /* @__PURE__ */ ((ColorName2) => {
196
+ ColorName2["white"] = "rgba(255, 255, 255, 1)";
197
+ ColorName2["black"] = "rgba(0, 0, 0, 1)";
198
+ ColorName2["red"] = "rgba(255, 0, 0, 1)";
199
+ ColorName2["green"] = "rgba(0, 255, 0, 1)";
200
+ ColorName2["blue"] = "rgba(0, 0, 255, 1)";
201
+ ColorName2["yellow"] = "rgba(255, 255, 0, 1)";
202
+ ColorName2["cyan"] = "rgba(0, 255, 255, 1)";
203
+ ColorName2["magenta"] = "rgba(255, 0, 255, 1)";
204
+ ColorName2["purple"] = "rgba(128, 0, 128, 1)";
205
+ ColorName2["brown"] = "rgba(165, 42, 42, 1)";
206
+ ColorName2["gray"] = "rgba(128, 128, 128, 1)";
207
+ ColorName2["orange"] = "rgba(255, 165, 0, 1)";
208
+ ColorName2["golden"] = "rgba(255, 215, 0, 1)";
209
+ ColorName2["silvery"] = "rgba(192, 192, 192, 1)";
210
+ ColorName2["transparent"] = "rgba(0, 0, 0, 0)";
211
+ return ColorName2;
212
+ })(ColorName || {});
213
+ class Color {
214
+ constructor(r, g, b, a) {
215
+ __publicField(this, "_r");
216
+ __publicField(this, "_g");
217
+ __publicField(this, "_b");
218
+ __publicField(this, "_alpha");
219
+ this._validateColorChannel(r);
220
+ this._validateColorChannel(g);
221
+ this._validateColorChannel(b);
222
+ this._r = r;
223
+ this._g = g;
224
+ this._b = b;
225
+ this._alpha = MathUtil.clamp(a || 1, 0, 1);
226
+ }
227
+ _validateColorChannel(channel) {
228
+ if (channel < 0 || channel > 255) {
229
+ throw new Error("Color channel must be between 0 and 255.");
210
230
  }
211
- },
231
+ }
232
+ toString() {
233
+ return `rgba(${this._r}, ${this._g}, ${this._b}, ${this._alpha})`;
234
+ }
235
+ toJson() {
236
+ return { r: this._r, g: this._g, b: this._b, a: this._alpha };
237
+ }
238
+ get rgba() {
239
+ return `rgba(${this._r}, ${this._g}, ${this._b}, ${this._alpha})`;
240
+ }
241
+ get hex() {
242
+ return Color.rgb2hex(this._r, this._g, this._b, this._alpha);
243
+ }
244
+ setAlpha(a) {
245
+ this._alpha = MathUtil.clamp(a, 0, 1);
246
+ return this;
247
+ }
248
+ // 设置颜色的RGB值
249
+ setRgb(r, g, b) {
250
+ this._validateColorChannel(r);
251
+ this._validateColorChannel(g);
252
+ this._validateColorChannel(b);
253
+ this._r = r;
254
+ this._g = g;
255
+ this._b = b;
256
+ return this;
257
+ }
212
258
  /**
213
- * 将JSON对象转换为FormData对象
259
+ * 从RGBA字符串创建Color对象
214
260
  *
215
- * @param json 待转换的JSON对象,其属性值为字符串或Blob类型
216
- * @returns 转换后的FormData对象
261
+ * @param rgbaValue RGBA颜色值字符串,格式为"rgba(r,g,b,a)"或"rgb(r,g,b)"
262
+ * @returns 返回Color对象
263
+ * @throws 如果rgbaValue不是有效的RGBA颜色值,则抛出错误
217
264
  */
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
- },
265
+ static fromRgba(rgbaValue) {
266
+ const rgbaMatch = rgbaValue.match(/^rgba?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([\d.]+))?\s*\)$/);
267
+ if (!rgbaMatch) throw new Error("Invalid RGBA color value");
268
+ const r = parseInt(rgbaMatch[1], 10);
269
+ const g = parseInt(rgbaMatch[2], 10);
270
+ const b = parseInt(rgbaMatch[3], 10);
271
+ const a = rgbaMatch[5] ? parseFloat(rgbaMatch[5]) : 1;
272
+ return new Color(r, g, b, a);
273
+ }
226
274
  /**
227
- * 生成GUID
275
+ * 将十六进制颜色值转换为颜色对象
228
276
  *
229
- * @returns 返回一个由8个16进制数组成的GUID字符串
277
+ * @param hexValue 十六进制颜色值,可带或不带#前缀,支持3位和6位表示
278
+ * @returns 返回颜色对象
230
279
  */
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
- },
280
+ static fromHex(hexValue, a = 1) {
281
+ const rgxShort = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
282
+ const hex = hexValue.replace(rgxShort, (m, r2, g2, b2) => r2 + r2 + g2 + g2 + b2 + b2);
283
+ const rgx = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i;
284
+ const rgb = rgx.exec(hex);
285
+ if (!rgb) {
286
+ throw new Error("Invalid HEX color value");
287
+ }
288
+ const r = parseInt(rgb[1], 16);
289
+ const g = parseInt(rgb[2], 16);
290
+ const b = parseInt(rgb[3], 16);
291
+ return new Color(r, g, b, a);
292
+ }
237
293
  /**
238
- * 将参数进行解码并返回解码后的字符串
294
+ * 从 HSL 字符串创建颜色对象
239
295
  *
240
- * @param args 参数
241
- * @returns 解码后的字符串
296
+ * @param hsl HSL 字符串,格式为 hsl(h, s%, l%) 或 hsla(h, s%, l%, a)
297
+ * @returns 返回颜色对象,如果 hsl 字符串无效则返回 null
242
298
  */
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
- }
299
+ static fromHsl(hslValue) {
300
+ const hsl = /hsl\((\d+),\s*([\d.]+)%,\s*([\d.]+)%\)/g.exec(hslValue) || /hsla\((\d+),\s*([\d.]+)%,\s*([\d.]+)%,\s*([\d.]+)\)/g.exec(hslValue);
301
+ if (!hsl) {
302
+ throw new Error("Invalid HSL color value");
303
+ }
304
+ const h = parseInt(hsl[1], 10) / 360;
305
+ const s = parseInt(hsl[2], 10) / 100;
306
+ const l = parseInt(hsl[3], 10) / 100;
307
+ const a = hsl[4] ? parseFloat(hsl[4]) : 1;
308
+ function hue2rgb(p, q, t) {
309
+ if (t < 0) t += 1;
310
+ if (t > 1) t -= 1;
311
+ if (t < 1 / 6) return p + (q - p) * 6 * t;
312
+ if (t < 1 / 2) return q;
313
+ if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
314
+ return p;
315
+ }
316
+ let r, g, b;
317
+ if (s === 0) {
318
+ r = g = b = l;
256
319
  } else {
257
- res = args[0];
320
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
321
+ const p = 2 * l - q;
322
+ r = hue2rgb(p, q, h + 1 / 3);
323
+ g = hue2rgb(p, q, h);
324
+ b = hue2rgb(p, q, h - 1 / 3);
258
325
  }
259
- return res;
260
- },
326
+ return new Color(Math.round(r * 255), Math.round(g * 255), Math.round(b * 255), a);
327
+ }
328
+ static fromName(str) {
329
+ const rgba = ColorName[str];
330
+ return this.fromRgba(rgba);
331
+ }
261
332
  /**
262
- * 将一个或多个对象的所有可枚举属性复制到目标对象。
333
+ * 从字符串中创建颜色对象
263
334
  *
264
- * @param dest 目标对象,用于接收复制的属性。
265
- * @param args 一个或多个源对象,用于提供要复制的属性。
266
- * @returns 返回目标对象,包含所有复制的属性。
335
+ * @param str 字符串类型的颜色值,支持rgba、hex、hsl格式
336
+ * @returns 返回创建的颜色对象
337
+ * @throws 当颜色值无效时,抛出错误
267
338
  */
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
- }
339
+ static from(str) {
340
+ if (this.isRgb(str)) {
341
+ return this.fromRgba(str);
342
+ } else if (this.isHex(str)) {
343
+ return this.fromHex(str);
344
+ } else if (this.isHsl(str)) {
345
+ return this.fromHsl(str);
346
+ } else if (Object.values(ColorName).map((value) => value.toString()).includes(str)) {
347
+ return this.fromName(str);
348
+ } else {
349
+ throw new Error("Invalid color value");
275
350
  }
276
- return dest;
277
- },
351
+ }
278
352
  /**
279
- * 将扁平化数组转换为树形结构数组
353
+ * 将RGB颜色值转换为十六进制颜色值
280
354
  *
281
- * @param data 扁平化数组
282
- * @param idPropertyName 数据中标识id的字段名,默认为'id'
283
- * @param parentIdPropertyName 数据中标识父节点id的字段名,默认为'parentId'
284
- * @param childrenPropertyName 树形结构中标识子节点的字段名,默认为'children'
285
- * @returns 转换后的树形结构数组
355
+ * @param r 红色分量值,取值范围0-255
356
+ * @param g 绿色分量值,取值范围0-255
357
+ * @param b 蓝色分量值,取值范围0-255
358
+ * @param a 可选参数,透明度分量值,取值范围0-1
359
+ * @returns 十六进制颜色值,格式为#RRGGBB或#RRGGBBAA
286
360
  */
287
- convertToTree2(data, idPropertyName = "id", parentIdPropertyName = "parentId", childrenPropertyName = "children") {
361
+ static rgb2hex(r, g, b, a) {
362
+ var hex = "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
363
+ if (a !== void 0) {
364
+ const alpha = Math.round(a * 255).toString(16).padStart(2, "0");
365
+ return hex + alpha;
366
+ }
367
+ return hex;
368
+ }
369
+ static isHex(a) {
370
+ return /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(a);
371
+ }
372
+ static isRgb(a) {
373
+ return /^rgba?\s*\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*(,\s*[\d.]+)?\s*\)$/.test(a);
374
+ }
375
+ static isHsl(a) {
376
+ return /^(hsl|hsla)\(\d+,\s*[\d.]+%,\s*[\d.]+%(,\s*[\d.]+)?\)$/.test(a);
377
+ }
378
+ static isColor(a) {
379
+ return this.isHex(a) || this.isRgb(a) || this.isHsl(a);
380
+ }
381
+ static random() {
382
+ let r = Math.floor(Math.random() * 256);
383
+ let g = Math.floor(Math.random() * 256);
384
+ let b = Math.floor(Math.random() * 256);
385
+ let a = Math.random();
386
+ return new Color(r, g, b, a);
387
+ }
388
+ }
389
+ class CanvasDrawer {
390
+ constructor(el) {
391
+ __publicField(this, "context", null);
392
+ if (typeof el === "string") {
393
+ el = document.querySelector("#" + el);
394
+ if (!el) {
395
+ throw new Error("Element not found");
396
+ }
397
+ }
398
+ if (el instanceof HTMLElement) {
399
+ const canvas = el;
400
+ if (canvas.getContext) {
401
+ this.context = canvas.getContext("2d");
402
+ } else {
403
+ throw new Error("getContext is not available on this element");
404
+ }
405
+ } else {
406
+ throw new Error("Element is not an HTMLElement");
407
+ }
408
+ }
409
+ /**
410
+ * 绘制线条
411
+ *
412
+ * @param start 起始坐标点
413
+ * @param end 终止坐标点
414
+ * @param options 绘制选项,包括线条宽度和颜色
415
+ * @throws 当画布上下文不存在时抛出错误
416
+ */
417
+ drawLine({ x: startX, y: startY }, { x: endX, y: endY }, options = {}) {
418
+ if (!this.context) {
419
+ throw new Error("Canvas context is null or undefined");
420
+ }
421
+ this.context.beginPath();
422
+ const width = options.width || 1;
423
+ const color = options.color || "#000";
424
+ this.context.lineWidth = width;
425
+ this.context.strokeStyle = color;
426
+ this.context.moveTo(startX, startY);
427
+ this.context.lineTo(endX, endY);
428
+ this.context.stroke();
429
+ }
430
+ /**
431
+ * 绘制圆弧
432
+ *
433
+ * @param x 圆心x坐标
434
+ * @param y 圆心y坐标
435
+ * @param radius 半径
436
+ * @param startAngle 起始角度(度)
437
+ * @param endAngle 结束角度(度)
438
+ * @param anticlockwise 是否逆时针绘制
439
+ * @param isFill 是否填充
440
+ * @param bgColor 背景颜色
441
+ * @throws 当Canvas context为null或undefined时抛出错误
442
+ */
443
+ drawArc({ x, y }, radius, startAngle, endAngle, anticlockwise, isFill, bgColor) {
444
+ if (!this.context) {
445
+ throw new Error("Canvas context is null or undefined");
446
+ }
447
+ if (isFill) {
448
+ this.context.fillStyle = bgColor;
449
+ this.context.beginPath();
450
+ this.context.arc(x, y, radius, MathUtil.deg2Rad(startAngle), MathUtil.deg2Rad(endAngle), anticlockwise);
451
+ this.context.fill();
452
+ } else {
453
+ this.context.strokeStyle = bgColor;
454
+ this.context.beginPath();
455
+ this.context.arc(x, y, radius, MathUtil.deg2Rad(startAngle), MathUtil.deg2Rad(endAngle), anticlockwise);
456
+ this.context.stroke();
457
+ }
458
+ }
459
+ static createCanvas(width = 1, height = 1) {
460
+ const canvas = document.createElement("canvas");
461
+ if (width) {
462
+ canvas.width = width;
463
+ }
464
+ if (height) {
465
+ canvas.height = height;
466
+ }
467
+ return canvas;
468
+ }
469
+ }
470
+ class EventDispatcher {
471
+ constructor() {
472
+ __publicField(this, "_listeners");
473
+ __publicField(this, "_mutex", {});
474
+ __publicField(this, "_context");
475
+ }
476
+ addEventListener(type, listener, context, mutexStatus) {
477
+ if (this._listeners === void 0) this._listeners = {};
478
+ this._context = context;
479
+ const mutex = this._mutex;
480
+ const listeners = this._listeners;
481
+ if (listeners[type] === void 0) {
482
+ listeners[type] = [];
483
+ }
484
+ if (listeners[type].indexOf(listener) === -1) {
485
+ if (mutexStatus) {
486
+ mutex[type] = listener;
487
+ }
488
+ listeners[type].push(listener);
489
+ }
490
+ return this;
491
+ }
492
+ hasEventListener(type, listener) {
493
+ if (this._listeners === null || this._listeners === void 0) return false;
494
+ const listeners = this._listeners;
495
+ return listeners[type] !== void 0 && listeners[type].indexOf(listener) !== -1;
496
+ }
497
+ removeEventListener(type, listener) {
498
+ if (this._listeners === void 0) return;
499
+ const listeners = this._listeners;
500
+ const listenerArray = listeners[type];
501
+ if (this._mutex[type] === listener) {
502
+ this._mutex[type] = null;
503
+ }
504
+ if (listenerArray !== void 0) {
505
+ const index = listenerArray.map((d) => d.toString()).indexOf(listener.toString());
506
+ if (index !== -1) {
507
+ listenerArray.splice(index, 1);
508
+ }
509
+ }
510
+ }
511
+ dispatchEvent(event) {
512
+ if (this._listeners === void 0) return;
513
+ const listeners = this._listeners;
514
+ const listenerArray = listeners[event.type];
515
+ if (listenerArray !== void 0) {
516
+ event.target = this;
517
+ const array = listenerArray.slice(0);
518
+ if (this._mutex[event.type] !== void 0) {
519
+ const find = array.find((item) => item === this._mutex[event.type]);
520
+ if (find) {
521
+ find.call(this._context || this, event);
522
+ return;
523
+ }
524
+ }
525
+ for (let i = 0, l = array.length; i < l; i++) {
526
+ const item = array[i];
527
+ if (typeof item === "function") {
528
+ item.call(this._context || this, event);
529
+ }
530
+ }
531
+ }
532
+ }
533
+ removeAllListener() {
534
+ this._mutex = {};
535
+ for (const key in this._listeners) {
536
+ this._listeners[key] = [];
537
+ }
538
+ }
539
+ }
540
+ const Util = {
541
+ /**
542
+ * 获取数据类型
543
+ *
544
+ * @param data 待判断的数据
545
+ * @returns 返回数据类型字符串
546
+ */
547
+ getDataType(data) {
548
+ return Object.prototype.toString.call(data).slice(8, -1);
549
+ },
550
+ asArray(obj) {
551
+ return this.isEmpty(obj) ? [] : Array.isArray(obj) ? obj : [obj];
552
+ },
553
+ asNumber(a) {
554
+ return Number.isNaN(Number(a)) ? 0 : Number(a);
555
+ },
556
+ /**
557
+ * 将值转换为字符串
558
+ *
559
+ * @param value 要转换的值
560
+ * @returns 转换后的字符串,如果值为空,则返回空字符串
561
+ */
562
+ asString(value) {
563
+ if (this.isEmpty(value)) {
564
+ return "";
565
+ } else {
566
+ switch (this.getDataType(value)) {
567
+ case "Object":
568
+ case "Array":
569
+ return JSON.stringify(value);
570
+ default:
571
+ return value;
572
+ }
573
+ }
574
+ },
575
+ /**
576
+ * 判断传入的值是否为空
577
+ *
578
+ * @param value 待判断的值
579
+ * @returns 返回布尔值,表示是否为空
580
+ */
581
+ isEmpty(value) {
582
+ if (value == null) {
583
+ return true;
584
+ }
585
+ const type = this.getDataType(value);
586
+ switch (type) {
587
+ case "String":
588
+ return value.trim() === "";
589
+ case "Array":
590
+ return !value.length;
591
+ case "Object":
592
+ return !Object.keys(value).length;
593
+ case "Boolean":
594
+ return !value;
595
+ default:
596
+ return false;
597
+ }
598
+ },
599
+ /**
600
+ * 将JSON对象转换为FormData对象
601
+ *
602
+ * @param json 待转换的JSON对象,其属性值为字符串或Blob类型
603
+ * @returns 转换后的FormData对象
604
+ */
605
+ json2form(json) {
606
+ const formData = new FormData();
607
+ if (this.isEmpty(json)) return formData;
608
+ Object.keys(json).forEach((key) => {
609
+ formData.append(key, json[key] instanceof Object ? JSON.stringify(json[key]) : json[key]);
610
+ });
611
+ return formData;
612
+ },
613
+ /**
614
+ * 生成GUID
615
+ *
616
+ * @returns 返回一个由8个16进制数组成的GUID字符串
617
+ */
618
+ guid() {
619
+ const S4 = function() {
620
+ return ((1 + Math.random()) * 65536 | 0).toString(16).substring(1);
621
+ };
622
+ return S4() + S4() + S4() + S4() + S4() + S4() + S4() + S4();
623
+ },
624
+ /**
625
+ * 将参数进行解码并返回解码后的字符串
626
+ *
627
+ * @param args 参数
628
+ * @returns 解码后的字符串
629
+ */
630
+ decodeDict(...args) {
631
+ let res = "";
632
+ if (args.length > 1) {
633
+ const items = args.slice(1, args.length % 2 === 0 ? args.length - 1 : args.length);
634
+ for (let i = 0; i < items.length; i = i + 2) {
635
+ const item = items[i];
636
+ if (args[0] === item) {
637
+ res = items[i + 1];
638
+ }
639
+ }
640
+ if (!res && args.length % 2 === 0) {
641
+ res = args[args.length - 1];
642
+ }
643
+ } else {
644
+ res = args[0];
645
+ }
646
+ return res;
647
+ },
648
+ /**
649
+ * 将一个或多个对象的所有可枚举属性复制到目标对象。
650
+ *
651
+ * @param dest 目标对象,用于接收复制的属性。
652
+ * @param args 一个或多个源对象,用于提供要复制的属性。
653
+ * @returns 返回目标对象,包含所有复制的属性。
654
+ */
655
+ extend(dest, ...args) {
656
+ let i, j, len, src;
657
+ for (j = 0, len = args.length; j < len; j++) {
658
+ src = args[j];
659
+ for (i in src) {
660
+ dest[i] = src[i];
661
+ }
662
+ }
663
+ return dest;
664
+ },
665
+ /**
666
+ * 将扁平化数组转换为树形结构数组
667
+ *
668
+ * @param data 扁平化数组
669
+ * @param idPropertyName 数据中标识id的字段名,默认为'id'
670
+ * @param parentIdPropertyName 数据中标识父节点id的字段名,默认为'parentId'
671
+ * @param childrenPropertyName 树形结构中标识子节点的字段名,默认为'children'
672
+ * @returns 转换后的树形结构数组
673
+ */
674
+ convertToTree2(data, idPropertyName = "id", parentIdPropertyName = "parentId", childrenPropertyName = "children") {
288
675
  const result = [];
289
676
  function buildChildren(item) {
290
677
  const children = data.filter((item2) => item2[parentIdPropertyName] === item[idPropertyName]).map((child) => {
@@ -490,10 +877,143 @@ const ObjectUtil = {
490
877
  },
491
878
  parse(str) {
492
879
  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;
880
+ if (Util.isEmpty(str)) return {};
881
+ if (Util.isObject(str)) return str;
882
+ }
883
+ };
884
+ class HashMap extends Map {
885
+ isEmpty() {
886
+ return this.size === 0;
887
+ }
888
+ _values() {
889
+ return Array.from(this.values());
890
+ }
891
+ _keys() {
892
+ return Array.from(this.keys());
893
+ }
894
+ _entries() {
895
+ return Array.from(this.entries());
896
+ }
897
+ /**
898
+ * 从键值对数组创建一个HashMap对象
899
+ *
900
+ * @param array 键值对数组,默认为空数组
901
+ * @returns 返回一个新的HashMap对象
902
+ */
903
+ static fromEntries(array = []) {
904
+ const hashMap = new HashMap();
905
+ array.forEach((element) => {
906
+ if (Array.isArray(element) && element.length === 2) {
907
+ hashMap.set(element[0], element[1]);
908
+ }
909
+ });
910
+ return hashMap;
911
+ }
912
+ /**
913
+ * 从JSON字符串创建HashMap实例
914
+ *
915
+ * @param str JSON字符串
916
+ * @returns HashMap实例
917
+ */
918
+ static fromJson(str) {
919
+ const json = ObjectUtil.parse(str);
920
+ return new HashMap(Object.entries(json));
921
+ }
922
+ }
923
+ class WebSocketClient extends EventDispatcher {
924
+ constructor(url = "ws://127.0.0.1:10088") {
925
+ super();
926
+ __publicField(this, "maxCheckTimes", 10);
927
+ __publicField(this, "url");
928
+ __publicField(this, "checkTimes", 0);
929
+ __publicField(this, "connectStatus", false);
930
+ __publicField(this, "client", null);
931
+ this.maxCheckTimes = 10;
932
+ this.url = url;
933
+ this.checkTimes = 0;
934
+ this.connect();
935
+ this.connCheckStatus(this.maxCheckTimes);
936
+ }
937
+ connect() {
938
+ this.disconnect();
939
+ if (this.url) {
940
+ try {
941
+ console.info("创建ws连接>>>" + this.url);
942
+ this.client = new WebSocket(this.url);
943
+ if (this.client) {
944
+ const self = this;
945
+ this.client.onopen = function(message) {
946
+ self.dispatchEvent({
947
+ type: EventType.WEB_SOCKET_CONNECT,
948
+ message
949
+ });
950
+ };
951
+ this.client.onmessage = function(message) {
952
+ self.connectStatus = true;
953
+ self.dispatchEvent({
954
+ type: EventType.WEB_SOCKET_MESSAGE,
955
+ message
956
+ });
957
+ };
958
+ this.client.onclose = function(message) {
959
+ self.dispatchEvent({
960
+ type: EventType.WEB_SOCKET_CLOSE,
961
+ message
962
+ });
963
+ };
964
+ if (this.checkTimes === this.maxCheckTimes) {
965
+ this.client.onerror = function(message) {
966
+ self.dispatchEvent({
967
+ type: EventType.WEB_SOCKET_ERROR,
968
+ message
969
+ });
970
+ };
971
+ }
972
+ }
973
+ } catch (ex) {
974
+ console.error("创建ws连接失败" + this.url + ":" + ex);
975
+ }
976
+ }
977
+ }
978
+ disconnect() {
979
+ if (this.client) {
980
+ try {
981
+ console.log("ws断开连接" + this.url);
982
+ this.client.close();
983
+ this.client = null;
984
+ } catch (ex) {
985
+ this.client = null;
986
+ }
987
+ }
988
+ }
989
+ connCheckStatus(times) {
990
+ if (this.checkTimes > times) return;
991
+ setTimeout(() => {
992
+ this.checkTimes++;
993
+ if (this.client && this.client.readyState !== 0 && this.client.readyState !== 1) {
994
+ this.connect();
995
+ }
996
+ this.connCheckStatus(times);
997
+ }, 2e3);
998
+ }
999
+ send(message) {
1000
+ if (this.client && this.client.readyState === 1) {
1001
+ this.client.send(message);
1002
+ return true;
1003
+ }
1004
+ console.error(this.url + "消息发送失败:" + message);
1005
+ return this;
495
1006
  }
496
- };
1007
+ heartbeat() {
1008
+ setTimeout(() => {
1009
+ if (this.client && this.client.readyState === 1) {
1010
+ this.send("HeartBeat");
1011
+ }
1012
+ console.log("HeartBeat," + this.url);
1013
+ setTimeout(this.heartbeat, 3e4);
1014
+ }, 1e3);
1015
+ }
1016
+ }
497
1017
  const ImageUtil = {
498
1018
  emptyImageUrl: "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",
499
1019
  /**
@@ -606,7 +1126,7 @@ const AjaxUtil = {
606
1126
  * @param callback - callback function when completed
607
1127
  */
608
1128
  jsonp(url, callback) {
609
- const name = "_jsonp_" + CommUtils.guid();
1129
+ const name = "_jsonp_" + Util.guid();
610
1130
  const head = document.getElementsByTagName("head")[0];
611
1131
  if (url.includes("?")) {
612
1132
  url += "&callback=" + name;
@@ -645,7 +1165,7 @@ const AjaxUtil = {
645
1165
  * );
646
1166
  */
647
1167
  get(url, options = {}, cb) {
648
- if (CommUtils.isFunction(options)) {
1168
+ if (Util.isFunction(options)) {
649
1169
  const t = cb;
650
1170
  cb = options;
651
1171
  options = t;
@@ -777,7 +1297,7 @@ const AjaxUtil = {
777
1297
  * );
778
1298
  */
779
1299
  getArrayBuffer(url, options, cb) {
780
- if (CommUtils.isFunction(options)) {
1300
+ if (Util.isFunction(options)) {
781
1301
  const t = cb;
782
1302
  cb = options;
783
1303
  options = t;
@@ -830,7 +1350,7 @@ const AjaxUtil = {
830
1350
  * );
831
1351
  */
832
1352
  getJSON(url, options, cb) {
833
- if (CommUtils.isFunction(options)) {
1353
+ if (Util.isFunction(options)) {
834
1354
  const t = cb;
835
1355
  cb = options;
836
1356
  options = t;
@@ -847,48 +1367,6 @@ const AjaxUtil = {
847
1367
  return this.get(url, options, callback);
848
1368
  }
849
1369
  };
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
1370
  class GeoUtil {
893
1371
  /**
894
1372
  * 判断给定的经纬度是否合法
@@ -898,7 +1376,7 @@ class GeoUtil {
898
1376
  * @returns 如果经纬度合法,返回true;否则返回false
899
1377
  */
900
1378
  static isLnglat(lng, lat) {
901
- return !isNaN(lng) && !isNaN(lat) && !!(+lat > -90 && +lat < 90 && +lng > -180 && +lng < 180);
1379
+ return Util.isNumber(lng) && Util.isNumber(lat) && !!(+lat > -90 && +lat < 90 && +lng > -180 && +lng < 180);
902
1380
  }
903
1381
  /**
904
1382
  * 计算两哥平面坐标点间的距离
@@ -1090,15 +1568,15 @@ class GeoUtil {
1090
1568
  * @returns 返回计算后的新经纬度点,类型为{lat: number, lng: number}
1091
1569
  */
1092
1570
  static calcPointByBearAndDis(latlng, angle, distance) {
1093
- const sLat = MathUtils.deg2Rad(latlng.lat * 1);
1094
- const sLng = MathUtils.deg2Rad(latlng.lng * 1);
1571
+ const sLat = MathUtil.deg2Rad(latlng.lat * 1);
1572
+ const sLng = MathUtil.deg2Rad(latlng.lng * 1);
1095
1573
  const d = distance / this.R;
1096
- angle = MathUtils.deg2Rad(angle);
1574
+ angle = MathUtil.deg2Rad(angle);
1097
1575
  const lat = Math.asin(Math.sin(sLat) * Math.cos(d) + Math.cos(sLat) * Math.sin(d) * Math.cos(angle));
1098
1576
  const lon = sLng + Math.atan2(Math.sin(angle) * Math.sin(d) * Math.cos(sLat), Math.cos(d) - Math.sin(sLat) * Math.sin(lat));
1099
1577
  return {
1100
- lat: MathUtils.rad2Deg(lat),
1101
- lng: MathUtils.rad2Deg(lon)
1578
+ lat: MathUtil.rad2Deg(lat),
1579
+ lng: MathUtil.rad2Deg(lon)
1102
1580
  };
1103
1581
  }
1104
1582
  /**
@@ -1247,26 +1725,6 @@ const StringUtil = {
1247
1725
  return str;
1248
1726
  }
1249
1727
  },
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
1728
  /**
1271
1729
  * 计算字符串的字节长度
1272
1730
  *
@@ -1589,42 +2047,42 @@ const GeoJsonUtil = {
1589
2047
  const AssertUtil = {
1590
2048
  assertEmpty(...arg) {
1591
2049
  arg.forEach((a) => {
1592
- if (CommUtils.isEmpty(a)) {
2050
+ if (Util.isEmpty(a)) {
1593
2051
  throw Error(ErrorType.PARAMETER_ERROR_LACK + " -> " + a);
1594
2052
  }
1595
2053
  });
1596
2054
  },
1597
2055
  assertInteger(...arg) {
1598
2056
  arg.forEach((a) => {
1599
- if (!CommUtils.isInteger(a)) {
2057
+ if (!Util.isInteger(a)) {
1600
2058
  throw Error(ErrorType.PARAMETER_ERROR_INTEGER + " -> " + a);
1601
2059
  }
1602
2060
  });
1603
2061
  },
1604
2062
  assertNumber(...arg) {
1605
2063
  arg.forEach((a) => {
1606
- if (!CommUtils.isNumber(a)) {
2064
+ if (!Util.isNumber(a)) {
1607
2065
  throw Error(ErrorType.PARAMETER_ERROR_NUMBER + " -> " + a);
1608
2066
  }
1609
2067
  });
1610
2068
  },
1611
2069
  assertArray(...arg) {
1612
2070
  arg.forEach((a) => {
1613
- if (!CommUtils.isArray(a)) {
2071
+ if (!Util.isArray(a)) {
1614
2072
  throw Error(ErrorType.PARAMETER_ERROR_ARRAY + " -> " + a);
1615
2073
  }
1616
2074
  });
1617
2075
  },
1618
2076
  assertFunction(...arg) {
1619
2077
  arg.forEach((a) => {
1620
- if (!CommUtils.isFunction(a)) {
2078
+ if (!Util.isFunction(a)) {
1621
2079
  throw Error(ErrorType.PARAMETER_ERROR_FUNCTION + " -> " + a);
1622
2080
  }
1623
2081
  });
1624
2082
  },
1625
2083
  assertObject(...arg) {
1626
2084
  arg.forEach((a) => {
1627
- if (!CommUtils.isObject(a)) {
2085
+ if (!Util.isObject(a)) {
1628
2086
  throw Error(ErrorType.PARAMETER_ERROR_OBJECT + " -> " + a);
1629
2087
  }
1630
2088
  });
@@ -2328,7 +2786,7 @@ class CoordsUtil {
2328
2786
  let p, pp;
2329
2787
  for (let i = 0, len = arr.length; i < len; i++) {
2330
2788
  p = arr[i];
2331
- if (CommUtils.isNil(p)) {
2789
+ if (Util.isNil(p)) {
2332
2790
  result.push(null);
2333
2791
  continue;
2334
2792
  }
@@ -2518,940 +2976,486 @@ function trim(str) {
2518
2976
  return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, "");
2519
2977
  }
2520
2978
  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 = {
2684
- /**
2685
- * 将Base64编码的字符串转换为Blob对象
2686
- *
2687
- * @param data Base64编码的字符串
2688
- * @returns 转换后的Blob对象
2689
- */
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);
2696
- }
2697
- const byteArray = new Uint8Array(byteNumbers);
2698
- const blob = new Blob([byteArray], { type: mimeString });
2699
- return blob;
2700
- },
2701
- /**
2702
- * 将图片的URL转换为Base64编码
2703
- *
2704
- * @param url 图片的URL地址
2705
- * @param width 图片的宽度,默认为图片原始宽度
2706
- * @param height 图片的高度,默认为图片原始高度
2707
- * @returns 返回Promise对象,解析后得到包含Base64编码数据的对象
2708
- */
2979
+ return trim(str).split(/\s+/);
2980
+ }
2981
+ const DomUtil = {
2709
2982
  /**
2710
- * 将base64字符串转换为文件对象
2983
+ * 获取元素的样式值
2711
2984
  *
2712
- * @param dataurl 包含base64字符串的数据URL
2713
- * @param filename 文件的名称
2714
- * @returns 返回文件对象
2985
+ * @param el 元素对象
2986
+ * @param style 样式属性名称
2987
+ * @returns 元素的样式值,如果获取不到则返回 null
2715
2988
  */
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);
2989
+ getStyle(el, style) {
2990
+ var _a2;
2991
+ let value = el.style[style];
2992
+ if (!value || value === "auto") {
2993
+ const css = (_a2 = document.defaultView) == null ? void 0 : _a2.getComputedStyle(el, null);
2994
+ value = css ? css[style] : null;
2995
+ if (value === "auto") value = null;
2724
2996
  }
2725
- const file = new File([u8arr], filename, { type: mime });
2726
- return file;
2997
+ return value;
2727
2998
  },
2728
2999
  /**
2729
- * 从文件下载数据
3000
+ * 创建一个HTML元素
2730
3001
  *
2731
- * @param data 要下载的数据,可以是字符串数组、BlobPart 或 MediaSource
2732
- * @param saveName 下载后文件的保存名称
3002
+ * @param tagName 元素标签名
3003
+ * @param className 元素类名
3004
+ * @param container 父容器,若传入,则新创建的元素会被添加到该容器中
3005
+ * @returns 返回新创建的HTML元素
2733
3006
  */
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 MessageUtil {
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
- this.warned[message] = true;
3007
+ create(tagName, className, container) {
3008
+ const el = document.createElement(tagName);
3009
+ el.className = className || "";
3010
+ if (container) {
3011
+ container.appendChild(el);
2766
3012
  }
2767
- }
3013
+ return el;
3014
+ },
2768
3015
  /**
2769
- * 播放消息提示音和文字朗读
3016
+ * 从父节点中移除指定元素。
2770
3017
  *
2771
- * @param type 消息类型
2772
- * @param message 消息内容
2773
- * @param options 配置选项,可选参数,包括语言、音量、语速和音高
2774
- * @returns 无返回值
3018
+ * @param el 要移除的元素对象,必须包含parentNode属性。
2775
3019
  */
2776
- static msg(type, message, options = {}) {
2777
- Message({ type, message });
2778
- if (this.isMute) return;
2779
- const typename = CommUtils.decodeDict(type, "success", "恭喜:", "error", "发生错误:", "warning", "警告:", "info", "友情提示:") + ":";
2780
- this.speechSynthesisUtterance.text = typename + message;
2781
- this.speechSynthesisUtterance.lang = options.lang || "zh-CN";
2782
- this.speechSynthesisUtterance.volume = options.volume || 1;
2783
- this.speechSynthesisUtterance.rate = options.rate || 1;
2784
- this.speechSynthesisUtterance.pitch = options.pitch || 1;
2785
- this.speechSynthesis.speak(this.speechSynthesisUtterance);
2786
- }
2787
- static stop(e) {
2788
- this.speechSynthesisUtterance.text = e;
2789
- this.speechSynthesis.cancel();
2790
- }
2791
- static warning(message) {
2792
- if (process.env.NODE_ENV === "development" && console !== void 0) {
2793
- console.warn(`Warning: ${message}`);
2794
- }
2795
- this.msg("warning", message);
2796
- }
2797
- static warningOnce(message) {
2798
- this._call(this.warning.bind(this), message);
2799
- }
2800
- static info(message) {
2801
- if (process.env.NODE_ENV === "development" && console !== void 0) {
2802
- console.info(`Info: ${message}`);
2803
- }
2804
- this.msg("info", message);
2805
- }
2806
- static infoOnce(message) {
2807
- this._call(this.info.bind(this), message);
2808
- }
2809
- static error(message) {
2810
- if (process.env.NODE_ENV === "development" && console !== void 0) {
2811
- console.error(`Error: ${message}`);
2812
- }
2813
- this.msg("error", message);
2814
- }
2815
- static errorOnce(message) {
2816
- this._call(this.error.bind(this), message);
2817
- }
2818
- static success(message) {
2819
- if (process.env.NODE_ENV === "development" && console !== void 0) {
2820
- console.log(`Success: ${message}`);
3020
+ remove(el) {
3021
+ const parent = el.parentNode;
3022
+ if (parent) {
3023
+ parent.removeChild(el);
2821
3024
  }
2822
- this.msg("success", message);
2823
- }
2824
- static successOnce(message) {
2825
- this._call(this.success.bind(this), message);
2826
- }
2827
- }
2828
- __publicField(MessageUtil, "warned", {});
2829
- __publicField(MessageUtil, "isMute", !!Number(localStorage.getItem("mute")) || false);
2830
- __publicField(MessageUtil, "speechSynthesis", window.speechSynthesis);
2831
- __publicField(MessageUtil, "speechSynthesisUtterance", new SpeechSynthesisUtterance());
2832
- const OptimizeUtil = {
3025
+ },
2833
3026
  /**
2834
- * 防抖函数,在指定的等待时间内,如果连续触发事件,则只在最后一次触发后执行函数。适用于像搜索输入框这种需要用户停止输入后才调用的场景
3027
+ * 清空给定元素的子节点
2835
3028
  *
2836
- * @param func 需要防抖的函数。
2837
- * @param wait 等待时间,单位毫秒。
2838
- * @param immediate 是否立即执行函数,默认为true。
2839
- * @returns 返回防抖后的函数。
3029
+ * @param el 要清空子节点的元素,包含firstChild和removeChild属性
2840
3030
  */
2841
- debounce(func, wait, immediate = true) {
2842
- let timeout = null;
2843
- let args;
2844
- let timestamp;
2845
- let result;
2846
- const later = () => {
2847
- const last = Date.now() - timestamp;
2848
- if (last < wait && last > 0) {
2849
- timeout = setTimeout(later, wait - last);
2850
- } else {
2851
- timeout = null;
2852
- if (!immediate) {
2853
- result = func.apply(this, args);
2854
- }
2855
- }
2856
- };
2857
- return (...args2) => {
2858
- timestamp = Date.now();
2859
- const callNow = immediate && !timeout;
2860
- if (!timeout) {
2861
- timeout = setTimeout(later, wait);
2862
- }
2863
- if (callNow) {
2864
- result = func.apply(this, args2);
2865
- if (!timeout) {
2866
- args2 = null;
2867
- }
2868
- }
2869
- return result;
2870
- };
3031
+ empty(el) {
3032
+ while (el.firstChild) {
3033
+ el.removeChild(el.firstChild);
3034
+ }
2871
3035
  },
2872
3036
  /**
2873
- * 节流函数,适用于像滚动事件、窗口resize事件这种需要限制调用频率的场景
3037
+ * 将元素移到父节点的最前面
2874
3038
  *
2875
- * @param func 需要节流的函数
2876
- * @param wait 节流间隔,单位为毫秒
2877
- * @param type 节流类型,1表示时间戳方式,2表示定时器方式
2878
- * @returns 返回一个新的函数,该函数在节流控制下执行传入的函数
3039
+ * @param el 要移动的元素,需要包含 parentNode 属性
2879
3040
  */
2880
- throttle(func, wait, type = 1) {
2881
- let previous = 0;
2882
- let timeout = null;
2883
- return (...args) => {
2884
- if (type === 1) {
2885
- const now = Date.now();
2886
- if (now - previous >= wait) {
2887
- func.apply(this, args);
2888
- previous = now;
2889
- }
2890
- } else if (type === 2) {
2891
- if (!timeout) {
2892
- timeout = setTimeout(() => {
2893
- timeout = null;
2894
- func.apply(this, args);
2895
- }, wait);
2896
- }
2897
- }
2898
- };
3041
+ toFront(el) {
3042
+ const parent = el.parentNode;
3043
+ if (parent && parent.lastChild !== el) {
3044
+ parent.appendChild(el);
3045
+ }
2899
3046
  },
2900
3047
  /**
2901
- * 缓存函数,将传入的函数fn的计算结果缓存,提高重复计算的效率
3048
+ * 将元素移动到其父节点的最前面
2902
3049
  *
2903
- * @param fn 传入待缓存的函数
2904
- * @returns 返回缓存后的函数
3050
+ * @param el 要移动的元素,需要包含parentNode属性
2905
3051
  */
2906
- memoize(fn) {
2907
- const cache = /* @__PURE__ */ new Map();
2908
- return (...args) => {
2909
- const argsString = JSON.stringify(args);
2910
- if (cache.has(argsString)) {
2911
- return cache.get(argsString);
2912
- } else {
2913
- const result = fn.apply(this, args);
2914
- cache.set(argsString, result);
2915
- return result;
2916
- }
2917
- };
3052
+ toBack(el) {
3053
+ const parent = el.parentNode;
3054
+ if (parent && parent.firstChild !== el) {
3055
+ parent.insertBefore(el, parent.firstChild);
3056
+ }
2918
3057
  },
2919
3058
  /**
2920
- * 递归调用函数,以一定的频率和持续时间执行。
3059
+ * 获取元素的类名
2921
3060
  *
2922
- * @param fun 要递归调用的函数。
2923
- * @param frequency 每次调用函数之间的时间间隔,单位为毫秒,默认为500毫秒。
2924
- * @param duration 函数递归调用的总时长,单位为毫秒,默认为5000毫秒。
2925
- */
2926
- recurve(fun, frequency = 500, duration = 5e3) {
2927
- let timer = 0;
2928
- setTimeout(() => {
2929
- timer++;
2930
- if (timer < Math.floor(duration / frequency)) {
2931
- fun.call(this);
2932
- setTimeout(this.recurve.bind(this, fun, frequency, duration), frequency);
2933
- }
2934
- }, frequency);
3061
+ * @param el 包含对应元素和类名的对象
3062
+ * @param el.correspondingElement 对应的元素
3063
+ * @param el.className 类名对象
3064
+ * @param el.className.baseVal 类名字符串
3065
+ * @returns 返回元素的类名字符串
3066
+ */
3067
+ getClass(el) {
3068
+ const shadowElement = (el == null ? void 0 : el.host) || el;
3069
+ return shadowElement.className.toString();
2935
3070
  },
2936
3071
  /**
2937
- * 确保函数只被调用一次
3072
+ * 判断元素是否包含指定类名
2938
3073
  *
2939
- * @param func 要被调用的函数
2940
- * @returns 返回一个新的函数,该函数在被首次调用时会执行传入的函数,之后再次调用将不再执行
3074
+ * @param el 元素对象,包含classList属性,classList属性包含contains方法
3075
+ * @param name 要判断的类名
3076
+ * @returns 返回一个布尔值,表示元素是否包含指定类名
2941
3077
  */
2942
- once(func) {
2943
- let called = false;
2944
- return function(...args) {
2945
- if (!called) {
2946
- called = true;
2947
- return func(...args);
2948
- }
2949
- };
2950
- }
2951
- };
2952
- const UrlUtil = {
3078
+ hasClass(el, name) {
3079
+ var _a2;
3080
+ if ((_a2 = el.classList) == null ? void 0 : _a2.contains(name)) {
3081
+ return true;
3082
+ }
3083
+ const className = this.getClass(el);
3084
+ return className.length > 0 && new RegExp(`(^|\\s)${name}(\\s|$)`).test(className);
3085
+ },
2953
3086
  /**
2954
- * 将json对象转换为查询字符串
3087
+ * 给指定的 HTML 元素添加类名
2955
3088
  *
2956
- * @param json 待转换的json对象
2957
- * @returns 转换后的查询字符串
3089
+ * @param el 要添加类名的 HTML 元素
3090
+ * @param name 要添加的类名,多个类名之间用空格分隔
2958
3091
  */
2959
- json2Query(json) {
2960
- var tempArr = [];
2961
- for (var i in json) {
2962
- if (json.hasOwnProperty(i)) {
2963
- var key = i;
2964
- var value = json[i];
2965
- tempArr.push(encodeURIComponent(key) + "=" + encodeURIComponent(value));
3092
+ addClass(el, name) {
3093
+ if (el.classList !== void 0) {
3094
+ const classes = splitWords(name);
3095
+ for (let i = 0, len = classes.length; i < len; i++) {
3096
+ el.classList.add(classes[i]);
2966
3097
  }
3098
+ } else if (!this.hasClass(el, name)) {
3099
+ const className = this.getClass(el);
3100
+ this.setClass(el, (className ? className + " " : "") + name);
2967
3101
  }
2968
- var urlParamsStr = tempArr.join("&");
2969
- return urlParamsStr;
2970
3102
  },
2971
3103
  /**
2972
- * 从 URL 中解析出查询参数和哈希参数,并以对象的形式返回
3104
+ * 从元素中移除指定类名
2973
3105
  *
2974
- * @param href 需要解析的 URL 链接,默认为当前窗口的 URL
2975
- * @param needDecode 是否需要解码参数值,默认为 true
2976
- * @returns 返回一个包含解析后参数的对象,其中键为参数名,值为参数值
3106
+ * @param el 要移除类名的元素
3107
+ * @param name 要移除的类名,多个类名用空格分隔
2977
3108
  */
2978
- query2Json(href = window.location.href, needDecode = true) {
2979
- const reg = /([^&=]+)=([\w\W]*?)(&|$|#)/g;
2980
- const { search, hash } = new URL(href);
2981
- const args = [search, hash];
2982
- let obj = {};
2983
- for (let i = 0; i < args.length; i++) {
2984
- const str = args[i];
2985
- if (str) {
2986
- const s = str.replace(/#|\//g, "");
2987
- const arr = s.split("?");
2988
- if (arr.length > 1) {
2989
- for (let j = 1; j < arr.length; j++) {
2990
- let res;
2991
- while (res = reg.exec(arr[j])) {
2992
- obj[res[1]] = needDecode ? decodeURIComponent(res[2]) : res[2];
2993
- }
2994
- }
2995
- }
2996
- }
3109
+ removeClass(el, name) {
3110
+ if (el.classList !== void 0) {
3111
+ const classes = splitWords(name);
3112
+ classes.forEach((className) => el.classList.remove(className));
3113
+ } else {
3114
+ this.setClass(el, (" " + this.getClass(el) + " ").replace(" " + name + " ", " ").trim());
2997
3115
  }
2998
- return obj;
2999
- }
3000
- };
3001
- class Color {
3002
- constructor(r, g, b, a) {
3003
- __publicField(this, "_r");
3004
- __publicField(this, "_g");
3005
- __publicField(this, "_b");
3006
- __publicField(this, "_alpha");
3007
- this._validateColorChannel(r);
3008
- this._validateColorChannel(g);
3009
- this._validateColorChannel(b);
3010
- this._r = r;
3011
- this._g = g;
3012
- this._b = b;
3013
- this._alpha = MathUtils.clamp(a || 1, 0, 1);
3014
- }
3015
- _validateColorChannel(channel) {
3016
- if (channel < 0 || channel > 255) {
3017
- throw new Error("Color channel must be between 0 and 255.");
3116
+ },
3117
+ /**
3118
+ * 设置元素的 CSS 类名
3119
+ *
3120
+ * @param el HTML 或 SVG 元素
3121
+ * @param name 要设置的类名,多个类名之间用空格分隔
3122
+ */
3123
+ setClass(el, name) {
3124
+ if ("classList" in el) {
3125
+ el.classList.value = "";
3126
+ name.split(" ").forEach((className) => el.classList.add(className));
3018
3127
  }
3019
- }
3020
- toString() {
3021
- return `rgba(${this._r}, ${this._g}, ${this._b}, ${this._alpha})`;
3022
- }
3023
- toJson() {
3024
- return { r: this._r, g: this._g, b: this._b, a: this._alpha };
3025
- }
3026
- get rgba() {
3027
- return `rgba(${this._r}, ${this._g}, ${this._b}, ${this._alpha})`;
3028
- }
3029
- get hex() {
3030
- return Color.rgb2hex(this._r, this._g, this._b, this._alpha);
3031
- }
3032
- setAlpha(a) {
3033
- this._alpha = MathUtils.clamp(a, 0, 1);
3034
- return this;
3035
- }
3036
- // 设置颜色的RGB值
3037
- setRgb(r, g, b) {
3038
- this._validateColorChannel(r);
3039
- this._validateColorChannel(g);
3040
- this._validateColorChannel(b);
3041
- this._r = r;
3042
- this._g = g;
3043
- this._b = b;
3044
- return this;
3045
- }
3128
+ },
3046
3129
  /**
3047
- * 从RGBA字符串创建Color对象
3130
+ * 从字符串中解析XML文档,并返回根节点
3048
3131
  *
3049
- * @param rgbaValue RGBA颜色值字符串,格式为"rgba(r,g,b,a)"或"rgb(r,g,b)"
3050
- * @returns 返回Color对象
3051
- * @throws 如果rgbaValue不是有效的RGBA颜色值,则抛出错误
3132
+ * @param str 要解析的XML字符串
3133
+ * @returns 解析后的XML文档的根节点
3052
3134
  */
3053
- static fromRgba(rgbaValue) {
3054
- const rgbaMatch = rgbaValue.match(/^rgba?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([\d.]+))?\s*\)$/);
3055
- if (!rgbaMatch) throw new Error("Invalid RGBA color value");
3056
- const r = parseInt(rgbaMatch[1], 10);
3057
- const g = parseInt(rgbaMatch[2], 10);
3058
- const b = parseInt(rgbaMatch[3], 10);
3059
- const a = rgbaMatch[5] ? parseFloat(rgbaMatch[5]) : 1;
3060
- return new Color(r, g, b, a);
3135
+ parseFromString(str) {
3136
+ const parser = new DOMParser();
3137
+ const doc = parser.parseFromString(str, "text/xml");
3138
+ return doc.children[0];
3061
3139
  }
3140
+ };
3141
+ const FileUtil = {
3062
3142
  /**
3063
- * 将十六进制颜色值转换为颜色对象
3143
+ * 将Base64编码的字符串转换为Blob对象
3064
3144
  *
3065
- * @param hexValue 十六进制颜色值,可带或不带#前缀,支持3位和6位表示
3066
- * @returns 返回颜色对象
3145
+ * @param data Base64编码的字符串
3146
+ * @returns 转换后的Blob对象
3067
3147
  */
3068
- static fromHex(hexValue, a = 1) {
3069
- const rgxShort = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
3070
- const hex = hexValue.replace(rgxShort, (m, r2, g2, b2) => r2 + r2 + g2 + g2 + b2 + b2);
3071
- const rgx = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i;
3072
- const rgb = rgx.exec(hex);
3073
- if (!rgb) {
3074
- throw new Error("Invalid HEX color value");
3148
+ convertBase64ToBlob(data) {
3149
+ const mimeString = data.split(",")[0].split(":")[1].split(";")[0];
3150
+ const byteCharacters = atob(data.split(",")[1]);
3151
+ const byteNumbers = new Array(byteCharacters.length);
3152
+ for (let i = 0; i < byteCharacters.length; i++) {
3153
+ byteNumbers[i] = byteCharacters.charCodeAt(i);
3075
3154
  }
3076
- const r = parseInt(rgb[1], 16);
3077
- const g = parseInt(rgb[2], 16);
3078
- const b = parseInt(rgb[3], 16);
3079
- return new Color(r, g, b, a);
3080
- }
3155
+ const byteArray = new Uint8Array(byteNumbers);
3156
+ const blob = new Blob([byteArray], { type: mimeString });
3157
+ return blob;
3158
+ },
3081
3159
  /**
3082
- * 从 HSL 字符串创建颜色对象
3160
+ * 将图片的URL转换为Base64编码
3083
3161
  *
3084
- * @param hsl HSL 字符串,格式为 hsl(h, s%, l%) 或 hsla(h, s%, l%, a)
3085
- * @returns 返回颜色对象,如果 hsl 字符串无效则返回 null
3162
+ * @param url 图片的URL地址
3163
+ * @param width 图片的宽度,默认为图片原始宽度
3164
+ * @param height 图片的高度,默认为图片原始高度
3165
+ * @returns 返回Promise对象,解析后得到包含Base64编码数据的对象
3086
3166
  */
3087
- static fromHsl(hslValue) {
3088
- const hsl = /hsl\((\d+),\s*([\d.]+)%,\s*([\d.]+)%\)/g.exec(hslValue) || /hsla\((\d+),\s*([\d.]+)%,\s*([\d.]+)%,\s*([\d.]+)\)/g.exec(hslValue);
3089
- if (!hsl) {
3090
- throw new Error("Invalid HSL color value");
3091
- }
3092
- const h = parseInt(hsl[1], 10) / 360;
3093
- const s = parseInt(hsl[2], 10) / 100;
3094
- const l = parseInt(hsl[3], 10) / 100;
3095
- const a = hsl[4] ? parseFloat(hsl[4]) : 1;
3096
- function hue2rgb(p, q, t) {
3097
- if (t < 0) t += 1;
3098
- if (t > 1) t -= 1;
3099
- if (t < 1 / 6) return p + (q - p) * 6 * t;
3100
- if (t < 1 / 2) return q;
3101
- if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
3102
- return p;
3103
- }
3104
- let r, g, b;
3105
- if (s === 0) {
3106
- r = g = b = l;
3107
- } else {
3108
- const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
3109
- const p = 2 * l - q;
3110
- r = hue2rgb(p, q, h + 1 / 3);
3111
- g = hue2rgb(p, q, h);
3112
- b = hue2rgb(p, q, h - 1 / 3);
3113
- }
3114
- return new Color(Math.round(r * 255), Math.round(g * 255), Math.round(b * 255), a);
3115
- }
3116
3167
  /**
3117
- * 从字符串中创建颜色对象
3168
+ * 将base64字符串转换为文件对象
3118
3169
  *
3119
- * @param str 字符串类型的颜色值,支持rgba、hex、hsl格式
3120
- * @returns 返回创建的颜色对象
3121
- * @throws 当颜色值无效时,抛出错误
3170
+ * @param dataurl 包含base64字符串的数据URL
3171
+ * @param filename 文件的名称
3172
+ * @returns 返回文件对象
3122
3173
  */
3123
- static from(str) {
3124
- if (this.isRgb(str)) {
3125
- return this.fromRgba(str);
3126
- } else if (this.isHex(str)) {
3127
- return this.fromHex(str);
3128
- } else if (this.isHsl(str)) {
3129
- return this.fromHsl(str);
3130
- } else {
3131
- throw new Error("Invalid color value");
3174
+ convertBase64ToFile(dataurl, filename) {
3175
+ const arr = dataurl.split(",");
3176
+ const mimeMatch = arr[0].match(/:(.*?);/);
3177
+ const mime = mimeMatch ? mimeMatch[1] : "image/png";
3178
+ const bstr = atob(arr[1]);
3179
+ const u8arr = new Uint8Array(bstr.length);
3180
+ for (let i = 0; i < bstr.length; i++) {
3181
+ u8arr[i] = bstr.charCodeAt(i);
3132
3182
  }
3133
- }
3183
+ const file = new File([u8arr], filename, { type: mime });
3184
+ return file;
3185
+ },
3134
3186
  /**
3135
- * 将RGB颜色值转换为十六进制颜色值
3187
+ * 从文件下载数据
3136
3188
  *
3137
- * @param r 红色分量值,取值范围0-255
3138
- * @param g 绿色分量值,取值范围0-255
3139
- * @param b 蓝色分量值,取值范围0-255
3140
- * @param a 可选参数,透明度分量值,取值范围0-1
3141
- * @returns 十六进制颜色值,格式为#RRGGBB或#RRGGBBAA
3189
+ * @param data 要下载的数据,可以是字符串数组、BlobPart 或 MediaSource
3190
+ * @param saveName 下载后文件的保存名称
3142
3191
  */
3143
- static rgb2hex(r, g, b, a) {
3144
- var hex = "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
3145
- if (a !== void 0) {
3146
- const alpha = Math.round(a * 255).toString(16).padStart(2, "0");
3147
- return hex + alpha;
3192
+ downloadFromFile(data, saveName) {
3193
+ if (typeof data == "object") {
3194
+ if (data instanceof Blob) {
3195
+ data = URL.createObjectURL(data);
3196
+ } else {
3197
+ const str = JSON.stringify(data);
3198
+ const blob = new Blob([str], { type: "text/json" });
3199
+ data = window.URL.createObjectURL(blob);
3200
+ }
3201
+ } else if (typeof data == "string" && data.indexOf("http") === -1) {
3202
+ const blob = new Blob([data], { type: "text/json" });
3203
+ data = window.URL.createObjectURL(blob);
3148
3204
  }
3149
- return hex;
3150
- }
3151
- static isHex(a) {
3152
- return /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(a);
3153
- }
3154
- static isRgb(a) {
3155
- return /^rgba?\s*\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*(,\s*[\d.]+)?\s*\)$/.test(a);
3156
- }
3157
- static isHsl(a) {
3158
- return /^(hsl|hsla)\(\d+,\s*[\d.]+%,\s*[\d.]+%(,\s*[\d.]+)?\)$/.test(a);
3205
+ var link = document.createElement("a");
3206
+ link.href = data;
3207
+ link.download = saveName || "";
3208
+ link.click();
3209
+ window.URL.revokeObjectURL(link.href);
3159
3210
  }
3160
- static isColor(a) {
3161
- return this.isHex(a) || this.isRgb(a) || this.isHsl(a);
3211
+ };
3212
+ class MessageUtil {
3213
+ static resetWarned() {
3214
+ this.warned = {};
3162
3215
  }
3163
- static random() {
3164
- let r = Math.floor(Math.random() * 256);
3165
- let g = Math.floor(Math.random() * 256);
3166
- let b = Math.floor(Math.random() * 256);
3167
- let a = Math.random();
3168
- return new Color(r, g, b, a);
3216
+ static changeVoice() {
3217
+ this.isMute = !!Number(!this.isMute);
3218
+ localStorage.setItem("mute", Number(this.isMute).toString());
3169
3219
  }
3170
- }
3171
- class CanvasDrawer {
3172
- constructor(el) {
3173
- __publicField(this, "context", null);
3174
- if (typeof el === "string") {
3175
- el = document.querySelector("#" + el);
3176
- if (!el) {
3177
- throw new Error("Element not found");
3178
- }
3179
- }
3180
- if (el instanceof HTMLElement) {
3181
- const canvas = el;
3182
- if (canvas.getContext) {
3183
- this.context = canvas.getContext("2d");
3184
- } else {
3185
- throw new Error("getContext is not available on this element");
3186
- }
3187
- } else {
3188
- throw new Error("Element is not an HTMLElement");
3220
+ static _call(method, message) {
3221
+ if (!this.warned[message]) {
3222
+ method(message);
3223
+ this.warned[message] = true;
3189
3224
  }
3190
3225
  }
3191
3226
  /**
3192
- * 绘制线条
3227
+ * 播放消息提示音和文字朗读
3193
3228
  *
3194
- * @param start 起始坐标点
3195
- * @param end 终止坐标点
3196
- * @param options 绘制选项,包括线条宽度和颜色
3197
- * @throws 当画布上下文不存在时抛出错误
3229
+ * @param type 消息类型
3230
+ * @param message 消息内容
3231
+ * @param options 配置选项,可选参数,包括语言、音量、语速和音高
3232
+ * @returns 无返回值
3198
3233
  */
3199
- drawLine({ x: startX, y: startY }, { x: endX, y: endY }, options = {}) {
3200
- if (!this.context) {
3201
- throw new Error("Canvas context is null or undefined");
3202
- }
3203
- this.context.beginPath();
3204
- const width = options.width || 1;
3205
- const color = options.color || "#000";
3206
- this.context.lineWidth = width;
3207
- this.context.strokeStyle = color;
3208
- this.context.moveTo(startX, startY);
3209
- this.context.lineTo(endX, endY);
3210
- this.context.stroke();
3234
+ static msg(type, message, options = {}) {
3235
+ Message({ type, message });
3236
+ if (this.isMute) return;
3237
+ const typename = Util.decodeDict(type, "success", "恭喜:", "error", "发生错误:", "warning", "警告:", "info", "友情提示:") + ":";
3238
+ this.speechSynthesisUtterance.text = typename + message;
3239
+ this.speechSynthesisUtterance.lang = options.lang || "zh-CN";
3240
+ this.speechSynthesisUtterance.volume = options.volume || 1;
3241
+ this.speechSynthesisUtterance.rate = options.rate || 1;
3242
+ this.speechSynthesisUtterance.pitch = options.pitch || 1;
3243
+ this.speechSynthesis.speak(this.speechSynthesisUtterance);
3211
3244
  }
3212
- /**
3213
- * 绘制圆弧
3214
- *
3215
- * @param x 圆心x坐标
3216
- * @param y 圆心y坐标
3217
- * @param radius 半径
3218
- * @param startAngle 起始角度(度)
3219
- * @param endAngle 结束角度(度)
3220
- * @param anticlockwise 是否逆时针绘制
3221
- * @param isFill 是否填充
3222
- * @param bgColor 背景颜色
3223
- * @throws 当Canvas context为null或undefined时抛出错误
3224
- */
3225
- drawArc({ x, y }, radius, startAngle, endAngle, anticlockwise, isFill, bgColor) {
3226
- if (!this.context) {
3227
- throw new Error("Canvas context is null or undefined");
3228
- }
3229
- if (isFill) {
3230
- this.context.fillStyle = bgColor;
3231
- this.context.beginPath();
3232
- this.context.arc(x, y, radius, MathUtils.deg2Rad(startAngle), MathUtils.deg2Rad(endAngle), anticlockwise);
3233
- this.context.fill();
3234
- } else {
3235
- this.context.strokeStyle = bgColor;
3236
- this.context.beginPath();
3237
- this.context.arc(x, y, radius, MathUtils.deg2Rad(startAngle), MathUtils.deg2Rad(endAngle), anticlockwise);
3238
- this.context.stroke();
3239
- }
3245
+ static stop(e) {
3246
+ this.speechSynthesisUtterance.text = e;
3247
+ this.speechSynthesis.cancel();
3240
3248
  }
3241
- static createCanvas(width = 1, height = 1) {
3242
- const canvas = document.createElement("canvas");
3243
- if (width) {
3244
- canvas.width = width;
3245
- }
3246
- if (height) {
3247
- canvas.height = height;
3249
+ static warning(message) {
3250
+ if (process.env.NODE_ENV === "development" && console !== void 0) {
3251
+ console.warn(`Warning: ${message}`);
3248
3252
  }
3249
- return canvas;
3253
+ this.msg("warning", message);
3250
3254
  }
3251
- }
3252
- class EventDispatcher {
3253
- constructor() {
3254
- __publicField(this, "_listeners");
3255
- __publicField(this, "_mutex", {});
3256
- __publicField(this, "_context");
3255
+ static warningOnce(message) {
3256
+ this._call(this.warning.bind(this), message);
3257
3257
  }
3258
- addEventListener(type, listener, context, mutexStatus) {
3259
- if (this._listeners === void 0) this._listeners = {};
3260
- this._context = context;
3261
- const mutex = this._mutex;
3262
- const listeners = this._listeners;
3263
- if (listeners[type] === void 0) {
3264
- listeners[type] = [];
3265
- }
3266
- if (listeners[type].indexOf(listener) === -1) {
3267
- if (mutexStatus) {
3268
- mutex[type] = listener;
3269
- }
3270
- listeners[type].push(listener);
3258
+ static info(message) {
3259
+ if (process.env.NODE_ENV === "development" && console !== void 0) {
3260
+ console.info(`Info: ${message}`);
3271
3261
  }
3272
- return this;
3262
+ this.msg("info", message);
3273
3263
  }
3274
- hasEventListener(type, listener) {
3275
- if (this._listeners === null || this._listeners === void 0) return false;
3276
- const listeners = this._listeners;
3277
- return listeners[type] !== void 0 && listeners[type].indexOf(listener) !== -1;
3264
+ static infoOnce(message) {
3265
+ this._call(this.info.bind(this), message);
3278
3266
  }
3279
- removeEventListener(type, listener) {
3280
- if (this._listeners === void 0) return;
3281
- const listeners = this._listeners;
3282
- const listenerArray = listeners[type];
3283
- if (this._mutex[type] === listener) {
3284
- this._mutex[type] = null;
3267
+ static error(message) {
3268
+ if (process.env.NODE_ENV === "development" && console !== void 0) {
3269
+ console.error(`Error: ${message}`);
3285
3270
  }
3286
- if (listenerArray !== void 0) {
3287
- const index = listenerArray.map((d) => d.toString()).indexOf(listener.toString());
3288
- if (index !== -1) {
3289
- listenerArray.splice(index, 1);
3290
- }
3271
+ this.msg("error", message);
3272
+ }
3273
+ static errorOnce(message) {
3274
+ this._call(this.error.bind(this), message);
3275
+ }
3276
+ static success(message) {
3277
+ if (process.env.NODE_ENV === "development" && console !== void 0) {
3278
+ console.log(`Success: ${message}`);
3291
3279
  }
3280
+ this.msg("success", message);
3292
3281
  }
3293
- dispatchEvent(event) {
3294
- if (this._listeners === void 0) return;
3295
- const listeners = this._listeners;
3296
- const listenerArray = listeners[event.type];
3297
- if (listenerArray !== void 0) {
3298
- event.target = this;
3299
- const array = listenerArray.slice(0);
3300
- if (this._mutex[event.type] !== void 0) {
3301
- const find = array.find((item) => item === this._mutex[event.type]);
3302
- if (find) {
3303
- find.call(this._context || this, event);
3304
- return;
3282
+ static successOnce(message) {
3283
+ this._call(this.success.bind(this), message);
3284
+ }
3285
+ }
3286
+ __publicField(MessageUtil, "warned", {});
3287
+ __publicField(MessageUtil, "isMute", !!Number(localStorage.getItem("mute")) || false);
3288
+ __publicField(MessageUtil, "speechSynthesis", window.speechSynthesis);
3289
+ __publicField(MessageUtil, "speechSynthesisUtterance", new SpeechSynthesisUtterance());
3290
+ const OptimizeUtil = {
3291
+ /**
3292
+ * 防抖函数,在指定的等待时间内,如果连续触发事件,则只在最后一次触发后执行函数。适用于像搜索输入框这种需要用户停止输入后才调用的场景
3293
+ *
3294
+ * @param func 需要防抖的函数。
3295
+ * @param wait 等待时间,单位毫秒。
3296
+ * @param immediate 是否立即执行函数,默认为true。
3297
+ * @returns 返回防抖后的函数。
3298
+ */
3299
+ debounce(func, wait, immediate = true) {
3300
+ let timeout = null;
3301
+ let args;
3302
+ let timestamp;
3303
+ let result;
3304
+ const later = () => {
3305
+ const last = Date.now() - timestamp;
3306
+ if (last < wait && last > 0) {
3307
+ timeout = setTimeout(later, wait - last);
3308
+ } else {
3309
+ timeout = null;
3310
+ if (!immediate) {
3311
+ result = func.apply(this, args);
3312
+ }
3313
+ }
3314
+ };
3315
+ return (...args2) => {
3316
+ timestamp = Date.now();
3317
+ const callNow = immediate && !timeout;
3318
+ if (!timeout) {
3319
+ timeout = setTimeout(later, wait);
3320
+ }
3321
+ if (callNow) {
3322
+ result = func.apply(this, args2);
3323
+ if (!timeout) {
3324
+ args2 = null;
3305
3325
  }
3306
3326
  }
3307
- for (let i = 0, l = array.length; i < l; i++) {
3308
- const item = array[i];
3309
- if (typeof item === "function") {
3310
- item.call(this._context || this, event);
3327
+ return result;
3328
+ };
3329
+ },
3330
+ /**
3331
+ * 节流函数,适用于像滚动事件、窗口resize事件这种需要限制调用频率的场景
3332
+ *
3333
+ * @param func 需要节流的函数
3334
+ * @param wait 节流间隔,单位为毫秒
3335
+ * @param type 节流类型,1表示时间戳方式,2表示定时器方式
3336
+ * @returns 返回一个新的函数,该函数在节流控制下执行传入的函数
3337
+ */
3338
+ throttle(func, wait, type = 1) {
3339
+ let previous = 0;
3340
+ let timeout = null;
3341
+ return (...args) => {
3342
+ if (type === 1) {
3343
+ const now = Date.now();
3344
+ if (now - previous >= wait) {
3345
+ func.apply(this, args);
3346
+ previous = now;
3347
+ }
3348
+ } else if (type === 2) {
3349
+ if (!timeout) {
3350
+ timeout = setTimeout(() => {
3351
+ timeout = null;
3352
+ func.apply(this, args);
3353
+ }, wait);
3311
3354
  }
3312
3355
  }
3313
- }
3314
- }
3315
- removeAllListener() {
3316
- this._mutex = {};
3317
- for (const key in this._listeners) {
3318
- this._listeners[key] = [];
3319
- }
3320
- }
3321
- }
3322
- class HashMap extends Map {
3323
- isEmpty() {
3324
- return this.size === 0;
3325
- }
3326
- _values() {
3327
- return Array.from(this.values());
3328
- }
3329
- _keys() {
3330
- return Array.from(this.keys());
3331
- }
3332
- _entries() {
3333
- return Array.from(this.entries());
3334
- }
3356
+ };
3357
+ },
3335
3358
  /**
3336
- * 从键值对数组创建一个HashMap对象
3359
+ * 缓存函数,将传入的函数fn的计算结果缓存,提高重复计算的效率
3337
3360
  *
3338
- * @param array 键值对数组,默认为空数组
3339
- * @returns 返回一个新的HashMap对象
3361
+ * @param fn 传入待缓存的函数
3362
+ * @returns 返回缓存后的函数
3340
3363
  */
3341
- static fromEntries(array = []) {
3342
- const hashMap = new HashMap();
3343
- array.forEach((element) => {
3344
- if (Array.isArray(element) && element.length === 2) {
3345
- hashMap.set(element[0], element[1]);
3364
+ memoize(fn) {
3365
+ const cache = /* @__PURE__ */ new Map();
3366
+ return (...args) => {
3367
+ const argsString = JSON.stringify(args);
3368
+ if (cache.has(argsString)) {
3369
+ return cache.get(argsString);
3370
+ } else {
3371
+ const result = fn.apply(this, args);
3372
+ cache.set(argsString, result);
3373
+ return result;
3346
3374
  }
3347
- });
3348
- return hashMap;
3349
- }
3375
+ };
3376
+ },
3350
3377
  /**
3351
- * 从JSON字符串创建HashMap实例
3378
+ * 递归调用函数,以一定的频率和持续时间执行。
3352
3379
  *
3353
- * @param str JSON字符串
3354
- * @returns HashMap实例
3380
+ * @param fun 要递归调用的函数。
3381
+ * @param frequency 每次调用函数之间的时间间隔,单位为毫秒,默认为500毫秒。
3382
+ * @param duration 函数递归调用的总时长,单位为毫秒,默认为5000毫秒。
3355
3383
  */
3356
- static fromJson(str) {
3357
- const json = ObjectUtil.parse(str);
3358
- return new HashMap(Object.entries(json));
3359
- }
3360
- }
3361
- class WebSocketClient extends EventDispatcher {
3362
- constructor(url = "ws://127.0.0.1:10088") {
3363
- super();
3364
- __publicField(this, "maxCheckTimes", 10);
3365
- __publicField(this, "url");
3366
- __publicField(this, "checkTimes", 0);
3367
- __publicField(this, "connectStatus", false);
3368
- __publicField(this, "client", null);
3369
- this.maxCheckTimes = 10;
3370
- this.url = url;
3371
- this.checkTimes = 0;
3372
- this.connect();
3373
- this.connCheckStatus(this.maxCheckTimes);
3374
- }
3375
- connect() {
3376
- this.disconnect();
3377
- if (this.url) {
3378
- try {
3379
- console.info("创建ws连接>>>" + this.url);
3380
- this.client = new WebSocket(this.url);
3381
- if (this.client) {
3382
- const self = this;
3383
- this.client.onopen = function(message) {
3384
- self.dispatchEvent({
3385
- type: EventType.WEB_SOCKET_CONNECT,
3386
- message
3387
- });
3388
- };
3389
- this.client.onmessage = function(message) {
3390
- self.connectStatus = true;
3391
- self.dispatchEvent({
3392
- type: EventType.WEB_SOCKET_MESSAGE,
3393
- message
3394
- });
3395
- };
3396
- this.client.onclose = function(message) {
3397
- self.dispatchEvent({
3398
- type: EventType.WEB_SOCKET_CLOSE,
3399
- message
3400
- });
3401
- };
3402
- if (this.checkTimes === this.maxCheckTimes) {
3403
- this.client.onerror = function(message) {
3404
- self.dispatchEvent({
3405
- type: EventType.WEB_SOCKET_ERROR,
3406
- message
3407
- });
3408
- };
3409
- }
3410
- }
3411
- } catch (ex) {
3412
- console.error("创建ws连接失败" + this.url + ":" + ex);
3384
+ recurve(fun, frequency = 500, duration = 5e3) {
3385
+ let timer = 0;
3386
+ setTimeout(() => {
3387
+ timer++;
3388
+ if (timer < Math.floor(duration / frequency)) {
3389
+ fun.call(this);
3390
+ setTimeout(this.recurve.bind(this, fun, frequency, duration), frequency);
3413
3391
  }
3414
- }
3415
- }
3416
- disconnect() {
3417
- if (this.client) {
3418
- try {
3419
- console.log("ws断开连接" + this.url);
3420
- this.client.close();
3421
- this.client = null;
3422
- } catch (ex) {
3423
- this.client = null;
3392
+ }, frequency);
3393
+ },
3394
+ /**
3395
+ * 确保函数只被调用一次
3396
+ *
3397
+ * @param func 要被调用的函数
3398
+ * @returns 返回一个新的函数,该函数在被首次调用时会执行传入的函数,之后再次调用将不再执行
3399
+ */
3400
+ once(func) {
3401
+ let called = false;
3402
+ return function(...args) {
3403
+ if (!called) {
3404
+ called = true;
3405
+ return func(...args);
3424
3406
  }
3425
- }
3407
+ };
3426
3408
  }
3427
- connCheckStatus(times) {
3428
- if (this.checkTimes > times) return;
3429
- setTimeout(() => {
3430
- this.checkTimes++;
3431
- if (this.client && this.client.readyState !== 0 && this.client.readyState !== 1) {
3432
- this.connect();
3409
+ };
3410
+ const UrlUtil = {
3411
+ /**
3412
+ * 将json对象转换为查询字符串
3413
+ *
3414
+ * @param json 待转换的json对象
3415
+ * @returns 转换后的查询字符串
3416
+ */
3417
+ json2Query(json) {
3418
+ var tempArr = [];
3419
+ for (var i in json) {
3420
+ if (json.hasOwnProperty(i)) {
3421
+ var key = i;
3422
+ var value = json[i];
3423
+ tempArr.push(encodeURIComponent(key) + "=" + encodeURIComponent(value));
3433
3424
  }
3434
- this.connCheckStatus(times);
3435
- }, 2e3);
3436
- }
3437
- send(message) {
3438
- if (this.client && this.client.readyState === 1) {
3439
- this.client.send(message);
3440
- return true;
3441
3425
  }
3442
- console.error(this.url + "消息发送失败:" + message);
3443
- return this;
3444
- }
3445
- heartbeat() {
3446
- setTimeout(() => {
3447
- if (this.client && this.client.readyState === 1) {
3448
- this.send("HeartBeat");
3426
+ var urlParamsStr = tempArr.join("&");
3427
+ return urlParamsStr;
3428
+ },
3429
+ /**
3430
+ * URL 中解析出查询参数和哈希参数,并以对象的形式返回
3431
+ *
3432
+ * @param href 需要解析的 URL 链接,默认为当前窗口的 URL
3433
+ * @param needDecode 是否需要解码参数值,默认为 true
3434
+ * @returns 返回一个包含解析后参数的对象,其中键为参数名,值为参数值
3435
+ */
3436
+ query2Json(href = window.location.href, needDecode = true) {
3437
+ const reg = /([^&=]+)=([\w\W]*?)(&|$|#)/g;
3438
+ const { search, hash } = new URL(href);
3439
+ const args = [search, hash];
3440
+ let obj = {};
3441
+ for (let i = 0; i < args.length; i++) {
3442
+ const str = args[i];
3443
+ if (str) {
3444
+ const s = str.replace(/#|\//g, "");
3445
+ const arr = s.split("?");
3446
+ if (arr.length > 1) {
3447
+ for (let j = 1; j < arr.length; j++) {
3448
+ let res;
3449
+ while (res = reg.exec(arr[j])) {
3450
+ obj[res[1]] = needDecode ? decodeURIComponent(res[2]) : res[2];
3451
+ }
3452
+ }
3453
+ }
3449
3454
  }
3450
- console.log("HeartBeat," + this.url);
3451
- setTimeout(this.heartbeat, 3e4);
3452
- }, 1e3);
3455
+ }
3456
+ return obj;
3453
3457
  }
3454
- }
3458
+ };
3455
3459
  const _MqttClient = class _MqttClient extends EventDispatcher {
3456
3460
  constructor(url = `ws://${window.document.domain}:20007/mqtt`, config = {}) {
3457
3461
  super();
@@ -3461,10 +3465,10 @@ const _MqttClient = class _MqttClient extends EventDispatcher {
3461
3465
  __publicField(this, "options");
3462
3466
  __publicField(this, "client");
3463
3467
  __publicField(this, "topics");
3464
- this.context = CommUtils.extend(_MqttClient.defaultContext, config);
3468
+ this.context = Util.extend(_MqttClient.defaultContext, config);
3465
3469
  this.options = {
3466
3470
  connectTimeout: this.context.MQTT_TIMEOUTM,
3467
- clientId: CommUtils.guid(),
3471
+ clientId: Util.guid(),
3468
3472
  username: this.context.MQTT_USERNAME,
3469
3473
  password: this.context.MQTT_PASSWORD,
3470
3474
  clean: true
@@ -3657,7 +3661,7 @@ const _Storage = class _Storage {
3657
3661
  }
3658
3662
  }
3659
3663
  };
3660
- __publicField(_Storage, "store", window.localStorage);
3664
+ __publicField(_Storage, "store", window.sessionStorage);
3661
3665
  __publicField(_Storage, "prefix", "");
3662
3666
  __publicField(_Storage, "_getPrefixedKey", function(key, options) {
3663
3667
  options = options || {};
@@ -3691,7 +3695,7 @@ export {
3691
3695
  ImageUtil,
3692
3696
  LayerType,
3693
3697
  LineSymbol,
3694
- MathUtils as MathUtil,
3698
+ MathUtil,
3695
3699
  MeasureMode,
3696
3700
  MessageUtil,
3697
3701
  MqttClient,
@@ -3701,6 +3705,6 @@ export {
3701
3705
  Storage,
3702
3706
  StringUtil,
3703
3707
  UrlUtil,
3704
- CommUtils as Util,
3708
+ Util,
3705
3709
  WebSocketClient
3706
3710
  };