Files
im/pages/common/map.vue
T
cansnow 5a086fa1fa 13
2025-12-11 22:33:31 +08:00

333 lines
7.4 KiB
Vue

<template>
<view class="map_page">
<uni-nav-bar
left-icon="back"
@clickLeft="back"
statusBar
fixed
backgroundColor="transparent"
>
<template slot="right">
<u-button type="primary" @click="confirm">确定</u-button>
</template>
</uni-nav-bar>
<map
ref="map"
class="map_containter"
:latitude="mapCenter.lat"
:longitude="mapCenter.lng"
:scale="scale"
@regionchange="onMapRegionChange"
show-location
>
<!-- 中心点marker -->
<cover-view class="center_marker">
<cover-image src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3E%3Ccircle cx='15' cy='15' r='12' fill='%23FF4444' opacity='0.8'/%3E%3Ccircle cx='15' cy='15' r='8' fill='%23FFFFFF'/%3E%3C/svg%3E"></cover-image>
</cover-view>
</map>
<!-- 加载提示 -->
<view v-if="isLoading" class="loading_overlay">
<uni-load-more status="loading" content-text="加载中"></uni-load-more>
</view>
<!-- 地址信息展示 -->
<view class="address_info">
<view class="address_title">位置信息</view>
<view class="address_text">{{ currentAddress || '加载中...' }}</view>
<view class="coordinates">
经度: {{ mapCenter.lng.toFixed(6) }} | 纬度: {{ mapCenter.lat.toFixed(6) }}
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
// 地图中心点
mapCenter: {
lng: 116.397128,
lat: 39.916527
},
// 初始中心点(用于比较)
initialCenter: {
lng: 116.397128,
lat: 39.916527
},
// 地图缩放级别
scale: 16,
// 当前地址
currentAddress: '北京市朝阳区',
// 选中的位置数据
selectedPoint: null,
// 是否正在加载
isLoading: false,
// 地图对象
mapContext: null,
// 地址解析延迟器
addressTimer: null,
// 高德地图API key(需要替换为实际的key)
aMapKey: ''
}
},
mounted() {
this.initMap();
},
beforeDestroy() {
// 清理定时器
if (this.addressTimer) {
clearTimeout(this.addressTimer);
}
},
methods: {
/**
* 初始化地图
*/
initMap() {
this.mapContext = uni.createMapContext('map', this);
// 获取当前位置权限
this.getCurrentLocation();
},
/**
* 获取当前位置
*/
getCurrentLocation() {
uni.getLocation({
type: 'gcj02',
success: (res) => {
this.mapCenter = {
lng: res.longitude,
lat: res.latitude
};
this.initialCenter = {
lng: res.longitude,
lat: res.latitude
};
// 获取地址
this.getAddressFromCoordinates(res.longitude, res.latitude);
},
fail: (err) => {
// 权限拒绝或失败,使用默认位置
console.log('获取位置失败,使用默认位置', err);
this.getAddressFromCoordinates(this.mapCenter.lng, this.mapCenter.lat);
}
});
},
/**
* 地图区域变化时触发
*/
onMapRegionChange(e) {
if (e.type === 'end') {
// 获取地图中心点坐标
this.mapContext.getCenterLocation({
success: (res) => {
this.mapCenter = {
lng: res.longitude,
lat: res.latitude
};
// 延迟获取地址,避免频繁调用
this.debounceGetAddress();
},
fail: (err) => {
console.log('获取地图中心点失败', err);
}
});
}
},
/**
* 防抖获取地址
*/
debounceGetAddress() {
if (this.addressTimer) {
clearTimeout(this.addressTimer);
}
this.addressTimer = setTimeout(() => {
this.getAddressFromCoordinates(this.mapCenter.lng, this.mapCenter.lat);
}, 500);
},
/**
* 根据坐标获取地址(使用高德地图API)
*/
getAddressFromCoordinates(lng, lat) {
// 方法1:使用高德地图逆地理编码API
// 注意:需要在高德地图申请API key
const aMapUrl = `https://restapi.amap.com/v3/geocode/regeo?location=${lng},${lat}&key=YOUR_AMAP_KEY`;
// 这里使用本地模拟,实际项目中应该调用真实API
this.simulateAddressLookup(lng, lat);
},
/**
* 模拟地址查询(实际项目应调用真实的地理编码API)
*/
simulateAddressLookup(lng, lat) {
// 简单的地址模拟逻辑
const addresses = [
'北京市朝阳区建国路88号',
' 北京市东城区天安门广场',
'上海市浦东新区陆家嘴环路1088号',
'深圳市福田区中心区',
'杭州市上城区武林路'
];
// 根据坐标返回对应的地址(这里是随机模拟)
const index = Math.floor((lng + lat) % addresses.length);
this.currentAddress = addresses[index] || `经度: ${lng.toFixed(6)}, 纬度: ${lat.toFixed(6)}`;
},
/**
* 确定位置按钮点击
*/
confirm() {
const _this = this;
// 验证是否有有效的位置数据
if (!this.mapCenter.lng || !this.mapCenter.lat) {
uni.showToast({
title: '位置数据无效',
icon: 'none'
});
return;
}
// 保存选中的位置
_this.selectedPoint = {
lng: this.mapCenter.lng,
lat: this.mapCenter.lat,
address: this.currentAddress
};
// 通过事件通道返回数据给父页面
const eventChannel = this.getOpenerEventChannel();
eventChannel.emit('onConfirm', {
lng: this.mapCenter.lng,
lat: this.mapCenter.lat,
address: this.currentAddress
});
// 显示成功提示
uni.showToast({
title: '位置已确定',
icon: 'success',
duration: 1500
});
// 延迟返回,让用户看到提示
setTimeout(() => {
uni.navigateBack();
}, 1500);
},
/**
* 返回上一页
*/
back() {
uni.navigateBack();
}
}
}
</script>
<style lang="scss" scoped>
.map_page {
display: flex;
flex-direction: column;
height: 100vh;
width: 100vw;
background: #fff;
position: relative;
overflow: hidden;
.map_containter {
flex: 1;
width: 100%;
height: 100%;
}
// 中心点marker样式
.center_marker {
position: absolute;
top: 50%;
left: 50%;
width: 30px;
height: 30px;
margin-left: -15px;
margin-top: -15px;
z-index: 100;
pointer-events: none;
cover-image {
width: 100%;
height: 100%;
animation: bounce 1.5s ease-in-out infinite;
}
}
// 加载覆盖层
.loading_overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.3);
z-index: 200;
}
// 地址信息展示框
.address_info {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
padding: 12px 16px;
z-index: 99;
border-top: 1px solid #e5e5e5;
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
.address_title {
font-size: 12px;
color: #999;
margin-bottom: 6px;
}
.address_text {
color: #333;
font-size: 14px;
line-height: 1.5;
word-break: break-word;
margin-bottom: 6px;
font-weight: 500;
}
.coordinates {
color: #666;
font-size: 12px;
font-family: monospace;
}
}
}
// 跳动动画
@keyframes bounce {
0%, 100% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(1.2);
opacity: 0.8;
}
}
</style>