1. 在app.json中添加requiredPrivateInfos字段,并声明chooseLocation。
  2. 同时,确保已经配置了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) => {
  // 这里应该跳转到编辑页面,传递地址ID
  console.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设置为false
    addressList.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的地图选择API
    uni.chooseLocation({
      success: (res) => {
        try {
          console.log('选择地址成功', res)
          
          // 创建一个全新的临时对象
          const newAddressData = {
            id: Date.now().toString(), // 生成临时ID
            name: '', // 需要用户填写
            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>

Logo

加入社区!打开量化的大门,首批课程上线啦!

更多推荐