4 - vue项目中,在腾讯地图的infoWindow信息窗

腾讯地图提供的api中没有infoWindow信息窗口怎么传入组件的介绍,所以我简单记录一下在vue项目开发中,使用腾讯地图,默认展开多个infoWindow信息窗口,遇到的一些问题:1.腾讯地图要怎么默认显示多个信息窗口;2.如果重新渲染地图,要怎么清空之前显示的信息窗口;3.infoWindow信息窗口怎么传入组件?...

一、效果

1. 参考链接

  1. 腾讯地图官方案例:lbs.qq.com/webDemoCent…
  2. demo仓库:gitee.com/mayxue/vue2…

2. 效果图

图1:搜索定位

图2:默认展示多个信息窗口

二、案例实现

2.1 公共部分

安装js跨域请求插件:npm i vue-jsonp -S

2.1.1 在main.js中引入

import Vue from 'vue'
import App from './App.vue'
import { VueJsonp } from 'vue-jsonp';
Vue.use(VueJsonp);
new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

2.1.2 地图组件(components -> TxMap -> index.vue)

<template>
  <!-- 腾讯地图 -->
  <section>
    <div id="container"></div>
    <span class="text-warning">&nbsp;可拖动放大</span>
  </section>
</template>

<script>
export default {
  name: "Tx-Map",
  props: {},
  comments: {},
  data() {
    return {};
  },
  watch: {},
  methods: {
    init(zoom = 8, lat = 39.911265, lng = 116.375212, storeAddress) {
      console.log("地图初始化-纬度--lat", lat);
      console.log("地图初始化-经度--lng", lng);
      let that = this;
      //步骤:定义map变量 调用 qq.maps.Map() 构造函数   获取地图显示容器
      //设置地图中心点23.140873000522223, 113.34551811218262
      var myLatlng = new qq.maps.LatLng(lat, lng);
      //定义工厂模式函数
      var myOptions = {
        zoom, //设置地图缩放级别
        center: myLatlng, //设置中心点样式
        mapTypeId: qq.maps.MapTypeId.ROADMAP, //设置地图样式详情参见MapType
      };
      //获取dom元素添加地图信息
      var map = new qq.maps.Map(
        document.getElementById("container"),
        myOptions
      );

      var marker;
      if (storeAddress) {
        if (!marker) {
          marker = new qq.maps.Marker({
            position: myLatlng,
            draggable: true,
            map: map,
          });
        }
      }
      // 添加地图点击事件
      qq.maps.event.addListener(map, "click", function (event) {
        // event.latLng.getLat() -- 纬度
        // event.latLng.getLng() -- 经度
        let map_key = "5PLBZ-KIZC6-NCKSH-MOPXS-QP6OK-M4BIO";
        that
          .$jsonp(
            `https://apis.map.qq.com/ws/geocoder/v1/?output=jsonp&key=${map_key}&location=${event.latLng.getLat()},${event.latLng.getLng()}`
          )
          .then((res) => {
            that.$emit("setAddress", res.result);
            myLatlng = new qq.maps.LatLng(
              res.result.location.lat,
              res.result.location.lng
            );
            marker.setMap(null); //清除地图的所有marker标点
            marker = new qq.maps.Marker({
              position: myLatlng,
              draggable: true,
              map: map,
            });
          })
          .catch((err) => {});
      });
    },
  },
};
</script>

<style lang="scss" scoped>
#container {
  width: 100%;
  min-height: 500px;
  display: inline-block;
  // 容器可拖放
  resize: both;
  overflow: auto;
}
</style>

2.2 搜索定位效果

2.2.1 搜索定位效果页面布局

