一、需求&效果图
需求:
1、实现地图显示标记点markers与聚合点,点击标记点或聚合点,页面下弹出详情弹框,
详情弹窗支持左右滑动,支持拨打电话与复制文本
2、视野展示所有经纬度
3、实现按钮功能:刷新地图重点:需要编译在小程序开发工具中使用 “ 真机预览 ” 才可展示如图所示效果
效果图
图中绿色1点标记为自定义点标记,聚合点则使用的默认的聚合样式,目前存在设置自定义点聚合簇之后,markerClusterClick 事件在ios上触发不了,至今官方还没给出解答,我就用的默认样式,当然代码中也写了我自定义的样式,只是enableDefaultStyle设置了true,当把该属性改为false的时候聚合点就显示了自定义的样式了,废话不多说直接上代码。。。
注意:点击聚合点,拿到聚合点下用户的数据,ios跟安卓系统返回的数据类型不一致,因此我都转成了number型
二、具体实现
1.使用地图组件
<view>
<map id="alarm_map" class="map" :latitude="latitude" :longitude="longitude" :show-location="true" scale="12"
:markers="allMarkers" @markertap="markerTap" @labeltap='markerTap'></map>
<view class="tab">
<view class="tabs" :class="status==1 ? 'tab1' : 'tab2'" @click="tabAction(1)">待接单</view>
<view class="tabs" :class="status==2 ? 'tab1' : 'tab2'" @click="tabAction(2)">待服务</view>
</view>
<view class="uButton" v-if="isShow">
<swiper class="scroll-view" :indicator-dots="false" :autoplay="false" :circular="true" :current="current"
@change="swiperChange">
<swiper-item class="scroll-item" :style="{width: windowWidth + 'px' }" v-for="(item,index) in infoList"
:key="index">
<p class="username">{{item.username}}</p>
<p @click="confirmNumber(item.phone)">联系电话:<text class="decoration">{{item.phone}}</text></p>
<p v-if="item.backup_phone.length>0">备用电话:<text class="decoration" v-for="(items,indexs) in item.backup_phone"
:key="indexs" @click="confirmNumber(items)">{{items}}{{item.backup_phone.length!==indexs+1?'、':''}}</text>
</p>
<p>区市街道:{{item.county.name}}{{item.street.name}}</p>
<view @click="copyText(item.detail_address)">详细地址:<text class="decoration">{{item.detail_address}}</text>
</view>
<p class="page">{{index+1}} / {{infoList.length}}</p>
</swiper-item>
</swiper>
</view>
<view class="refresh">
<view @click="getallStations()">
<image style="width: 60rpx; height: 60rpx" src="/static/refreshs.png" />
<view>刷新</view>
</view>
</view>
</view>
2、逻辑
data() {
return {
latitude: 30.6635, // 纬度
longitude: 104.07243, // 经度
allMarkers: [],
status: 2,
allList: [],
infoList: [],
windowWidth: 0,
current: 0,
isShow: false
}
},
onReady() {
uni.getSystemInfo({
success: (res) => {
this.windowWidth = res.windowWidth;
},
});
// 1.页面准备好后,获取到map组件的执行上下文。注意:这里是取的map的id属性
this.mapContext = uni.createMapContext("alarm_map", this);
// 2.请求数据
this.getallStations();
},
methods: {
swiperChange(e) {
this.current = e.detail.current;
},
//自动拨打电话
confirmNumber(number) {
wx.makePhoneCall({
phoneNumber: number,
});
},
//复制功能
copyText(value) {
uni.setClipboardData({
data: value,
success() {
uni.showToast({
title: '已复制到剪贴板',
icon: 'none',
});
},
});
},
//切换状态
tabAction(e) {
this.status = e;
this.getallStations();
},
//点击点标记
markerTap(e) {
this.getInfo(e.detail.markerId)
},
//获取点标记或者聚合下的参保人信息
getInfo(data) {
this.isShow = true
this.infoList = this.allList?.filter(item => item.id == data)
},
async getallStations() {
this.isShow = false;
uni.showLoading();
const {
data: res
} = await getMapElecList({
status: this.status
});
uni.hideLoading();
if (!Array.isArray(res?.list) || res?.list?.length <= 0) {
this.infoList = [];
this.allList = [];
this.allMarkers = [];
return;
};
this.allList = res.list?.map(item => {
return {
...item,
latitude: +item.latitude,
longitude: +item.longitude,
}
})
// 缩放视野展示所有经纬度
this.mapContext.includePoints({
points: this.allList,
padding: [10, 10, 10, 10]
})
// 拿到请求数据后,把数据传给点聚合功能;
this.setMarkersAndCluster(this.allList) // 3、调用聚合功能
},
// 聚合功能
setMarkersAndCluster(markerList) {
// 1.组装数据之后,并赋值给地图上的marker
this.allMarkers = Array.from(markerList).map((item, i) => {
return {
...item,
width: 1,
height: 1,
iconPath: 'https://mapapi.qq.com/web/lbs/javascriptGL/demo/img/marker_blue.png',
joinCluster: true, // 这个属性很重要,必须要,否则将无法开启点聚合功能
label: {
content: '1',
fontSize: 16,
width: 40,
height: 40,
color: '#ffffff',
bgColor: '#0DC7BE',
borderRadius: 25,
textAlign: 'center',
anchorX: 0,
anchorY: -20,
}
}
});
// 2.初始化点聚合的配置,未调用时采用默认配置
this.mapContext.initMarkerCluster({
enableDefaultStyle: true, // 是否启用默认的聚合样式(是否用自定义图标)
zoomOnClick: true,
gridSize: 60,
complete(res) {
console.log('initMarkerCluster', res)
}
});
// 3.发生聚合时,给聚合点设置marker标签
this.mapContext.on('markerClusterCreate', res => {
const clusters = res.clusters // 新产生的聚合簇
const zhou = clusters.map(item => {
const {
center, // 聚合点的经纬度数组
clusterId, // 聚合簇id
markerIds // 已经聚合了的标记点id数组
} = item
return {
...center,
width: 10,
height: 10,
clusterId, // 必须有
iconPath: 'https://mapapi.qq.com/web/lbs/javascriptGL/demo/img/marker_blue.png',
// iconPath: '',
// borderRadius: 8,
joinCluster: true,
label: { // 定制聚合点样式
content: markerIds.length + '',
fontSize: 16,
width: 50,
height: 50,
color: '#ffffff',
bgColor: '#0DC7BE',
borderRadius: 25,
textAlign: 'center',
anchorX: 0,
anchorY: -20,
}
}
})
// 4. 添加聚合簇标签
this.mapContext.addMarkers({
markers: zhou,
clear: false, //是否先清空地图上所有的marker
})
})
// 点击聚合点,拿到聚合点下老人的数据
this.mapContext.on('markerClusterClick', (res) => {
let numArray = res?.cluster?.markerIds.map(Number);//安卓跟ios反的数据类型不一样,因为要处理下
this.isShow = true
this.infoList = this.allList.filter(item => numArray.includes(item.id))
})
},
},
}
3.页面样式
<style>
.map {
width: 100%;
height: 100vh;
}
.tab {
width: 400rpx;
height: 60rpx;
background-color: #fff;
position: fixed;
top: 100rpx;
left: 50%;
transform: translate(-50%, -50%);
border: 4rpx solid #0DC7BE;
border-radius: 8rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
.tabs {
width: 50%;
height: 90%;
display: flex;
justify-content: center;
align-items: center;
margin: 0 4rpx;
border-radius: 4rpx;
}
.tab1 {
background-color: #0DC7BE;
color: #fff;
}
.tab2 {
background-color: #fff;
color: #0DC7BE;
}
.uButton {
width: 100%;
position: fixed;
bottom: 0;
right: 0;
padding-bottom: calc(env(safe-area-inset-bottom)+40rpx);
background-color: #fff;
font-size: 30rpx;
border-radius: 4rpx;
}
.scroll-view {
padding: 10rpx 30rpx 10rpx;
height: 380rpx;
}
.scroll-item {
display: inline-block;
box-sizing: border-box;
vertical-align: top;
position: relative;
}
.refresh {
width: 80rpx;
position: fixed;
bottom: 60%;
right: 0;
background-color: #fff;
text-align: center;
font-size: 24rpx;
padding: 20rpx 0;
border-radius: 4rpx;
}
.username {
font-size: 36rpx;
font-weight: 600;
}
.decoration {
text-decoration: underline;
}
.page {
position: absolute;
bottom: 0%;
left: 50%;
transform: translate(-50%, -50%);
}
</style>