携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天,点击查看活动详情
9.3 结算区域
9.3.1 把结算区域封装为组件
-
在
components目录中,新建my-settle结算组件 -
初始化
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> -
在
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 渲染结算区域的结构和样式
-
定义如下的 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> -
美化样式:
.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 动态渲染已勾选商品的总数量
-
在
store/cart.js模块中,定义一个名称为checkedCount的getters,用来统计已勾选商品的总数量:// 勾选的商品的总数量 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的初始值。
-
在
my-settle组件中,通过mapGetters辅助函数,将需要的getters映射到当前组件中使用:import { mapGetters } from 'vuex' export default { computed: { ...mapGetters('m_cart', ['checkedCount']), }, data() { return {} }, } -
将
checkedCount的值渲染到页面中:<!-- 结算按钮 --> <view class="btn-settle">结算({{checkedCount}})</view>
9.3.4 动态渲染全选按钮的选中状态
-
使用
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 {} }, } -
为
radio组件动态绑定checked属性的值:<!-- 全选区域 --> <label class="radio"> <radio color="#C00000" :checked="isFullCheck" /><text>全选</text> </label>
9.3.5 实现商品的全选/反选功能
-
在
store/cart.js模块中,定义一个叫做updateAllGoodsState的mutations方法,用来修改所有商品的勾选状态:// 更新所有商品的勾选状态 updateAllGoodsState(state, newState) { // 循环更新购物车中每件商品的勾选状态 state.cart.forEach(x => x.goods_state = newState) // 持久化存储到本地 this.commit('m_cart/saveToStorage') } -
在
my-settle组件中,通过mapMutations辅助函数,将需要的mutations方法映射到当前组件中使用:// 1. 按需导入 mapMutations 辅助函数 import { mapGetters, mapMutations } from 'vuex' export default { // 省略其它代码 methods: { // 2. 使用 mapMutations 辅助函数,把 m_cart 模块提供的 updateAllGoodsState 方法映射到当前组件中使用 ...mapMutations('m_cart', ['updateAllGoodsState']), }, } -
为 UI 中的
label组件绑定click事件处理函数:<!-- 全选区域 --> <label class="radio" @click="changeAllState"> <radio color="#C00000" :checked="isFullCheck" /><text>全选</text> </label> -
在
my-settle组件的methods节点中,声明changeAllState事件处理函数:methods: { ...mapMutations('m_cart', ['updateAllGoodsState']), // label 的点击事件处理函数 changeAllState() { // 修改购物车中所有商品的选中状态 // !this.isFullCheck 表示:当前全选按钮的状态取反之后,就是最新的勾选状态 this.updateAllGoodsState(!this.isFullCheck) } }
9.3.6 动态渲染已勾选商品的总价格
-
在
store/cart.js模块中,定义一个叫做checkedGoodsAmount的getters,用来统计已勾选商品的总价格:// 已勾选的商品的总价 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) } -
在
my-settle组件中,使用mapGetters辅助函数,把需要的checkedGoodsAmount映射到当前组件中使用:...mapGetters('m_cart', ['total', 'checkedCount', 'checkedGoodsAmount']) -
在组件的 UI 结构中,渲染已勾选的商品的总价:
<!-- 合计区域 --> <view class="amount-box"> 合计:<text class="amount">¥{{checkedGoodsAmount}}</text> </view>
9.3.7 动态计算购物车徽标的数值
-
问题说明:当修改购物车中商品的数量之后,
tabBar上的数字徽标不会自动更新。 -
解决方案:改造
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-if 和 v-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 分支的合并与提交
-
将
cart分支进行本地提交:git add . git commit -m "完成了购物车的开发" -
将本地的
cart分支推送到码云:git push -u origin cart -
将本地
cart分支中的代码合并到master分支:git checkout master git merge cart git push -
删除本地的
cart分支:git branch -d cart