uni-app黑马优购项目学习记录(九)(下篇)

125 阅读5分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天,点击查看活动详情

9.3 结算区域

9.3.1 把结算区域封装为组件

  1. components 目录中,新建 my-settle 结算组件

  2. 初始化 my-settle 组件的基本结构和样式:

    <template>
      <!-- 最外层的容器 -->
      <view class="my-settle-container">
        结算组件
      </view>
    </template>
    
    <script>
    export default {
      data() {
        return {}
      },
    }
    </script>
    
    <style lang="scss">
    .my-settle-container {
      /* 底部固定定位 */
      position: fixed;
      bottom: 0;
      left: 0;
      /* 设置宽高和背景色 */
      width: 100%;
      height: 50px;
      background-color: cyan;
    }
    </style>
    
  3. cart.vue 页面中使用自定义的 my-settle 组件,并美化页面样式,防止页面底部被覆盖:

    <template>
      <view class="cart-container">
        <!-- 使用自定义的 address 组件 -->
    
        <!-- 购物车商品列表的标题区域 -->
    
        <!-- 商品列表区域 -->
    
        <!-- 结算区域 -->
        <my-settle></my-settle>
      </view>
    </template>
    
    <style lang="scss">
    .cart-container {
      padding-bottom: 50px;
    }
    </style>
    

9.3.2 渲染结算区域的结构和样式

  1. 定义如下的 UI 结构:

    <!-- 最外层的容器 -->
    <view class="my-settle-container">
      <!-- 全选区域 -->
      <label class="radio">
        <radio color="#C00000" :checked="true" /><text>全选</text>
      </label>
    
      <!-- 合计区域 -->
      <view class="amount-box">
        合计:<text class="amount">¥1234.00</text>
      </view>
    
      <!-- 结算按钮 -->
      <view class="btn-settle">结算(0)</view>
    </view>
    
  2. 美化样式:

    .my-settle-container {
      position: fixed;
      bottom: 0;
      left: 0;
      width: 100%;
      height: 50px;
      // 将背景色从 cyan 改为 white
      background-color: white;
      display: flex;
      justify-content: space-between;
      align-items: center;
      padding-left: 5px;
      font-size: 14px;
    
      .radio {
        display: flex;
        align-items: center;
      }
    
      .amount {
        color: #c00000;
      }
    
      .btn-settle {
        height: 50px;
        min-width: 100px;
        background-color: #c00000;
        color: white;
        line-height: 50px;
        text-align: center;
        padding: 0 10px;
      }
    }
    

9.3.3 动态渲染已勾选商品的总数量

  1. store/cart.js 模块中,定义一个名称为 checkedCountgetters,用来统计已勾选商品的总数量:

    // 勾选的商品的总数量
    checkedCount(state) {
      // 先使用 filter 方法,从购物车中过滤器已勾选的商品
      // 再使用 reduce 方法,将已勾选的商品总数量进行累加
      // reduce() 的返回值就是已勾选的商品的总数量
      return state.cart.filter(x => x.goods_state).reduce((total, item) => total += item.goods_count, 0)
    }
    

    reduce()方法:

      array.reduce((pre, cur, index, arr)=>{
      	...
      }, init);
    

    pre: 必需。初始值, 或者计算结束后的返回值。 cur: 必需。当前元素。 index: 可选。当前元素的索引。 arr: 可选。当前元素所属的数组对象。 init: 可选。传递给函数的初始值,相当于pre的初始值。

  2. my-settle 组件中,通过 mapGetters 辅助函数,将需要的 getters 映射到当前组件中使用:

    import { mapGetters } from 'vuex'
    
    export default {
      computed: {
        ...mapGetters('m_cart', ['checkedCount']),
      },
      data() {
        return {}
      },
    }
    
  3. checkedCount 的值渲染到页面中:

    <!-- 结算按钮 -->
    <view class="btn-settle">结算({{checkedCount}})</view>
    

9.3.4 动态渲染全选按钮的选中状态

  1. 使用 mapGetters 辅助函数,将商品的总数量映射到当前组件中使用,并定义一个叫做 isFullCheck 的计算属性:

    import { mapGetters } from 'vuex'
    
    export default {
      computed: {
        // 1. 将 total 映射到当前组件中
        ...mapGetters('m_cart', ['checkedCount', 'total']),
        // 2. 是否全选
        isFullCheck() {
          return this.total === this.checkedCount
        },
      },
      data() {
        return {}
      },
    }
    
  2. radio 组件动态绑定 checked 属性的值:

    <!-- 全选区域 -->
    <label class="radio">
      <radio color="#C00000" :checked="isFullCheck" /><text>全选</text>
    </label>
    

