| | |
| | | type="primary" |
| | | size="small" |
| | | :onClick="evt => handleChooseLocation(evt)" |
| | | >选择位置</AtButton> |
| | | >选择地点</AtButton> |
| | | <AtButton |
| | | type="primary" |
| | | size="small" |
| | |
| | | size="small" |
| | | class="btn-info" |
| | | :onClick="evt => handleSaveLocation(evt)" |
| | | >保存</AtButton> |
| | | >确认</AtButton> |
| | | </view> |
| | | </view> |
| | | </view> |
| | |
| | | import Taro from '@tarojs/taro'; |
| | | import { AtButton } from 'taro-ui-vue'; |
| | | import { BAIDU_MAP, BMapWX } from './index'; |
| | | import './cBaiduMap.scss'; |
| | | |
| | | export default { |
| | | name: 'CBaiduMap', |
| | |
| | | }, |
| | | 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) { |
| | | console.log(newVal, '纬度'); |
| | | if (newVal && this.longitude && this.isValidLatitude(newVal)) { |
| | | this.mapLatitude = newVal; |
| | | // 只在回显时更新本地坐标 |
| | | this.localLatitude = newVal; |
| | | this.moveToLocation(newVal, this.longitude); |
| | | this.initLocation(); |
| | | } |
| | | }, |
| | | immediate: true, |
| | | immediate: false, |
| | | }, |
| | | longitude: { |
| | | handler(newVal) { |
| | | console.log(newVal, '经度'); |
| | | if (newVal && this.latitude && this.isValidLongitude(newVal)) { |
| | | this.mapLongitude = newVal; |
| | | // 只在回显时更新本地坐标 |
| | | this.localLongitude = newVal; |
| | | this.moveToLocation(this.latitude, newVal); |
| | | this.initLocation(); |
| | | } |
| | | }, |
| | | immediate: true, |
| | | immediate: false, |
| | | }, |
| | | }, |
| | | mounted() { |
| | | // 初始化地图 |
| | | this.initMap(); |
| | | // 如果有初始经纬度,添加标记点 |
| | | if ( |
| | | this.latitude && |
| | | this.longitude && |
| | | this.isValidLatitude(this.latitude) && |
| | | this.isValidLongitude(this.longitude) |
| | | ) { |
| | | this.initLocation(); |
| | | } |
| | | // 设置一个延时,确保地图组件已完全加载 |
| | | setTimeout(() => { |
| | | // 使用默认值处理可能的undefined或null |
| | | const latitude = this.latitude || 39.90886; |
| | | const longitude = this.longitude || 116.39739; |
| | | // 确保坐标有效 |
| | | if (this.isValidLatitude(latitude) && this.isValidLongitude(longitude)) { |
| | | this.localLatitude = latitude; |
| | | this.localLongitude = longitude; |
| | | this.initLocation(); |
| | | } |
| | | }, 800); |
| | | }, |
| | | 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) { |
| | |
| | | |
| | | // 地图区域改变事件 |
| | | 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(latitude, longitude); |
| | | const address = await this.getAddressFromLocation( |
| | | baiduCoords.lat, |
| | | baiduCoords.lng, |
| | | ); |
| | | if (!address || !address.address) { |
| | | return; |
| | | } |
| | | |
| | | // 更新当前位置信息 |
| | | this.currentLocation = { |
| | | latitude, |
| | | longitude, |
| | | latitude: baiduCoords.lat, |
| | | longitude: baiduCoords.lng, |
| | | gcjLatitude: latitude, |
| | | gcjLongitude: longitude, |
| | | address: address.address, |
| | | province: address.province, |
| | | city: address.city, |
| | |
| | | street: address.street, |
| | | streetNumber: address.streetNumber, |
| | | }; |
| | | |
| | | // 添加标记点并显示地址信息 |
| | | this.addMarker(latitude, longitude, address.address); |
| | | // 通知父组件位置已更新 |
| | |
| | | width: 20, |
| | | height: 20, |
| | | callout: { |
| | | content: title, |
| | | content: title || '未知位置', |
| | | color: '#000000', |
| | | fontSize: 14, |
| | | borderRadius: 4, |
| | |
| | | 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(latitude, longitude); |
| | | |
| | | const address = await this.getAddressFromLocation( |
| | | baiduCoords.lat, |
| | | baiduCoords.lng, |
| | | ); |
| | | if (!address || !address.address) { |
| | | Taro.showToast({ |
| | | title: '获取地址失败', |
| | |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | this.currentLocation = { |
| | | latitude, |
| | | longitude, |
| | | latitude: baiduCoords.lat, |
| | | longitude: baiduCoords.lng, |
| | | gcjLatitude: latitude, |
| | | gcjLongitude: longitude, |
| | | address: address.address, |
| | | province: address.province, |
| | | city: address.city, |
| | |
| | | street: address.street, |
| | | streetNumber: address.streetNumber, |
| | | }; |
| | | |
| | | // 添加标记点 |
| | | this.addMarker(latitude, longitude, address.address); |
| | | this.$emit('locationSelected', this.currentLocation); |
| | |
| | | const { markerId } = e.detail; |
| | | const marker = this.markers.find(m => m.id === markerId); |
| | | if (marker) { |
| | | // 转换为百度坐标 |
| | | const baiduCoords = this.gcj02ToBaidu(marker.latitude, marker.longitude); |
| | | // 获取地址信息 |
| | | const address = await this.getAddressFromLocation( |
| | | marker.latitude, |
| | | marker.longitude, |
| | | baiduCoords.lat, |
| | | baiduCoords.lng, |
| | | ); |
| | | this.currentLocation = { |
| | | latitude: marker.latitude, |
| | | longitude: marker.longitude, |
| | | latitude: baiduCoords.lat, |
| | | longitude: baiduCoords.lng, |
| | | gcjLatitude: marker.latitude, |
| | | gcjLongitude: marker.longitude, |
| | | address: address.address, |
| | | province: address.province, |
| | | city: address.city, |
| | |
| | | |
| | | // 移动到指定位置 |
| | | moveToLocation(latitude, longitude) { |
| | | // 更新地图位置 |
| | | // 更新地图位置数据 |
| | | this.mapLatitude = latitude; |
| | | this.mapLongitude = longitude; |
| | | this.mapScale = 16; // 设置合适的缩放级别 |
| | | |
| | | // 更新标记点位置 |
| | | if (this.currentLocation) { |
| | | this.addMarker(latitude, longitude, this.currentLocation.address); |
| | | // 使用地图上下文移动到指定位置 |
| | | 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', |
| | | }); |
| | | |
| | | // 将GCJ-02坐标转换为百度坐标 |
| | | const baiduLocation = this.gcj02ToBaidu(res.latitude, res.longitude); |
| | | |
| | | // 更新本地坐标 |
| | | 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( |
| | | baiduLocation.lat, |
| | | baiduLocation.lng, |
| | | baiduCoords.lat, |
| | | baiduCoords.lng, |
| | | ); |
| | | |
| | | this.currentLocation = { |
| | | latitude: baiduLocation.lat, |
| | | longitude: baiduLocation.lng, |
| | | latitude: baiduCoords.lat, |
| | | longitude: baiduCoords.lng, |
| | | gcjLatitude: res.latitude, |
| | | gcjLongitude: res.longitude, |
| | | address: address.address, |
| | | province: address.province, |
| | | city: address.city, |
| | |
| | | street: address.street, |
| | | streetNumber: address.streetNumber, |
| | | }; |
| | | |
| | | // 移动到当前位置(使用原始GCJ-02坐标) |
| | | this.moveToLocation(res.latitude, res.longitude); |
| | | // 直接更新地图位置 |
| | | 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(); |
| | | |
| | | // 将GCJ-02坐标转换为百度坐标 |
| | | const baiduLocation = this.gcj02ToBaidu(res.latitude, res.longitude); |
| | | |
| | | // 设置标志,防止handleRegionChange触发时覆盖地址 |
| | | this.isFromChooseLocation = true; |
| | | // 更新本地坐标 |
| | | this.localLatitude = res.latitude; |
| | | this.localLongitude = res.longitude; |
| | | // 转换为百度坐标 |
| | | const baiduCoords = this.gcj02ToBaidu(res.latitude, res.longitude); |
| | | this.currentLocation = { |
| | | latitude: baiduLocation.lat, |
| | | longitude: baiduLocation.lng, |
| | | latitude: baiduCoords.lat, |
| | | longitude: baiduCoords.lng, |
| | | gcjLatitude: res.latitude, |
| | | gcjLongitude: res.longitude, |
| | | address: res.address, |
| | | name: res.name || '', |
| | | province: '', |
| | | city: '', |
| | | district: '', |
| | | street: '', |
| | | streetNumber: '', |
| | | }; |
| | | |
| | | // 移动到选择的位置(使用原始GCJ-02坐标) |
| | | this.moveToLocation(res.latitude, res.longitude); |
| | | // 直接更新地图位置和添加标记点 |
| | | 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', |
| | |
| | | }, |
| | | timeout: 10000, |
| | | }); |
| | | |
| | | if (!res.data) { |
| | | throw new Error('百度地图API返回数据为空'); |
| | | } |
| | | |
| | | if (res.data.status === 0) { |
| | | const result = res.data.result; |
| | | return { |
| | |
| | | |
| | | // GCJ-02坐标转百度坐标 |
| | | gcj02ToBaidu(lat, lng) { |
| | | const x_pi = (3.14159265358979324 * 3000.0) / 180.0; |
| | | const x = lng; |
| | | const y = lat; |
| | | 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 lngs = z * Math.cos(theta); |
| | | const lats = z * Math.sin(theta); |
| | | 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: lats, |
| | | lng: lngs, |
| | | lat: bd_lat, |
| | | lng: bd_lng, |
| | | }; |
| | | }, |
| | | |
| | | // 百度坐标转腾讯坐标 |
| | | baiduToGcj02(lat, lng) { |
| | | const x_pi = (3.14159265358979324 * 3000.0) / 180.0; |
| | | 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 lngs = z * Math.cos(theta); |
| | | const lats = z * Math.sin(theta); |
| | | const gg_lng = z * Math.cos(theta); |
| | | const gg_lat = z * Math.sin(theta); |
| | | return { |
| | | lat: lats, |
| | | lng: lngs, |
| | | lat: gg_lat, |
| | | lng: gg_lng, |
| | | }; |
| | | }, |
| | | |
| | | // 初始化位置信息 |
| | | async initLocation() { |
| | | try { |
| | | // 获取地址信息 |
| | | const address = await this.getAddressFromLocation( |
| | | this.latitude, |
| | | this.longitude, |
| | | ); |
| | | if (!address || !address.address) { |
| | | return; |
| | | // 设置标志,防止handleRegionChange触发时覆盖地址 |
| | | this.isFromChooseLocation = true; |
| | | // 显示加载提示 |
| | | Taro.showLoading({ |
| | | title: '加载位置信息...', |
| | | }); |
| | | try { |
| | | // 使用本地坐标(初始默认值或props传入值)获取地址信息 |
| | | const address = await this.getAddressFromLocation( |
| | | this.localLatitude, |
| | | this.localLongitude, |
| | | ); |
| | | if (!address || !address.address) { |
| | | console.warn('获取地址信息失败'); |
| | | Taro.hideLoading(); |
| | | this.isFromChooseLocation = false; |
| | | return; |
| | | } |
| | | // 将百度坐标转换为腾讯坐标 |
| | | const gcjCoords = this.baiduToGcj02( |
| | | this.localLatitude, |
| | | this.localLongitude, |
| | | ); |
| | | // 更新当前位置信息 |
| | | this.currentLocation = { |
| | | latitude: this.localLatitude, |
| | | longitude: this.localLongitude, |
| | | gcjLatitude: gcjCoords.lat, |
| | | gcjLongitude: gcjCoords.lng, |
| | | address: address.address, |
| | | province: address.province, |
| | | city: address.city, |
| | | district: address.district, |
| | | street: address.street, |
| | | streetNumber: address.streetNumber, |
| | | }; |
| | | // 使用腾讯坐标更新地图位置 |
| | | this.mapLatitude = gcjCoords.lat; |
| | | this.mapLongitude = gcjCoords.lng; |
| | | this.mapScale = 16; |
| | | // 添加标记点 |
| | | this.markers = [ |
| | | { |
| | | id: this.selectedMarkerId++, |
| | | latitude: gcjCoords.lat, |
| | | longitude: gcjCoords.lng, |
| | | title: address.address, |
| | | width: 30, |
| | | height: 30, |
| | | callout: { |
| | | content: address.address || '未知位置', |
| | | color: '#000000', |
| | | fontSize: 14, |
| | | borderRadius: 4, |
| | | padding: 8, |
| | | display: 'ALWAYS', |
| | | bgColor: '#ffffff', |
| | | borderWidth: 1, |
| | | borderColor: '#cccccc', |
| | | textAlign: 'center', |
| | | anchorY: -10, |
| | | }, |
| | | }, |
| | | ]; |
| | | // 通知父组件位置已更新 |
| | | this.$emit('locationSelected', this.currentLocation); |
| | | // 隐藏加载提示 |
| | | Taro.hideLoading(); |
| | | // 使用地图上下文移动到位置中心 |
| | | if (this.mapCtx) { |
| | | this.mapCtx.moveToLocation({ |
| | | latitude: gcjCoords.lat, |
| | | longitude: gcjCoords.lng, |
| | | success: () => { |
| | | // 延迟重置标志 |
| | | setTimeout(() => { |
| | | this.isFromChooseLocation = false; |
| | | }, 800); |
| | | }, |
| | | fail: err => { |
| | | console.error('地图移动失败:', err); |
| | | this.isFromChooseLocation = false; |
| | | }, |
| | | }); |
| | | } else { |
| | | this.isFromChooseLocation = false; |
| | | } |
| | | } catch (error) { |
| | | console.error('处理位置信息失败:', error); |
| | | Taro.hideLoading(); |
| | | this.isFromChooseLocation = false; |
| | | } |
| | | |
| | | // 更新当前位置信息 |
| | | 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); |
| | | console.error('初始化位置信息失败:', error); |
| | | Taro.hideLoading(); |
| | | this.isFromChooseLocation = false; |
| | | } |
| | | }, |
| | | }, |
| | | }; |
| | | </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> |
| | | </script> |