一、业务需求

  1. 展示出地图,并在指定的经纬度展示对应的图标,点击图标展示详细信息
  2. 有搜索功能,进行搜索时,重新根据搜索后的数据在地图进行标点

二、参考文档

地图开发商提供的文档(该文档中API是地图厂商基于 LeafletJS 封装的他们 gis 超图用到的 API _ 最终要在内网中使用该文档类似的只能内网用的文档进行开发 _ 内网不能访问互联网的地址)

  1. 开发平台 https://iclient.supermap.io/10.2.0/web/index.html
  2. 地图服务 https://iserver.supermap.io/iserver/services/map-world/rest/maps/World
  3. 可以展示出瓦片的地址示例:https://iserver.supermap.io/iserver/services/map-china400/rest/maps/China/tileImage.png?scale=2.1634093716513966e-7&x=103&y=50

LeafJS 参考文档

  1. 英文官网:https://leafletjs.com/
  2. 中文参考:https://www.imangodoc.com/19243.html

三、实现思路

  1. 在 vue 项目中实现,首先下载依赖
    npm install @supermap/iclient-leaflet: 超图依赖(地图厂商自己的 _ 版本 11.0.1)
    npm install leaflet:LeafletJS 依赖(不用下载,下载超图依赖的时候会自动带上该依赖)
  2. 生成一张地图(设置中心点、最大缩放级别、当前缩放级别等等)
  3. 根据搜索获取的数据中 经纬度 和 标点图标 在地图上进行标点
  4. 搜索数据时,先将所有标点清除,然后根据搜索后的数据重新进行标点

四、参考代码

4.1 安装依赖

npm install @supermap/iclient-leaflet

4.2 生成一张地图

  1. html 中要有一个放地图的盒子

    <div id="map"></div>
    
    #map {
      position: absolute;
      top: 0;
      bottom: 0;
      width: calc(100% - 20px);
      border: 2px solid red; // 调页面添加的样式,看的清晰一点,之后会去掉
    }
    
  2. public/index.html 中引入 css 文件

    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.5.1/leaflet.css" />
    <link rel="stylesheet" href="https://iclient.supermap.io/dist/leaflet/iclient-leaflet.min.css" />
    

    以上导入 css 文件办法有点麻烦,直接在需要使用到css文件的页面中导入 leaflet 依赖中的 css 即可

    import 'leaflet/dist/leaflet.css'
    

    【注意】一定要记得引入 css 文件,不然地图出来的瓦片不全,并且瓦片拼接很乱
    在这里插入图片描述

  3. 导入依赖

    import L from 'leaflet'
    import 'leaflet/dist/leaflet.css'
    import { tiledMapLayer } from '@supermap/iclient-leaflet'
    
  4. 生成一张地图

    var url = 'https://iserver.supermap.io/iserver/services/map-china400/rest/maps/China'
    // var url = 'https://iserver.supermap.io/iserver/services/map-china400/rest/maps/China_4326'
    // var url = 'https://iserver.supermap.io/iserver/services/map-china400/rest/maps/ChinaDark'
    this.map = L.map('map', { // this.map初始值为 null
      center: [22.822074, 108.371154], // 中心点[经度, 纬度]
      maxZoom: 18, // 最大层级
      zoom: 11 // 当前层级
    })
    tiledMapLayer(url).addTo(this.map) // 添加地图(tiledMapLayer() 方法是地图厂商自己封装的,我们也可以用 LeafletJS 自己的方法进行地图的添加)
    
  5. 地图初步展示
    在这里插入图片描述

