uniapp——项目day04

购物车页面——商品列表区域

渲染购物车商品列表的标题区域

1. 定义如下的 UI 结构:

2.美化样式

渲染商品列表区域的基本结构

1. 通过 mapState 辅助函数,将 Store 中的 cart 数组映射到当前页面中使用:

  import badgeMix from '@/mixins/tabbar-badge.js'
  // 按需导入 mapState 这个辅助函数
  import {
    mapState
  } from 'vuex'
  export default {
    mixins: [badgeMix],
    computed: {
      // 将 m_cart 模块中的 cart 数组映射到当前页面中使用
      ...mapState('m_cart', ['cart']),
    },
    data() {
      return {}
    },
  }

2. 在 UI 结构中,通过 v-for 指令循环渲染自定义的 my-goods 组件:

    <!-- 商品列表区域 -->
    <block v-for="(goods, i) in cart" :key="i">
      <my-goods :goods="goods"></my-goods>
    </block>

为 my-goods 组件封装 radio 勾选状态

1. 打开 my-goods.vue 组件的源代码,为商品的左侧图片区域添加 radio 组件:

2. 给类名为 goods-item-left 的 view 组件添加样式,实现 radio 组件和 image 组件的左 右布局:

  .goods-item-left {
    margin-right: 5px;
    display: flex;
    justify-content: space-between;
    align-items: center;

    .goods-pic {
      width: 100px;
      height: 100px;
      display: block;
    }
  }

 3. 封装名称为 showRadio 的 props 属性,来控制当前组件中是否显示 radio 组件:

    // 定义 props 属性,用来接收外界传递到当前组件的数据
    props: {
      // 商品的信息对象
      goods: {
        type: Object,
        default: {},
      },
      // 是否展示图片左侧的 radio
      showRadio: {
        type: Boolean,
        // 如果外界没有指定 show-radio 属性的值,则默认不展示 radio 组件
        default: false,
      },
    },

4. 使用 v-if 指令控制 radio 组件的按需展示:

 5. 在 cart.vue 页面中的商品列表区域,指定 :show-radio="true" 属性,从而显示 radio 组 件:

6. 修改 my-goods.vue 组件,动态为 radio 绑定选中状态:

 为 my-goods 组件封装 radio-change 事件

1. 当用户点击 radio 组件,希望修改当前商品的勾选状态,此时用户可以为 my-goods 组件绑定 @radio-change 事件,从而获取当前商品的 goods_id 和 goods_state :

    <!-- 商品列表区域 -->
    <block v-for="(goods, i) in cart" :key="i">
      <!-- 在 radioChangeHandler 事件处理函数中,通过事件对象 e,得到商品的 goods_id
      和 goods_state -->
      <my-goods :goods="goods" :show-radio="true" @radiochange="radioChangeHandler"></my-goods>
    </block>

定义 radioChangeHandler 事件处理函数如下:

methods: {
// 商品的勾选状态发生了变化
radioChangeHandler(e) {
console.log(e) // 输出得到的数据 -> {goods_id: 395, goods_state: false}
}
}

 2. 在 my-goods.vue 组件中,为 radio 组件绑定 @click 事件处理函数如下:

3. 在 my-goods.vue 组件的 methods 节点中,定义 radioClickHandler 事件处理函数:

修改购物车中商品的勾选状态

1. 在 store/cart.js 模块中,声明如下的 mutations 方法,用来修改对应商品的勾选状态:

// 更新购物车中商品的勾选状态
    updateGoodsState(state, goods) {
      // 根据 goods_id 查询购物车中对应商品的信息对象
      const findResult = state.cart.find(x => x.goods_id === goods.goods_id)
      // 有对应的商品信息对象
      if (findResult) {
        // 更新对应商品的勾选状态
        findResult.goods_state = goods.goods_state
        // 持久化存储到本地
        this.commit('m_cart/saveToStorage')
      }
    }

2. 在 cart.vue 页面中,导入 mapMutations 这个辅助函数,从而将需要的 mutations 方法映 射到当前页面中使用:

  import badgeMix from '@/mixins/tabbar-badge.js'
  import {
    mapState,
    mapMutations
  } from 'vuex'
  export default {
    mixins: [badgeMix],
    computed: {
      ...mapState('m_cart', ['cart']),
    },
    data() {
      return {}
    },
    methods: {
      ...mapMutations('m_cart', ['updateGoodsState']),
      // 商品的勾选状态发生了变化
      radioChangeHandler(e) {
        this.updateGoodsState(e)
      },
    },
  }