<template>
  <!-- 在vue项目中使用腾讯地图 -->
  <div>
    <el-form
      :model="form"
      ref="ruleForm"
      label-width="180px"
      class="form"
      size="small"
    >
      <el-form-item label="门店地址:">
        <el-input
          style="width: 400px"
          v-model="form.store_address"
          placeholder="请输入门店详细地址"
          clearable
          @input="addressChange"
        ></el-input>
        <el-button
          type="primary"
          plain
          clearable
          @click="addressChange"
          style="margin-left: 10px"
          >搜索地址</el-button
        ></el-form-item
      >
      <el-form-item label="门店定位:">
        <tx-map ref="map" @setAddress="setAddress" />
      </el-form-item>
    </el-form>
  </div>
</template>

<script>
import TxMap from "@/components/TxMap"; //腾讯地图
export default {
  components: { TxMap },
  data() {
    return {
      form: {
        store_address: "广东省广州市天河区信源大厦",
        prov_name: "",
        city_name: "",
        district_name: "",
        latitude: "",
        longitude: "",
      },
    };
  },
  methods: {
    //更新地点
    setAddress(newAddress) {
      this.form.store_address =
        newAddress.formatted_addresses.recommend; //地址
      this.form.latitude = newAddress.location.lat; //纬度
      this.form.longitude = newAddress.location.lng; //经度
      this.form.prov_name = newAddress.address_component.province; //省
      this.form.city_name = newAddress.address_component.city; //市
      this.form.district_name = newAddress.address_component.district; //区
      this.form = Object.assign({}, this.form, {
        store_address: newAddress.formatted_addresses.recommend,
      });
      console.log("this.form.store_address", this.form.store_address);
    },

    // 地址搜索事件
    addressChange(zoom = 15) {
      let map_key = "5PLBZ-KIZC6-NCKSH-MOPXS-QP6OK-M4BIO"; //腾讯地图key
      //省市区
      let prov_name = this.form.prov_name || "",
        city_name = this.form.city_name || "",
        district_name = this.form.district_name || "";
      //如果有省市区的选择,地图搜索就要把省市区组装起来,传入到address参数里
      let store_city = prov_name + city_name + district_name;

      // 地址转坐标
      this.$jsonp(
        `https://apis.map.qq.com/ws/geocoder/v1/?output=jsonp&key=${map_key}&address=${store_city}${this.form.store_address}`
      ).then((res) => {
        // 根据点击地点获取经纬度
        let lat = res.result.location.lat; //纬度
        let lng = res.result.location.lng; //经度
        //在地图上定位地点
        this.$refs["map"].init(
          zoom,
          lat,
          lng,
          this.form.store_address
        );
      });
    },
  },
  created() {
    this.addressChange(15);
  },
};
</script>

<style lang="scss" scoped></style>

2.3 默认展示多个信息窗口

2.3.1 默认展示多个信息窗口页面布局

<template>
  <!-- 在vue项目中使用腾讯地图 -->
  <div class="mapContainer" v-loading="mapLoading">
    <tx-map ref="map" id="map" class="container" />
  </div>
</template>

