gis-common 4.2.3 → 4.2.5

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.
@@ -37,16 +37,20 @@ var ErrorType = /* @__PURE__ */ ((ErrorType2) => {
37
37
  ErrorType2["AUTH_VERIFY_ERROR"] = "权限验证失败";
38
38
  ErrorType2["NO_DATA_FOUND"] = "未找到数据";
39
39
  ErrorType2["DUPLICATE_INSTANCE"] = "实例为单例模式,不允许重复构建";
40
- ErrorType2["COORDINATE_ERROR"] = "坐标验证失败";
41
40
  ErrorType2["JSON_PARSE_ERROR"] = "JSON解析失败,格式有误";
42
41
  ErrorType2["JSON_VALUE_ERROR"] = "JSON无此键";
43
- ErrorType2["PARAMETER_ERROR"] = "验证数据类型失败";
44
- ErrorType2["PARAMETER_ERROR_ARRAY"] = "格式类型验证失败:必须是数组";
45
- ErrorType2["PARAMETER_ERROR_STRING"] = "格式类型验证失败:必须是字符";
46
- ErrorType2["PARAMETER_ERROR_FUNCTION"] = "格式类型验证失败:必须是函数";
47
- ErrorType2["PARAMETER_ERROR_OBJECT"] = "格式类型验证失败:必须是对象";
48
- ErrorType2["PARAMETER_ERROR_LACK"] = "参数缺失";
49
42
  ErrorType2["STRING_CHECK_LOSS"] = "字符缺少关键字";
43
+ ErrorType2["PARAMETER_ERROR"] = "验证数据类型失败";
44
+ ErrorType2["PARAMETER_ERROR_ARRAY"] = "验证数据类型失败,必须是数组";
45
+ ErrorType2["PARAMETER_ERROR_STRING"] = "验证数据类型失败,必须是字符";
46
+ ErrorType2["PARAMETER_ERROR_FUNCTION"] = "验证数据类型失败,必须是函数";
47
+ ErrorType2["PARAMETER_ERROR_OBJECT"] = "验证数据类型失败,必须是对象";
48
+ ErrorType2["PARAMETER_ERROR_NUMBER"] = "验证数据类型失败,必须是数值";
49
+ ErrorType2["PARAMETER_ERROR_LACK"] = "验证数据类型失败,必须非空";
50
+ ErrorType2["DATA_ERROR"] = "格式类型验证失败";
51
+ ErrorType2["DATA_ERROR_COORDINATE"] = "格式类型验证失败,必须是坐标";
52
+ ErrorType2["DATA_ERROR_COLOR"] = "格式类型验证失败,必须是颜色代码";
53
+ ErrorType2["DATA_ERROR_GEOJSON"] = "格式类型验证失败,必须是GeoJSON";
50
54
  return ErrorType2;
51
55
  })(ErrorType || {});
52
56
  var LayerType = /* @__PURE__ */ ((LayerType2) => {
@@ -161,580 +165,268 @@ class Cookie {
161
165
  }
162
166
  }
163
167
  }
