HomeLease/components/map-location/map-location.vue

432 lines
10 KiB
Vue
Raw Normal View History

2025-08-19 09:51:32 +08:00
<template>
<view class="map-location-component">
<!-- 地址输入框 -->
<view class="form-item address-form-item">
<text class="field-label">{{ label || '地址' }}</text>
<view class="address-input-wrapper">
<input
v-model="addressValue"
:placeholder="placeholder || '请输入或选择收货地址'"
2025-08-21 09:46:31 +08:00
class="address-input"
2025-08-19 09:51:32 +08:00
@focus="onAddressFocus"
@input="onAddressInput"
/>
2025-08-21 09:46:31 +08:00
<view class="map-icon-wrapper" title="点击获取定位并打开地图" @click="openMapWithLocation">
2025-08-19 09:51:32 +08:00
<text class="map-icon">🗺</text>
</view>
</view>
</view>
<!-- 当前定位 -->
2025-08-21 09:46:31 +08:00
<!-- <view class="location-suggestion">-->
<!-- <view v-if="currentLocation" class="location-card">-->
<!-- <view class="location-content">-->
<!-- <view class="location-header">-->
<!-- <text class="location-title">当前定位</text>-->
<!-- </view>-->
<!-- <text class="location-address">{{ currentLocation.address }}</text>-->
<!-- </view>-->
<!-- <button class="use-location-btn" @click="useCurrentLocation">使用</button>-->
<!-- </view>-->
<!-- <view v-else class="location-card">-->
<!-- <view class="location-content">-->
<!-- <view class="location-header">-->
<!-- <text class="location-title">获取位置</text>-->
<!-- <text class="location-company">点击获取当前位置</text>-->
<!-- </view>-->
<!-- <text class="location-address">需要定位权限</text>-->
<!-- </view>-->
<!-- <button class="get-location-btn" @click="getCurrentLocation">获取</button>-->
<!-- </view>-->
<!-- </view>-->
2025-08-19 09:51:32 +08:00
</view>
</template>
<script>
import { getLocationWithPermission, handleLocationError } from '@/utils/permission.js'
export default {
name: 'MapLocation',
props: {
// 表单标签
label: {
type: String,
2025-08-21 09:46:31 +08:00
default: '地址',
2025-08-19 09:51:32 +08:00
},
// 输入框占位符
placeholder: {
type: String,
2025-08-21 09:46:31 +08:00
default: '请输入或选择收货地址',
2025-08-19 09:51:32 +08:00
},
// 地址值
value: {
type: String,
2025-08-21 09:46:31 +08:00
default: '',
2025-08-19 09:51:32 +08:00
},
// 是否显示位置建议卡片
showLocationCard: {
type: Boolean,
2025-08-21 09:46:31 +08:00
default: true,
},
2025-08-19 09:51:32 +08:00
},
data() {
return {
currentLocation: null,
isMapTriggered: false,
2025-08-21 09:46:31 +08:00
addressValue: this.value,
2025-08-19 09:51:32 +08:00
}
},
watch: {
value(newVal) {
this.addressValue = newVal
},
addressValue(newVal) {
this.$emit('input', newVal)
2025-08-21 09:46:31 +08:00
},
2025-08-19 09:51:32 +08:00
},
methods: {
// 获取当前位置信息
async getCurrentLocation() {
try {
uni.showLoading({
title: '获取位置中...',
})
const location = await getLocationWithPermission()
console.log('位置信息:', location)
// 直接使用坐标信息,简化处理
this.currentLocation = {
address: `纬度: ${location.latitude.toFixed(6)}, 经度: ${location.longitude.toFixed(6)}`,
latitude: location.latitude,
longitude: location.longitude,
}
uni.hideLoading()
// 如果不是通过地图图标触发的,才显示提示
if (!this.isMapTriggered) {
uni.showToast({
title: '位置获取成功,点击地图图标查看',
icon: 'success',
duration: 3000,
})
}
// 触发位置获取成功事件
this.$emit('location-success', this.currentLocation)
return this.currentLocation
} catch (err) {
uni.hideLoading()
handleLocationError(err)
this.$emit('location-error', err)
throw err
}
},
// 地址输入框获得焦点
onAddressFocus() {
this.$emit('address-focus')
},
// 地址输入
onAddressInput(e) {
2025-08-19 11:11:27 +08:00
const value = e.detail.value
this.addressValue = value
this.$emit('input', value)
this.$emit('address-input', value)
2025-08-19 09:51:32 +08:00
},
// 点击地图图标,自动获取定位并打开地图
async openMapWithLocation() {
try {
this.isMapTriggered = true
// 先获取当前位置
await this.getCurrentLocation()
// 获取成功后自动打开地图
this.openMap()
} catch (err) {
console.error('获取位置失败:', err)
uni.showToast({
title: '获取位置失败',
icon: 'error',
})
} finally {
this.isMapTriggered = false
}
},
// 打开地图
openMap() {
if (!this.currentLocation) {
uni.showToast({
title: '请先获取位置信息',
icon: 'none',
})
return
}
const { latitude, longitude } = this.currentLocation
// 显示加载提示
uni.showLoading({
title: '打开地图中...',
})
// 尝试打开系统地图应用
// #ifdef APP-PLUS
uni.openLocation({
latitude: latitude,
longitude: longitude,
name: '当前位置',
address: this.currentLocation.address || '未知地址',
scale: 18,
success: () => {
uni.hideLoading()
console.log('地图打开成功')
this.$emit('map-opened')
},
fail: err => {
uni.hideLoading()
console.error('打开地图失败:', err)
this.showMapInApp()
},
})
// #endif
// #ifdef MP-WEIXIN
uni.openLocation({
latitude: latitude,
longitude: longitude,
name: '当前位置',
address: this.currentLocation.address || '未知地址',
scale: 18,
success: () => {
uni.hideLoading()
this.$emit('map-opened')
},
fail: err => {
uni.hideLoading()
console.error('打开地图失败:', err)
this.showMapInApp()
},
})
// #endif
// #ifdef H5
// H5环境下使用在线地图
uni.hideLoading()
const mapUrl = `https://uri.amap.com/marker?position=${longitude},${latitude}&name=${encodeURIComponent('当前位置')}&address=${encodeURIComponent(this.currentLocation.address || '未知地址')}`
window.open(mapUrl, '_blank')
this.$emit('map-opened')
// #endif
},
// 在应用内显示地图(备用方案)
showMapInApp() {
uni.showModal({
title: '地图功能',
content: `${this.currentLocation.address}\n\n经纬度${this.currentLocation.latitude.toFixed(6)}, ${this.currentLocation.longitude.toFixed(6)}`,
showCancel: false,
confirmText: '确定',
})
},
// 使用当前定位
useCurrentLocation() {
if (!this.currentLocation) {
this.getCurrentLocation()
return
}
this.addressValue = this.currentLocation.address
2025-08-19 11:11:27 +08:00
this.$emit('input', this.currentLocation.address)
2025-08-19 09:51:32 +08:00
this.$emit('use-location', this.currentLocation)
2025-08-21 09:46:31 +08:00
2025-08-19 09:51:32 +08:00
uni.showToast({
title: '已使用当前定位',
icon: 'success',
})
},
// 手动设置位置信息(供外部调用)
setLocation(location) {
this.currentLocation = location
},
// 清空位置信息
clearLocation() {
this.currentLocation = null
2025-08-21 09:46:31 +08:00
},
},
2025-08-19 09:51:32 +08:00
}
</script>
<style lang="scss" scoped>
.map-location-component {
width: 100%;
}
// 地址表单项特殊样式
.address-form-item {
margin-bottom: 20rpx;
display: flex;
align-items: center;
border-bottom: 1rpx solid #d8d8d8;
.field-label {
display: block;
font-size: 28rpx;
color: #333;
font-weight: 400;
flex: 1;
}
}
// 地址输入框包装器
.address-input-wrapper {
flex: 3;
display: flex;
align-items: center;
height: 80rpx;
padding: 0 20rpx;
border-radius: 12rpx;
background: #fff;
transition: all 0.3s ease;
&:focus-within {
border-color: #f15a04;
box-shadow: 0 0 0 2rpx rgba(241, 90, 4, 0.1);
}
.address-input {
flex: 1;
height: 100%;
font-size: 28rpx;
color: #333;
border: none;
outline: none;
background: transparent;
&::placeholder {
color: #999;
}
}
.map-icon-wrapper {
display: flex;
align-items: center;
justify-content: center;
width: 48rpx;
height: 48rpx;
background: #f15a04;
border-radius: 50%;
transition: all 0.3s ease;
margin-left: 16rpx;
&:active {
transform: scale(0.9);
box-shadow: 0 2rpx 8rpx rgba(241, 90, 4, 0.4);
}
.map-icon {
font-size: 28rpx;
color: white;
}
}
}
// 位置建议卡片
.location-suggestion {
margin-bottom: 40rpx;
.location-card {
display: flex;
align-items: center;
justify-content: space-between;
background: white;
border: 2rpx solid #e8f4fd;
border-radius: 16rpx;
padding: 24rpx;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
position: relative;
&::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 6rpx;
background: #f3f5f6;
border-radius: 6rpx 0 0 6rpx;
}
.location-content {
flex: 1;
margin-right: 20rpx;
.location-header {
display: flex;
align-items: center;
margin-bottom: 8rpx;
.location-title {
font-size: 24rpx;
color: #f15a04;
font-weight: 500;
margin-right: 12rpx;
}
.location-company {
font-size: 26rpx;
color: #333;
font-weight: 500;
}
}
.location-address {
font-size: 24rpx;
color: #666;
line-height: 1.4;
}
}
.use-location-btn {
background: #f15a04;
color: #000000;
border: none;
border-radius: 24rpx;
padding: 12rpx 24rpx;
font-size: 24rpx;
font-weight: 500;
box-shadow: 0 4rpx 12rpx rgba(255, 107, 107, 0.3);
transition: all 0.3s ease;
&:active {
transform: translateY(2rpx);
box-shadow: 0 2rpx 8rpx rgba(255, 107, 107, 0.4);
}
}
.get-location-btn {
background: #f15a04;
color: #ffffff;
border: none;
border-radius: 24rpx;
padding: 12rpx 24rpx;
font-size: 24rpx;
font-weight: 500;
box-shadow: 0 4rpx 12rpx rgba(255, 107, 107, 0.3);
transition: all 0.3s ease;
&:active {
transform: translateY(2rpx);
box-shadow: 0 2rpx 8rpx rgba(255, 107, 107, 0.4);
}
}
}
}
2025-08-21 09:46:31 +08:00
</style>