为 my-goods 组件封装 NumberBox

注意:NumberBox 组件是 uni-ui 提供的

1. 修改 my-goods.vue 组件的源代码,在类名为 goods-info-box 的 view 组件内部渲染 NumberBox 组件的基本结构:

2. 美化页面的结构:

 .goods-item-right {
      display: flex;
      flex: 1;
      flex-direction: column;
      justify-content: space-between;

      .goods-name {
        font-size: 13px;
      }

      .goods-info-box {
        display: flex;
        align-items: center;
        justify-content: space-between;
      }

      .goods-price {
        font-size: 16px;
        color: #c00000;
      }
    }

3. 在 my-goods.vue 组件中,动态为 NumberBox 组件绑定商品的数量值:

      <view class="goods-info-box">
        <!-- 商品价格 -->
        <view class="goods-price">¥{{goods.goods_price | tofixed}}</view>
        <!-- 商品数量 -->
        <uni-number-box :min="1" :value="goods.goods_count"></uni-number-box>
      </view>

4. 在 my-goods.vue 组件中,封装名称为 showNum 的 props 属性,来控制当前组件中是否显 示 NumberBox 组件:

 // 是否展示价格右侧的 NumberBox 组件
      showNum: {
        type: Boolean,
        default: false,
      },

 5. 在 my-goods.vue 组件中,使用 v-if 指令控制 NumberBox 组件的按需展示:

      <view class="goods-info-box">
        <!-- 商品价格 -->
        <view class="goods-price">¥{{goods.goods_price | tofixed}}</view>
        <!-- 商品数量 -->
        <uni-number-box :min="1" :value="goods.goods_count" @change="numChangeHandler" v-if="showNum"></uni-number-box>
      </view>

6. 在 cart.vue 页面中的商品列表区域,指定 :show-num="true" 属性,从而显示 NumberBox 组件:

    <!-- 商品列表区域 -->
    <block v-for="(goods, i) in cart" :key="i">
      <my-goods :goods="goods" :show-radio="true" :show-num="true" @radio-change="radioChangeHandler"></my-goods>
    </block>

为 my-goods 组件封装 num-change 事件

1. 当用户修改了 NumberBox 的值以后,希望将最新的商品数量更新到购物车中,此时用户可以为 my-goods 组件绑定 @num-change 事件,从而获取当前商品的 goods_id 和 goods_count:

    <!-- 商品列表区域 -->
    <block v-for="(goods, i) in cart" :key="i">
      <my-goods :goods="goods" :show-radio="true" :show-num="true" @radiochange="radioChangeHandler"
        @num-change="numberChangeHandler"></my-goods>
    </block>

定义 numberChangeHandler 事件处理函数如下:

// 商品的数量发生了变化
numberChangeHandler(e) {
console.log(e)
}

2. 在 my-goods.vue 组件中,为 uni-number-box 组件绑定 @change 事件处理函数如下:

      <view class="goods-info-box">
        <!-- 商品价格 -->
        <view class="goods-price">¥{{goods.goods_price | tofixed}}</view>
        <!-- 商品数量 -->
        <uni-number-box :min="1" :value="goods.goods_count" @change="numChangeHandler"></uni-number-box>
      </view>

3. 在 my-goods.vue 组件的 methods 节点中,定义 numChangeHandler 事件处理函数:

// NumberBox 组件的 change 事件处理函数
      numChangeHandler(val) {
        // 通过 this.$emit() 触发外界通过 @ 绑定的 num-change 事件
        this.$emit('num-change', {
          // 商品的 Id
          goods_id: this.goods.goods_id,
          // 商品的最新数量
          goods_count: +val
        })
      }

解决 NumberBox 数据不合法的问题

问题说明:当用户在 NumberBox 中输入字母等非法字符之后,会导致 NumberBox 数据紊乱的问 题

现在那个组件已经修复了这个问题了,所以不需要再修改源代码。

完善 NumberBox 的 inputValue 侦听器

