Files
dvcp_v2_wechat_app/src/project/weiyang/AppBuilding/components/searchMap.vue
2024-08-14 16:18:38 +08:00

725 lines
20 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="searchMap">
<div class="grid-input" :style="{'top': `${statusBarHeight+60}px`}">
<img src="./img/back-icon.png" alt="" class="back-icon" v-if="name && show" @click="show=false">
<img src="./img/search-icon.png" alt="" class="search-icon" v-else>
<input type="text" class="input" placeholder="请输入姓名、房屋信息" v-model="name" maxlength="10" confirm-type="search"
@confirm="search"/>
<div class="clear-btn">
<img src="./img/del-icon.png" alt="" class="del-icon" v-if="name" @click="clear">
</div>
<span class="search-btn" @click="search">搜索</span>
</div>
<div class="search-list" v-if="show" :style="{'top': `${statusBarHeight+116}px`}">
<div class="title border">
<img src="./img/search-icon.png" alt="" class="search-icon">{{ name }}
</div>
<div class="item border" v-for="(item, index) in buildList" :key="index" @click="getBuildingInfo(item)">
<img src="./img/user-icon.png" alt="" class="search-icon user-icon">
<div class="item-content">
<h3>{{ item.residentName }}</h3>
<p>{{ item.areaName || '' }}{{ item.createAddress }}</p>
</div>
</div>
</div>
<div class="build-btn" @click="toList()">
<img src="./img/build-icon.png" alt=""> 楼栋<br/>列表
</div>
<div class="map-content">
<!-- <AiMap :map.sync="map" :lib.sync="lib" :ops="ops" :libraries="['service', 'tools']"/> -->
<map style="width: 100%; height: 100%;" id="map" :show-location="true" :latitude="latitude" :longitude="longitude" @labeltap="markerTap"></map>
</div>
<u-popup v-model="showPop" mode="bottom" border-radius="14">
<div class="popup">
<div class="bg"></div>
<div class="title">{{ detailInfo.house.createAddress || '' }}</div>
<p class="address">{{ detailInfo.community.address || '' }}</p>
<div class="info-flex">
<span class="label">所属社区</span>
<span class="value">{{ detailInfo.build.areaName }}</span>
</div>
<div class="info-flex">
<span class="label">所属小区</span>
<span class="value">{{ detailInfo.build.communityName }}</span>
</div>
<div class="info-flex">
<span class="label">房屋类型</span>
<span class="value">{{ $dict.getLabel('communityBuildingType', detailInfo.house.buildingType) }}</span>
</div>
<div class="info-flex">
<span class="label">所属网格</span>
<span class="value" v-if="detailInfo.gird">{{ detailInfo.gird.girdName || '' }}</span>
</div>
<div class="info-flex">
<span class="label">网格管理员</span>
<span class="value" v-if="detailInfo.gird">{{ detailInfo.gird.girdMemberNames || '' }}</span>
</div>
<div class="info-flex">
<span class="label">楼栋长</span>
<span
class="value">{{ detailInfo.build.managerName || '' }}&nbsp;&nbsp;{{
detailInfo.build.managerPhone || ''
}}
<img :src="$cdn + 'common/phone.png'" alt="" @click="callPhone(detailInfo.build.managerPhone)"
class="phone-icon" v-if="detailInfo.build.managerPhone">
</span>
</div>
</div>
<div class="popup-btn" @click="toDetail(detailInfo.build.id)">查看楼栋模型</div>
</u-popup>
<u-popup v-model="buildPopup" mode="bottom" border-radius="14">
<div class="popup">
<div class="bg"/>
<div class="title">{{ building.createAddress || '' }}</div>
<div class="info-flex">
<span class="label">所属社区</span>
<span class="value">{{ building.areaName }}</span>
</div>
<div class="info-flex">
<span class="label">所属小区</span>
<span class="value">{{ building.communityName }}</span>
</div>
<div class="info-flex">
<span class="label">房屋类型</span>
<span class="value">{{ $dict.getLabel('communityBuildingType', building.buildingType) }}</span>
</div>
<div class="info-flex">
<span class="label">所属网格</span>
<span class="value" v-text="building.girdName||''"/>
</div>
<div class="info-flex">
<span class="label">网格管理员</span>
<span class="value" v-text="building.girdMemberNames||''"/>
</div>
<div class="info-flex">
<span class="label">楼栋长</span>
<span
class="value">{{ building.managerName || '' }}&nbsp;&nbsp;{{ building.managerPhone || '' }}
<img :src="$cdn + 'common/phone.png'" alt="" @click="callPhone(detailInfo.build.managerPhone)"
class="phone-icon" v-if="detailInfo.build.managerPhone">
</span>
</div>
</div>
<div class="popup-btn" @click="toDetail(building.id)">查看楼栋模型</div>
</u-popup>
</div>
</template>
<script>
import {mapState} from 'vuex'
export default {
name: "searchMap",
data() {
return {
ops: {},
lib: null,
map: null,
markerArr: [],
show: false,
value: '',
ClusterBubble: null,
name: '',
buildList: [],
detailInfo: {
house: {},
build: {},
community: {},
gird: {}
},
showPop: false,
retryMapCount: 0,
building: {},
buildPopup: false,
// latitude: 30.606355,
// longitude: 114.429633,
latitude: '',
longitude: '',
_mapContext: null,
markList: [],
statusBarHeight: uni.getSystemInfoSync().statusBarHeight,
}
},
computed: {...mapState(['user'])},
mounted() {
uni.getLocation({
type: 'wgs84',
success: (res) => {
this.latitude = res.latitude
this.longitude = res.longitude
}
});
this._mapContext = uni.createMapContext("map", this);
// 仅调用初始化,才会触发 on.("markerClusterCreate", (e) => {})
this._mapContext.initMarkerCluster({
enableDefaultStyle: false, // 是否使用默认样式
zoomOnClick: true, // 点击聚合的点,是否改变地图的缩放级别
gridSize: 60, // 聚合计算时网格的像素大小默认60
complete(res) {
}
});
this.cluster();
},
onShow() {
// document.title = "以房找人"
},
created() {
this.$dict.load("communityBuildingType")
},
methods: {
cluster() {
// 仅调用初始化,才会触发 on.("markerClusterCreate", (e) => {})
this._mapContext.initMarkerCluster({
enableDefaultStyle: false, // 是否使用默认样式
zoomOnClick: true, // 点击聚合的点,是否改变地图的缩放级别
gridSize: 60, // 聚合计算时网格的像素大小默认60
complete(res) {
}
});
this._mapContext.on("markerClusterCreate", (res) => {
const clusters = res.clusters
const markers = clusters.map(cluster => {
const {
center,
clusterId,
markerIds
} = cluster
return {
...center,
width: 0,
height: 0,
clusterId, // 必须
label: {
content: markerIds.length + '',
fontSize: 16,
width: 64,
height: 64,
bgColor: '#5088FF',
borderRadius: 32,
textAlign: 'center',
anchorX: 0,
anchorY: -20,
color: '#fff',
borderWidth: 3,
borderColor : '#B0C8FC'
}
}
})
this._mapContext.addMarkers({
markers,
clear: false,
complete(res) {
}
})
});
this._mapContext.on('markerClusterClick', (res) => {
console.log(res)
})
// this.addMarkers();
this.getCommunityList().then(points => {
this.addMarkers(points)
})
},
// 添加标记点
addMarkers(positions) {
const markers = []
positions.forEach((p, i) => {
markers.push(
Object.assign({}, {
id: i,
iconPath: "https://cdn.sinoecare.com/i/2024/07/18/6698c40bdc9e3.png",
width: 10,
height: 10,
joinCluster: true, // 指定了该参数才会参与聚合
label:{
bgColor: "#5088FF",
color: "#fff",
content: `${this.buildList[i].createAddress} | ${this.buildList[i].houseNum}`,
display: "ALWAYS",
fontSize: "12",
padding: 6,
textAlign: "center",
borderRadius: 15,
}
}, p)
)
})
// this.markList = markers
this._mapContext.addMarkers({
markers,
clear: false,
complete(res) {
console.log('addMarkers', res)
}
})
},
markerTap(e) {
var index = Number(e.markerId)
this.$instance.post(`/app/appcommunitybuildinginfo/queryBuildingInfo?buildingId=${this.buildList[index].id}`).then(res => {
this.building = res.data
this.buildPopup = true
})
},
getCommunityList() {
return this.$instance.post('/app/appcommunitybuildinginfo/listByBuilding', null, {
params: {
current: 1,
size: 1000000,
areaId: this.user.areaId
}
}).then(res => {
if (res?.data) {
res.data.map(item => {
item.buildingNumber = item.name || item.buildingNumber
item.communityName = item.name || item.communityName
item.latitude = item.lat
item.longitude = item.lng
})
this.buildList = res.data
return res.data.map(item => {
return {
// ...item,
// lnglat: [item.lng, item.lat],
latitude: item.lat,
longitude: item.lng,
// buildingNumber: item.name || item.buildingNumber,
// communityName: item.name || item.communityName,
// clusterId: item.id
}
})
}
})
},
getMarkerCluster(points, count = 0) {
let {lib: TMap, map} = this
if (map) {
class ClusterBubble extends TMap.DOMOverlay {
constructor(options) {
super(options);
}
onInit(options) {
this.content = options.content;
this.position = options.position;
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');
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.wxwork.config.latlng && map.setCenter(this.wxwork.config.latlng)
let MarkerCluster = new TMap.MarkerCluster({
map, gridSize: 60,
enableDefaultStyle: false, // 关闭默认样式
geometries: points?.map(e => ({
position: new TMap.LatLng(e.lat, e.lng),
content: `${e.createAddress} | ${e.houseNum}`,
properties: {...e}
})) || [],
zoomOnClick: true
})
let markers = [], cls = []
MarkerCluster.on('cluster_changed', () => {
if (markers.length) {
markers.forEach(function (item) {
item.destroy();
})
markers = [];
}
cls.forEach(e => e.destroy())
cls = []
let clusters = 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} = item.geometries?.[0] || {},
overlay = new ClusterBubble({map, position: item.center, content})
overlay.on('click', () => {
// this.building = item.geometries?.[0]?.properties || {}
if(item.geometries[0].properties.id) {
this.$instance.post(`/app/appcommunitybuildinginfo/queryBuildingInfo?buildingId=${item.geometries[0].properties.id}`).then(res => {
this.building = res.data
this.buildPopup = true
})
}
})
markers.push(overlay)
}
});
})
return Promise.resolve()
} else {
if (count < 5) {
setTimeout(() => {
return this.getMarkerCluster(points, ++count)
}, 1000)
} else Promise.reject("加载失败")
}
},
clear() {
this.name = ''
},
search() {
this.buildList = []
this.$instance.post('/app/appcommunityhouseinfo/queryHouseByName', null, {
params: {
current: 1,
size: 20,
areaId: this.user.areaId,
name: this.name
}
}).then(res => {
if (res.code == 0 && res.data.length) {
this.show = true
this.buildList = res.data
// const points = res.data.map(item => {
// return {
// lnglat: [item.lng, item.lat],
// id: item.id,
// corpId: item.corpId,
// areaName: item.areaName,
// buildingNumber: item.residentName || item.buildingNumber,
// communityName: item.residentName || item.communityName,
// }
// })
// this.getMarkerCluster(points)
} else {
this.show = false
this.$u.toast('未搜索到结果,请换个关键字重试!')
}
})
},
getBuildingInfo(item) {
this.$instance.post(`/app/appcommunityhouseinfo/queryDetailByIdWithBuilding?buildId=${item.buildingId}&houseId=${item.id}`).then(res => {
if (res.code == 0) {
this.show = false
this.detailInfo = res.data
this.showPop = true
}
})
},
toList() {
uni.navigateTo({url: './list'})
},
callPhone(phone) {
uni.makePhoneCall({phoneNumber: phone})
},
toDetail(id) {
uni.navigateTo({url: `./detail?id=${id}`})
}
}
}
</script>
<style lang="scss" scoped>
.searchMap {
height: 100vh;
.grid-input {
width: calc(100% - 64px);
height: 88px;
background: #FFF;
box-shadow: 0 4px 8px 0 rgba(192, 185, 185, 0.5);
border-radius: 16px;
position: fixed;
left: 50%;
transform: translateX(-50%);
z-index: 99999;
padding: 16px 20px;
box-sizing: border-box;
.search-icon {
width: 48px;
height: 48px;
margin-right: 16px;
}
.input {
display: inline-block;
padding: 8px 0;
height: 32px;
line-height: 32px;
width: calc(100% - 220px);
font-size: 28px;
font-family: MicrosoftYaHei;
color: #666;
}
.back-icon {
width: 16px;
height: 32px;
margin-right: 24px;
vertical-align: super;
}
.clear-btn {
display: inline-block;
width: 32px;
height: 32px;
margin-right: 30px;
}
.del-icon {
width: 32px;
height: 32px;
vertical-align: super;
}
.search-btn {
display: inline-block;
width: 80px;
height: 32px;
line-height: 32px;
text-align: right;
font-size: 28px;
font-family: MicrosoftYaHei;
color: #1365DD;
border-left: 1px solid #DEDFE0;
vertical-align: top;
margin-top: 12px;
}
}
.search-list {
position: fixed;
width: 100%;
height: calc(100% - 130px);
overflow-y: scroll;
top: 128px;
left: 0;
box-sizing: border-box;
z-index: 99999;
.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: 22px 44px 24px 44px;
background-color: #fff;
}
.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;
}
}
.build-btn {
width: 80px;
height: 160px;
background: #FFF;
box-shadow: 0 4px 8px 0 rgba(138, 138, 138, 0.5);
border-radius: 8px;
position: fixed;
bottom: 136px;
right: 24px;
z-index: 99999;
padding: 16px 16px 0;
box-sizing: border-box;
font-size: 24px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #666;
line-height: 30px;
img {
width: 48px;
height: 48px;
margin-bottom: 8px;
}
}
.map-content {
width: 100%;
height: 100%;
}
.popup {
padding: 0 32px 16px;
.bg {
width: 64px;
height: 10px;
background: #CCC;
border-radius: 6px;
margin: 32px 0 32px 344px;
}
.title {
font-size: 36px;
font-family: PingFang-SC-Heavy, PingFang-SC;
font-weight: 800;
color: #333;
line-height: 50px;
margin-bottom: 24px;
}
.info-flex {
padding: 26px 0 30px 0;
width: 100%;
border-bottom: 1px solid #D8DDE6;
line-height: 40px;
font-size: 28px;
.label {
display: inline-block;
width: 160px;
font-weight: 800;
color: #333;
}
.value {
color: #666;
font-size: 26px;
.phone-icon {
width: 40px;
height: 40px;
vertical-align: sub;
margin-left: 16px;
}
}
}
}
.popup-btn {
width: 100%;
font-size: 32px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
height: 112px;
line-height: 112px;
text-align: center;
background: #1365DD;
color: #FFF;
}
::v-deep.marker {
color: #fff;
background: #5088FF;
padding: 0 32px;
width: fit-content;
height: 56px;
border-radius: 52px;
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: #5088FF;
}
}
::v-deep.cluster {
color: #fff;
border-radius: 50%;
height: 120px;
width: 120px;
background: radial-gradient(circle, #5088ff 50%, rgba(#5088ff, .4) 100%);
display: flex;
align-items: center;
justify-content: center;
}
}
</style>