腾讯地图选取地址的vue开发组件

完成后的具体样子
在这里插入图片描述
改组件实现点击地图返回详细的坐标与具体地址,若有具体地址则父组件传给改组件地址与左边,组件实现回显。组件内具体代码如下:

<template>
  <el-dialog
    title="请选择详细地址"
    :visible.sync="mapVisible"
    :fullscreen="false"
    id="mapDialog"
    :close-on-click-modal="false"
    @closed="closeDialog"
  >
    <div class="well-map">
      <div class="selected-info">
        <div>
          <span class="label">所选地址:</span>
          <span>{{
            locData.address ? locData.address : selectedValue.address
          }}</span>
        </div>
      </div>
      <div class="search-row" v-clickoutside="handleBlur">
        <el-row class="input-wrap" :gutter="20">
          <el-col :span="20">
            <el-input
              v-model="searchKey"
              @click="handleFoucus"
              @input="handleSearch()"
              placeholder="请输入要搜索的地址"
            ></el-input>
          </el-col>
          <el-col :span="2">
            <el-button type="primary" @click="handleSearch()">搜索</el-button>
          </el-col>
        </el-row>

        <ul v-show="showAddressList && addressList.length">
          <li
            v-for="(item, index) in addressList"
            :key="index"
            @click.stop="handleSelect(item)"
          >
            <span class="title">{{ item.title }}</span>
            <span class="other-info">{{ item.address }}</span>
          </li>
        </ul>
      </div>
      <div id="well-container" class="well-map-container"></div>
    </div>
    <div slot="footer" class="dialog-footer">
      <el-button
        type="default"
        size="small"
        icon="el-icon-close"
        @click.native="closeDialog"
        >取消</el-button
      >
      <el-button
        type="primary"
        size="small"
        icon="el-icon-check"
        @click.native="findLocation"
        >确定</el-button
      >
    </div>
  </el-dialog>
</template>

<script>
/**
 * 函数:
 * 1. selected-change:当选中的点发生变化时,触发
 * **/
import mapApi from "../../api/class/sign";