问题说明:在用户每次输入内容之后,都会触发 inputValue 侦听器,从而调用 this.$emit("change", newVal) 方法。这种做法可能会把不合法的内容传递出去!

现在这个问题也修复了,输入带小数的数字也会四舍五入。

修改购物车中商品的数量

1. 在 store/cart.js 模块中,声明如下的 mutations 方法,用来修改对应商品的数量:

    // 更新购物车中商品的数量
    updateGoodsCount(state, goods) {
      // 根据 goods_id 查询购物车中对应商品的信息对象
      const findResult = state.cart.find(x => x.goods_id === goods.goods_id)
      if (findResult) {
        // 更新对应商品的数量
        findResult.goods_count = goods.goods_count
        // 持久化存储到本地
        this.commit('m_cart/saveToStorage')
      }
    }

2. 在 cart.vue 页面中,通过 mapMutations 这个辅助函数,将需要的 mutations 方法映射 到当前页面中使用:

  import badgeMix from '@/mixins/tabbar-badge.js'
  import {
    mapState,
    mapMutations
  } from 'vuex'
  export default {
    mixins: [badgeMix],
    computed: {
      ...mapState('m_cart', ['cart']),
    },
    data() {
      return {}
    },
    methods: {
      ...mapMutations('m_cart', ['updateGoodsState', 'updateGoodsCount']),
      // 商品的勾选状态发生了变化
      radioChangeHandler(e) {
        this.updateGoodsState(e)
      },
      // 商品的数量发生了变化
      numberChangeHandler(e) {
        this.updateGoodsCount(e)

      },
    },
  }

渲染滑动删除的 UI 效果

滑动删除需要用到 uni-ui 的 uni-swipe-action 组件和 uni-swipe-action-item。详细的官方文档请参 考SwipeAction 滑动操作。

1. 改造 cart.vue 页面的 UI 结构,将商品列表区域的结构修改如下(可以使用 uSwipeAction 代 码块快速生成基本的 UI 结构):

    <!-- 商品列表区域 -->
    <!-- uni-swipe-action 是最外层包裹性质的容器 -->
    <uni-swipe-action>
      <block v-for="(goods, i) in cart" :key="i">
        <!-- uni-swipe-action-item 可以为其子节点提供滑动操作的效果。需要通过
options 属性来指定操作按钮的配置信息 -->
        <uni-swipe-action-item :right-options="options" @click="swipeActionClickHandler(goods)">
          <my-goods :goods="goods" :show-radio="true" :show-num="true" @radio-change="radioChangeHandler"
            @num-change="numberChangeHandler"></my-goods>
        </uni-swipe-action-item>
      </block>
    </uni-swipe-action>

2. 在 data 节点中声明 options 数组,用来定义操作按钮的配置信息:

        options: [{
          text: '删除', // 显示的文本内容
          style: {
            backgroundColor: '#C00000' // 按钮的背景颜色
          }
        }]

3. 在 methods 中声明 uni-swipe-action-item 组件的 @click 事件处理函数:

// 点击了滑动操作按钮
swipeActionClickHandler(goods) {
console.log(goods)
}

4. 美化 my-goods.vue 组件的样式: 

.goods-item {
// 让 goods-item 项占满整个屏幕的宽度
width: 750rpx;
// 设置盒模型为 border-box
box-sizing: border-box;
display: flex;
padding: 10px 5px;
border-bottom: 1px solid #f0f0f0;
}

实现滑动删除的功能

1. 在 store/cart.js 模块的 mutations 节点中声明如下的方法,从而根据商品的 Id 从购物车 中移除对应的商品:

// 根据 Id 从购物车中删除对应的商品信息
removeGoodsById(state, goods_id) {
// 调用数组的 filter 方法进行过滤
state.cart = state.cart.filter(x => x.goods_id !== goods_id)
// 持久化存储到本地
this.commit('m_cart/saveToStorage')
}

2. 在 cart.vue 页面中,使用 mapMutations 辅助函数,把需要的方法映射到当前页面中使 用:

购物车页面——收货地址区域

创建收货地址组件

1. 在 components 目录上鼠标右键,选择 新建组件 ,并填写组件相关的信息:

然后要在购物车页面使用这个组件

