4 - vue项目中,在腾讯地图的infoWindow信息窗
腾讯地图提供的api中没有infoWindow信息窗口怎么传入组件的介绍,所以我简单记录一下在vue项目开发中,使用腾讯地图,默认展开多个infoWindow信息窗口,遇到的一些问题:1.腾讯地图要怎么默认显示多个信息窗口;2.如果重新渲染地图,要怎么清空之前显示的信息窗口;3.infoWindow信息窗口怎么传入组件?...
一、效果
1. 参考链接
- 腾讯地图官方案例:lbs.qq.com/webDemoCent…
- 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"> 可拖动放大</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>