9.3.5 实现商品的全选/反选功能

  1. store/cart.js 模块中,定义一个叫做 updateAllGoodsStatemutations 方法,用来修改所有商品的勾选状态:

    // 更新所有商品的勾选状态
    updateAllGoodsState(state, newState) {
      // 循环更新购物车中每件商品的勾选状态
      state.cart.forEach(x => x.goods_state = newState)
      // 持久化存储到本地
      this.commit('m_cart/saveToStorage')
    }
    
  2. my-settle 组件中,通过 mapMutations 辅助函数,将需要的 mutations 方法映射到当前组件中使用:

    // 1. 按需导入 mapMutations 辅助函数
    import { mapGetters, mapMutations } from 'vuex'
    
    export default {
      // 省略其它代码
      methods: {
        // 2. 使用 mapMutations 辅助函数,把 m_cart 模块提供的 updateAllGoodsState 方法映射到当前组件中使用
        ...mapMutations('m_cart', ['updateAllGoodsState']),
      },
    }
    
  3. 为 UI 中的 label 组件绑定 click 事件处理函数:

    <!-- 全选区域 -->
    <label class="radio" @click="changeAllState">
      <radio color="#C00000" :checked="isFullCheck" /><text>全选</text>
    </label>
    
  4. my-settle 组件的 methods 节点中,声明 changeAllState 事件处理函数:

    methods: {
      ...mapMutations('m_cart', ['updateAllGoodsState']),
      // label 的点击事件处理函数
      changeAllState() {
        // 修改购物车中所有商品的选中状态
        // !this.isFullCheck 表示:当前全选按钮的状态取反之后,就是最新的勾选状态
        this.updateAllGoodsState(!this.isFullCheck)
      }
    }
    

9.3.6 动态渲染已勾选商品的总价格

  1. store/cart.js 模块中,定义一个叫做 checkedGoodsAmountgetters,用来统计已勾选商品的总价格:

    // 已勾选的商品的总价
    checkedGoodsAmount(state) {
      // 先使用 filter 方法,从购物车中过滤器已勾选的商品
      // 再使用 reduce 方法,将已勾选的商品数量 * 单价之后,进行累加
      // reduce() 的返回值就是已勾选的商品的总价
      // 最后调用 toFixed(2) 方法,保留两位小数
      return state.cart.filter(x => x.goods_state)
                       .reduce((total, item) => total += item.goods_count * item.goods_price, 0)
                       .toFixed(2)
    }
    
  2. my-settle 组件中,使用 mapGetters 辅助函数,把需要的 checkedGoodsAmount 映射到当前组件中使用:

    ...mapGetters('m_cart', ['total', 'checkedCount', 'checkedGoodsAmount'])
    
  3. 在组件的 UI 结构中,渲染已勾选的商品的总价:

    <!-- 合计区域 -->
    <view class="amount-box">
      合计:<text class="amount">¥{{checkedGoodsAmount}}</text>
    </view>
    

9.3.7 动态计算购物车徽标的数值

  1. 问题说明:当修改购物车中商品的数量之后,tabBar 上的数字徽标不会自动更新。

  2. 解决方案:改造 mixins/tabbar-badge.js 中的代码,使用 watch 侦听器,监听 total 总数量的变化,从而动态为 tabBar 的徽标赋值:

    import { mapGetters } from 'vuex'
    
    // 导出一个 mixin 对象
    export default {
      computed: {
        ...mapGetters('m_cart', ['total']),
      },
      watch: {
        // 监听 total 值的变化
        total() {
          // 调用 methods 中的 setBadge 方法,重新为 tabBar 的数字徽章赋值
          this.setBadge()
        },
      },
      onShow() {
        // 在页面刚展示的时候,设置数字徽标
        this.setBadge()
      },
      methods: {
        setBadge() {
          // 调用 uni.setTabBarBadge() 方法,为购物车设置右上角的徽标
          uni.setTabBarBadge({
            index: 2,
            text: this.total + '', // 注意:text 的值必须是字符串,不能是数字
          })
        },
      },
    }
    

9.3.8 渲染购物车为空时的页面结构

改造 cart.vue 页面的 UI 结构,使用 v-ifv-else 控制购物车区域和空白购物车区域的按需展示:

	<template>
	  <view class="cart-container" v-if="cart.length !== 0">
	
	    <!-- 使用自定义的 address 组件 -->
	
	    <!-- 购物车商品列表的标题区域 -->
	
	    <!-- 商品列表区域 -->
	
	    <!-- 结算区域 -->
	
	  </view>
	
	  <!-- 空白购物车区域 -->
	  <view class="empty-cart" v-else>
	    <text class="tip-text">空空如也~</text>
	  </view>
	</template>

美化空白购物车区域的样式:

	.empty-cart {
	  display: flex;
	  justify-content: center;
	  align-items: center;
	  padding-top: 150px;
	
	  .empty-img {
	    width: 90px;
	    height: 90px;
	  }
	
	  .tip-text {
	    font-size: 12px;
	    color: gray;
	    margin-top: 15px;
	  }
	}

9.4 分支的合并与提交

  1. cart 分支进行本地提交:

    git add .
    git commit -m "完成了购物车的开发"
    
  2. 将本地的 cart 分支推送到码云:

    git push -u origin cart
    
  3. 将本地 cart 分支中的代码合并到 master 分支:

    git checkout master
    git merge cart
    git push
    
  4. 删除本地的 cart 分支:

    git branch -d cart