2. 渲染收货地址组件的基本结构:

  <view>
    <!-- 选择收货地址的盒子 -->
    <view class="address-choose-box">
      <button type="primary" size="mini" class="btnChooseAddress">请选择收货
        地址+</button>
    </view>
    <!-- 渲染收货信息的盒子 -->
    <view class="address-info-box">
      <view class="row1">
        <view class="row1-left">
          <view class="username">收货人:<text>escook</text></view>
        </view>
        <view class="row1-right">
          <view class="phone">电话:<text>138XXXX5555</text></view>
          <uni-icons type="arrowright" size="16"></uni-icons>
        </view>
      </view>
      <view class="row2">
        <view class="row2-left">收货地址:</view>
        <view class="row2-right">河北省邯郸市肥乡区xxx 河北省邯郸市肥乡区xxx 河北
          省邯郸市肥乡区xxx 河北省邯郸市肥乡区xxx </view>
      </view>
    </view>
    <!-- 底部的边框线 -->
    <image src="/static/cart_border@2x.png" class="address-border"></image>
  </view>

3. 美化收货地址组件的样式:

  // 底部边框线的样式
  .address-border {
    display: block;
    width: 100%;
    height: 5px;
  }

  // 选择收货地址的盒子
  .address-choose-box {
    height: 90px;
    display: flex;
    align-items: center;
    justify-content: center;
  }

  // 渲染收货信息的盒子
  .address-info-box {
    font-size: 12px;
    height: 90px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: 0 5px;

    // 第一行
    .row1 {
      display: flex;
      justify-content: space-between;

      .row1-right {
        display: flex;
        align-items: center;

        .phone {
          margin-right: 5px;
        }
      }
    }

    // 第二行
    .row2 {
      display: flex;
      align-items: center;
      margin-top: 10px;

      .row2-left {
        white-space: nowrap;
      }
    }
  }

实现收货地址区域的按需展示

1. 在 data 中定义收货地址的信息对象:

        // 收货地址
        address: {},

2. 使用 v-if 和 v-else 实现按需展示:

<!-- 选择收货地址的盒子 -->
<view class="address-choose-box" v-if="JSON.stringify(address) === '{}'">
<button type="primary" size="mini" class="btnChooseAddress">请选择收货地址+
</button>
</view>
<!-- 渲染收货信息的盒子 -->
<view class="address-info-box" v-else>
<!-- 省略其它代码 -->
</view>

实现选择收货地址的功能

避坑:

小程序提供的chooseAddress()方法显示张三地址页面,在manifest.json文件中,找到mp-weixin节点,添加上:

 "requiredPrivateInfos": [

         "chooseLocation",

         "getLocation", 

         "chooseAddress"

         ]

然后才能看见这个页面,不然会一直报错

1. 为 请选择收货地址+ 的 button 按钮绑定点击事件处理函数:

<!-- 选择收货地址的盒子 -->
<view class="address-choose-box" v-if="JSON.stringify(address) === '{}'">
<button type="primary" size="mini" class="btnChooseAddress"
@click="chooseAddress">请选择收货地址+</button>
</view>

