shuishen
2024-05-09 5c31f779360404e38021ad1c8df1a9b03a07d7bf
可用资源图,风险隐患图及其他内容调整
5 files modified
17 files added
1307 ■■■■■ changed files
public/img/multip/cz.png patch | view | raw | blame | history
public/img/multip/jd.png patch | view | raw | blame | history
public/img/multip/jr.png patch | view | raw | blame | history
public/img/multip/jw.png patch | view | raw | blame | history
public/img/multip/jy.png patch | view | raw | blame | history
public/img/multip/lao.png patch | view | raw | blame | history
public/img/multip/location.png patch | view | raw | blame | history
public/img/multip/nm.png patch | view | raw | blame | history
public/img/multip/wb.png patch | view | raw | blame | history
public/img/multip/yl.png patch | view | raw | blame | history
public/img/multip/yle.png patch | view | raw | blame | history
public/img/multip/zf.png patch | view | raw | blame | history
public/img/multip/未标题-11.png patch | view | raw | blame | history
src/api/multiMap/index.js 31 ●●●●● patch | view | raw | blame | history
src/components/map/components/multipMapPopup.vue 358 ●●●●● patch | view | raw | blame | history
src/components/map/index.vue 70 ●●●● patch | view | raw | blame | history
src/router/page/index.js 17 ●●●●● patch | view | raw | blame | history
src/store/getters.js 6 ●●●● patch | view | raw | blame | history
src/store/modules/popupParams.js 13 ●●●●● patch | view | raw | blame | history
src/views/available/index.vue 385 ●●●●● patch | view | raw | blame | history
src/views/layout/index.vue 40 ●●●● patch | view | raw | blame | history
src/views/risk/index.vue 387 ●●●●● patch | view | raw | blame | history
public/img/multip/cz.png
public/img/multip/jd.png
public/img/multip/jr.png
public/img/multip/jw.png
public/img/multip/jy.png
public/img/multip/lao.png
public/img/multip/location.png
public/img/multip/nm.png
public/img/multip/wb.png
public/img/multip/yl.png
public/img/multip/yle.png
public/img/multip/zf.png
public/img/multip/未标题-11.png
src/api/multiMap/index.js
New file
@@ -0,0 +1,31 @@
import request from "@/router/axios.js";
/**
 * 获取可用资源图
 * @param {*}
 * @returns
 */
export const getAvailableList = (params) => {
  return request({
    url: "/api/blade-resPicDetail/resPicDetail/lazy-tree",
    method: "get",
    params: {
      ...params,
    },
  });
};
/**
 * 获取风险隐患图
 * @param {*}
 * @returns
 */
export const getRiskList = (params) => {
  return request({
    url: "/api/blade-riskHidDangPicDetail/riskHidDangPicDetail/lazy-tree",
    method: "get",
    params: {
      ...params,
    },
  });
};
src/components/map/components/multipMapPopup.vue
New file
@@ -0,0 +1,358 @@
<!--
 * @Author: shuishen 1109946754@qq.com
 * @Date: 2023-03-02 09:39:32
 * @LastEditors: shuishen 1109946754@qq.com
 * @LastEditTime: 2024-05-09 11:13:06
 * @FilePath: \srs-police-affairs\src\components\map\components\multipMapPopup.vue
 * @Description:
 *
 * Copyright (c) 2023 by ${git_name_email}, All Rights Reserved.
