vue2+高德地图实现区域采点功能

前言

最近业务中遇到一个需求,要把一些小区的边界画在地图上,而客户是没有坐标提供的,那么就只能自己在地图上将小区边界画出来。这里使用的是高德地图,并没有找到相关的可以直接画边界的功能,所以这里采用 绘制覆盖物 + 覆盖物编辑 来实现效果。
vue2+高德地图实现区域采点功能_第1张图片
效果可参考线上地址

步骤

这里大概解释一下步骤,查看详细代码可以直接跳过

  1. 创建vue2项目,添加 JSAPI Loader 依赖
  2. 初始化地图
  3. 添加 鼠标工具-绘制覆盖物
  4. 添加 多边形编辑器
  5. 添加 右键菜单
  6. 添加 关键字搜索 (非必须)

详细代码

<template>
  <div id="app">
    <div class="container">
      <!-- 地图-底层 -->
      <div id="amap" style="width: 100%; height: 100%;"></div>
      <div class="search">
        <el-input v-model="keywords" placeholder="请输入关键词"></el-input>
        <el-button type="primary" style="margin-left: 10px;" @click="onSearch">搜索</el-button>
      </div>
      <div id="panel"></div>
      <div class="input-card">
        <div class="input-item" @change="drawOverlay(selectStatus)">
          <input type="radio" name='func' value='polyline' v-model="selectStatus"><span class="input-text">画折线</span>
          <input type="radio" name='func' value='polygon' v-model="selectStatus"><span class="input-text" style='width:5rem;'>画多边形</span>
        </div>
      </div>
    </div>

    <el-dialog title="保存区划" :visible.sync="dialogFormVisible" width="30%">
      <el-form :model="form">
        <el-form-item label="小区名称" label-width="120px">
          <el-select v-model="form.xqbh" placeholder="请选择小区">
            <el-option :label="item.name" :value="item.value" v-for="item in xqList" :key="item.value"></el-option>
          </el-select>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogFormVisible = false">取 消</el-button>
        <el-button type="primary" @click="onConfirm">确 定</el-button>
      </div>
    </el-dialog>
  </div>
</template>
<script>
import { getXq, saveLnglat } from '@/apis'
import AMapLoader from '@amap/amap-jsapi-loader'