2. 定义 chooseAddress 事件处理函数,调用小程序提供的 chooseAddress() API 实现选择收 货地址的功能:

 // 选择收货地址
      async chooseAddress() {
        // 1. 调用小程序提供的 chooseAddress() 方法,即可使用选择收货地址的功能
        // 返回值是一个数组:第 1 项为错误对象;第 2 项为成功之后的收货地址对象
        const [err, succ] = await uni.chooseAddress().catch(err => err)
        // 2. 用户成功的选择了收货地址
        if (err === null && succ.errMsg === 'chooseAddress:ok') {
          // 为 data 里面的收货地址对象赋值
          this.address = succ
        }

3. 定义收货详细地址的计算属性:

computed: {
        // 收货详细地址的计算属性
        addstr() {
          if (!this.address.provinceName) return ''
          // 拼接 省,市,区,详细地址 的字符串并返回给用户
          return this.address.provinceName + this.address.cityName +
            this.address.countyName + this.address.detailInfo
        }
      }

4. 渲染收货地址区域的数据:

    <!-- 渲染收货信息的盒子 -->
    <view class="address-info-box" v-else>
      <view class="row1">
        <view class="row1-left">
          <view class="username">收货人:<text>{{address.userName}}</text>
          </view>
        </view>
        <view class="row1-right">
          <view class="phone">电话:<text>{{address.telNumber}}</text></view>
          <uni-icons type="arrowright" size="16"></uni-icons>
        </view>
      </view>
      <view class="row2">
        <view class="row2-left">收货地址:</view>
        <view class="row2-right">{{addstr}}</view>
      </view>
    </view>

将 address 信息存储到 vuex 中

这里好像要真机调试才可以获取到地址。

1. 在 store 目录中,创建用户相关的 vuex 模块,命名为 user.js :

export default {
  // 开启命名空间
  namespaced: true,
  // state 数据
  state: () => ({
    // 收货地址
    address: {},
  }),
  // 方法
  mutations: {
    // 更新收货地址
    updateAddress(state, address) {
      state.address = address
    },
  },
  // 数据包装器
  getters: {},
}

2. 在 store/store.js 模块中,导入并挂载 user.js 模块:

这个是vue3的做法

// 1. 导入 Vue 和 Vuex
import {
  createStore
} from 'vuex'
import moduleCart from './cart.js'
// 导入用户的 vuex 模块
import moduleUser from './user.js'
// 2. 创建 Store 的实例对象
const store = createStore({
  modules: {
    m_cart: moduleCart,
    // 挂载用户的 vuex 模块,访问路径为 m_user
    m_user: moduleUser,
  },
})

// 3. 向外共享 Store 的实例对象
export default store

3. 改造 address.vue 组件中的代码,使用 vuex 提供的 address 计算属性 替代 data 中定义的本 地 address 对象:

// 1. 按需导入 mapState 和 mapMutations 这两个辅助函数
  import {
    mapState,
    mapMutations
  } from 'vuex'
  export default {
    data() {
      return {
        // 2.1 注释掉下面的 address 对象,使用 2.2 中的代码替代之
        // address: {}
      }
    },
    methods: {
      // 3.1 把 m_user 模块中的 updateAddress 函数映射到当前组件
      ...mapMutations('m_user', ['updateAddress']),
      // 选择收货地址
      async chooseAddress() {
        const [err, succ] = await uni.chooseAddress().catch((err) => err)
        // 用户成功的选择了收货地址
        if (err === null && succ.errMsg === 'chooseAddress:ok') {
          // 3.2 把下面这行代码注释掉,使用 3.3 中的代码替代之
          // this.address = succ
          // 3.3 调用 Store 中提供的 updateAddress 方法,将 address 保存到Store 里面
          this.updateAddress(succ)
        }
      },
    },
    computed: {
      // 2.2 把 m_user 模块中的 address 对象映射当前组件中使用,代替 data 中address 对象
      ...mapState('m_user', ['address']),
      // 收货详细地址的计算属性
      addstr() {
        if (!this.address.provinceName) return ''
        // 拼接 省,市,区,详细地址 的字符串并返回给用户
        return this.address.provinceName + this.address.cityName +
          this.address.countyName + this.address.detailInfo
      },
    },
  }

将 Store 中的 address 持久化存储到本地

1. 修改 store/user.js 模块中的代码如下:

export default {
  // 开启命名空间
  namespaced: true,
  // state 数据
  state: () => ({
    // 3. 读取本地的收货地址数据,初始化 address 对象
    address: JSON.parse(uni.getStorageSync('address') || '{}'),
  }),
  // 方法
  mutations: {
    // 更新收货地址
    updateAddress(state, address) {
      state.address = address
      // 2. 通过 this.commit() 方法,调用 m_user 模块下的
      saveAddressToStorage 方法将 address 对象持久化存储到本地
      this.commit('m_user/saveAddressToStorage')
    },
    // 1. 定义将 address 持久化存储到本地 mutations 方法
    saveAddressToStorage(state) {
      uni.setStorageSync('address', JSON.stringify(state.address))
    },
  },
  // 数据包装器
  getters: {},
}

将 addstr 抽离为 getters

目的:为了提高代码的复用性,可以把收货的详细地址抽离为 getters,方便在多个页面和组件之 间实现复用。

1. 剪切 my-address.vue 组件中的 addstr 计算属性的代码,粘贴到 user.js 模块中,作为 一个 getters 节点:

    // 收货详细地址的计算属性
    addstr(state) {
      if (!state.address.provinceName) return ''
      // 拼接 省,市,区,详细地址 的字符串并返回给用户
      return state.address.provinceName + state.address.cityName +
        state.address.countyName + state.address.detailInfo
    }

2. 改造 my-address.vue 组件中的代码,通过 mapGetters 辅助函数,将 m_user 模块中的 addstr 映射到当前组件中使用:

// 按需导入 mapGetters 辅助函数
import { mapState, mapMutations, mapGetters } from 'vuex'
export default {
// 省略其它代码
computed: {
...mapState('m_user', ['address']),
// 将 m_user 模块中的 addstr 映射到当前组件中使用
...mapGetters('m_user', ['addstr']),
},
}

重新选择收货地址

1. 为 class 类名为 address-info-box 的盒子绑定 click 事件处理函数如下:

<!-- 渲染收货信息的盒子 -->
<view class="address-info-box" v-else @click="chooseAddress">
<!-- 省略其它代码 -->
</view>

解决收货地址授权失败的问题

如果在选择收货地址的时候,用户点击了取消授权,则需要进行特殊的处理,否则用户将无法再 次选择收货地址!

1. 改造 chooseAddress 方法如下:

      // 选择收货地址
      async chooseAddress() {
        // 1. 调用小程序提供的 chooseAddress() 方法,即可使用选择收货地址的功能
        // 返回值是一个数组:第1项为错误对象;第2项为成功之后的收货地址对象
        const [err, succ] = await uni.chooseAddress().catch(err => err)
        // 2. 用户成功的选择了收货地址
        if (succ && succ.errMsg === 'chooseAddress:ok') {
          // 更新 vuex 中的收货地址
          this.updateAddress(succ)
        }
        // 3. 用户没有授权
        if (err && err.errMsg === 'chooseAddress:fail auth deny') {
          this.reAuth() // 调用 this.reAuth() 方法,向用户重新发起授权申请
        }
      }

2. 在 methods 节点中声明 reAuth 方法如下:

// 调用此方法,重新发起收货地址的授权
      async reAuth() {
        // 3.1 提示用户对地址进行授权
        const [err2, confirmResult] = await uni.showModal({
          content: '检测到您没打开地址权限,是否去设置打开?',
          confirmText: "确认",
          cancelText: "取消",
        })
        // 3.2 如果弹框异常,则直接退出
        if (err2) return
        // 3.3 如果用户点击了 “取消” 按钮,则提示用户 “您取消了地址授权!”
        if (confirmResult.cancel) return uni.$showMsg('您取消了地址授权!')
        // 3.4 如果用户点击了 “确认” 按钮,则调用 uni.openSetting() 方法进入授权页面,
        让用户重新进行授权
        if (confirmResult.confirm) return uni.openSetting({
          // 3.4.1 授权结束,需要对授权的结果做进一步判断
          success: (settingResult) => {
            // 3.4.2 地址授权的值等于 true,提示用户 “授权成功”
            if (settingResult.authSetting['scope.address']) return
            uni.$showMsg('授权成功!请选择地址')
            // 3.4.3 地址授权的值等于 false,提示用户 “您取消了地址授权”
            if (!settingResult.authSetting['scope.address']) return
            uni.$showMsg('您取消了地址授权!')
          }
        })
      }

解决 iPhone 真机上无法重新授权的问题 

问题说明:在 iPhone 设备上,当用户取消授权之后,再次点击选择收货地址按钮的时候,无法弹 出授权的提示框!

      async chooseAddress() {
        // 1. 调用小程序提供的 chooseAddress() 方法,即可使用选择收货地址的功能
        // 返回值是一个数组:第1项为错误对象;第2项为成功之后的收货地址对象
        const [err, succ] = await uni.chooseAddress().catch(err => err)
        // 2. 用户成功的选择了收货地址
        if (succ && succ.errMsg === 'chooseAddress:ok') {
          this.updateAddress(succ)
        }
        // 3. 用户没有授权
        if (err && (err.errMsg === 'chooseAddress:fail auth deny' || err.errMsg ===
            'chooseAddress:fail authorize no response')) {
          this.reAuth()
        }
      }

 BUG修改

在选择收货地址方法里面

        const [err, succ] = await uni.chooseAddress().catch(err => err)
这条语句会报错不能执行,需要改成如下语句

// 选择收货地址
      async chooseAddress() {
        // 1. 调用小程序提供的 chooseAddress() 方法,即可使用选择收货地址的功能
        // 返回值是一个数组:第1项为错误对象;第2项为成功之后的收货地址对象
        let succ = null;
        let err = null;
        try {
          const res = await uni.chooseAddress();
           err = null;
           succ = res;
          console.log(res)
        } catch (error) {
           err = error;
           succ = null;
          // 处理错误的逻辑
          console.error(error);
        }
        
        // 2. 用户成功的选择了收货地址
        if (succ && succ.errMsg === 'chooseAddress:ok') {
          this.updateAddress(succ)
        }
        // 3. 用户没有授权
        if (err && (err.errMsg === 'chooseAddress:fail auth deny' || err.errMsg ===
            'chooseAddress:fail authorize no response')) {
          this.reAuth()
        }
      }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/140440.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Opentracing概念介绍——Span

文章首发公众号&#xff1a;海天二路搬砖工 引言 作为分布式跟踪系统的标准化API&#xff0c;OpenTracing提供了一种通用的方式来追踪和分析分布式系统中的请求和操作。 在Opentracing中&#xff0c;Span是基本的跟踪单元&#xff0c;用于描述在分布式系统中的一个操作或事件…

YOLO目标检测——红花数据集下载分享【含对应voc、coco和yolo三种格式标签】

实际项目应用&#xff1a;红花检测数据集可以用于监测和分析红花的生长情况&#xff0c;包括生长速度、叶面积、花朵数量等&#xff0c;为农民提供精确的决策支持&#xff0c;以提高红花产量和品质。数据集说明&#xff1a;红花检测数据集&#xff0c;真实场景的高质量图片数据…

长江存储诉讼镁光侵权的8个专利是什么?

1.事件背景回顾 据《环球时报》周日从美国加州北区地方法院官方网站获悉&#xff0c;中国领先的存储芯片生产商长江存储科技股份有限公司&#xff08;YMTC&#xff09;周四对美国美光科技及其全资子公司美光消费品集团提起诉讼&#xff0c;指控其侵犯了长江存储的八项专利。 …

ObRegisterCallbacks()返回0xC0000022(拒绝访问)解决办法

在开发测试环境下&#xff0c;没有打签名的驱动调用ObRegisterCallbacks会返回0xC0000022&#xff08;拒绝访问&#xff09;的错误码。这是由于该函数内部会进行驱动的签名校验。 具体位置在 因此可以用以下代码绕过该检查 // 以下代码放在DriverEntry中 ULONG_PTR pDrvSectio…

基于servlet+jsp+mysql网上书店系统

基于servletjspmysql网上书店系统 一、系统介绍二、功能展示四、其它1.其他系统实现五.获取源码 一、系统介绍 项目类型&#xff1a;Java web项目 项目名称&#xff1a;基于servletjspmysql网上书店系统 项目架构&#xff1a;B/S架构 开发语言&#xff1a;Java语言 前端技…

csdn2023必看系列:最牛最全面的JMeter实现接口自动化测试教程

【文章末尾给大家留下了大量的福利哦】 一、JMETER的环境搭建 参考&#xff1a;https://www.cnblogs.com/qmfsun/p/4902534.html 二、JMETER的汉化 临时汉化方法&#xff1a;打开jmeter&#xff0c;options-->choose language-->选择语言 可以根据自己的需要选择简体…

基于SSM的“镜头人生”约拍网站设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;JSP 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

【OS】操作系统课程笔记 第七章 内存管理

目录 7.1 内存管理的功能 7.1.1 内存分配 7.1.2 地址转换 1. 空间的概念 2. 地址转换 7.1.3 存储保护 7.1.4 存储共享 7.1.5 存储扩充 7.2 程序的链接和加载 7.2.1 程序的链接 链接的分类 7.2.2 程序的加载 1. 加载器的功能 2. 装入方式分类 7.3 连续分配方式 7.…

Unity性能优化分析篇

性能优化是游戏项目开发中一个重要环节。游戏帧率过低&#xff0c;手机发烫&#xff0c; 包体太大&#xff0c;低端机上跑不起来等, 这些都需要来做优化&#xff0c;不管过去&#xff0c;现在&#xff0c;未来&#xff0c;性能优化都是永恒的话题。 而性能优化首先要掌握的是性…

Linux C 进程编程

进程编程 进程介绍进程的定义进程和线程以及程序的区别进程块PCB进程的状态相关指令 进程调度算法先来先服务调度算法 FCFS短作业(进程)优先调度算法 SJF优先权调度算法 FPF优先权调度算法的类型非抢占式优先权算法抢占式优先权算法 优先权类型静态优先权动态优先权 高响应比优…

接口测试--知识问答

1 做接口测试当请求参数多时tps下降明显&#xff0c;此接口根据参数从redis中获取数据&#xff0c;每个参数与redis交互一次&#xff0c;当一组参数是tps5133&#xff0c;五组参数是tps1169&#xff0c;多次交互影响了处理性能&#xff0c;请详细阐述如何改进增进效果的方案。 …

酷柚易汛ERP - 序列号状态表操作指南

1、应用场景 序列表状态表统计商品的每个序列号目前的状态&#xff08;在库、已出库&#xff09;&#xff0c;每个序列号仅会显示一条记录。 2、主要操作 打开【仓库】-【序列号状态表】&#xff0c;可勾选序列号在库/已出库两种状态查询&#xff0c;其它筛选操作与上文其它…

垃圾/垃圾桶识别相关开源数据集汇总

垃圾箱图片数据集 数据集下载链接&#xff1a;http://suo.nz/3cvbiC 垃圾箱多类检测数据集 数据集下载链接&#xff1a;http://suo.nz/2eluH3 蒙得维亚的垃圾箱图片 数据集下载链接&#xff1a;http://suo.nz/2lRHLK 垃圾桶满溢检测数据集 数据集下载链接&#xff1a;http:…

【HttpRunner】接口自动化测试框架

简介 2018年python开发者大会上&#xff0c;了解到HttpRuuner开源自动化测试框架&#xff0c;采用YAML/JSON格式管理用例&#xff0c;能录制和转换生成用例功能&#xff0c;充分做到用例与测试代码分离&#xff0c;相比excel维护测试场景数据更加简洁。在此&#xff0c;利用业余…

filte(过滤数组)

根据条件&#xff0c;保留满足条件的对应项&#xff0c;得到一个新数组

关于我在配置zookeeper出现,启动成功,进程存在,但是查看状态却没有出现Mode:xxxxx的问题和我的解决方案

在我输入:zkServer.sh status 之后出现报错码. 报错码&#xff1a; ZooKeeper JMX enabled by default Using config: /opt/software/zookeeper/bin/../conf/zoo.cfgClient port found: 2181. Client address: localhost. Error contacting service. It is probably not runni…

11.读取文件长度-fseek和ftell函数的使用

文章目录 简介1. 写入测试文件2. 读取文件长度 简介 主要讲使用fopen读取文件&#xff0c;配合使用fseek和ftell来读取文件长度。1. 写入测试文件 执行下方程序&#xff0c;使用fwrite函数写入40字节的数据&#xff0c;使其形成文件存入本地目录。#define _CRT_SECURE_NO_WARNI…

【SpringBoot】SpringBoot自动配置底层源码解析

概述 EnableAutoConfiguration源码解析SpringBoot常用条件注解源码解析SpringBoot之Mybatis自动配置源码解析SpringBoot之AOP自动配置源码解析SpringBoot Jar包启动过程源码解析 DeferredImportSelector接口 DeferredImportSelector和ImportSelector的区别在于&#xff1a; …

Leetcode—202.快乐数【简单】

2023每日刷题&#xff08;二十八&#xff09; Leetcode—202.快乐数 快慢指针思想 通过手玩2&#xff0c;可以发现 会走入一个循环&#xff0c;并且fast和slow会在一个数字相遇&#xff0c;以下也大概花了一下推倒出来了。如果slow不是因为1和fast相等的&#xff0c;就说明它…

Nginx的安装

Nginx的安装 Windows下Nginx的安装 1、下载nginx 下载稳定版本: http://nginx.org/en/download.html 直接下载 nginx-1.20.0.zip : http://nginx.org/download/nginx-1.20.0.zip 下载后解压&#xff0c;解压后如下&#xff1a; 2、启动Nginx 有很多种方法启动nginx (…