Newer
Older
dcms_front / src / views / supControl / supControl.vue
<template>
  <div>
    <el-row>
      <el-col :span="6" class="staticPanel">
        <sup-statis-panel ref="caseStatics" @changeTimeTerm="handleTimeTermChanged" />
      </el-col>

      <el-col :span="18" class="mapPanel">
        <el-row>
          <el-col :span="6">
            <div id="today-visitor" class="caseStatisBlock" style="margin-left: 0px;">
              <span v-text="caseCounts.labelText"/>立案
              <span class="bigNumber" v-text="caseCounts.register"/>件
            </div>
          </el-col>

          <el-col :span="6">
            <div id="total-alarm" class="caseStatisBlock">
              本日立案
              <span class="bigNumber" v-text="caseCounts.todayRegister" />件
            </div>
          </el-col>

          <el-col :span="6">
            <div id="online-device" class="caseStatisBlock">
              <span v-text="caseCounts.labelText" />处理率
              <span class="bigNumber" v-text="caseCounts.oughtHandleRate" />%
            </div>
          </el-col>

          <el-col :span="6">
            <div id="total-device" class="caseStatisBlock">
              <span v-text="caseCounts.labelText" />未处理
              <span class="bigNumber" v-text="caseCounts.notHandled" />件
            </div>
          </el-col>
        </el-row>

        <el-row :gutter="20" style="margin-right: -20px">
          <el-col :span="24" style="padding: 0px 20px">
            <div v-loading="mapLoading" ref="mapDiv" class="baseMap" style="margin-left: -10px;"/>
          </el-col>
        </el-row>

        <!-- 浮层 -->
        <div class="mapOverLayer">
          <el-row :gutter="20">
            <el-col :span="6" style="margin-top: 10px; padding-left: 25px; line-height: 40px;">
              <el-radio-group v-model="queryEventSwitch" size="medium" @change="switchContent">
                <el-radio-button label="0">案卷</el-radio-button>
                <el-radio-button label="1">人员</el-radio-button>
                <!--                <el-radio-button label="2">部件</el-radio-button>-->
              </el-radio-group>
            </el-col>

            <el-col :span="18" style="margin-top: 10px;">
              <el-form v-if="showCaseSelect" :label-position="labelPosition" label-width="120px">
                <el-form-item label="案卷状态" class="item">
                  <el-select v-model="parts.caseStatusCode" size="medium" placeholder="选择案卷状态" clearable value="" class="notLastSelect" @change="changeCaseScope()">
                    <el-option value="toHandle" label="处置中"/>
                    <el-option value="toVerify" label="待核实"/>
                    <el-option value="toCheck" label="待核查"/>
                  </el-select>

                  <label class="customLabel">是否超期</label>
                  <el-radio-group v-model="isOverTime" size="medium" @change="changeCaseScope()">
                    <el-radio-button label="">全部</el-radio-button>
                    <el-radio-button label="Ot">超期</el-radio-button>
                    <el-radio-button label="Not">未超期</el-radio-button>
                  </el-radio-group>
                </el-form-item>
              </el-form>

              <el-form v-if="showPersonSelect" :label-position="labelPosition" label-width="120px">
                <el-row :gutter="10" class="rowSelect">
                  <el-col :span="20">
                    <el-form-item label="人员分类" prop="casepersonCode" class="item">
                      <el-radio-group v-model="casepersonCode" size="medium" @change="queryPerson()">
                        <el-radio-button label="supervisor">监督员</el-radio-button>
                        <el-radio-button label="process">处置员</el-radio-button>
                      </el-radio-group>
                    </el-form-item>
                  </el-col>
                </el-row>
              </el-form>

              <el-form v-if="showPartsSelect" :label-position="labelPosition" label-width="120px">
                <el-row :gutter="10" class="rowSelect">
                  <el-col :span="20">
                    <el-form-item label="部件大类" prop="casetypeCode" class="item">
                      <el-select v-model="parts.casetypeCode" size="medium" placeholder="选择部件大类" clearable class="notLastSelect">
                        <el-option
                          v-for="item in caseTypeOpts"
                          :key="item.id"
                          :label="item.typeName"
                          :value="item.typeCode"/>
                      </el-select>

                      <label class="customLabel">部件小类</label>
                      <el-select v-model="parts.casetypeDetailCode" size="medium" placeholder="选择部件小类" clearable>
                        <el-option
                          v-for="item in caseDetailTypeOpts"
                          :key="item.id"
                          :label="item.typeDetailName"
                          :value="item.typeDetailCode"/>
                      </el-select>
                    </el-form-item>
                  </el-col>
                </el-row>
              </el-form>
            </el-col>
          </el-row>
        </div>
      </el-col>
    </el-row>
  </div>