164
- const MathUtils = {
165
- DEG2RAD: Math.PI / 180,
166
- RAD2DEG: 180 / Math.PI,
167
- randInt(low, high) {
168
- return low + Math.floor(Math.random() * (high - low + 1));
168
+ const CommUtils = {
169
+ /**
170
+ * 获取数据类型
171
+ *
172
+ * @param data 待判断的数据
173
+ * @returns 返回数据类型字符串
174
+ */
175
+ getDataType(data) {
176
+ return Object.prototype.toString.call(data).slice(8, -1);
169
177
  },
170
- randFloat(low, high) {
171
- return low + Math.random() * (high - low);
178
+ asArray(obj) {
179
+ return this.isEmpty(obj) ? [] : Array.isArray(obj) ? obj : [obj];
180
+ },
181
+ asNumber(a) {
182
+ return Number.isNaN(Number(a)) ? 0 : Number(a);
172
183
  },
173
184
  /**
174
- * 角度转弧度
185
+ * 将值转换为字符串
175
186
  *
176
- * @param {*} degrees
177
- * @returns {*}
187
+ * @param value 要转换的值
188
+ * @returns 转换后的字符串,如果值为空,则返回空字符串
178
189
  */
179
- deg2Rad(degrees) {
180
- return degrees * this.DEG2RAD;
190
+ asString(value) {
191
+ if (this.isEmpty(value)) {
192
+ return "";
193
+ } else {
194
+ switch (this.getDataType(value)) {
195
+ case "Object":
196
+ case "Array":
197
+ return JSON.stringify(value);
198
+ default:
199
+ return value;
200
+ }
201
+ }
181
202
  },
182
203
  /**
183
- * 弧度转角度
204
+ * 判断传入的值是否为空
184
205
  *
185
- * @param {*} radians
186
- * @returns {*}
206
+ * @param value 待判断的值
207
+ * @returns 返回布尔值,表示是否为空
187
208
  */
188
- rad2Deg(radians) {
189
- return radians * this.RAD2DEG;
209
+ isEmpty(value) {
210
+ if (value == null) {
211
+ return true;
212
+ }
213
+ const type = this.getDataType(value);
214
+ switch (type) {
215
+ case "String":
216
+ return value.trim() === "";
217
+ case "Array":
218
+ return !value.length;
219
+ case "Object":
220
+ return !Object.keys(value).length;
221
+ case "Boolean":
222
+ return !value;
223
+ default:
224
+ return false;
225
+ }
190
226
  },
191
- round(value, n = 2) {
192
- return Math.round(value * Math.pow(10, n)) / Math.pow(10, n);
227
+ /**
228
+ * 将JSON对象转换为FormData对象
229
+ *
230
+ * @param json 待转换的JSON对象,其属性值为字符串或Blob类型
231
+ * @returns 转换后的FormData对象
232
+ */
233
+ json2form(json) {
234
+ const formData = new FormData();
235
+ Object.keys(json).forEach((key) => {
236
+ formData.append(key, json[key] instanceof Object ? JSON.stringify(json[key]) : json[key]);
237
+ });
238
+ return formData;
193
239
  },
194
240
  /**
195
- * 将数值限制在指定范围内
241
+ * 生成GUID
196
242
  *
197
- * @param val 需要限制的数值
198
- * @param min 最小值
199
- * @param max 最大值
200
- * @returns 返回限制后的数值
243
+ * @returns 返回一个由8个16进制数组成的GUID字符串
201
244
  */
202
- clamp(val, min, max) {
203
- return Math.min(Math.max(val, min), max);
204
- }
205
- };
206
- class CanvasDrawer {
207
- constructor(el) {
208
- __publicField(this, "context", null);
209
- if (typeof el === "string") {
210
- el = document.querySelector("#" + el);
211
- if (!el) {
212
- throw new Error("Element not found");
245
+ guid() {
246
+ const S4 = function() {
247
+ return ((1 + Math.random()) * 65536 | 0).toString(16).substring(1);
248
+ };
249
+ return S4() + S4() + S4() + S4() + S4() + S4() + S4() + S4();
250
+ },
251
+ /**
252
+ * 将参数进行解码并返回解码后的字符串
253
+ *
254
+ * @param args 参数
255
+ * @returns 解码后的字符串
256
+ */
257
+ decodeDict(...args) {
258
+ let res = "";
259
+ if (args.length > 1) {
260
+ const items = args.slice(1, args.length % 2 === 0 ? args.length - 1 : args.length);
261
+ for (let i = 0; i < items.length; i = i + 2) {
262
+ const item = items[i];
263
+ if (args[0] === item) {
264
+ res = items[i + 1];
265
+ }
213
266
  }
214
- }
215
- if (el instanceof HTMLElement) {
216
- const canvas = el;
217
- if (canvas.getContext) {
218
- this.context = canvas.getContext("2d");
219
- } else {
220
- throw new Error("getContext is not available on this element");
267
+ if (!res && args.length % 2 === 0) {
268
+ res = args[args.length - 1];
221
269
  }
222
270
  } else {
223
- throw new Error("Element is not an HTMLElement");
271
+ res = args[0];
224
272
  }
225
- }
273
+ return res;
274
+ },
226
275
  /**
227
- * 绘制线条
276
+ * 将一个或多个对象的所有可枚举属性复制到目标对象。
228
277
  *
229
- * @param start 起始坐标点
230
- * @param end 终止坐标点
231
- * @param options 绘制选项,包括线条宽度和颜色
232
- * @throws 当画布上下文不存在时抛出错误
278
+ * @param dest 目标对象,用于接收复制的属性。
279
+ * @param args 一个或多个源对象,用于提供要复制的属性。
280
+ * @returns 返回目标对象,包含所有复制的属性。
233
281
  */
234
- drawLine({ x: startX, y: startY }, { x: endX, y: endY }, options = {}) {
235
- if (!this.context) {
236
- throw new Error("Canvas context is null or undefined");
282
+ extend(dest, ...args) {
283
+ let i, j, len, src;
284
+ for (j = 0, len = args.length; j < len; j++) {
285
+ src = args[j];
286
+ for (i in src) {
287
+ dest[i] = src[i];
288
+ }
237
289
  }
238
- this.context.beginPath();
239
- const width = options.width || 1;
240
- const color = options.color || "#000";
241
- this.context.lineWidth = width;
242
- this.context.strokeStyle = color;
243
- this.context.moveTo(startX, startY);
244
- this.context.lineTo(endX, endY);
245
- this.context.stroke();
246
- }
290
+ return dest;
291
+ },
247
292
  /**
248
- * 绘制圆弧
293
+ * 将扁平化数组转换为树形结构数组
249
294
  *
250
- * @param x 圆心x坐标
251
- * @param y 圆心y坐标
252
- * @param radius 半径
253
- * @param startAngle 起始角度(度)
254
- * @param endAngle 结束角度(度)
255
- * @param anticlockwise 是否逆时针绘制
256
- * @param isFill 是否填充
257
- * @param bgColor 背景颜色
258
- * @throws 当Canvas context为null或undefined时抛出错误
295
+ * @param data 扁平化数组
296
+ * @param idPropertyName 数据中标识id的字段名,默认为'id'
297
+ * @param parentIdPropertyName 数据中标识父节点id的字段名,默认为'parentId'
298
+ * @param childrenPropertyName 树形结构中标识子节点的字段名,默认为'children'
299
+ * @returns 转换后的树形结构数组
259
300
  */
260
- drawArc({ x, y }, radius, startAngle, endAngle, anticlockwise, isFill, bgColor) {
261
- if (!this.context) {
262
- throw new Error("Canvas context is null or undefined");
263
- }
264
- if (isFill) {
265
- this.context.fillStyle = bgColor;
266
- this.context.beginPath();
267
- this.context.arc(x, y, radius, MathUtils.deg2Rad(startAngle), MathUtils.deg2Rad(endAngle), anticlockwise);
268
- this.context.fill();
269
- } else {
270
- this.context.strokeStyle = bgColor;
271
- this.context.beginPath();
272
- this.context.arc(x, y, radius, MathUtils.deg2Rad(startAngle), MathUtils.deg2Rad(endAngle), anticlockwise);
273
- this.context.stroke();
274
- }
275
- }
276
- static createCanvas(width = 1, height = 1) {
277
- const canvas = document.createElement("canvas");
278
- if (width) {
279
- canvas.width = width;
280
- }
281
- if (height) {
282
- canvas.height = height;
283
- }
284
- return canvas;
285
- }
286
- }
287
- class EventDispatcher {
288
- constructor() {
289
- __publicField(this, "_listeners");
290
- __publicField(this, "_mutex", {});
291
- __publicField(this, "_context");
292
- }
293
- addEventListener(type, listener, context, mutexStatus) {
294
- if (this._listeners === void 0) this._listeners = {};
295
- this._context = context;
296
- const mutex = this._mutex;
297
- const listeners = this._listeners;
298
- if (listeners[type] === void 0) {
299
- listeners[type] = [];
300
- }
301
- if (listeners[type].indexOf(listener) === -1) {
302
- if (mutexStatus) {
303
- mutex[type] = listener;
304
- }
305
- listeners[type].push(listener);
306
- }
307
- return this;
308
- }
309
- hasEventListener(type, listener) {
310
- if (this._listeners === null || this._listeners === void 0) return false;
311
- const listeners = this._listeners;
312
- return listeners[type] !== void 0 && listeners[type].indexOf(listener) !== -1;
313
- }
314
- removeEventListener(type, listener) {
315
- if (this._listeners === void 0) return;
316
- const listeners = this._listeners;
317
- const listenerArray = listeners[type];
318
- if (this._mutex[type] === listener) {
319
- this._mutex[type] = null;
320
- }
321
- if (listenerArray !== void 0) {
322
- const index = listenerArray.map((d) => d.toString()).indexOf(listener.toString());
323
- if (index !== -1) {
324
- listenerArray.splice(index, 1);
301
+ convertToTree2(data, idPropertyName = "id", parentIdPropertyName = "parentId", childrenPropertyName = "children") {
302
+ const result = [];
303
+ function buildChildren(item) {
304
+ const children = data.filter((item2) => item2[parentIdPropertyName] === item[idPropertyName]).map((child) => {
305
+ if (!result.some((r) => r[idPropertyName] === child[idPropertyName])) {
306
+ buildChildren(child);
307
+ }
308
+ return child;
309
+ });
310
+ if (children.length > 0) {
311
+ item[childrenPropertyName] = children;
325
312
  }
326
313
  }
327
- }
328
- dispatchEvent(event) {
329
- if (this._listeners === void 0) return;
330
- const listeners = this._listeners;
331
- const listenerArray = listeners[event.type];
332
- if (listenerArray !== void 0) {
333
- event.target = this;
334
- const array = listenerArray.slice(0);
335
- if (this._mutex[event.type] !== void 0) {
336
- const find = array.find((item) => item === this._mutex[event.type]);
337
- if (find) {
338
- find.call(this._context || this, event);
339
- return;
340
- }
341
- }
342
- for (let i = 0, l = array.length; i < l; i++) {
343
- const item = array[i];
344
- if (typeof item === "function") {
345
- item.call(this._context || this, event);
346
- }
314
+ data.forEach((item) => {
315
+ if (!data.some((other) => other[parentIdPropertyName] === item[idPropertyName])) {
316
+ buildChildren(item);
317
+ result.push(item);
347
318
  }
348
- }
349
- }
350
- removeAllListener() {
351
- this._mutex = {};
352
- for (const key in this._listeners) {
353
- this._listeners[key] = [];
354
- }
355
- }
356
- }
357
- class HashMap extends Map {
358
- isEmpty() {
359
- return this.size === 0;
360
- }
361
- _values() {
362
- return Array.from(this.values());
363
- }
364
- _keys() {
365
- return Array.from(this.keys());
366
- }
367
- _entries() {
368
- return Array.from(this.entries());
369
- }
370
- fromEntries() {
371
- }
372
- }
373
- HashMap.prototype.fromEntries = function(array = []) {
374
- const hashMap = new HashMap();
375
- array.forEach((element) => {
376
- if (Array.isArray(element) && element.length === 2) {
377
- hashMap.set(element[0], element[1]);
378
- }
379
- });
380
- return hashMap;
381
- };
382
- class WebSocketClient extends EventDispatcher {
383
- constructor(url = "ws://127.0.0.1:10088") {
384
- super();
385
- __publicField(this, "maxCheckTimes", 10);
386
- __publicField(this, "url");
387
- __publicField(this, "checkTimes", 0);
388
- __publicField(this, "connectStatus", false);
389
- __publicField(this, "client", null);
390
- this.maxCheckTimes = 10;
391
- this.url = url;
392
- this.checkTimes = 0;
393
- this.connect();
394
- this.connCheckStatus(this.maxCheckTimes);
395
- }
396
- connect() {
397
- this.disconnect();
398
- if (this.url) {
319
+ });
320
+ return result;
321
+ },
322
+ /**
323
+ * 异步加载script
324
+ *
325
+ * @param {*} url
326
+ */
327
+ asyncLoadScript(url) {
328
+ return new Promise((resolve, reject) => {
399
329
  try {
400
- console.info("创建ws连接>>>" + this.url);
401
- this.client = new WebSocket(this.url);
402
- if (this.client) {
403
- const self = this;
404
- this.client.onopen = function(message) {
405
- self.dispatchEvent({
406
- type: EventType.WEB_SOCKET_CONNECT,
407
- message
408
- });
330
+ const oscript = document.createElement("script");
331
+ oscript.type = "text/javascript";
332
+ oscript.src = url;
333
+ if ("readyState" in oscript) {
334
+ oscript.onreadystatechange = function() {
335
+ if (oscript.readyState === "complete" || oscript.readyState === "loaded") {
336
+ resolve(oscript);
337
+ }
409
338
  };
410
- this.client.onmessage = function(message) {
411
- self.connectStatus = true;
412
- self.dispatchEvent({
413
- type: EventType.WEB_SOCKET_MESSAGE,
414
- message
415
- });
339
+ } else {
340
+ oscript.onload = function() {
341
+ resolve(oscript);
416
342
  };
417
- this.client.onclose = function(message) {
418
- self.dispatchEvent({
419
- type: EventType.WEB_SOCKET_CLOSE,
420
- message
421
- });
343
+ oscript.onerror = function() {
344
+ reject(new Error("Script failed to load for URL: " + url));
422
345
  };
423
- if (this.checkTimes === this.maxCheckTimes) {
424
- this.client.onerror = function(message) {
425
- self.dispatchEvent({
426
- type: EventType.WEB_SOCKET_ERROR,
427
- message
428
- });
429
- };
430
- }
431
346
  }
432
- } catch (ex) {
433
- console.error("创建ws连接失败" + this.url + ":" + ex);
434
- }
435
- }
436
- }
437
- disconnect() {
438
- if (this.client) {
439
- try {
440
- console.log("ws断开连接" + this.url);
441
- this.client.close();
442
- this.client = null;
443
- } catch (ex) {
444
- this.client = null;
445
- }
446
- }
447
- }
448
- connCheckStatus(times) {
449
- if (this.checkTimes > times) return;
450
- setTimeout(() => {
451
- this.checkTimes++;
452
- if (this.client && this.client.readyState !== 0 && this.client.readyState !== 1) {
453
- this.connect();
454
- }
455
- this.connCheckStatus(times);
456
- }, 2e3);
457
- }
458
- send(message) {
459
- if (this.client && this.client.readyState === 1) {
460
- this.client.send(message);
461
- return true;
462
- }
463
- console.error(this.url + "消息发送失败:" + message);
464
- return false;
465
- }
466
- heartbeat() {
467
- setTimeout(() => {
468
- if (this.client && this.client.readyState === 1) {
469
- this.send("HeartBeat");
347
+ document.body.appendChild(oscript);
348
+ } catch (error) {
349
+ reject(error);
470
350
  }
471
- console.log("HeartBeat," + this.url);
472
- setTimeout(this.heartbeat, 3e4);
473
- }, 1e3);
474
- }
475
- }
476
- const CommUtil = {
351
+ });
352
+ },
477
353
  /**
478
- * 获取数据类型
354
+ * 加载样式文件
479
355
  *
480
- * @param data 待判断的数据
481
- * @returns 返回数据类型字符串
356
+ * @param urls 样式文件URL数组
357
+ * @returns 无返回值
482
358
  */
483
- getDataType(data) {
484
- return Object.prototype.toString.call(data).slice(8, -1);
485
- },
486
- asArray(obj) {
487
- return this.isEmpty(obj) ? [] : Array.isArray(obj) ? obj : [obj];
359
+ loadStyle(urls) {
360
+ urls.forEach((url) => {
361
+ const css = document.createElement("link");
362
+ css.href = url;
363
+ css.rel = "stylesheet";
364
+ css.type = "text/css";
365
+ css.onerror = function() {
366
+ console.error(`Style loading failed for URL: ${url}`);
367
+ };
368
+ document.head.appendChild(css);
369
+ });
488
370
  },
489
- asNumber(a) {
490
- return Number.isNaN(Number(a)) ? 0 : Number(a);
371
+ /**
372
+ * 将模板字符串中的占位符替换为给定对象中的值
373
+ *
374
+ * @param str 模板字符串
375
+ * @param data 包含替换值的对象
376
+ * @returns 替换后的字符串
377
+ * @throws 当对象中没有找到与占位符对应的值时,抛出错误
378
+ */
379
+ template(str, data) {
380
+ const templateRe = /\{ *([\w_-]+) *\}/g;
381
+ return str.replace(templateRe, (match, key) => {
382
+ const value = data[key];
383
+ if (value === void 0) {
384
+ throw new Error(`${ErrorType.JSON_VALUE_ERROR}: ${match}`);
385
+ } else if (typeof value === "function") {
386
+ return value(data);
387
+ } else {
388
+ return value;
389
+ }
390
+ });
491
391
  },
492
392
  /**
493
- * 将值转换为字符串
393
+ * 删除对象中所有值为空的属性
494
394
  *
495
- * @param value 要转换的值
496
- * @returns 转换后的字符串,如果值为空,则返回空字符串
395
+ * @param data 待处理的对象
396
+ * @returns 返回处理后的对象
497
397
  */
498
- asString(value) {
499
- if (this.isEmpty(value)) {
500
- return "";
501
- } else {
502
- switch (this.getDataType(value)) {
503
- case "Object":
504
- case "Array":
505
- return JSON.stringify(value);
506
- default:
507
- return value;
398
+ deleteEmptyProperty(data) {
399
+ return Object.fromEntries(
400
+ Object.keys(data).filter((d) => !this.isEmpty(data[d])).map((i) => [i, data[i]])
401
+ );
402
+ },
403
+ deepAssign(target, ...sources) {
404
+ if (typeof target !== "object" || target === null) {
405
+ target = {};
406
+ }
407
+ for (const source of sources) {
408
+ if (typeof source === "object" && source !== null) {
409
+ for (const key in source) {
410
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
411
+ if (typeof source[key] === "object" && source[key] !== null) {
412
+ if (!target[key]) {
413
+ target[key] = Array.isArray(source[key]) ? [] : {};
414
+ }
415
+ this.deepAssign(target[key], source[key]);
416
+ } else {
417
+ target[key] = source[key];
418
+ }
419
+ }
420
+ }
508
421
  }
509
422
  }
423
+ return target;
510
424
  },
511
425
  /**
512
- * 判断传入的值是否为空
426
+ * 复制文本到剪贴板
513
427
  *
514
- * @param value 待判断的值
515
- * @returns 返回布尔值,表示是否为空
516
- */
517
- isEmpty(value) {
518
- if (value == null) {
519
- return true;
520
- }
521
- const type = this.getDataType(value);
522
- switch (type) {
523
- case "String":
524
- return value.trim() === "";
525
- case "Array":
526
- return !value.length;
527
- case "Object":
528
- return !Object.keys(value).length;
529
- case "Boolean":
530
- return !value;
531
- default:
532
- return false;
533
- }
534
- },
535
- /**
536
- * 将JSON对象转换为FormData对象
537
- *
538
- * @param json 待转换的JSON对象,其属性值为字符串或Blob类型
539
- * @returns 转换后的FormData对象
540
- */
541
- json2form(json) {
542
- const formData = new FormData();
543
- Object.keys(json).forEach((key) => {
544
- formData.append(key, json[key] instanceof Object ? JSON.stringify(json[key]) : json[key]);
545
- });
546
- return formData;
547
- },
548
- /**
549
- * 生成GUID
550
- *
551
- * @returns 返回一个由8个16进制数组成的GUID字符串
552
- */
553
- guid() {
554
- const S4 = function() {
555
- return ((1 + Math.random()) * 65536 | 0).toString(16).substring(1);
556
- };
557
- return S4() + S4() + S4() + S4() + S4() + S4() + S4() + S4();
558
- },
559
- /**
560
- * 将参数进行解码并返回解码后的字符串
561
- *
562
- * @param args 参数
563
- * @returns 解码后的字符串
564
- */
565
- decodeDict(...args) {
566
- let res = "";
567
- if (args.length > 1) {
568
- const items = args.slice(1, args.length % 2 === 0 ? args.length - 1 : args.length);
569
- for (let i = 0; i < items.length; i = i + 2) {
570
- const item = items[i];
571
- if (args[0] === item) {
572
- res = items[i + 1];
573
- }
574
- }
575
- if (!res && args.length % 2 === 0) {
576
- res = args[args.length - 1];
577
- }
578
- } else {
579
- res = args[0];
580
- }
581
- return res;
582
- },
583
- /**
584
- * 将一个或多个对象的所有可枚举属性复制到目标对象。
585
- *
586
- * @param dest 目标对象,用于接收复制的属性。
587
- * @param args 一个或多个源对象,用于提供要复制的属性。
588
- * @returns 返回目标对象,包含所有复制的属性。
589
- */
590
- extend(dest, ...args) {
591
- let i, j, len, src;
592
- for (j = 0, len = args.length; j < len; j++) {
593
- src = args[j];
594
- for (i in src) {
595
- dest[i] = src[i];
596
- }
597
- }
598
- return dest;
599
- },
600
- /**
601
- * 将扁平化数组转换为树形结构数组
602
- *
603
- * @param data 扁平化数组
604
- * @param idPropertyName 数据中标识id的字段名,默认为'id'
605
- * @param parentIdPropertyName 数据中标识父节点id的字段名,默认为'parentId'
606
- * @param childrenPropertyName 树形结构中标识子节点的字段名,默认为'children'
607
- * @returns 转换后的树形结构数组
608
- */
609
- convertToTree2(data, idPropertyName = "id", parentIdPropertyName = "parentId", childrenPropertyName = "children") {
610
- const result = [];
611
- function buildChildren(item) {
612
- const children = data.filter((item2) => item2[parentIdPropertyName] === item[idPropertyName]).map((child) => {
613
- if (!result.some((r) => r[idPropertyName] === child[idPropertyName])) {
614
- buildChildren(child);
615
- }
616
- return child;
617
- });
618
- if (children.length > 0) {
619
- item[childrenPropertyName] = children;
620
- }
621
- }
622
- data.forEach((item) => {
623
- if (!data.some((other) => other[parentIdPropertyName] === item[idPropertyName])) {
624
- buildChildren(item);
625
- result.push(item);
626
- }
627
- });
628
- return result;
629
- },
630
- /**
631
- * 异步加载script
632
- *
633
- * @param {*} url
634
- */
635
- asyncLoadScript(url) {
636
- return new Promise((resolve, reject) => {
637
- try {
638
- const oscript = document.createElement("script");
639
- oscript.type = "text/javascript";
640
- oscript.src = url;
641
- if ("readyState" in oscript) {
642
- oscript.onreadystatechange = function() {
643
- if (oscript.readyState === "complete" || oscript.readyState === "loaded") {
644
- resolve(oscript);
645
- }
646
- };
647
- } else {
648
- oscript.onload = function() {
649
- resolve(oscript);
650
- };
651
- oscript.onerror = function() {
652
- reject(new Error("Script failed to load for URL: " + url));
653
- };
654
- }
655
- document.body.appendChild(oscript);
656
- } catch (error) {
657
- reject(error);
658
- }
659
- });
660
- },
661
- /**
662
- * 加载样式文件
663
- *
664
- * @param urls 样式文件URL数组
665
- * @returns 无返回值
666
- */
667
- loadStyle(urls) {
668
- urls.forEach((url) => {
669
- const css = document.createElement("link");
670
- css.href = url;
671
- css.rel = "stylesheet";
672
- css.type = "text/css";
673
- css.onerror = function() {
674
- console.error(`Style loading failed for URL: ${url}`);
675
- };
676
- document.head.appendChild(css);
677
- });
678
- },
679
- /**
680
- * 将模板字符串中的占位符替换为给定对象中的值
681
- *
682
- * @param str 模板字符串
683
- * @param data 包含替换值的对象
684
- * @returns 替换后的字符串
685
- * @throws 当对象中没有找到与占位符对应的值时,抛出错误
686
- */
687
- template(str, data) {
688
- const templateRe = /\{ *([\w_-]+) *\}/g;
689
- return str.replace(templateRe, (match, key) => {
690
- const value = data[key];
691
- if (value === void 0) {
692
- throw new Error(`${ErrorType.JSON_VALUE_ERROR}: ${match}`);
693
- } else if (typeof value === "function") {
694
- return value(data);
695
- } else {
696
- return value;
697
- }
698
- });
699
- },
700
- /**
701
- * 删除对象中所有值为空的属性
702
- *
703
- * @param data 待处理的对象
704
- * @returns 返回处理后的对象
705
- */
706
- deleteEmptyProperty(data) {
707
- return Object.fromEntries(
708
- Object.keys(data).filter((d) => !this.isEmpty(data[d])).map((i) => [i, data[i]])
709
- );
710
- },
711
- deepAssign(target, ...sources) {
712
- if (typeof target !== "object" || target === null) {
713
- target = {};
714
- }
715
- for (const source of sources) {
716
- if (typeof source === "object" && source !== null) {
717
- for (const key in source) {
718
- if (Object.prototype.hasOwnProperty.call(source, key)) {
719
- if (typeof source[key] === "object" && source[key] !== null) {
720
- if (!target[key]) {
721
- target[key] = Array.isArray(source[key]) ? [] : {};
722
- }
723
- this.deepAssign(target[key], source[key]);
724
- } else {
725
- target[key] = source[key];
726
- }
727
- }
728
- }
729
- }
730
- }
731
- return target;
732
- },
733
- /**
734
- * 复制文本到剪贴板
735
- *
736
- * @param text 要复制的文本
737
- * @returns 返回一个Promise,表示复制操作的结果
428
+ * @param text 要复制的文本
429
+ * @returns 返回一个Promise,表示复制操作的结果
738
430
  */
739
431
  handleCopyValue(text) {
740
432
  if (navigator.clipboard && window.isSecureContext) {
@@ -814,434 +506,119 @@ const ObjectUtil = {
814
506
  return JSON.parse(str);
815
507
  }
816
508
  };
817
- const myArray = Object.create(Array);
818
- myArray.groupBy = function(f) {
819
- var groups = {};
820
- this.forEach(function(o) {
821
- var group = JSON.stringify(f(o));
822
- groups[group] = groups[group] || [];
823
- groups[group].push(o);
824
- });
825
- return Object.keys(groups).map((group) => groups[group]);
826
- };
827
- myArray.distinct = function(f = (d) => d) {
828
- const arr = [];
829
- const obj = {};
830
- this.forEach((item) => {
831
- const val = f(item);
832
- const key = String(val);
833
- if (!obj[key]) {
834
- obj[key] = true;
835
- arr.push(item);
509
+ const ImageUtil = {
510
+ emptyImageUrl: "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",
511
+ /**
512
+ *
513
+ * @param image image,类型可以是HTMLCanvasElement、ImageData
514
+ * @returns
515
+ */
516
+ getURL(image) {
517
+ let _canvas;
518
+ if (/^data:/i.test(image.src)) {
519
+ return image.src;
836
520
  }
837
- });
838
- return arr;
839
- };
840
- myArray.prototype.max = function() {
841
- return Math.max.apply({}, this);
842
- };
843
- myArray.prototype.min = function() {
844
- return Math.min.apply({}, this);
845
- };
846
- myArray.sum = function() {
847
- return this.length > 0 ? this.reduce((prev = 0, curr = 0) => prev + curr) : 0;
848
- };
849
- myArray.avg = function() {
850
- return this.length ? this.sum() / this.length : 0;
851
- };
852
- myArray.desc = function(f = (d) => d) {
853
- return this.sort((n1, n2) => f(n2) - f(n1));
854
- };
855
- myArray.asc = function(f = (d) => d) {
856
- return this.sort((n1, n2) => f(n1) - f(n2));
857
- };
858
- myArray.remove = function(obj) {
859
- const i = this.indexOf(obj);
860
- if (i > -1) {
861
- this.splice(i, 1);
862
- }
863
- return this;
864
- };
865
- const ArrayUtil = {
866
- /**
867
- * 创建指定长度的数组,并返回其索引数组
868
- *
869
- * @param length 数组长度
870
- * @returns 索引数组
871
- */
872
- create(length) {
873
- return [...new Array(length).keys()];
874
- },
875
- /**
876
- * 合并多个数组,并去重
877
- *
878
- * @param args 需要合并的数组
879
- * @returns 合并后的去重数组
880
- */
881
- union(...args) {
882
- let res = [];
883
- args.forEach((arg) => {
884
- if (Array.isArray(arg)) {
885
- res = res.concat(arg.filter((v) => !res.includes(v)));
521
+ if (typeof HTMLCanvasElement === "undefined") {
522
+ return image.src;
523
+ }
524
+ let canvas;
525
+ if (image instanceof HTMLCanvasElement) {
526
+ canvas = image;
527
+ } else {
528
+ if (_canvas === void 0) _canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
529
+ _canvas.width = image.width;
530
+ _canvas.height = image.height;
531
+ const context = _canvas.getContext("2d");
532
+ if (context) {
533
+ if (image instanceof ImageData) {
534
+ context.putImageData(image, 0, 0);
535
+ } else {
536
+ context.drawImage(image, 0, 0, image.width, image.height);
537
+ }
886
538
  }
887
- });
888
- return res;
539
+ canvas = _canvas;
540
+ }
541
+ if (canvas.width > 2048 || canvas.height > 2048) {
542
+ console.warn("ImageUtil.getDataURL: Image converted to jpg for performance reasons", image);
543
+ return canvas.toDataURL("image/jpeg", 0.6);
544
+ } else {
545
+ return canvas.toDataURL("image/png");
546
+ }
889
547
  },
890
548
  /**
891
- * 求多个数组的交集
549
+ * 将图片的URL转换为Base64编码
892
550
  *
893
- * @param args 多个需要求交集的数组
894
- * @returns 返回多个数组的交集数组
551
+ * @param url 图片的URL地址
552
+ * @param width 图片的宽度,默认为图片原始宽度
553
+ * @param height 图片的高度,默认为图片原始高度
554
+ * @returns 返回Promise对象,解析后得到包含Base64编码数据的对象
895
555
  */
896
- intersection(...args) {
897
- let res = args[0] || [];
898
- args.forEach((arg) => {
899
- if (Array.isArray(arg)) {
900
- res = res.filter((v) => arg.includes(v));
901
- }
556
+ getBase64(url) {
557
+ return new Promise((resolve, reject) => {
558
+ let image = new Image();
559
+ image.setAttribute("crossOrigin", "Anonymous");
560
+ image.src = url;
561
+ image.onload = () => {
562
+ let dataURL = this.getURL(image);
563
+ resolve(dataURL);
564
+ };
565
+ image.onerror = reject;
902
566
  });
903
- return res;
904
567
  },
905
568
  /**
906
- * 将多个数组拼接为一个数组,并去除其中的空值。
569
+ * 解析base64编码
907
570
  *
908
- * @param args 需要拼接的数组列表。
909
- * @returns 拼接并去空后的数组。
571
+ * @param base64 base64编码字符串
572
+ * @returns 返回一个对象,包含type(类型)、ext(扩展名)和data(数据)字段,如果解析失败则返回null
910
573
  */
911
- unionAll(...args) {
912
- return [...args].flat().filter((d) => !!d);
574
+ parseBase64(base64) {
575
+ let re = new RegExp("data:(?<type>.*?);base64,(?<data>.*)");
576
+ let res = re.exec(base64);
577
+ if (res && res.groups) {
578
+ return {
579
+ type: res.groups.type,
580
+ ext: res.groups.type.split("/").slice(-1)[0],
581
+ data: res.groups.data
582
+ };
583
+ }
584
+ return null;
913
585
  },
914
586
  /**
915
- * 求差集
587
+ * 复制图片到剪贴板
916
588
  *
917
- * @param args 任意个集合
918
- * @returns 返回差集结果
589
+ * @param url 图片的URL地址
590
+ * @returns 无返回值
591
+ * @throws 如果解析base64数据失败,则抛出异常
919
592
  */
920
- difference(...args) {
921
- if (args.length === 0) return [];
922
- return this.union(...args).filter((d) => !this.intersection(...args).includes(d));
923
- }
924
- };
925
- const _MqttClient = class _MqttClient extends EventDispatcher {
926
- constructor(url = `ws://${window.document.domain}:20007/mqtt`, config = {}) {
927
- super();
928
- __publicField(this, "state");
929
- __publicField(this, "url");
930
- __publicField(this, "context");
931
- __publicField(this, "options");
932
- __publicField(this, "client");
933
- __publicField(this, "topics");
934
- this.context = CommUtil.extend(_MqttClient.defaultContext, config);
935
- this.options = {
936
- connectTimeout: this.context.MQTT_TIMEOUTM,
937
- clientId: CommUtil.guid(),
938
- username: this.context.MQTT_USERNAME,
939
- password: this.context.MQTT_PASSWORD,
940
- clean: true
941
- };
942
- this.url = url;
943
- this.client = connect(this.url, this.options);
944
- this._onConnect();
945
- this._onMessage();
946
- this.state = 0;
947
- this.topics = [];
948
- }
949
- _onConnect() {
950
- this.client.on("connect", () => {
951
- this.state = 1;
952
- console.log("链接mqtt成功==>" + this.url);
953
- this.dispatchEvent({ type: EventType.MQTT_CONNECT, message: this });
954
- });
955
- this.client.on("error", (err) => {
956
- console.log("链接mqtt报错", err);
957
- this.state = -1;
958
- this.dispatchEvent({ type: EventType.MQTT_ERROR, message: this });
959
- this.client.end();
960
- this.client.reconnect();
961
- });
962
- }
963
- _onMessage() {
964
- this.client.on("message", (topic, message) => {
965
- let dataString = message;
966
- let data = "";
967
- if (message instanceof Uint8Array) {
968
- dataString = message.toString();
593
+ async copyImage(url) {
594
+ try {
595
+ const base64Result = await this.getBase64(url);
596
+ const parsedBase64 = this.parseBase64(base64Result.dataURL);
597
+ if (!parsedBase64) {
598
+ throw new Error("Failed to parse base64 data.");
969
599
  }
970
- try {
971
- data = ObjectUtil.parse(dataString);
972
- } catch (error) {
973
- throw new Error(ErrorType.JSON_PARSE_ERROR);
600
+ let type = parsedBase64.type;
601
+ let bytes = atob(parsedBase64.data);
602
+ let ab = new ArrayBuffer(bytes.length);
603
+ let ua = new Uint8Array(ab);
604
+ for (let i = 0; i < bytes.length; i++) {
605
+ ua[i] = bytes.charCodeAt(i);
974
606
  }
975
- this.dispatchEvent({
976
- type: EventType.MQTT_MESSAGE,
977
- message: { topic, data }
978
- });
979
- });
980
- }
981
- sendMsg(topic, msg) {
982
- if (!this.client.connected) {
983
- console.error("客户端未连接");
984
- return;
607
+ let blob = new Blob([ab], { type });
608
+ await navigator.clipboard.write([new ClipboardItem({ [type]: blob })]);
609
+ } catch (error) {
610
+ console.error("Failed to copy image to clipboard:", error);
985
611
  }
986
- this.client.publish(topic, msg, { qos: 1, retain: true });
987
- }
988
- subscribe(topic) {
989
- this.state === 1 ? this.client.subscribe(topic, { qos: 1 }, (error, e) => {
990
- error instanceof Error ? console.error("订阅失败==>" + topic, error) : (this.topics = ArrayUtil.union(this.topics, topic), console.log("订阅成功==>" + topic));
991
- }) : this.addEventListener(EventType.MQTT_CONNECT, (res) => {
992
- this.client.subscribe(topic, { qos: 1 }, (error, e) => {
993
- error instanceof Error ? console.error("订阅失败==>" + topic, error) : (this.topics = ArrayUtil.union(this.topics, topic), console.log("订阅成功==>" + topic));
994
- });
995
- });
996
- return this;
997
- }
998
- unsubscribe(topic) {
999
- this.client.unsubscribe(topic, { qos: 1 }, (error, res) => {
1000
- if (error instanceof Error) {
1001
- console.error(`取消订阅失败==>${topic}`, error);
1002
- } else {
1003
- this.topics = ArrayUtil.difference(this.topics, topic);
1004
- console.log(`取消订阅成功==>${topic}`);
1005
- }
1006
- });
1007
- return this;
1008
- }
1009
- unsubscribeAll() {
1010
- this.unsubscribe(this.topics);
1011
- }
1012
- unconnect() {
1013
- this.client.end();
1014
- this.client = null;
1015
- this.dispatchEvent({ type: EventType.MQTT_CLOSE, message: null });
1016
- console.log("断开mqtt成功==>" + this.url);
1017
612
  }
1018
613
  };
1019
- /**
1020
- * Creates an instance of MqttClient.
1021
- * @param {*} config mqtt实例参数
1022
- */
1023
- __publicField(_MqttClient, "defaultContext", {
1024
- MQTT_USERNAME: "iRVMS-WEB",
1025
- MQTT_PASSWORD: "novasky888",
1026
- MQTT_TIMEOUTM: 2e4
1027
- });
1028
- let MqttClient = _MqttClient;
1029
- const _Storage = class _Storage {
614
+ const AjaxUtil = {
1030
615
  /**
1031
- * 将键值对存储到localStorage中
1032
- *
1033
- * @param key 键名
1034
- * @param value 值,默认为null
1035
- * @param options 存储选项,可选参数
1036
- * @param options.expires 过期时间,单位为毫秒,默认为null
1037
- */
1038
- static set(key, value = null, options = {}) {
1039
- var query_key = this._getPrefixedKey(key, options);
1040
- try {
1041
- const { expires } = options;
1042
- const data = { data: value };
1043
- if (expires) {
1044
- data.expires = expires;
1045
- }
1046
- localStorage.setItem(query_key, JSON.stringify(data));
1047
- } catch (e) {
1048
- if (console) console.warn(`Storage didn't successfully save the '{"${key}": "${value}"}' pair, because the localStorage is full.`);
1049
- }
1050
- }
1051
- /**
1052
- * 从localStorage中获取指定key的存储值
1053
- *
1054
- * @param key 存储键名
1055
- * @param missing 当获取不到指定key的存储值时返回的默认值
1056
- * @param options 其他配置选项
1057
- * @returns 返回指定key的存储值,若获取不到则返回missing参数指定的默认值
1058
- */
1059
- static get(key, missing, options) {
1060
- var query_key = this._getPrefixedKey(key, options), value;
1061
- try {
1062
- value = JSON.parse(localStorage.getItem(query_key) || "");
1063
- } catch (e) {
1064
- if (localStorage[query_key]) {
1065
- value = { data: localStorage.getItem(query_key) };
1066
- } else {
1067
- value = null;
1068
- }
1069
- }
1070
- if (!value) {
1071
- return missing;
1072
- } else if (typeof value === "object" && typeof value.data !== "undefined") {
1073
- const expires = value.expires;
1074
- if (expires && Date.now() > expires) {
1075
- return missing;
1076
- }
1077
- return value.data;
1078
- }
1079
- }
1080
- static keys() {
1081
- const keys = [];
1082
- var allKeys = Object.keys(localStorage);
1083
- if (_Storage.prefix.length === 0) {
1084
- return allKeys;
1085
- }
1086
- allKeys.forEach(function(key) {
1087
- if (key.indexOf(_Storage.prefix) !== -1) {
1088
- keys.push(key.replace(_Storage.prefix, ""));
1089
- }
1090
- });
1091
- return keys;
1092
- }
1093
- static getAll(includeKeys) {
1094
- var keys = _Storage.keys();
1095
- if (includeKeys) {
1096
- const result = [];
1097
- keys.forEach((key) => {
1098
- if (includeKeys.includes(key)) {
1099
- const tempObj = {};
1100
- tempObj[key] = _Storage.get(key, null, null);
1101
- result.push(tempObj);
1102
- }
1103
- });
1104
- return result;
1105
- }
1106
- return keys.map((key) => _Storage.get(key, null, null));
1107
- }
1108
- static remove(key, options) {
1109
- var queryKey = this._getPrefixedKey(key, options);
1110
- localStorage.removeItem(queryKey);
1111
- }
1112
- static clear(options) {
1113
- if (_Storage.prefix.length) {
1114
- this.keys().forEach((key) => {
1115
- localStorage.removeItem(this._getPrefixedKey(key, options));
1116
- });
1117
- } else {
1118
- localStorage.clear();
1119
- }
1120
- }
1121
- };
1122
- __publicField(_Storage, "prefix", "");
1123
- __publicField(_Storage, "_getPrefixedKey", function(key, options) {
1124
- options = options || {};
1125
- if (options.noPrefix) {
1126
- return key;
1127
- } else {
1128
- return _Storage.prefix + key;
1129
- }
1130
- });
1131
- let Storage = _Storage;
1132
- const ImageUtil = {
1133
- emptyImageUrl: "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",
1134
- /**
1135
- *
1136
- * @param image image,类型可以是HTMLCanvasElement、ImageData
1137
- * @returns
1138
- */
1139
- getURL(image) {
1140
- let _canvas;
1141
- if (/^data:/i.test(image.src)) {
1142
- return image.src;
1143
- }
1144
- if (typeof HTMLCanvasElement === "undefined") {
1145
- return image.src;
1146
- }
1147
- let canvas;
1148
- if (image instanceof HTMLCanvasElement) {
1149
- canvas = image;
1150
- } else {
1151
- if (_canvas === void 0) _canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
1152
- _canvas.width = image.width;
1153
- _canvas.height = image.height;
1154
- const context = _canvas.getContext("2d");
1155
- if (context) {
1156
- if (image instanceof ImageData) {
1157
- context.putImageData(image, 0, 0);
1158
- } else {
1159
- context.drawImage(image, 0, 0, image.width, image.height);
1160
- }
1161
- }
1162
- canvas = _canvas;
1163
- }
1164
- if (canvas.width > 2048 || canvas.height > 2048) {
1165
- console.warn("ImageUtil.getDataURL: Image converted to jpg for performance reasons", image);
1166
- return canvas.toDataURL("image/jpeg", 0.6);
1167
- } else {
1168
- return canvas.toDataURL("image/png");
1169
- }
1170
- },
1171
- /**
1172
- * 将图片的URL转换为Base64编码
1173
- *
1174
- * @param url 图片的URL地址
1175
- * @param width 图片的宽度,默认为图片原始宽度
1176
- * @param height 图片的高度,默认为图片原始高度
1177
- * @returns 返回Promise对象,解析后得到包含Base64编码数据的对象
1178
- */
1179
- getBase64(url) {
1180
- return new Promise((resolve, reject) => {
1181
- let image = new Image();
1182
- image.setAttribute("crossOrigin", "Anonymous");
1183
- image.src = url;
1184
- image.onload = () => {
1185
- let dataURL = this.getURL(image);
1186
- resolve(dataURL);
1187
- };
1188
- image.onerror = reject;
1189
- });
1190
- },
1191
- /**
1192
- * 解析base64编码
1193
- *
1194
- * @param base64 base64编码字符串
1195
- * @returns 返回一个对象,包含type(类型)、ext(扩展名)和data(数据)字段,如果解析失败则返回null
1196
- */
1197
- parseBase64(base64) {
1198
- let re = new RegExp("data:(?<type>.*?);base64,(?<data>.*)");
1199
- let res = re.exec(base64);
1200
- if (res && res.groups) {
1201
- return {
1202
- type: res.groups.type,
1203
- ext: res.groups.type.split("/").slice(-1)[0],
1204
- data: res.groups.data
1205
- };
1206
- }
1207
- return null;
1208
- },
1209
- /**
1210
- * 复制图片到剪贴板
1211
- *
1212
- * @param url 图片的URL地址
1213
- * @returns 无返回值
1214
- * @throws 如果解析base64数据失败,则抛出异常
1215
- */
1216
- async copyImage(url) {
1217
- try {
1218
- const base64Result = await this.getBase64(url);
1219
- const parsedBase64 = this.parseBase64(base64Result.dataURL);
1220
- if (!parsedBase64) {
1221
- throw new Error("Failed to parse base64 data.");
1222
- }
1223
- let type = parsedBase64.type;
1224
- let bytes = atob(parsedBase64.data);
1225
- let ab = new ArrayBuffer(bytes.length);
1226
- let ua = new Uint8Array(ab);
1227
- for (let i = 0; i < bytes.length; i++) {
1228
- ua[i] = bytes.charCodeAt(i);
1229
- }
1230
- let blob = new Blob([ab], { type });
1231
- await navigator.clipboard.write([new ClipboardItem({ [type]: blob })]);
1232
- } catch (error) {
1233
- console.error("Failed to copy image to clipboard:", error);
1234
- }
1235
- }
1236
- };
1237
- const AjaxUtil = {
1238
- /**
1239
- * Get JSON data by jsonp
1240
- * @param url - resource url
1241
- * @param callback - callback function when completed
616
+ * Get JSON data by jsonp
617
+ * @param url - resource url
618
+ * @param callback - callback function when completed
1242
619
  */
1243
620
  jsonp(url, callback) {
1244
- const name = "_jsonp_" + CommUtil.guid();
621
+ const name = "_jsonp_" + CommUtils.guid();
1245
622
  const head = document.getElementsByTagName("head")[0];
1246
623
  if (url.includes("?")) {
1247
624
  url += "&callback=" + name;
@@ -1280,7 +657,7 @@ const AjaxUtil = {
1280
657
  * );
1281
658
  */
1282
659
  get(url, options, cb) {
1283
- if (CommUtil.isFunction(options)) {
660
+ if (CommUtils.isFunction(options)) {
1284
661
  const t = cb;
1285
662
  cb = options;
1286
663
  options = t;
@@ -1412,7 +789,7 @@ const AjaxUtil = {
1412
789
  * );
1413
790
  */
1414
791
  getArrayBuffer(url, options, cb) {
1415
- if (CommUtil.isFunction(options)) {
792
+ if (CommUtils.isFunction(options)) {
1416
793
  const t = cb;
1417
794
  cb = options;
1418
795
  options = t;
@@ -1465,7 +842,7 @@ const AjaxUtil = {
1465
842
  * );
1466
843
  */
1467
844
  getJSON(url, options, cb) {
1468
- if (CommUtil.isFunction(options)) {
845
+ if (CommUtils.isFunction(options)) {
1469
846
  const t = cb;
1470
847
  cb = options;
1471
848
  options = t;
@@ -1482,6 +859,48 @@ const AjaxUtil = {
1482
859
  return this.get(url, options, callback);
1483
860
  }
1484
861
  };
862
+ const MathUtils = {
863
+ DEG2RAD: Math.PI / 180,
864
+ RAD2DEG: 180 / Math.PI,
865
+ randInt(low, high) {
866
+ return low + Math.floor(Math.random() * (high - low + 1));
867
+ },
868
+ randFloat(low, high) {
869
+ return low + Math.random() * (high - low);
870
+ },
871
+ /**
872
+ * 角度转弧度
873
+ *
874
+ * @param {*} degrees
875
+ * @returns {*}
876
+ */
877
+ deg2Rad(degrees) {
878
+ return degrees * this.DEG2RAD;
879
+ },
880
+ /**
881
+ * 弧度转角度
882
+ *
883
+ * @param {*} radians
884
+ * @returns {*}
885
+ */
886
+ rad2Deg(radians) {
887
+ return radians * this.RAD2DEG;
888
+ },
889
+ round(value, n = 2) {
890
+ return Math.round(value * Math.pow(10, n)) / Math.pow(10, n);
891
+ },
892
+ /**
893
+ * 将数值限制在指定范围内
894
+ *
895
+ * @param val 需要限制的数值
896
+ * @param min 最小值
897
+ * @param max 最大值
898
+ * @returns 返回限制后的数值
899
+ */
900
+ clamp(val, min, max) {
901
+ return Math.max(min, Math.min(max, val));
902
+ }
903
+ };
1485
904
  const GeoUtil = {
1486
905
  toRadian: Math.PI / 180,
1487
906
  R: 6371393,
@@ -1849,7 +1268,7 @@ const StringUtil = {
1849
1268
  */
1850
1269
  tag(strArray, ...args) {
1851
1270
  args = args.map((val) => {
1852
- switch (CommUtil.getDataType(val)) {
1271
+ switch (CommUtils.getDataType(val)) {
1853
1272
  case "Object":
1854
1273
  return val || "{}";
1855
1274
  case "Array":
@@ -1892,127 +1311,22 @@ const StringUtil = {
1892
1311
  return str;
1893
1312
  }
1894
1313
  };
1895
- const ColorUtil = {
1896
- random() {
1897
- let r = Math.floor(Math.random() * 256).toString(16);
1898
- let g = Math.floor(Math.random() * 256).toString(16);
1899
- let b = Math.floor(Math.random() * 256).toString(16);
1900
- r = r.length === 1 ? "0" + r : r;
1901
- g = g.length === 1 ? "0" + g : g;
1902
- b = b.length === 1 ? "0" + b : b;
1903
- return "#" + r + g + b;
1904
- },
1314
+ const TYPES = ["Point", "MultiPoint", "LineString", "MultiLineString", "Polygon", "MultiPolygon"];
1315
+ const GeoJsonUtil = {
1905
1316
  /**
1906
- * 将RGB颜色值转换为十六进制颜色值
1317
+ * 获取GeoJSON要素的几何类型
1907
1318
  *
1908
- * @param rgb RGB颜色值数组,包含三个0-255之间的整数
1909
- * @returns 转换后的十六进制颜色值,以#开头
1319
+ * @param feature GeoJSONFeature 类型的要素
1320
+ * @returns 返回要素的几何类型,如果要素没有几何属性则返回 null
1910
1321
  */
1911
- rgb2hex(rgb) {
1912
- var hex = "#" + ((1 << 24) + (rgb[0] << 16) + (rgb[1] << 8) + rgb[2]).toString(16).slice(1);
1913
- return hex;
1322
+ getGeoJsonType(feature) {
1323
+ return feature.geometry ? feature.geometry.type : null;
1914
1324
  },
1915
1325
  /**
1916
- * 将RGB颜色值转换为RGBA颜色值,并返回转换后的颜色值。
1326
+ * 判断给定的GeoJSON要素是否为有效的GeoJSON格式
1917
1327
  *
1918
- * @param rgbValue RGB颜色值,格式为"rgb(r, g, b)"。
1919
- * @returns 转换后的RGBA颜色值,格式为"rgba(r, g, b, 1)"。如果输入值不符合RGB格式,则返回原值。
1920
- */
1921
- rgbToRgba(rgbValue) {
1922
- const rgb = /rgb\((\d+,\s*[\d]+,\s*[\d]+)\)/g.exec(rgbValue);
1923
- return rgb ? `rgba(${rgb[1]}, 1)` : rgbValue;
1924
- },
1925
- /**
1926
- * 将十六进制颜色值转换为rgba格式的颜色值
1927
- *
1928
- * @param hexValue 十六进制颜色值,可带或不带#前缀,支持3位和6位表示
1929
- * @returns 返回rgba格式的颜色值,格式为rgba(r,g,b,1)
1930
- */
1931
- hexToRgba(hexValue) {
1932
- const rgxShort = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
1933
- const hex = hexValue.replace(rgxShort, (m, r2, g2, b2) => r2 + r2 + g2 + g2 + b2 + b2);
1934
- const rgx = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i;
1935
- const rgb = rgx.exec(hex);
1936
- if (!rgb) {
1937
- return hexValue;
1938
- }
1939
- const r = parseInt(rgb[1], 16);
1940
- const g = parseInt(rgb[2], 16);
1941
- const b = parseInt(rgb[3], 16);
1942
- return `rgba(${r},${g},${b},1)`;
1943
- },
1944
- /**
1945
- * 将 HSL 颜色值转换为 RGBA 颜色值
1946
- *
1947
- * @param hslValue HSL 颜色值字符串,格式为 "hsl(h, s%, l%)" 或 "hsla(h, s%, l%, a)",其中 h 为色相,s 为饱和度,l 为亮度,a 为透明度(可选)。
1948
- * @returns 转换后的 RGBA 颜色值字符串,格式为 "rgba(r, g, b, a)",其中 r、g、b 为红绿蓝分量,a 为透明度。若输入为空或无效,则返回 null。
1949
- */
1950
- hslToRgba(hslValue) {
1951
- if (!hslValue) {
1952
- return null;
1953
- }
1954
- const hsl = /hsl\((\d+),\s*([\d.]+)%,\s*([\d.]+)%\)/g.exec(hslValue) || /hsla\((\d+),\s*([\d.]+)%,\s*([\d.]+)%,\s*([\d.]+)\)/g.exec(hslValue);
1955
- if (!hsl) {
1956
- return null;
1957
- }
1958
- const h = parseInt(hsl[1], 10) / 360;
1959
- const s = parseInt(hsl[2], 10) / 100;
1960
- const l = parseInt(hsl[3], 10) / 100;
1961
- const a = hsl[4] ? parseFloat(hsl[4]) : 1;
1962
- function hue2rgb(p, q, t) {
1963
- if (t < 0) t += 1;
1964
- if (t > 1) t -= 1;
1965
- if (t < 1 / 6) return p + (q - p) * 6 * t;
1966
- if (t < 1 / 2) return q;
1967
- if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
1968
- return p;
1969
- }
1970
- let r, g, b;
1971
- if (s === 0) {
1972
- r = g = b = l;
1973
- } else {
1974
- const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
1975
- const p = 2 * l - q;
1976
- r = hue2rgb(p, q, h + 1 / 3);
1977
- g = hue2rgb(p, q, h);
1978
- b = hue2rgb(p, q, h - 1 / 3);
1979
- }
1980
- return `rgba(${Math.round(r * 255)},${Math.round(g * 255)},${Math.round(b * 255)},${a})`;
1981
- },
1982
- isHex(a) {
1983
- return /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(a);
1984
- },
1985
- isRgb(a) {
1986
- return /^rgb/.test(a);
1987
- },
1988
- isHsl(a) {
1989
- return /^hsl/.test(a);
1990
- },
1991
- isColor(a) {
1992
- return this.isHex(a) || this.isRgb(a) || this.isHsl(a);
1993
- },
1994
- colorToRgb(val) {
1995
- if (this.isRgb(val)) return this.rgbToRgba(val);
1996
- if (this.isHex(val)) return this.hexToRgba(val);
1997
- if (this.isHsl(val)) return this.hslToRgba(val);
1998
- }
1999
- };
2000
- const TYPES = ["Point", "MultiPoint", "LineString", "MultiLineString", "Polygon", "MultiPolygon"];
2001
- const GeoJsonUtil = {
2002
- /**
2003
- * 获取GeoJSON要素的几何类型
2004
- *
2005
- * @param feature GeoJSONFeature 类型的要素
2006
- * @returns 返回要素的几何类型,如果要素没有几何属性则返回 null
2007
- */
2008
- getGeoJsonType(feature) {
2009
- return feature.geometry ? feature.geometry.type : null;
2010
- },
2011
- /**
2012
- * 判断给定的GeoJSON要素是否为有效的GeoJSON格式
2013
- *
2014
- * @param feature 要判断的GeoJSON要素
2015
- * @returns 如果为有效的GeoJSON格式则返回true,否则返回false
1328
+ * @param feature 要判断的GeoJSON要素
1329
+ * @returns 如果为有效的GeoJSON格式则返回true,否则返回false
2016
1330
  */
2017
1331
  isGeoJson(feature) {
2018
1332
  const type = this.getGeoJsonType(feature);
@@ -2241,57 +1555,57 @@ const GeoJsonUtil = {
2241
1555
  const AssertUtil = {
2242
1556
  assertEmpty(...arg) {
2243
1557
  arg.forEach((a) => {
2244
- if (CommUtil.isEmpty(a)) {
2245
- throw Error("变量为空:>>>" + a);
1558
+ if (CommUtils.isEmpty(a)) {
1559
+ throw Error(ErrorType.PARAMETER_ERROR_LACK + " -> " + a);
2246
1560
  }
2247
1561
  });
2248
1562
  },
2249
1563
  assertNumber(...arg) {
2250
1564
  arg.forEach((a) => {
2251
- if (!CommUtil.isNumber(a)) {
2252
- throw Error("不是数字:>>>" + a);
1565
+ if (!CommUtils.isNumber(a)) {
1566
+ throw Error(ErrorType.PARAMETER_ERROR_NUMBER + " -> " + a);
2253
1567
  }
2254
1568
  });
2255
1569
  },
2256
1570
  assertArray(...arg) {
2257
1571
  arg.forEach((a) => {
2258
- if (!CommUtil.isArray(a)) {
2259
- throw Error(ErrorType.PARAMETER_ERROR_ARRAY + ":>>>" + a);
1572
+ if (!CommUtils.isArray(a)) {
1573
+ throw Error(ErrorType.PARAMETER_ERROR_ARRAY + " -> " + a);
2260
1574
  }
2261
1575
  });
2262
1576
  },
2263
1577
  assertFunction(...arg) {
2264
1578
  arg.forEach((a) => {
2265
- if (!CommUtil.isFunction(a)) {
2266
- throw Error(ErrorType.PARAMETER_ERROR_FUNCTION + ":>>>" + a);
1579
+ if (!CommUtils.isFunction(a)) {
1580
+ throw Error(ErrorType.PARAMETER_ERROR_FUNCTION + " -> " + a);
2267
1581
  }
2268
1582
  });
2269
1583
  },
2270
1584
  assertObject(...arg) {
2271
1585
  arg.forEach((a) => {
2272
- if (!CommUtil.isObject(a)) {
2273
- throw Error(ErrorType.PARAMETER_ERROR_OBJECT + ":>>>" + a);
1586
+ if (!CommUtils.isObject(a)) {
1587
+ throw Error(ErrorType.PARAMETER_ERROR_OBJECT + " -> " + a);
2274
1588
  }
2275
1589
  });
2276
1590
  },
2277
1591
  assertColor(...arg) {
2278
1592
  arg.forEach((a) => {
2279
- if (!ColorUtil.isColor(a)) {
2280
- throw Error("颜色代码不正确:>>>" + a);
1593
+ if (!Color.isColor(a)) {
1594
+ throw Error(ErrorType.DATA_ERROR_COLOR + " -> " + a);
2281
1595
  }
2282
1596
  });
2283
1597
  },
2284
1598
  assertLnglat(...arg) {
2285
1599
  arg.forEach((a) => {
2286
1600
  if (!GeoUtil.isLnglat(a.lng, a.lat)) {
2287
- throw Error(ErrorType.COORDINATE_ERROR + ":>>>" + a);
1601
+ throw Error(ErrorType.DATA_ERROR_COORDINATE + " -> " + a);
2288
1602
  }
2289
1603
  });
2290
1604
  },
2291
1605
  assertGeoJson(...arg) {
2292
1606
  arg.forEach((a) => {
2293
1607
  if (!GeoJsonUtil.isGeoJson(a)) {
2294
- throw Error("不是GeoJSON:>>>" + a);
1608
+ throw Error(ErrorType.DATA_ERROR_GEOJSON + " -> " + a);
2295
1609
  }
2296
1610
  });
2297
1611
  },
@@ -2302,7 +1616,7 @@ const AssertUtil = {
2302
1616
  res = str.indexOf(args[i]) >= 0;
2303
1617
  }
2304
1618
  if (res) {
2305
- throw Error(ErrorType.STRING_CHECK_LOSS + ":>>>" + str);
1619
+ throw Error(ErrorType.STRING_CHECK_LOSS + " -> " + str);
2306
1620
  }
2307
1621
  },
2308
1622
  /**
@@ -2310,7 +1624,7 @@ const AssertUtil = {
2310
1624
  *
2311
1625
  * @param value 待判断的字符串
2312
1626
  * @param type 字符串类型
2313
- * @throws 当字符串不合法时,抛出错误,错误信息为“参数错误:>>>不是{typename}”
1627
+ * @throws 当字符串不合法时,抛出错误,错误信息为“参数错误 -> 不是{typename}”
2314
1628
  */
2315
1629
  assertLegal(value, type) {
2316
1630
  const bool = StringUtil.checkStr(value, type);
@@ -2369,8 +1683,119 @@ const AssertUtil = {
2369
1683
  break;
2370
1684
  }
2371
1685
  if (!bool) {
2372
- throw Error(ErrorType.PARAMETER_ERROR + ":>>>不是" + typename);
1686
+ throw Error(ErrorType.DATA_ERROR + " -> 不是" + typename);
1687
+ }
1688
+ }
1689
+ };
1690
+ const myArray = Object.create(Array);
1691
+ myArray.groupBy = function(f) {
1692
+ var groups = {};
1693
+ this.forEach(function(o) {
1694
+ var group = JSON.stringify(f(o));
1695
+ groups[group] = groups[group] || [];
1696
+ groups[group].push(o);
1697
+ });
1698
+ return Object.keys(groups).map((group) => groups[group]);
1699
+ };
1700
+ myArray.distinct = function(f = (d) => d) {
1701
+ const arr = [];
1702
+ const obj = {};
1703
+ this.forEach((item) => {
1704
+ const val = f(item);
1705
+ const key = String(val);
1706
+ if (!obj[key]) {
1707
+ obj[key] = true;
1708
+ arr.push(item);
2373
1709
  }
1710
+ });
1711
+ return arr;
1712
+ };
1713
+ myArray.prototype.max = function() {
1714
+ return Math.max.apply({}, this);
1715
+ };
1716
+ myArray.prototype.min = function() {
1717
+ return Math.min.apply({}, this);
1718
+ };
1719
+ myArray.sum = function() {
1720
+ return this.length > 0 ? this.reduce((prev = 0, curr = 0) => prev + curr) : 0;
1721
+ };
1722
+ myArray.avg = function() {
1723
+ return this.length ? this.sum() / this.length : 0;
1724
+ };
1725
+ myArray.desc = function(f = (d) => d) {
1726
+ return this.sort((n1, n2) => f(n2) - f(n1));
1727
+ };
1728
+ myArray.asc = function(f = (d) => d) {
1729
+ return this.sort((n1, n2) => f(n1) - f(n2));
1730
+ };
1731
+ myArray.random = function() {
1732
+ return this[Math.floor(Math.random() * this.length)];
1733
+ };
1734
+ myArray.remove = function(obj) {
1735
+ const i = this.indexOf(obj);
1736
+ if (i > -1) {
1737
+ this.splice(i, 1);
1738
+ }
1739
+ return this;
1740
+ };
1741
+ const ArrayUtil = {
1742
+ /**
1743
+ * 创建指定长度的数组,并返回其索引数组
1744
+ *
1745
+ * @param length 数组长度
1746
+ * @returns 索引数组
1747
+ */
1748
+ create(length) {
1749
+ return [...new Array(length).keys()];
1750
+ },
1751
+ /**
1752
+ * 合并多个数组,并去重
1753
+ *
1754
+ * @param args 需要合并的数组
1755
+ * @returns 合并后的去重数组
1756
+ */
1757
+ union(...args) {
1758
+ let res = [];
1759
+ args.forEach((arg) => {
1760
+ if (Array.isArray(arg)) {
1761
+ res = res.concat(arg.filter((v) => !res.includes(v)));
1762
+ }
1763
+ });
1764
+ return res;
1765
+ },
1766
+ /**
1767
+ * 求多个数组的交集
1768
+ *
1769
+ * @param args 多个需要求交集的数组
1770
+ * @returns 返回多个数组的交集数组
1771
+ */
1772
+ intersection(...args) {
1773
+ let res = args[0] || [];
1774
+ args.forEach((arg) => {
1775
+ if (Array.isArray(arg)) {
1776
+ res = res.filter((v) => arg.includes(v));
1777
+ }
1778
+ });
1779
+ return res;
1780
+ },
1781
+ /**
1782
+ * 将多个数组拼接为一个数组,并去除其中的空值。
1783
+ *
1784
+ * @param args 需要拼接的数组列表。
1785
+ * @returns 拼接并去空后的数组。
1786
+ */
1787
+ unionAll(...args) {
1788
+ return [...args].flat().filter((d) => !!d);
1789
+ },
1790
+ /**
1791
+ * 求差集
1792
+ *
1793
+ * @param args 任意个集合
1794
+ * @returns 返回差集结果
1795
+ */
1796
+ difference(...args) {
1797
+ if (args.length === 0) return [];
1798
+ return this.union(...args).filter((d) => !this.intersection(...args).includes(d));
2374
1799
  }
2375
1800
  };
2376
1801
  const BrowserUtil = {
@@ -2603,18 +2028,26 @@ const CoordsUtil = {
2603
2028
  return ret;
2604
2029
  },
2605
2030
  /**
2606
- * 生成指定范围内的随机经纬度坐标
2031
+ * 生成一个介于两个坐标之间的随机坐标
2607
2032
  *
2608
- * @param min 最小坐标,包含属性 x y,分别表示最小经度和最小纬度
2609
- * @param max 最大坐标,包含属性 x y,分别表示最大经度和最大纬度
2610
- * @returns 返回生成的随机经纬度坐标,包含属性 lat lng,分别表示纬度和经度
2033
+ * @param start 起始坐标,包含x和y属性
2034
+ * @param end 结束坐标,包含x和y属性
2035
+ * @returns 返回一个包含xy属性的随机坐标
2611
2036
  */
2612
2037
  random({ x: minX, y: minY }, { x: maxX, y: maxY }) {
2613
2038
  return {
2614
- lat: Math.random() * (maxY - minY) + minY,
2615
- lng: Math.random() * (maxX - minX) + minX
2039
+ x: Math.random() * (maxX - minX) + minX,
2040
+ y: Math.random() * (maxY - minY) + minY
2616
2041
  };
2617
2042
  },
2043
+ /**
2044
+ * 对坐标数组进行解构并应用函数处理
2045
+ *
2046
+ * @param arr 待解构的数组
2047
+ * @param fn 处理函数
2048
+ * @param context 函数执行上下文,可选
2049
+ * @returns 处理后的数组
2050
+ */
2618
2051
  deCompose(arr, fn, context) {
2619
2052
  if (!Array.isArray(arr)) {
2620
2053
  return context ? fn.call(context, arr) : fn(arr);
@@ -2623,7 +2056,7 @@ const CoordsUtil = {
2623
2056
  let p, pp;
2624
2057
  for (let i = 0, len = arr.length; i < len; i++) {
2625
2058
  p = arr[i];
2626
- if (CommUtil.isNil(p)) {
2059
+ if (CommUtils.isNil(p)) {
2627
2060
  result.push(null);
2628
2061
  continue;
2629
2062
  }
@@ -3195,6 +2628,643 @@ const UrlUtil = {
3195
2628
  return obj;
3196
2629
  }
3197
2630
  };
2631
+ class Color {
2632
+ constructor(r, g, b, a) {
2633
+ __publicField(this, "_r");
2634
+ __publicField(this, "_g");
2635
+ __publicField(this, "_b");
2636
+ __publicField(this, "_alpha");
2637
+ this._validateColorChannel(r);
2638
+ this._validateColorChannel(g);
2639
+ this._validateColorChannel(b);
2640
+ this._r = r;
2641
+ this._g = g;
2642
+ this._b = b;
2643
+ this._alpha = MathUtils.clamp(a || 1, 0, 1);
2644
+ }
2645
+ _validateColorChannel(channel) {
2646
+ if (channel < 0 || channel > 255) {
2647
+ throw new Error("Color channel must be between 0 and 255.");
2648
+ }
2649
+ }
2650
+ // 获取颜色的RGB值
2651
+ get rgba() {
2652
+ return { r: this._r, g: this._g, b: this._b, a: this._alpha };
2653
+ }
2654
+ get hex() {
2655
+ return Color.rgb2hex(this._r, this._g, this._b, this._alpha);
2656
+ }
2657
+ // 设置颜色的RGB值
2658
+ setRgb(r, g, b, a) {
2659
+ this._validateColorChannel(r);
2660
+ this._validateColorChannel(g);
2661
+ this._validateColorChannel(b);
2662
+ this._r = r;
2663
+ this._g = g;
2664
+ this._b = b;
2665
+ this._alpha = MathUtils.clamp(a, 0, 1);
2666
+ return this;
2667
+ }
2668
+ /**
2669
+ * 从RGBA字符串创建Color对象
2670
+ *
2671
+ * @param rgbaValue RGBA颜色值字符串,格式为"rgba(r,g,b,a)"或"rgb(r,g,b)"
2672
+ * @returns 返回Color对象
2673
+ * @throws 如果rgbaValue不是有效的RGBA颜色值,则抛出错误
2674
+ */
2675
+ static fromRgba(rgbaValue) {
2676
+ const rgbaMatch = rgbaValue.match(/^rgba?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([\d.]+))?\s*\)$/);
2677
+ if (!rgbaMatch) throw new Error("Invalid RGBA color value");
2678
+ const r = parseInt(rgbaMatch[1], 10);
2679
+ const g = parseInt(rgbaMatch[2], 10);
2680
+ const b = parseInt(rgbaMatch[3], 10);
2681
+ const a = rgbaMatch[5] ? parseFloat(rgbaMatch[5]) : 1;
2682
+ return new Color(r, g, b, a);
2683
+ }
2684
+ /**
2685
+ * 将十六进制颜色值转换为颜色对象
2686
+ *
2687
+ * @param hexValue 十六进制颜色值,可带或不带#前缀,支持3位和6位表示
2688
+ * @returns 返回颜色对象
2689
+ */
2690
+ static fromHex(hexValue, a = 1) {
2691
+ const rgxShort = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
2692
+ const hex = hexValue.replace(rgxShort, (m, r2, g2, b2) => r2 + r2 + g2 + g2 + b2 + b2);
2693
+ const rgx = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i;
2694
+ const rgb = rgx.exec(hex);
2695
+ if (!rgb) {
2696
+ throw new Error("Invalid HEX color value");
2697
+ }
2698
+ const r = parseInt(rgb[1], 16);
2699
+ const g = parseInt(rgb[2], 16);
2700
+ const b = parseInt(rgb[3], 16);
2701
+ return new Color(r, g, b, a);
2702
+ }
2703
+ /**
2704
+ * 从 HSL 字符串创建颜色对象
2705
+ *
2706
+ * @param hsl HSL 字符串,格式为 hsl(h, s%, l%) 或 hsla(h, s%, l%, a)
2707
+ * @returns 返回颜色对象,如果 hsl 字符串无效则返回 null
2708
+ */
2709
+ static fromHsl(hslValue) {
2710
+ const hsl = /hsl\((\d+),\s*([\d.]+)%,\s*([\d.]+)%\)/g.exec(hslValue) || /hsla\((\d+),\s*([\d.]+)%,\s*([\d.]+)%,\s*([\d.]+)\)/g.exec(hslValue);
2711
+ if (!hsl) {
2712
+ throw new Error("Invalid HSL color value");
2713
+ }
2714
+ const h = parseInt(hsl[1], 10) / 360;
2715
+ const s = parseInt(hsl[2], 10) / 100;
2716
+ const l = parseInt(hsl[3], 10) / 100;
2717
+ const a = hsl[4] ? parseFloat(hsl[4]) : 1;
2718
+ function hue2rgb(p, q, t) {
2719
+ if (t < 0) t += 1;
2720
+ if (t > 1) t -= 1;
2721
+ if (t < 1 / 6) return p + (q - p) * 6 * t;
2722
+ if (t < 1 / 2) return q;
2723
+ if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
2724
+ return p;
2725
+ }
2726
+ let r, g, b;
2727
+ if (s === 0) {
2728
+ r = g = b = l;
2729
+ } else {
2730
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
2731
+ const p = 2 * l - q;
2732
+ r = hue2rgb(p, q, h + 1 / 3);
2733
+ g = hue2rgb(p, q, h);
2734
+ b = hue2rgb(p, q, h - 1 / 3);
2735
+ }
2736
+ return new Color(Math.round(r * 255), Math.round(g * 255), Math.round(b * 255), a);
2737
+ }
2738
+ /**
2739
+ * 从字符串中创建颜色对象
2740
+ *
2741
+ * @param str 字符串类型的颜色值,支持rgba、hex、hsl格式
2742
+ * @returns 返回创建的颜色对象
2743
+ * @throws 当颜色值无效时,抛出错误
2744
+ */
2745
+ static from(str) {
2746
+ if (this.isRgb(str)) {
2747
+ return this.fromRgba(str);
2748
+ } else if (this.isHex(str)) {
2749
+ return this.fromHex(str);
2750
+ } else if (this.isHsl(str)) {
2751
+ return this.fromHsl(str);
2752
+ } else {
2753
+ throw new Error("Invalid color value");
2754
+ }
2755
+ }
2756
+ /**
2757
+ * 将RGB颜色值转换为十六进制颜色值
2758
+ *
2759
+ * @param r 红色分量值,取值范围0-255
2760
+ * @param g 绿色分量值,取值范围0-255
2761
+ * @param b 蓝色分量值,取值范围0-255
2762
+ * @param a 可选参数,透明度分量值,取值范围0-1
2763
+ * @returns 十六进制颜色值,格式为#RRGGBB或#RRGGBBAA
2764
+ */
2765
+ static rgb2hex(r, g, b, a) {
2766
+ var hex = "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
2767
+ if (a !== void 0) {
2768
+ const alpha = Math.round(a * 255).toString(16).padStart(2, "0");
2769
+ return hex + alpha;
2770
+ }
2771
+ return hex;
2772
+ }
2773
+ static isHex(a) {
2774
+ return /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(a);
2775
+ }
2776
+ static isRgb(a) {
2777
+ return /^rgb/.test(a);
2778
+ }
2779
+ static isHsl(a) {
2780
+ return /^hsl/.test(a);
2781
+ }
2782
+ static isColor(a) {
2783
+ return this.isHex(a) || this.isRgb(a) || this.isHsl(a);
2784
+ }
2785
+ static random() {
2786
+ let r = Math.floor(Math.random() * 256);
2787
+ let g = Math.floor(Math.random() * 256);
2788
+ let b = Math.floor(Math.random() * 256);
2789
+ let a = Math.random();
2790
+ return new Color(r, g, b, a);
2791
+ }
2792
+ }
2793
+ class CanvasDrawer {
2794
+ constructor(el) {
2795
+ __publicField(this, "context", null);
2796
+ if (typeof el === "string") {
2797
+ el = document.querySelector("#" + el);
2798
+ if (!el) {
2799
+ throw new Error("Element not found");
2800
+ }
2801
+ }
2802
+ if (el instanceof HTMLElement) {
2803
+ const canvas = el;
2804
+ if (canvas.getContext) {
2805
+ this.context = canvas.getContext("2d");
2806
+ } else {
2807
+ throw new Error("getContext is not available on this element");
2808
+ }
2809
+ } else {
2810
+ throw new Error("Element is not an HTMLElement");
2811
+ }
2812
+ }
2813
+ /**
2814
+ * 绘制线条
2815
+ *
2816
+ * @param start 起始坐标点
2817
+ * @param end 终止坐标点
2818
+ * @param options 绘制选项,包括线条宽度和颜色
2819
+ * @throws 当画布上下文不存在时抛出错误
2820
+ */
2821
+ drawLine({ x: startX, y: startY }, { x: endX, y: endY }, options = {}) {
2822
+ if (!this.context) {
2823
+ throw new Error("Canvas context is null or undefined");
2824
+ }
2825
+ this.context.beginPath();
2826
+ const width = options.width || 1;
2827
+ const color = options.color || "#000";
2828
+ this.context.lineWidth = width;
2829
+ this.context.strokeStyle = color;
2830
+ this.context.moveTo(startX, startY);
2831
+ this.context.lineTo(endX, endY);
2832
+ this.context.stroke();
2833
+ }
2834
+ /**
2835
+ * 绘制圆弧
2836
+ *
2837
+ * @param x 圆心x坐标
2838
+ * @param y 圆心y坐标
2839
+ * @param radius 半径
2840
+ * @param startAngle 起始角度(度)
2841
+ * @param endAngle 结束角度(度)
2842
+ * @param anticlockwise 是否逆时针绘制
2843
+ * @param isFill 是否填充
2844
+ * @param bgColor 背景颜色
2845
+ * @throws 当Canvas context为null或undefined时抛出错误
2846
+ */
2847
+ drawArc({ x, y }, radius, startAngle, endAngle, anticlockwise, isFill, bgColor) {
2848
+ if (!this.context) {
2849
+ throw new Error("Canvas context is null or undefined");
2850
+ }
2851
+ if (isFill) {
2852
+ this.context.fillStyle = bgColor;
2853
+ this.context.beginPath();
2854
+ this.context.arc(x, y, radius, MathUtils.deg2Rad(startAngle), MathUtils.deg2Rad(endAngle), anticlockwise);
2855
+ this.context.fill();
2856
+ } else {
2857
+ this.context.strokeStyle = bgColor;
2858
+ this.context.beginPath();
2859
+ this.context.arc(x, y, radius, MathUtils.deg2Rad(startAngle), MathUtils.deg2Rad(endAngle), anticlockwise);
2860
+ this.context.stroke();
2861
+ }
2862
+ }
2863
+ static createCanvas(width = 1, height = 1) {
2864
+ const canvas = document.createElement("canvas");
2865
+ if (width) {
2866
+ canvas.width = width;
2867
+ }
2868
+ if (height) {
2869
+ canvas.height = height;
2870
+ }
2871
+ return canvas;
2872
+ }
2873
+ }
2874
+ class EventDispatcher {
2875
+ constructor() {
2876
+ __publicField(this, "_listeners");
2877
+ __publicField(this, "_mutex", {});
2878
+ __publicField(this, "_context");
2879
+ }
2880
+ addEventListener(type, listener, context, mutexStatus) {
2881
+ if (this._listeners === void 0) this._listeners = {};
2882
+ this._context = context;
2883
+ const mutex = this._mutex;
2884
+ const listeners = this._listeners;
2885
+ if (listeners[type] === void 0) {
2886
+ listeners[type] = [];
2887
+ }
2888
+ if (listeners[type].indexOf(listener) === -1) {
2889
+ if (mutexStatus) {
2890
+ mutex[type] = listener;
2891
+ }
2892
+ listeners[type].push(listener);
2893
+ }
2894
+ return this;
2895
+ }
2896
+ hasEventListener(type, listener) {
2897
+ if (this._listeners === null || this._listeners === void 0) return false;
2898
+ const listeners = this._listeners;
2899
+ return listeners[type] !== void 0 && listeners[type].indexOf(listener) !== -1;
2900
+ }
2901
+ removeEventListener(type, listener) {
2902
+ if (this._listeners === void 0) return;
2903
+ const listeners = this._listeners;
2904
+ const listenerArray = listeners[type];
2905
+ if (this._mutex[type] === listener) {
2906
+ this._mutex[type] = null;
2907
+ }
2908
+ if (listenerArray !== void 0) {
2909
+ const index = listenerArray.map((d) => d.toString()).indexOf(listener.toString());
2910
+ if (index !== -1) {
2911
+ listenerArray.splice(index, 1);
2912
+ }
2913
+ }
2914
+ }
2915
+ dispatchEvent(event) {
2916
+ if (this._listeners === void 0) return;
2917
+ const listeners = this._listeners;
2918
+ const listenerArray = listeners[event.type];
2919
+ if (listenerArray !== void 0) {
2920
+ event.target = this;
2921
+ const array = listenerArray.slice(0);
2922
+ if (this._mutex[event.type] !== void 0) {
2923
+ const find = array.find((item) => item === this._mutex[event.type]);
2924
+ if (find) {
2925
+ find.call(this._context || this, event);
2926
+ return;
2927
+ }
2928
+ }
2929
+ for (let i = 0, l = array.length; i < l; i++) {
2930
+ const item = array[i];
2931
+ if (typeof item === "function") {
2932
+ item.call(this._context || this, event);
2933
+ }
2934
+ }
2935
+ }
2936
+ }
2937
+ removeAllListener() {
2938
+ this._mutex = {};
2939
+ for (const key in this._listeners) {
2940
+ this._listeners[key] = [];
2941
+ }
2942
+ }
2943
+ }
2944
+ class HashMap extends Map {
2945
+ isEmpty() {
2946
+ return this.size === 0;
2947
+ }
2948
+ _values() {
2949
+ return Array.from(this.values());
2950
+ }
2951
+ _keys() {
2952
+ return Array.from(this.keys());
2953
+ }
2954
+ _entries() {
2955
+ return Array.from(this.entries());
2956
+ }
2957
+ static fromEntries(array = []) {
2958
+ const hashMap = new HashMap();
2959
+ array.forEach((element) => {
2960
+ if (Array.isArray(element) && element.length === 2) {
2961
+ hashMap.set(element[0], element[1]);
2962
+ }
2963
+ });
2964
+ return hashMap;
2965
+ }
2966
+ }
2967
+ class WebSocketClient extends EventDispatcher {
2968
+ constructor(url = "ws://127.0.0.1:10088") {
2969
+ super();
2970
+ __publicField(this, "maxCheckTimes", 10);
2971
+ __publicField(this, "url");
2972
+ __publicField(this, "checkTimes", 0);
2973
+ __publicField(this, "connectStatus", false);
2974
+ __publicField(this, "client", null);
2975
+ this.maxCheckTimes = 10;
2976
+ this.url = url;
2977
+ this.checkTimes = 0;
2978
+ this.connect();
2979
+ this.connCheckStatus(this.maxCheckTimes);
2980
+ }
2981
+ connect() {
2982
+ this.disconnect();
2983
+ if (this.url) {
2984
+ try {
2985
+ console.info("创建ws连接>>>" + this.url);
2986
+ this.client = new WebSocket(this.url);
2987
+ if (this.client) {
2988
+ const self = this;
2989
+ this.client.onopen = function(message) {
2990
+ self.dispatchEvent({
2991
+ type: EventType.WEB_SOCKET_CONNECT,
2992
+ message
2993
+ });
2994
+ };
2995
+ this.client.onmessage = function(message) {
2996
+ self.connectStatus = true;
2997
+ self.dispatchEvent({
2998
+ type: EventType.WEB_SOCKET_MESSAGE,
2999
+ message
3000
+ });
3001
+ };
3002
+ this.client.onclose = function(message) {
3003
+ self.dispatchEvent({
3004
+ type: EventType.WEB_SOCKET_CLOSE,
3005
+ message
3006
+ });
3007
+ };
3008
+ if (this.checkTimes === this.maxCheckTimes) {
3009
+ this.client.onerror = function(message) {
3010
+ self.dispatchEvent({
3011
+ type: EventType.WEB_SOCKET_ERROR,
3012
+ message
3013
+ });
3014
+ };
3015
+ }
3016
+ }
3017
+ } catch (ex) {
3018
+ console.error("创建ws连接失败" + this.url + ":" + ex);
3019
+ }
3020
+ }
3021
+ }
3022
+ disconnect() {
3023
+ if (this.client) {
3024
+ try {
3025
+ console.log("ws断开连接" + this.url);
3026
+ this.client.close();
3027
+ this.client = null;
3028
+ } catch (ex) {
3029
+ this.client = null;
3030
+ }
3031
+ }
3032
+ }
3033
+ connCheckStatus(times) {
3034
+ if (this.checkTimes > times) return;
3035
+ setTimeout(() => {
3036
+ this.checkTimes++;
3037
+ if (this.client && this.client.readyState !== 0 && this.client.readyState !== 1) {
3038
+ this.connect();
3039
+ }
3040
+ this.connCheckStatus(times);
3041
+ }, 2e3);
3042
+ }
3043
+ send(message) {
3044
+ if (this.client && this.client.readyState === 1) {
3045
+ this.client.send(message);
3046
+ return true;
3047
+ }
3048
+ console.error(this.url + "消息发送失败:" + message);
3049
+ return false;
3050
+ }
3051
+ heartbeat() {
3052
+ setTimeout(() => {
3053
+ if (this.client && this.client.readyState === 1) {
3054
+ this.send("HeartBeat");
3055
+ }
3056
+ console.log("HeartBeat," + this.url);
3057
+ setTimeout(this.heartbeat, 3e4);
3058
+ }, 1e3);
3059
+ }
3060
+ }
3061
+ const _MqttClient = class _MqttClient extends EventDispatcher {
3062
+ constructor(url = `ws://${window.document.domain}:20007/mqtt`, config = {}) {
3063
+ super();
3064
+ __publicField(this, "state");
3065
+ __publicField(this, "url");
3066
+ __publicField(this, "context");
3067
+ __publicField(this, "options");
3068
+ __publicField(this, "client");
3069
+ __publicField(this, "topics");
3070
+ this.context = CommUtils.extend(_MqttClient.defaultContext, config);
3071
+ this.options = {
3072
+ connectTimeout: this.context.MQTT_TIMEOUTM,
3073
+ clientId: CommUtils.guid(),
3074
+ username: this.context.MQTT_USERNAME,
3075
+ password: this.context.MQTT_PASSWORD,
3076
+ clean: true
3077
+ };
3078
+ this.url = url;
3079
+ this.client = connect(this.url, this.options);
3080
+ this._onConnect();
3081
+ this._onMessage();
3082
+ this.state = 0;
3083
+ this.topics = [];
3084
+ }
3085
+ _onConnect() {
3086
+ this.client.on("connect", () => {
3087
+ this.state = 1;
3088
+ console.log("链接mqtt成功==>" + this.url);
3089
+ this.dispatchEvent({ type: EventType.MQTT_CONNECT, message: this });
3090
+ });
3091
+ this.client.on("error", (err) => {
3092
+ console.log("链接mqtt报错", err);
3093
+ this.state = -1;
3094
+ this.dispatchEvent({ type: EventType.MQTT_ERROR, message: this });
3095
+ this.client.end();
3096
+ this.client.reconnect();
3097
+ });
3098
+ }
3099
+ _onMessage() {
3100
+ this.client.on("message", (topic, message) => {
3101
+ let dataString = message;
3102
+ let data = "";
3103
+ if (message instanceof Uint8Array) {
3104
+ dataString = message.toString();
3105
+ }
3106
+ try {
3107
+ data = ObjectUtil.parse(dataString);
3108
+ } catch (error) {
3109
+ throw new Error(ErrorType.JSON_PARSE_ERROR);
3110
+ }
3111
+ this.dispatchEvent({
3112
+ type: EventType.MQTT_MESSAGE,
3113
+ message: { topic, data }
3114
+ });
3115
+ });
3116
+ }
3117
+ sendMsg(topic, msg) {
3118
+ if (!this.client.connected) {
3119
+ console.error("客户端未连接");
3120
+ return;
3121
+ }
3122
+ this.client.publish(topic, msg, { qos: 1, retain: true });
3123
+ }
3124
+ subscribe(topic) {
3125
+ this.state === 1 ? this.client.subscribe(topic, { qos: 1 }, (error, e) => {
3126
+ error instanceof Error ? console.error("订阅失败==>" + topic, error) : (this.topics = ArrayUtil.union(this.topics, topic), console.log("订阅成功==>" + topic));
3127
+ }) : this.addEventListener(EventType.MQTT_CONNECT, (res) => {
3128
+ this.client.subscribe(topic, { qos: 1 }, (error, e) => {
3129
+ error instanceof Error ? console.error("订阅失败==>" + topic, error) : (this.topics = ArrayUtil.union(this.topics, topic), console.log("订阅成功==>" + topic));
3130
+ });
3131
+ });
3132
+ return this;
3133
+ }
3134
+ unsubscribe(topic) {
3135
+ this.client.unsubscribe(topic, { qos: 1 }, (error, res) => {
3136
+ if (error instanceof Error) {
3137
+ console.error(`取消订阅失败==>${topic}`, error);
3138
+ } else {
3139
+ this.topics = ArrayUtil.difference(this.topics, topic);
3140
+ console.log(`取消订阅成功==>${topic}`);
3141
+ }
3142
+ });
3143
+ return this;
3144
+ }
3145
+ unsubscribeAll() {
3146
+ this.unsubscribe(this.topics);
3147
+ }
3148
+ unconnect() {
3149
+ this.client.end();
3150
+ this.client = null;
3151
+ this.dispatchEvent({ type: EventType.MQTT_CLOSE, message: null });
3152
+ console.log("断开mqtt成功==>" + this.url);
3153
+ }
3154
+ };
3155
+ /**
3156
+ * Creates an instance of MqttClient.
3157
+ * @param {*} config mqtt实例参数
3158
+ */
3159
+ __publicField(_MqttClient, "defaultContext", {
3160
+ MQTT_USERNAME: "iRVMS-WEB",
3161
+ MQTT_PASSWORD: "novasky888",
3162
+ MQTT_TIMEOUTM: 2e4
3163
+ });
3164
+ let MqttClient = _MqttClient;
3165
+ const _Storage = class _Storage {
3166
+ /**
3167
+ * 将键值对存储到localStorage中
3168
+ *
3169
+ * @param key 键名
3170
+ * @param value 值,默认为null
3171
+ * @param options 存储选项,可选参数
3172
+ * @param options.expires 过期时间,单位为毫秒,默认为null
3173
+ */
3174
+ static set(key, value = null, options = {}) {
3175
+ var query_key = this._getPrefixedKey(key, options);
3176
+ try {
3177
+ const { expires } = options;
3178
+ const data = { data: value };
3179
+ if (expires) {
3180
+ data.expires = expires;
3181
+ }
3182
+ localStorage.setItem(query_key, JSON.stringify(data));
3183
+ } catch (e) {
3184
+ if (console) console.warn(`Storage didn't successfully save the '{"${key}": "${value}"}' pair, because the localStorage is full.`);
3185
+ }
3186
+ }
3187
+ /**
3188
+ * 从localStorage中获取指定key的存储值
3189
+ *
3190
+ * @param key 存储键名
3191
+ * @param missing 当获取不到指定key的存储值时返回的默认值
3192
+ * @param options 其他配置选项
3193
+ * @returns 返回指定key的存储值,若获取不到则返回missing参数指定的默认值
3194
+ */
3195
+ static get(key, missing, options) {
3196
+ var query_key = this._getPrefixedKey(key, options), value;
3197
+ try {
3198
+ value = JSON.parse(localStorage.getItem(query_key) || "");
3199
+ } catch (e) {
3200
+ if (localStorage[query_key]) {
3201
+ value = { data: localStorage.getItem(query_key) };
3202
+ } else {
3203
+ value = null;
3204
+ }
3205
+ }
3206
+ if (!value) {
3207
+ return missing;
3208
+ } else if (typeof value === "object" && typeof value.data !== "undefined") {
3209
+ const expires = value.expires;
3210
+ if (expires && Date.now() > expires) {
3211
+ return missing;
3212
+ }
3213
+ return value.data;
3214
+ }
3215
+ }
3216
+ static keys() {
3217
+ const keys = [];
3218
+ var allKeys = Object.keys(localStorage);
3219
+ if (_Storage.prefix.length === 0) {
3220
+ return allKeys;
3221
+ }
3222
+ allKeys.forEach(function(key) {
3223
+ if (key.indexOf(_Storage.prefix) !== -1) {
3224
+ keys.push(key.replace(_Storage.prefix, ""));
3225
+ }
3226
+ });
3227
+ return keys;
3228
+ }
3229
+ static getAll(includeKeys) {
3230
+ var keys = _Storage.keys();
3231
+ if (includeKeys) {
3232
+ const result = [];
3233
+ keys.forEach((key) => {
3234
+ if (includeKeys.includes(key)) {
3235
+ const tempObj = {};
3236
+ tempObj[key] = _Storage.get(key, null, null);
3237
+ result.push(tempObj);
3238
+ }
3239
+ });
3240
+ return result;
3241
+ }
3242
+ return keys.map((key) => _Storage.get(key, null, null));
3243
+ }
3244
+ static remove(key, options) {
3245
+ var queryKey = this._getPrefixedKey(key, options);
3246
+ localStorage.removeItem(queryKey);
3247
+ }
3248
+ static clear(options) {
3249
+ if (_Storage.prefix.length) {
3250
+ this.keys().forEach((key) => {
3251
+ localStorage.removeItem(this._getPrefixedKey(key, options));
3252
+ });
3253
+ } else {
3254
+ localStorage.clear();
3255
+ }
3256
+ }
3257
+ };
3258
+ __publicField(_Storage, "prefix", "");
3259
+ __publicField(_Storage, "_getPrefixedKey", function(key, options) {
3260
+ options = options || {};
3261
+ if (options.noPrefix) {
3262
+ return key;
3263
+ } else {
3264
+ return _Storage.prefix + key;
3265
+ }
3266
+ });
3267
+ let Storage = _Storage;
3198
3268
  export {
3199
3269
  AjaxUtil,
3200
3270
  ArrayUtil,
@@ -3202,7 +3272,7 @@ export {
3202
3272
  AudioPlayer,
3203
3273
  BrowserUtil,
3204
3274
  CanvasDrawer,
3205
- ColorUtil,
3275
+ Color,
3206
3276
  Cookie,
3207
3277
  CoordsUtil,
3208
3278
  DateUtil,
@@ -3227,6 +3297,6 @@ export {
3227
3297
  Storage,
3228
3298
  StringUtil,
3229
3299
  UrlUtil,
3230
- CommUtil as Util,
3300
+ CommUtils as Util,
3231
3301
  WebSocketClient
3232
3302
  };