小程序调用地图api
- 在app.json中添加requiredPrivateInfos字段,并声明chooseLocation。
- 同时,确保已经配置了permission字段,以获取用户位置权限。
app.json
"permission": {"scope.userLocation": {"desc": "您的位置信息将用于获取发货地址"}},"requiredPrivateInfos": ["getLocation","chooseLocation"],
address.vue
<script lang="ts">
export default { name: 'AddressList' }
</script>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import type { AddressList } from '@/api/post/model'
import AddressListItem from '@/components/common/AddressListItem.vue'
import { useUserStore } from '@/store/user';const userStore = useUserStore()// 获取用户地址
const getUserAddress = async () => {const data = await userStore.getUserAddressPage()console.log("用户地址列表", data)addressList.value = data.records
}
getUserAddress()// 模拟地址数据,确保地址格式包含街道信息和具体地址
const addressList = ref<Array<AddressList & { id: string; isDefault: boolean }>>([])// 弹出层显示状态
const showAddressModal = ref(false);// 使用普通对象而不是ref对象来存储表单数据
let tempAddressForm: AddressList & { id: string; isDefault: boolean, latitude?: number, longitude?: number } = {id: '',name: '',phone: '',address: '',street: '',provice: '',area: '',isDefault: false,latitude: undefined,longitude: undefined
}// 创建响应式的表单数据引用,用于模板绑定
const addressForm = ref({ ...tempAddressForm })// 重置表单数据
const resetForm = () => {// 创建全新的对象,避免任何可能的只读问题tempAddressForm = {id: '',name: '',phone: '',address: '',street: '',provice: '',area: '',isDefault: false,latitude: undefined,longitude: undefined}// 更新响应式引用addressForm.value = { ...tempAddressForm }
}// 编辑地址
const handleEdit = (id: string) => {// 这里应该跳转到编辑页面,传递地址IDconsole.log('编辑地址', id)// 实际应用中应该使用路由跳转// uni.$u.route({// url: 'pages/me/addressEdit',// params: { id }// })
}// 删除地址
const handleDelete = async (id: string) => {// 调用接口删除const data = await userStore.deletedUserAddress(id);console.log(data)// 从列表中移除地址addressList.value = addressList.value.filter(item => item.id !== id)console.log('删除地址', id)
}// 设置默认地址
const handleSetDefault = async (id: string) => {const res = await userStore.updateUserAddressDefault(id);console.log(res)if (res.code === 0) {// 成功// 将所有地址的isDefault设置为falseaddressList.value.forEach(item => {item.isDefault = false})// 将选中的地址设置为默认const target = addressList.value.find(item => item.id === id)if (target) {target.isDefault = true}uni.showToast({title: res.message})} else {uni.showModal({title: '提示',content: res.msg,showCancel: false})}console.log(res)
}// 添加新地址
const handleAddAddress = () => {try {console.log('添加新地址,调用地图选择功能')// 重置表单resetForm()// 调用uni-app的地图选择APIuni.chooseLocation({success: (res) => {try {console.log('选择地址成功', res)// 创建一个全新的临时对象const newAddressData = {id: Date.now().toString(), // 生成临时IDname: '', // 需要用户填写phone: '', // 需要用户填写address: res.address, // 详细地址street: res.address.split(' ')[0] || '', // 提取街道信息provice: res.address.match(/^([^市]+市)?/)?.[1] || '', // 简单提取省份area: res.address.match(/市([^区]+区)/)?.[1] || '', // 简单提取区县isDefault: false,latitude: res.latitude, // 纬度longitude: res.longitude // 经度}console.log('准备更新的地址数据:', newAddressData)// 更新临时表单数据tempAddressForm = { ...newAddressData }// 更新响应式引用 - 创建全新的对象避免只读问题addressForm.value = { ...tempAddressForm }console.log('更新后的地址信息:', addressForm.value)// 显示地址表单弹出层showAddressModal.value = true} catch (innerError) {console.error('处理地址数据时发生错误:', innerError)uni.showToast({title: '处理地址信息失败',icon: 'none',duration: 2000})}},fail: (err) => {console.error('选择地址失败', err)// 如果是用户取消操作,不做任何提示if (err.errMsg.indexOf('cancel') >= 0) {return}// 权限被拒绝,提示用户if (err.errMsg.indexOf('auth deny') >= 0) {uni.showModal({title: '提示',content: '需要您授权位置权限才能选择地址',success: (res) => {if (res.confirm) {// 引导用户打开授权设置uni.openSetting()}}})} else {uni.showToast({title: '获取地址失败: ' + err.errMsg,icon: 'none',duration: 2000})}}})} catch (error) {console.error('handleAddAddress函数执行错误:', error)// 显示详细错误提示给用户const errorMessage = error instanceof Error ? error.message : '未知错误'console.log('错误详情:', { name: error.name, message: error.message, stack: error.stack })uni.showToast({title: '操作失败: ' + errorMessage,icon: 'none',duration: 3000})}
}// 保存新地址
const handleSaveAddress = () => {try {// 验证表单if (!addressForm.value.name) {uni.showToast({ title: '请输入收货人姓名', icon: 'none' })return}if (!addressForm.value.phone) {uni.showToast({ title: '请输入收货人电话', icon: 'none' })return}// 创建一个全新的地址对象,避免引用问题const addressToAdd = { ...addressForm.value }// 如果设置为默认地址,取消其他地址的默认状态if (addressToAdd.isDefault) {addressList.value.forEach(item => {item.isDefault = false})}// 添加新地址到列表addressList.value.push(addressToAdd)// 关闭弹出层showAddressModal.value = false// 重置表单resetForm()// 提示保存成功uni.showToast({ title: '添加成功', icon: 'success' })} catch (error) {console.error('保存地址时发生错误:', error)uni.showToast({title: '保存失败,请重试',icon: 'none',duration: 2000})}
}// 取消添加地址
const handleCancelAddress = () => {showAddressModal.value = false// 重置表单,避免下次打开时有残留数据resetForm()
}// 切换默认地址状态
const toggleDefaultAddress = () => {try {// 创建新对象来更新isDefault状态,避免直接修改只读属性const updatedForm = { ...addressForm.value }updatedForm.isDefault = !updatedForm.isDefault// 更新响应式引用addressForm.value = updatedForm} catch (error) {console.error('切换默认地址状态失败:', error)}
}// 地图标记点击事件
const handleMarkerTap = (e: any) => {console.log('地图标记点击', e)
}// 地图区域变化事件
const handleRegionChange = (e: any) => {console.log('地图区域变化', e)// 可以在这里处理地图拖拽、缩放等操作
}onMounted(() => {// 这里可以调用API获取用户的地址列表// 模拟API请求console.log('获取地址列表')
})
</script><template><!-- 非H5平台显示状态栏 --><!-- #ifndef H5 --><status-bar /><!-- #endif --><view class="address-list-container"><!-- 地址列表 --><view class="address-content" v-if="addressList.length > 0"><view v-for="item in addressList" :key="item.id"><AddressListItem :data="item" @edit="handleEdit" @delete="handleDelete" @setDefault="handleSetDefault" /></view></view><!-- 空状态 --><view class="empty-state" v-else><text class="empty-text">暂无收货地址</text></view><!-- 添加新地址按钮 --><view class="add-address-btn" @click="handleAddAddress"><text>+ 添加新地址</text></view><!-- 地址表单弹出层 --><uni-popup ref="popup" v-model="showAddressModal" type="bottom" :mask-click="false"><view class="address-form-container"><view class="form-header"><text class="cancel-btn" @click="handleCancelAddress">取消</text><text class="title">新增收货地址</text><text class="save-btn" @click="handleSaveAddress">保存</text></view><map style="width: 100%; height: 300px;" scale="14" /><view class="form-content"><!-- 所在地区 --><view class="form-item"><text class="label">所在地区</text><view class="area-info"><text>{{ addressForm.provice }}{{ addressForm.area }}</text><view class="default-checkbox" @click="toggleDefaultAddress"><view class="checkbox-icon" :class="{ checked: addressForm.isDefault }"><u-icon v-if="addressForm.isDefault" name="success" size="22" color="#fff" /></view><text class="checkbox-text">设为默认</text></view></view></view><!-- 详细地址 --><view class="form-item"><text class="label">详细地址</text><input class="input" v-model="addressForm.address" placeholder="请输入详细地址" /></view><!-- 收货人姓名 --><view class="form-item"><text class="label">收货人</text><input class="input" v-model="addressForm.name" placeholder="请输入收货人姓名" /></view><!-- 手机号码 --><view class="form-item"><text class="label">手机号码</text><input class="input" v-model="addressForm.phone" type="number" placeholder="请输入手机号码" maxlength="11" /></view></view></view></uni-popup></view>
</template><style lang="scss" scoped>
.address-list-container {height: 100vh;width: 100%;padding-bottom: 20rpx;.page-header {padding: 30rpx;background: #fff;margin-bottom: 20rpx;.title {font-size: 36rpx;font-weight: 600;color: #333;}}.address-content {padding: 0 30rpx;}.empty-state {padding: 100rpx 0;text-align: center;.empty-text {font-size: 28rpx;color: #999;}}.add-address-btn {margin: 40rpx 30rpx 0;padding: 25rpx 0;background: #3FBDFF;color: #fff;font-size: 32rpx;text-align: center;border-radius: 10rpx;&:active {background: #94bcff;}}// 地址表单弹出层样式.address-form-container {background: #fff;border-radius: 20rpx 20rpx 0 0;.form-header {display: flex;justify-content: space-between;align-items: center;padding: 30rpx;border-bottom: 1rpx solid #eee;.cancel-btn,.save-btn {font-size: 32rpx;color: #999;}.save-btn {color: #3FBDFF;}.title {font-size: 34rpx;font-weight: 600;color: #333;}}.form-content {padding: 30rpx;.form-item {margin-bottom: 40rpx;.label {display: block;font-size: 28rpx;color: #333;margin-bottom: 15rpx;}.input {width: 100%;height: 88rpx;background: #f5f5f5;border-radius: 10rpx;padding: 0 24rpx;font-size: 28rpx;color: #333;box-sizing: border-box;}.area-info {display: flex;justify-content: space-between;align-items: center;height: 88rpx;background: #f5f5f5;border-radius: 10rpx;padding: 0 24rpx;font-size: 28rpx;color: #333;}.default-checkbox {display: flex;align-items: center;.checkbox-icon {width: 32rpx;height: 32rpx;border: 2rpx solid #ccc;border-radius: 50%;display: flex;justify-content: center;align-items: center;margin-right: 10rpx;&.checked {background: #3FBDFF;border-color: #3FBDFF;}}.checkbox-text {font-size: 28rpx;color: #666;}}}}}
}
</style>