gis-common 4.2.2 → 4.2.4

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) => {
@@ -274,17 +278,14 @@ class CanvasDrawer {
274
278
  }
275
279
  }
276
280
  static createCanvas(width = 1, height = 1) {
277
- let canvas;
278
- if (typeof document !== "undefined") {
279
- canvas = document.createElement("canvas");
280
- if (width) {
281
- canvas.width = width;
282
- }
283
- if (height) {
284
- canvas.height = height;
285
- }
286
- return canvas;
281
+ const canvas = document.createElement("canvas");
282
+ if (width) {
283
+ canvas.width = width;
284
+ }
285
+ if (height) {
286
+ canvas.height = height;
287
287
  }
288
+ return canvas;
288
289
  }
289
290
  }
290
291
  class EventDispatcher {
@@ -476,7 +477,7 @@ class WebSocketClient extends EventDispatcher {
476
477
  }, 1e3);
477
478
  }
478
479
  }
479
- const CommUtils = {
480
+ const CommUtil = {
480
481
  /**
481
482
  * 获取数据类型
482
483
  *
@@ -535,9 +536,6 @@ const CommUtils = {
535
536
  return false;
536
537
  }
537
538
  },
538
- isNotEmpty(val) {
539
- return !this.isEmpty(val);
540
- },
541
539
  /**
542
540
  * 将JSON对象转换为FormData对象
543
541
  *
@@ -937,10 +935,10 @@ const _MqttClient = class _MqttClient extends EventDispatcher {
937
935
  __publicField(this, "options");
938
936
  __publicField(this, "client");
939
937
  __publicField(this, "topics");
940
- this.context = CommUtils.extend(_MqttClient.defaultContext, config);
938
+ this.context = CommUtil.extend(_MqttClient.defaultContext, config);
941
939
  this.options = {
942
940
  connectTimeout: this.context.MQTT_TIMEOUTM,
943
- clientId: CommUtils.guid(),
941
+ clientId: CommUtil.guid(),
944
942
  username: this.context.MQTT_USERNAME,
945
943
  password: this.context.MQTT_PASSWORD,
946
944
  clean: true
@@ -1247,7 +1245,7 @@ const AjaxUtil = {
1247
1245
  * @param callback - callback function when completed
1248
1246
  */
1249
1247
  jsonp(url, callback) {
1250
- const name = "_jsonp_" + CommUtils.guid();
1248
+ const name = "_jsonp_" + CommUtil.guid();
1251
1249
  const head = document.getElementsByTagName("head")[0];
1252
1250
  if (url.includes("?")) {
1253
1251
  url += "&callback=" + name;
@@ -1286,7 +1284,7 @@ const AjaxUtil = {
1286
1284
  * );
1287
1285
  */
1288
1286
  get(url, options, cb) {
1289
- if (CommUtils.isFunction(options)) {
1287
+ if (CommUtil.isFunction(options)) {
1290
1288
  const t = cb;
1291
1289
  cb = options;
1292
1290
  options = t;
@@ -1418,7 +1416,7 @@ const AjaxUtil = {
1418
1416
  * );
1419
1417
  */
1420
1418
  getArrayBuffer(url, options, cb) {
1421
- if (CommUtils.isFunction(options)) {
1419
+ if (CommUtil.isFunction(options)) {
1422
1420
  const t = cb;
1423
1421
  cb = options;
1424
1422
  options = t;
@@ -1471,7 +1469,7 @@ const AjaxUtil = {
1471
1469
  * );
1472
1470
  */
1473
1471
  getJSON(url, options, cb) {
1474
- if (CommUtils.isFunction(options)) {
1472
+ if (CommUtil.isFunction(options)) {
1475
1473
  const t = cb;
1476
1474
  cb = options;
1477
1475
  options = t;
@@ -1488,691 +1486,519 @@ const AjaxUtil = {
1488
1486
  return this.get(url, options, callback);
1489
1487
  }
1490
1488
  };
1491
- const BrowserUtil = {
1489
+ const GeoUtil = {
1490
+ toRadian: Math.PI / 180,
1491
+ R: 6371393,
1492
1492
  /**
1493
- * 获取浏览器类型
1493
+ * 判断给定的经纬度是否合法
1494
1494
  *
1495
- * @returns 返回浏览器类型字符串,可能的值为 'IE'、'Firefox'、'Chrome'、'Opera'、'Safari' 或 'Unknown'
1495
+ * @param lng 经度值
1496
+ * @param lat 纬度值
1497
+ * @returns 如果经纬度合法,返回true;否则返回false
1496
1498
  */
1497
- getExplorer() {
1498
- var explorer = window.navigator.userAgent;
1499
- if (explorer.indexOf("MSIE") >= 0 || /Trident\//.test(explorer)) {
1500
- return "IE";
1501
- } else if (explorer.indexOf("Firefox") >= 0) {
1502
- return "Firefox";
1503
- } else if (explorer.indexOf("Chrome") >= 0) {
1504
- return "Chrome";
1505
- } else if (explorer.indexOf("Opera") >= 0) {
1506
- return "Opera";
1507
- } else if (explorer.indexOf("Safari") >= 0 && explorer.indexOf("Chrome") === -1) {
1508
- return "Safari";
1509
- }
1510
- return "Unknown";
1499
+ isLnglat(lng, lat) {
1500
+ return !isNaN(lng) && !isNaN(lat) && !!(+lat > -90 && +lat < 90 && +lng > -180 && +lng < 180);
1511
1501
  },
1512
1502
  /**
1513
- * 检测操作系统类型
1503
+ * 计算两哥平面坐标点间的距离
1514
1504
  *
1515
- * @returns 返回操作系统类型字符串,可能的值有:'MS Windows'、'Apple mac'、'Linux'、'Unix'
1505
+ * @param p1 坐标点1,包含x和y属性
1506
+ * @param p2 坐标点2,包含x和y属性
1507
+ * @returns 返回两点间的欧几里得距离
1516
1508
  */
1517
- detectOS() {
1518
- let os_type = "";
1519
- const windows = navigator.userAgent.indexOf("Windows", 0) != -1 ? 1 : 0;
1520
- const mac = navigator.userAgent.indexOf("mac", 0) != -1 ? 1 : 0;
1521
- const linux = navigator.userAgent.indexOf("Linux", 0) != -1 ? 1 : 0;
1522
- const unix = navigator.userAgent.indexOf("X11", 0) != -1 ? 1 : 0;
1523
- if (windows) os_type = "MS Windows";
1524
- else if (mac) os_type = "Apple mac";
1525
- else if (linux) os_type = "Linux";
1526
- else if (unix) os_type = "Unix";
1527
- return os_type;
1509
+ distance(p1, p2) {
1510
+ return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
1528
1511
  },
1529
1512
  /**
1530
- * 切换全屏状态
1513
+ * 计算两个经纬度点之间的距离
1531
1514
  *
1532
- * @param status 是否全屏
1515
+ * @param A 经纬度点A,包含lng(经度)和lat(纬度)两个属性
1516
+ * @param B 经纬度点B,包含lng(经度)和lat(纬度)两个属性
1517
+ * @returns 返回两点之间的距离,单位为米
1533
1518
  */
1534
- switchFullScreen(status) {
1535
- if (status) {
1536
- const element = document.documentElement;
1537
- if (element.requestFullscreen) {
1538
- element.requestFullscreen();
1539
- } else if ("msRequestFullscreen" in element) {
1540
- element.msRequestFullscreen();
1541
- } else if ("mozRequestFullScreen" in element) {
1542
- element.mozRequestFullScreen();
1543
- } else if ("webkitRequestFullscreen" in element) {
1544
- element.webkitRequestFullscreen();
1545
- }
1546
- } else {
1547
- if (document.exitFullscreen) {
1548
- document.exitFullscreen();
1549
- } else if ("msExitFullscreen" in document) {
1550
- document.msExitFullscreen();
1551
- } else if ("mozCancelFullScreen" in document) {
1552
- document.mozCancelFullScreen();
1553
- } else if ("webkitExitFullscreen" in document) {
1554
- document.webkitExitFullscreen();
1555
- }
1556
- }
1519
+ distanceByPoints(A, B) {
1520
+ const { lng: lngA, lat: latA } = A;
1521
+ const { lng: lngB, lat: latB } = B;
1522
+ const earthR = 6371e3;
1523
+ const x = Math.cos(latA * Math.PI / 180) * Math.cos(latB * Math.PI / 180) * Math.cos((lngA - lngB) * Math.PI / 180);
1524
+ const y = Math.sin(latA * Math.PI / 180) * Math.sin(latB * Math.PI / 180);
1525
+ let s = x + y;
1526
+ if (s > 1) s = 1;
1527
+ if (s < -1) s = -1;
1528
+ const alpha = Math.acos(s);
1529
+ const distance = alpha * earthR;
1530
+ return distance;
1557
1531
  },
1558
1532
  /**
1559
- * 刷新缩放比例
1533
+ * 格式化经纬度为度分秒格式
1560
1534
  *
1561
- * @returns 无返回值
1535
+ * @param lng 经度
1536
+ * @param lat 纬度
1537
+ * @returns 返回格式化后的经纬度字符串,格式为:经度度分秒,纬度度分秒
1562
1538
  */
1563
- refreshScale() {
1564
- const baseWidth = document.documentElement.clientWidth || 0;
1565
- const baseHeight = document.documentElement.clientHeight || 0;
1566
- const appElement = document.getElementById("app");
1567
- if (!appElement) return;
1568
- const appStyle = appElement.style;
1569
- const realRatio = baseWidth / baseHeight;
1570
- const designRatio = 16 / 9;
1571
- let scaleRate = baseWidth / 1920;
1572
- if (realRatio > designRatio) {
1573
- scaleRate = baseHeight / 1080;
1539
+ formatLnglat(lng, lat) {
1540
+ let res = "";
1541
+ function formatDegreeToDMS(valueInDegrees) {
1542
+ const degree = Math.floor(valueInDegrees);
1543
+ const minutes = Math.floor((valueInDegrees - degree) * 60);
1544
+ const seconds = (valueInDegrees - degree) * 3600 - minutes * 60;
1545
+ return `${degree}°${minutes}′${seconds.toFixed(2)}″`;
1574
1546
  }
1575
- appStyle.transformOrigin = "left top";
1576
- appStyle.transform = `scale(${scaleRate}) translateX(-49.99%)`;
1577
- appStyle.width = `${baseWidth / scaleRate}px`;
1547
+ if (this.isLnglat(lng, lat)) {
1548
+ res = formatDegreeToDMS(lng) + "," + formatDegreeToDMS(lat);
1549
+ } else if (!isNaN(lng)) {
1550
+ res = formatDegreeToDMS(lng);
1551
+ } else if (!isNaN(lat)) {
1552
+ res = formatDegreeToDMS(lat);
1553
+ }
1554
+ return res;
1578
1555
  },
1579
1556
  /**
1580
- * 获取HTML字体大小
1557
+ * 将经纬度字符串转换为度
1581
1558
  *
1582
- * @returns 无返回值,该函数会直接修改HTML元素的字体大小
1559
+ * @param lng 经度字符串
1560
+ * @param lat 纬度字符串
1561
+ * @returns 转换后的经纬度对象
1583
1562
  */
1584
- getHtmlFontSize() {
1585
- const htmlwidth = document.documentElement.clientWidth || document.body.clientWidth;
1586
- const htmlDom = document.querySelector("html");
1587
- if (htmlDom) {
1588
- htmlDom.style.fontSize = htmlwidth / 192 + "px";
1563
+ transformLnglat(lng, lat) {
1564
+ function dms2deg(dmsString) {
1565
+ const isNegative = /[sw]/i.test(dmsString);
1566
+ let factor = isNegative ? -1 : 1;
1567
+ const numericParts = dmsString.match(/[\d.]+/g) || [];
1568
+ let degrees = 0;
1569
+ for (let i = 0; i < numericParts.length; i++) {
1570
+ degrees += parseFloat(numericParts[i]) / factor;
1571
+ factor *= 60;
1572
+ }
1573
+ return degrees;
1574
+ }
1575
+ if (lng && lat) {
1576
+ return {
1577
+ lng: dms2deg(lng),
1578
+ lat: dms2deg(lat)
1579
+ };
1589
1580
  }
1590
- }
1591
- };
1592
- const CoordsUtil = {
1593
- PI: 3.141592653589793,
1594
- XPI: 3.141592653589793 * 3e3 / 180,
1595
- delta(lat, lng) {
1596
- const a = 6378245;
1597
- const ee = 0.006693421622965943;
1598
- let dLat = this.transformLat(lng - 105, lat - 35);
1599
- let dLon = this.transformLon(lng - 105, lat - 35);
1600
- const radLat = lat / 180 * this.PI;
1601
- let magic = Math.sin(radLat);
1602
- magic = 1 - ee * magic * magic;
1603
- const sqrtMagic = Math.sqrt(magic);
1604
- dLat = dLat * 180 / (a * (1 - ee) / (magic * sqrtMagic) * this.PI);
1605
- dLon = dLon * 180 / (a / sqrtMagic * Math.cos(radLat) * this.PI);
1606
- return { lat: dLat, lng: dLon };
1607
1581
  },
1608
1582
  /**
1609
- * 判断经纬度是否不在中国境内
1583
+ * 射线法判断点是否在多边形内
1610
1584
  *
1611
- * @param lng 经度
1612
- * @param lat 纬度
1613
- * @returns 如果经纬度不在中国境内则返回true,否则返回false
1585
+ * @param p 点对象,包含x和y属性
1586
+ * @param poly 多边形顶点数组,可以是字符串数组或对象数组
1587
+ * @returns 返回字符串,表示点相对于多边形的位置:'in'表示在多边形内,'out'表示在多边形外,'on'表示在多边形上
1614
1588
  */
1615
- outOfChina(lng, lat) {
1616
- if (lng < 72.004 || lng > 137.8347) {
1617
- return true;
1618
- }
1619
- if (lat < 0.8293 || lat > 55.8271) {
1620
- return true;
1589
+ rayCasting(p, poly) {
1590
+ var px = p.x, py = p.y, flag = false;
1591
+ for (var i = 0, l = poly.length, j = l - 1; i < l; j = i, i++) {
1592
+ var sx = poly[i].x, sy = poly[i].y, tx = poly[j].x, ty = poly[j].y;
1593
+ if (sx === px && sy === py || tx === px && ty === py) {
1594
+ return "on";
1595
+ }
1596
+ if (sy < py && ty >= py || sy >= py && ty < py) {
1597
+ var x = sx + (py - sy) * (tx - sx) / (ty - sy);
1598
+ if (x === px) {
1599
+ return "on";
1600
+ }
1601
+ if (x > px) {
1602
+ flag = !flag;
1603
+ }
1604
+ }
1621
1605
  }
1622
- return false;
1606
+ return flag ? "in" : "out";
1623
1607
  },
1624
- // WGS-84 to GCJ-02
1625
- gcjEncrypt(wgsLat, wgsLon) {
1626
- if (this.outOfChina(wgsLat, wgsLon)) {
1627
- return { lat: wgsLat, lng: wgsLon };
1628
- }
1629
- const d = this.delta(wgsLat, wgsLon);
1630
- return { lat: wgsLat + d.lat, lng: wgsLon + d.lng };
1608
+ /**
1609
+ * 旋转点
1610
+ *
1611
+ * @param p1 旋转前点坐标
1612
+ * @param p2 旋转中心坐标
1613
+ * @param θ 旋转角度(顺时针旋转为正)
1614
+ * @returns 旋转后点坐标
1615
+ */
1616
+ rotatePoint(p1, p2, θ) {
1617
+ const x = (p1.x - p2.x) * Math.cos(Math.PI / 180 * -θ) - (p1.y - p2.y) * Math.sin(Math.PI / 180 * -θ) + p2.x;
1618
+ const y = (p1.x - p2.x) * Math.sin(Math.PI / 180 * -θ) + (p1.y - p2.y) * Math.cos(Math.PI / 180 * -θ) + p2.y;
1619
+ return { x, y };
1631
1620
  },
1632
- // GCJ-02 to WGS-84
1633
- gcjDecrypt(gcjLat, gcjLon) {
1634
- if (this.outOfChina(gcjLat, gcjLon)) {
1635
- return { lat: gcjLat, lng: gcjLon };
1636
- }
1637
- const d = this.delta(gcjLat, gcjLon);
1638
- return { lat: gcjLat - d.lat, lng: gcjLon - d.lng };
1639
- },
1640
- // GCJ-02 to WGS-84 exactly
1641
- gcjDecryptExact(gcjLat, gcjLon) {
1642
- const initDelta = 0.01;
1643
- const threshold = 1e-9;
1644
- let dLat = initDelta;
1645
- let dLon = initDelta;
1646
- let mLat = gcjLat - dLat;
1647
- let mLon = gcjLon - dLon;
1648
- let pLat = gcjLat + dLat;
1649
- let pLon = gcjLon + dLon;
1650
- let wgsLat = 0;
1651
- let wgsLon = 0;
1652
- let i = 0;
1653
- while (1) {
1654
- wgsLat = (mLat + pLat) / 2;
1655
- wgsLon = (mLon + pLon) / 2;
1656
- const tmp = this.gcjEncrypt(wgsLat, wgsLon);
1657
- dLat = tmp.lat - gcjLat;
1658
- dLon = tmp.lng - gcjLon;
1659
- if (Math.abs(dLat) < threshold && Math.abs(dLon) < threshold) {
1660
- break;
1661
- }
1662
- if (dLat > 0) pLat = wgsLat;
1663
- else mLat = wgsLat;
1664
- if (dLon > 0) pLon = wgsLon;
1665
- else mLon = wgsLon;
1666
- if (++i > 1e4) break;
1667
- }
1668
- return { lat: wgsLat, lng: wgsLon };
1669
- },
1670
- // GCJ-02 to BD-09
1671
- bdEncrypt(gcjLat, gcjLon) {
1672
- const x = gcjLon;
1673
- const y = gcjLat;
1674
- const z = Math.sqrt(x * x + y * y) + 2e-5 * Math.sin(y * this.XPI);
1675
- const theta = Math.atan2(y, x) + 3e-6 * Math.cos(x * this.XPI);
1676
- const bdLon = z * Math.cos(theta) + 65e-4;
1677
- const bdLat = z * Math.sin(theta) + 6e-3;
1678
- return { lat: bdLat, lng: bdLon };
1679
- },
1680
- // BD-09 to GCJ-02
1681
- bdDecrypt(bdLat, bdLon) {
1682
- const x = bdLon - 65e-4;
1683
- const y = bdLat - 6e-3;
1684
- const z = Math.sqrt(x * x + y * y) - 2e-5 * Math.sin(y * this.XPI);
1685
- const theta = Math.atan2(y, x) - 3e-6 * Math.cos(x * this.XPI);
1686
- const gcjLon = z * Math.cos(theta);
1687
- const gcjLat = z * Math.sin(theta);
1688
- return { lat: gcjLat, lng: gcjLon };
1689
- },
1690
- // WGS-84 to Web mercator
1691
- // mercatorLat -> y mercatorLon -> x
1692
- mercatorEncrypt(wgsLat, wgsLon) {
1693
- const x = wgsLon * 2003750834e-2 / 180;
1694
- let y = Math.log(Math.tan((90 + wgsLat) * this.PI / 360)) / (this.PI / 180);
1695
- y = y * 2003750834e-2 / 180;
1696
- return { lat: y, lng: x };
1697
- },
1698
- // Web mercator to WGS-84
1699
- // mercatorLat -> y mercatorLon -> x
1700
- mercatorDecrypt(mercatorLat, mercatorLon) {
1701
- const x = mercatorLon / 2003750834e-2 * 180;
1702
- let y = mercatorLat / 2003750834e-2 * 180;
1703
- y = 180 / this.PI * (2 * Math.atan(Math.exp(y * this.PI / 180)) - this.PI / 2);
1704
- return { lat: y, lng: x };
1705
- },
1706
- transformLat(x, y) {
1707
- let ret = -100 + 2 * x + 3 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
1708
- ret += (20 * Math.sin(6 * x * this.PI) + 20 * Math.sin(2 * x * this.PI)) * 2 / 3;
1709
- ret += (20 * Math.sin(y * this.PI) + 40 * Math.sin(y / 3 * this.PI)) * 2 / 3;
1710
- ret += (160 * Math.sin(y / 12 * this.PI) + 320 * Math.sin(y * this.PI / 30)) * 2 / 3;
1711
- return ret;
1712
- },
1713
- transformLon(x, y) {
1714
- let ret = 300 + x + 2 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
1715
- ret += (20 * Math.sin(6 * x * this.PI) + 20 * Math.sin(2 * x * this.PI)) * 2 / 3;
1716
- ret += (20 * Math.sin(x * this.PI) + 40 * Math.sin(x / 3 * this.PI)) * 2 / 3;
1717
- ret += (150 * Math.sin(x / 12 * this.PI) + 300 * Math.sin(x / 30 * this.PI)) * 2 / 3;
1718
- return ret;
1621
+ /**
1622
+ * 根据两个平面坐标点计算方位角和距离
1623
+ *
1624
+ * @param p1 第一个点的坐标对象
1625
+ * @param p2 第二个点的坐标对象
1626
+ * @returns 返回一个对象,包含angle和distance属性,分别表示两点之间的角度(以度为单位,取值范围为0~359)和距离
1627
+ */
1628
+ calcBearAndDis(p1, p2) {
1629
+ const { x: x1, y: y1 } = p1;
1630
+ const { x: x2, y: y2 } = p2;
1631
+ const dx = x2 - x1;
1632
+ const dy = y2 - y1;
1633
+ const distance = Math.sqrt(dx * dx + dy * dy);
1634
+ const angleInRadians = Math.atan2(dy, dx);
1635
+ const angle = (angleInRadians * (180 / Math.PI) + 360 + 90) % 360;
1636
+ return { angle, distance };
1719
1637
  },
1720
1638
  /**
1721
- * 生成指定范围内的随机经纬度坐标
1639
+ * 根据两个经纬度点计算方位角和距离
1722
1640
  *
1723
- * @param min 最小坐标,包含属性 x 和 y,分别表示最小经度和最小纬度
1724
- * @param max 最大坐标,包含属性 x 和 y,分别表示最大经度和最大纬度
1725
- * @returns 返回生成的随机经纬度坐标,包含属性 lat 和 lng,分别表示纬度和经度
1641
+ * @param latlng1 第一个经纬度点
1642
+ * @param latlng2 第二个经纬度点
1643
+ * @returns 包含方位角和距离的对象
1726
1644
  */
1727
- random({ x: minX, y: minY }, { x: maxX, y: maxY }) {
1645
+ calcBearAndDisByPoints(latlng1, latlng2) {
1646
+ var f1 = latlng1.lat * 1, l1 = latlng1.lng * 1, f2 = latlng2.lat * 1, l2 = latlng2.lng * 1;
1647
+ var y = Math.sin((l2 - l1) * this.toRadian) * Math.cos(f2 * this.toRadian);
1648
+ var x = Math.cos(f1 * this.toRadian) * Math.sin(f2 * this.toRadian) - Math.sin(f1 * this.toRadian) * Math.cos(f2 * this.toRadian) * Math.cos((l2 - l1) * this.toRadian);
1649
+ var angle = Math.atan2(y, x) * (180 / Math.PI);
1650
+ var deltaF = (f2 - f1) * this.toRadian;
1651
+ var deltaL = (l2 - l1) * this.toRadian;
1652
+ var a = Math.sin(deltaF / 2) * Math.sin(deltaF / 2) + Math.cos(f1 * this.toRadian) * Math.cos(f2 * this.toRadian) * Math.sin(deltaL / 2) * Math.sin(deltaL / 2);
1653
+ var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
1654
+ var distance = this.R * c;
1728
1655
  return {
1729
- lat: Math.random() * (maxY - minY) + minY,
1730
- lng: Math.random() * (maxX - minX) + minX
1656
+ angle,
1657
+ distance
1731
1658
  };
1732
1659
  },
1733
- deCompose(arr, fn, context) {
1734
- if (!Array.isArray(arr)) {
1735
- return context ? fn.call(context, arr) : fn(arr);
1660
+ /**
1661
+ * 计算点P到线段P1P2的最短距离
1662
+ *
1663
+ * @param p 点P的坐标
1664
+ * @param p1 线段起点P1的坐标
1665
+ * @param p2 线段终点P2的坐标
1666
+ * @returns 点P到线段P1P2的最短距离
1667
+ */
1668
+ distanceToSegment(p, p1, p2) {
1669
+ const x = p.x, y = p.y, x1 = p1.x, y1 = p1.y, x2 = p2.x, y2 = p2.y;
1670
+ const cross = (x2 - x1) * (x - x1) + (y2 - y1) * (y - y1);
1671
+ if (cross <= 0) {
1672
+ return Math.sqrt((x - x1) * (x - x1) + (y - y1) * (y - y1));
1736
1673
  }
1737
- const result = [];
1738
- let p, pp;
1739
- for (let i = 0, len = arr.length; i < len; i++) {
1740
- p = arr[i];
1741
- if (CommUtils.isNil(p)) {
1742
- result.push(null);
1743
- continue;
1744
- }
1745
- if (Array.isArray(p)) {
1746
- result.push(this.deCompose(p, fn, context));
1747
- } else {
1748
- pp = context ? fn.call(context, p) : fn(p);
1749
- result.push(pp);
1750
- }
1674
+ const d2 = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
1675
+ if (cross >= d2) {
1676
+ return Math.sqrt((x - x2) * (x - x2) + (y - y2) * (y - y2));
1751
1677
  }
1752
- return result;
1753
- }
1754
- };
1755
- const ColorUtil = {
1756
- random() {
1757
- let r = Math.floor(Math.random() * 256).toString(16);
1758
- let g = Math.floor(Math.random() * 256).toString(16);
1759
- let b = Math.floor(Math.random() * 256).toString(16);
1760
- r = r.length === 1 ? "0" + r : r;
1761
- g = g.length === 1 ? "0" + g : g;
1762
- b = b.length === 1 ? "0" + b : b;
1763
- return "#" + r + g + b;
1678
+ const r = cross / d2;
1679
+ const px = x1 + (x2 - x1) * r;
1680
+ const py = y1 + (y2 - y1) * r;
1681
+ return Math.sqrt((x - px) * (x - px) + (y - py) * (y - py));
1764
1682
  },
1765
1683
  /**
1766
- * 将RGB颜色值转换为十六进制颜色值
1684
+ * 根据给定的经纬度、角度和距离计算新的经纬度点
1767
1685
  *
1768
- * @param rgb RGB颜色值数组,包含三个0-255之间的整数
1769
- * @returns 转换后的十六进制颜色值,以#开头
1686
+ * @param latlng 给定的经纬度点,类型为LngLat
1687
+ * @param angle 角度值,单位为度,表示从当前点出发的方向
1688
+ * @param distance 距离值,单位为米,表示从当前点出发的距离
1689
+ * @returns 返回计算后的新经纬度点,类型为{lat: number, lng: number}
1770
1690
  */
1771
- rgb2hex(rgb) {
1772
- var hex = "#" + ((1 << 24) + (rgb[0] << 16) + (rgb[1] << 8) + rgb[2]).toString(16).slice(1);
1773
- return hex;
1691
+ calcPointByBearAndDis(latlng, angle, distance) {
1692
+ const sLat = MathUtils.deg2Rad(latlng.lat * 1);
1693
+ const sLng = MathUtils.deg2Rad(latlng.lng * 1);
1694
+ const d = distance / this.R;
1695
+ angle = MathUtils.deg2Rad(angle);
1696
+ const lat = Math.asin(Math.sin(sLat) * Math.cos(d) + Math.cos(sLat) * Math.sin(d) * Math.cos(angle));
1697
+ const lon = sLng + Math.atan2(Math.sin(angle) * Math.sin(d) * Math.cos(sLat), Math.cos(d) - Math.sin(sLat) * Math.sin(lat));
1698
+ return {
1699
+ lat: MathUtils.rad2Deg(lat),
1700
+ lng: MathUtils.rad2Deg(lon)
1701
+ };
1774
1702
  },
1775
1703
  /**
1776
- * 将RGB颜色值转换为RGBA颜色值,并返回转换后的颜色值。
1704
+ * 将墨卡托坐标转换为经纬度坐标
1777
1705
  *
1778
- * @param rgbValue RGB颜色值,格式为"rgb(r, g, b)"。
1779
- * @returns 转换后的RGBA颜色值,格式为"rgba(r, g, b, 1)"。如果输入值不符合RGB格式,则返回原值。
1706
+ * @param x 墨卡托坐标的x值
1707
+ * @param y 墨卡托坐标的y值
1708
+ * @returns 返回包含转换后的经度lng和纬度lat的对象
1780
1709
  */
1781
- rgbToRgba(rgbValue) {
1782
- const rgb = /rgb\((\d+,\s*[\d]+,\s*[\d]+)\)/g.exec(rgbValue);
1783
- return rgb ? `rgba(${rgb[1]}, 1)` : rgbValue;
1710
+ mercatorTolonlat(x, y) {
1711
+ const lng = x / 2003750834e-2 * 180;
1712
+ var mmy = y / 2003750834e-2 * 180;
1713
+ const lat = 180 / Math.PI * (2 * Math.atan(Math.exp(mmy * Math.PI / 180)) - Math.PI / 2);
1714
+ return { lng, lat };
1784
1715
  },
1785
1716
  /**
1786
- * 将十六进制颜色值转换为rgba格式的颜色值
1717
+ * 将经纬度坐标转换为墨卡托坐标
1787
1718
  *
1788
- * @param hexValue 十六进制颜色值,可带或不带#前缀,支持3位和6位表示
1789
- * @returns 返回rgba格式的颜色值,格式为rgba(r,g,b,1)
1719
+ * @param lng 经度值
1720
+ * @param lat 纬度值
1721
+ * @returns 墨卡托坐标对象,包含x和y属性
1790
1722
  */
1791
- hexToRgba(hexValue) {
1792
- const rgxShort = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
1793
- const hex = hexValue.replace(rgxShort, (m, r2, g2, b2) => r2 + r2 + g2 + g2 + b2 + b2);
1794
- const rgx = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i;
1795
- const rgb = rgx.exec(hex);
1796
- if (!rgb) {
1797
- return hexValue;
1798
- }
1799
- const r = parseInt(rgb[1], 16);
1800
- const g = parseInt(rgb[2], 16);
1801
- const b = parseInt(rgb[3], 16);
1802
- return `rgba(${r},${g},${b},1)`;
1723
+ lonlatToMercator(lng, lat) {
1724
+ var earthRad = 6378137;
1725
+ const x = lng * Math.PI / 180 * earthRad;
1726
+ var a = lat * Math.PI / 180;
1727
+ const y = earthRad / 2 * Math.log((1 + Math.sin(a)) / (1 - Math.sin(a)));
1728
+ return { x, y };
1803
1729
  },
1804
1730
  /**
1805
- * 将 HSL 颜色值转换为 RGBA 颜色值
1731
+ * 根据百分比获取坐标
1806
1732
  *
1807
- * @param hslValue HSL 颜色值字符串,格式为 "hsl(h, s%, l%)" 或 "hsla(h, s%, l%, a)",其中 h 为色相,s 为饱和度,l 为亮度,a 为透明度(可选)。
1808
- * @returns 转换后的 RGBA 颜色值字符串,格式为 "rgba(r, g, b, a)",其中 r、g、b 为红绿蓝分量,a 为透明度。若输入为空或无效,则返回 null。
1733
+ * @param start 起点坐标
1734
+ * @param end 终点坐标
1735
+ * @param percent 百分比,取值范围0-1
1736
+ * @returns 返回插值后的坐标
1809
1737
  */
1810
- hslToRgba(hslValue) {
1811
- if (!hslValue) {
1812
- return null;
1813
- }
1814
- const hsl = /hsl\((\d+),\s*([\d.]+)%,\s*([\d.]+)%\)/g.exec(hslValue) || /hsla\((\d+),\s*([\d.]+)%,\s*([\d.]+)%,\s*([\d.]+)\)/g.exec(hslValue);
1815
- if (!hsl) {
1816
- return null;
1817
- }
1818
- const h = parseInt(hsl[1], 10) / 360;
1819
- const s = parseInt(hsl[2], 10) / 100;
1820
- const l = parseInt(hsl[3], 10) / 100;
1821
- const a = hsl[4] ? parseFloat(hsl[4]) : 1;
1822
- function hue2rgb(p, q, t) {
1823
- if (t < 0) t += 1;
1824
- if (t > 1) t -= 1;
1825
- if (t < 1 / 6) return p + (q - p) * 6 * t;
1826
- if (t < 1 / 2) return q;
1827
- if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
1828
- return p;
1829
- }
1830
- let r, g, b;
1831
- if (s === 0) {
1832
- r = g = b = l;
1833
- } else {
1834
- const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
1835
- const p = 2 * l - q;
1836
- r = hue2rgb(p, q, h + 1 / 3);
1837
- g = hue2rgb(p, q, h);
1838
- b = hue2rgb(p, q, h - 1 / 3);
1839
- }
1840
- return `rgba(${Math.round(r * 255)},${Math.round(g * 255)},${Math.round(b * 255)},${a})`;
1841
- },
1842
- isHex(a) {
1843
- return /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(a);
1844
- },
1845
- isRgb(a) {
1846
- return /^rgb/.test(a);
1847
- },
1848
- isHsl(a) {
1849
- return /^hsl/.test(a);
1850
- },
1851
- isColor(a) {
1852
- return this.isHex(a) || this.isRgb(a) || this.isHsl(a);
1853
- },
1854
- colorToRgb(val) {
1855
- if (this.isRgb(val)) return this.rgbToRgba(val);
1856
- if (this.isHex(val)) return this.hexToRgba(val);
1857
- if (this.isHsl(val)) return this.hslToRgba(val);
1858
- }
1859
- };
1860
- const myDate = Object.create(Date);
1861
- myDate.prototype.format = function(fmt = "yyyy-MM-dd hh:mm:ss") {
1862
- const o = {
1863
- "M+": this.getMonth() + 1,
1864
- // 月份
1865
- "d+": this.getDate(),
1866
- // 日
1867
- "h+": this.getHours() % 12,
1868
- // 小时
1869
- "H+": this.getHours(),
1870
- // 小时 (24小时制)
1871
- "m+": this.getMinutes(),
1872
- // 分
1873
- "s+": this.getSeconds(),
1874
- // 秒
1875
- "q+": Math.floor((this.getMonth() + 3) / 3),
1876
- // 季度
1877
- S: this.getMilliseconds()
1878
- // 毫秒
1879
- };
1880
- if (/(y+)/.test(fmt)) {
1881
- fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
1882
- }
1883
- for (const k in o) {
1884
- if (new RegExp("(" + k + ")").test(fmt)) {
1885
- const len = k.length === 1 ? 1 : Number(k.slice(1));
1886
- fmt = fmt.replace(RegExp.$1, ("00" + o[k]).substr(("" + o[k]).length + len - (o[k] + "").length));
1887
- }
1888
- }
1889
- return fmt;
1890
- };
1891
- myDate.prototype.addDate = function(interval, number) {
1892
- const date = new Date(this);
1893
- switch (interval) {
1894
- case "y":
1895
- date.setFullYear(this.getFullYear() + number);
1896
- break;
1897
- case "q":
1898
- date.setMonth(this.getMonth() + number * 3);
1899
- break;
1900
- case "M":
1901
- date.setMonth(this.getMonth() + number);
1902
- break;
1903
- case "w":
1904
- date.setDate(this.getDate() + number * 7);
1905
- break;
1906
- case "d":
1907
- date.setDate(this.getDate() + number);
1908
- break;
1909
- case "h":
1910
- date.setHours(this.getHours() + number);
1911
- break;
1912
- case "m":
1913
- date.setMinutes(this.getMinutes() + number);
1914
- break;
1915
- case "s":
1916
- date.setSeconds(this.getSeconds() + number);
1917
- break;
1918
- default:
1919
- date.setDate(this.getDate() + number);
1920
- break;
1738
+ interpolate({ x: x1, y: y1, z: z1 = 0 }, { x: x2, y: y2, z: z2 = 0 }, percent) {
1739
+ const dx = x2 - x1, dy = y2 - y1, dz = z2 - z1;
1740
+ return { x: x1 + dx * percent, y: y1 + dy * percent, z: z1 + dz * percent };
1921
1741
  }
1922
- return date;
1923
1742
  };
1924
- const DateUtil = {
1925
- lastMonthDate: new Date((/* @__PURE__ */ new Date()).getFullYear(), (/* @__PURE__ */ new Date()).getMonth() - 1, 1),
1926
- thisMonthDate: new Date((/* @__PURE__ */ new Date()).getFullYear(), (/* @__PURE__ */ new Date()).getMonth(), 1),
1927
- nextMonthDate: new Date((/* @__PURE__ */ new Date()).getFullYear(), (/* @__PURE__ */ new Date()).getMonth() + 1, 1),
1928
- lastWeekDate: new Date((/* @__PURE__ */ new Date()).getFullYear(), (/* @__PURE__ */ new Date()).getMonth(), (/* @__PURE__ */ new Date()).getDate() + 1 - 7 - (/* @__PURE__ */ new Date()).getDay()),
1929
- thisWeekDate: new Date((/* @__PURE__ */ new Date()).getFullYear(), (/* @__PURE__ */ new Date()).getMonth(), (/* @__PURE__ */ new Date()).getDate() + 1 - (/* @__PURE__ */ new Date()).getDay()),
1930
- nextWeekDate: new Date((/* @__PURE__ */ new Date()).getFullYear(), (/* @__PURE__ */ new Date()).getMonth(), (/* @__PURE__ */ new Date()).getDate() + 1 + 7 - (/* @__PURE__ */ new Date()).getDay()),
1931
- lastDayDate: new Date((/* @__PURE__ */ new Date()).getFullYear(), (/* @__PURE__ */ new Date()).getMonth(), (/* @__PURE__ */ new Date()).getDate() - 1),
1932
- thisDayDate: new Date((/* @__PURE__ */ new Date()).setHours(0, 0, 0, 0)),
1933
- nextDayDate: new Date((/* @__PURE__ */ new Date()).getFullYear(), (/* @__PURE__ */ new Date()).getMonth(), (/* @__PURE__ */ new Date()).getDate() + 1),
1934
- parseDate(str) {
1935
- if (typeof str == "string") {
1936
- var results = str.match(/^ *(\d{4})-(\d{1,2})-(\d{1,2}) *$/);
1937
- if (results && results.length > 3) return new Date(parseInt(results[1]), parseInt(results[2]) - 1, parseInt(results[3]));
1938
- results = str.match(/^ *(\d{4})-(\d{1,2})-(\d{1,2}) +(\d{1,2}):(\d{1,2}):(\d{1,2}) *$/);
1939
- if (results && results.length > 6)
1940
- return new Date(
1941
- parseInt(results[1]),
1942
- parseInt(results[2]) - 1,
1943
- parseInt(results[3]),
1944
- parseInt(results[4]),
1945
- parseInt(results[5]),
1946
- parseInt(results[6])
1947
- );
1948
- results = str.match(/^ *(\d{4})-(\d{1,2})-(\d{1,2}) +(\d{1,2}):(\d{1,2}):(\d{1,2})\.(\d{1,9}) *$/);
1949
- if (results && results.length > 7)
1950
- return new Date(
1951
- parseInt(results[1]),
1952
- parseInt(results[2]) - 1,
1953
- parseInt(results[3]),
1954
- parseInt(results[4]),
1955
- parseInt(results[5]),
1956
- parseInt(results[6]),
1957
- parseInt(results[7])
1958
- );
1959
- }
1960
- return null;
1961
- },
1743
+ const StringUtil = {
1962
1744
  /**
1963
- * 格式化时间间隔
1745
+ * 校验字符串是否符合指定类型
1964
1746
  *
1965
- * @param startTime 开始时间,可以是字符串、数字或日期类型
1966
- * @param endTime 结束时间,可以是字符串、数字或日期类型
1967
- * @returns 返回格式化后的时间间隔字符串,格式为"天数 天 小时 时 分钟 分 秒 秒"或"少于1秒"
1747
+ * @param str 待校验字符串
1748
+ * @param type 校验类型,可选值包括:
1749
+ * - 'phone': 手机号码
1750
+ * - 'tel': 座机
1751
+ * - 'card': 身份证
1752
+ * - 'pwd': 密码(以字母开头,长度在6~18之间,只能包含字母、数字和下划线)
1753
+ * - 'postal': 邮政编码
1754
+ * - 'QQ': QQ号
1755
+ * - 'email': 邮箱
1756
+ * - 'money': 金额(小数点2位)
1757
+ * - 'URL': 网址
1758
+ * - 'IP': IP地址
1759
+ * - 'date': 日期时间
1760
+ * - 'number': 数字
1761
+ * - 'english': 英文
1762
+ * - 'chinese': 中文
1763
+ * - 'lower': 小写字母
1764
+ * - 'upper': 大写字母
1765
+ * - 'HTML': HTML标记
1766
+ * @returns 校验结果,符合返回true,否则返回false
1968
1767
  */
1969
- formatDateInterval(startTime, endTime) {
1970
- const dateCreateTime = new Date(startTime);
1971
- const dateFinishTime = new Date(endTime);
1972
- const dateInterval = dateFinishTime.getTime() - dateCreateTime.getTime();
1973
- const days = Math.floor(dateInterval / (24 * 3600 * 1e3));
1974
- const leave1 = dateInterval % (24 * 3600 * 1e3);
1975
- const hours = Math.floor(leave1 / (3600 * 1e3));
1976
- const leave2 = leave1 % (3600 * 1e3);
1977
- const minutes = Math.floor(leave2 / (60 * 1e3));
1978
- const leave3 = leave2 % (60 * 1e3);
1979
- const seconds = Math.round(leave3 / 1e3);
1980
- let intervalDes = "";
1981
- if (days > 0) {
1982
- intervalDes += days + "天";
1983
- }
1984
- if (hours > 0) {
1985
- intervalDes += hours + "";
1986
- }
1987
- if (minutes > 0) {
1988
- intervalDes += minutes + "分";
1989
- }
1990
- if (seconds > 0) {
1991
- intervalDes += seconds + "";
1992
- }
1993
- if (days === 0 && hours === 0 && minutes === 0 && seconds === 0) {
1994
- intervalDes = "少于1秒";
1768
+ checkStr(str, type) {
1769
+ switch (type) {
1770
+ case "phone":
1771
+ return /^1[3|4|5|6|7|8|9][0-9]{9}$/.test(str);
1772
+ case "tel":
1773
+ return /^(0\d{2,3}-\d{7,8})(-\d{1,4})?$/.test(str);
1774
+ case "card":
1775
+ return /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/.test(str);
1776
+ case "pwd":
1777
+ return /^[a-zA-Z]\w{5,17}$/.test(str);
1778
+ case "postal":
1779
+ return /[1-9]\d{5}(?!\d)/.test(str);
1780
+ case "QQ":
1781
+ return /^[1-9][0-9]{4,9}$/.test(str);
1782
+ case "email":
1783
+ return /^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/.test(str);
1784
+ case "money":
1785
+ return /^\d*(?:\.\d{0,2})?$/.test(str);
1786
+ case "URL":
1787
+ return /(http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?/.test(str);
1788
+ case "IP":
1789
+ return /((?:(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d))/.test(str);
1790
+ case "date":
1791
+ return /^(\d{4})\-(\d{2})\-(\d{2}) (\d{2})(?:\:\d{2}|:(\d{2}):(\d{2}))$/.test(str) || /^(\d{4})\-(\d{2})\-(\d{2})$/.test(str);
1792
+ case "number":
1793
+ return /^[0-9]$/.test(str);
1794
+ case "english":
1795
+ return /^[a-zA-Z]+$/.test(str);
1796
+ case "chinese":
1797
+ return /^[\u4E00-\u9FA5]+$/.test(str);
1798
+ case "lower":
1799
+ return /^[a-z]+$/.test(str);
1800
+ case "upper":
1801
+ return /^[A-Z]+$/.test(str);
1802
+ case "HTML":
1803
+ return /<("[^"]*"|'[^']*'|[^'">])*>/.test(str);
1804
+ default:
1805
+ return true;
1995
1806
  }
1996
- return intervalDes;
1997
1807
  },
1998
- formatterCounter(times) {
1999
- const checked = function(j) {
2000
- return (j > 10 ? "" : "0") + (j || 0);
2001
- };
2002
- const houres = checked(Math.floor(times / 3600));
2003
- const level1 = times % 3600;
2004
- const minutes = checked(Math.floor(level1 / 60));
2005
- const leave2 = level1 % 60;
2006
- const seconds = checked(Math.round(leave2));
2007
- return `${houres}:${minutes}:${seconds}`;
2008
- },
2009
- sleep(d) {
2010
- }
2011
- };
2012
- function trim(str) {
2013
- return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, "");
2014
- }
2015
- function splitWords(str) {
2016
- return trim(str).split(/\s+/);
2017
- }
2018
- const DomUtil = {
2019
1808
  /**
2020
- * 获取元素的样式值
1809
+ * 转换字符串大小写
2021
1810
  *
2022
- * @param el 元素对象
2023
- * @param style 样式属性名称
2024
- * @returns 元素的样式值,如果获取不到则返回 null
1811
+ * @param str 待转换的字符串
1812
+ * @param type 转换类型,可选值为 1-5,默认为 4
1813
+ * 1:首字母大写,其余小写
1814
+ * 2:首字母小写,其余大写
1815
+ * 3:字母大小写反转
1816
+ * 4:全部大写
1817
+ * 5:全部小写
1818
+ * @returns 转换后的字符串
2025
1819
  */
2026
- getStyle(el, style) {
2027
- var _a;
2028
- let value = el.style[style];
2029
- if (!value || value === "auto") {
2030
- const css = (_a = document.defaultView) == null ? void 0 : _a.getComputedStyle(el, null);
2031
- value = css ? css[style] : null;
2032
- if (value === "auto") value = null;
1820
+ changeCase(str, type) {
1821
+ type = type || 4;
1822
+ switch (type) {
1823
+ case 1:
1824
+ return str.replace(/\b\w+\b/g, function(word) {
1825
+ return word.substring(0, 1).toUpperCase() + word.substring(1).toLowerCase();
1826
+ });
1827
+ case 2:
1828
+ return str.replace(/\b\w+\b/g, function(word) {
1829
+ return word.substring(0, 1).toLowerCase() + word.substring(1).toUpperCase();
1830
+ });
1831
+ case 3:
1832
+ return str.split("").map(function(word) {
1833
+ if (/[a-z]/.test(word)) {
1834
+ return word.toUpperCase();
1835
+ } else {
1836
+ return word.toLowerCase();
1837
+ }
1838
+ }).join("");
1839
+ case 4:
1840
+ return str.toUpperCase();
1841
+ case 5:
1842
+ return str.toLowerCase();
1843
+ default:
1844
+ return str;
2033
1845
  }
2034
- return value;
2035
1846
  },
2036
1847
  /**
2037
- * 创建一个HTML元素
1848
+ * 根据字符串数组和参数生成新的字符串
2038
1849
  *
2039
- * @param tagName 元素标签名
2040
- * @param className 元素类名
2041
- * @param container 父容器,若传入,则新创建的元素会被添加到该容器中
2042
- * @returns 返回新创建的HTML元素
1850
+ * @param strArray 字符串数组
1851
+ * @param args 可变参数列表,支持 Object、Array 类型和任意其他类型,若为 null 或 undefined,则按类型默认转换为 '{}'、'[]' 或 ''
1852
+ * @returns 返回生成的新字符串
2043
1853
  */
2044
- create(tagName, className, container) {
2045
- const el = document.createElement(tagName);
2046
- el.className = className || "";
2047
- if (container) {
2048
- container.appendChild(el);
2049
- }
2050
- return el;
1854
+ tag(strArray, ...args) {
1855
+ args = args.map((val) => {
1856
+ switch (CommUtil.getDataType(val)) {
1857
+ case "Object":
1858
+ return val || "{}";
1859
+ case "Array":
1860
+ return val || "[]";
1861
+ default:
1862
+ return val || "";
1863
+ }
1864
+ });
1865
+ return strArray.reduce((prev, next, index) => `${prev}${args[index - 1]}${next}`);
2051
1866
  },
2052
1867
  /**
2053
- * 从父节点中移除指定元素。
1868
+ * 计算字符串的字节长度
2054
1869
  *
2055
- * @param el 要移除的元素对象,必须包含parentNode属性。
1870
+ * @param str 需要计算字节长度的字符串
1871
+ * @returns 返回字符串的字节长度
2056
1872
  */
2057
- remove(el) {
2058
- const parent = el.parentNode;
2059
- if (parent) {
2060
- parent.removeChild(el);
2061
- }
1873
+ getByteLength(str) {
1874
+ return str.replace(/[\u0391-\uFFE5]/g, "aa").length;
2062
1875
  },
2063
1876
  /**
2064
- * 清空给定元素的子节点
1877
+ * 截取字符串中指定字节长度的子串
2065
1878
  *
2066
- * @param el 要清空子节点的元素,包含firstChildremoveChild属性
1879
+ * @param str 字符串对象,包含replace、lengthsubstring方法
1880
+ * @param start 截取起始位置
1881
+ * @param n 截取字节长度
1882
+ * @returns 返回截取后的子串
2067
1883
  */
2068
- empty(el) {
2069
- while (el.firstChild) {
2070
- el.removeChild(el.firstChild);
1884
+ subStringByte(str, start, n) {
1885
+ var r = /[^\x00-\xff]/g;
1886
+ if (str.replace(r, "mm").length <= n) {
1887
+ return str;
2071
1888
  }
2072
- },
2073
- /**
2074
- * 将元素移到父节点的最前面
2075
- *
2076
- * @param el 要移动的元素,需要包含 parentNode 属性
2077
- */
2078
- toFront(el) {
2079
- const parent = el.parentNode;
2080
- if (parent && parent.lastChild !== el) {
2081
- parent.appendChild(el);
1889
+ var m = Math.floor(n / 2);
1890
+ for (var i = m; i < str.length; i++) {
1891
+ let sub = str.substring(start, i);
1892
+ if (sub.replace(r, "mm").length >= n) {
1893
+ return sub;
1894
+ }
2082
1895
  }
1896
+ return str;
1897
+ }
1898
+ };
1899
+ const ColorUtil = {
1900
+ random() {
1901
+ let r = Math.floor(Math.random() * 256).toString(16);
1902
+ let g = Math.floor(Math.random() * 256).toString(16);
1903
+ let b = Math.floor(Math.random() * 256).toString(16);
1904
+ r = r.length === 1 ? "0" + r : r;
1905
+ g = g.length === 1 ? "0" + g : g;
1906
+ b = b.length === 1 ? "0" + b : b;
1907
+ return "#" + r + g + b;
2083
1908
  },
2084
1909
  /**
2085
- * 将元素移动到其父节点的最前面
1910
+ * 将RGB颜色值转换为十六进制颜色值
2086
1911
  *
2087
- * @param el 要移动的元素,需要包含parentNode属性
1912
+ * @param rgb RGB颜色值数组,包含三个0-255之间的整数
1913
+ * @returns 转换后的十六进制颜色值,以#开头
2088
1914
  */
2089
- toBack(el) {
2090
- const parent = el.parentNode;
2091
- if (parent && parent.firstChild !== el) {
2092
- parent.insertBefore(el, parent.firstChild);
2093
- }
1915
+ rgb2hex(rgb) {
1916
+ var hex = "#" + ((1 << 24) + (rgb[0] << 16) + (rgb[1] << 8) + rgb[2]).toString(16).slice(1);
1917
+ return hex;
2094
1918
  },
2095
1919
  /**
2096
- * 获取元素的类名
1920
+ * 将RGB颜色值转换为RGBA颜色值,并返回转换后的颜色值。
2097
1921
  *
2098
- * @param el 包含对应元素和类名的对象
2099
- * @param el.correspondingElement 对应的元素
2100
- * @param el.className 类名对象
2101
- * @param el.className.baseVal 类名字符串
2102
- * @returns 返回元素的类名字符串
1922
+ * @param rgbValue RGB颜色值,格式为"rgb(r, g, b)"。
1923
+ * @returns 转换后的RGBA颜色值,格式为"rgba(r, g, b, 1)"。如果输入值不符合RGB格式,则返回原值。
2103
1924
  */
2104
- getClass(el) {
2105
- const shadowElement = (el == null ? void 0 : el.host) || el;
2106
- return shadowElement.className.toString();
1925
+ rgbToRgba(rgbValue) {
1926
+ const rgb = /rgb\((\d+,\s*[\d]+,\s*[\d]+)\)/g.exec(rgbValue);
1927
+ return rgb ? `rgba(${rgb[1]}, 1)` : rgbValue;
2107
1928
  },
2108
1929
  /**
2109
- * 判断元素是否包含指定类名
1930
+ * 将十六进制颜色值转换为rgba格式的颜色值
2110
1931
  *
2111
- * @param el 元素对象,包含classList属性,classList属性包含contains方法
2112
- * @param name 要判断的类名
2113
- * @returns 返回一个布尔值,表示元素是否包含指定类名
1932
+ * @param hexValue 十六进制颜色值,可带或不带#前缀,支持3位和6位表示
1933
+ * @returns 返回rgba格式的颜色值,格式为rgba(r,g,b,1)
2114
1934
  */
2115
- hasClass(el, name) {
2116
- var _a;
2117
- if ((_a = el.classList) == null ? void 0 : _a.contains(name)) {
2118
- return true;
1935
+ hexToRgba(hexValue) {
1936
+ const rgxShort = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
1937
+ const hex = hexValue.replace(rgxShort, (m, r2, g2, b2) => r2 + r2 + g2 + g2 + b2 + b2);
1938
+ const rgx = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i;
1939
+ const rgb = rgx.exec(hex);
1940
+ if (!rgb) {
1941
+ return hexValue;
2119
1942
  }
2120
- const className = this.getClass(el);
2121
- return className.length > 0 && new RegExp(`(^|\\s)${name}(\\s|$)`).test(className);
1943
+ const r = parseInt(rgb[1], 16);
1944
+ const g = parseInt(rgb[2], 16);
1945
+ const b = parseInt(rgb[3], 16);
1946
+ return `rgba(${r},${g},${b},1)`;
2122
1947
  },
2123
1948
  /**
2124
- * 给指定的 HTML 元素添加类名
1949
+ * HSL 颜色值转换为 RGBA 颜色值
2125
1950
  *
2126
- * @param el 要添加类名的 HTML 元素
2127
- * @param name 要添加的类名,多个类名之间用空格分隔
1951
+ * @param hslValue HSL 颜色值字符串,格式为 "hsl(h, s%, l%)" 或 "hsla(h, s%, l%, a)",其中 h 为色相,s 为饱和度,l 为亮度,a 为透明度(可选)。
1952
+ * @returns 转换后的 RGBA 颜色值字符串,格式为 "rgba(r, g, b, a)",其中 r、g、b 为红绿蓝分量,a 为透明度。若输入为空或无效,则返回 null。
2128
1953
  */
2129
- addClass(el, name) {
2130
- if (el.classList !== void 0) {
2131
- const classes = splitWords(name);
2132
- for (let i = 0, len = classes.length; i < len; i++) {
2133
- el.classList.add(classes[i]);
2134
- }
2135
- } else if (!this.hasClass(el, name)) {
2136
- const className = this.getClass(el);
2137
- this.setClass(el, (className ? className + " " : "") + name);
1954
+ hslToRgba(hslValue) {
1955
+ if (!hslValue) {
1956
+ return null;
2138
1957
  }
2139
- },
2140
- /**
2141
- * 从元素中移除指定类名
2142
- *
2143
- * @param el 要移除类名的元素
2144
- * @param name 要移除的类名,多个类名用空格分隔
2145
- */
2146
- removeClass(el, name) {
2147
- if (el.classList !== void 0) {
2148
- const classes = splitWords(name);
2149
- classes.forEach((className) => el.classList.remove(className));
1958
+ const hsl = /hsl\((\d+),\s*([\d.]+)%,\s*([\d.]+)%\)/g.exec(hslValue) || /hsla\((\d+),\s*([\d.]+)%,\s*([\d.]+)%,\s*([\d.]+)\)/g.exec(hslValue);
1959
+ if (!hsl) {
1960
+ return null;
1961
+ }
1962
+ const h = parseInt(hsl[1], 10) / 360;
1963
+ const s = parseInt(hsl[2], 10) / 100;
1964
+ const l = parseInt(hsl[3], 10) / 100;
1965
+ const a = hsl[4] ? parseFloat(hsl[4]) : 1;
1966
+ function hue2rgb(p, q, t) {
1967
+ if (t < 0) t += 1;
1968
+ if (t > 1) t -= 1;
1969
+ if (t < 1 / 6) return p + (q - p) * 6 * t;
1970
+ if (t < 1 / 2) return q;
1971
+ if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
1972
+ return p;
1973
+ }
1974
+ let r, g, b;
1975
+ if (s === 0) {
1976
+ r = g = b = l;
2150
1977
  } else {
2151
- this.setClass(el, (" " + this.getClass(el) + " ").replace(" " + name + " ", " ").trim());
1978
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
1979
+ const p = 2 * l - q;
1980
+ r = hue2rgb(p, q, h + 1 / 3);
1981
+ g = hue2rgb(p, q, h);
1982
+ b = hue2rgb(p, q, h - 1 / 3);
2152
1983
  }
1984
+ return `rgba(${Math.round(r * 255)},${Math.round(g * 255)},${Math.round(b * 255)},${a})`;
2153
1985
  },
2154
- /**
2155
- * 设置元素的 CSS 类名
2156
- *
2157
- * @param el HTML 或 SVG 元素
2158
- * @param name 要设置的类名,多个类名之间用空格分隔
2159
- */
2160
- setClass(el, name) {
2161
- if ("classList" in el) {
2162
- el.classList.value = "";
2163
- name.split(" ").forEach((className) => el.classList.add(className));
2164
- }
1986
+ isHex(a) {
1987
+ return /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(a);
2165
1988
  },
2166
- /**
2167
- * 从字符串中解析XML文档,并返回根节点
2168
- *
2169
- * @param str 要解析的XML字符串
2170
- * @returns 解析后的XML文档的根节点
2171
- */
2172
- parseFromString(str) {
2173
- const parser = new DOMParser();
2174
- const doc = parser.parseFromString(str, "text/xml");
2175
- return doc.children[0];
1989
+ isRgb(a) {
1990
+ return /^rgb/.test(a);
1991
+ },
1992
+ isHsl(a) {
1993
+ return /^hsl/.test(a);
1994
+ },
1995
+ isColor(a) {
1996
+ return this.isHex(a) || this.isRgb(a) || this.isHsl(a);
1997
+ },
1998
+ colorToRgb(val) {
1999
+ if (this.isRgb(val)) return this.rgbToRgba(val);
2000
+ if (this.isHex(val)) return this.hexToRgba(val);
2001
+ if (this.isHsl(val)) return this.hslToRgba(val);
2176
2002
  }
2177
2003
  };
2178
2004
  const TYPES = ["Point", "MultiPoint", "LineString", "MultiLineString", "Polygon", "MultiPolygon"];
@@ -2365,309 +2191,772 @@ const GeoJsonUtil = {
2365
2191
  break;
2366
2192
  }
2367
2193
  }
2368
- if (fType) {
2369
- for (let i = 0, len = coordinates.length; i < len; i++) {
2370
- features.push({
2371
- type: "Feature",
2372
- geometry: {
2373
- type: fType,
2374
- coordinates: coordinates[i]
2375
- },
2376
- properties
2377
- });
2194
+ if (fType) {
2195
+ for (let i = 0, len = coordinates.length; i < len; i++) {
2196
+ features.push({
2197
+ type: "Feature",
2198
+ geometry: {
2199
+ type: fType,
2200
+ coordinates: coordinates[i]
2201
+ },
2202
+ properties
2203
+ });
2204
+ }
2205
+ } else {
2206
+ features.push(feature);
2207
+ }
2208
+ return features;
2209
+ },
2210
+ /**
2211
+ * 根据坐标数组生成GeoJSON要素
2212
+ *
2213
+ * @param coordinates 坐标数组
2214
+ * @returns GeoJSONFeature 生成的GeoJSON要素
2215
+ * @throws Error 如果coordinates参数格式错误
2216
+ */
2217
+ getGeoJsonByCoordinates(coordinates) {
2218
+ if (!Array.isArray(coordinates)) {
2219
+ throw Error("coordinates 参数格式错误");
2220
+ }
2221
+ let type;
2222
+ if (coordinates.length === 2 && typeof coordinates[0] === "number" && typeof coordinates[1] === "number") {
2223
+ type = "Point";
2224
+ } else if (Array.isArray(coordinates[0]) && coordinates[0].length === 2) {
2225
+ type = "LineString";
2226
+ } else if (Array.isArray(coordinates[0]) && Array.isArray(coordinates[0][0])) {
2227
+ const outerRing = coordinates[0];
2228
+ const isClosed = outerRing[0].join(",") === outerRing[outerRing.length - 1].join(",");
2229
+ if (isClosed) {
2230
+ type = "Polygon";
2231
+ } else if (coordinates.length > 1) {
2232
+ type = "MultiPolygon";
2233
+ } else {
2234
+ throw Error("coordinates 参数格式错误");
2235
+ }
2236
+ } else {
2237
+ throw Error("coordinates 参数格式错误");
2238
+ }
2239
+ return {
2240
+ type: "Feature",
2241
+ geometry: { type, coordinates }
2242
+ };
2243
+ }
2244
+ };
2245
+ const AssertUtil = {
2246
+ assertEmpty(...arg) {
2247
+ arg.forEach((a) => {
2248
+ if (CommUtil.isEmpty(a)) {
2249
+ throw Error(ErrorType.PARAMETER_ERROR_LACK + " -> " + a);
2250
+ }
2251
+ });
2252
+ },
2253
+ assertNumber(...arg) {
2254
+ arg.forEach((a) => {
2255
+ if (!CommUtil.isNumber(a)) {
2256
+ throw Error(ErrorType.PARAMETER_ERROR_NUMBER + " -> " + a);
2257
+ }
2258
+ });
2259
+ },
2260
+ assertArray(...arg) {
2261
+ arg.forEach((a) => {
2262
+ if (!CommUtil.isArray(a)) {
2263
+ throw Error(ErrorType.PARAMETER_ERROR_ARRAY + " -> " + a);
2264
+ }
2265
+ });
2266
+ },
2267
+ assertFunction(...arg) {
2268
+ arg.forEach((a) => {
2269
+ if (!CommUtil.isFunction(a)) {
2270
+ throw Error(ErrorType.PARAMETER_ERROR_FUNCTION + " -> " + a);
2271
+ }
2272
+ });
2273
+ },
2274
+ assertObject(...arg) {
2275
+ arg.forEach((a) => {
2276
+ if (!CommUtil.isObject(a)) {
2277
+ throw Error(ErrorType.PARAMETER_ERROR_OBJECT + " -> " + a);
2278
+ }
2279
+ });
2280
+ },
2281
+ assertColor(...arg) {
2282
+ arg.forEach((a) => {
2283
+ if (!ColorUtil.isColor(a)) {
2284
+ throw Error(ErrorType.DATA_ERROR_COLOR + " -> " + a);
2285
+ }
2286
+ });
2287
+ },
2288
+ assertLnglat(...arg) {
2289
+ arg.forEach((a) => {
2290
+ if (!GeoUtil.isLnglat(a.lng, a.lat)) {
2291
+ throw Error(ErrorType.DATA_ERROR_COORDINATE + " -> " + a);
2292
+ }
2293
+ });
2294
+ },
2295
+ assertGeoJson(...arg) {
2296
+ arg.forEach((a) => {
2297
+ if (!GeoJsonUtil.isGeoJson(a)) {
2298
+ throw Error(ErrorType.DATA_ERROR_GEOJSON + " -> " + a);
2299
+ }
2300
+ });
2301
+ },
2302
+ assertContain(str, ...args) {
2303
+ let res = false;
2304
+ const len = args.length || 0;
2305
+ for (let i = 0, l = len; i < l; i++) {
2306
+ res = str.indexOf(args[i]) >= 0;
2307
+ }
2308
+ if (res) {
2309
+ throw Error(ErrorType.STRING_CHECK_LOSS + " -> " + str);
2310
+ }
2311
+ },
2312
+ /**
2313
+ * 判断字符串是否合法
2314
+ *
2315
+ * @param value 待判断的字符串
2316
+ * @param type 字符串类型
2317
+ * @throws 当字符串不合法时,抛出错误,错误信息为“参数错误 -> 不是{typename}”
2318
+ */
2319
+ assertLegal(value, type) {
2320
+ const bool = StringUtil.checkStr(value, type);
2321
+ let typename = "";
2322
+ switch (type) {
2323
+ case "phone":
2324
+ typename = "电话";
2325
+ break;
2326
+ case "tel":
2327
+ typename = "座机";
2328
+ break;
2329
+ case "card":
2330
+ typename = "身份证";
2331
+ break;
2332
+ case "pwd":
2333
+ typename = "密码";
2334
+ break;
2335
+ case "postal":
2336
+ typename = "邮政编码";
2337
+ break;
2338
+ case "QQ":
2339
+ typename = "QQ";
2340
+ break;
2341
+ case "email":
2342
+ typename = "邮箱";
2343
+ break;
2344
+ case "money":
2345
+ typename = "金额";
2346
+ break;
2347
+ case "URL":
2348
+ typename = "网址";
2349
+ break;
2350
+ case "IP":
2351
+ typename = "IP";
2352
+ break;
2353
+ case "date":
2354
+ typename = "日期时间";
2355
+ break;
2356
+ case "number":
2357
+ typename = "数字";
2358
+ break;
2359
+ case "english":
2360
+ typename = "英文";
2361
+ break;
2362
+ case "chinese":
2363
+ typename = "中文";
2364
+ break;
2365
+ case "lower":
2366
+ typename = "小写";
2367
+ break;
2368
+ case "upper":
2369
+ typename = "大写";
2370
+ break;
2371
+ case "HTML":
2372
+ typename = "HTML标记";
2373
+ break;
2374
+ }
2375
+ if (!bool) {
2376
+ throw Error(ErrorType.DATA_ERROR + " -> 不是" + typename);
2377
+ }
2378
+ }
2379
+ };
2380
+ const BrowserUtil = {
2381
+ /**
2382
+ * 获取浏览器类型
2383
+ *
2384
+ * @returns 返回浏览器类型字符串,可能的值为 'IE'、'Firefox'、'Chrome'、'Opera'、'Safari' 或 'Unknown'
2385
+ */
2386
+ getExplorer() {
2387
+ var explorer = window.navigator.userAgent;
2388
+ if (explorer.indexOf("MSIE") >= 0 || /Trident\//.test(explorer)) {
2389
+ return "IE";
2390
+ } else if (explorer.indexOf("Firefox") >= 0) {
2391
+ return "Firefox";
2392
+ } else if (explorer.indexOf("Chrome") >= 0) {
2393
+ return "Chrome";
2394
+ } else if (explorer.indexOf("Opera") >= 0) {
2395
+ return "Opera";
2396
+ } else if (explorer.indexOf("Safari") >= 0 && explorer.indexOf("Chrome") === -1) {
2397
+ return "Safari";
2398
+ }
2399
+ return "Unknown";
2400
+ },
2401
+ /**
2402
+ * 检测操作系统类型
2403
+ *
2404
+ * @returns 返回操作系统类型字符串,可能的值有:'MS Windows'、'Apple mac'、'Linux'、'Unix'
2405
+ */
2406
+ detectOS() {
2407
+ let os_type = "";
2408
+ const windows = navigator.userAgent.indexOf("Windows", 0) != -1 ? 1 : 0;
2409
+ const mac = navigator.userAgent.indexOf("mac", 0) != -1 ? 1 : 0;
2410
+ const linux = navigator.userAgent.indexOf("Linux", 0) != -1 ? 1 : 0;
2411
+ const unix = navigator.userAgent.indexOf("X11", 0) != -1 ? 1 : 0;
2412
+ if (windows) os_type = "MS Windows";
2413
+ else if (mac) os_type = "Apple mac";
2414
+ else if (linux) os_type = "Linux";
2415
+ else if (unix) os_type = "Unix";
2416
+ return os_type;
2417
+ },
2418
+ /**
2419
+ * 切换全屏状态
2420
+ *
2421
+ * @param status 是否全屏
2422
+ */
2423
+ switchFullScreen(status) {
2424
+ if (status) {
2425
+ const element = document.documentElement;
2426
+ if (element.requestFullscreen) {
2427
+ element.requestFullscreen();
2428
+ } else if ("msRequestFullscreen" in element) {
2429
+ element.msRequestFullscreen();
2430
+ } else if ("mozRequestFullScreen" in element) {
2431
+ element.mozRequestFullScreen();
2432
+ } else if ("webkitRequestFullscreen" in element) {
2433
+ element.webkitRequestFullscreen();
2434
+ }
2435
+ } else {
2436
+ if (document.exitFullscreen) {
2437
+ document.exitFullscreen();
2438
+ } else if ("msExitFullscreen" in document) {
2439
+ document.msExitFullscreen();
2440
+ } else if ("mozCancelFullScreen" in document) {
2441
+ document.mozCancelFullScreen();
2442
+ } else if ("webkitExitFullscreen" in document) {
2443
+ document.webkitExitFullscreen();
2444
+ }
2445
+ }
2446
+ },
2447
+ /**
2448
+ * 刷新缩放比例
2449
+ *
2450
+ * @returns 无返回值
2451
+ */
2452
+ refreshScale() {
2453
+ const baseWidth = document.documentElement.clientWidth || 0;
2454
+ const baseHeight = document.documentElement.clientHeight || 0;
2455
+ const appElement = document.getElementById("app");
2456
+ if (!appElement) return;
2457
+ const appStyle = appElement.style;
2458
+ const realRatio = baseWidth / baseHeight;
2459
+ const designRatio = 16 / 9;
2460
+ let scaleRate = baseWidth / 1920;
2461
+ if (realRatio > designRatio) {
2462
+ scaleRate = baseHeight / 1080;
2463
+ }
2464
+ appStyle.transformOrigin = "left top";
2465
+ appStyle.transform = `scale(${scaleRate}) translateX(-49.99%)`;
2466
+ appStyle.width = `${baseWidth / scaleRate}px`;
2467
+ },
2468
+ /**
2469
+ * 获取HTML字体大小
2470
+ *
2471
+ * @returns 无返回值,该函数会直接修改HTML元素的字体大小
2472
+ */
2473
+ getHtmlFontSize() {
2474
+ const htmlwidth = document.documentElement.clientWidth || document.body.clientWidth;
2475
+ const htmlDom = document.querySelector("html");
2476
+ if (htmlDom) {
2477
+ htmlDom.style.fontSize = htmlwidth / 192 + "px";
2478
+ }
2479
+ }
2480
+ };
2481
+ const CoordsUtil = {
2482
+ PI: 3.141592653589793,
2483
+ XPI: 3.141592653589793 * 3e3 / 180,
2484
+ delta(lat, lng) {
2485
+ const a = 6378245;
2486
+ const ee = 0.006693421622965943;
2487
+ let dLat = this.transformLat(lng - 105, lat - 35);
2488
+ let dLon = this.transformLon(lng - 105, lat - 35);
2489
+ const radLat = lat / 180 * this.PI;
2490
+ let magic = Math.sin(radLat);
2491
+ magic = 1 - ee * magic * magic;
2492
+ const sqrtMagic = Math.sqrt(magic);
2493
+ dLat = dLat * 180 / (a * (1 - ee) / (magic * sqrtMagic) * this.PI);
2494
+ dLon = dLon * 180 / (a / sqrtMagic * Math.cos(radLat) * this.PI);
2495
+ return { lat: dLat, lng: dLon };
2496
+ },
2497
+ /**
2498
+ * 判断经纬度是否不在中国境内
2499
+ *
2500
+ * @param lng 经度
2501
+ * @param lat 纬度
2502
+ * @returns 如果经纬度不在中国境内则返回true,否则返回false
2503
+ */
2504
+ outOfChina(lng, lat) {
2505
+ if (lng < 72.004 || lng > 137.8347) {
2506
+ return true;
2507
+ }
2508
+ if (lat < 0.8293 || lat > 55.8271) {
2509
+ return true;
2510
+ }
2511
+ return false;
2512
+ },
2513
+ // WGS-84 to GCJ-02
2514
+ gcjEncrypt(wgsLat, wgsLon) {
2515
+ if (this.outOfChina(wgsLat, wgsLon)) {
2516
+ return { lat: wgsLat, lng: wgsLon };
2517
+ }
2518
+ const d = this.delta(wgsLat, wgsLon);
2519
+ return { lat: wgsLat + d.lat, lng: wgsLon + d.lng };
2520
+ },
2521
+ // GCJ-02 to WGS-84
2522
+ gcjDecrypt(gcjLat, gcjLon) {
2523
+ if (this.outOfChina(gcjLat, gcjLon)) {
2524
+ return { lat: gcjLat, lng: gcjLon };
2525
+ }
2526
+ const d = this.delta(gcjLat, gcjLon);
2527
+ return { lat: gcjLat - d.lat, lng: gcjLon - d.lng };
2528
+ },
2529
+ // GCJ-02 to WGS-84 exactly
2530
+ gcjDecryptExact(gcjLat, gcjLon) {
2531
+ const initDelta = 0.01;
2532
+ const threshold = 1e-9;
2533
+ let dLat = initDelta;
2534
+ let dLon = initDelta;
2535
+ let mLat = gcjLat - dLat;
2536
+ let mLon = gcjLon - dLon;
2537
+ let pLat = gcjLat + dLat;
2538
+ let pLon = gcjLon + dLon;
2539
+ let wgsLat = 0;
2540
+ let wgsLon = 0;
2541
+ let i = 0;
2542
+ while (1) {
2543
+ wgsLat = (mLat + pLat) / 2;
2544
+ wgsLon = (mLon + pLon) / 2;
2545
+ const tmp = this.gcjEncrypt(wgsLat, wgsLon);
2546
+ dLat = tmp.lat - gcjLat;
2547
+ dLon = tmp.lng - gcjLon;
2548
+ if (Math.abs(dLat) < threshold && Math.abs(dLon) < threshold) {
2549
+ break;
2378
2550
  }
2379
- } else {
2380
- features.push(feature);
2551
+ if (dLat > 0) pLat = wgsLat;
2552
+ else mLat = wgsLat;
2553
+ if (dLon > 0) pLon = wgsLon;
2554
+ else mLon = wgsLon;
2555
+ if (++i > 1e4) break;
2381
2556
  }
2382
- return features;
2557
+ return { lat: wgsLat, lng: wgsLon };
2558
+ },
2559
+ // GCJ-02 to BD-09
2560
+ bdEncrypt(gcjLat, gcjLon) {
2561
+ const x = gcjLon;
2562
+ const y = gcjLat;
2563
+ const z = Math.sqrt(x * x + y * y) + 2e-5 * Math.sin(y * this.XPI);
2564
+ const theta = Math.atan2(y, x) + 3e-6 * Math.cos(x * this.XPI);
2565
+ const bdLon = z * Math.cos(theta) + 65e-4;
2566
+ const bdLat = z * Math.sin(theta) + 6e-3;
2567
+ return { lat: bdLat, lng: bdLon };
2568
+ },
2569
+ // BD-09 to GCJ-02
2570
+ bdDecrypt(bdLat, bdLon) {
2571
+ const x = bdLon - 65e-4;
2572
+ const y = bdLat - 6e-3;
2573
+ const z = Math.sqrt(x * x + y * y) - 2e-5 * Math.sin(y * this.XPI);
2574
+ const theta = Math.atan2(y, x) - 3e-6 * Math.cos(x * this.XPI);
2575
+ const gcjLon = z * Math.cos(theta);
2576
+ const gcjLat = z * Math.sin(theta);
2577
+ return { lat: gcjLat, lng: gcjLon };
2578
+ },
2579
+ // WGS-84 to Web mercator
2580
+ // mercatorLat -> y mercatorLon -> x
2581
+ mercatorEncrypt(wgsLat, wgsLon) {
2582
+ const x = wgsLon * 2003750834e-2 / 180;
2583
+ let y = Math.log(Math.tan((90 + wgsLat) * this.PI / 360)) / (this.PI / 180);
2584
+ y = y * 2003750834e-2 / 180;
2585
+ return { lat: y, lng: x };
2586
+ },
2587
+ // Web mercator to WGS-84
2588
+ // mercatorLat -> y mercatorLon -> x
2589
+ mercatorDecrypt(mercatorLat, mercatorLon) {
2590
+ const x = mercatorLon / 2003750834e-2 * 180;
2591
+ let y = mercatorLat / 2003750834e-2 * 180;
2592
+ y = 180 / this.PI * (2 * Math.atan(Math.exp(y * this.PI / 180)) - this.PI / 2);
2593
+ return { lat: y, lng: x };
2594
+ },
2595
+ transformLat(x, y) {
2596
+ let ret = -100 + 2 * x + 3 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
2597
+ ret += (20 * Math.sin(6 * x * this.PI) + 20 * Math.sin(2 * x * this.PI)) * 2 / 3;
2598
+ ret += (20 * Math.sin(y * this.PI) + 40 * Math.sin(y / 3 * this.PI)) * 2 / 3;
2599
+ ret += (160 * Math.sin(y / 12 * this.PI) + 320 * Math.sin(y * this.PI / 30)) * 2 / 3;
2600
+ return ret;
2601
+ },
2602
+ transformLon(x, y) {
2603
+ let ret = 300 + x + 2 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
2604
+ ret += (20 * Math.sin(6 * x * this.PI) + 20 * Math.sin(2 * x * this.PI)) * 2 / 3;
2605
+ ret += (20 * Math.sin(x * this.PI) + 40 * Math.sin(x / 3 * this.PI)) * 2 / 3;
2606
+ ret += (150 * Math.sin(x / 12 * this.PI) + 300 * Math.sin(x / 30 * this.PI)) * 2 / 3;
2607
+ return ret;
2383
2608
  },
2384
2609
  /**
2385
- * 根据坐标数组生成GeoJSON要素
2610
+ * 生成指定范围内的随机经纬度坐标
2386
2611
  *
2387
- * @param coordinates 坐标数组
2388
- * @returns GeoJSONFeature 生成的GeoJSON要素
2389
- * @throws Error 如果coordinates参数格式错误
2612
+ * @param min 最小坐标,包含属性 x 和 y,分别表示最小经度和最小纬度
2613
+ * @param max 最大坐标,包含属性 x 和 y,分别表示最大经度和最大纬度
2614
+ * @returns 返回生成的随机经纬度坐标,包含属性 lat 和 lng,分别表示纬度和经度
2390
2615
  */
2391
- getGeoJsonByCoordinates(coordinates) {
2392
- if (!Array.isArray(coordinates)) {
2393
- throw Error("coordinates 参数格式错误");
2616
+ random({ x: minX, y: minY }, { x: maxX, y: maxY }) {
2617
+ return {
2618
+ lat: Math.random() * (maxY - minY) + minY,
2619
+ lng: Math.random() * (maxX - minX) + minX
2620
+ };
2621
+ },
2622
+ deCompose(arr, fn, context) {
2623
+ if (!Array.isArray(arr)) {
2624
+ return context ? fn.call(context, arr) : fn(arr);
2394
2625
  }
2395
- let type;
2396
- if (coordinates.length === 2 && typeof coordinates[0] === "number" && typeof coordinates[1] === "number") {
2397
- type = "Point";
2398
- } else if (Array.isArray(coordinates[0]) && coordinates[0].length === 2) {
2399
- type = "LineString";
2400
- } else if (Array.isArray(coordinates[0]) && Array.isArray(coordinates[0][0])) {
2401
- const outerRing = coordinates[0];
2402
- const isClosed = outerRing[0].join(",") === outerRing[outerRing.length - 1].join(",");
2403
- if (isClosed) {
2404
- type = "Polygon";
2405
- } else if (coordinates.length > 1) {
2406
- type = "MultiPolygon";
2626
+ const result = [];
2627
+ let p, pp;
2628
+ for (let i = 0, len = arr.length; i < len; i++) {
2629
+ p = arr[i];
2630
+ if (CommUtil.isNil(p)) {
2631
+ result.push(null);
2632
+ continue;
2633
+ }
2634
+ if (Array.isArray(p)) {
2635
+ result.push(this.deCompose(p, fn, context));
2407
2636
  } else {
2408
- throw Error("coordinates 参数格式错误");
2637
+ pp = context ? fn.call(context, p) : fn(p);
2638
+ result.push(pp);
2409
2639
  }
2410
- } else {
2411
- throw Error("coordinates 参数格式错误");
2412
2640
  }
2413
- return {
2414
- type: "Feature",
2415
- geometry: { type, coordinates }
2416
- };
2641
+ return result;
2417
2642
  }
2418
2643
  };
2419
- const GeoUtil = {
2420
- toRadian: Math.PI / 180,
2421
- R: 6371393,
2422
- /**
2423
- * 判断给定的经纬度是否合法
2424
- *
2425
- * @param lng 经度值
2426
- * @param lat 纬度值
2427
- * @returns 如果经纬度合法,返回true;否则返回false
2428
- */
2429
- isLnglat(lng, lat) {
2430
- return !isNaN(lng) && !isNaN(lat) && !!(+lat > -90 && +lat < 90 && +lng > -180 && +lng < 180);
2644
+ const myDate = Object.create(Date);
2645
+ myDate.prototype.format = function(fmt = "yyyy-MM-dd hh:mm:ss") {
2646
+ const o = {
2647
+ "M+": this.getMonth() + 1,
2648
+ // 月份
2649
+ "d+": this.getDate(),
2650
+ //
2651
+ "h+": this.getHours() % 12,
2652
+ // 小时
2653
+ "H+": this.getHours(),
2654
+ // 小时 (24小时制)
2655
+ "m+": this.getMinutes(),
2656
+ // 分
2657
+ "s+": this.getSeconds(),
2658
+ // 秒
2659
+ "q+": Math.floor((this.getMonth() + 3) / 3),
2660
+ // 季度
2661
+ S: this.getMilliseconds()
2662
+ // 毫秒
2663
+ };
2664
+ if (/(y+)/.test(fmt)) {
2665
+ fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
2666
+ }
2667
+ for (const k in o) {
2668
+ if (new RegExp("(" + k + ")").test(fmt)) {
2669
+ const len = k.length === 1 ? 1 : Number(k.slice(1));
2670
+ fmt = fmt.replace(RegExp.$1, ("00" + o[k]).substr(("" + o[k]).length + len - (o[k] + "").length));
2671
+ }
2672
+ }
2673
+ return fmt;
2674
+ };
2675
+ myDate.prototype.addDate = function(interval, number) {
2676
+ const date = new Date(this);
2677
+ switch (interval) {
2678
+ case "y":
2679
+ date.setFullYear(this.getFullYear() + number);
2680
+ break;
2681
+ case "q":
2682
+ date.setMonth(this.getMonth() + number * 3);
2683
+ break;
2684
+ case "M":
2685
+ date.setMonth(this.getMonth() + number);
2686
+ break;
2687
+ case "w":
2688
+ date.setDate(this.getDate() + number * 7);
2689
+ break;
2690
+ case "d":
2691
+ date.setDate(this.getDate() + number);
2692
+ break;
2693
+ case "h":
2694
+ date.setHours(this.getHours() + number);
2695
+ break;
2696
+ case "m":
2697
+ date.setMinutes(this.getMinutes() + number);
2698
+ break;
2699
+ case "s":
2700
+ date.setSeconds(this.getSeconds() + number);
2701
+ break;
2702
+ default:
2703
+ date.setDate(this.getDate() + number);
2704
+ break;
2705
+ }
2706
+ return date;
2707
+ };
2708
+ const DateUtil = {
2709
+ lastMonthDate: new Date((/* @__PURE__ */ new Date()).getFullYear(), (/* @__PURE__ */ new Date()).getMonth() - 1, 1),
2710
+ thisMonthDate: new Date((/* @__PURE__ */ new Date()).getFullYear(), (/* @__PURE__ */ new Date()).getMonth(), 1),
2711
+ nextMonthDate: new Date((/* @__PURE__ */ new Date()).getFullYear(), (/* @__PURE__ */ new Date()).getMonth() + 1, 1),
2712
+ lastWeekDate: new Date((/* @__PURE__ */ new Date()).getFullYear(), (/* @__PURE__ */ new Date()).getMonth(), (/* @__PURE__ */ new Date()).getDate() + 1 - 7 - (/* @__PURE__ */ new Date()).getDay()),
2713
+ thisWeekDate: new Date((/* @__PURE__ */ new Date()).getFullYear(), (/* @__PURE__ */ new Date()).getMonth(), (/* @__PURE__ */ new Date()).getDate() + 1 - (/* @__PURE__ */ new Date()).getDay()),
2714
+ nextWeekDate: new Date((/* @__PURE__ */ new Date()).getFullYear(), (/* @__PURE__ */ new Date()).getMonth(), (/* @__PURE__ */ new Date()).getDate() + 1 + 7 - (/* @__PURE__ */ new Date()).getDay()),
2715
+ lastDayDate: new Date((/* @__PURE__ */ new Date()).getFullYear(), (/* @__PURE__ */ new Date()).getMonth(), (/* @__PURE__ */ new Date()).getDate() - 1),
2716
+ thisDayDate: new Date((/* @__PURE__ */ new Date()).setHours(0, 0, 0, 0)),
2717
+ nextDayDate: new Date((/* @__PURE__ */ new Date()).getFullYear(), (/* @__PURE__ */ new Date()).getMonth(), (/* @__PURE__ */ new Date()).getDate() + 1),
2718
+ parseDate(str) {
2719
+ if (typeof str == "string") {
2720
+ var results = str.match(/^ *(\d{4})-(\d{1,2})-(\d{1,2}) *$/);
2721
+ if (results && results.length > 3) return new Date(parseInt(results[1]), parseInt(results[2]) - 1, parseInt(results[3]));
2722
+ results = str.match(/^ *(\d{4})-(\d{1,2})-(\d{1,2}) +(\d{1,2}):(\d{1,2}):(\d{1,2}) *$/);
2723
+ if (results && results.length > 6)
2724
+ return new Date(
2725
+ parseInt(results[1]),
2726
+ parseInt(results[2]) - 1,
2727
+ parseInt(results[3]),
2728
+ parseInt(results[4]),
2729
+ parseInt(results[5]),
2730
+ parseInt(results[6])
2731
+ );
2732
+ results = str.match(/^ *(\d{4})-(\d{1,2})-(\d{1,2}) +(\d{1,2}):(\d{1,2}):(\d{1,2})\.(\d{1,9}) *$/);
2733
+ if (results && results.length > 7)
2734
+ return new Date(
2735
+ parseInt(results[1]),
2736
+ parseInt(results[2]) - 1,
2737
+ parseInt(results[3]),
2738
+ parseInt(results[4]),
2739
+ parseInt(results[5]),
2740
+ parseInt(results[6]),
2741
+ parseInt(results[7])
2742
+ );
2743
+ }
2744
+ return null;
2431
2745
  },
2432
2746
  /**
2433
- * 计算两哥平面坐标点间的距离
2747
+ * 格式化时间间隔
2434
2748
  *
2435
- * @param p1 坐标点1,包含x和y属性
2436
- * @param p2 坐标点2,包含x和y属性
2437
- * @returns 返回两点间的欧几里得距离
2749
+ * @param startTime 开始时间,可以是字符串、数字或日期类型
2750
+ * @param endTime 结束时间,可以是字符串、数字或日期类型
2751
+ * @returns 返回格式化后的时间间隔字符串,格式为"天数 天 小时 时 分钟 分 秒 秒"或"少于1秒"
2438
2752
  */
2439
- distance(p1, p2) {
2440
- return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
2753
+ formatDateInterval(startTime, endTime) {
2754
+ const dateCreateTime = new Date(startTime);
2755
+ const dateFinishTime = new Date(endTime);
2756
+ const dateInterval = dateFinishTime.getTime() - dateCreateTime.getTime();
2757
+ const days = Math.floor(dateInterval / (24 * 3600 * 1e3));
2758
+ const leave1 = dateInterval % (24 * 3600 * 1e3);
2759
+ const hours = Math.floor(leave1 / (3600 * 1e3));
2760
+ const leave2 = leave1 % (3600 * 1e3);
2761
+ const minutes = Math.floor(leave2 / (60 * 1e3));
2762
+ const leave3 = leave2 % (60 * 1e3);
2763
+ const seconds = Math.round(leave3 / 1e3);
2764
+ let intervalDes = "";
2765
+ if (days > 0) {
2766
+ intervalDes += days + "天";
2767
+ }
2768
+ if (hours > 0) {
2769
+ intervalDes += hours + "时";
2770
+ }
2771
+ if (minutes > 0) {
2772
+ intervalDes += minutes + "分";
2773
+ }
2774
+ if (seconds > 0) {
2775
+ intervalDes += seconds + "秒";
2776
+ }
2777
+ if (days === 0 && hours === 0 && minutes === 0 && seconds === 0) {
2778
+ intervalDes = "少于1秒";
2779
+ }
2780
+ return intervalDes;
2781
+ },
2782
+ formatterCounter(times) {
2783
+ const checked = function(j) {
2784
+ return (j > 10 ? "" : "0") + (j || 0);
2785
+ };
2786
+ const houres = checked(Math.floor(times / 3600));
2787
+ const level1 = times % 3600;
2788
+ const minutes = checked(Math.floor(level1 / 60));
2789
+ const leave2 = level1 % 60;
2790
+ const seconds = checked(Math.round(leave2));
2791
+ return `${houres}:${minutes}:${seconds}`;
2441
2792
  },
2793
+ sleep(d) {
2794
+ }
2795
+ };
2796
+ function trim(str) {
2797
+ return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, "");
2798
+ }
2799
+ function splitWords(str) {
2800
+ return trim(str).split(/\s+/);
2801
+ }
2802
+ const DomUtil = {
2442
2803
  /**
2443
- * 计算两个经纬度点之间的距离
2804
+ * 获取元素的样式值
2444
2805
  *
2445
- * @param A 经纬度点A,包含lng(经度)和lat(纬度)两个属性
2446
- * @param B 经纬度点B,包含lng(经度)和lat(纬度)两个属性
2447
- * @returns 返回两点之间的距离,单位为米
2806
+ * @param el 元素对象
2807
+ * @param style 样式属性名称
2808
+ * @returns 元素的样式值,如果获取不到则返回 null
2448
2809
  */
2449
- distanceByPoints(A, B) {
2450
- const { lng: lngA, lat: latA } = A;
2451
- const { lng: lngB, lat: latB } = B;
2452
- const earthR = 6371e3;
2453
- const x = Math.cos(latA * Math.PI / 180) * Math.cos(latB * Math.PI / 180) * Math.cos((lngA - lngB) * Math.PI / 180);
2454
- const y = Math.sin(latA * Math.PI / 180) * Math.sin(latB * Math.PI / 180);
2455
- let s = x + y;
2456
- if (s > 1) s = 1;
2457
- if (s < -1) s = -1;
2458
- const alpha = Math.acos(s);
2459
- const distance = alpha * earthR;
2460
- return distance;
2810
+ getStyle(el, style) {
2811
+ var _a;
2812
+ let value = el.style[style];
2813
+ if (!value || value === "auto") {
2814
+ const css = (_a = document.defaultView) == null ? void 0 : _a.getComputedStyle(el, null);
2815
+ value = css ? css[style] : null;
2816
+ if (value === "auto") value = null;
2817
+ }
2818
+ return value;
2461
2819
  },
2462
2820
  /**
2463
- * 格式化经纬度为度分秒格式
2821
+ * 创建一个HTML元素
2464
2822
  *
2465
- * @param lng 经度
2466
- * @param lat 纬度
2467
- * @returns 返回格式化后的经纬度字符串,格式为:经度度分秒,纬度度分秒
2823
+ * @param tagName 元素标签名
2824
+ * @param className 元素类名
2825
+ * @param container 父容器,若传入,则新创建的元素会被添加到该容器中
2826
+ * @returns 返回新创建的HTML元素
2468
2827
  */
2469
- formatLnglat(lng, lat) {
2470
- let res = "";
2471
- function formatDegreeToDMS(valueInDegrees) {
2472
- const degree = Math.floor(valueInDegrees);
2473
- const minutes = Math.floor((valueInDegrees - degree) * 60);
2474
- const seconds = (valueInDegrees - degree) * 3600 - minutes * 60;
2475
- return `${degree}°${minutes}′${seconds.toFixed(2)}″`;
2476
- }
2477
- if (this.isLnglat(lng, lat)) {
2478
- res = formatDegreeToDMS(lng) + "," + formatDegreeToDMS(lat);
2479
- } else if (!isNaN(lng)) {
2480
- res = formatDegreeToDMS(lng);
2481
- } else if (!isNaN(lat)) {
2482
- res = formatDegreeToDMS(lat);
2828
+ create(tagName, className, container) {
2829
+ const el = document.createElement(tagName);
2830
+ el.className = className || "";
2831
+ if (container) {
2832
+ container.appendChild(el);
2483
2833
  }
2484
- return res;
2834
+ return el;
2485
2835
  },
2486
2836
  /**
2487
- * 将经纬度字符串转换为度
2837
+ * 从父节点中移除指定元素。
2488
2838
  *
2489
- * @param lng 经度字符串
2490
- * @param lat 纬度字符串
2491
- * @returns 转换后的经纬度对象
2839
+ * @param el 要移除的元素对象,必须包含parentNode属性。
2492
2840
  */
2493
- transformLnglat(lng, lat) {
2494
- function dms2deg(dmsString) {
2495
- const isNegative = /[sw]/i.test(dmsString);
2496
- let factor = isNegative ? -1 : 1;
2497
- const numericParts = dmsString.match(/[\d.]+/g) || [];
2498
- let degrees = 0;
2499
- for (let i = 0; i < numericParts.length; i++) {
2500
- degrees += parseFloat(numericParts[i]) / factor;
2501
- factor *= 60;
2502
- }
2503
- return degrees;
2504
- }
2505
- if (lng && lat) {
2506
- return {
2507
- lng: dms2deg(lng),
2508
- lat: dms2deg(lat)
2509
- };
2841
+ remove(el) {
2842
+ const parent = el.parentNode;
2843
+ if (parent) {
2844
+ parent.removeChild(el);
2510
2845
  }
2511
2846
  },
2512
2847
  /**
2513
- * 射线法判断点是否在多边形内
2848
+ * 清空给定元素的子节点
2514
2849
  *
2515
- * @param p 点对象,包含xy属性
2516
- * @param poly 多边形顶点数组,可以是字符串数组或对象数组
2517
- * @returns 返回字符串,表示点相对于多边形的位置:'in'表示在多边形内,'out'表示在多边形外,'on'表示在多边形上
2850
+ * @param el 要清空子节点的元素,包含firstChildremoveChild属性
2518
2851
  */
2519
- rayCasting(p, poly) {
2520
- var px = p.x, py = p.y, flag = false;
2521
- for (var i = 0, l = poly.length, j = l - 1; i < l; j = i, i++) {
2522
- var sx = poly[i].x, sy = poly[i].y, tx = poly[j].x, ty = poly[j].y;
2523
- if (sx === px && sy === py || tx === px && ty === py) {
2524
- return "on";
2525
- }
2526
- if (sy < py && ty >= py || sy >= py && ty < py) {
2527
- var x = sx + (py - sy) * (tx - sx) / (ty - sy);
2528
- if (x === px) {
2529
- return "on";
2530
- }
2531
- if (x > px) {
2532
- flag = !flag;
2533
- }
2534
- }
2852
+ empty(el) {
2853
+ while (el.firstChild) {
2854
+ el.removeChild(el.firstChild);
2535
2855
  }
2536
- return flag ? "in" : "out";
2537
2856
  },
2538
2857
  /**
2539
- * 旋转点
2858
+ * 将元素移到父节点的最前面
2540
2859
  *
2541
- * @param p1 旋转前点坐标
2542
- * @param p2 旋转中心坐标
2543
- * @param θ 旋转角度(顺时针旋转为正)
2544
- * @returns 旋转后点坐标
2860
+ * @param el 要移动的元素,需要包含 parentNode 属性
2545
2861
  */
2546
- rotatePoint(p1, p2, θ) {
2547
- const x = (p1.x - p2.x) * Math.cos(Math.PI / 180 * -θ) - (p1.y - p2.y) * Math.sin(Math.PI / 180 * -θ) + p2.x;
2548
- const y = (p1.x - p2.x) * Math.sin(Math.PI / 180 * -θ) + (p1.y - p2.y) * Math.cos(Math.PI / 180 * -θ) + p2.y;
2549
- return { x, y };
2862
+ toFront(el) {
2863
+ const parent = el.parentNode;
2864
+ if (parent && parent.lastChild !== el) {
2865
+ parent.appendChild(el);
2866
+ }
2550
2867
  },
2551
2868
  /**
2552
- * 根据两个平面坐标点计算方位角和距离
2869
+ * 将元素移动到其父节点的最前面
2553
2870
  *
2554
- * @param p1 第一个点的坐标对象
2555
- * @param p2 第二个点的坐标对象
2556
- * @returns 返回一个对象,包含angle和distance属性,分别表示两点之间的角度(以度为单位,取值范围为0~359)和距离
2871
+ * @param el 要移动的元素,需要包含parentNode属性
2557
2872
  */
2558
- calcBearAndDis(p1, p2) {
2559
- const { x: x1, y: y1 } = p1;
2560
- const { x: x2, y: y2 } = p2;
2561
- const dx = x2 - x1;
2562
- const dy = y2 - y1;
2563
- const distance = Math.sqrt(dx * dx + dy * dy);
2564
- const angleInRadians = Math.atan2(dy, dx);
2565
- const angle = (angleInRadians * (180 / Math.PI) + 360 + 90) % 360;
2566
- return { angle, distance };
2873
+ toBack(el) {
2874
+ const parent = el.parentNode;
2875
+ if (parent && parent.firstChild !== el) {
2876
+ parent.insertBefore(el, parent.firstChild);
2877
+ }
2567
2878
  },
2568
2879
  /**
2569
- * 根据两个经纬度点计算方位角和距离
2880
+ * 获取元素的类名
2570
2881
  *
2571
- * @param latlng1 第一个经纬度点
2572
- * @param latlng2 第二个经纬度点
2573
- * @returns 包含方位角和距离的对象
2882
+ * @param el 包含对应元素和类名的对象
2883
+ * @param el.correspondingElement 对应的元素
2884
+ * @param el.className 类名对象
2885
+ * @param el.className.baseVal 类名字符串
2886
+ * @returns 返回元素的类名字符串
2574
2887
  */
2575
- calcBearAndDisByPoints(latlng1, latlng2) {
2576
- var f1 = latlng1.lat * 1, l1 = latlng1.lng * 1, f2 = latlng2.lat * 1, l2 = latlng2.lng * 1;
2577
- var y = Math.sin((l2 - l1) * this.toRadian) * Math.cos(f2 * this.toRadian);
2578
- var x = Math.cos(f1 * this.toRadian) * Math.sin(f2 * this.toRadian) - Math.sin(f1 * this.toRadian) * Math.cos(f2 * this.toRadian) * Math.cos((l2 - l1) * this.toRadian);
2579
- var angle = Math.atan2(y, x) * (180 / Math.PI);
2580
- var deltaF = (f2 - f1) * this.toRadian;
2581
- var deltaL = (l2 - l1) * this.toRadian;
2582
- var a = Math.sin(deltaF / 2) * Math.sin(deltaF / 2) + Math.cos(f1 * this.toRadian) * Math.cos(f2 * this.toRadian) * Math.sin(deltaL / 2) * Math.sin(deltaL / 2);
2583
- var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
2584
- var distance = this.R * c;
2585
- return {
2586
- angle,
2587
- distance
2588
- };
2888
+ getClass(el) {
2889
+ const shadowElement = (el == null ? void 0 : el.host) || el;
2890
+ return shadowElement.className.toString();
2589
2891
  },
2590
2892
  /**
2591
- * 计算点P到线段P1P2的最短距离
2893
+ * 判断元素是否包含指定类名
2592
2894
  *
2593
- * @param p 点P的坐标
2594
- * @param p1 线段起点P1的坐标
2595
- * @param p2 线段终点P2的坐标
2596
- * @returns 点P到线段P1P2的最短距离
2895
+ * @param el 元素对象,包含classList属性,classList属性包含contains方法
2896
+ * @param name 要判断的类名
2897
+ * @returns 返回一个布尔值,表示元素是否包含指定类名
2597
2898
  */
2598
- distanceToSegment(p, p1, p2) {
2599
- const x = p.x, y = p.y, x1 = p1.x, y1 = p1.y, x2 = p2.x, y2 = p2.y;
2600
- const cross = (x2 - x1) * (x - x1) + (y2 - y1) * (y - y1);
2601
- if (cross <= 0) {
2602
- return Math.sqrt((x - x1) * (x - x1) + (y - y1) * (y - y1));
2603
- }
2604
- const d2 = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
2605
- if (cross >= d2) {
2606
- return Math.sqrt((x - x2) * (x - x2) + (y - y2) * (y - y2));
2899
+ hasClass(el, name) {
2900
+ var _a;
2901
+ if ((_a = el.classList) == null ? void 0 : _a.contains(name)) {
2902
+ return true;
2607
2903
  }
2608
- const r = cross / d2;
2609
- const px = x1 + (x2 - x1) * r;
2610
- const py = y1 + (y2 - y1) * r;
2611
- return Math.sqrt((x - px) * (x - px) + (y - py) * (y - py));
2904
+ const className = this.getClass(el);
2905
+ return className.length > 0 && new RegExp(`(^|\\s)${name}(\\s|$)`).test(className);
2612
2906
  },
2613
2907
  /**
2614
- * 根据给定的经纬度、角度和距离计算新的经纬度点
2908
+ * 给指定的 HTML 元素添加类名
2615
2909
  *
2616
- * @param latlng 给定的经纬度点,类型为LngLat
2617
- * @param angle 角度值,单位为度,表示从当前点出发的方向
2618
- * @param distance 距离值,单位为米,表示从当前点出发的距离
2619
- * @returns 返回计算后的新经纬度点,类型为{lat: number, lng: number}
2910
+ * @param el 要添加类名的 HTML 元素
2911
+ * @param name 要添加的类名,多个类名之间用空格分隔
2620
2912
  */
2621
- calcPointByBearAndDis(latlng, angle, distance) {
2622
- const sLat = MathUtils.deg2Rad(latlng.lat * 1);
2623
- const sLng = MathUtils.deg2Rad(latlng.lng * 1);
2624
- const d = distance / this.R;
2625
- angle = MathUtils.deg2Rad(angle);
2626
- const lat = Math.asin(Math.sin(sLat) * Math.cos(d) + Math.cos(sLat) * Math.sin(d) * Math.cos(angle));
2627
- const lon = sLng + Math.atan2(Math.sin(angle) * Math.sin(d) * Math.cos(sLat), Math.cos(d) - Math.sin(sLat) * Math.sin(lat));
2628
- return {
2629
- lat: MathUtils.rad2Deg(lat),
2630
- lng: MathUtils.rad2Deg(lon)
2631
- };
2913
+ addClass(el, name) {
2914
+ if (el.classList !== void 0) {
2915
+ const classes = splitWords(name);
2916
+ for (let i = 0, len = classes.length; i < len; i++) {
2917
+ el.classList.add(classes[i]);
2918
+ }
2919
+ } else if (!this.hasClass(el, name)) {
2920
+ const className = this.getClass(el);
2921
+ this.setClass(el, (className ? className + " " : "") + name);
2922
+ }
2632
2923
  },
2633
2924
  /**
2634
- * 将墨卡托坐标转换为经纬度坐标
2925
+ * 从元素中移除指定类名
2635
2926
  *
2636
- * @param x 墨卡托坐标的x值
2637
- * @param y 墨卡托坐标的y值
2638
- * @returns 返回包含转换后的经度lng和纬度lat的对象
2927
+ * @param el 要移除类名的元素
2928
+ * @param name 要移除的类名,多个类名用空格分隔
2639
2929
  */
2640
- mercatorTolonlat(x, y) {
2641
- const lng = x / 2003750834e-2 * 180;
2642
- var mmy = y / 2003750834e-2 * 180;
2643
- const lat = 180 / Math.PI * (2 * Math.atan(Math.exp(mmy * Math.PI / 180)) - Math.PI / 2);
2644
- return { lng, lat };
2930
+ removeClass(el, name) {
2931
+ if (el.classList !== void 0) {
2932
+ const classes = splitWords(name);
2933
+ classes.forEach((className) => el.classList.remove(className));
2934
+ } else {
2935
+ this.setClass(el, (" " + this.getClass(el) + " ").replace(" " + name + " ", " ").trim());
2936
+ }
2645
2937
  },
2646
2938
  /**
2647
- * 将经纬度坐标转换为墨卡托坐标
2939
+ * 设置元素的 CSS 类名
2648
2940
  *
2649
- * @param lng 经度值
2650
- * @param lat 纬度值
2651
- * @returns 墨卡托坐标对象,包含x和y属性
2941
+ * @param el HTML 或 SVG 元素
2942
+ * @param name 要设置的类名,多个类名之间用空格分隔
2652
2943
  */
2653
- lonlatToMercator(lng, lat) {
2654
- var earthRad = 6378137;
2655
- const x = lng * Math.PI / 180 * earthRad;
2656
- var a = lat * Math.PI / 180;
2657
- const y = earthRad / 2 * Math.log((1 + Math.sin(a)) / (1 - Math.sin(a)));
2658
- return { x, y };
2944
+ setClass(el, name) {
2945
+ if ("classList" in el) {
2946
+ el.classList.value = "";
2947
+ name.split(" ").forEach((className) => el.classList.add(className));
2948
+ }
2659
2949
  },
2660
2950
  /**
2661
- * 根据百分比获取坐标
2951
+ * 从字符串中解析XML文档,并返回根节点
2662
2952
  *
2663
- * @param start 起点坐标
2664
- * @param end 终点坐标
2665
- * @param percent 百分比,取值范围0-1
2666
- * @returns 返回插值后的坐标
2953
+ * @param str 要解析的XML字符串
2954
+ * @returns 解析后的XML文档的根节点
2667
2955
  */
2668
- interpolate({ x: x1, y: y1, z: z1 = 0 }, { x: x2, y: y2, z: z2 = 0 }, percent) {
2669
- const dx = x2 - x1, dy = y2 - y1, dz = z2 - z1;
2670
- return { x: x1 + dx * percent, y: y1 + dy * percent, z: z1 + dz * percent };
2956
+ parseFromString(str) {
2957
+ const parser = new DOMParser();
2958
+ const doc = parser.parseFromString(str, "text/xml");
2959
+ return doc.children[0];
2671
2960
  }
2672
2961
  };
2673
2962
  const FileUtil = {
@@ -2861,162 +3150,6 @@ const OptimizeUtil = {
2861
3150
  };
2862
3151
  }
2863
3152
  };
2864
- const StringUtil = {
2865
- /**
2866
- * 校验字符串是否符合指定类型
2867
- *
2868
- * @param str 待校验字符串
2869
- * @param type 校验类型,可选值包括:
2870
- * - 'phone': 手机号码
2871
- * - 'tel': 座机
2872
- * - 'card': 身份证
2873
- * - 'pwd': 密码(以字母开头,长度在6~18之间,只能包含字母、数字和下划线)
2874
- * - 'postal': 邮政编码
2875
- * - 'QQ': QQ号
2876
- * - 'email': 邮箱
2877
- * - 'money': 金额(小数点2位)
2878
- * - 'URL': 网址
2879
- * - 'IP': IP地址
2880
- * - 'date': 日期时间
2881
- * - 'number': 数字
2882
- * - 'english': 英文
2883
- * - 'chinese': 中文
2884
- * - 'lower': 小写字母
2885
- * - 'upper': 大写字母
2886
- * - 'HTML': HTML标记
2887
- * @returns 校验结果,符合返回true,否则返回false
2888
- */
2889
- checkStr(str, type) {
2890
- switch (type) {
2891
- case "phone":
2892
- return /^1[3|4|5|6|7|8|9][0-9]{9}$/.test(str);
2893
- case "tel":
2894
- return /^(0\d{2,3}-\d{7,8})(-\d{1,4})?$/.test(str);
2895
- case "card":
2896
- return /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/.test(str);
2897
- case "pwd":
2898
- return /^[a-zA-Z]\w{5,17}$/.test(str);
2899
- case "postal":
2900
- return /[1-9]\d{5}(?!\d)/.test(str);
2901
- case "QQ":
2902
- return /^[1-9][0-9]{4,9}$/.test(str);
2903
- case "email":
2904
- return /^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/.test(str);
2905
- case "money":
2906
- return /^\d*(?:\.\d{0,2})?$/.test(str);
2907
- case "URL":
2908
- return /(http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?/.test(str);
2909
- case "IP":
2910
- return /((?:(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d))/.test(str);
2911
- case "date":
2912
- return /^(\d{4})\-(\d{2})\-(\d{2}) (\d{2})(?:\:\d{2}|:(\d{2}):(\d{2}))$/.test(str) || /^(\d{4})\-(\d{2})\-(\d{2})$/.test(str);
2913
- case "number":
2914
- return /^[0-9]$/.test(str);
2915
- case "english":
2916
- return /^[a-zA-Z]+$/.test(str);
2917
- case "chinese":
2918
- return /^[\u4E00-\u9FA5]+$/.test(str);
2919
- case "lower":
2920
- return /^[a-z]+$/.test(str);
2921
- case "upper":
2922
- return /^[A-Z]+$/.test(str);
2923
- case "HTML":
2924
- return /<("[^"]*"|'[^']*'|[^'">])*>/.test(str);
2925
- default:
2926
- return true;
2927
- }
2928
- },
2929
- /**
2930
- * 转换字符串大小写
2931
- *
2932
- * @param str 待转换的字符串
2933
- * @param type 转换类型,可选值为 1-5,默认为 4
2934
- * 1:首字母大写,其余小写
2935
- * 2:首字母小写,其余大写
2936
- * 3:字母大小写反转
2937
- * 4:全部大写
2938
- * 5:全部小写
2939
- * @returns 转换后的字符串
2940
- */
2941
- changeCase(str, type) {
2942
- type = type || 4;
2943
- switch (type) {
2944
- case 1:
2945
- return str.replace(/\b\w+\b/g, function(word) {
2946
- return word.substring(0, 1).toUpperCase() + word.substring(1).toLowerCase();
2947
- });
2948
- case 2:
2949
- return str.replace(/\b\w+\b/g, function(word) {
2950
- return word.substring(0, 1).toLowerCase() + word.substring(1).toUpperCase();
2951
- });
2952
- case 3:
2953
- return str.split("").map(function(word) {
2954
- if (/[a-z]/.test(word)) {
2955
- return word.toUpperCase();
2956
- } else {
2957
- return word.toLowerCase();
2958
- }
2959
- }).join("");
2960
- case 4:
2961
- return str.toUpperCase();
2962
- case 5:
2963
- return str.toLowerCase();
2964
- default:
2965
- return str;
2966
- }
2967
- },
2968
- /**
2969
- * 根据字符串数组和参数生成新的字符串
2970
- *
2971
- * @param strArray 字符串数组
2972
- * @param args 可变参数列表,支持 Object、Array 类型和任意其他类型,若为 null 或 undefined,则按类型默认转换为 '{}'、'[]' 或 ''
2973
- * @returns 返回生成的新字符串
2974
- */
2975
- tag(strArray, ...args) {
2976
- args = args.map((val) => {
2977
- switch (CommUtils.getDataType(val)) {
2978
- case "Object":
2979
- return val || "{}";
2980
- case "Array":
2981
- return val || "[]";
2982
- default:
2983
- return val || "";
2984
- }
2985
- });
2986
- return strArray.reduce((prev, next, index) => `${prev}${args[index - 1]}${next}`);
2987
- },
2988
- /**
2989
- * 计算字符串的字节长度
2990
- *
2991
- * @param str 需要计算字节长度的字符串
2992
- * @returns 返回字符串的字节长度
2993
- */
2994
- getByteLength(str) {
2995
- return str.replace(/[\u0391-\uFFE5]/g, "aa").length;
2996
- },
2997
- /**
2998
- * 截取字符串中指定字节长度的子串
2999
- *
3000
- * @param str 字符串对象,包含replace、length和substring方法
3001
- * @param start 截取起始位置
3002
- * @param n 截取字节长度
3003
- * @returns 返回截取后的子串
3004
- */
3005
- subStringByte(str, start, n) {
3006
- var r = /[^\x00-\xff]/g;
3007
- if (str.replace(r, "mm").length <= n) {
3008
- return str;
3009
- }
3010
- var m = Math.floor(n / 2);
3011
- for (var i = m; i < str.length; i++) {
3012
- let sub = str.substring(start, i);
3013
- if (sub.replace(r, "mm").length >= n) {
3014
- return sub;
3015
- }
3016
- }
3017
- return str;
3018
- }
3019
- };
3020
3153
  const UrlUtil = {
3021
3154
  /**
3022
3155
  * 将json对象转换为查询字符串
@@ -3069,6 +3202,7 @@ const UrlUtil = {
3069
3202
  export {
3070
3203
  AjaxUtil,
3071
3204
  ArrayUtil,
3205
+ AssertUtil,
3072
3206
  AudioPlayer,
3073
3207
  BrowserUtil,
3074
3208
  CanvasDrawer,
@@ -3097,6 +3231,6 @@ export {
3097
3231
  Storage,
3098
3232
  StringUtil,
3099
3233
  UrlUtil,
3100
- CommUtils as Util,
3234
+ CommUtil as Util,
3101
3235
  WebSocketClient
3102
3236
  };