保安服务企业管理项目备份
guanqb
2024-01-16 8e3865214943c20e1269a06d6e1cbbc4c8c7890f
派遣服务地图标点
3 files modified
2 files added
502 ■■■■■ changed files
public/img/map/unit.png patch | view | raw | blame | history
src/api/dispatch/dispatch.js 11 ●●●●● patch | view | raw | blame | history
src/views/dispatch/components/OlMapBox.vue 428 ●●●●● patch | view | raw | blame | history
src/views/dispatch/components/dispatchMap.vue 23 ●●●●● patch | view | raw | blame | history
src/views/dispatch/dispatchView.vue 40 ●●●●● patch | view | raw | blame | history
public/img/map/unit.png
src/api/dispatch/dispatch.js
@@ -1,4 +1,4 @@
import request from '@/router/axios';
import request from '@/router/axios'
export const getdata = (current, size, params) => {
    return request({
@@ -12,6 +12,15 @@
    })
}
export const getAlldata = (params) => {//不分页
    return request({
        url: '/api/dispatcherUnit/getAll',
        method: 'get',
        params: {
            ...params
        }
    })
}
export const getDispatcherUnitByDeptId = (deptId) => {
    return request({
src/views/dispatch/components/OlMapBox.vue
New file
@@ -0,0 +1,428 @@
<!--
 * @Author: shuishen 1109946754@qq.com
 * @Date: 2023-10-11 16:01:58
 * @LastEditors: shuishen 1109946754@qq.com
 * @LastEditTime: 2023-11-29 14:17:09
 * @FilePath: \web\business\src\components\OlMapBox\index.vue
 * @Description:
 *
 * Copyright (c) 2023 by shuishen, All Rights Reserved.
-->
<template>
    <div class="w100 h100">
        <div id="OlMapBoxElement" class="w100 h100">
        </div>
        <div id="popup" class="ol-popup" v-show="isShowUnitInfoPopover">
            <a href="#" id="popup-closer" class="ol-popup-closer"></a>
            <div id="popup-content">
                <p>负责人:{{ unitData.securityLinkman }}</p>
                <p>联系方式:{{ unitData.cell }}</p>
                <p>派遣保安员数:{{ unitData.num }}</p>
                <p>派遣单位行业:{{ unitData.professionName }}</p>
            </div>
        </div>
    </div>
</template>
<script>
import "ol/ol.css"
import OlView from "ol/View.js"
import OlLayerTile from "ol/layer/Tile.js"
import Overlay from "ol/Overlay.js"
import OlMap from "ol/Map.js"
import XYZ from "ol/source/XYZ"
import Cluster from "ol/source/Cluster"
import VectorLayer from "ol/layer/Vector"
import VectorSource from "ol/source/Vector"
import Feature from 'ol/Feature.js'
import Point from 'ol/geom/Point.js'
import { Icon, Style, Fill, Text, Stroke, Circle } from 'ol/style.js'
import { getAlldata } from "@/api/dispatch/dispatch"
import { mapGetters } from "vuex"
import { mapState } from "vuex"
let mapView = null
let layersObjcect = {}
let baseMapLayer = {
    yx: [],
    sl: []
}
let unitInfoPopover = null
export default {
    name: 'OlMapBox',
    props: {
        mapType: {
            type: String,
            default: 'sl'
        },
        mapContrast: {
            type: String,
            default: 'custom'
        }
    },
    data () {
        return {
            publicPath: process.env.BASE_URL,
            unitData: {},
            isShowUnitInfoPopover: false
        }
    },
    computed: {
        ...mapGetters(["permission", "userInfo"]),
        ...mapState({
            userInfo: state => state.user.userInfo
        })
    },
    mounted () {
        const that = this
        this.$nextTick(() => {
            mapView = new OlMap({
                target: 'OlMapBoxElement',
                layers: [],
                view: new OlView({
                    // 初始化中心点坐标,经纬度一会自己换一下
                    center: [112.85857823133, 35.496284586473],
                    zoom: 18,
                    projection: "EPSG:4326"
                }),
            })
            mapView.on("singleclick", function (event) {
                mapView.forEachFeatureAtPixel(
                    event.pixel,
                    (feature) => {
                        feature.dispatchEvent && feature.dispatchEvent({ type: 'click', event: event })
                    }
                )
            })
            mapView.getView().on('change:resolution', function () {
                // console.log('mapView地图缩放事件')
            })
            that.getAlldata()
            that.baseInitLayer(that.mapType)
        })
    },
    methods: {
        // 图标点击事件
        pqfwIconClick (e) {
            this.generateMapPopup(e)
        },
        // 获取派遣服务列表
        getAlldata () {
            let params = {}
            let roleName = this.userInfo.role_name
            if (
                roleName == "保安公司管理员" ||
                roleName == "保安" ||
                roleName == "分公司管理员"
            ) {
                params.deptId = this.userInfo.dept_id
            } else if (roleName == "公安管理员" || roleName == "administrator" || roleName == "民警") {
                params.jurisdiction = this.userInfo.jurisdiction
            }
            getAlldata(params).then(res => {
                this.$nextTick(() => {
                    this.mapRemoveLayer(['pqfwLayer'])
                    this.mapAddClusterLayer('pqfwLayer', 'img/map/unit.png', res.data.data, this.pqfwIconClick)
                })
            })
        },
        /**
         * @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 || [32, 32],
                    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 }),
                    // offsetX: -60,
                    offsetY: 30
                })
            }
            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]) {
                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.longitude,
                    lat: item.latitude,
                    text: item.name,
                    url: imgUrl,
                    event: incident
                }))
            })
            mapView.getView().animate({ // 只设置需要的属性即可
                center: [positionData[0].longitude, positionData[0].latitude], // 中心点
                // zoom: 18, // 缩放级别
                rotation: undefined, // 缩放完成view视图旋转弧度
                // duration: 1000 // 缩放持续时间,默认不需要设置
            })
        },
        // 生成地图弹窗
        generateMapPopup (e) {
            const that = this
            this.unitData = e
            this.isShowUnitInfoPopover = true
            var container = document.getElementById('popup')
            var closer = document.getElementById('popup-closer')
            // 创建popup
            unitInfoPopover = new Overlay({
                element: container,
                autoPan: true,
                positioning: 'bottom-center',
                stopEvent: false,
                autoPanAnimation: {
                    duration: 250
                }
            })
            mapView.addOverlay(unitInfoPopover)
            closer.onclick = function () {
                unitInfoPopover.setPosition(undefined)
                closer.blur()
                return false
            }
            unitInfoPopover.setPosition([e.longitude, e.latitude])
        },
    }
}
</script>
<style lang='scss' scope>
.w100 {
    width: 100%;
}
.h100 {
    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: 34px;
    left: -46px;
    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/dispatch/components/dispatchMap.vue
@@ -1,15 +1,24 @@
<template>
  <div>
    222
  </div>
    <div style="width: 100%; height: 100%;">
        <OlMapBox ref="olmapboxelement"></OlMapBox>
    </div>
</template>
<script>
import OlMapBox from "./OlMapBox.vue"
export default {
  name: "dispatchMap"
    name: "dispatchMap",
    components: { OlMapBox },
    mounted () {
        this.$nextTick(() => {
            this.$refs.olmapboxelement.getAlldata()
        })
    }
}
</script>
<style scoped>
</style>
<style scoped></style>
src/views/dispatch/dispatchView.vue
@@ -1,29 +1,35 @@
<template>
  <el-tabs v-model="activeName" >
    <el-tab-pane label="列表展示" name="list">
      <dispatch-list></dispatch-list>
    </el-tab-pane>
    <el-tab-pane label="地图展示" name="map">
      <dispatch-map></dispatch-map>
    </el-tab-pane>
  </el-tabs>
    <el-tabs v-model="activeName" style="height: 100%;">
        <el-tab-pane label="列表展示" name="list">
            <dispatch-list></dispatch-list>
        </el-tab-pane>
        <el-tab-pane label="地图展示" name="map">
            <dispatch-map :key="new Date().getTime()"></dispatch-map>
        </el-tab-pane>
    </el-tabs>
</template>
<script>
import DispatchMap from "@/views/dispatch/components/dispatchMap.vue";
import DispatchList from "@/views/dispatch/dispatch.vue";
import DispatchMap from "@/views/dispatch/components/dispatchMap.vue"
import DispatchList from "@/views/dispatch/dispatch.vue"
export default {
  name: "dispatchView",
  components: {DispatchList, DispatchMap},
  data(){
    return{
      activeName: 'list'
    name: "dispatchView",
    components: { DispatchList, DispatchMap },
    data () {
        return {
            activeName: 'list'
        }
    }
  }
}
</script>
<style scoped>
<style lang="scss"scoped>
::v-deep .el-tabs__content {
    height: calc(100% - 55px);
}
::v-deep .el-tab-pane {
    height: 100%;
}
</style>