小程序 地理位置授权怎么搞

用uniapp开发小程序,获取位置信息-地理位置授权,怎么做

大概实现样子:

真机

代码如下:

javascript 复制代码
<template>
	<view class="choose-store-wrap">
		<view class="search-section">
			<view class="city-selector" @click="selectCity">
				<text class="city-name">{{ selectedCity }}</text>
				<text class="arrow">▼</text>
			</view>
			<view class="search-box">
				<text class="search-icon">🔍</text>
				<input 
					class="search-input" 
					type="text" 
					placeholder="搜索你要选择的门店"
					v-model="searchKeyword"
					@input="handleSearch"
				/>
			</view>
		</view>

		<view class="store-list">
			<view 
				class="store-item" 
				v-for="(store, index) in filteredStores" 
				:key="store.id"
				@click="selectStore(store)"
			>
				<view class="store-header">
					<text class="store-name">{{ store.outShopName }}</text>
					<view class="distance-info">
						<text class="distance-icon">📍</text>
						<text class="distance">{{ store.lat }}</text>
					</view>
				</view>
				<view class="store-tag" v-if="index === 0">距您最近</view>
				<view class="store-address">{{ store.address }}</view>
				<!-- <view class="store-room">{{ store.termNo }}</view> -->
			</view>
		</view>
	</view>
</template>

<script>
export default {
	data() {
		return {
			selectedCity: '绿藤市',
			searchKeyword: '',
			stores: [
				// {
				// 	id: 1,
				// 	outShopName: '绿藤绿地新都汇店',
				// 	address: '绿藤市时光里广场时光里广场时光里广场',
				// 	lat: '距您304m'
				// }
			]
		}
	},
	computed: {
		filteredStores() {
			if (!this.searchKeyword) {
				return this.stores
			}
			return this.stores.filter(store => 
				store.outShopName.includes(this.searchKeyword) || 
				store.address.includes(this.searchKeyword)
			)
		}
	},
	onLoad() {
		this.getNearbyStores()
		// 页面加载时请求位置权限
		this.requestLocationPermission()
	},
	methods: {
		goBack() {
			uni.navigateBack()
		},
		selectCity() {
			// 跳转到城市选择页面
			uni.navigateTo({
				url: '/pages/citySelect/citySelect'
			})
		},
		handleSearch() {
			// 搜索逻辑已在computed中处理
		},
		selectStore(store) {
			// 选择门店后返回上一页
			const pages = getCurrentPages()
			const prevPage = pages[pages.length - 2]
			
			if (prevPage) {
				// 将选中的门店信息传递给上一页
				prevPage.$vm.selectedStore = store
			}
			
			uni.navigateBack()
		},
		requestLocationPermission() {
			// 使用微信原生位置授权API
			uni.getSetting({
				success: (res) => {
					console.log('获取设置成功:', res)
					if (res.authSetting['scope.userLocation']) {
						// 已经授权,直接获取位置
						this.getUserLocation()
					} else if (res.authSetting['scope.userLocation'] === false) {
						// 用户之前拒绝过,引导用户手动开启
						this.showLocationGuide()
					} else {
						// 首次请求位置权限
						this.requestLocationAuth()
					}
				},
				fail: (err) => {
					console.error('获取设置失败:', err)
					// 如果获取设置失败,直接尝试获取位置
					this.getUserLocation()
				}
			})
		},
		requestLocationAuth() {
			// 请求位置权限
			uni.authorize({
				scope: 'scope.userLocation',
				success: () => {
					console.log('位置权限授权成功')
					this.getUserLocation()
				},
				fail: (err) => {
					console.log('位置权限授权失败:', err)
					if (err.errMsg.includes('auth deny')) {
						// 用户拒绝授权
						this.showLocationGuide()
					} else {
						uni.showToast({
							title: '位置权限获取失败',
							icon: 'none'
						})
					}
				}
			})
		},
		getUserLocation() {
			// 获取用户位置
			uni.getLocation({
				type: 'gcj02',
				success: (res) => {
					console.log('位置获取成功:', res)
					// 根据位置获取附近门店
					this.getNearbyStores(res.latitude, res.longitude)
				},
				fail: (err) => {
					console.error('位置获取失败:', err)
					if (err.errMsg.includes('auth deny')) {
						this.showLocationGuide()
					} else {
						uni.showToast({
							title: '位置获取失败,请手动选择城市',
							icon: 'none'
						})
					}
				}
			})
		},
		showLocationGuide() {
			// 引导用户手动开启位置权限
			uni.showModal({
				title: '位置权限',
				content: '需要获取您的位置信息来匹配附近门店,请在设置中开启位置权限',
				confirmText: '去设置',
				cancelText: '手动选择',
				success: (res) => {
					if (res.confirm) {
						// 跳转到设置页面
						uni.openSetting({
							success: (settingRes) => {
								if (settingRes.authSetting['scope.userLocation']) {
									// 用户开启了位置权限
									this.getUserLocation()
								}
							}
						})
					} else {
						// 用户选择手动选择城市
						uni.showToast({
							title: '请手动选择城市',
							icon: 'none'
						})
					}
				}
			})
		},
		getNearbyStores(lat, lng) {
			// 调用API获取附近门店
			// =============获取门店接口============
			console.log('获取附近门店', lat, lng)
			uni.request({
				url:'https://xxx/app/info/shop/shopList',
				method:'GET',
				data:{},
				success: (res) => {
					console.log('门店res',res);
					this.stores = res.data.data
				},
				fail: (err) => {
					console.error('获取门店列表失败:', err)
					uni.showToast({
						title: '获取门店列表失败',
						icon: 'none'
					})
				}
			})
		}
	}
}
</script>

