1

需求 & 图例

标题功能分为2部分的需求:

  1. 【原需求】填写地址:省市区(下拉)+详情(输入),
    【新需求】原需求保持不变,但是增加快捷方式选择地址,a.增加地图搜索选中后 回填地址 b.常用地址/历史地址 列表,选中后 回填地址;
图示说明:
所有图示案例均为代码功能展示用,不考虑美观性。

图示1:
image.png

图示2:
image.png

  1. 地理编码:地址转为经纬度;逆地理编码:经纬度转为地址;
    【需求】:地图打开中心点默认在ip所在的城市;根据历史GPS数据展示车辆的历史轨迹信息,点击轨迹点展示信息窗体,右侧展示中显示当前车辆的所在的经纬度对应的详细地址;
    图示3:(中心点默认在ip所在的城市)
    image.png

图示4:(根据GPS数据展示车辆历史轨迹,基于后端返回的数据展示)
image.png

图示5:(前端模拟数据,展示轨迹,点击展示信息窗体,右侧展示逆地理编码信息)
image.png

图示6:(根据地址信息转为经纬度,标记在地图上)
image.png

main.js

import VueAMap from 'vue-amap'; // 命名尽量与AMap做区分

Vue.use(VueAMap); 

VueAMap.initAMapApiLoader({
  key: 'your key',
  plugin: [
    "AMap.Autocomplete",
    "AMap.PlaceSearch",
    "AMap.Scale",
    "AMap.ToolBar",
    "AMap.OverView",
    'AMap.MapType',
    'AMap.PolyEditor',
    'AMap.CircleEditor',
    "AMap.Geolocation",
    "AMap.Geocoder",
    "AMap.CitySearch",
  ],
  v: '1.4.15',
  uiVersion: "1.1"
});
window._AMapSecurityConfig = {
  securityJsCode: 'your security js code',
}
结构以图例1、2 为基础,其他的功能仅以逻辑为主,以说明为准
所有样式代码均省略
省市区下拉是通过访问后端接口,这里需要将地图选点中获得的省市区的名字传给后端,得到下拉需要的codes
常用/历史 选点列表是通过后端接口获得,接口逻辑略,只展示数据结构

map-dialog.vue

template

<!-- 弹窗相关代码略 -->
<div class="flex">
  <!-- 左边 -->
  <el-amap
    vid="amapContainer"
    :center="amap.center"
    :plugin="amap.plugin"
    :zoom="amap.zoom"
    :zoomEnable="true"
    class="amap" 
  >
    <!-- 搜索功能 -->
    <el-amap-search-box
      :search-option="searchOption"
      :on-search-result="onSearchResult"
      class="search-box"
      v-if="params.show"
    >
    </el-amap-search-box>
    <!-- 搜索后得到的点,可拖拽重新选点,点击确认选点 -->
    <el-amap-marker
      :clickable="true"
      :draggable="true"
      :events="amap.events"
      :position="amap.marker"
    />
  </el-amap>
  <!-- 右边 -->
  <el-tabs v-model="activeName" type="border-card">
    <el-tab-pane label="常用" name="first">
    </el-tab-pane>
    <el-tab-pane label="历史" name="second">
    </el-tab-pane>
    <el-table :data="dataList" border height="100%">
      <el-table-column label="省市区">
        <template slot-scope="scope">
          <span>{{ scope.row.province+scope.row.city+scope.row.district }}</span><br>
        </template>
      </el-table-column>
      <el-table-column label="详细地址" prop="address"></el-table-column>
      <el-table-column label="操作" width="60">
        <template slot-scope="scope">
          <span class="blue" @click="selectData(scope.row)">选择</span>
        </template>
      </el-table-column>
    </el-table>
  </el-tabs>
</div>

script

import { GET_CITYS_CODES } from '@/api/xx/index.js';  // 用地图得到的省市区名称换省市区下拉codes