export default {
  props: {
    selectedValue: {
      type: Object,
      required: false,
    },
  },
  data() {
    return {
      mapVisible: true,
      timeout: null, //搜索防抖
      searchKey: "", //搜索Key
      addressList: [], //搜索结果
      mapKey: "HSUBZ-*****-*****-*****-*****-*****", //腾讯地图mapKey
      map: null,
      markerLayer: null,
      showAddressList: false,

      locData: {
        longitude: "",
        latitude: "",
        address: "",
      },
    };
  },
  mounted() {
    this.$nextTick(() => {
      this.initMap();
      this.resetPoint();
    });
  },
  created() {
    console.log(this.selectedValue);
    if (this.selectedValue.latitude) {
      this.locData.latitude = this.selectedValue.latitude;
    }
    if (this.selectedValue.longitude) {
      this.locData.longitude = this.selectedValue.longitude;
    }
    if (this.selectedValue.address) {
      this.locData.address = this.selectedValue.address;
    }
    if (!this.selectedValue.region) {
      this.selectedValue.region = "杭州市"
    }
  },
  directives: {
    /**
     * 封装指令,监听点击非目标元素之外的dom
     * ***/
    clickoutside: {
      bind(el, binding, vnode) {
        function documentHandler(e) {
          // 这里判断点击的元素是否是本身,是本身,则返回
          if (el.contains(e.target)) {
            return false;
          }
          // 判断指令中是否绑定了函数
          if (binding.expression) {
            // 如果绑定了函数 则调用那个函数,此处binding.value就是handleClose方法
            binding.value(e);
          }
        }

        // 给当前元素绑定个私有变量,方便在unbind中可以解除事件监听
        el.__vueClickOutside__ = documentHandler;
        document.addEventListener("click", documentHandler);
      },
      update() {},
      unbind(el, binding) {
        // 解除事件监听
        document.removeEventListener("click", el.__vueClickOutside__);
        delete el.__vueClickOutside__;
      },
    },
  },
  methods: {
    /**
     * 初始化地图
     * **/
    initMap() {
      //初始化地图
      this.map = new TMap.Map("well-container", {
        rotation: 20, //设置地图旋转角度
        pitch: 0, //设置俯仰角度(0~45)
        zoom: 16, //设置地图缩放级别
      });

      //初始化marker图层
      this.markerLayer = new TMap.MultiMarker({
        id: "marker-layer",
        map: this.map,
      });

      //监听点击事件添加marker
      this.map.on("click", this._clickMap);
    },

    /**
     * 搜索框聚焦
     * **/
    handleFoucus(e) {
      this.showAddressList = true;
    },
    /**
     * 搜索框失焦
     * **/
    handleBlur() {
      this.showAddressList = false;
    },
    /**
     * 搜索框内容发生变化
     * timeout是为了防抖
     * **/
    handleSearch() {
      this.showAddressList = true;
      clearTimeout(this.timeout);
      if (!this.searchKey) {
        this.addressList = [];
      } else {
        this.timeout = setTimeout(() => {
          mapApi.placeSuggest({keyword: this.searchKey,region: this.selectedValue.region,}).then((res) => {
            if (res.code === 200) {
              this.addressList = res.data;
            } else {
              this.$message(res.msg);
            }
          }).catch((err) => {
            console.log(err);
          });
        }, 300);
      }
    },

    handleSelect(row) {
      console.log(JSON.stringify(row));
      this.searchKey = row.title;
      //searchKey发生变化了,需触发搜索
      this.handleSearch();
      this.locData.address = row.address;
      this.locData.latitude = row.latitude;
      this.locData.longitude = row.longitude;
      //selectedValue发生变化了,需重新绘制点
      this._drawPoint(row.latitude, row.longitude, true);
      //选中之后需触发失去焦点
      this.handleBlur();
    },

    /**
     * 根据经纬度在地图上标注
     * @param lat 纬度
     * @param lng 经度
     * @param isUpdateCenter 是否更新地图中心点,有以下三种情况:
     * 1. 外部传入的点信息发生变化时,需更新中心点
     * 2. 选中搜索列表的某一项时,需更新中心点
     * 3. 点击地图标记点时,不需要更新中心点
     * **/
    _drawPoint(lat, lng, isUpdateCenter) {
      //先清空点
      this.markerLayer.setGeometries([]);
      if (!lat || !lng) {
        return;
      }
      //更新地图中心位置
      if (isUpdateCenter) {
        this.map.setCenter(new TMap.LatLng(lat, lng));
      }
      this.markerLayer.add({
        position: new TMap.LatLng(lat, lng),
      });
    },

    /**
     * 点击地图
     * 1. 根据经纬度画点
     * 2, 根据经纬度逆向查询到地址详细信息
     * 3. 通知父组件
     * **/
    _clickMap(evt) {
      console.log("点击地图:");
      this.searchKey = "";
      this._drawPoint(evt.latLng.lat, evt.latLng.lng, false);
      mapApi
        .codeToaddress({ longitude: evt.latLng.lng, latitude: evt.latLng.lat })
        .then((res) => {
          console.log("逆地址解析结果:", res);
          if (res.code === 200) {
            this.locData.address = res.data.address;
            this.locData.longitude = evt.latLng.lng;
            this.locData.latitude = evt.latLng.lat;
          } else {
            this.$message.error(res.msg);
          }
        })
        .catch((err) => {
          console.log("逆地址解析失败", err);
        });
    },

    // 进入地图弹窗重新定位
    resetPoint() {
      if (this.selectedValue.latitude && this.selectedValue.longitude) {
        this._drawPoint(
          this.selectedValue.latitude,
          this.selectedValue.longitude,
          true
        );
      } else {
        mapApi.addressTocode({ address: this.selectedValue.region }).then((res) => {
          if (res.code === 200) {
            this._drawPoint(res.data.latitude, res.data.longitude, true);
          } else {
            this.$message.error(res.msg);
          }
        });
      }
    },

    closeDialog() {
      this.mapVisible = false;
      this.$emit("closeDialog", false);
    },
    //返回选中点的位置
    findLocation() {
      if (
        !this.locData.latitude &&
        !this.locData.latitude &&
        !this.locData.address
      ) {
        this.$message.warning("请选择地址");
      } else {
        this.$emit("getLocation", this.locData);
        this.$emit("closeDialog", false);
        this.mapVisible = false;
      }
    },
  },
};
</script>