4.3 标点_自定义标点图标

  1. 进行标点_初步实现
    /**** 标记图标 start ****/
    var marker = L.marker([22.822074, 108.371154], { // L.marker([纬度, 经度], {})
      // 此处数据之后在 e.options 中可以取到
      title: '标点标记的title',
      clickable: true, // 是否可以点击
      draggable: false, // 是否可以拖拽
      icon: L.icon({
        iconUrl: 'https://api.map.baidu.com/lbsapi/getpoint/Images/logo.gif', // 标点图标地址
        iconSize: [50, 50] // 图标大小
      })
    }).on('click', (e) => {
      // 点击事件
      console.log('e----', e)
      console.log('"点击图标"----', '点击图标')
    })
    marker.bindPopup(`<span style="color:red;">我是点击图标时弹出框展示的内容</span>`).openPopup() // 由于要取当前标点信息,下边添加信息用 (e)=>{} 而不是直接写模板字符串
    marker.addTo(this.map) // 为了方便清除标点,下边添加标注点不用该方法
    /**** 标记图标 end ****/
    
    /**** 官网 ****/
    // L.marker([51.5, -0.09])
    //   .addTo(this.map)
    //   .bindPopup('A pretty CSS3 popup.<br> Easily customizable.')
    //   .openPopup()
    
    /**** 地图厂商封装 ****/
    // var marker = L.marker([纬度, 经度], { icon: markerIcon }).on('click', function (e) {
    //   L.popup().setLatLng(e.latlng).setContent(innerHTML2).openOn(map) // 通过popup添加点击弹出框
    // })
    
  2. 地图标点标记初步展示(地图中心点处 “百度图标” 即为标的点)
    在这里插入图片描述
  3. 根据返回数据进行标点 _ 并对展示信息弹框进行样式处理
    methods:{
    	// 加点标注
        addMakersToMap() {
          let layers = [] // 为添加、清除图标做准备【方法二_1】
          this.markers.forEach((item) => { // this.markers 为接口返回的需要进行标点的数据
            var marker = new L.marker([item.wd, item.jd], {
              title: item.jgmc,
              clickable: true,
              draggable: false,
              // opacity: 0.5, // 不透明度
              info: item, // 当前标点数据,点击图标展示信息时用
              icon: L.icon({
                iconUrl: `/images/${this.jglxIconObj[item.jglx]}.png`,
                iconSize: [30, 36]
              })
            }).on('click', (e) => { console.log('"点击图标"----', '点击图标') })
            
            /**** 点击图标弹出信息框 start ****/
            marker
              .bindPopup((e) => {
                console.log('点击图标 e----', e)
                return `<div class="popup">
                  <p class="title">${ e.options.info.jgmc || '暂无' }</p>
                  <p><i class="iconfont ico-didian"></i><span>${ e.options.info.jgdz || '暂无' }</span></p>
                  <p><i class="iconfont ico-yewu"></i><span>${ e.options.info.kbyw || '暂无' }</span></p>
                  <p><i class="iconfont ico-dianhua"></i><span>${ e.options.info.lxdh || '暂无' }</span></p>
                  <p><i class="iconfont ico-shijian"></i><span>${ e.options.info.gzsj || '暂无' }</span></p>
                  <p><i class="iconfont ico-cheguansuo"></i><span>${ e.options.info.jls || '暂无' }</span></p>
                  ${ ['6', '7', '9', '11'].indexOf(e.options.info.jglx) > -1 ? `<p><i class="iconfont ico-yewuchuangkou"></i><span>${ e.options.info.cks || '暂无' }</span></p>` : '' }
                  ${ e.options.info.jglx === '1' ? `<p><i class="iconfont ico-diannao"></i><span>${ e.options.info.kws || '暂无' }</span></p>` : '' }
                  ${ ['2', '3'].indexOf(e.options.info.jglx) > -1 ? `<p><i class="iconfont ico-che"></i><span>${ e.options.info.kcs || '暂无' }</span></p>` : '' }
                  ${ e.options.info.jglx === '2' ? `<p><i class="iconfont ico-changdi"></i><span>${ e.options.info.ksxls || '暂无' }</span></p>` : '' }
                  ${ e.options.info.jglx === '3' ? `<p><i class="iconfont ico-malu"></i><span>${ e.options.info.ksxls || '暂无' }</span></p>` : '' }
                  ${ e.options.info.jglx === '4' ? `<p><i class="iconfont ico-jiancezhan"></i><span>${ e.options.info.jcxs || '暂无' }</span></p>` : '' }
                  ${ e.options.info.jglx === '5' ? `<p><i class="iconfont ico-chayan"></i><span>${ e.options.info.cytds || '暂无' }</span></p>` : '' }
                </div>`
              })
              .openPopup()
            /**** 点击图标弹出信息框 end ****/
    
            // marker.addTo(this.map) // 将图标添加到地图上【方法一】(由于还要清除图标,所以不用该方法添加图标,用方法二)
            layers.push(marker) // 【方法二_2】
          })
          this.layerGroup = L.layerGroup(layers); // this.layerGroup 初始值为 null【方法二_3】
          this.map.addLayer(this.layerGroup) // 将图标添加到地图上【方法二_4】
        },
    }
    
    <style lang='scss' scoped>
    #map {
      position: absolute;
      top: 0;
      bottom: 0;
      width: calc(100% - 20px);
    
      border: 2px solid red;
    }
    /* 去掉原本点击图标展示对话框样式 */
    ::v-deep .leaflet-popup-content-wrapper,
    ::v-deep .leaflet-popup-tip {
      background-color: transparent;
      box-shadow: unset;
    }
    ::v-deep .leaflet-popup-close-button {
      display: none;
    }
    </style>
    
    <style lang="scss">
    #MapHome {
      /* 自己写 点击图标显示图标信息 对话框的样式 */
      .popup {
        position: absolute;
        left: 65px;
        top: 10px;
        width: 294px;
        font-family: 'MicroSoft Yahei';
        background: rgba(#000, 0.6);
        color: #fff;
        font-size: 14px;
        line-height: 30px;
        letter-spacing: 2px;
        text-align: left;
        p {
          padding: 0 12px;
          span {
            display: inline-block;
            width: calc(100% - 30px);
            vertical-align: top;
          }
          .iconfont {
            display: inline-block;
            margin-right: 6px;
            color: rgba(#fff, 0.5);
            &.ico-chepai::before {
              margin-left: -4px;
            }
          }
        }
        .title {
          padding: 2px 12px;
          background: rgba(#fff, 0.2);
          font-size: 16px;
          line-height: 40px;
          text-align: center;
        }
        .btns {
          border-top: 1px solid rgba(#fff, 0.2);
          .el-button {
            display: inline-block;
            width: calc((100% - 17px) / 2);
            color: #3ac7e0;
            &.warning {
              color: #ffb555;
            }
          }
          .el-divider {
            background-color: rgba(#fff, 0.2);
          }
        }
        .popCorner {
          position: absolute;
          width: 0;
          height: 0;
          &.cornerRT {
            border-top: 10px solid transparent;
            border-bottom: 10px solid transparent;
            border-left: 10px solid rgba(#000, 0.46);
            right: -10px;
            top: 10px;
          }
          &.cornerRB {
            border-top: 10px solid transparent;
            border-bottom: 10px solid transparent;
            border-left: 10px solid rgba(#000, 0.6);
            right: -10px;
            bottom: 10px;
          }
          &.cornerLT {
            border-top: 10px solid transparent;
            border-bottom: 10px solid transparent;
            border-right: 10px solid rgba(#000, 0.46);
            left: -10px;
            top: 10px;
          }
          &.cornerLB {
            border-top: 10px solid transparent;
            border-bottom: 10px solid transparent;
            border-right: 10px solid rgba(#000, 0.6);
            left: -10px;
            bottom: 10px;
          }
        }
      }
    }
    </style>
    
  4. 根据返回数据进行标点展示
    在这里插入图片描述

4.4 清除标点

  • 侦听 jglxSeleList (复选框数据_处理成数组后进行搜索接口的传参) → 先判断是否有值
    • 有值_调用标记点的方法_清除所有标记点_获取新的数据_根据新的数据重新进行标记点
    • 无值_当前页面如果还有标记_清除所有标记
    watch() {
    	jglxSeleList: {
          handler(val) {
            if (val && this.map) {
              this.queryParam.jglx = this.jglx2Str(val) // 将所有的复选框数据转为一个数组
              
              if (this.queryParam.jglx.length === 0) {
                // this.map.iclear()
                if(this.layerGroup !== null) this.layerGroup.clearLayers() // 移除所有标记
              } else {
                this.fetchMonDeptConfigList() // 获取新的I标记点的数据
              }
            }
          },
          immediate: true,
          deep: true
        },
    },
    methods: {
    	fetchMonDeptConfigList() {
          console.log('获取机构list 11111----', 11111)
          if (this.map) {
            if(this.layerGroup !== null) this.layerGroup.clearLayers() // 移除所有标记
          }
          fetchMonDeptConfigList(this.queryParam).then((res) => {
            console.log('获取机构list res----', res)
            this.common.CheckCode(res, null, () => {
              if (res.data && res.data.length > 0) {
                this.markers = res.data // 获取到新的标记点数据
                this.addMakersToMap() // 重新添加标记点
                this.map.panTo(new L.LatLng(this.markers[0].wd, this.markers[0].jd))  // 重新设置中心点(第一个标点数据为中心点)
              } else {
                this.$message({
                  type: 'info',
                  message: '未查询到相关机构网点'
                })
              }
            })
          })
        },
    }
    

五、用到的 API

  1. 标点 let maeker = new L.marker()
    • 点击标点图标-展示内容:marker.bindPopup('展示文字').openPopup()
    • 添加图层的 group:this.layerGroup = L.layerGroup(layers);
    • 添加图层:this.map.addLayer(this.layerGroup)
    • 清除所有标记点:this.layerGroup.clearLayers()
    • 清除一个标记点:this.map.removeLayer(this.layer) 本文未涉及
      • this.layer 是 L.polygon([[经度, 纬度],...], {color:'blue',fillColor:'none',opacity: 0.2 } (经纬度是二维数据,实际是化线的api,opacity:透明度,fileColor:填充颜色,color:边框颜色)
      • 添加标记点 this.map.addLayer(this.layer);
  2. 设置地图中心点
    • this.map.panTo(new L.LatLng(纬度, 经度))

六、整体代码 + 页面展示

  • 整体代码
    <!-- 地图 - new - 参照地图厂商demo写的 -->
    <template>
      <div id="MapHome">
        <div class="queryDiv">
          <el-input
            placeholder="通过机构关键字查询"
            suffix-icon="iconfont ico-chaxun"
            v-model="jggjz"
          ></el-input>
        </div>
    
        <el-checkbox-group v-model="jglxSeleList">
          <el-collapse v-model="activeNames" class="jglxCon" accordion>
            <el-collapse-item v-for="(item, index) in jglxList" :name="item.name" :key="item.key">
              <template slot="title">
                <el-checkbox
                  class="checkAll"
                  v-model="item.allCheck"
                  :checked="item.allCheck"
                  @change="handleCheckAll(item.allCheck, index)"
                  :label="item.key"
                ></el-checkbox>
                <p class="title">
                  <span class="pdl20">{{ item.name }}</span>
                  <span class="floatR">{{ item.count }}</span>
                </p>
              </template>
              <el-checkbox
                v-for="obj in item.xllist"
                :key="obj.key"
                :label="obj.key"
                name="jglxSeleList"
              >
                <i :class="['iconfont', obj.icon]"></i>
                <span>{{ obj.name }}</span>
                <span class="floatR">{{ obj.count }}</span>
              </el-checkbox>
              <!--  -->
            </el-collapse-item>
          </el-collapse>
        </el-checkbox-group>
        <!-- <cdt-map @result="mapInit"></cdt-map> -->
        <div id="map"></div>
      </div>
    </template>
    
    <script>
    import { mapGetters } from 'vuex'
    // import CdtMap from '@/components/cdt-map/index'
    import { fetchMonDeptConfigList, queryMonDeptCount } from '@/api/service.js'
    
    import L from 'leaflet'
    import 'leaflet/dist/leaflet.css'
    import { tiledMapLayer } from '@supermap/iclient-leaflet'
    
    export default {
      name: 'Map',
      components: {
        // CdtMap
      },
      data() {
        return {
          map: null,
          jggjz: '', // 机构关键字
          markers: [],
          jgdlList: [],
          jglxList: [],
          jglxIconObj: {},
          jglxSeleList: [], // 选中显示的机构类型
          activeNames: [], // 下拉框展开
          activeItem: {}, // 选中的网点
          cornerType: '', // 弹框角标
          flag1: false, //
          flag2: false, // 地图初始化成功
          queryParam: {}, // 查询条件
          layerGroup: null // 地图标点图标
        }
      },
      computed: {
        ...mapGetters(['sysList'])
      },
      watch: {
        jglxSeleList: {
          handler(val) {
            if (val && this.map) {
              this.queryParam.jglx = this.jglx2Str(val)
    
              if (this.queryParam.jglx.length === 0) {
                if (this.layerGroup !== null) this.layerGroup.clearLayers() // 移除所有标记
              } else {
                this.fetchMonDeptConfigList()
              }
            }
          },
          immediate: true,
          deep: true
        },
        jggjz: {
          handler(val) {
            if (this.map) {
              if (val) {
                this.queryParam = {
                  jgmc: val
                }
              } else {
                this.queryParam = {
                  jglx: this.jglx2Str(this.jglxSeleList)
                }
              }
              this.fetchMonDeptConfigList()
            }
          },
          immediate: true,
          deep: true
        }
      },
    
      mounted() {
        this.queryMonDeptCount() // 地图首页机构数量统计接口
        this.mapInit()
      },
    
      methods: {
        mapInit() {
          console.log('"init start"----', 'init start')
          var url = 'https://iserver.supermap.io/iserver/services/map-china400/rest/maps/China'
          // var url = 'https://iserver.supermap.io/iserver/services/map-china400/rest/maps/China_4326'
          // var url = 'https://iserver.supermap.io/iserver/services/map-china400/rest/maps/ChinaDark'
          this.map = L.map('map', {
            center: [22.822074, 108.371154],
            maxZoom: 18,
            zoom: 11
          })
    
          tiledMapLayer(url).addTo(this.map)
          console.log('"init end"----', 'init end')
        },
    
        // 加点标注
        addMakersToMap() {
          let layers = [] // 为清除图标做准备
          this.markers.forEach((item) => {
            var marker = new L.marker([item.wd, item.jd], {
              title: item.jgmc,
              clickable: true,
              draggable: false,
              // opacity: 0.5, // 不透明度
              info: item, // 当前标点数据,点击图标展示信息时用
              icon: L.icon({
                iconUrl: `/images/${this.jglxIconObj[item.jglx]}.png`,
                iconSize: [30, 36]
              })
            }).on('click', (e) => {
              console.log('e----', e)
              console.log('"点击图标"----', '点击图标')
            })
    
            /**** 点击图标弹出信息框 start ****/
            marker
              .bindPopup((e) => {
                console.log('点击图标 e----', e)
                return `<div class="popup">
                  <p class="title">${ e.options.info.jgmc || '暂无' }</p>
                  <p><i class="iconfont ico-didian"></i><span>${ e.options.info.jgdz || '暂无' }</span></p>
                  <p><i class="iconfont ico-yewu"></i><span>${ e.options.info.kbyw || '暂无' }</span></p>
                  <p><i class="iconfont ico-dianhua"></i><span>${ e.options.info.lxdh || '暂无' }</span></p>
                  <p><i class="iconfont ico-shijian"></i><span>${ e.options.info.gzsj || '暂无' }</span></p>
                  <p><i class="iconfont ico-cheguansuo"></i><span>${ e.options.info.jls || '暂无' }</span></p>
                  ${ ['6', '7', '9', '11'].indexOf(e.options.info.jglx) > -1 ? `<p><i class="iconfont ico-yewuchuangkou"></i><span>${ e.options.info.cks || '暂无' }</span></p>` : '' }
                  ${ e.options.info.jglx === '1' ? `<p><i class="iconfont ico-diannao"></i><span>${ e.options.info.kws || '暂无' }</span></p>` : '' }
                  ${ ['2', '3'].indexOf(e.options.info.jglx) > -1 ? `<p><i class="iconfont ico-che"></i><span>${ e.options.info.kcs || '暂无' }</span></p>` : '' }
                  ${ e.options.info.jglx === '2' ? `<p><i class="iconfont ico-changdi"></i><span>${ e.options.info.ksxls || '暂无' }</span></p>` : '' }
                  ${ e.options.info.jglx === '3' ? `<p><i class="iconfont ico-malu"></i><span>${ e.options.info.ksxls || '暂无' }</span></p>` : '' }
                  ${ e.options.info.jglx === '4' ? `<p><i class="iconfont ico-jiancezhan"></i><span>${ e.options.info.jcxs || '暂无' }</span></p>` : '' }
                  ${ e.options.info.jglx === '5' ? `<p><i class="iconfont ico-chayan"></i><span>${ e.options.info.cytds || '暂无' }</span></p>` : '' }
                  <div :class="['popCorner', cornerType]"></div>
                </div>`
              })
              .openPopup()
            /**** 点击图标弹出信息框 end ****/
    
            // marker.addTo(this.map) // 将图标添加到地图上【方法一】(由于还要清除图标,所以不用该方法添加图标)
            layers.push(marker)
          })
          this.layerGroup = L.layerGroup(layers)
          this.map.addLayer(this.layerGroup) // 将图标添加到地图上【方法二】
        },
    
        // 地图首页机构数量统计接口
        queryMonDeptCount() {
          queryMonDeptCount().then((res) => {
            this.common.CheckCode(res, null, () => {
              let jglxList = res.data
              jglxList.map((item) => {
                return (item.allCheck = true)
              })
              this.jglxList = jglxList
              this.jglxList.forEach((item) => {
                this.jgdlList.push(item.key)
                item.xllist.forEach((obj) => {
                  this.jglxSeleList.push(obj.key)
                  this.jglxIconObj[obj.key] = obj.icon
                })
              })
              this.flag1 = true
            })
          })
        },
    
        // 获取机构list
        fetchMonDeptConfigList() {
          if (this.map) {
            if (this.layerGroup !== null) this.layerGroup.clearLayers() // 移除所有标记
          }
          fetchMonDeptConfigList(this.queryParam).then((res) => {
            this.common.CheckCode(res, null, () => {
              if (res.data && res.data.length > 0) {
                this.markers = res.data
                this.addMakersToMap()
                this.map.panTo(new L.LatLng(this.markers[0].wd, this.markers[0].jd)) // 取第一条标点数据的经纬度作为地图的中心点
              } else {
                this.$message({
                  type: 'info',
                  message: '未查询到相关机构网点'
                })
              }
            })
          })
        },
    
        // 获取jglx to string
        jglx2Str(val) {
          let jglx = val.filter((item) => {
            if (this.jgdlList.indexOf(item) < 0) {
              return item
            }
          })
          return jglx.join(',')
        },
    
        addArr(val) {
          let flag = false
          for (let i = 0; i < this.jglxSeleList.length; i++) {
            if (this.jglxSeleList[i] === val) {
              flag = true
              break
            }
          }
          if (!flag) {
            this.jglxSeleList.push(val)
          }
        },
        cutArr(val) {
          for (let i = 0; i < this.jglxSeleList.length; i++) {
            if (this.jglxSeleList[i] === val) {
              this.jglxSeleList.splice(i, 1)
              break
            }
          }
        },
        // 全选/反选
        handleCheckAll(val, index) {
          this.jglxList[index].allCheck = !val
          if (this.jglxList[index].allCheck) {
            this.jglxList[index].xllist.forEach((row) => {
              this.addArr(row.key)
            })
          } else {
            this.jglxList[index].xllist.forEach((row) => {
              this.cutArr(row.key)
            })
          }
          this.$forceUpdate()
        }
      }
    }
    </script>
    
    <style lang='scss' scoped>
    #map {
      position: absolute;
      top: 0;
      bottom: 0;
      width: calc(100% - 20px);
    
      border: 2px solid red;
    }
    /* 去掉原本点击图标展示对话框样式 */
    ::v-deep .leaflet-popup-content-wrapper,
    ::v-deep .leaflet-popup-tip {
      background-color: transparent;
      box-shadow: unset;
    }
    ::v-deep .leaflet-popup-close-button {
      display: none;
    }
    </style>
    
    <style lang="scss">
    #MapHome {
      $colorList: #063b7f, #4a7feb, #33a8e4, #1da8ad, #e88e19, #e75748;
      $color1: #063b7f;
      $color2: #4a7feb;
      $color3: #33a8e4;
      $color4: #1da8ad;
      $color5: #e88e19;
      $color6: #e75748;
      width: 100%;
      height: 100%;
      .queryDiv {
        position: absolute;
        left: 80px;
        top: 20px;
        width: 380px;
        z-index: 999;
        .el-input {
          .el-input__inner {
            border: none;
            padding-right: 60px;
            height: 60px;
            box-shadow: 3px 3px 5px #ccc;
          }
          .el-input__suffix {
            right: 12px;
            .el-input__icon {
              width: 45px;
              font-size: 42px;
              line-height: 60px;
            }
          }
        }
      }
      .jglxCon {
        position: absolute;
        left: 80px;
        top: 96px;
        width: 226px;
        z-index: 999;
        .el-collapse-item {
          margin-bottom: 8px;
          .el-collapse-item__header {
            height: 40px;
            border-radius: 8px;
            border-bottom: none !important;
            padding: 0 10px;
            color: #fff;
            &.is-active {
              height: 40px;
              border-bottom: 1px solid rgba(#fff, 0.5) !important;
              border-radius: 8px 8px 0 0;
              transition: transform 0.3s;
            }
            .title {
              width: calc(100% - 16px);
              text-align: left;
            }
            .pdl20 {
              padding-left: 26px;
            }
            .el-collapse-item__arrow {
              position: absolute;
              left: 30px;
            }
            .el-checkbox__label {
              // color: transparent!important;
              display: none;
            }
            .el-checkbox.is-checked {
              background: none !important;
            }
          }
          .el-collapse-item__wrap {
            // padding: 6px 0;
            border-radius: 0 0 8px 8px;
            .el-checkbox {
              display: block;
              margin-right: 0;
              padding: 0 10px;
              width: 100%;
              text-align: left;
              color: #fff;
              letter-spacing: 2px;
              line-height: 30px;
              .el-checkbox__label {
                padding-left: 6px;
                width: calc(100% - 16px);
                .iconfont {
                  display: inline-block;
                  width: 20px;
                  color: rgba(#fff, 0.5);
                }
              }
            }
          }
          .el-checkbox.is-checked {
            .el-checkbox__label,
            .iconfont {
              color: #fff;
            }
            .el-checkbox__inner {
              background-color: #fff;
              border-color: #fff;
            }
          }
          @each $c in $colorList {
            $i: index($colorList, $c); // 获取索引
            &:nth-child(#{$i}) {
              .el-collapse-item__header {
                background: $c;
              }
              .el-collapse-item__wrap {
                background: rgba($c, 0.7);
              }
              .el-checkbox.is-checked {
                background: rgba($c, 0.85);
                .el-checkbox__inner::after {
                  border-color: $c;
                }
              }
            }
          }
        }
      }
      .popup {
        position: absolute;
        left: 65px;
        top: 10px;
        width: 294px;
        font-family: 'MicroSoft Yahei';
        background: rgba(#000, 0.6);
        color: #fff;
        font-size: 14px;
        line-height: 30px;
        letter-spacing: 2px;
        text-align: left;
        p {
          padding: 0 12px;
          span {
            display: inline-block;
            width: calc(100% - 30px);
            vertical-align: top;
          }
          .iconfont {
            display: inline-block;
            margin-right: 6px;
            color: rgba(#fff, 0.5);
            &.ico-chepai::before {
              margin-left: -4px;
            }
          }
        }
        .title {
          padding: 2px 12px;
          background: rgba(#fff, 0.2);
          font-size: 16px;
          line-height: 40px;
          text-align: center;
        }
        .btns {
          border-top: 1px solid rgba(#fff, 0.2);
          .el-button {
            display: inline-block;
            width: calc((100% - 17px) / 2);
            color: #3ac7e0;
            &.warning {
              color: #ffb555;
            }
          }
          .el-divider {
            background-color: rgba(#fff, 0.2);
          }
        }
        .popCorner {
          position: absolute;
          width: 0;
          height: 0;
          &.cornerRT {
            border-top: 10px solid transparent;
            border-bottom: 10px solid transparent;
            border-left: 10px solid rgba(#000, 0.46);
            right: -10px;
            top: 10px;
          }
          &.cornerRB {
            border-top: 10px solid transparent;
            border-bottom: 10px solid transparent;
            border-left: 10px solid rgba(#000, 0.6);
            right: -10px;
            bottom: 10px;
          }
          &.cornerLT {
            border-top: 10px solid transparent;
            border-bottom: 10px solid transparent;
            border-right: 10px solid rgba(#000, 0.46);
            left: -10px;
            top: 10px;
          }
          &.cornerLB {
            border-top: 10px solid transparent;
            border-bottom: 10px solid transparent;
            border-right: 10px solid rgba(#000, 0.6);
            left: -10px;
            bottom: 10px;
          }
        }
      }
    }
    </style>
    
  • 效果展示
    在这里插入图片描述

七、报错

打包后 - 报错1: SyntaxError: Unexpected token ...

  • 以上代码在互联网环境运行正常,在内网环境会报如下错误
    在这里插入图片描述

  • 找了一天原因,发现是使用的地图厂商的依赖 @supermap/iclient-leaflet 的问题,导致的报错 Uncaught SyntaxError: Unexpected token ... (这个报错是提示有语法错误)

  • 报错原因
    @supermap/iclient-leaflet 依赖有用ES6语法,项目打包的时候需要进行配置

  • 解决

    1. 进行配置:项目打包时进行语法转换(地图厂商提供的官网截图↓↓↓)
      在这里插入图片描述
    • 首先安装依赖 npm i babel-loader @babel/core @babel/preset-env @babel/plugin-transform-runtime -D 然后进行配置
      在这里插入图片描述
      const fs = require('fs')
      const webpack = require('webpack')
      const path = require('path')  // 【主要代码】
      
      const APP_Version = 'V1.0.1'
      const Timestamp = new Date().getTime();  //当前时间为了防止打包缓存不刷新,所以给每个js文件都加一个时间戳
      module.exports = {
          // 基本路径
          publicPath: './',
          // 输出文件目录
          outputDir: 'superviseAppWeb',
      
          // 放置生成的静态资源 (js、css、img、fonts) 的 (相对于 outputDir 的) 目录。
          // assetsDir: '',
      
          //指定生成的 index.html 的输出路径 (相对于 outputDir)。也可以是一个绝对路径。
          indexPath: 'index.html',
      
          // 是否使用包含运行时编译器的 Vue 构建版本。设置为 true 后你就可以在 Vue 组件中使用 template 选项了,但是这会让你的应用额外增加 10kb 左右。
          runtimeCompiler: true,
      
          // 默认情况下 babel-loader 会忽略所有 node_modules 中的文件。如果你想要通过 Babel 显式转译一个依赖,可以在这个选项中列出来。
          transpileDependencies: [],
      
          // 如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建。
          productionSourceMap: false,
      
          // eslint-loader 是否在保存的时候检查
          lintOnSave: true,
      
          // webpack配置
          // 如果这个值是一个对象,则会通过 webpack-merge 合并到最终的配置中。
          // 如果这个值是一个函数,则会接收被解析的配置作为参数。该函数及可以修改配置并不返回任何东西,也可以返回一个被克隆或合并过的配置版本。
          // see https://github.com/vuejs/vue-cli/blob/dev/docs/webpack.md
          chainWebpack: () => {
          },
          configureWebpack: {
              plugins: [
                  new webpack.DefinePlugin({
                      'process.env': {
                          BUILD_LOCAL: JSON.stringify(process.env.BUILD_LOCAL)
                      }
                  }),
                  // new webpack.ProvidePlugin({
                  //     $: "jquery",
                  //     jQuery: "jquery",
                  //     "windows.jQuery": "jquery"
                  // })
              ],
              output: { // 输出重构  打包编译后的 文件名称  【模块名称.版本号.时间戳】
                  filename: `[name].${APP_Version}.${Timestamp}.js`,
                  chunkFilename: `[name].${APP_Version}.${Timestamp}.js`
              },
              /**** 【主要代码】@supermap/iclient-leaflet 依赖打包配置(npm安装依赖) start ****/
              module: {
                rules: [{
                  // 使用babel-loader将ES6语法转换为ES5
                  test: /\.js$/,
                  include: [
                      path.resolve(__dirname, "node_modules/@supermap/iclient-common"),
                      path.resolve(__dirname, "node_modules/@supermap/iclient-leaflet"),
                      // 由于iClient对Elasticsearch的API进行了封装而Elasticsearch也使用了ES6的语法
                      path.resolve(__dirname, "node_modules/elasticsearch"),
                  ],
                  loader: 'babel-loader',
                  options: {
                    presets: ['@babel/preset-env'],
                    plugins: [
                        [
                            '@babel/plugin-transform-runtime',
                            {
                                absoluteRuntime: false,
                                corejs: false,
                                helpers: false,
                                regenerator: true,
                                useESModules: false
                            }
                        ]
                    ]
                  }
                }]
              }
              /**** @supermap/iclient-leaflet 依赖打包配置(npm安装依赖) end ****/
          },
          // vue-loader 配置项
      
          // css相关配置
          css: {
              // 是否使用css分离插件 ExtractTextPlugin
              extract: false,
              // 开启 CSS source maps?
              sourceMap: false,
              // css预设器配置项
              loaderOptions: {
                  sass: {
                      data: fs.readFileSync('src/styles/variables.scss', 'utf-8')
                  }
              },
              // 启用 CSS modules for all css / pre-processor files.
              modules: false
          },
      
          // 是否为 Babel 或 TypeScript 使用 thread-loader。该选项在系统的 CPU 有多于一个内核时自动启用,仅作用于生产构建。
          parallel: require('os').cpus().length > 1,
      
          // PWA 插件相关配置
          // see https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-pwa
          pwa: {},
      
          // webpack-dev-server 相关配置
          devServer: {
              host: '0.0.0.0',
              port: 8080,
              https: false,
              hotOnly: false,
              open: true, // 自动打开浏览器
              proxy: {
                  "/etm/": {
                      target: "http://192.168.31.125:8094/etm/", // 缉查布控地图服务
                      changeOrigin: true,
                      pathRewrite: {"^/etm/": ""}
                  },
                  '/': {
                      ws: false, // proxy websockets
                      target: 'http://192.168.0.26:9000',
                      // target: 'http://192.168.11.209:9381', // 郑胡训
                      // target: 'http://192.168.11.139:9181', // 何斌
                    
                      // target: 'http://192.168.0.42:9999', // 获取token
                      // target: 'http://192.168.0.42:9181', 
      
                      // target: 'http://192.168.20.37:9181',
                      pathRewrite: {'^/': ''},
                      changeOrigin: true,
                      secure: false
                  }
              }
          },
          // 第三方插件配置
          pluginOptions: {
              // ...
          },
      }
      
    1. 不使用地图厂商的依赖 @supermap/iclient-leaflet 使用 leaflet 依赖(也就是不使用超图封装的 tiledMapLayer() 方法,用 LeafletJS 自己的 TileLayer() 方法) 【没有试过】

打包后 - 报错2:TypeError: Cannot assign to read only property 'export' of object '#<Object>'

  • 报错1解决之后出现报错2
    在这里插入图片描述
    百度发现是 @babel/plugin-transform-runtime 依赖的问题,需要在 babel.config.js 添加 sourceType: 'unambiguous'
    // babel.config.js文件
    module.exports = {
      presets: [
        '@vue/app'
      ],
      plugins: ['@babel/plugin-proposal-nullish-coalescing-operator'],
      sourceType: 'unambiguous' // 【主要代码】
    }
    
    参考:https://blog.csdn.net/qq_41619796/article/details/108076869

内网 - 报错3:SyntaxError: Unexpected token , in JSON at position 10

在这里插入图片描述

Logo

前往低代码交流专区

更多推荐