<script>
import Vue from "vue";
import TxMap from "@/components/TxMap"; //腾讯地图
import mapInfowindowPane from "./components/mapInfowindowPane";
export default {
  components: { TxMap, mapInfowindowPane },
  data() {
    return {
      mapData: {
        latitude: "23.16584388345954",
        longitude: "113.36079213212736",
        max_distance: "5000",
        poiname: "广州体育职业技术学院",
      },
      //模拟后端返回的数据
      mapArray: [
        {
          amount: 0.1,
          count: 1,
          distance: "1.1 km",
          id: 122817,
          latitude: "23.150952",
          longitude: "113.34669",
          order_info: [
            {
              bank_type: "其他",
              bank_type_id: 3,
              level_name: "1号营业所",
              merchant_name: "小李",
              money: 50,
              pay_time: "2023-07-28 13:24:37",
            },
            {
              bank_type: "建设银行",
              bank_type_id: 1,
              level_name: "1号营业所",
              merchant_name: "老张",
              money: 18.88,
              pay_time: "2023-07-28 13:24:37",
            },
          ],
        },
        {
          amount: 0.1,
          count: 1,
          distance: "1.1 km",
          id: 122817,
          latitude: "23.170952",
          longitude: "113.35669",
          order_info: [
            {
              bank_type: "其他",
              bank_type_id: 3,
              level_name: "1号营业所",
              merchant_name: "小李",
              money: 50,
              pay_time: "2023-07-28 13:24:37",
            },
          ],
        },
      ],
      map: "",
      infoWindow: "",
      mapLoading: false,
    };
  },
  methods: {
    // 地址搜索事件
    loadMap() {
      this.mapLoading = true;
      // 地址转坐标
      let map_key = "5PLBZ-KIZC6-NCKSH-MOPXS-QP6OK-M4BIO"; //腾讯地图key
      this.$jsonp(
        `https://map.qq.com/api/gljs?v=1.exp&key=${map_key}&address=${this.mapData.poiname}`
      )
        .then((res) => {
          /**
           * 已加载的信息窗体,重新定位后不会关闭,
           * 所以需要销毁地图重新绘制地图再渲染对应的信息窗体数据
           */
          if (this.map) {
            this.map.destroy(); //销毁地图
          }

          //设置地图的中心点坐标
          let center = new TMap.LatLng(
            this.mapData.latitude,
            this.mapData.longitude
          );

          //初始化地图
          this.map = new TMap.Map("container", {
            center: center,
            zoom: 14,
          });

          //初始化marker图层
          var markerLayer;
          markerLayer =  markerLayer = this.setMarkerLayer();
          //初始化地图的设中心点坐标
          markerLayer.add({
            position: center,
          });

          /**
           * 监听点击事件添加marker
           * 只保留一个marker
           * 定位改变后重新绘制地图,重新定位地图的中心点经纬度
           */
          this.map.on("click", (evt) => {
            console.log("evt", evt);
            console.log("this.infoWindow", this.infoWindow);
            //this.map.destroy();
            this.loadMap();
            this.mapData.latitude = evt.latLng.lat;
            this.mapData.longitude = evt.latLng.lng;
            /**
             * 判断地图定位是否已存在
             * 已存在:清空再添加
             * 不存在:直接添加
             */
            if (markerLayer) {
              markerLayer.setMap(null); //清空已存在的定位
              markerLayer = this.setMarkerLayer();
              markerLayer.add({
                position: evt.latLng,
              });
            } else {
              markerLayer.add({
                position: evt.latLng,
              });
            }
          });

          // 地图比例尺改变事件
          this.map.on("zoom_changed", () => {
            var zoom = this.map.getZoom();
            console.log("缩放", zoom);
            console.log("标尺", this.map.getScale());
          });

          //在地图上定位地点
          this.init();
        })
        .finally(() => {
          this.mapLoading = false;
        });
    },

    //初始化中心标点定位参数
    setMarkerLayer() {
      return new TMap.MultiMarker({
        id: "marker-layer",
        map: this.map,
        styles: {
          marker: new TMap.MarkerStyle({
            width: 20, // 样式宽
            height: 30, // 样式高
            anchor: { x: 10, y: 30 }, // 描点位置
            src: "https://mapapi.qq.com/web/lbs/javascriptGL/demo/img/markerDefault.png",
          }),
        },
      });
    },

    init() {
      this.mapPointArray = [];
      if (this.mapArray && this.mapArray.length > 0) {
        this.mapArray.forEach((item1) => {
          item1.order_info.forEach((item, index) => {
            console.log("item", item);
            console.log("index", index);
            let xPosition =
                (item1.order_info.length - 1) * 4 - index * 4,
              yPosition = -(
                32 +
                (item1.order_info.length - 1) * 4 -
                index * 4
              );

            this.mapPointArray.push({
              styleId: "marker",
              position: new TMap.LatLng(
                item1.latitude,
                item1.longitude
              ), // 点标记的坐标位置
              amount: item.money,
              time: item.pay_time,
              merchant: item.merchant_name,
              pay_way: item.bank_type_id,
              pay_way_str: item.bank_type,
              x: xPosition,
              y: yPosition,
              latitude: item1.latitude,
              longitude: item1.longitude,
              distance: item1.distance,
            });
          });
        });
      } else {
        this.mapPointArray = [];
      }

      //初始化marker
      var marker = new TMap.MultiMarker({
        map: this.map,
        styles: {
          marker: new TMap.MarkerStyle({
            width: 20, // 样式宽
            height: 30, // 样式高
            anchor: { x: 10, y: 30 }, // 描点位置
            //src: "https://mapapi.qq.com/web/lbs/javascriptGL/demo/img/markerDefault.png",
          }),
        },
        enableCollision: true, // 开启碰撞
        geometries: this.mapPointArray, //多个信息窗口
      });

      //初始化infoWindow
      this.mapPointArray.forEach((item) => {
        this.setInfoWindowContent(item, this.map);
        this.infoWindow.open(); //打开信息窗
        this.infoWindow.setPosition(item.position); //设置信息窗位置
      });
    },

    //将vue组件传入腾讯地图信息窗体infoWindow中
    setInfoWindowContent(data, map) {
      let Content = Vue.extend({
        //自定义模板继承
        template: `<mapInfowindowPane
          ref="infoWindowPart"
          :infowindowData="mapArray"
        ></mapInfowindowPane>`,
        components: {
          mapInfowindowPane, //弹框组件
        },
        data() {
          return {
            mapArray: data,
          };
        },
      });
      let component = new Content().$mount(); //手动挂载组件
      console.log("data", data);
      this.infoWindow = new TMap.InfoWindow({
        map: map,
        position: data.position, //信息窗体的经纬度
        offset: {
          x: data.x ? data.x : 0,
          y: data.y ? data.y : -32,
        }, //设置信息窗相对position偏移像素
        content: component.$el.outerHTML,
        enableCustom: true, //是否自定义信息窗体(默认为false:就是使用腾讯地图自带的信息窗体样式)
      });
    },
  },
  mounted() {
    this.loadMap();
  },
};
</script>

