HomeLease/components/map-location/map-location.vue
2025-08-19 11:11:27 +08:00

435 lines
10 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>
<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"
class="address-input"
:placeholder="placeholder || '请输入或选择收货地址'"
@focus="onAddressFocus"
@input="onAddressInput"
/>
<view
class="map-icon-wrapper"
title="点击获取定位并打开地图"
@click="openMapWithLocation"
>
<text class="map-icon">🗺️</text>
</view>
</view>
</view>
<!-- 当前定位 -->
<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>
</view>
</template>
<script>
import { getLocationWithPermission, handleLocationError } from '@/utils/permission.js'
export default {
name: 'MapLocation',
props: {
// 表单标签
label: {
type: String,
default: '地址'
},
// 输入框占位符
placeholder: {
type: String,
default: '请输入或选择收货地址'
},
// 地址值
value: {
type: String,
default: ''
},
// 是否显示位置建议卡片
showLocationCard: {
type: Boolean,
default: true
}
},
data() {
return {
currentLocation: null,
isMapTriggered: false,
addressValue: this.value
}
},
watch: {
value(newVal) {
this.addressValue = newVal
},
addressValue(newVal) {
this.$emit('input', newVal)
}
},
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) {
const value = e.detail.value
this.addressValue = value
this.$emit('input', value)
this.$emit('address-input', value)
},
// 点击地图图标,自动获取定位并打开地图
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
this.$emit('input', this.currentLocation.address)
this.$emit('use-location', this.currentLocation)
uni.showToast({
title: '已使用当前定位',
icon: 'success',
})
},
// 手动设置位置信息(供外部调用)
setLocation(location) {
this.currentLocation = location
},
// 清空位置信息
clearLocation() {
this.currentLocation = null
}
}
}
</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);
}
}
}
}
</style>