Files
dvcp_v2_wxcp_app/src/project/xicheng/AppMerchantMap/AppMerchantMap.vue
yanran200730 a1e18f7c8b 地图优化
2022-07-07 11:09:50 +08:00

461 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="AppMerchantMap">
<div class="search">
<image :src="$cdn + 'xincheng/search.png'" />
<input placeholder="请输入店主姓名、店名" v-model="businessName" @input="getList(true)">
</div>
<div class="search-list" v-if="isShow">
<div class="item border" v-for="(item, index) in list" :key="index" @click="toDetail(item)">
<div class="item-content">
<h3>{{ item.businessName }} - {{ item.bossName }}</h3>
<p>{{ item.businessAddress }}</p>
</div>
</div>
</div>
<div class="map-content" @click.stop="isShow = false">
<AiTMap :map.sync="map" :lib.sync="lib" :ops="ops" :libraries="['service', 'tools']"/>
</div>
</div>
</template>
<script>
import {mapState} from 'vuex'
export default {
name: 'AppMerchantMap',
appName: '商户地图',
data() {
return {
areaId: '',
lib: null,
map: null,
polygons: [],
labels: [],
businessName: '',
ops: {},
markers: [],
isShow: false,
MarkerCluster: null,
list: []
}
},
computed: {...mapState(['user', 'config'])},
created () {
this.areaId = this.user.areaId
this.getList()
},
mounted () {
this.initMap()
},
methods: {
toDetail (item) {
if (!item.lat) {
return this.$u.toast('该商户尚未标记')
}
this.businessName = item.businessName
this.isShow = false
this.map.easeTo({
center: new this.lib.LatLng(item.lat, item.lng),
zoom: 23
}, {
duration: 800
})
setTimeout(() => {
document.querySelectorAll('.marker').forEach(e => {
e.classList.remove('marker-active')
})
document.querySelector(`.marker-${item.id}`).classList.add('marker-active')
}, 900)
},
getList (isSearch) {
this.$http.post('/app/appcompany/list', null, {
params: {
size: isSearch ? 20 : 100000,
current: 1,
businessName: this.businessName
}
}).then((res) => {
if (res.code == 0) {
const markers = res.data.records.filter(item => item.lat).map(item => {
return {
...item,
title: item.businessName + '-' + item.bossName,
lnglat: [item.lng, item.lat],
lat: item.lat,
lng: item.lng,
id: item.id,
name: item.businessName + '-' + item.bossName
}
})
if (isSearch) {
this.list = res.data.records
this.isShow = true
} else {
this.getMarkerCluster(markers)
}
}
})
},
getMarkerCluster(points, count = 0) {
let {lib: TMap, map} = this
if (map) {
if (this.markers.length) {
this.markers.forEach(v => {
v.setMap(null)
})
}
class ClusterBubble extends TMap.DOMOverlay {
constructor(options) {
super(options);
}
onInit(options) {
this.content = options.content;
this.position = options.position;
this.markersId = options.markersId
this.customClass = options.customClass
};
onDestroy() {
this.dom.removeEventListener('click', this.onClick);
this.removeAllListeners();
}
createDOM() {
let dom = document.createElement('div');
dom.classList.add(this.customClass || 'marker');
if (this.markersId) {
dom.classList.add(this.markersId);
}
dom.innerText = this.content;
// 监听点击事件实现zoomOnClick
this.onClick = this.onClick.bind(this);
// pc端注册click事件移动端注册touchend事件
dom.addEventListener('click', this.onClick);
dom.addEventListener('touchend', this.onClick);
return dom;
};
updateDOM() {
if (!this.map) {
return;
}
// 经纬度坐标转容器像素坐标
let pixel = this.map.projectToContainer(this.position);
// 使文本框中心点对齐经纬度坐标点
let left = pixel.getX() - this.dom.clientWidth / 2 + 'px';
let top = pixel.getY() - this.dom.clientHeight / 2 + 'px';
this.dom.style.transform = `translate(${left}, ${top})`;
this.emit('dom_updated');
};
onClick() {
this.emit('click');
}
}
this.MarkerCluster = new TMap.MarkerCluster({
map, gridSize: 60,
enableDefaultStyle: false, // 关闭默认样式
geometries: points.map(e => ({
position: new TMap.LatLng(e.lat, e.lng),
id: e.id,
content: `${e.name}`,
properties: {...e}
})) || [],
zoomOnClick: true,
maxZoom: 20
})
this.setCenter(points.map(e => {
return new TMap.LatLng(e.lat, e.lng)
}))
let cls = []
this.MarkerCluster.on('cluster_changed', () => {
if (this.markers.length) {
this.markers.forEach(function (item) {
item.destroy();
})
}
cls.forEach(e => e.destroy())
cls = []
let clusters = this.MarkerCluster.getClusters()
clusters.forEach((item) => {
if (item.geometries?.length > 1) {
//聚合样式
let clusterBubble = new ClusterBubble({
map,
position: item.center,
content: item.geometries.length,
customClass: 'cluster'
})
clusterBubble.on('click', () => {
map.fitBounds(item.bounds);
});
cls.push(clusterBubble)
} else {
//点标记样式
let {content, id} = item.geometries?.[0] || {},
overlay = new ClusterBubble({map, position: item.center, content, markersId: `marker-${id}`})
overlay.on('click', () => {
uni.navigateTo({
url: `../AppMerchantManage/detail?id=${id}`
})
})
this.markers.push(overlay)
}
});
})
return Promise.resolve()
} else {
if (count < 5) {
setTimeout(() => {
return this.getMarkerCluster(points, ++count)
}, 1000)
} else Promise.reject("加载失败")
}
},
initMap (retryTimes = 0) {
this.$nextTick(() => {
let { map, TMap } = this
if (map) {
map.setZoom(15)
} else {
if (retryTimes < 10) {
setTimeout(() => {
this.initMap(++retryTimes)
}, 500)
}
}
})
},
setCenter (arr) {
var bounds = new TMap.LatLngBounds()
arr.forEach(function(item){
if (bounds.isEmpty() || !bounds.contains(item)){
bounds.extend(item)
}
})
this.map.fitBounds(bounds, {
padding: 100
})
}
}
}
</script>
<style lang="scss" scoped>
.AppMerchantMap {
height: 100vh;
overflow: hidden;
.search-list {
position: fixed;
left: 50%;
top: 128px;
width: 718px;
height: 70%;
overflow-y: scroll;
box-sizing: border-box;
z-index: 2222;
transform: translateX(-50%);
.search-icon {
width: 36px;
height: 36px;
margin-right: 16px;
vertical-align: sub;
}
.title {
height: 82px;
line-height: 82px;
font-size: 26px;
font-family: MicrosoftYaHeiSemibold;
color: #1365DD;
padding-left: 44px;
background-color: #fff;
}
.item {
padding: 20px 30px;
background-color: #fff;
&:last-child {
border: none;
}
}
.item-content {
display: inline-block;
width: 610px;
color: #333;
h3 {
font-size: 28px;
font-family: MicrosoftYaHeiSemibold;
line-height: 32px;
margin-bottom: 8px;
}
p {
width: 100%;
font-size: 24px;
font-family: MicrosoftYaHei;
line-height: 32px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
.user-icon {
vertical-align: top;
}
.border {
border-bottom: 1px solid #DEDFE1;
}
}
@keyframes warn {
0% {
transform: scale(.5);
opacity: 1
}
30% {
opacity: .5
}
to {
transform: scale(1.6);
opacity: 0
}
}
::v-deep.cluster {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
width: 120px;
height: 120px;
color: #fff;
border-radius: 50%;
background: #0f8f64;
&:after {
position: absolute;
z-index: -1;
width: 120px;
height: 120px;
border-radius: 50%;
-webkit-animation: warn 1s ease-out 0s infinite;
animation: warn 1s ease-out 0s infinite;
background-color: #0f8f64;
content: " ";
}
}
::v-deep.marker {
color: #fff;
background: #558BFE;
padding: 0 32px;
width: fit-content;
height: 56px;
border-radius: 52px;
z-index: 1;
transform: translate(-50%, -50%);
display: flex;
align-items: center;
&:before {
content: " ";
display: block;
position: absolute;
bottom: 0;
left: 50%;
width: 0;
height: 0;
transform: translate(-50%, 100%);
border: 12px solid transparent;
border-top-color: #558BFE;
}
&.marker-active {
z-index: 11;
background: red;
&:before {
content: " ";
display: block;
position: absolute;
bottom: 0;
left: 50%;
width: 0;
height: 0;
transform: translate(-50%, 100%);
border: 12px solid transparent;
border-top-color: red;
}
}
}
* {
box-sizing: border-box;
}
.search {
display: flex;
position: fixed;
align-items: center;
top: 24px;
left: 50%;
z-index: 11111;
width: 718px;
height: 88px;
padding: 0 16px;
background: #FFFFFF;
box-shadow: 0px 4px 8px 0px rgba(192, 185, 185, 0.5);
border-radius: 16px;
transform: translateX(-50%);
image {
width: 48px;
height: 48px;
margin-right: 16px;
}
input {
flex: 1;
font-size: 28px;
color: #666666;
}
}
.map-content {
width: 100%;
height: 100vh;
map {
width: 100%;
height: 100vh;
}
}
}
</style>