<style lang="scss" scoped>
.container {
  width: 100%;
  height: 800px;
  display: inline-block;
  // 容器可拖放
  resize: both;
  overflow: auto;
}

.contextmenu {
  margin: 0;
  background: #fff;
  z-index: 99999999999;
  position: fixed; //关键样式设置固定定位
  list-style-type: none;
  padding: 5px 0;
  border-radius: 4px;
  font-size: 12px;
  font-weight: 400;
  color: #333;
  box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3);
}

.contextmenu li {
  margin: 0;
  padding: 7px 16px;
  cursor: pointer;
}
.contextmenu li:hover {
  background: #eee;
}
</style>

2.3.2 信息窗口组件components -> mapInfowindowPane.vue

<template>
  <div class="consumptionRecords detail-form-item">
    <div class="InfowindowCon">
      <el-form class="my-form" size="mini" label-width="100px">
        <el-form-item label="消费时间:">
          {{ infowindowData.time }}
        </el-form-item>
        <el-form-item label="消费商户:">
          {{ infowindowData.merchant }}
        </el-form-item>
        <el-form-item label="消费金额:">
          <b class="amount text-danger">¥{{ infowindowData.amount }}</b>
          <el-tag
            :type="getPayWayTag(infowindowData.pay_way)"
            size="mini"
            >{{ infowindowData.pay_way_str }}</el-tag
          >
        </el-form-item>
        <el-form-item label="距离:">
          {{ infowindowData.distance }}
        </el-form-item>
      </el-form>
    </div>
    <div class="point"></div>
  </div>
</template>

<script>
import getTagType from "@/utils/getTagType";
export default {
  components: {},
  props: {
    infowindowData: {},
  },
  data() {
    return {};
  },
  methods: {
    getPayWayTag(type) {
      return getTagType.getPayWayTag(type);
    },
  },
  created() {},
};
</script>