-->
<template>
    <div>
        <div v-if="multipMapPopup" class="divForms-dom" id="multipMapPopupDomBox">
            <div style="width: 100%; height: 100%; transform: translate(0, 50%);">
                <div class="divForms divForms-theme" :class="{ 'scale-dom': scaleDomFlag }">
                    <div class="divForms-wrap">
                        <div class="area" :class="{ 'area-plus': isShowAll }">
                            <div class="arrow-lt"></div>
                            <div class="b-t"></div>
                            <div class="b-r"></div>
                            <div class="b-b"></div>
                            <div class="b-l"></div>
                            <div class="arrow-rb"></div>
                            <div class="label-wrap">
                                <div class="title">
                                    {{ multipMapPopupData.title }}
                                    <div class="close" @click="closePopup" title="关闭"></div>
                                </div>
                                <div class="lable-box">
                                    <div class="label-content">
                                        <div class="item" v-for="(val, key, index) in multipMapPopupData.detail"
                                            :key="index">
                                            <div>{{ key }}:</div>
                                            <div>{{ val || '' }} </div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                        <div class="b-t-l"></div>
                        <div class="b-b-r"></div>
                    </div>
                    <div class="arrow"></div>
                </div>
            </div>
        </div>
    </div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
    name: 'multipMapPopup',
    inject: ["getWidth", 'userInfo'],
    data () {
        return {
            scaleDomFlag: false
        }
    },
    computed: {
        ...mapGetters([
            'multipMapPopup',
            'multipMapPopupData'
        ])
    },
    mounted () {
        if (this.getWidth() > 7000) {
            this.scaleDomFlag = true
        } else {
            this.scaleDomFlag = false
        }
    },
    methods: {
        closePopup () {
            this.$store.commit('SET_MULTIPMAPPOPUP', false)
        },
    }
}
</script>
<style lang="scss" scoped>
.divForms-dom {
    position: fixed;
    left: 0;
    z-index: 1 !important;
    .scale-dom {
        transform: scale(3);
        transform-origin: left;
    }
}
.divForms .arrow {
    position: absolute;
    bottom: 50%;
    left: 0;
    width: 45px;
    height: 2px;
    transform: rotate(-45deg) translate(5px, -15px);
    background-color: #28bbf0;
}
.divForms-theme .area {
    background-image: linear-gradient(135deg,
            transparent 30px,
            #1831a79a 30px,
            #3f487ba6 50%,
            transparent 50%),
        linear-gradient(-45deg,
            transparent 30px,
            #1831a79a 30px,
            #3f487ba6 50.1%,
            transparent 50%);
}
.divForms .area {
    position: relative;
    min-width: 360px;
}
.divForms-wrap {
    position: relative;
    overflow: hidden;
    padding: 30px;
}
.divForms-theme .b-b,
.divForms-theme .b-b-r,
.divForms-theme .b-l,
.divForms-theme .b-r,
.divForms-theme .b-t,
.divForms-theme .b-t-l {
    background-color: #1831a7;
    box-shadow: 0 0 10px 2px #1831a7;
}
.divForms .b-t {
    position: absolute;
    top: 0;
    left: 44px;
    right: 0;
    height: 1px;
    z-index: 10;
}
.divForms .b-r {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 44px;
    width: 1px;
    z-index: 10;
}
.divForms .b-l {
    position: absolute;
    top: 44px;
    left: 0;
    bottom: 0;
    width: 1px;
    z-index: 10;
}
.divForms .b-b {
    position: absolute;
    left: 0;
    right: 44px;
    bottom: 0;
    height: 1px;
    z-index: 10;
}
.divForms .b-b-r {
    position: absolute;
    bottom: 0;
    right: 0;
    width: 1px;
    height: 62px;
    transform: rotate(45deg) translate(-52px, 22px);
    z-index: 10;
}
.divForms .b-t-l {
    position: absolute;
    top: 0;
    left: 0;
    width: 1px;
    height: 62px;
    transform: rotate(45deg) translate(52px, -22px);
    z-index: 10;
}
.divForms .label-wrap {
    padding: 0 12px;
    color: #fff;
    font-size: 16px;
    white-space: nowrap;
    overflow: hidden;
    .change-btn {
        margin-top: 6px;
        display: flex;
        align-items: center;
        justify-content: space-around;
        &>div {
            width: 36%;
            height: 36px;
            line-height: 36px;
            cursor: pointer;
        }
        &>div.on {
            color: #3d95f3;
            border-bottom: 2px solid #3d95f3;
        }
    }
    .label-content {
        padding: 10px 0;
        .item {
            display: flex;
            flex-direction: row;
            width: calc(100% - 20px);
            //height: 30px;
            // flex-wrap: wrap;
            white-space: wrap;
            line-height: 30px;
            margin: 0 10px;
            text-align: left;
            &>div:first-child {
                min-width: 100px;
                text-align: justify;
                display: inline-block;
                text-align-last: justify;
                // margin-right: 20px;
            }
            &>div:nth-of-type(2) {
                flex: 1;
            }
            &>div:last-child {
                max-width: 180px;
            }
        }
        .item-pic {
            height: 120px;
            img {
                border-bottom-right-radius: 22px;
            }
        }
        // .item:nth-of-type(3) {
        //     height: 90px;
        //     &>div:nth-of-type(2) {
        //         word-wrap: break-word;
        //         white-space: break-spaces;
        //         width: 190px;
        //     }
        // }
    }
    .label-content.no-data {
        display: flex;
        align-items: center;
        justify-content: center;
        min-height: 240px;
    }
}
.divForms .title {
    position: relative;
    margin-top: 20px;
    padding: 0 12px 0 30px;
    height: 36px;
    line-height: 36px;
    .details-link {
        color: #6185e9;
        cursor: pointer;
        position: absolute;
        right: 34px;
        top: 0;
    }
    .details-link:hover {
        // color: #fff;
        border-bottom: 1px solid #3760cf;
    }
    .details-btn {
        position: absolute;
        top: 0;
        right: 28px;
        height: 100%;
        cursor: pointer;
        span {
            font-size: 14px;
        }
    }
    .details-btn:hover {
        color: #3d95f3;
    }
    .close {
        position: absolute;
        right: 5px;
        top: 0;
    }
    .close::before {
        font-family: element-icons;
        content: '\e6db';
        cursor: pointer;
        font-size: 16px;
    }
    .close:hover::before {
        color: #3d95f3;
    }
}
.divForms-theme .title {
    background-image: linear-gradient(135deg, transparent 25px, #1c309e9a 25px);
}
.divForms-theme .arrow,
.divForms-theme .title::before {
    background-color: #1c309e;
}
.divForms .title::before {
    content: '';
    position: absolute;
    bottom: -4px;
    left: 0;
    right: 0;
    z-index: 10;
    height: 2px;
}
</style>
src/components/map/index.vue
@@ -2,7 +2,7 @@
 * @Author: shuishen 1109946754@qq.com
 * @Date: 2022-08-18 17:00:30
 * @LastEditors: shuishen 1109946754@qq.com
 * @LastEditTime: 2024-04-10 16:59:00
 * @LastEditTime: 2024-05-09 16:57:19
 * @FilePath: \srs-police-affairs\src\components\map\index.vue
 * @Description: 公用地图组件
 * 
@@ -40,7 +40,8 @@
                <i class="fa fa-expand" style="vertical-align: middle; cursor: pointer" @click="fullscreenchange"></i>
            </el-tooltip>
            <el-tooltip v-if="isShowHomeContentBtn" content="显示/隐藏" placement="top" effect="dark">
                <i class="fa fa-expand" style="vertical-align: middle; cursor: pointer" @click="switchShowHomeContent"></i>
                <i class="fa fa-expand" style="vertical-align: middle; cursor: pointer"
                    @click="switchShowHomeContent"></i>
            </el-tooltip>
        </div>
        <!-- 切换矢量图按钮 -->
@@ -92,6 +93,8 @@
        <architecture-popup></architecture-popup>
        <activity-police-popup></activity-police-popup>
        <multip-map-popup></multip-map-popup>
        <video-list-popup></video-list-popup>
@@ -178,6 +181,7 @@
import mapPopup from './components/mapPopup.vue'
import architecturePopup from './components/architecturePopup.vue'
import activityPolicePopup from './components/activityPolicePopup.vue'
import multipMapPopup from './components/multipMapPopup.vue'
import videoListPopup from './components/videoListPopup.vue'
import videoRowClickPopup from './components/videoRowClickPopup.vue'
import mapSearchPopup from './components/mapSearchPopup.vue'
@@ -195,7 +199,7 @@
export default {
    name: 'mapBox',
    components: { mapPopup, architecturePopup, activityPolicePopup, videoListPopup, mapSearchPopup, houseDeepDataPopup, hotelPopup, businessDataPopup, dialogDetailPopup, yToolTip, videoRowClickPopup },
    components: { mapPopup, architecturePopup, activityPolicePopup, multipMapPopup, videoListPopup, mapSearchPopup, houseDeepDataPopup, hotelPopup, businessDataPopup, dialogDetailPopup, yToolTip, videoRowClickPopup },
    data () {
        return {
@@ -470,6 +474,10 @@
        this.$EventBus.$on('mapRemoveLayer', (params) => {
            that.mapRemoveLayer(params.layerName, params.type)
        })
        this.$EventBus.$on('mapRemoveSiteLayer', (params) => {
            that.mapRemoveSiteLayer(params.layerName, params.siteId)
        })
        this.$EventBus.$on('mapRemovePolygonLayer', (params) => {
@@ -1054,31 +1062,74 @@
                layersObjcect[layerName].addOverlay(labelElement)
                labelElement.on(global.DC.MouseEventType.CLICK, incident)
            } else if (type == "billboardOrLabel") {
                let allSite = layersObjcect[layerName].getOverlays()
                let flag = allSite.filter(item => item.attrParams.id == params.id)
                if (flag == true) {
                    return
                }
                // pointElement = new global.DC.Billboard(new global.DC.Position(Number(params.lng), Number(params.lat), Number(alt)), params.url)
                pointElement = new global.DC.Billboard(new global.DC.Position(Number(params.lng), Number(params.lat), 0), params.url)
                pointElement.size = [32, 32]
                pointElement.setStyle(params.setStyle)
                pointElement.size = params.size || [32, 32]
                pointElement.style = "clustering"
                pointElement.attrParams = params
                layersObjcect[layerName].addOverlay(pointElement)
                pointElement.on(global.DC.MouseEventType.CLICK, incident)
                pointElement.on(global.DC.MouseEventType.MOUSE_OVER, mouseOverIncident)
                pointElement.on(global.DC.MouseEventType.MOUSE_OUT, mouseOutIncident)
                pointElement.on(global.DC.MouseEventType.MOUSE_MOVE, pointMouseMove)
                // labelElement = new global.DC.Label(new global.DC.Position(Number(params.lng), Number(params.lat), Number(alt)), params.text)
                labelElement = new global.DC.Label(new global.DC.Position(Number(params.lng), Number(params.lat), 0), params.text)
                labelElement = new global.DC.Label(new global.DC.Position(Number(params.lng), Number(params.lat), 0), String(params.text))
                let fontColor = 'fontColor' in params ? params.fontColor : global.DC.Color.CRIMSON
                let style = 'fontColor' in params ? global.DC.Namespace.Cesium.LabelStyle.FILL : global.DC.Namespace.Cesium.LabelStyle.FILL_AND_OUTLINE
                labelElement.setStyle({
                    fillColor: global.DC.Color.CRIMSON,
                    style: global.DC.Namespace.Cesium.LabelStyle.FILL_AND_OUTLINE,
                    style: style,
                    fillColor: fontColor,
                    outlineColor: global.DC.Color.WHITE, // 边框颜色
                    outlineWidth: 4, // 边框大小,
                    outlineWidth: params.outW || 4, // 边框大小,
                    font: '16px sans-serif',
                    pixelOffset: { x: 0, y: -30 }
                    pixelOffset: { x: 0, y: -30 },
                    distanceDisplayCondition: distanceDisplayCondition,
                    ...params.setStyle
                })
                labelElement.attrParams = params
                layersObjcect[layerName].addOverlay(labelElement)
                labelElement.on(global.DC.MouseEventType.CLICK, incident)
            } else if (type == 'HeatPoint') {
                layersObjcect[layerName].setPositions(params.positions)
            } else if (type == "newBillboard") {
                // pointElement = new global.DC.Billboard(new global.DC.Position(Number(params.lng), Number(params.lat), Number(alt)), params.url)
                pointElement = new global.DC.Billboard(new global.DC.Position(Number(params.lng), Number(params.lat), 0), params.url)
                pointElement.setStyle(params.setStyle)
                pointElement.size = params.size || [32, 32]
                pointElement.style = "clustering"
                pointElement.attrParams = params
                layersObjcect[layerName].addOverlay(pointElement)
                pointElement.on(global.DC.MouseEventType.CLICK, incident)
                pointElement.on(global.DC.MouseEventType.MOUSE_OVER, mouseOverIncident)
                pointElement.on(global.DC.MouseEventType.MOUSE_OUT, mouseOutIncident)
                pointElement.on(global.DC.MouseEventType.MOUSE_MOVE, pointMouseMove)
            }
        },
        /**
         * 删除图层指定点
         * @param {*} layerName 图层名称
         * @param {*} siteId 点ID
         */
        mapRemoveSiteLayer (layerName, siteId) {
            if (layersObjcect[layerName]) {
                let allSite = layersObjcect[layerName].getOverlays()
                allSite.filter(item => item.attrParams.id == siteId).forEach(item => layersObjcect[layerName].removeOverlay(item))
            }
        },
        /**
         * 图层面添加
@@ -1438,6 +1489,7 @@
        this.$EventBus.$off('mapAddLayer')
        this.$EventBus.$off('mapRemoveLayer')
        this.$EventBus.$off('mapRemovePolygonLayer')
        this.$EventBus.$off('mapRemoveSiteLayer')
        this.$EventBus.$off('mapRemovePolygon')
        this.$EventBus.$off('mapClearLayer')
        this.$EventBus.$off('layerPointAdd')
src/router/page/index.js
@@ -29,6 +29,9 @@
  import("../../views/practitionersManage/index.vue");
const policeAlarmRecordsManage = () =>
  import("../../views/policeAlarmRecordsManage/index.vue");
const available = () => import("../../views/available/index.vue");
const risk = () => import("../../views/risk/index.vue");
const intelligentSearch = () =>
  import("../../views/intelligentSearch/index.vue");
@@ -178,6 +181,20 @@
        },
        component: policeAlarmRecordsManage,
      },
      {
        path: "available",
        meta: {
          title: "可用资源图",
        },
        component: available,
      },
      {
        path: "risk",
        meta: {
          title: "风险隐患图",
        },
        component: risk,
      },
    ],
  },
  // {
src/store/getters.js
@@ -2,7 +2,7 @@
 * @Author: shuishen 1109946754@qq.com
 * @Date: 2023-03-02 16:36:47
 * @LastEditors: shuishen 1109946754@qq.com
 * @LastEditTime: 2023-07-12 09:58:42
 * @LastEditTime: 2024-05-08 15:35:43
 * @FilePath: \srs-police-affairs\src\store\getters.js
 * @Description: 
 * 
@@ -17,6 +17,10 @@
    activityPolicePopup: (state) => state.popupParams.activityPolicePopup,
    activityPolicePopupData: (state) => state.popupParams.activityPolicePopupData,
    multipMapPopup: (state) => state.popupParams.multipMapPopup,
    multipMapPopupData: (state) => state.popupParams.multipMapPopupData,
    videoListPopup: (state) => state.popupParams.videoListPopup,
    videoListPopupData: (state) => state.popupParams.videoListPopupData,
    mapSearchPopup: (state) => state.popupParams.mapSearchPopup,
src/store/modules/popupParams.js
@@ -2,7 +2,7 @@
 * @Author: shuishen 1109946754@qq.com
 * @Date: 2023-03-02 16:36:47
 * @LastEditors: shuishen 1109946754@qq.com
 * @LastEditTime: 2023-05-02 17:29:46
 * @LastEditTime: 2024-05-08 15:37:22
 * @FilePath: \srs-police-affairs\src\store\modules\popupParams.js
 * @Description: 
 * 
@@ -22,6 +22,9 @@
        activityPolicePopup: false,
        activityPolicePopupData: {},
        multipMapPopup: false,
        multipMapPopupData: {},
        videoListPopup: false,
        videoListPopupData: {},
@@ -71,6 +74,14 @@
            state.activityPolicePopupData = activityPolicePopupData
        },
        SET_MULTIPMAPPOPUP (state, multipMapPopup) {
            state.multipMapPopup = multipMapPopup
        },
        SET_MULTIPMAPPOPUPDATA (state, multipMapPopupData) {
            state.multipMapPopupData = multipMapPopupData
        },
        SET_VIDEOLISTPOPUP (state, videoListPopup) {
            state.videoListPopup = videoListPopup
        },
src/views/available/index.vue
New file
@@ -0,0 +1,385 @@
<!--
 * @Author: shuishen 1109946754@qq.com
 * @Date: 2022-08-18 16:18:17
 * @LastEditors: shuishen 1109946754@qq.com
 * @LastEditTime: 2024-05-09 15:39:27
 * @FilePath: \srs-police-affairs\src\views\available\index.vue
 * @Description: 辖区管理
 *
 * Copyright (c) 2022 by shuishen 1109946754@qq.com, All Rights Reserved.
-->
<template>
    <div class="police-page container">
        <div v-show="boxShow" class="container-content" ref="containerContent">
            <div class="down-tree">
                <el-scrollbar>
                    <el-tree show-checkbox @check-change="handleCheckChange" v-show="searchLazyFlag" :props="props"
                        :load="loadNode" lazy node-key="id" @node-click="treeClick"
                        :default-expanded-keys="defaultExpandedKeys" :empty-text="''">
                        <span class="custom-tree-node" slot-scope="node">
                            <span>
                                {{ node.data.label }}
                            </span>
                            <span v-show="'num' in node.data && node.data.num != -1">
                                ({{ node.data.num }}个)
                            </span>
                        </span>
                    </el-tree>
                </el-scrollbar>
            </div>
        </div>
    </div>
</template>
<script>
/**
 * @description: 将图片和文字合成新图标使用(参考Cesium源码)
 * @param {*} url:图片地址
 * @param {*} label:文字
 * @param {*} size:画布大小
 * @return {*} 返回canvas
 */
function combineIconAndLabel (url, label, size) {
    // 创建画布对象
    let canvas = document.createElement('canvas')
    canvas.width = size
    canvas.height = size
    let ctx = canvas.getContext("2d")
    let promise = new global.DC.Namespace.Cesium.Resource.fetchImage(url).then(image => {
        // 异常判断
        try {
            ctx.drawImage(image, 0, 0)
        } catch (e) {
            console.log(e)
        }
        // 渲染字体
        // font属性设置顺序:font-style, font-variant, font-weight, font-size, line-height, font-family
        ctx.fillStyle = global.DC.Namespace.Cesium.Color.WHITE.toCssColorString()
        ctx.font = 'bold 20px Microsoft YaHei'
        ctx.textAlign = "center"
        ctx.textBaseline = "middle"
        ctx.fillText(label, size / 2, size / 2)
        return canvas
    })
    return promise
}
import { initMapPosition } from '@/utils/mapPositionInit'
import {
    getAvailableList,
} from '@/api/multiMap/index.js'
let loading = null
let pngData = {
    '政府': 'zf',
    '警务': 'jw',
    '医疗': 'yl',
    '教育': 'jy',
    '金融': 'jr',
    '景点': 'jd',
    '娱乐': 'yle',
    '农贸': 'nm',
    '车站': 'cz',
}
let layerData = []
export default {
    inject: ['userInfo'],
    data () {
        return {
            boxShow: false,
            searchLazyFlag: true,
            defaultExpandedKeys: [],
            props: {
                label: 'label',
                isLeaf: 'leaf'
            },
        }
    },
    created () {
        const that = this
        this.$nextTick(() => {
            initMapPosition()
        })
    },
    mounted () {
        this.$parent.$parent.resize('400px', true)
        this.$nextTick(() => {
            this.$EventBus.$emit('closeMxTileset')
        })
    },
    computed: {
    },
    methods: {
        loading () {
            loading = this.$loading({
                lock: true,
                text: '拼命加载中',
                spinner: 'el-icon-loading',
                background: 'rgba(0, 0, 0, 0.5)'
            })
        },
        uuid () {
            return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
                var r = Math.random() * 16 | 0,
                    v = c == 'x' ? r : (r & 0x3 | 0x8)
                return v.toString(16)
            })
        },
        async loadNode (node, resolve) {
            let data = null
            let type = {}
            let flag = false
            if ('data' in node && node.data && 'hasChildren' in node.data && node.data.hasChildren) {
                type = {
                    type: node.data.type
                }
                flag = true
            }
            await getAvailableList(type).then(res => {
                data = res.data.data.map((item, index) => {
                    let layerName = this.uuid()
                    if (flag == true) {
                        layerName = node.data.layerName
                    } else {
                        layerData.push(layerName)
                    }
                    return {
                        ...item,
                        ind: index + 1,
                        layerName,
                        label: item.placeName,
                        leaf: !item.hasChildren
                    }
                })
            })
            return resolve(data)
        },
        handleCheckChange (data, checked, indeterminate) {
            if (!checked) {
                if (data && 'hasChildren' in data && data.hasChildren) {
                    this.$EventBus.$emit('mapClearLayer', {
                        layerName: data.layerName,
                        type: 'VectorLayer'
                    })
                } else {
                    this.$EventBus.$emit('mapRemoveSiteLayer', {
                        layerName: data.layerName,
                        siteId: data.id
                    })
                }
            } else {
                if (data && 'hasChildren' in data && data.hasChildren) {
                    getAvailableList({
                        type: data.type
                    }).then(res => {
                        res.data.data.forEach((item, index) => {
                            if (item.lng && item.lat) {
                                let pngUrl = 'location'
                                for (let i in pngData) {
                                    if (item.type.indexOf(i) != -1) {
                                        pngUrl = pngData[i]
                                    }
                                }
                                this.$EventBus.$emit('layerPointAdd', {
                                    layerName: data.layerName,
                                    type: "newBillboard",
                                    params: {
                                        ...item,
                                        lng: item.lng,
                                        lat: item.lat,
                                        url: `/img/multip/${pngUrl}.png`,
                                        text: index + 1,
                                        setStyle: {
                                            fontColor: global.DC.Color.BLACK,
                                            disableDepthTestDistance: Number.POSITIVE_INFINITY
                                        }
                                    },
                                    incident: this.siteClick,
                                })
                            }
                        })
                    })
                } else {
                    if (data.lng && data.lat) {
                        let pngUrl = 'location'
                        for (let i in pngData) {
                            if (data.type.indexOf(i) != -1) {
                                pngUrl = pngData[i]
                            }
                        }
                        this.$EventBus.$emit('layerPointAdd', {
                            layerName: data.layerName,
                            type: "newBillboard",
                            params: {
                                ...data,
                                lng: data.lng,
                                lat: data.lat,
                                url: `/img/multip/${pngUrl}.png`,
                                text: data.ind,
                                setStyle: {
                                    fontColor: global.DC.Color.BLACK,
                                    disableDepthTestDistance: Number.POSITIVE_INFINITY
                                }
                            },
                            incident: this.siteClick,
                        })
                    }
                }
            }
        },
        treeClick (node) {
            if (node.parent == true) return
            const { lng, lat } = node
            if (lng == undefined || lng == 0 || lng == null || lng == ''
                || lat == undefined || lat == 0 || lat == null || lat == '') {
                this.$message({
                    message: '该设备暂无位置信息',
                    type: 'warning'
                })
                return
            }
            this.$EventBus.$emit('toPosition', {
                siteJd: node.lng,
                siteWd: node.lat,
                siteGd: 600,
            })
            this.$store.commit('SET_MULTIPMAPPOPUP', true)
            this.$store.commit('SET_MULTIPMAPPOPUPDATA', {
                title: node.placeName,
                detail: {
                    '场所名称': node.placeName,
                    '责任民警': node.policeman,
                    '责任民警联系电话': node.policemanPhone,
                    '所属派出所': node.pcsName,
                    '地址': node.address,
                }
            })
            var popup = new global.DC.DivForms(global.viewer, {
                domId: 'multipMapPopupDomBox',
                position: [
                    global.DC.Transform.transformWGS84ToCartesian(
                        new global.DC.Position(
                            Number(node.lng),
                            Number(node.lat),
                            0
                        )
                    )
                ]
            })
        },
        siteClick (e) {
            console.log(e, 6666655)
        },
        // 大小重置
        boxResize (val) {
            this.boxShow = val
        },
    },
    destroyed () {
        loading && loading.close()
        layerData.forEach(item => {
            this.$EventBus.$emit('mapClearLayer', {
                layerName: item,
                type: 'VectorLayer'
            })
        })
        this.$store.commit('SET_MULTIPMAPPOPUP', false)
        this.$parent.$parent.resize('0px')
    }
}
</script>
<style scoped lang="scss">
.container {
    position: relative;
    width: 100%;
    height: 100%;
    &-content {
        padding: 10px 0;
        display: flex;
        flex-direction: column;
        width: 100%;
        height: 100%;
        color: #fff;
        background: $bg-color;
        .down-tree {
            flex: 1;
            overflow: hidden;
            :deep(.el-tree-node__content) {
                position: relative;
            }
            :deep(.el-tree-node.is-expanded > .el-tree-node__children) {
                display: inline;
            }
            :deep(.el-scrollbar) {
                height: 100%;
            }
            .custom-tree-node {
                display: flex;
                align-items: center;
                .status-box {
                    margin: auto;
                    margin-right: 6px;
                    width: 10px;
                    height: 10px;
                    text-align: center;
                    border-radius: 50%;
                    background-color: #adadad;
                }
            }
        }
    }
}
</style>
src/views/layout/index.vue
@@ -2,7 +2,7 @@
 * @Author: shuishen 1109946754@qq.com
 * @Date: 2022-08-18 16:18:24
 * @LastEditors: shuishen 1109946754@qq.com
 * @LastEditTime: 2024-05-07 11:17:07
 * @LastEditTime: 2024-05-08 10:54:58
 * @FilePath: \srs-police-affairs\src\views\layout\index.vue
 * @Description: 
 * 
@@ -14,7 +14,8 @@
            <div class="menu-list left">
                <div @mouseenter="item.childrenFlag = true" @mouseleave="item.childrenFlag = false" class="nav-list"
                    @click="goToPath(item)" v-for="(item, index) in menuLeftList" :key="index">
                    <span :class="{ on: currentUrl.indexOf(item.path) != -1 }">{{ item.menuName }}</span>
                    <span :class="{ on: showChange(item) }">{{ item.menuName }}</span>
                    <div class="sub-nav-list" v-show="item.children && item.childrenFlag">
                        <div v-for="(subItem, subIndex) in item.children" :key="subIndex"
                            @click.stop="goToPath(subItem)">
@@ -34,7 +35,7 @@
            <div class="menu-list right">
                <div @mouseenter="item.childrenFlag = true" @mouseleave="item.childrenFlag = false" class="nav-list"
                    @click="goToPath(item)" v-for="(item, index) in menuRightList" :key="index">
                    <span :class="{ on: currentUrl.indexOf(item.path) != -1 || currentUrl.indexOf(item.pathT) != -1 }">
                    <span :class="{ on: showChange(item) }">
                        {{
                        item.menuName }}
                    </span>
@@ -196,7 +197,6 @@
                {
                    menuName: '视频监控',
                    path: '/layout/video/list',
                    pathT: '/layout/video/region',
                    childrenFlag: false,
                    children: [
                        {
@@ -216,13 +216,27 @@
                    path: '/layout/activity'
                },
                {
                    menuName: '警情信息',
                    path: '/layout/policeinfor'
                    menuName: '三张图',
                    path: '/layout/policeinfor',
                    childrenFlag: false,
                    children: [
                        {
                            menuName: '警情信息',
                            path: '/layout/policeinfor'
                        },
                        {
                            menuName: '可用资源图',
                            path: '/layout/available'
                        },
                        {
                            menuName: '风险隐患图',
                            path: '/layout/risk'
                        }
                    ]
                },
                {
                    menuName: '扫码应用',
                    path: '/layout/scanOrCode',
                    pathT: '/layout/site',
                    childrenFlag: false,
                    children: [
                        {
@@ -287,7 +301,17 @@
    },
    computed: {
        ...mapGetters(['permission'])
        ...mapGetters(['permission']),
        showChange () {
            return (item) => {
                if ('children' in item && item.children.length) {
                    return item.children.some(i => this.currentUrl == i.path)
                } else {
                    return this.currentUrl.indexOf(item.path) != -1
                }
            }
        }
    },
    created () {
src/views/risk/index.vue
New file
@@ -0,0 +1,387 @@
<!--
 * @Author: shuishen 1109946754@qq.com
 * @Date: 2022-08-18 16:18:17
 * @LastEditors: shuishen 1109946754@qq.com
 * @LastEditTime: 2024-05-09 11:20:17
 * @FilePath: \srs-police-affairs\src\views\risk\index.vue
 * @Description: 辖区管理
 *
 * Copyright (c) 2022 by shuishen 1109946754@qq.com, All Rights Reserved.
-->
<template>
    <div class="police-page container">
        <div v-show="boxShow" class="container-content" ref="containerContent">
            <div class="down-tree">
                <el-scrollbar>
                    <el-tree show-checkbox @check-change="handleCheckChange" v-show="searchLazyFlag" :props="props"
                        :load="loadNode" lazy node-key="id" @node-click="treeClick"
                        :default-expanded-keys="defaultExpandedKeys" :empty-text="''">
                        <span class="custom-tree-node" slot-scope="node">
                            <span>
                                {{ node.data.label }}
                            </span>
                        </span>
                    </el-tree>
                </el-scrollbar>
            </div>
        </div>
    </div>
</template>
<script>
import { initMapPosition } from '@/utils/mapPositionInit'
import {
    getRiskList,
} from '@/api/multiMap/index.js'
let loading = null
let pngData = [
    {
        name: '涝',
        url: 'lao',
        width: 32,
        height: 32,
    },
    {
        name: '危爆',
        url: 'wb',
        width: 38,
        height: 32,
        xOffset: 0,
        yOffset: 10
    }
]
let layerData = []
/**
 * @description: 将图片和文字合成新图标使用(参考Cesium源码)
 * @param {*} url:图片地址
 * @param {*} label:文字
 * @param {*} size:画布大小
 * @return {*} 返回canvas
 */
function combineIconAndLabel (url, label, width, height, xOfffset = 0, yOffset = 0) {
    // 创建画布对象
    let canvas = document.createElement('canvas')
    canvas.width = width
    canvas.height = height
    let ctx = canvas.getContext("2d")
    let promise = new global.DC.Namespace.Cesium.Resource.fetchImage(url).then(image => {
        // 异常判断
        try {
            ctx.drawImage(image, 0, 0)
        } catch (e) {
            console.log(e)
        }
        // 渲染字体
        // font属性设置顺序:font-style, font-variant, font-weight, font-size, line-height, font-family
        ctx.fillStyle = global.DC.Namespace.Cesium.Color.BLACK.toCssColorString()
        ctx.font = 'bold 14px Microsoft YaHei'
        ctx.textAlign = "center"
        ctx.textBaseline = "middle"
        console.log(yOffset, 60666)
        ctx.fillText(label, (width + xOfffset) / 2, (height + yOffset) / 2)
        return canvas
    })
    return promise
}
export default {
    inject: ['userInfo'],
    data () {
        return {
            boxShow: false,
            searchLazyFlag: true,
            defaultExpandedKeys: [],
            props: {
                label: 'label',
                isLeaf: 'leaf'
            },
        }
    },
    created () {
        const that = this
        this.$nextTick(() => {
            initMapPosition()
        })
    },
    mounted () {
        this.$parent.$parent.resize('400px', true)
        this.$nextTick(() => {
            this.$EventBus.$emit('closeMxTileset')
        })
    },
    computed: {
    },
    methods: {
        loading () {
            loading = this.$loading({
                lock: true,
                text: '拼命加载中',
                spinner: 'el-icon-loading',
                background: 'rgba(0, 0, 0, 0.5)'
            })
        },
        uuid () {
            return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
                var r = Math.random() * 16 | 0,
                    v = c == 'x' ? r : (r & 0x3 | 0x8)
                return v.toString(16)
            })
        },
        async loadNode (node, resolve) {
            let data = null
            let type = {}
            let flag = false
            if ('data' in node && node.data && 'hasChildren' in node.data && node.data.hasChildren) {
                type = {
                    type: node.data.type
                }
                flag = true
            }
            await getRiskList(type).then(res => {
                data = res.data.data.map((item, index) => {
                    let layerName = this.uuid()
                    if (flag == true) {
                        layerName = node.data.layerName
                    } else {
                        layerData.push(layerName)
                    }
                    return {
                        ...item,
                        ind: index + 1,
                        layerName,
                        label: item.placeName,
                        leaf: !item.hasChildren
                    }
                })
            })
            return resolve(data)
        },
        handleCheckChange (data, checked, indeterminate) {
            if (!checked) {
                if (data && 'hasChildren' in data && data.hasChildren) {
                    this.$EventBus.$emit('mapClearLayer', {
                        layerName: data.layerName,
                        type: 'VectorLayer'
                    })
                } else {
                    this.$EventBus.$emit('mapRemoveSiteLayer', {
                        layerName: data.layerName,
                        siteId: data.id
                    })
                }
            } else {
                if (data && 'hasChildren' in data && data.hasChildren) {
                    getRiskList({
                        type: data.type
                    }).then(res => {
                        res.data.data.forEach((item, index) => {
                            if (item.lng && item.lat) {
                                let pngObj = {
                                    url: 'location',
                                    width: 32,
                                    height: 32
                                }
                                let flag = pngData.some(i => item.type.indexOf(i.name) != -1)
                                if (flag) {
                                    pngObj = pngData.find(i => item.type.indexOf(i.name) != -1)
                                }
                                this.$EventBus.$emit('layerPointAdd', {
                                    layerName: data.layerName,
                                    type: "newBillboard",
                                    params: {
                                        ...item,
                                        lng: item.lng,
                                        lat: item.lat,
                                        url: `/img/multip/${pngObj.url}.png`,
                                        // combineIconAndLabel(, index + 1, pngObj.width, pngObj.height, pngObj.xOfffset, pngObj.yOffset),
                                    },
                                    incident: this.siteClick,
                                })
                            }
                        })
                    })
                } else {
                    if (data.lng && data.lat) {
                        let pngObj = {
                            url: 'location',
                            width: 32,
                            height: 32
                        }
                        let flag = pngData.some(i => data.type.indexOf(i.name) != -1)
                        if (flag) {
                            pngObj = pngData.find(i => data.type.indexOf(i.name) != -1)
                        }
                        this.$EventBus.$emit('layerPointAdd', {
                            layerName: data.layerName,
                            type: "newBillboard",
                            params: {
                                ...data,
                                lng: data.lng,
                                lat: data.lat,
                                url: `/img/multip/${pngObj.url}.png`,
                                //  combineIconAndLabel(`/img/multip/${pngObj.url}.png`, data.ind, pngObj.width, pngObj.height, pngObj.xOfffset, pngObj.yOffset)
                            },
                            incident: this.siteClick,
                        })
                    }
                }
            }
        },
        treeClick (node) {
            if (node.parent == true) return
            const { lng, lat } = node
            if (lng == undefined || lng == 0 || lng == null || lng == ''
                || lat == undefined || lat == 0 || lat == null || lat == '') {
                this.$message({
                    message: '该设备暂无位置信息',
                    type: 'warning'
                })
                return
            }
            this.$EventBus.$emit('toPosition', {
                siteJd: node.lng,
                siteWd: node.lat,
                siteGd: 600,
            })
            this.$store.commit('SET_MULTIPMAPPOPUP', true)
            this.$store.commit('SET_MULTIPMAPPOPUPDATA', {
                title: node.placeName,
                detail: {
                    '场所名称': node.placeName,
                    '存在隐患': node.hiddenDangerDesc,
                    '所属派出所': node.pcsName,
                    '地址': node.address,
                }
            })
            var popup = new global.DC.DivForms(global.viewer, {
                domId: 'multipMapPopupDomBox',
                position: [
                    global.DC.Transform.transformWGS84ToCartesian(
                        new global.DC.Position(
                            Number(node.lng),
                            Number(node.lat),
                            0
                        )
                    )
                ]
            })
        },
        siteClick (e) {
            console.log(e, 6666655)
        },
        // 大小重置
        boxResize (val) {
            this.boxShow = val
        },
    },
    destroyed () {
        loading && loading.close()
        layerData.forEach(item => {
            this.$EventBus.$emit('mapClearLayer', {
                layerName: item,
                type: 'VectorLayer'
            })
        })
        this.$store.commit('SET_MULTIPMAPPOPUP', false)
        this.$parent.$parent.resize('0px')
    }
}
</script>
<style scoped lang="scss">
.container {
    position: relative;
    width: 100%;
    height: 100%;
    &-content {
        padding: 10px 0;
        display: flex;
        flex-direction: column;
        width: 100%;
        height: 100%;
        color: #fff;
        background: $bg-color;
        .down-tree {
            flex: 1;
            overflow: hidden;
            :deep(.el-tree-node__content) {
                position: relative;
            }
            :deep(.el-tree-node.is-expanded > .el-tree-node__children) {
                display: inline;
            }
            :deep(.el-scrollbar) {
                height: 100%;
            }
            .custom-tree-node {
                display: flex;
                align-items: center;
                .status-box {
                    margin: auto;
                    margin-right: 6px;
                    width: 10px;
                    height: 10px;
                    text-align: center;
                    border-radius: 50%;
                    background-color: #adadad;
                }
            }
        }
    }
}
</style>