</template>

<script>
import request from '@/utils/request'
import SupStatisPanel from '@/views/supControl/SupStatisPanel'
import { getCaseType, getCaseDetailType } from '@/api/callCase/callCase'
import { getUserOnLine } from '@/api/system/user'
import Leaflet from 'leaflet'
import { mapGetters } from 'vuex'

const toHandleIcon = Leaflet.icon({
  iconUrl: require('@/assets/case/status_toHandle.png'),
  iconSize: [19, 31],
  iconAnchor: [10, 31],
  popupAnchor: [-0, -32]
})

const toVerifyIcon = Leaflet.icon({
  iconUrl: require('@/assets/case/status_toVerify.png'),
  iconSize: [19, 31],
  iconAnchor: [10, 31],
  popupAnchor: [-0, -32]
})

const toCheckIcon = Leaflet.icon({
  iconUrl: require('@/assets/case/status_toCheck.png'),
  iconSize: [19, 31],
  iconAnchor: [10, 31],
  popupAnchor: [-0, -32]
})

const redIcon = Leaflet.icon({
  iconUrl: require('@/assets/case/status_overTime.png'),
  iconSize: [19, 31],
  iconAnchor: [10, 31],
  popupAnchor: [-0, -32]
})

const offlineIcon = Leaflet.icon({
  iconUrl: require('@/assets/case/offline.png'),
  iconSize: [32, 32],
  iconAnchor: [16, 32],
  popupAnchor: [-0, -32]
})

const onlineIcon = Leaflet.icon({
  iconUrl: require('@/assets/case/online.png'),
  iconSize: [32, 32],
  iconAnchor: [16, 32],
  popupAnchor: [-0, -32]
})

const esri = require('esri-leaflet')