export default {
  name: 'App',
  data() {
    return {
      xqList: [
        {
          name: '测试小区A',
          value: 'A',
        },
        {
          name: '测试小区B',
          value: 'B',
        },
      ],
      _AMap: null,
      amap: null,
      placeSearch: null, // 地点查询类实例
      keywords: '', // 关键词查询
      mouseTool: null, // 鼠标工具插件
      contextMenu: null, // 右键菜单
      polyEditor: null, // 多边形编辑器
      selectStatus: '', // 右下角选择模式
      curPath: [], // 当前要保存的区划

      dialogFormVisible: false,
      form: {
        xqbh: '',
      },
    }
  },
  mounted() {
    this.initAMap()
  },
  methods: {
    // 初始化高德地图
    initAMap() {
      AMapLoader.load({
        key: '9714666324f9aa888a63e7719f0abdfb', // 申请好的Web端开发者Key,首次调用 load 时必填
        version: '2.0', // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
        plugins: ['AMap.MouseTool', 'AMap.PolygonEditor', 'AMap.PlaceSearch'], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
      }).then((AMap) => {
        this._AMap = AMap
        this.amap = new AMap.Map('amap', {
          center: [116.400274, 39.905812], //初始中心经纬度
          zoom: 14, //地图显示的缩放级别,可以设置为浮点数;若center与level未赋值,地图初始化默认显示用户所在城市范围。
          zooms: [5, 20], //地图显示的缩放级别范围, 默认为 [2, 20] ,取值范围 [2 ~ 30]
          // rotation: -15, //地图顺时针旋转角度,取值范围 [0-360] ,默认值:0
          // pitch: 42, //俯仰角度,默认 0,最大值根据地图当前 zoom 级别不断增大,2D地图下无效 。
          // viewMode: '3D', //开启3D视图,默认为关闭
        })

        this.initArea(AMap)
        this.initPlaceSearch(AMap)
        this.initContextMenu(AMap)
        this.initMouseTool(AMap)
      })
    },
    // 初始化小区边界
    initArea(AMap) {
      let polygonArray = [
        {
          xqbh: 'A',
          lnglat: [
            [121.036125, 31.373269],
            [121.038342, 31.373319],
            [121.038399, 31.371219],
            [121.037822, 31.371249],
            [121.037487, 31.371239],
            [121.037245, 31.371475],
            [121.036183, 31.371574],
          ],
        },
        {
          xqbh: 'B',
          lnglat: [
            [121.04289, 31.37323],
            [121.044841, 31.373289],
            [121.044021, 31.370046],
            [121.042682, 31.370007],
            [121.042786, 31.371446],
          ],
        },
      ]

      polygonArray.forEach((item) => {
        let polygon = new AMap.Polygon({
          path: item.lnglat,
          fillColor: '#ccebc5',
          strokeOpacity: 1,
          fillOpacity: 0.5,
          strokeColor: '#2b8cbe',
          strokeWeight: 1,
          strokeStyle: 'dashed',
          strokeDasharray: [5, 5],
        })
        polygon.on('click', () => {
          this.polyEditor && this.polyEditor.close()
          this.polyEditor = new AMap.PolygonEditor(this.amap, polygon)
          this.polyEditor.open()
        })
        polygon.on('rightclick', (e) => {
          this.form.xqbh = item.xqbh
          this.curPath = e.target._opts.path
          this.contextMenu.open(this.amap, e.lnglat)
        })
        this.amap.add(polygon)
      })

      this.amap.setFitView()
    },
    // 初始化地点查询
    initPlaceSearch(AMap) {
      this.placeSearch = new AMap.PlaceSearch({
        pageSize: 1, // 单页显示结果条数
        pageIndex: 1, // 页码
        map: this.amap, // 展现结果的地图实例
        panel: 'panel', // 结果列表将在此容器中进行展示。
        autoFitView: true, // 是否自动调整地图视野使绘制的 Marker点都处于视口的可见范围
      })
    },
    // 初始化右键菜单
    initContextMenu(AMap) {
      // 创建一个右键菜单实例
      this.contextMenu = new AMap.ContextMenu()
      //右键放大
      this.contextMenu.addItem(
        '放大一级',
        () => {
          this.amap.zoomIn()
        },
        0
      )
      //右键放大
      this.contextMenu.addItem(
        '缩小一级',
        () => {
          this.amap.zoomOut()
        },
        0
      )
      this.contextMenu.addItem(
        '保存',
        () => {
          this.contextMenu.close()
          this.dialogFormVisible = true
        },
        0
      )
    },
    // 初始化鼠标工具
    initMouseTool(AMap) {
      this.mouseTool = new AMap.MouseTool(this.amap)
      this.mouseTool.on('draw', (overlay) => {
        // 根据鼠标绘制的覆盖物 生成Polygon实例
        let polygon = new AMap.Polygon({
          path: overlay.obj._opts.path,
          fillColor: '#ccebc5',
          strokeOpacity: 1,
          fillOpacity: 0.5,
          strokeColor: '#2b8cbe',
          strokeWeight: 1,
          strokeStyle: 'dashed',
          strokeDasharray: [5, 5],
        })
        // 多边形-左键操作
        polygon.on('click', () => {
          this.polyEditor && this.polyEditor.close()
          this.polyEditor = new AMap.PolygonEditor(this.amap, polygon)
          this.polyEditor.open()
        })
        // 多边形-右键操作
        polygon.on('rightclick', (e) => {
          this.form.xqbh = ''
          this.curPath = e.target._opts.path
          this.contextMenu.open(this.amap, e.lnglat)
        })
        this.amap.add(polygon)

        this.mouseTool.close(true) //关闭,并清除覆盖物
        this.clearRadio()
      })
    },
    // 关键词搜索
    onSearch() {
      this.placeSearch.search(this.keywords)
    },
    /**
     * 绘制覆盖物
     * @param type 类型
     * return void
     */
    drawOverlay(type) {
      switch (type) {
        case 'polyline': {
          this.mouseTool.polyline({
            strokeColor: '#80d8ff',
          })
          break
        }
        case 'polygon': {
          this.mouseTool.polygon({
            fillColor: '#00b0ff',
            strokeColor: '#80d8ff',
          })
          break
        }
      }
    },
    // 清空选中状态
    clearRadio() {
      let radios = document.querySelectorAll("input[name='func']")
      radios.forEach((item) => {
        if (item.checked) {
          item.checked = false
        }
      })
    },
    // 确认提交
    onConfirm() {
      alert(this.curPath)
      console.log(this.curPath)
      this.dialogFormVisible = false
    },
  },
}
</script>

<style lang="less" scoped>
#app {
  width: 100vw;
  height: 100vh;

  .container {
    position: relative;
    width: 100%;
    height: 100%;
    margin: 0 auto;

    .search {
      position: absolute;
      top: 10px;
      left: 10px;
      display: flex;
      align-items: center;
      background-color: #fff;
    }

    #panel {
      position: absolute;
      background-color: white;
      max-height: 90%;
      overflow-y: auto;
      top: 10px;
      right: 10px;
      width: 280px;
    }

    .input-card {
      display: flex;
      flex-direction: column;
      word-wrap: break-word;
      background-color: #fff;
      border-radius: 0.25rem;
      border-width: 0;
      border-radius: 0.4rem;
      box-shadow: 0 2px 6px 0 rgb(114 124 245 / 50%);
      position: fixed;
      bottom: 1rem;
      right: 1rem;
      -ms-flex: 1 1 auto;
      flex: 1 1 auto;
      padding: 0.75rem 1.25rem;

      .btn {
        width: 6rem;
        margin: 0 1rem 0 2rem;
        display: inline-block;
        font-weight: 400;
        text-align: center;
        white-space: nowrap;
        vertical-align: middle;
        -webkit-user-select: none;
        -moz-user-select: none;
        -ms-user-select: none;
        user-select: none;
        border: 1px solid transparent;
        transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out,
          border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
        background-color: transparent;
        background-image: none;
        color: #25a5f7;
        border-color: #25a5f7;
        padding: 0.25rem 0.5rem;
        line-height: 1.5;
        border-radius: 1rem;
        -webkit-appearance: button;
        cursor: pointer;
      }

      .input-text {
        width: 4rem;
        margin-right: 1rem;
      }
    }
  }
}
</style>

完整DEMO发布在Gitee仓库

你可能感兴趣的:(Vue,vue.js)