<style lang="scss" scoped>
#mapDialog {
  ::v-deep .el-dialog {
    min-width: 1000px;
    min-height: 800px;
    margin-top: 8vh !important;
    .el-dialog__body {
      padding-top: 10px;
    }
  }
}
.well-map {
  line-height: normal;
  .search-row {
    position: relative;
    width: 100%;
    margin: 12px 0;
    z-index: 99009;

    .input-wrap {
      height: 32px;
      line-height: 32px;
      display: flex;

      > input {
        box-sizing: border-box;
        margin: 0;

        position: relative;
        width: 100%;
        height: 100%;
        padding: 4px 11px;
        color: rgba(0, 0, 0, 0.65);
        font-size: 14px;
        background-color: #fff;
        /*background-image: none;*/
        border: 1px solid #d9d9d9;
        border-radius: 4px 0 0 4px;
        transition: all 0.3s;
        border-right: none;

        &:hover {
          border-color: #40a9ff;
          border-right-width: 1px !important;
        }

        &:focus {
          border-color: #40a9ff;
          border-right-width: 1px !important;
          outline: 0;
          box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
        }
      }

      > button {
        padding: 0 15px;
        border-radius: 0 4px 4px 0;
        color: #fff;
        background: #1890ff;
        border-color: #1890ff;
        box-shadow: none;
        border: none;
        width: 82px;
        height: 100%;
        cursor: pointer;

        &:hover,
        &:focus {
          color: #fff;
          background: #40a9ff;
          border-color: #40a9ff;
          outline: none;
        }

        &:active {
          color: #fff;
          background: #096dd9;
          border-color: #096dd9;
          outline: none;
        }
      }
    }

    > ul {
      position: absolute;
      top: 100%;
      left: 0;
      width: 100%;
      background: rgba(252, 250, 250, 0.918);
      border: 1px solid #f1f1f1;
      font-size: 13px;
      color: #5a5a5a;
      max-height: 280px;
      overflow-y: auto;
      list-style: none;
      padding: 0;
      margin: 0;

      > li {
        text-overflow: ellipsis;
        white-space: nowrap;
        overflow: hidden;
        width: 100%;
        border-bottom: 1px solid #f1f1f1;
        padding: 10px;
        margin: 0;
        cursor: pointer;

        &:hover {
          background: #eff6fd;
        }

        .title {
          display: block;
          line-height: normal;
          margin-bottom: 4px;
        }

        .other-info {
          font-size: 12px;
          color: #b9b9b9;
          display: block;
          line-height: normal;
        }
      }
    }
  }

  .well-map-container {
    width: 100%;
    height: 550px;
    margin-top: 20px;
  }

  .selected-info {
    background: #ecf5ff;
    padding: 10px 14px;
    color: #565656;
    font-size: 13px;

    > div {
      margin-bottom: 4px;

      .label {
        color: #757575;
      }

      &:last-child {
        margin-bottom: 0;
      }
    }
  }
}
</style>

地图组件为一个dialog弹窗,所以父组件通过v-if控制其开关,getLocation获取子组件传递过来的详细地址与坐标,closeDialog子组件弹窗关闭,父组件中也要设置showMap关闭,selectedValue来传递已选的详细地址或坐标(注:腾讯组件中的地址解析接口需要一个region字段,也可以通过该对象传递给子组件,此对象内所有值可以为null)。
在这里插入图片描述
本文参考博客:https://www.jianshu.com/p/a138889e8ea2

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值