/**
|
* CBaiduMap - 百度地图组件
|
* @author chensi
|
*/
|
|
<template>
|
<view class="c-baidu-map">
|
<map
|
id="map"
|
class="map"
|
:latitude="mapLatitude"
|
:longitude="mapLongitude"
|
:markers="markers"
|
:scale="mapScale"
|
show-location
|
enable-rotate
|
enable-satellite
|
enable-traffic
|
enable-3D
|
enable-building
|
@tap="evt => handleMapTap(evt)"
|
@markertap="evt => handleMarkerTap(evt)"
|
@regionchange="evt => handleRegionChange(evt)"
|
></map>
|
<view class="map-controls">
|
<view class="map-controls-top">
|
<AtButton
|
type="primary"
|
size="small"
|
:onClick="evt => handleChooseLocation(evt)"
|
>选择位置</AtButton>
|
<AtButton
|
type="primary"
|
size="small"
|
:onClick="evt => handleGetLocation(evt)"
|
>获取当前位置</AtButton>
|
</view>
|
<view class="map-controls-bottom">
|
<AtButton
|
type="primary"
|
size="small"
|
class="btn-info"
|
:onClick="evt => handleSaveLocation(evt)"
|
>保存</AtButton>
|
</view>
|
</view>
|
</view>
|
</template>
|
|
<script>
|
import Taro from '@tarojs/taro';
|
import { AtButton } from 'taro-ui-vue';
|
import { BAIDU_MAP, BMapWX } from './index';
|
|
export default {
|
name: 'CBaiduMap',
|
components: {
|
AtButton,
|
},
|
props: {
|
latitude: {
|
type: Number,
|
default: 39.90886,
|
},
|
longitude: {
|
type: Number,
|
default: 116.39739,
|
},
|
},
|
data() {
|
return {
|
// 地图上下文
|
mapCtx: null,
|
// 地图是否准备就绪
|
isMapReady: false,
|
// 当前位置
|
currentLocation: null,
|
// 标记点id
|
selectedMarkerId: 0,
|
// 标记点列表
|
markers: [],
|
// 本地维护的坐标
|
localLatitude: this.latitude,
|
localLongitude: this.longitude,
|
// 地图坐标
|
mapLatitude: this.latitude,
|
mapLongitude: this.longitude,
|
// 地图缩放级别
|
mapScale: 14,
|
// 是否由chooseLocation触发移动
|
isFromChooseLocation: false,
|
};
|
},
|
watch: {
|
latitude: {
|
handler(newVal) {
|
if (newVal && this.longitude && this.isValidLatitude(newVal)) {
|
// 只在回显时更新本地坐标
|
this.localLatitude = newVal;
|
this.mapLatitude = newVal;
|
this.moveToLocation(newVal, this.longitude);
|
this.initLocation();
|
}
|
},
|
immediate: true,
|
},
|
longitude: {
|
handler(newVal) {
|
if (newVal && this.latitude && this.isValidLongitude(newVal)) {
|
// 只在回显时更新本地坐标
|
this.localLongitude = newVal;
|
this.mapLongitude = newVal;
|
this.moveToLocation(this.latitude, newVal);
|
this.initLocation();
|
}
|
},
|
immediate: true,
|
},
|
},
|
mounted() {
|
// 初始化地图
|
this.initMap();
|
// 如果有初始经纬度,添加标记点
|
if (
|
this.latitude &&
|
this.longitude &&
|
this.isValidLatitude(this.latitude) &&
|
this.isValidLongitude(this.longitude)
|
) {
|
this.initLocation();
|
}
|
},
|
methods: {
|
// 验证纬度是否有效
|
isValidLatitude(lat) {
|
return lat >= -90 && lat <= 90;
|
},
|
// 验证经度是否有效
|
isValidLongitude(lng) {
|
return lng >= -180 && lng <= 180;
|
},
|
// 初始化地图
|
initMap() {
|
try {
|
// 初始化地图上下文
|
this.mapCtx = Taro.createMapContext('map', this);
|
// 检查地图是否准备就绪
|
this.checkMapReady();
|
} catch (error) {
|
console.error('初始化地图失败:', error);
|
}
|
},
|
|
// 检查地图是否准备就绪
|
checkMapReady() {
|
if (this.mapCtx) {
|
this.mapCtx.getCenterLocation({
|
success: res => {
|
this.isMapReady = true;
|
},
|
fail: err => {
|
console.error('获取地图中心点失败:', err);
|
},
|
});
|
}
|
},
|
|
// 地图区域改变事件
|
async handleRegionChange(evt) {
|
// 如果是由chooseLocation触发的移动,则不处理
|
if (this.isFromChooseLocation) {
|
return;
|
}
|
if (!evt.detail || !evt.detail.centerLocation) {
|
return;
|
}
|
const { latitude, longitude } = evt.detail.centerLocation;
|
if (!latitude || !longitude) {
|
return;
|
}
|
try {
|
// 将 GCJ-02 坐标转换为百度坐标用于获取地址
|
const baiduCoords = this.gcj02ToBaidu(latitude, longitude);
|
// 获取地址信息
|
const address = await this.getAddressFromLocation(
|
baiduCoords.lat,
|
baiduCoords.lng,
|
);
|
if (!address || !address.address) {
|
return;
|
}
|
// 更新当前位置信息
|
this.currentLocation = {
|
latitude,
|
longitude,
|
address: address.address,
|
province: address.province,
|
city: address.city,
|
district: address.district,
|
street: address.street,
|
streetNumber: address.streetNumber,
|
};
|
// 添加标记点并显示地址信息
|
this.addMarker(latitude, longitude, address.address);
|
// 通知父组件位置已更新
|
this.$emit('locationSelected', this.currentLocation);
|
} catch (error) {
|
console.error('处理地图区域改变事件失败:', error);
|
}
|
},
|
|
// 添加标记点
|
addMarker(latitude, longitude, title = '') {
|
// 清除之前的标记点
|
this.markers = [];
|
// 添加新的标记点
|
this.markers.push({
|
id: this.selectedMarkerId++,
|
latitude,
|
longitude,
|
title,
|
width: 20,
|
height: 20,
|
callout: {
|
content: title || '未知位置',
|
color: '#000000',
|
fontSize: 14,
|
borderRadius: 4,
|
padding: 8,
|
display: 'ALWAYS', // 始终显示
|
bgColor: '#ffffff', // 背景色
|
borderWidth: 1, // 边框宽度
|
borderColor: '#cccccc', // 边框颜色
|
textAlign: 'center', // 文字居中
|
anchorY: -10, // 向上偏移,使文字显示在标记点上方
|
},
|
});
|
},
|
|
// 地图点击事件
|
async handleMapTap(e) {
|
if (!e || !e.detail) {
|
return;
|
}
|
const { latitude, longitude } = e.detail;
|
if (!latitude || !longitude) {
|
return;
|
}
|
try {
|
// 更新本地坐标
|
this.localLatitude = latitude;
|
this.localLongitude = longitude;
|
// 将 GCJ-02 坐标转换为百度坐标用于获取地址
|
const baiduCoords = this.gcj02ToBaidu(latitude, longitude);
|
// 获取地址信息
|
const address = await this.getAddressFromLocation(
|
baiduCoords.lat,
|
baiduCoords.lng,
|
);
|
if (!address || !address.address) {
|
Taro.showToast({
|
title: '获取地址失败',
|
icon: 'none',
|
});
|
return;
|
}
|
this.currentLocation = {
|
latitude,
|
longitude,
|
address: address.address,
|
province: address.province,
|
city: address.city,
|
district: address.district,
|
street: address.street,
|
streetNumber: address.streetNumber,
|
};
|
// 添加标记点
|
this.addMarker(latitude, longitude, address.address);
|
this.$emit('locationSelected', this.currentLocation);
|
} catch (error) {
|
console.error('处理地图点击事件失败:', error);
|
Taro.showToast({
|
title: '获取地址失败',
|
icon: 'none',
|
});
|
}
|
},
|
|
// 标记点点击事件
|
async handleMarkerTap(e) {
|
const { markerId } = e.detail;
|
const marker = this.markers.find(m => m.id === markerId);
|
if (marker) {
|
// 获取地址信息
|
const address = await this.getAddressFromLocation(
|
marker.latitude,
|
marker.longitude,
|
);
|
this.currentLocation = {
|
latitude: marker.latitude,
|
longitude: marker.longitude,
|
address: address.address,
|
province: address.province,
|
city: address.city,
|
district: address.district,
|
street: address.street,
|
streetNumber: address.streetNumber,
|
};
|
this.$emit('locationSelected', this.currentLocation);
|
}
|
},
|
|
// 移动到指定位置
|
moveToLocation(latitude, longitude) {
|
// 更新地图位置数据
|
this.mapLatitude = latitude;
|
this.mapLongitude = longitude;
|
this.mapScale = 16; // 设置合适的缩放级别
|
|
// 使用地图上下文移动到指定位置
|
if (this.mapCtx) {
|
this.mapCtx.moveToLocation({
|
latitude: latitude,
|
longitude: longitude,
|
success: () => {
|
// 更新标记点位置
|
if (this.currentLocation) {
|
this.addMarker(
|
latitude,
|
longitude,
|
this.currentLocation.address,
|
);
|
}
|
},
|
fail: err => {
|
console.error('移动失败:', err);
|
// 失败时也要尝试更新标记点
|
if (this.currentLocation) {
|
this.addMarker(
|
latitude,
|
longitude,
|
this.currentLocation.address,
|
);
|
}
|
},
|
});
|
} else {
|
// 地图上下文不可用时,直接更新标记点
|
if (this.currentLocation) {
|
this.addMarker(latitude, longitude, this.currentLocation.address);
|
}
|
}
|
},
|
|
// 获取当前位置按钮点击事件
|
async handleGetLocation(e) {
|
try {
|
// 显示加载中提示
|
Taro.showLoading({
|
title: '获取位置中...',
|
});
|
const res = await Taro.getLocation({
|
type: 'gcj02',
|
});
|
// 更新本地坐标
|
this.localLatitude = res.latitude;
|
this.localLongitude = res.longitude;
|
// 设置标志,防止handleRegionChange触发时覆盖地址
|
this.isFromChooseLocation = true;
|
// 将 GCJ-02 坐标转换为百度坐标用于获取地址
|
const baiduCoords = this.gcj02ToBaidu(res.latitude, res.longitude);
|
// 获取地址信息
|
const address = await this.getAddressFromLocation(
|
baiduCoords.lat,
|
baiduCoords.lng,
|
);
|
this.currentLocation = {
|
latitude: res.latitude,
|
longitude: res.longitude,
|
address: address.address,
|
province: address.province,
|
city: address.city,
|
district: address.district,
|
street: address.street,
|
streetNumber: address.streetNumber,
|
};
|
// 直接更新地图位置
|
this.mapLatitude = res.latitude;
|
this.mapLongitude = res.longitude;
|
this.mapScale = 16;
|
// 添加标记点
|
this.addMarker(res.latitude, res.longitude, address.address);
|
// 通知父组件位置已更新
|
this.$emit('locationSelected', this.currentLocation);
|
// 完成后隐藏加载提示
|
Taro.hideLoading();
|
// 使用延时恢复标志
|
setTimeout(() => {
|
this.isFromChooseLocation = false;
|
}, 800);
|
} catch (error) {
|
console.error('获取位置失败:', error);
|
Taro.hideLoading();
|
this.isFromChooseLocation = false;
|
Taro.showToast({
|
title: '获取位置失败',
|
icon: 'none',
|
});
|
}
|
},
|
|
// 选择位置按钮点击事件
|
async handleChooseLocation(e) {
|
try {
|
const res = await Taro.chooseLocation();
|
// 设置标志,防止handleRegionChange触发时覆盖地址
|
this.isFromChooseLocation = true;
|
// 更新本地坐标
|
this.localLatitude = res.latitude;
|
this.localLongitude = res.longitude;
|
this.currentLocation = {
|
latitude: res.latitude,
|
longitude: res.longitude,
|
address: res.address,
|
name: res.name || '',
|
province: '',
|
city: '',
|
district: '',
|
street: '',
|
streetNumber: '',
|
};
|
// 直接更新地图位置和添加标记点
|
this.mapLatitude = res.latitude;
|
this.mapLongitude = res.longitude;
|
this.mapScale = 16;
|
// 添加标记点
|
this.addMarker(res.latitude, res.longitude, res.address);
|
// 使用地图上下文移动到选择的位置
|
if (this.mapCtx) {
|
this.mapCtx.moveToLocation({
|
latitude: res.latitude,
|
longitude: res.longitude,
|
success: () => {
|
// 再次确保标记点显示
|
setTimeout(() => {
|
// 重置标志
|
this.isFromChooseLocation = false;
|
}, 800);
|
},
|
fail: err => {
|
console.error('移动失败:', err);
|
// 重置标志
|
this.isFromChooseLocation = false;
|
},
|
});
|
}
|
this.$emit('locationSelected', this.currentLocation);
|
} catch (error) {
|
console.error('选择位置失败:', error);
|
// 重置标志
|
this.isFromChooseLocation = false;
|
Taro.showToast({
|
title: '选择位置失败',
|
icon: 'none',
|
});
|
}
|
},
|
|
// 保存位置按钮点击事件
|
handleSaveLocation(e) {
|
if (!this.currentLocation) {
|
Taro.showToast({
|
title: '请先选择位置',
|
icon: 'none',
|
});
|
return;
|
}
|
this.$emit('saveLocation', this.currentLocation);
|
},
|
|
// 获取地址信息
|
async getAddressFromLocation(latitude, longitude) {
|
try {
|
const res = await Taro.request({
|
url: `${BAIDU_MAP.apiUrl}/reverse_geocoding/v3/`,
|
data: {
|
ak: BAIDU_MAP.ak,
|
output: 'json',
|
coordtype: BAIDU_MAP.coordType,
|
location: `${latitude},${longitude}`,
|
},
|
timeout: 10000,
|
});
|
|
if (!res.data) {
|
throw new Error('百度地图API返回数据为空');
|
}
|
|
if (res.data.status === 0) {
|
const result = res.data.result;
|
return {
|
address: result.formatted_address,
|
province: result.addressComponent.province,
|
city: result.addressComponent.city,
|
district: result.addressComponent.district,
|
street: result.addressComponent.street,
|
streetNumber: result.addressComponent.street_number,
|
};
|
} else {
|
return {
|
address: '获取地址失败',
|
province: '',
|
city: '',
|
district: '',
|
street: '',
|
streetNumber: '',
|
};
|
}
|
} catch (error) {
|
console.error('获取地址异常:', error);
|
throw error;
|
}
|
},
|
|
// GCJ-02坐标转百度坐标
|
gcj02ToBaidu(lat, lng) {
|
const PI = 3.14159265358979324;
|
const x_pi = (PI * 3000.0) / 180.0;
|
const z = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * x_pi);
|
const theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * x_pi);
|
const bd_lng = z * Math.cos(theta) + 0.0065;
|
const bd_lat = z * Math.sin(theta) + 0.006;
|
return {
|
lat: bd_lat,
|
lng: bd_lng,
|
};
|
},
|
|
// 百度坐标转腾讯坐标
|
baiduToGcj02(lat, lng) {
|
const PI = 3.14159265358979324;
|
const x_pi = (PI * 3000.0) / 180.0;
|
const x = lng - 0.0065;
|
const y = lat - 0.006;
|
const z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
|
const theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
|
const gg_lng = z * Math.cos(theta);
|
const gg_lat = z * Math.sin(theta);
|
return {
|
lat: gg_lat,
|
lng: gg_lng,
|
};
|
},
|
|
// 初始化位置信息
|
async initLocation() {
|
try {
|
// 将 GCJ-02 坐标转换为百度坐标用于获取地址
|
const baiduCoords = this.gcj02ToBaidu(this.latitude, this.longitude);
|
// 获取地址信息
|
const address = await this.getAddressFromLocation(
|
baiduCoords.lat,
|
baiduCoords.lng,
|
);
|
if (!address || !address.address) {
|
return;
|
}
|
// 更新当前位置信息
|
this.currentLocation = {
|
latitude: this.latitude,
|
longitude: this.longitude,
|
address: address.address,
|
province: address.province,
|
city: address.city,
|
district: address.district,
|
street: address.street,
|
streetNumber: address.streetNumber,
|
};
|
// 添加标记点
|
this.addMarker(this.latitude, this.longitude, address.address);
|
// 通知父组件位置已更新
|
this.$emit('locationSelected', this.currentLocation);
|
} catch (error) {
|
console.error('初始化位置信息失败:', error);
|
}
|
},
|
},
|
};
|
</script>
|
|
<style lang="scss">
|
.c-baidu-map {
|
width: 100%;
|
height: 100%;
|
position: relative;
|
display: block;
|
|
.map {
|
width: 100%;
|
height: 100%;
|
display: block;
|
}
|
|
.map-controls {
|
position: absolute;
|
bottom: 0;
|
left: 0;
|
right: 0;
|
background: rgba(255, 255, 255, 0.9);
|
z-index: 100;
|
|
.map-controls-top {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
height: 100px;
|
padding: 0 20px;
|
.at-button {
|
width: 45%;
|
height: 76px !important;
|
line-height: 72px;
|
font-size: 40px;
|
}
|
}
|
.map-controls-bottom {
|
display: flex;
|
align-items: center;
|
height: 100px;
|
padding: 0 20px;
|
.at-button {
|
width: 95%;
|
height: 76px !important;
|
line-height: 72px;
|
font-size: 40px;
|
}
|
}
|
}
|
}
|
</style>
|