保安服务企业管理项目备份
guanqb
2023-12-22 4c5ccd55411d61c379bc17abfde2699f50da20d3
保安员位置接口对接、生成图标点击弹窗展示
2 files modified
1 files added
822 ■■■■■ changed files
src/api/livePersonLocation/livePersonLocation.js 11 ●●●●● patch | view | raw | blame | history
src/views/check/components/OlMapBox.vue 742 ●●●●● patch | view | raw | blame | history
src/views/check/livePersonLocation.vue 69 ●●●● patch | view | raw | blame | history
src/api/livePersonLocation/livePersonLocation.js
New file
@@ -0,0 +1,11 @@
import request from '@/router/axios'
export const getlivePersonLocationList = (params) => {
    return request({
        url: '/api/livePersonLocation/getList',
        method: 'get',
        params: {
            ...params
        }
    })
}
src/views/check/components/OlMapBox.vue
@@ -9,10 +9,19 @@
 * Copyright (c) 2023 by shuishen, All Rights Reserved.
-->
<template>
  <div class="w100 h100">
    <div id="OlMapBoxElement" class="w100 h100">
    <div class="w100 h100">
        <div id="OlMapBoxElement" class="w100 h100">
        </div>
        <div id="popup" class="ol-popup" v-show="isShowLivePersonInfoPopover">
            <a href="#" id="popup-closer" class="ol-popup-closer"></a>
            <div id="popup-content">
                <p>名称:{{ livePersonData.realName }}</p>
                <p>所属公司:{{ livePersonData.deptName }}</p>
                <p>派遣单位:{{ livePersonData.dispatchCompany }}</p>
                <p>更新时间:{{ livePersonData.recordTime }}</p>
            </div>
        </div>
    </div>
  </div>
</template>
<script>
@@ -20,31 +29,32 @@
import "ol/ol.css"
import OlView from "ol/View.js"
import {
  // eslint-disable-next-line no-unused-vars
  defaults as OlControlDefaults,
  defaults,
  // 全屏控件
  FullScreen,
  // 比例尺控件
  ScaleLine,
  // 缩放滚动条控件
  // eslint-disable-next-line no-unused-vars
  ZoomSlider,
  // 鼠标位置控件
  // eslint-disable-next-line no-unused-vars
  MousePosition,
  // -地图属性控件
  Attribution,
  // 鹰眼控件
  // eslint-disable-next-line no-unused-vars
  OverviewMap,
  // 缩放到范围控件
  // eslint-disable-next-line no-unused-vars
  ZoomToExtent,
  Rotate,
    // eslint-disable-next-line no-unused-vars
    defaults as OlControlDefaults,
    defaults,
    // 全屏控件
    FullScreen,
    // 比例尺控件
    ScaleLine,
    // 缩放滚动条控件
    // eslint-disable-next-line no-unused-vars
    ZoomSlider,
    // 鼠标位置控件
    // eslint-disable-next-line no-unused-vars
    MousePosition,
    // -地图属性控件
    Attribution,
    // 鹰眼控件
    // eslint-disable-next-line no-unused-vars
    OverviewMap,
    // 缩放到范围控件
    // eslint-disable-next-line no-unused-vars
    ZoomToExtent,
    Rotate,
} from "ol/control.js"
import OlSourceOSM from "ol/source/OSM.js"
import OlLayerTile from "ol/layer/Tile.js"
import Overlay from "ol/Overlay.js"
import { Image as ImageLayer } from 'ol/layer'
import ImageStatic from 'ol/source/ImageStatic.js'
@@ -80,332 +90,414 @@
let layersObjcect = {}
let baseMapLayer = {
  yx: [],
  sl: []
    yx: [],
    sl: []
}
let livePersonInfoPopover = null
let options,
  epsgcode,
  isMvt = false
    epsgcode,
    isMvt = false