<style lang="scss" scoped>
.detail-form-item ::v-deep .el-form-item__label {
  line-height: 30px;
}
.detail-form-item ::v-deep .el-form-item__content {
  line-height: 30px;
  text-align: left;
}
.detail-form-item ::v-deep .el-form-item {
  margin-bottom: 0;
}
.consumptionRecords {
  position: relative;
  padding-bottom: 5px;
  .InfowindowCon {
    min-width: 100px;
    white-space: nowrap;
    background-color: white;
    text-align: center;
    padding: 12px 10px 12px;
    border-radius: 6px;
    box-shadow: rgba(0, 0, 0, 0.15) 0px 2px 4px 0px;
    border: 1px solid #eee;
    .amount{
      display: inline-block;
      margin-right: 10px;
    }
  }

  .point {
    width: 10px;
    height: 10px;
    background-color: white;
    transform: rotate(45deg);
    position: absolute;
    bottom: 1px;
    left: 0px;
    right: 0px;
    margin: auto;
    box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px;
  }
}
</style>

三、需要注意的点

3.1 地图中心标点修改后,只保留最新的一个Marker

必须要先清空地图的已经存在的标点:marker.setMap(null); //清除地图的所有marker标点

// 添加地图点击事件
qq.maps.event.addListener(map, "click", function (event) {
  // event.latLng.getLat() -- 纬度
  // event.latLng.getLng() -- 经度
  let map_key = "5PLBZ-KIZC6-NCKSH-MOPXS-QP6OK-M4BIO";
  that
    .$jsonp(
      `https://apis.map.qq.com/ws/geocoder/v1/?output=jsonp&key=${map_key}&location=${event.latLng.getLat()},${event.latLng.getLng()}`
    )
    .then((res) => {
      that.$emit("setAddress", res.result);
      myLatlng = new qq.maps.LatLng(
        res.result.location.lat,
        res.result.location.lng
      );
      marker.setMap(null); //清除地图的所有marker标点
      marker = new qq.maps.Marker({
        position: myLatlng,
        draggable: true,
        map: map,
      });
    })
    .catch((err) => {});
});
},

3.2在腾讯地图的信息窗口中传入vue组件

3.2.1 在vue.config.js中的设置

module.exports = {
  runtimeCompiler: true, //添加这一行代码的配置,即可实现支持template编译
}

3.2.2 将vue组件传入infoWindow信息窗口

<script>
import Vue from "vue";
import mapInfowindowPane from "./components/mapInfowindowPane";
export default {
  //将vue组件传入腾讯地图信息窗体infoWindow中
  setInfoWindowContent(data, map) {
    let Content = Vue.extend({
      //自定义模板继承
      template: `<mapInfowindowPane
        ref="infoWindowPart"
        :infowindowData="mapArray"
      ></mapInfowindowPane>`,
      components: {
        mapInfowindowPane, //弹框组件
      },
      data() {
        return {
          mapArray: data,
        };
      },
    });
    let component = new Content().$mount(); //手动挂载组件
    console.log("data", data);
    this.infoWindow = new TMap.InfoWindow({
      map: map,
      position: data.position, //信息窗体的经纬度
      offset: {
        x: data.x ? data.x : 0,
        y: data.y ? data.y : -32,
      }, //设置信息窗相对position偏移像素
      content: component.$el.outerHTML,
      enableCustom: true, //是否自定义信息窗体(默认为false:就是使用腾讯地图自带的信息窗体样式)
    });
  },

  //infoWindow信息窗口vue组件渲染
  init(){
    //初始化infoWindow
      this.mapPointArray.forEach((item) => {
        this.setInfoWindowContent(item, this.map);
        this.infoWindow.open(); //打开信息窗
        this.infoWindow.setPosition(item.position); //设置信息窗位置
      });
  }
}
</script>

全部评论

相关推荐

03-30 19:30
石家庄学院 Java
野蛮的柯基在游泳:都能入股了,还得是Java
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务