export default {
  name: 'SupControl',
  components: { SupStatisPanel },
  data() {
    return {
      map: '',
      baseLayer: [],
      parts: {
        eorc: '2', // 类别
        casetypeCode: '', // 部件大类编码,
        casetypeDetailCode: '', // 部件小类编码
        caseStatusCode: ''
      },
      timeTerm: '1',
      personQuery: {
        roleTips: ''
      },
      caseCounts: {
        labelText: '本月',
        register: 0, // 本期立案数
        todayRegister: 0, // 当日立案数
        totalCheckNum: 0, // 本期总应处置数
        checkedNum: 0, // 本期处置数
        oughtHandleRate: 0, // 本期应处置率
        notHandled: 0 // 本期未处置数
      },
      casepersonCode: 'supervisor',
      labelPosition: 'right',
      caseTypeOpts: [], // 部件大类下拉框
      caseDetailTypeOpts: [], // 部件小类下拉框
      caseTypeCodeAuto: '', // 自动生成的案卷大类代码
      caseTypeDetailCodeAuto: '', // 自动生成的案卷小类代码
      mapLoading: false,
      queryEventSwitch: '0', // 0==查询案件;1==查询人员;2==查询部件
      showPartsSelect: false,
      showPersonSelect: false,
      showCaseSelect: true,
      isOverTime: '', // 是否区分超时工单
      toVerifyListOt: [], // 待核实超时
      toVerifyListNot: [], // 待核实未超时
      toCheckListOt: [], // 待核查超时
      toCheckListNot: [], // 待核查
      toHandleListOt: [], // 处置中超时
      toHandleListNot: [], // 处置中
      caseLayerGroup: [], // 案卷图层组
      toVerifyCaseNot: null,
      toVerifyCaseOt: null,
      toCheckCaseNot: null,
      toCheckCaseOt: null,
      toHandleCaseNot: null,
      toHandleCaseOt: null,
      onlineList: [], // 在岗人员列表
      offlineList: [], // 离线人员列表
      personLayerGroup: [], // 人员图层组
      offlinePerson: null,
      onlinePerson: null,
      imageBaseUrl: this.baseConfig.baseUrl + '/static/'
    }
  },
  computed: {
    eorc() {
      return this.parts.eorc
    },
    caseType() {
      return this.parts.casetypeCode
    },
    caseTypeDetail() {
      return this.parts.casetypeDetailCode
    },
    ...mapGetters([
      'baseUrl',
      'partsUrl',
      'mapUrl'
    ])
  },
  watch: {
    caseType(typeVal) {
      this.cascaderCaseDetailType(typeVal)
    },
    caseTypeDetail(typeVal) {
      this.clearPoint()

      const obj = this.caseDetailTypeOpts.find(function(item) {
        return item.typeDetailCode === typeVal
      })
      if (obj) {
        this.addPartsPoints(obj.typeDetailName)
      }
    }
  },
  mounted() {
    this.initMap()
    this.switchContent()

    // 案卷图层
    this.caseLayerGroup.push(this.toVerifyCaseNot)
    this.caseLayerGroup.push(this.toVerifyCaseOt)
    this.caseLayerGroup.push(this.toCheckCaseNot)
    this.caseLayerGroup.push(this.toCheckCaseOt)
    this.caseLayerGroup.push(this.toHandleCaseNot)
    this.caseLayerGroup.push(this.toHandleCaseOt)

    // 人员图层
    this.personLayerGroup.push(this.offlinePerson)
    this.personLayerGroup.push(this.onlinePerson)
  },
  methods: {
    handleTimeTermChanged: function(caseCounts) {
      this.caseCounts = caseCounts
    },
    switchContent: function() {
      this.clearPoint() // 清除绘制的点

      // 案卷(0)、人员(1)、部件(2),
      if (this.queryEventSwitch === '2') {
        this.showPartsSelect = true
        this.showPersonSelect = false
        this.showCaseSelect = false
        this.cascaderCaseType('2')
      } else if (this.queryEventSwitch === '1') {
        this.showPersonSelect = true
        this.showPartsSelect = false
        this.showCaseSelect = false

        // 查询人员
        this.queryPerson()
      } else if (this.queryEventSwitch === '0') {
        this.showCaseSelect = true
        this.showPartsSelect = false
        this.showPersonSelect = false

        // 查询三个状态的案卷列表
        this.queryCaseToVerify()
        this.queryCaseToCheck()
        this.queryCaseToHandle()
      }
    },
    // 切换不同案卷状态的显示与否
    changeCaseScope: function() {
      // isOverTime: ''--全部,'Ot'--超时,'Not'--未超时
      // caseStatusCode: 'toHandle'--待处置,'toVerify'--待核实,'toCheck'--待核查
      let filterIndex = []

      const that = this
      if (this.parts.caseStatusCode === '') {
        if (this.isOverTime === 'Not') {
          filterIndex = [0, 2, 4]
        } else if (this.isOverTime === 'Ot') {
          filterIndex = [1, 3, 5]
        } else {
          filterIndex = [0, 1, 2, 3, 4, 5]
        }
      } else if (this.parts.caseStatusCode === 'toHandle') {
        if (this.isOverTime === 'Not') {
          filterIndex = [4]
        } else if (this.isOverTime === 'Ot') {
          filterIndex = [5]
        } else {
          filterIndex = [4, 5]
        }
      } else if (this.parts.caseStatusCode === 'toVerify') {
        if (this.isOverTime === 'Not') {
          filterIndex = [0]
        } else if (this.isOverTime === 'Ot') {
          filterIndex = [1]
        } else {
          filterIndex = [0, 1]
        }
      } else if (this.parts.caseStatusCode === 'toCheck') {
        if (this.isOverTime === 'Not') {
          filterIndex = [2]
        } else if (this.isOverTime === 'Ot') {
          filterIndex = [3]
        } else {
          filterIndex = [2, 3]
        }
      }

      // 清除所有的图层
      this.caseLayerGroup.forEach(item => {
        that.map.removeLayer(item)
      })

      // 重新添加指定状态的图层
      filterIndex.forEach(i => {
        that.caseLayerGroup[i].addTo(that.map)
      })
    },
    queryCaseToVerify: function() {
      const that = this
      const onePageParam = {
        limit: 1000,
        offset: 1
      }

      // 查询待核实案卷
      request({
        url: 'case/toVerifyListPage',
        method: 'get',
        params: onePageParam
      }).then(response => {
        if (response.code === 200) {
          if (response.data.total > 0) {
            // 查询有新结果时清除原有的列表
            this.toVerifyListNot = []
            this.toVerifyListOt = []

            response.data.rows.forEach(item => {
              if (item.isOvertime === '1') { // 超时标志位为1,放入超时列表中
                that.toVerifyListOt.push(item)
              } else { // 未超时
                that.toVerifyListNot.push(item)
              }
            })

            this.addCasePointOnMap(this.toVerifyListNot, this.toVerifyCaseNot, toVerifyIcon)
            this.addCasePointOnMap(this.toVerifyListOt, this.toVerifyCaseOt, redIcon)
          }
        }
      })
    },
    queryCaseToCheck: function() {
      const that = this
      const onePageParam = {
        limit: 1000,
        offset: 1
      }

      // 查询待核查案卷
      request({
        url: 'case/toCheckListPage',
        method: 'get',
        params: onePageParam
      }).then(response => {
        if (response.code === 200) {
          if (response.data.total > 0) {
            // 查询有新结果时清除原有的列表
            this.toCheckListNot = []
            this.toCheckListOt = []

            response.data.rows.forEach(item => {
              if (item.isOvertime === '1') { // 超时标志位为1,放入超时列表中
                that.toCheckListOt.push(item)
              } else { // 未超时
                that.toCheckListNot.push(item)
              }
            })

            this.addCasePointOnMap(this.toCheckListNot, this.toCheckCaseNot, toCheckIcon)
            this.addCasePointOnMap(this.toCheckListOt, this.toCheckCaseOt, redIcon)
          }
        }
      })
    },
    queryCaseToHandle: function() {
      const that = this
      const onePageParam = {
        limit: 1000,
        offset: 1
      }

      // 查询处理中案卷
      request({
        url: 'case/toHandleListPage',
        method: 'get',
        params: onePageParam
      }).then(response => {
        if (response.code === 200) {
          if (response.data.total > 0) {
            // 查询有新结果时清除原有的列表
            this.toHandleListNot = []
            this.toHandleListOt = []

            response.data.rows.forEach(item => {
              if (item.isOvertime === '1') { // 超时标志位为1,放入超时列表中
                that.toHandleListOt.push(item)
              } else { // 未超时
                that.toHandleListNot.push(item)
              }
            })

            this.addCasePointOnMap(this.toHandleListNot, this.toHandleCaseNot, toHandleIcon)
            this.addCasePointOnMap(this.toHandleListOt, this.toHandleCaseOt, redIcon)
          }
        }
      })
    },
    // 查询人员
    queryPerson: function() {
      const that = this

      // 清除所有的人员图层
      this.personLayerGroup.forEach(item => {
        that.map.removeLayer(item)
      })

      this.personQuery.roleTips = this.casepersonCode
      if (this.casepersonCode !== '') {
        getUserOnLine(this.personQuery).then(res => {
          if (res.code === 200) {
            if (res.data.length > 0) {
              // 查询有新结果时清除原有的列表
              this.onlineList = []
              this.offlineList = []

              // 清除图层组
              this.onlinePerson.clearLayers()
              this.offlinePerson.clearLayers()

              res.data.forEach(item => {
                if (item.onLine === true) { // 在线标志位
                  that.onlineList.push(item)
                } else {
                  that.offlineList.push(item)
                }
              })

              this.addPersonPoints(this.onlineList, this.onlinePerson, onlineIcon)
              this.addPersonPoints(this.offlineList, this.offlinePerson, offlineIcon)
            }
          }
        })
      }
    },
    // 级联查询部件大类
    cascaderCaseType: function(eorc) {
      // 前两行的作用是清除部件大类的当前值和select中的所有选项
      this.caseTypeOpts = []
      this.parts.casetypeCode = ''
      // 以下两行的作用是清除部件小类的当前值和select中的所有选项
      this.caseDetailTypeOpts = []
      this.parts.casetypeDetailCode = ''
      // 部件大类
      if (eorc !== null && eorc.length > 0) {
        getCaseType(eorc).then(response => {
          for (const opt of response.data) {
            this.caseTypeOpts.push(opt)

            if (opt.typeCode === this.caseTypeCodeAuto) {
              this.parts.casetypeCode = opt.typeCode
            }
          }
        })
      }
    },
    cascaderCaseDetailType: function(typeCode) {
      // 以下两行的作用是清除案卷小类的当前值和select中的所有选项
      this.caseDetailTypeOpts = []
      this.parts.casetypeDetailCode = ''

      // 根据code获取ID,进行级联查询
      let typeId = 0
      this.caseTypeOpts.forEach(item => {
        if (item.typeCode === typeCode) {
          typeId = item.id
        }
      })

      if (typeId > 0) {
        getCaseDetailType(typeId).then(response => {
          for (const opt of response.data) {
            this.caseDetailTypeOpts.push(opt)
            if (opt.typeDetailCode === this.caseTypeDetailCodeAuto) {
              this.parts.casetypeDetailCode = opt.typeDetailCode
            }
          }
        })
      }
    },
    clearPoint: function() {
      const that = this
      // 清除所有案卷的图层
      this.caseLayerGroup.forEach(item => {
        that.map.removeLayer(item)
      })

      // 清除所有的人员图层
      this.personLayerGroup.forEach(item => {
        that.map.removeLayer(item)
      })
    },
    addCasePointOnMap: function(caseList, caseLayerGroup, icon) {
      caseList.forEach(caseDetail => {
        let popupStr = '<div class="popup-div">' +
          '<div class="popup-title">案卷概要信息</div>' +
          '<div class="dashed-line"></div>' +
          '<div class="popup-item"><label>案卷编号</label>' + caseDetail.caseid + '</div>' +
          '<div class="popup-item"><label>案卷状态</label>' + caseDetail.caseStateName + '</div>' +
          '<div class="popup-item"><label>案卷类型</label>' + caseDetail.casetypeName + ' / ' + caseDetail.casetypeDetailName + '</div>' +
          '<div class="popup-item"><label>发生地点</label>' + caseDetail.fieldintro + '</div>' +
          '<div class="popup-item"><label>案卷描述</label>' + caseDetail.description + '</div>' +
          '<div class="popup-item"><label>登记时间</label>' + caseDetail.reportTime + '</div>'

        if (caseDetail.fileIdVerify !== '') {
          const verifyImgs = caseDetail.fileIdVerify.split(',')
          const imgUrl1 = this.imageBaseUrl + verifyImgs[0]
          popupStr += '<div class="popup-item"><label>案卷图片</label></div>'
          popupStr += '<div class="popup-item"><img src="' + imgUrl1 + '" width="100" />'
          if (verifyImgs.length > 1) {
            const imgUrl2 = this.imageBaseUrl + verifyImgs[1]
            popupStr += '<img src="' + imgUrl2 + '" width="100" style="margin-left: 10px;" />'
          }
          popupStr += '</div>'
        }

        const popup = Leaflet.popup().setContent(popupStr)

        if (caseDetail.lng > 0 && caseDetail.lat > 0) {
          // 添加marker
          Leaflet.marker([caseDetail.lat, caseDetail.lng], { icon: icon }).addTo(caseLayerGroup).bindPopup(popup)
        }

        this.map.addLayer(caseLayerGroup)
      })
    },
    addPersonPoints: function(personList, personLayerGroup, icon) {
      personList.forEach(person => {
        const popupStr = '<div class="popup-div">' +
          '<div class="popup-title">' + person.name + '</div>' +
          '<div class="dashed-line"></div>' +
          '<div class="popup-item"><label>所在部门</label>' + person.deptName + '</div>' +
          '<div class="popup-item"><label>人员姓名</label>' + person.name + '</div>' +
          '<div class="popup-item"><label>手机号码</label>' + person.phone + '</div>' +
          '<div class="popup-item"><label>轨迹时间点</label>' + person.lastTimeFmt + '</div>' +
          '</div>'

        const popup = Leaflet.popup().setContent(popupStr)

        if (person.positionLng > 0 && person.positionLat > 0) {
          // 添加marker
          Leaflet.marker([person.positionLat, person.positionLng], { icon: icon }).addTo(personLayerGroup).bindPopup(popup)
        }
      })

      this.map.addLayer(personLayerGroup)
    },
    // 初始化地图
    initMap: function() {
      const map = Leaflet.map(this.$refs.mapDiv, {
        minZoom: 4,
        maxZoom: 25,
        center: this.baseConfig.center,
        zoom: this.baseConfig.zoom,
        zoomControl: false,
        attributionControl: false,
        crs: Leaflet.CRS.EPSG3857
      })
      this.map = map // data上需要挂载
      window.map = map

      // 加载天地图底图和标注
      this.baseLayer.push(Leaflet.tileLayer(
        this.baseConfig.mapUrl,
        { subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'] }
      ).addTo(map))
      this.baseLayer.push(Leaflet.tileLayer(
        this.baseConfig.labelUrl,
        { subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'] }
      ).addTo(map))

      // 加载部件分层的图层
      const bjfcLayer = esri.dynamicMapLayer({
        url: `${this.baseUrl}${this.partsUrl}`,
        minZoom: 20, // 最小缩放等级,
        zIndex: 10 // 设置显示层级,在底图之上
      }).addTo(map)

      bjfcLayer.bindPopup(function(error, featureCollection) {
        if (error || featureCollection.features.length === 0) {
          return false
        } else {
          const bjItem = featureCollection.features[0] // 部件对象
          const popupStr = '<div class="popup-div">' +
            '<div class="popup-title">' + bjItem.properties.objname + '</div>' +
            '<div class="dashed-line"></div>' +
            '<div class="popup-item"><label>部件类别</label>' + bjItem.properties.dl + ' / ' + bjItem.properties.xl + '</div>' +
            '<div class="popup-item"><label>部件编号</label>' + bjItem.properties.objid + '</div>' +
            '<div class="popup-item"><label>所在网格</label>' + bjItem.properties.bgid + '</div>' +
            '<div class="popup-item"><label>权属部门</label>' + bjItem.properties.deptname2 + '</div>' +
            '<div class="popup-item"><label>责任部门</label>' + bjItem.properties.deptname1 + '</div>' +
            '<div class="popup-item"><label>养护部门</label>' + bjItem.properties.deptname3 + '</div>' +
            '</div>'

          return popupStr
        }
      })

      // 加载自行发布的底图服务
      esri.dynamicMapLayer({
        url: `${this.baseUrl}${this.mapUrl}`,
        zIndex: 1
      }).addTo(map)

      // 初始化图层组
      /* eslint-disable new-cap */
      this.toVerifyCaseNot = new Leaflet.layerGroup().addTo(map)
      this.toVerifyCaseOt = new Leaflet.layerGroup().addTo(map)
      this.toCheckCaseNot = new Leaflet.layerGroup().addTo(map)
      this.toCheckCaseOt = new Leaflet.layerGroup().addTo(map)
      this.toHandleCaseNot = new Leaflet.layerGroup().addTo(map)
      this.toHandleCaseOt = new Leaflet.layerGroup().addTo(map)

      this.offlinePerson = new Leaflet.layerGroup().addTo(map)
      this.onlinePerson = new Leaflet.layerGroup().addTo(map)
    }
  }
}
</script>

<style lang="scss" scoped>

  .staticPanel {
    height: calc(100vh - 56px);
    overflow-y: auto;
  }

  .mapPanel {
    height: calc(100vh - 56px);
  }

  .baseMap {
    height: calc(100vh - 136px);
    width: 100%;
    border: 1px solid #DCDCDC;
    border-radius: 4px;
  }

  .mapOverLayer {
    background-color: rgba(240, 240, 240, 0.9);
    position: absolute;
    top: 76px;
    width: calc(75vw - 10px); /* 浮层所占的宽度 */
    height: 60px;
    margin-left: 0px;
    z-index: 999;
  }

  .rowSelect {
    margin-bottom: 10px;
  }
  .customLabel {
    padding: 0 12px 0 0;
    color: #606266;
    font-size: 14px;
  }
  .item {
    margin-bottom: 10px;
  }
  .item .notLastSelect {
    margin-right: 30px;
  }

  .caseStatisBlock {
    text-align: center;
    margin: 10px;
    color: #fff;
    font-weight: bold;
    letter-spacing: 1px;
    border-radius: 4px;
    padding: 15px 5px 5px;

    .bigNumber {
      font-size: 2rem;
    }
  }

  #today-visitor {
    background-image: linear-gradient(to right, #ff8fdf, #ff01b4);
  }
  #total-alarm {
    background-image: linear-gradient(to right, #7fbbff, #0078ff);
  }
  #online-device {
    background-image: linear-gradient(to right, #72ff7e, #01a411);
  }
  #total-device {
    background-image: linear-gradient(to right, #ffa800, #ce7e00);
  }
</style>

<style>
  .leaflet-container a.leaflet-popup-close-button {
    top: 5px; /* 修改弹窗关闭按钮样式 */
    right: 10px; /* 修改弹窗关闭按钮样式 */
  }

  .leaflet-popup-content .popup-div {
    font-size: 15px;
    padding: 6px;
  }

  .leaflet-popup-content .popup-title {
    font-weight: bold;
  }

  .leaflet-popup-content .dashed-line {
    border-bottom: 1px dashed #dddddd;
    margin: 10px -10px;
  }

  .leaflet-popup-content .popup-item {
    padding-bottom: 6px;
  }

  .leaflet-popup-content .popup-item label {
    font-weight: bold;
    padding-right: 15px;
  }
</style>