let isMapResource = true
export default {
  name: 'OlMapBox',
    name: 'OlMapBox',
  props: {
    mapType: {
      type: String,
      default: 'sl'
    },
    props: {
        mapType: {
            type: String,
            default: 'sl'
        },
    mapContrast: {
      type: String,
      default: 'custom'
    }
  },
  data () {
    return {
      publicPath: process.env.BASE_URL,
    }
  },
  mounted () {
    const that = this
    this.$nextTick(() => {
      mapView = new OlMap({
        target: 'OlMapBoxElement',
        layers: [],
        view: new OlView({
          // 初始化中心点坐标,经纬度一会自己换一下
          center: [113, 24],
          zoom: 18,
          projection: "EPSG:4326"
        }),
      })
      mapView.on("singleclick", function (event) {
        mapView.forEachFeatureAtPixel(
          event.pixel,
          (feature) => {
            feature.dispatchEvent && feature.dispatchEvent({ type: 'click', event: event })
          }
        )
      })
      that.baseInitLayer(that.mapType)
      this.mapAddClusterLayer(
        'livePersonLocationLayer',
        '/public/img/map/location',
        [{lng: 113, lat: 24}, {lng: 115, lat:24 }],
        (e) => { },
      )
    })
  },
  methods: {
    /**
     * @description: 底图加载
     * @param {*} type
     * @return {*}
     */
    baseInitLayer (type) {
      if (baseMapLayer.yx.length) {
        baseMapLayer.yx.forEach(item => {
          mapView.removeLayer(item)
        })
        baseMapLayer.yx = []
      }
      if (baseMapLayer.sl.length) {
        baseMapLayer.sl.forEach(item => {
          mapView.removeLayer(item)
        })
        baseMapLayer.sl = []
      }
      if (type == 'yx') {
        let yxImgW = new OlLayerTile({
          zIndex: 1,
          title: '影像',
          source: new XYZ({
            url: 'https://t0.tianditu.gov.cn/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=e45274b0235bb913eceb393aabbf9c9c',
          })
        })
        mapView.addLayer(yxImgW)
        baseMapLayer.yx.push(yxImgW)
        let yxCvaW = new OlLayerTile({
          zIndex: 2,
          title: '影像标注',
          source: new XYZ({
            url: 'https://t0.tianditu.gov.cn/DataServer?T=cva_w&x={x}&y={y}&l={z}&tk=e45274b0235bb913eceb393aabbf9c9c',
          })
        })
        mapView.addLayer(yxCvaW)
        baseMapLayer.yx.push(yxCvaW)
      } else if (type == 'sl') {
        let slVecW = new OlLayerTile({
          zIndex: 2,
          title: '矢量',
          source: new XYZ({
            url: 'https://t0.tianditu.gov.cn/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=e45274b0235bb913eceb393aabbf9c9c',
          })
        })
        mapView.addLayer(slVecW)
        baseMapLayer.sl.push(slVecW)
        let slCvaW = new OlLayerTile({
          zIndex: 2,
          title: '矢量标注',
          source: new XYZ({
            url: 'https://t0.tianditu.gov.cn/DataServer?T=cva_w&x={x}&y={y}&l={z}&tk=e45274b0235bb913eceb393aabbf9c9c',
          })
        })
        mapView.addLayer(slCvaW)
        baseMapLayer.sl.push(slVecW)
      }
    },
    // javascript 转换
    lonLat2Mercator (lonlat) {
      var mercator = {
        x: 0,
        y: 0
      }
      var earthRad = 6378137.0
      mercator.x = lonlat.lng * Math.PI / 180 * earthRad
      var a = lonlat.lat * Math.PI / 180
      mercator.y = earthRad / 2 * Math.log((1.0 + Math.sin(a)) / (1.0 - Math.sin(a)))
      return mercator
    },
    getCurItemFeature (params) {
      const iconFeature = new Feature({
        geometry: new Point([Number(params.lng), Number(params.lat)]),
        attributes: params.item
      })
      let styleOptions = {
        cursor: 'pointer',
      }
      if (params.url) {
        styleOptions.image = new Icon({
          scale: params.scale || 1,
          imgSize: params.imgSize || [160, 221],
          src: params.url,
        })
      }
      if (params.text) {
        let textElement = document.createElement('span')
        textElement.innerHTML = params.text
        let color = '#0000FF'
        styleOptions.text = new Text({
          // 对齐方式
          textAlign: 'center',
          // 文本基线
          textBaseline: 'middle',
          text: textElement.innerHTML,
          font: '14px Arial',
          fill: new Fill({
            color: 'white'
          }),
          // 填充背景
          backgroundFill: new Fill({
            color: color
          }),
          padding: [2, 5, 2, 5],
          stroke: new Stroke({ color: '#00000000', width: 1 })
        })
      }
      iconFeature.setStyle(new Style(styleOptions))
      iconFeature.on('click', (e) => {
        if (params.event) {
          params.event(e.target.values_.attributes.attr)
        mapContrast: {
            type: String,
            default: 'custom'
        }
      })
      return iconFeature
    },
    /**
     * 删除图层
     * @param {*} layerName 图层名称
     */
    mapRemoveLayer (layerNames) {
      layerNames.forEach(item => {
        if (layersObjcect[item] && layersObjcect[item] != null) {
          mapView.removeLayer(layersObjcect[item])
          delete layersObjcect[item]
    data () {
        return {
            publicPath: process.env.BASE_URL,
            livePersonData: {},
            isShowLivePersonInfoPopover: false
        }
        if (layersObjcect[item + 'ONE'] && layersObjcect[item + 'ONE'] != null) {
          mapView.removeLayer(layersObjcect[item + 'ONE'])
          delete layersObjcect[item + 'ONE']
        }
        if (layersObjcect[item + 'TWO'] && layersObjcect[item + 'TWO'] != null) {
          mapView.removeLayer(layersObjcect[item + 'TWO'])
          delete layersObjcect[item + 'TWO']
        }
      })
    },
    // 添加聚合图层的新模式,水库点分布
    mapAddClusterLayer (layerName, imgUrl, positionData, incident = (e) => { }, type = '') {
      const that = this
      if (!layersObjcect[layerName]) {
        if (type == 'cluster') {
          let source = new VectorSource()
    mounted () {
        const that = this
          positionData.forEach(item => {
            source.addFeature(that.getCurItemFeature({
              item,
              lng: item.lng,
              lat: item.lat,
              url: imgUrl,
              event: incident
            }))
          })
          let layer = new Cluster({
            source: source,
            distance: 100
          })
          layersObjcect[layerName] = new VectorLayer({
            source: layer,
            zIndex: 24,
            style: (feature, resolution) => {
              let size = feature.get('features').length
              let radius = 10
              if (size > 900) {
                radius = 19
              } else if (size > 600) {
                radius = 16
              } else if (size > 400) {
                radius = 13
              }
              let style = new Style({
                image: new Circle({
                  radius: radius,
                  stroke: new Stroke({
                    color: 'blue',
                    width: 1
                  }),
                  fill: new Fill({
                    color: 'blue'
                  })
        this.$nextTick(() => {
            mapView = new OlMap({
                target: 'OlMapBoxElement',
                layers: [],
                view: new OlView({
                    // 初始化中心点坐标,经纬度一会自己换一下
                    center: [113, 24],
                    zoom: 18,
                    projection: "EPSG:4326"
                }),
                text: new Text({
                  text: size.toString(),
                  fill: new Fill({
                    color: 'white'
                  })
                })
              })
            })
              return style
            }
          })
            mapView.on("singleclick", function (event) {
                mapView.forEachFeatureAtPixel(
                    event.pixel,
                    (feature) => {
                        feature.dispatchEvent && feature.dispatchEvent({ type: 'click', event: event })
                    }
                )
            })
          mapView.addLayer(layersObjcect[layerName])
          return
        } else {
          layersObjcect[layerName] = new VectorLayer({
            // 图标图层
            zIndex: 24,
            minZoom: 12,
            source: new VectorSource(),
          })
        }
        mapView.addLayer(layersObjcect[layerName])
      }
      positionData.forEach(item => {
        layersObjcect[layerName].getSource().addFeature(that.getCurItemFeature({
          item,
          lng: item.lng,
          lat: item.lat,
          url: imgUrl,
          event: incident
        }))
      })
            that.baseInitLayer(that.mapType)
        })
    },
  },
    methods: {
        /**
         * @description: 底图加载
         * @param {*} type
         * @return {*}
         */
        baseInitLayer (type) {
            if (baseMapLayer.yx.length) {
                baseMapLayer.yx.forEach(item => {
                    mapView.removeLayer(item)
                })
                baseMapLayer.yx = []
            }
            if (baseMapLayer.sl.length) {
                baseMapLayer.sl.forEach(item => {
                    mapView.removeLayer(item)
                })
                baseMapLayer.sl = []
            }
            if (type == 'yx') {
                let yxImgW = new OlLayerTile({
                    zIndex: 1,
                    title: '影像',
                    source: new XYZ({
                        url: 'https://t0.tianditu.gov.cn/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=e45274b0235bb913eceb393aabbf9c9c',
                    })
                })
                mapView.addLayer(yxImgW)
                baseMapLayer.yx.push(yxImgW)
                let yxCvaW = new OlLayerTile({
                    zIndex: 2,
                    title: '影像标注',
                    source: new XYZ({
                        url: 'https://t0.tianditu.gov.cn/DataServer?T=cva_w&x={x}&y={y}&l={z}&tk=e45274b0235bb913eceb393aabbf9c9c',
                    })
                })
                mapView.addLayer(yxCvaW)
                baseMapLayer.yx.push(yxCvaW)
            } else if (type == 'sl') {
                let slVecW = new OlLayerTile({
                    zIndex: 2,
                    title: '矢量',
                    source: new XYZ({
                        url: 'https://t0.tianditu.gov.cn/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=e45274b0235bb913eceb393aabbf9c9c',
                    })
                })
                mapView.addLayer(slVecW)
                baseMapLayer.sl.push(slVecW)
                let slCvaW = new OlLayerTile({
                    zIndex: 2,
                    title: '矢量标注',
                    source: new XYZ({
                        url: 'https://t0.tianditu.gov.cn/DataServer?T=cva_w&x={x}&y={y}&l={z}&tk=e45274b0235bb913eceb393aabbf9c9c',
                    })
                })
                mapView.addLayer(slCvaW)
                baseMapLayer.sl.push(slVecW)
            }
        },
        // javascript 转换
        lonLat2Mercator (lonlat) {
            var mercator = {
                x: 0,
                y: 0
            }
            var earthRad = 6378137.0
            mercator.x = lonlat.lng * Math.PI / 180 * earthRad
            var a = lonlat.lat * Math.PI / 180
            mercator.y = earthRad / 2 * Math.log((1.0 + Math.sin(a)) / (1.0 - Math.sin(a)))
            return mercator
        },
        // 生成图标图层
        getCurItemFeature (params) {
            const iconFeature = new Feature({
                geometry: new Point([Number(params.lng), Number(params.lat)]),
                attributes: params.item
            })
            let styleOptions = {
                cursor: 'pointer',
            }
            if (params.url) {
                styleOptions.image = new Icon({
                    scale: params.scale || 1,
                    imgSize: params.imgSize || [160, 221],
                    src: params.url,
                })
            }
            if (params.text) {
                let textElement = document.createElement('span')
                textElement.innerHTML = params.text
                let color = '#0000FF'
                styleOptions.text = new Text({
                    // 对齐方式
                    textAlign: 'center',
                    // 文本基线
                    textBaseline: 'middle',
                    text: textElement.innerHTML,
                    font: '14px Arial',
                    fill: new Fill({
                        color: 'white'
                    }),
                    // 填充背景
                    backgroundFill: new Fill({
                        color: color
                    }),
                    padding: [2, 5, 2, 5],
                    stroke: new Stroke({ color: '#00000000', width: 1 })
                })
            }
            iconFeature.setStyle(new Style(styleOptions))
            iconFeature.on('click', (e) => {
                if (params.event) {//icon事件参数
                    params.event(e.target.values_.attributes)//点击事件触发事件并传参
                }
            })
            return iconFeature
        },
        /**
         * 删除图层
         * @param {*} layerName 图层名称
         */
        mapRemoveLayer (layerNames) {
            layerNames.forEach(item => {
                if (layersObjcect[item] && layersObjcect[item] != null) {
                    mapView.removeLayer(layersObjcect[item])
                    delete layersObjcect[item]
                }
                if (layersObjcect[item + 'ONE'] && layersObjcect[item + 'ONE'] != null) {
                    mapView.removeLayer(layersObjcect[item + 'ONE'])
                    delete layersObjcect[item + 'ONE']
                }
                if (layersObjcect[item + 'TWO'] && layersObjcect[item + 'TWO'] != null) {
                    mapView.removeLayer(layersObjcect[item + 'TWO'])
                    delete layersObjcect[item + 'TWO']
                }
            })
        },
        // 添加聚合图层的新模式,水库点分布
        mapAddClusterLayer (layerName, imgUrl, positionData, incident = (e) => { }, type = '') {
            const that = this
            if (!layersObjcect[layerName]) {
                if (type == 'cluster') {
                    let source = new VectorSource()
                    positionData.forEach(item => {
                        source.addFeature(that.getCurItemFeature({
                            item,
                            lng: item.lng,
                            lat: item.lat,
                            url: imgUrl,
                            event: incident
                        }))
                    })
                    let layer = new Cluster({
                        source: source,
                        distance: 100
                    })
                    layersObjcect[layerName] = new VectorLayer({
                        source: layer,
                        zIndex: 24,
                        style: (feature, resolution) => {
                            let size = feature.get('features').length
                            let radius = 10
                            if (size > 900) {
                                radius = 19
                            } else if (size > 600) {
                                radius = 16
                            } else if (size > 400) {
                                radius = 13
                            }
                            let style = new Style({
                                image: new Circle({
                                    radius: radius,
                                    stroke: new Stroke({
                                        color: 'blue',
                                        width: 1
                                    }),
                                    fill: new Fill({
                                        color: 'blue'
                                    })
                                }),
                                text: new Text({
                                    text: size.toString(),
                                    fill: new Fill({
                                        color: 'white'
                                    })
                                })
                            })
                            return style
                        }
                    })
                    mapView.addLayer(layersObjcect[layerName])
                    return
                } else {
                    layersObjcect[layerName] = new VectorLayer({
                        // 图标图层
                        zIndex: 24,
                        minZoom: 12,
                        source: new VectorSource(),
                    })
                }
                mapView.addLayer(layersObjcect[layerName])
            }
            positionData.forEach(item => {
                layersObjcect[layerName].getSource().addFeature(that.getCurItemFeature({
                    item,
                    lng: item.lng,
                    lat: item.lat,
                    url: imgUrl,
                    event: incident
                }))
                mapView.getView().animate({ // 只设置需要的属性即可
                    center: [item.lng, item.lat], // 中心点
                    zoom: 18, // 缩放级别
                    rotation: undefined, // 缩放完成view视图旋转弧度
                    duration: 1000 // 缩放持续时间,默认不需要设置
                })
            })
        },
        // 生成地图弹窗
        generateMapPopup (e) {
            const that = this
            this.livePersonData = e
            this.isShowLivePersonInfoPopover = true
            var container = document.getElementById('popup')
            var closer = document.getElementById('popup-closer')
            // 创建popup
            livePersonInfoPopover = new Overlay({
                element: container,
                autoPan: true,
                positioning: 'bottom-center',
                stopEvent: false,
                autoPanAnimation: {
                    duration: 250
                }
            })
            mapView.addOverlay(livePersonInfoPopover)
            closer.onclick = function () {
                livePersonInfoPopover.setPosition(undefined)
                closer.blur()
                return false
            }
            livePersonInfoPopover.setPosition([e.lng, e.lat])
        },
    }
}
</script>
<style lang='scss' scope>
.w100 {
  width: 100%;
    width: 100%;
}
.h100 {
  height: 100%;
    height: 100%;
}
.ol-popup {
    position: absolute;
    background-color: white;
    -webkit-filter: drop-shadow(0 1px 4px rgba(0, 0, 0, 0.2));
    filter: drop-shadow(0 1px 4px #FFC125);
    padding: 15px;
    border-radius: 10px;
    border: 1px solid #cccccc;
    bottom: 130px;
    left: -114px;
    min-width: 300px;
}
.ol-popup:after,
.ol-popup:before {
    top: 100%;
    border: solid transparent;
    content: " ";
    height: 0;
    width: 0;
    position: absolute;
    pointer-events: none;
}
.ol-popup:after {
    border-top-color: white;
    border-width: 10px;
    left: 48px;
    margin-left: -10px;
}
.ol-popup:before {
    border-top-color: #cccccc;
    border-width: 11px;
    left: 48px;
    margin-left: -11px;
}
.ol-popup-closer {
    text-decoration: none;
    position: absolute;
    top: 2px;
    right: 8px;
    color: red;
}
.ol-popup-closer:after {
    content: "✖";
}
</style>
src/views/check/livePersonLocation.vue
@@ -1,19 +1,68 @@
<template>
  <div style="width: 100%; height: 100%;">
    <OlMapBox></OlMapBox>
  </div>
    <div style="width: 100%; height: 100%;">
        <OlMapBox ref="olmapboxelement"></OlMapBox>
    </div>
</template>
<script>
import  OlMapBox  from "./components/OlMapBox.vue";
import OlMapBox from "./components/OlMapBox.vue"
import { getlivePersonLocationList } from "@/api/livePersonLocation/livePersonLocation"
// 获取实时保安位置定时器
let liveBATimer
export default {
  name: "livePersonLocation",
  components: {OlMapBox}
    name: "livePersonLocation",
    components: { OlMapBox },
    data () {
        return {
            // publicPath: process.env.BASE_URL,
            livePersonLocationList: []
        }
    },
    created () {
        // 首先执行一次,必须等地图渲染完成再去创建图标图层!
        this.$nextTick(() => {
            this.getlivePersonLocationList()
        })
        liveBATimer = setInterval(() => {
            this.getlivePersonLocationList()
        }, 60000)//一分钟执行一次
    },
    methods: {
        // 获取保安列表实时位置数据
        getlivePersonLocationList () {
            if (this.$refs.olmapboxelement) {//不清除就不新增
                this.$refs.olmapboxelement.mapRemoveLayer(['livePersonLocationLayer'])
            }
            getlivePersonLocationList().then(res => {
                this.livePersonLocationList = res.data.data
                res.data.data.forEach(item => {
                    item.lng = item.longitude
                    item.lat = item.latitude
                })
                this.$refs.olmapboxelement.mapAddClusterLayer('livePersonLocationLayer', 'img/map/security.png', res.data.data, this.livePersonIconClick)
            })
        },
        // 保安实时图标点击事件
        livePersonIconClick (e) {
            this.$refs.olmapboxelement.generateMapPopup(e)
        }
    },
    destroyed () {
        // 清除保安图标图层
        if (this.$refs.olmapboxelement) {//不清除就不新增
            this.$refs.olmapboxelement.mapRemoveLayer(['livePersonLocationLayer'])
        }
        liveBATimer && clearInterval(liveBATimer)
    }
}
</script>
<style scoped>
</style>
<style scoped></style>