lxf hai 1 semana
pai
achega
5fd062532d

+ 2 - 1
package.json

@@ -35,7 +35,8 @@
   "eslintConfig": {
     "root": true,
     "env": {
-      "node": true
+      "node": true,
+      "vue/setup-compiler-macros": true
     },
     "globals": {
       "VE_ENV": "readonly",

+ 1 - 3
src/api/config.js

@@ -1,13 +1,11 @@
 const newServer = VE_ENV.SERVER;
 const pyServer = VE_ENV.PYSERVER;
 const newPathServer = VE_ENV.NEW_SERVER;
-const oldServer = "https://birdseye-api.sysuimars.com/";
-const fosterServer = "https://foster-api.sysuimars.com/";
+const oldServer = "https://birdseye-api.feiniaotech.sysuimars.cn/";
 
 export default {
   base_url: oldServer + "site/",
   base_dev_site_url: newServer + "site/",
-  base_foster_url: fosterServer + "app/",
   base_mini_url: oldServer + "mini/",
   base_dev_url: newServer + "mini/",
   base_new_url: newPathServer + "api/",

+ 5 - 8
src/api/modules/system.js

@@ -1,16 +1,13 @@
-/**
- * API 模块示例
- * 新增业务接口:在 modules 下新建 xxx.js,自动挂到 VE_API.xxx
- *
- * @example
- * this.VE_API.system.ping()
- */
 import config from "../config";
 
 export default {
-  // 示例接口,可删除或按实际后端替换
   ping: {
     url: config.base_dev_url + "health",
     type: "get",
   },
+  /** 地图底图等配置 */
+  getCfg: {
+    url: config.base_url + "cfg/get",
+    type: "post",
+  },
 };

+ 2 - 1
src/common/ol_common.js

@@ -53,6 +53,7 @@ function crtLayerWMTS(wmtsData, opacity, projection) {
     matrixSet: wmtsData.matrixSet,
     format: "tiles",
     projection: projection,
+    crossOrigin: "anonymous",
     tileGrid: new WMTSTileGrid({
       origin: olExtent.getTopLeft(projectionExtent),
       resolutions: resolutions,
@@ -65,7 +66,7 @@ function crtLayerWMTS(wmtsData, opacity, projection) {
   const layer = new TileLayer({
     zIndex: wmtsData.zIndex,
     source: source,
-    visible: false,
+    visible: true,
     opacity: opacity ?? 1,
   });
   layer.id = wmtsData.layer + "_" + wmtsData.matrixSet;

+ 78 - 0
src/utils/ol-map/Bounds.js

@@ -0,0 +1,78 @@
+import * as extent  from "ol/extent"
+import LngLat from "./LngLat"
+/**
+ * @description KMap.Bounds 经纬度矩形范围类
+ */
+class Bounds{
+  /**
+   * @param {KMap.LngLat} southWest 矩形范围西南角坐标,必填,格式new KMap.LngLat()
+   * @param {KMap.LngLat} northEast 矩形范围东北角坐标,必填,格式new KMap.LngLat()
+   */
+  constructor(southWest,northEast){
+		const vm = this
+		vm.southWest = southWest
+		vm.northEast = northEast
+    let mapSouthWest = [southWest.getLng(),southWest.getLat()]
+    let mapNorthEast = [northEast.getLng(),northEast.getLat()]
+    let bounds = new extent.boundingExtent([mapSouthWest,mapNorthEast])
+    this.bounds = bounds
+  }
+
+	/**
+	 * @description 判断指定点坐标是否在矩形范围内
+	 * @param {KMap.LngLat} point 经纬度点,KMap.LngLat格式,必填
+	 * @returns {boolean}在矩形范围内返回true,否则返回false
+	 */
+  contains(point) {
+		const vm = this
+		if( (point.getLng() >= vm.southWest.getLng() && point.getLng() <= vm.northEast.getLng())
+			&& (point.getLat() >= vm.southWest.getLat() && point.getLat() <= vm.northEast.getLat()) )
+		{
+			return true
+		}else{
+			return false
+		}
+  }
+
+  /**
+   * @description 获取中心点坐标
+   * @returns {KMap.LngLat}中心点坐标,KMap.LngLat格式
+   */
+	getCenter() {
+		const vm = this
+		let lng = (vm.southWest.getLng() + vm.northEast.getLng()) / 2
+		let lat = (vm.southWest.getLat() + vm.northEast.getLat()) / 2
+		var center = new LngLat(lng,lat)
+		return center
+	}
+
+	/**
+	 * @description 获取西南角坐标
+	 * @returns {KMap.LngLat}西南角坐标,KMap.LngLat格式
+	 */
+	getSouthWest() {
+		const vm = this
+		return vm.southWest
+	}
+
+	/**
+	 * @description 获取东北角坐标
+	 * @returns {KMap.LngLat}东北角坐标,KMap.LngLat格式
+	 */
+	getNorthEast() {
+		const vm = this
+		return vm.northEast
+	}
+
+	/**
+	 * @description 以字符串形式返回地物对象的矩形范围
+	 * @returns {String}西南角经度、西南角纬度:东北角经度、东北角纬度
+	 */
+  toString() {
+		const vm = this
+        return vm.southWest.getLng() + "," + vm.southWest.getLat() + ":"
+               + vm.northEast.getLng() + "," + vm.northEast.getLat()
+  }
+}
+
+export default Bounds

+ 63 - 0
src/utils/ol-map/Check.js

@@ -0,0 +1,63 @@
+import * as Info from './Info'
+class Check{
+  static lngLat(lng,lat){
+    let msg = ''
+    let result = false
+    if(lat == null || lng == null || lat == undefined || lng == undefined){
+      msg = Check.addHeader("经纬度不能为null")
+      return Check.message(msg,result)
+    }
+    if(Check.isNumber(lat) || Check.isNumber(lng)){
+      msg = Check.addHeader("经纬度应为数字")
+      return Check.message(msg,result)
+    }
+    if(lat<-90 || lat>90){
+      msg = Check.addHeader("纬度lat应该大于-90小于90")
+      return Check.message(msg,result)
+    }
+    if(lng<-180 || lng>180){
+      msg = Check.addHeader("经度lng应该大于-180小于180")
+      return  Check.message(msg,result)
+    }
+    if(msg == ''){
+      result = true
+    }
+    return Check.message(msg,result)
+  }
+
+  static isNumber(str){
+    
+    if(str == null || undefined){
+      return false
+    }
+    
+    if((typeof str=='string')&&str.constructor==String){
+      return false
+    }
+    
+    if(!isNaN(str)){
+      return false
+    }
+    return true
+  }
+  static notEmpty(name,str){
+    let result = false;
+    if(str == null || undefined){
+      result =  false
+    }
+    
+    if((typeof str=='string') && str.constructor==String && str !=''){
+      result =  true
+    }else{
+      result =  false
+    }
+    return Check.message(Check.addHeader(name+"必须为字符串且不能为空"),result);
+  }
+  static addHeader(str){
+    return Info.version+":"+str
+  }
+  static message(msg,isPass){
+    return {msg:msg,isPass:isPass}
+  }
+}
+export default Check

+ 145 - 0
src/utils/ol-map/Common.js

@@ -0,0 +1,145 @@
+import Size from './Size'
+import Pixel from './Pixel'
+import LngLat from './LngLat'
+import Bounds from './Bounds'
+import Check from './Check'
+import * as olProj from 'ol/proj';
+/**
+ * @description KMap.Common类 通用静态方法
+ */
+class Common{
+	static ShowLevel = [1,22]
+	/**
+	 *@description 底图Zoom限制
+	*/
+	static BaseLayerZoom = [1,22]
+	/**
+	 * @description 利通地图像素转OpenLayers地图像素
+	 * @param {KMap.Pixel} pixel KMap.Pixel格式的像素,必填
+	 * @returns {Array} OpenLayers格式的像素,包含两个元素的数组[x,y]
+	*/
+	static KMapPixel2MapPixel(pixel){
+		let mapPixel = [pixel.getX(),pixel.getY()]
+		return mapPixel
+	}
+
+	/**
+	 * @description OpenLayers地图像素转利通地图像素
+	 * @param {Array} pixel OpenLayers格式的像素,包含两个元素的数组[x,y],必填
+	 * @returns {KMap.Pixel} KMap.Pixel格式的像素
+	*/
+	static MapPixel2KMapPixel(pixel) {
+		let ltPixel = new Pixel(pixel[0], pixel[1])
+		return ltPixel
+	}
+
+	/**
+	 * @description 利通地图像素尺寸转OpenLayers地图像素尺寸
+	 * @param {KMap.Size} size KMap.Size格式的尺寸,必填
+	 * @returns {Array} OpenLayers地图像素尺寸,包含两个元素的数组[width,height]
+	*/
+	static KMapSize2MapSize(size) {
+		let mapSize = [size.getWidth(), size.getHeight()]
+		return mapSize
+	}
+
+	/**
+	 * @description OpenLayers地图像素尺寸转利通地图像素尺寸
+	 * @param {Array} size OpenLayers地图像素尺寸,包含两个元素的数组[width,height],必填
+	 * @returns {KMap.Size} 格式的尺寸
+	*/
+	static MapSize2KMapSize(size) {
+		let ltSize = new Size(size[0], size[1])
+		return ltSize
+	}
+
+	/**
+	 * @description 利通地图经纬度转OpenLayers地图经纬度
+	 * @param {KMap.LngLat} lnglat KMap.LngLat格式的经纬度,必填
+	 * @returns {Array} OpenLayers的经纬度格式,包含两个元素的数组[lng,lat]
+	*/
+	static KMapLngLat2MapLngLat(lnglat) {
+		let alnglat = [lnglat.getLng(),lnglat.getLat()]
+		return alnglat
+	}
+
+	/**
+	 * @description OpenLayers地图经纬度转利通地图经纬度
+	 * @param {Array} lnglat OpenLayers的经纬度格式,包含两个元素的数组[lng,lat],必填
+	 * @returns {KMap.LngLat} KMap.LngLat格式的经纬度
+	*/
+	static MapLngLat2KMapLngLat(lnglat) {
+		let ltlnglat = new LngLat(lnglat[0], lnglat[1])
+		return ltlnglat
+	}
+
+	/**
+	 * @description 利通地图经纬度矩形范围转OpenLayers地图经纬度矩形范围
+	 * @param {KMap.Bounds} bounds KMap.Bounds对象,必填
+	 * @returns {Array} 西南角经度、西南角纬度、东北角经度、东北角纬度构成的数组
+	*/
+	static KMapBounds2MapBounds(bounds) {
+		let array = new Array()
+		let southWest = bounds.getSouthWest()
+		let northEast = bounds.getNorthEast()
+		array.push(southWest.getLng())
+		array.push(southWest.getLat())
+		array.push(northEast.getLng())
+		array.push(northEast.getLat())
+		return array
+	}
+
+	/**
+	 * @description OpenLayers地图经纬度矩形范围转利通地图经纬度范围
+	 * @param {Array} bounds 西南角经度、西南角纬度、东北角经度、东北角纬度构成的数组,必填
+	 * @returns {KMap.Bounds} KMap.Bounds类型对象
+	*/
+	static MapBounds2KMapBounds(bounds) {
+		let southWest = [bounds[0],bounds[1]]
+		let northEast = [bounds[2],bounds[3]]
+		southWest = new LngLat(southWest[0],southWest[1])
+		northEast = new LngLat(northEast[0],northEast[1])
+		bounds = new Bounds(southWest,northEast)
+		return bounds
+	}
+	static toWGS84LngLat(map,coordinate){
+		map.getCoor
+		return olProj.transform(coordinate,map.getProjection(),"EPSG:4326")
+	}
+
+	/**
+	 * @description 扩展JSON对象属性
+	 * @param {JSON} des 目标JSON对象,必填
+	 * @param {JSON} src 源JSON对象,必填
+	 * @param {boolean} override 是否覆盖属性,选填
+	 * @returns {JSON} 目标JSON对象
+	*/
+	static extend(des, src, override){
+		if(src instanceof Array){
+			for(let i = 0, len = src.length; i < len; i++)
+				Common.extend(des, src[i], override)
+		}
+		for( let i in src){
+			if(override || !(i in des)){
+				des[i] = src[i]
+			}
+		}
+		return des
+	}
+	static checkLngLat(lng,lat){
+		let info = Check.lngLat(lng,lat)
+    if(!info.isPass){
+      throw new Error(info.msg)
+    }
+	}
+
+	static notEmpty(name,str){
+		let info = Check.notEmpty(name,str)
+    if(!info.isPass){
+      throw new Error(info.msg)
+    }
+	}
+
+}
+
+export default Common

+ 9 - 0
src/utils/ol-map/Enum.js

@@ -0,0 +1,9 @@
+/**
+ * @description KMap.LayerTypeEnum 底图类型枚举
+*/
+export const LayerTypeEnum = {
+  'ARCGISTile':"ARCGISTile",
+  'GaoDeTile':"GaoDeTile",
+  "WGS84Tile":"WGS84Tile",
+  "BaiDuTile":"BaiDuTile"
+}

+ 19 - 0
src/utils/ol-map/Info.js

@@ -0,0 +1,19 @@
+/**
+ * @module KMap/Info
+ * @description api基本信息
+ */
+
+/**
+ * @const
+ * @version
+ * @type {string}
+ * @description 版本
+ */
+export const version = "KMap 1.0 Base On OpenLayers6.14.1"
+
+/**
+ * @const
+ * @type {string}
+ * @description 原生引擎名称
+ */
+export const srcApiName = "OpenLayers6.14.1"

+ 11 - 0
src/utils/ol-map/KBaseObject.js

@@ -0,0 +1,11 @@
+import Map from  './Map'
+class KBaseObject{
+  constructor(mapInstance){
+    const vm = this
+		//利通map实例
+		vm.mapInstance = mapInstance || Map.Instance
+		//获取ol map对象
+		vm.map = vm.mapInstance.map
+  }
+}
+export default KBaseObject

+ 15 - 0
src/utils/ol-map/KMap.js

@@ -0,0 +1,15 @@
+import Map from "./Map";
+import Common from "./Common";
+import WMTSLayer from "./WMTSLayer";
+import VectorLayer from "./VectorLayer";
+import XYZLayer from "./XYZLayer";
+import VectorStyle from "./VectorStyle";
+
+export {
+  Map,
+  Common,
+  WMTSLayer,
+  XYZLayer,
+  VectorLayer,
+  VectorStyle,
+};

+ 78 - 0
src/utils/ol-map/LngLat.js

@@ -0,0 +1,78 @@
+import Common from "./Common"
+/**
+ * @description KMap.LngLat 经纬度
+*/
+class LngLat{
+  /**
+   * @param {number} lng 纬度
+   * @param {number} lat 经度
+   * @constructor
+   */
+  constructor(lng,lat){
+    Common.checkLngLat(lng,lat)
+    let maplnglat = [Number(lng),Number(lat)]
+    this.lngLat = maplnglat
+  }
+
+
+	/**
+	 * @description 当前经纬度坐标值经度移动w,纬度移动s,得到新的坐标。 经度向右移为正值,纬度向上移为正值,单位为°
+	 * @param {number} w 经度移动量
+	 * @param {number} s 纬度移动量
+	 */
+  offset(w, s) {
+    let lng = this.lngLat[0]+w
+    let lat = this.lngLat[1]+s
+    return new LngLat(lng,lat)
+  }
+
+  /**
+   * @description 当前经纬度和传入经纬度之间的地面距离,单位为米----暂无该方法
+   * @param {number} lnglat 经纬度
+   */
+  distance(lnglat) {
+    return null
+  }
+
+  /**
+   * @description 获取经度
+   * @returns {number} 返回经度
+   */
+	getLng() {
+		let lng = this.lngLat[0]
+		return lng
+	}
+
+	/**
+	 * @description 获取纬度
+	 * @returns {number} 返回纬度
+	 */
+	getLat() {
+		let lat = this.lngLat[1]
+		return lat
+	}
+
+	/**
+	 * @description 判断当前坐标对象与传入坐标对象是否相等
+	 * @param {KMap.LngLat} lnglat 格式的经纬度,必填
+	 * @returns {boolean} 坐标相等返回true,坐标不相等返回false
+	 */
+  equals(lnglat) {
+    if(lnglat.getLng() == this.lngLat[0] && lnglat.getLat() == this.lngLat[1]){
+      return true
+    }
+    else{
+      return false
+    }
+  }
+
+  /**
+   * @description LngLat对象以字符串的形式返回。
+   * @returns {String} 返回经纬度格式的字符串,用逗号连接
+   */
+  toString() { 
+    return this.lngLat[0] + "," + this.lngLat[1]
+  }
+}
+
+export default LngLat

+ 127 - 0
src/utils/ol-map/Map.js

@@ -0,0 +1,127 @@
+import OLMap from "ol/Map";
+import View from "ol/View";
+import * as proj from "ol/proj";
+import * as interaction from "ol/interaction";
+import "ol/ol.css";
+import "./css/KMap.css";
+import Common from "./Common";
+import VectorLayer from "./VectorLayer";
+import WMTSLayer from "./WMTSLayer";
+import XYZLayer from "./XYZLayer";
+import config from "@/api/config.js";
+
+/**
+ * KMap.Map —— 与 feiniao-pc-vue 用法对齐的精简实现
+ * new KMap.Map(target, level, lng, lat, projection, minZoom, maxZoom, ...)
+ */
+class Map {
+  static Instance = null;
+
+  constructor(id, zoomLevel, lng, lat, projection, minZoom, maxZoom) {
+    this.defaultCursor = "default";
+    Map.Instance = this;
+
+    if (projection) {
+      projection = proj.get(projection);
+    }
+    projection = projection || proj.get("EPSG:4326");
+
+    let lnglat = [lng, lat];
+    if (projection.getCode() === "EPSG:3857") {
+      lnglat = proj.fromLonLat(lnglat);
+    }
+
+    Common.checkLngLat(lng, lat);
+
+    this.view = new View({
+      center: lnglat,
+      zoom: zoomLevel,
+      minZoom: minZoom || Common.ShowLevel[0],
+      maxZoom: maxZoom || Common.ShowLevel[1],
+      projection,
+    });
+
+    this.map = new OLMap({
+      interactions: interaction.defaults().extend([new interaction.DragRotateAndZoom()]),
+      target: id,
+      layers: [],
+      view: this.view,
+      controls: [],
+    });
+
+    this.initBaseLayer(projection);
+    this.initBusinessLayer();
+  }
+
+  /** 与 pc-vue 一致:img_wmts_mkt + cva_wmts_mkt + 默认 XYZ */
+  async initBaseLayer(projection) {
+    try {
+      const getCfg = window.VE_API?.system?.getCfg;
+      if (getCfg) {
+        const img_wmts = await getCfg({ k: "img_wmts_mkt", resultType: "json" });
+        const cva_wmts = await getCfg({ k: "cva_wmts_mkt", resultType: "json" });
+        const imgData = typeof img_wmts?.data === "string" ? JSON.parse(img_wmts.data) : img_wmts?.data;
+        const cvaData = typeof cva_wmts?.data === "string" ? JSON.parse(cva_wmts.data) : cva_wmts?.data;
+        if (imgData?.url) {
+          this.tdtImgLayer = new WMTSLayer(imgData, projection, this);
+        }
+        if (cvaData?.url) {
+          this.cva_torLayer = new WMTSLayer(cvaData, projection, this);
+        }
+      }
+    } catch (e) {
+      console.warn("[KMap.Map] 底图 WMTS 配置加载失败", e);
+    }
+
+    const xyz2 = config.base_img_url3 + "map/lby/{z}/{x}/{y}.png";
+    this.addXYZLayer(xyz2, { minZoom: 15, maxZoom: 22 });
+  }
+
+  addXYZLayer(url, options) {
+    return new XYZLayer(url, options, 3, this);
+  }
+
+  initBusinessLayer() {
+    const vm = this;
+    const map = vm.map;
+    vm.markerLayer = new VectorLayer("defaultMarkerLayer", 101);
+    vm.polyLineLayer = new VectorLayer("defaultPolylineLayer", 101);
+    vm.polygonLayer = new VectorLayer("defaultPolygonLayer", 1000);
+    vm.labelLayer = new VectorLayer("defaultLabelLayer", 99);
+
+    map.addLayer(vm.polygonLayer.layer);
+    map.once("postrender", () => {
+      map.addLayer(vm.markerLayer.layer);
+      map.addLayer(vm.polyLineLayer.layer);
+      map.addLayer(vm.labelLayer.layer);
+    });
+  }
+
+  addLayer(layer) {
+    this.map.addLayer(layer);
+  }
+
+  fit(geometryOrExtent, options) {
+    this.view.fit(geometryOrExtent, options);
+  }
+
+  on(type, listener) {
+    return this.map.on(type, listener);
+  }
+
+  updateSize() {
+    this.map.updateSize();
+  }
+
+  destroy() {
+    if (this.map) {
+      this.map.setTarget(null);
+      this.map = null;
+    }
+    if (Map.Instance === this) {
+      Map.Instance = null;
+    }
+  }
+}
+
+export default Map;

+ 55 - 0
src/utils/ol-map/Pixel.js

@@ -0,0 +1,55 @@
+/**
+ * @description KMap.Pixel 像素类
+*/
+class Pixel{
+  /**
+   * @description 像素类构造函数
+   * @param {number} x X像素,必填
+   * @param {number} y Y像素,必填
+   */
+  constructor(x,y){
+    let mapPixel = [Number(x),Number(y)];
+    this.pixel = mapPixel;
+  }
+
+  /**
+   * @description 获得X方向像素坐标
+   * @returns {number} 返回X方向像素坐标
+   */
+  getX() { 
+    return this.pixel[0]; 
+  }
+
+  /**
+   * @description 获得Y方向像素坐标
+   * @returns {number} 返回Y方向像素坐标
+   */
+  getY() { 
+    return this.pixel[1]; 
+  }
+
+  /**
+   * @description 当前像素坐标与传入像素坐标是否相等,必填
+   * @returns {boolean} 相等返回true,不相等返回false
+   */
+  equals(point) {
+      if(point.getX() == this.pixel[0] && point.getY() == this.pixel[1])
+      {
+        return true;
+      }
+      else
+      {
+        return false;
+      }
+  }
+
+  /**
+   * @description 以字符串形式返回像素坐标对象
+   * @returns {String} 像素坐标字符串,用逗号连接
+   */
+  toString() { 
+    return this.pixel[0] + "," + this.pixel[1] ; 
+  }
+}
+
+export default Pixel

+ 39 - 0
src/utils/ol-map/Size.js

@@ -0,0 +1,39 @@
+/**
+ * @description KMap.Size 大小类
+ */
+class Size{
+  /**
+   * @param {number} width 宽度
+   * @param {number} height 高度
+   * @constructor
+   */
+  constructor(width,height){
+    let mapSize = [Number(width),Number(height)]
+    this.size = mapSize
+  }
+
+  /**
+  * @description 获得宽度
+  * @returns {number} 宽度
+  */
+  getWidth() { 
+    return this.size[0]
+  };
+
+  /**
+  * @description 获取高度
+  * @returns {number} 高度
+  */
+  getHeight() { 
+    return this.size[1]
+  }
+
+  /**
+  * @description 以字符串形式返回尺寸大小对象
+  * @returns {number} 像素尺寸字符串,用逗号连接
+  */
+  toString() { 
+    return this.size[0] + "," + this.size[1]
+  }
+}
+export default Size

+ 91 - 0
src/utils/ol-map/VectorLayer.js

@@ -0,0 +1,91 @@
+import Layer from 'ol/layer/Vector'
+import Source from 'ol/source/Vector'
+import Common from './Common'
+import Select from 'ol/interaction/Select.js';
+import {singleClick} from 'ol/events/condition'
+/**
+ * @description KMap.VectorLayer 矢量图层
+ */
+class VectorLayer {
+  /**
+   * @param {string} name 图层名称
+   * @param {string} zIndex 图层层级
+  */
+  constructor(name,zIndex,options){
+    let source = new Source({
+      crossOrigin:'anonymous',
+    })
+    let ShowLevel = Common.ShowLevel
+    let minZoom = ShowLevel[0]
+    let maxZoom = ShowLevel[1]
+    let style = null;
+    if(options && options.source){
+      source = options.source
+    }
+    if(options && options.minZoom){
+      minZoom = options.minZoom
+    }
+    if(options && options.maxZoom){
+      maxZoom = options.maxZoom
+    }
+    if(options && options.style){
+      style = options.style
+    }
+    let layer = new Layer({
+      source:source,
+      zIndex:zIndex,
+      minZoom:minZoom,
+      maxZoom:maxZoom,
+    })
+    layer.set('name', name)
+    if(style){
+      layer.setStyle(style)
+    }
+    this.layer = layer
+    this.source = source
+  }
+  setMaxZoom(maxZoom){
+    this.layer.setMaxZoom(maxZoom);
+  }
+  setMinZoom(minZoom){
+    this.layer.setMinZoom(minZoom);
+  }
+  addFeature(feature){
+    this.source.addFeature(feature)
+  }
+  refresh(){
+    this.source.refresh()
+  }
+  getFeatureById(id){
+    return this.source.getFeatureById(id)
+  }
+  addSingleSelect(callback,map,style){
+    let option = {
+        condition:singleClick,
+        layers:[this.layer],
+        multi:false
+    };
+    if(style){
+      option["style"] = style
+    }
+    this.singleSelect = new Select(option)
+    this.singleSelect.on("select",callback)
+    map.addInteraction(this.singleSelect)
+  }
+  addToggleSelect(callback,map,style){
+    let option = {
+      condition:singleClick,
+      toggleCondition: singleClick,
+      layers:[this.layer],
+      multi:true
+    };
+    if(style){
+      option["style"] = style
+    }
+    this.toggleSelect = new Select(option)
+    this.toggleSelect.on("select",callback)
+    map.addInteraction(this.toggleSelect)
+  }
+}
+
+export default VectorLayer

+ 119 - 0
src/utils/ol-map/VectorStyle.js

@@ -0,0 +1,119 @@
+
+import LTBaseObject from './KBaseObject'
+import XYZ from 'ol/source/XYZ'
+import Tile from 'ol/layer/Tile'
+import Common from './Common'
+import Style from "ol/style/Style";
+import Fill from "ol/style/Fill";
+import Stroke from "ol/style/Stroke";
+import Text from "ol/style/Text";
+import Icon from "ol/style/Icon";
+import Circle from "ol/style/Circle";
+/**
+ * @description 样式类
+ */
+class VectorStyle{
+	constructor(){
+		this.cachePointSimpleStyle = {}
+	}
+	getLineStyle(fillColor, strokeColor, strokeWidth){
+		let style = new Style({
+			stroke: new Stroke({
+				color: strokeColor,
+				width: strokeWidth || 1,
+			}),
+		});
+		return style;
+	}
+
+	getPolygonStyle(fillColor, strokeColor, strokeWidth){
+		let style = new Style({
+			fill: new Fill({
+				color: fillColor
+			}),
+			stroke: new Stroke({
+				color: strokeColor,
+				width: strokeWidth || 1,
+			}),
+		});
+		return style;
+	}
+	getPointStyle(src, scale, anchor, text, font, textColor){
+		let textObj = null;
+		text &&	(textObj = new Text({
+			text:text,
+			stroke: new Stroke({
+				color: textColor,
+				width: 1,
+			}),
+			fill: new Fill({
+				color: textColor,
+			}),
+			font:font
+		}))
+		let style = new Style({
+			image: new Icon({
+				text:textObj,
+				src:src,
+				scale:scale,
+				anchor:anchor,
+			})
+		});
+		return style
+	}
+
+	/**
+	 *
+	 * @param radius 半径
+	 * @param fillColor 填充颜色
+	 * @param strokeColor 边框颜色
+	 * @param strokeWidth 边框宽度
+	 * @returns {Style}
+	 */
+	getPointSimpleStyle(radius, fillColor, strokeColor, strokeWidth){
+		let key = radius + fillColor + strokeColor + strokeWidth
+		if(this.cachePointSimpleStyle[key]){
+			return this.cachePointSimpleStyle[key]
+		}
+		let style = new Style({
+			image: new Circle({
+				radius: radius,                             // 半径
+				stroke: new Stroke({           // 边界样式
+					color: strokeColor,                    // 边界颜色
+					width: strokeWidth                           // 边界宽度
+				}),
+				fill: new Fill({               // 填充样式
+					color: fillColor                       // 填充颜色
+				})
+			})
+		});
+		this.cachePointSimpleStyle[key] = style
+		return style
+	}
+
+	/**
+	 *
+	 * @param text
+	 * @param fillColor 填充颜色
+	 * @param strokeColor 边框颜色
+	 * @param strokeWidth 边框宽度
+	 * @returns {Style}
+	 */
+	getPointTextStyle(text, fillColor, strokeColor, strokeWidth, fontSize){
+		let style = new Style({
+			text: new Text({
+				text:text,
+				stroke: new Stroke({
+					color: strokeColor,
+					width: strokeWidth,
+				}),
+				fill: new Fill({
+					color: fillColor,
+				}),
+				font: fontSize+"px sans-serif"
+			}),
+		});
+		return style
+	}
+}
+export default VectorStyle

+ 112 - 0
src/utils/ol-map/WMTSLayer.js

@@ -0,0 +1,112 @@
+import LTBaseObject from './KBaseObject'
+import TileLayer from 'ol/layer/Tile'
+import sourceWMTS from 'ol/source/WMTS';
+import WMTSTileGrid from 'ol/tilegrid/WMTS';
+import * as olExtent from 'ol/extent';
+/**
+ * @description KMap.WMTSLayer WMS图层类
+*/
+class WMTSLayer extends LTBaseObject{
+	/**
+	 * @description 构造函数
+	 * @param {String} url wms图层服务地址
+	 * @param {LTMap.Map} [mapInstance=null] map对象,单地图的时候可不传,多地图时候需要传
+	 * @memberof WMTSLayer
+	 */
+	constructor(wmtsData,projection,mapInstance = null){
+		super(mapInstance)
+		const vm = this;
+		vm.initLayer(wmtsData,projection)
+	}
+
+	/**
+		* @description 初始化ImageLayer
+		* @memberof WMTSLayer
+		*/
+	initLayer(wmtsData,projection){
+		const vm = this;
+		let projectionExtent = projection.getExtent();
+    let size = olExtent.getWidth(projectionExtent) / 256;
+    let resolutions = new Array(19);
+    let matrixIds = new Array(19);
+    for (var z = 1; z < 19; ++z) {
+        // generate resolutions and matrixIds arrays for this WMTS
+        resolutions[z] = size / Math.pow(2, z);
+        matrixIds[z] = z;
+    }
+
+    let source = new sourceWMTS({
+        url: wmtsData.url,
+        layer: wmtsData.layer,
+        matrixSet: wmtsData.matrixSet,
+        format: 'tiles',
+        projection: projection,
+		crossOrigin:'anonymous',
+        tileGrid: new WMTSTileGrid({
+            origin: olExtent.getTopLeft(projectionExtent),
+            resolutions: resolutions,
+            matrixIds: matrixIds
+        }),
+        style: 'default',
+        wrapX: true
+    })
+    let layer = new TileLayer({
+        zIndex:wmtsData.zIndex,
+        source: source,
+        // visible: false,
+    });
+    layer.id = wmtsData.layer+"_"+wmtsData.matrixSet;
+		vm.layer = layer;
+		vm.source = source;
+		vm.map.addLayer(vm.layer)
+    return layer;
+    //地图加载WMS图层
+	}
+
+  /**
+	* @description 添加WMS图层到地图
+	* @memberof WMTSLayer
+	*/
+  add(){
+		const vm = this
+		if(!vm.state) {
+			vm.map.addLayer(vm.layer)
+			vm.state = true
+		}
+	}
+
+	/**
+	 * @description 从当前地图中移除WMS图层
+	 * @memberof WMTSLayer
+	 */
+	remove() {
+		const vm = this
+		if(vm.state) {
+			vm.map.removeLayer(vm.layer)
+			vm.state = false
+		}
+	}
+
+	/**
+	 * @description 显示WMS图层数据
+	 * @memberof WMTSLayer
+	 */
+	show(){
+		const vm = this
+		if(vm.state) {
+			vm.layer.setVisible(true)
+		}
+	}
+
+	/**
+	 * @description 隐藏WMS图层数据
+	 * @memberof WMTSLayer
+	 */
+	hide(){
+		const vm = this
+		if(vm.state) {
+			vm.layer.setVisible(false)
+		}
+	}
+}
+export default WMTSLayer

+ 116 - 0
src/utils/ol-map/XYZLayer.js

@@ -0,0 +1,116 @@
+
+import LTBaseObject from './KBaseObject'
+import XYZ from 'ol/source/XYZ'
+import Tile from 'ol/layer/Tile'
+import Common from './Common'
+/**
+ * @description LTMap.XYZLayer XYZ图层类
+*/
+class XYZLayer extends LTBaseObject{
+	/**
+	 * @description 构造函数
+	 * @param {String} url XYZ图层服务地址
+	 * @param {LTMap.Map} [mapInstance=null] map对象,单地图的时候可不传,多地图时候需要传
+	 * @memberof XYZLayer
+	 */
+	constructor(url,options,zIndex,mapInstance = null){
+    super(mapInstance)
+		const vm = this
+		vm.initXYZLayer(url,options,zIndex)
+  }
+	/**
+	 * @description 切片加载XYZ图层
+	 * @param {*} url
+	 * @param {*} layerName
+	 * @memberof XYZLayer
+	 */
+	 initXYZLayer(url,options,zIndex){
+		var minZoom = Common.BaseLayerZoom[0];
+		var maxZoom = Common.BaseLayerZoom[1];
+		if(options && options.minZoom != undefined){
+			minZoom = options.minZoom
+		}
+		if(options && options.maxZoom != undefined){
+			maxZoom = options.maxZoom
+		}
+		if(options && options.zIndex != undefined){
+			zIndex = options.zIndex
+		}
+		const vm = this
+		vm.source = new XYZ({
+			crossOrigin:'anonymous',
+      		url : url
+    	})
+
+		vm.layer = new Tile({
+			source:vm.source,
+			maxZoom:maxZoom,
+			minZoom:minZoom,
+			zIndex:zIndex,
+			extent:options.extent || null
+		})
+		if(options && options.padding != undefined){
+			vm.layer.set('padding',options.padding)
+		}
+
+		vm.map.addLayer(vm.layer)
+	}
+	setProperty(properties){
+		const vm = this;
+		vm.properties = properties;
+	}
+	getProperty(){
+		const vm = this;
+		return vm.properties?vm.properties:null;
+	}
+  /**
+	* @description 添加XYZ图层到地图
+	* @memberof XYZLayer
+	*/
+  add(){
+		const vm = this
+		if(!vm.state) {
+			vm.map.addLayer(vm.layer)
+			vm.state = true
+		}
+	}
+
+	/**
+	 * @description 从当前地图中移除XYZ图层
+	 * @memberof XYZLayer
+	 */
+	remove() {
+		const vm = this
+		if(vm.state) {
+			vm.map.removeLayer(vm.layer)
+			vm.state = false
+		}
+	}
+
+	/**
+	 * @description 显示XYZ图层数据
+	 * @memberof XYZLayer
+	 */
+	show(){
+		const vm = this
+		vm.layer.setVisible(true)
+	}
+
+	/**
+	 * @description 隐藏XYZ图层数据
+	 * @memberof XYZLayer
+	 */
+	hide(){
+		const vm = this
+		vm.layer.setVisible(false)
+	}
+
+	/**
+	 * @description 缩放到过滤后的地图要素对应范围
+	 * @memberof XYZLayer
+	 */
+	zoomTo() {
+    const vm = this
+	}
+}
+export default XYZLayer

+ 88 - 0
src/utils/ol-map/css/KMap.css

@@ -0,0 +1,88 @@
+.ol-mouse-position-KMap{
+  color:#66b166;
+  position: absolute;
+  font-weight: 500;
+  bottom:8px;
+  right:8px;
+}
+/*鹰眼控件展开时的控件外边框*/
+.myOverview:not(.ol-collapsed){
+  border:1px solid black;
+}
+/*鹰眼控件地图容器边框样式*/
+.myOverview .ol-overviewmap-map{
+  border:none;
+  width:200px;
+  height: 100px;
+}
+/*鹰眼控件中显示当前窗口区域的边框样式*/
+.myOverview .ol-overviewmap-box{
+  border:2px solid red;
+}
+ /*鹰眼控件展开时其控件按钮图标的样式*/
+.myOverview:not(.ol-collapsed) button{
+  bottom:auto;
+  left:auto;
+  right:1px;
+  top:1px;
+}
+.ol-overviewmap{
+  left: auto !important;
+  right:0.5em;
+  bottom: 0.5em;
+}
+/*2D视图中添加自定义视图旋转控制器的样式*/
+.brightmap2d-rotate-control-custom {
+  position: absolute;
+  bottom: 90px;
+  right: 9px;
+  width: 52px;
+  height: 54px;
+  background: url(./rotate.png) 0% 0% / 266px no-repeat;
+}
+
+.center2d-button-custom {
+  position: absolute;
+  outline: none;
+  border: none;
+  background: url(./rotate.png) -56px -4px / 266px no-repeat;
+  cursor: pointer;
+  left: 19px;
+  top: 4px;
+  width: 14px;
+  height: 44px;
+  transform: rotate(0deg);
+}
+
+.right2d-button-custom {
+  position: absolute;
+  outline: none;
+  border: none;
+  background: url(./rotate.png) -75px -5px / 266px no-repeat;
+  right: 2px;
+  top: 5px;
+  width: 15px;
+  height: 42px;
+  transform: scaleX(-1);
+}
+
+.left2d-button-custom {
+  position: absolute;
+  outline: none;
+  border: none;
+  background: url(./rotate.png) -75px -5px / 266px no-repeat;
+  left: 2px;
+  top: 5px;
+  width: 15px;
+  height: 42px;
+}
+
+.left2d-button-custom:hover {
+  cursor: pointer;
+  background: url(./rotate.png) -89px -5px / 266px no-repeat;
+}
+
+.right2d-button-custom:hover {
+  cursor: pointer;
+  background: url(./rotate.png) -89px -5px / 266px no-repeat;
+}

+ 52 - 0
src/views/home/homeMap.js

@@ -0,0 +1,52 @@
+import config from "@/api/config.js";
+import * as KMap from "@/utils/ol-map/KMap";
+import * as util from "@/common/ol_common.js";
+
+/**
+ * 首页地图(参考 workDetail/areaMap)
+ */
+class HomeMap {
+  constructor() {
+    this.vectorStyle = new KMap.VectorStyle();
+    this.kmap = null;
+    this.blueRegionLayer = null;
+  }
+
+  initMap(location, target) {
+    this.blueRegionLayer = new KMap.VectorLayer("blueRegionLayer", 99999, {
+      minZoom: 1,
+      maxZoom: 22,
+    });
+    const level = 16;
+    const coordinate = util.wktCastGeom(location).getFirstCoordinate();
+    this.kmap = new KMap.Map(
+      target,
+      level,
+      coordinate[0],
+      coordinate[1],
+      null,
+      1,
+      22,
+      "vec",
+      true,
+      true
+    );
+    this.kmap.addLayer(this.blueRegionLayer.layer);
+    const xyz = config.base_img_url + "map/lby/{z}/{x}/{y}.png";
+    this.kmap.addXYZLayer(xyz, { minZoom: 8, maxZoom: 22 }, 2);
+  }
+
+  updateMap() {
+    setTimeout(() => {
+      this.kmap?.map?.updateSize();
+    }, 200);
+  }
+
+  destroy() {
+    this.kmap?.destroy?.();
+    this.kmap = null;
+    this.blueRegionLayer = null;
+  }
+}
+
+export default HomeMap;

+ 27 - 24
src/views/home/index.vue

@@ -1,37 +1,40 @@
 <template>
-  <div class="scaffold-home">
+  <div class="home-page">
     <h1>feiniao</h1>
-    <p>脚手架已就绪,在 <code>src/router/routes.js</code> 添加页面路由即可。</p>
+    <div class="map-wrap">
+      <div ref="mapRef" class="map-el"></div>
+    </div>
   </div>
 </template>
 
 <script setup>
-// 业务页面在 views 下新建,并通过 createPage 注册到 routes.js
+import { onBeforeUnmount, onMounted, ref } from "vue";
+import HomeMap from "./homeMap";
+
+const mapRef = ref(null);
+const homeMap = new HomeMap();
+
+onMounted(() => {
+  homeMap.initMap("POINT(113.61448114737868 23.585550924763083)", mapRef.value);
+  homeMap.updateMap();
+});
+
+onBeforeUnmount(() => {
+  homeMap.destroy();
+});
 </script>
 
 <style lang="scss" scoped>
-.scaffold-home {
+.home-page {
   box-sizing: border-box;
-  min-height: 100%;
-  padding: 48px 24px;
-  background: linear-gradient(180deg, #eef5ff 0%, $main-bg-color 40%);
-
-  h1 {
-    margin: 0 0 12px;
-    font-size: 28px;
-    color: #1f2d3d;
-  }
-
-  p {
-    margin: 0;
-    color: #606266;
-  }
+  width: 100%;
+  height: 100%;
+  padding: 0;
+}
 
-  code {
-    padding: 2px 6px;
-    border-radius: 4px;
-    background: #fff;
-    color: $base-color;
-  }
+.map-wrap,
+.map-el {
+  width: 500px;
+  height: 500px;
 }
 </style>