export default {
  props: {
    params: {
      type: Object,
      default: {},
    },
  },
  data() {
    let self = this;
    return {
      activeName: "first",
      amap: {
        zoom: 9,
        center: [0, 0],
        plugin:['ToolBar'],
        marker: [0, 0],
        events: {
          click(e) { self.markerClick(e) }, // 选点点击事件
          dragend(e) { self.dragEnd(e) }, // 选点拖拽事件
        }
      },
      searchOption: {
        city: '',
        citylimit: true
      },
      // dataList本来是后端接口获得,这里不重要,只作数据结构展示
      dataList: [{
        province: "四川省",
        city: "成都市",
        district: "高新区",
        address: "仁和街阿斯顿路250号",
        codes: ['510000', '510100', '510107']
      }],
      obj: {},
      addr: ""
    }
  },
  methods: {
    // 可以放在专门的utils文件中,这里为了方便展示,放在methods里
    // 获取当前ip所在城市
    getCurrentCity() {
      return new Promise((resolve, reject) => {
        let city;
        AMap.plugin('AMap.CitySearch',function(){
          city = new AMap.CitySearch()
        })
        city.getLocalCity(function (status, result) {
          if (status === 'complete' && result.info === 'OK') {
            resolve(result.bounds.oc)
          } else {
            reject('未检测到该地址')
          }
        });
      })
    },
    // 可以放在专门的utils文件中,这里为了方便展示,放在methods里
    // 逆地理编码:经纬度转为地址;
    getPositionByLonLats(lnglat) {
      return new Promise((resolve, reject) => {
        let geocoder;
        AMap.plugin('AMap.Geocoder',function(){
          geocoder = new AMap.Geocoder()
        })
        geocoder.getAddress(lnglat, function (status, result) {
          if (status === 'complete' && result.info === 'OK') {
            resolve(result.regeocode)
          } else {
            reject('未检测到该地址')
          }
        });
      })
    },
    // 可以放在专门的utils文件中,这里为了方便展示,放在methods里
    // 地理编码:地址转为经纬度(与逆地理编码类似,这里不在演示调用)
    getPositionByAddr(addr) {
      return new Promise((resolve, reject) => {
        let geocoder;
        AMap.plugin('AMap.Geocoder',function(){
          geocoder = new AMap.Geocoder()
        })
        geocoder.getLocation(addr, function (status, result) {
          if (status === 'complete' && result.geocodes.length) {
            resolve(result.geocodes[0].location)
          } else {
            reject('未检测到该地址')
          }
        });
      })
    },
    // 弹窗open事件:中心点默认在设在ip所在地址
    setData() {
      this.getCurrentCity().then((res) => {
        this.amap.center = [];
        this.amap.center.push(res.lng, res.lat);
      })
    },
     // 点击搜索列表结果
    // 注意:返回的结果是数组,默认选中第一个,作为点坐标
    async onSearchResult(pois) {
      let self = this;
      await this.getPositionByLonLats([pois[0].lng, pois[0].lat]).then((res) => {
        self.obj = { ...res.addressComponent };
        self.addr = res.formattedAddress;
      }).catch((err) => {
        errorMsg(err);
        return
      })
      self.amap.center = [];
      self.amap.center.push(pois[0].lng, pois[0].lat);
      self.amap.marker = [];
      self.amap.marker.push(pois[0].lng, pois[0].lat);
      self.amap.zoom = 18;
    },
    // 点坐标拖拽结束
    dragEnd(e) {
      let self = this;
      // 
      self.getPositionByLonLats([e.lnglat.lng, e.lnglat.lat]).then((res) => {
        self.obj = { ...res.addressComponent };
        self.addr = res.formattedAddress;
      }).catch((err) => {
        errorMsg(err);
        return
      })
    },
    // 点击点坐标,是否确认选择
    markerClick() {
      let self = this;
      self.$confirm(`是否确认选择【${this.addr}】?`, "确认选择", {
        closeOnClickModal: false,
        cancelButtonClass: "cancel-btn",
        confirmButtonClass: "delete-btn",
      }).then(() => {
        let a = self.obj.province;
        let b = self.obj.city;
        let c = self.obj.district;
        // 将点坐标省市区名称传给后端,获取省市区下拉codes
        // 注意:直辖市,高德不会返回市名称,后端要求,直辖市 省市同名
        GET_CITYS_CODES([a, b?b:a, c]).then((res) => {
          if (res.code === 10000) {
            let info = {
              address: self.obj.township+self.obj.street+self.obj.streetNumber,
              codes: res.result
            }
            // 将确认信息传给父组件
            self.cancel(info);
          }
        });
      })
      .catch(() => {});
    },
    // 常用/历史列表 选择按钮
    selectData(data) {
      let info = {
        address: data.address,
        codes: data.codes
      }
      // 将选择信息传给父组件
      this.cancel(info);
    },

其他功能的

template

// 结构与前面的template类似,这里只保留el-amap
<el-amap
  vid="amapContainer2"
  :center="amap.center"
  :plugin="amap.plugin"
  :zoom="amap.zoom"
  :zoomEnable="true"
  class="amap" 
>
  <!-- 轨迹 -->
  <el-amap-polyline
    :stroke-weight="6"
    :path="amap.path"
    line-join="round"
    stroke-color="#00BD00"
  />
  <!-- 点 -->
  <el-amap-marker
    v-for="(item, i) in pathData"
    :key="i+Math.random()"
    :clickable="true"
    :position="item.marker"
    :events="{ click(e){markerClick(e)} }"
  />
  <!-- 信息窗体 -->
  <el-amap-info-window
    :auto-move="true"
    :close-when-click-map="true"
    :is-custom="true"
    :offset="[-20, -30]"
    :position="info.marker"
    :visible="info.visible"
    v-if="info.visible"
  >
    <div id="info-window">
      <p>{{ info.waybillNo }}</p>
      <p>{{ info.plate }}</p>
      <p>{{ info.phone }}</p>
      <p>{{ info.address }}</p>
    </div>
  </el-amap-info-window>
</el-amap>

script

data() {
  return {
    // 其他类似的略
    info: {}, // 信息窗体
    infoList: [] // 信息窗体集合
  }
},
methods: {
  // 点击坐标点弹出 信息窗体
  markerClick(e) {
    let self = this;
    // 第一次点击 才给所有的信息窗体赋值(为了演示方便放在这里,赋值操作根据实际需求正常操作)
    if (self.infoList.length !== self.pathData.length) {
      for (let i = 0; i < self.pathData.length; i++) {
        let obj = {...self.pathData[i]};
        obj.visible = false
        self.infoList.push(obj);
      }
    // 切换信息窗体手动关闭所有窗体的visible
    } else {
      for (let i = 0; i < self.infoList.length; i++) {
        self.infoList[i].visible = false
      }
    }
    // 清空上一个信息窗体的信息
    self.info = {};
    let arr = [e.target.w.position.lng, e.target.w.position.lat];
    let index = self.pathData.findIndex(i => i.marker.toString() === arr.toString());
    this.$nextTick(() => {
      self.info = self.infoList[index];
      self.$set(self.info, 'visible', true);
    })
  },
}

Adele0
44 声望3 粉丝