<style>
.choose-store-wrap {
	min-height: 100vh;
	background: #f5f5f5;
}

.header {
	background: #fff;
	/* padding-top: 44px; */
}

.nav-bar {
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 12px 16px;
	height: 44px;
}

.back-btn {
	width: 44px;
	height: 44px;
	display: flex;
	align-items: center;
	justify-content: center;
}

.back-icon {
	font-size: 24px;
	color: #333;
}

.title {
	font-size: 18px;
	font-weight: 600;
	color: #333;
}

.right-icons {
	display: flex;
	gap: 12px;
	width: 44px;
	justify-content: flex-end;
}

.icon {
	font-size: 16px;
	color: #666;
}

.search-section {
	display: flex;
	padding: 12px 16px;
	background: #fff;
	gap: 12px;
}

.city-selector {
	display: flex;
	align-items: center;
	gap: 4px;
	padding: 8px 12px;
	background: #f5f5f5;
	border-radius: 6px;
	min-width: 80px;
}

.city-name {
	font-size: 14px;
	color: #333;
}

.arrow {
	font-size: 12px;
	color: #666;
}

.search-box {
	flex: 1;
	display: flex;
	align-items: center;
	background: #f5f5f5;
	border-radius: 6px;
	padding: 8px 12px;
	gap: 8px;
}

.search-icon {
	font-size: 16px;
	color: #999;
}

.search-input {
	flex: 1;
	font-size: 14px;
	color: #333;
}

.store-list {
	padding: 12px 16px;
}

.store-item {
	background: #fff;
	border-radius: 8px;
	padding: 16px;
	margin-bottom: 12px;
	position: relative;
}

.store-header {
	display: flex;
	justify-content: space-between;
	align-items: flex-start;
	margin-bottom: 8px;
}

.store-name {
	font-size: 16px;
	font-weight: 600;
	color: #333;
	flex: 1;
}

.distance-info {
	display: flex;
	align-items: center;
	gap: 4px;
}

.distance-icon {
	font-size: 12px;
}

.distance {
	font-size: 12px;
	color: #666;
}

.store-tag {
	position: absolute;
	top: 16px;
	right: 16px;
	background: #e7f4f2;
	color: #61aea7;
	font-size: 12px;
	padding: 2px 8px;
	border-radius: 4px;
}

.store-address {
	font-size: 14px;
	color: #666;
	line-height: 1.4;
	margin-bottom: 4px;
}

.store-room {
	font-size: 14px;
	color: #999;
}
</style>

注意,会报这个:

记得在manifest.json中配置:

相关推荐
牛头马面4 小时前
手把手教你在 Taro 小程序中用 axios 替代 Taro.request:@tarojs/plugin-http 配置与拦截器封装
前端
我不爱你了4 小时前
用 Python + Vue3 打造超炫酷音乐播放器:网易云歌单爬取 + Three.js 波形可视化
前端
Joyee6914 小时前
React native 设计初衷
前端
重生之我是菜鸡程序员4 小时前
uniapp 顶部通知 上滑隐藏
前端·javascript·uni-app
PCC4 小时前
语音控制的太空射击游戏开发笔记
前端
FliPPeDround4 小时前
告别 uni-app 启动烦恼:@uni-helper/unh 让开发流程更顺畅
前端·微信小程序·uni-app
东华帝君5 小时前
ref 和 reactive的区别
前端
joykit5 小时前
threejs 四元数
前端
Joyee6915 小时前
RN 的初版架构——通信机制
前端·react native