目录
[8.0 创建 cart 分支](#8.0 创建 cart 分支)
[8.1 配置 vuex](#8.1 配置 vuex)
[8.2 创建购物车的 store 模块](#8.2 创建购物车的 store 模块)
[8.3 在商品详情页中使用 Store 中的数据](#8.3 在商品详情页中使用 Store 中的数据)
[8.4 实现加入购物车的功能](#8.4 实现加入购物车的功能)
[8.5 动态统计购物车中商品的总数量](#8.5 动态统计购物车中商品的总数量)
[8.6 持久化存储购物车中的商品](#8.6 持久化存储购物车中的商品)
[8.7 优化商品详情页的 total 侦听器](#8.7 优化商品详情页的 total 侦听器)
[8.8 动态为 tabBar 页面设置数字徽标](#8.8 动态为 tabBar 页面设置数字徽标)
[8.9 将设置 tabBar 徽标的代码抽离为 mixins](#8.9 将设置 tabBar 徽标的代码抽离为 mixins)
[9.0 创建购物车页面的编译模式](#9.0 创建购物车页面的编译模式)
[9.1 商品列表区域](#9.1 商品列表区域)
[9.1.1 渲染购物车商品列表的标题区域](#9.1.1 渲染购物车商品列表的标题区域)
[9.1.2 渲染商品列表区域的基本结构](#9.1.2 渲染商品列表区域的基本结构)
[9.1.3 为 my-goods 组件封装 radio 勾选状态](#9.1.3 为 my-goods 组件封装 radio 勾选状态)
[9.1.4 为 my-goods 组件封装 radio-change 事件](#9.1.4 为 my-goods 组件封装 radio-change 事件)
[9.1.5 修改购物车中商品的勾选状态](#9.1.5 修改购物车中商品的勾选状态)
[9.1.6 为 my-goods 组件封装 NumberBox](#9.1.6 为 my-goods 组件封装 NumberBox)
[9.1.7 为 my-goods 组件封装 num-change 事件](#9.1.7 为 my-goods 组件封装 num-change 事件)
[9.1.8 解决 NumberBox 数据不合法的问题](#9.1.8 解决 NumberBox 数据不合法的问题)
[9.1.9 完善 NumberBox 的 inputValue 侦听器](#9.1.9 完善 NumberBox 的 inputValue 侦听器)
[9.1.10 修改购物车中商品的数量](#9.1.10 修改购物车中商品的数量)
[9.1.11 渲染滑动删除的 UI 效果](#9.1.11 渲染滑动删除的 UI 效果)
[9.1.12 实现滑动删除的功能](#9.1.12 实现滑动删除的功能)
[9.2 收货地址区域](#9.2 收货地址区域)
[9.2.1 创建收货地址组件](#9.2.1 创建收货地址组件)
[9.2.2 实现收货地址区域的按需展示](#9.2.2 实现收货地址区域的按需展示)
[9.2.3 实现选择收货地址的功能](#9.2.3 实现选择收货地址的功能)
[9.2.4 将 address 信息存储到 vuex 中](#9.2.4 将 address 信息存储到 vuex 中)
[9.2.5 将 Store 中的 address 持久化存储到本地](#9.2.5 将 Store 中的 address 持久化存储到本地)
[9.2.6 将 addstr 抽离为 getters](#9.2.6 将 addstr 抽离为 getters)
[9.2.7 重新选择收货地址](#9.2.7 重新选择收货地址)
[9.2.8 解决收货地址授权失败的问题](#9.2.8 解决收货地址授权失败的问题)
[9.2.9 解决 iPhone 真机上无法重新授权的问题](#9.2.9 解决 iPhone 真机上无法重新授权的问题)
[9.3 结算区域](#9.3 结算区域)
[9.3.1 把结算区域封装为组件](#9.3.1 把结算区域封装为组件)
[9.3.2 渲染结算区域的结构和样式](#9.3.2 渲染结算区域的结构和样式)
[9.3.3 动态渲染已勾选商品的总数量](#9.3.3 动态渲染已勾选商品的总数量)
[9.3.4 动态渲染全选按钮的选中状态](#9.3.4 动态渲染全选按钮的选中状态)
[9.3.5 实现商品的全选/反选功能](#9.3.5 实现商品的全选/反选功能)
[9.3.6 动态渲染已勾选商品的总价格](#9.3.6 动态渲染已勾选商品的总价格)
[9.3.7 动态计算购物车徽标的数值](#9.3.7 动态计算购物车徽标的数值)
[9.3.8 渲染购物车为空时的页面结构](#9.3.8 渲染购物车为空时的页面结构)
[9.4 分支的合并与提交](#9.4 分支的合并与提交)
八、加入购物车
8.0 创建 cart 分支
运行如下的命令,基于 master 分支在本地创建 cart 子分支,用来开发购物车相关的功能:
bash
git checkout -b cart

8.1 配置 vuex
-
在项目根目录中创建
store文件夹,专门用来存放 vuex 相关的模块 -
在
store目录上鼠标右键,选择新建 -> js文件,新建store.js文件:

- 在
store.js中按照如下 4 个步骤初始化 Store 的实例对象:

- 在
main.js中导入store实例对象并挂载到 Vue 的实例上:





8.2 创建购物车的 store 模块
- 在
store目录上鼠标右键,选择新建 -> js文件,创建购物车的 store 模块,命名为cart.js:

- 在
cart.js中,初始化如下的 vuex 模块:

- 在
store/store.js模块中,导入并挂载购物车的 vuex 模块,示例代码如下:




8.3 在商品详情页中使用 Store 中的数据
- 在
goods_detail.vue页面中,修改<script></script>标签中的代码如下:

注意:今后无论映射 mutations 方法,还是 getters 属性,还是 state 中的数据,都需要指定模块的名称,才能进行映射。
- 在页面渲染时,可以直接使用映射过来的数据,例如:




8.4 实现加入购物车的功能
- 在 store 目录下的
cart.js模块中,封装一个将商品信息加入购物车的 mutations 方法,命名为addToCart。示例代码如下:

- 在商品详情页面中,通过
mapMutations这个辅助方法,把 vuex 中m_cart模块下的addToCart方法映射到当前页面:

- 为商品导航组件
uni-goods-nav绑定@buttonClick="buttonClick"事件处理函数:











8.5 动态统计购物车中商品的总数量
- 在
cart.js模块中,在getters节点下定义一个total方法,用来统计购物车中商品的总数量:

- 在商品详情页面的
script标签中,按需导入mapGetters方法并进行使用:

- 通过
watch侦听器,监听计算属性total值的变化,从而动态为购物车按钮的徽标赋值:



8.6 持久化存储购物车中的商品
- 在
cart.js模块中,声明一个叫做saveToStorage的 mutations 方法,此方法负责将购物车中的数据持久化存储到本地:

- 修改
mutations节点中的addToCart方法,在处理完商品信息后,调用步骤 1 中定义的saveToStorage方法:

- 修改
cart.js模块中的state函数,读取本地存储的购物车数据,对 cart 数组进行初始化:



8.7 优化商品详情页的 total 侦听器
- 使用普通函数的形式 定义的 watch 侦听器,在页面首次加载后不会被调用。因此导致了商品详情页在首次加载完毕之后,不会将商品的总数量显示到商品导航区域:

- 为了防止这个上述问题,可以使用对象的形式来定义 watch 侦听器(详细文档请参考 Vue 官方的 watch 侦听器教程),示例代码如下:


8.8 动态为 tabBar 页面设置数字徽标
需求描述:从商品详情页面导航到购物车页面之后,需要为 tabBar 中的购物车动态设置数字徽标。
- 把 Store 中的 total 映射到
cart.vue中使用:

- 在页面刚显示出来的时候,立即调用
setBadge方法,为 tabBar 设置数字徽标:

- 在
methods节点中,声明setBadge方法如下,通过uni.setTabBarBadge()为 tabBar 设置数字徽标:




8.9 将设置 tabBar 徽标的代码抽离为 mixins
注意:除了要在 cart.vue 页面中设置购物车的数字徽标,还需要在其它 3 个 tabBar 页面中,为购物车设置数字徽标。
此时可以使用 Vue 提供的 mixins 特性,提高代码的可维护性。
- 在项目根目录中新建
mixins文件夹,并在mixins文件夹之下新建tabbar-badge.js文件,用来把设置 tabBar 徽标的代码封装为一个 mixin 文件:

- 修改
home.vue,cate.vue,cart.vue,my.vue这 4 个 tabBar 页面的源代码,分别导入@/mixins/tabbar-badge.js模块并进行使用:








九、购物车页面
9.0 创建购物车页面的编译模式
- 打开微信开发者工具,点击工具栏上的"编译模式"下拉菜单,选择"添加编译模式":

- 勾选"启动页面的路径"之后,点击"确定"按钮,新增购物车页面的编译模式:

9.1 商品列表区域
9.1.1 渲染购物车商品列表的标题区域
- 定义如下的 UI 结构:

- 美化样式:




9.1.2 渲染商品列表区域的基本结构
- 通过
mapState辅助函数,将 Store 中的cart数组映射到当前页面中使用:

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



9.1.3 为 my-goods 组件封装 radio 勾选状态
- 打开
my-goods.vue组件的源代码,为商品的左侧图片区域添加radio组件:

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

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

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

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

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









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

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

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

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




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

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



9.1.6 为 my-goods 组件封装 NumberBox
注意:NumberBox 组件是 uni-ui 提供的
- 修改
my-goods.vue组件的源代码,在类名为goods-info-box的 view 组件内部渲染NumberBox组件的基本结构:

- 美化页面的结构:

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

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

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

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







9.1.7 为 my-goods 组件封装 num-change 事件
- 当用户修改了
NumberBox的值以后,希望将最新的商品数量更新到购物车中,此时用户可以为my-goods组件绑定@num-change事件,从而获取当前商品的goods_id和goods_count:
html
<!-- 商品列表区域 -->
<block v-for="(goods, i) in cart" :key="i">
<my-goods :goods="goods" :show-radio="true" :show-num="true" @radio-change="radioChangeHandler" @num-change="numberChangeHandler"></my-goods>
</block>
定义 numberChangeHandler 事件处理函数如下:

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

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






9.1.8 解决 NumberBox 数据不合法的问题
问题说明:当用户在 NumberBox 中输入字母等非法字符之后,会导致 NumberBox 数据紊乱的问题
- 打开项目根目录中
uni_modules/uni-number-box/components/uni-number-box/uni-number-box.vue组件,修改methods节点中的_onBlur函数如下:

- 修改完毕之后,用户输入小数 会被转化为整数 ,用户输入非法字符 会被替换为默认值 1

9.1.9 完善 NumberBox 的 inputValue 侦听器
问题说明:在用户每次输入内容之后,都会触发 inputValue 侦听器,从而调用 this.$emit("change", newVal) 方法。这种做法可能会把不合法的内容传递出去!
- 打开项目根目录中
uni_modules/uni-number-box/components/uni-number-box/uni-number-box.vue组件,修改watch节点中的inputValue侦听器如下:

- 修改完毕之后,NumberBox 组件只会把合法的、且不包含小数点的新值传递出去。


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

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



9.1.11 渲染滑动删除的 UI 效果
滑动删除需要用到 uni-ui 的 uni-swipe-action 组件和 uni-swipe-action-item。详细的官方文档请参考SwipeAction 滑动操作。
效果如图:

- 改造
cart.vue页面的 UI 结构,将商品列表区域的结构修改如下(可以使用 uSwipeAction 代码块快速生成基本的 UI 结构):
html
<!-- 商品列表区域 -->
<!-- uni-swipe-action 是最外层包裹性质的容器 -->
<uni-swipe-action>
<block v-for="(goods, i) in cart" :key="i">
<!-- uni-swipe-action-item 可以为其子节点提供滑动操作的效果。需要通过 options 属性来指定操作按钮的配置信息 -->
<uni-swipe-action-item :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>
- 在 data 节点中声明
options数组,用来定义操作按钮的配置信息:

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

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






9.1.12 实现滑动删除的功能
- 在
store/cart.js模块的mutations节点中声明如下的方法,从而根据商品的 Id 从购物车中移除对应的商品:

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


9.2 收货地址区域
9.2.1 创建收货地址组件
- 在
components目录上鼠标右键,选择新建组件,并填写组件相关的信息:

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

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














9.2.2 实现收货地址区域的按需展示
- 在 data 中定义收货地址的信息对象:

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


9.2.3 实现选择收货地址的功能
- 为
请选择收货地址+的button按钮绑定点击事件处理函数:

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

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

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















9.2.4 将 address 信息存储到 vuex 中
- 在
store目录中,创建用户相关的vuex模块,命名为user.js:

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

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






修正:my-address.vue 52行那里少了个逗号。
9.2.5 将 Store 中的 address 持久化存储到本地
- 修改
store/user.js模块中的代码如下:



9.2.6 将 addstr 抽离为 getters
目的:为了提高代码的复用性,可以把收货的详细地址抽离为 getters,方便在多个页面和组件之间实现复用。
- 剪切
my-address.vue组件中的addstr计算属性的代码,粘贴到user.js模块中,作为一个 getters 节点:

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




9.2.7 重新选择收货地址
- 为 class 类名为
address-info-box的盒子绑定click事件处理函数如下:


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

- 改造
chooseAddress方法如下:

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













9.2.9 解决 iPhone 真机上无法重新授权的问题
问题说明:在 iPhone 设备上,当用户取消授权之后,再次点击选择收货地址按钮的时候,无法弹出授权的提示框!
-
导致问题的原因 - 用户取消授权后,再次点击 "选择收货地址" 按钮的时候:
-
在模拟器 和安卓真机 上,错误消息
err.errMsg的值为chooseAddress:fail auth deny -
在 iPhone 真机 上,错误消息
err.errMsg的值为chooseAddress:fail authorize no response
-
-
解决问题的方案 - 修改
chooseAddress方法中的代码,进一步完善用户没有授权时的if判断条件即可:


9.3 结算区域
9.3.1 把结算区域封装为组件
- 在
components目录中,新建my-settle结算组件:

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

- 在
cart.vue页面中使用自定义的my-settle组件,并美化页面样式,防止页面底部被覆盖:





9.3.2 渲染结算区域的结构和样式
- 定义如下的 UI 结构:

- 美化样式:



9.3.3 动态渲染已勾选商品的总数量
- 在
store/cart.js模块中,定义一个名称为checkedCount的 getters,用来统计已勾选商品的总数量:

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

- 将
checkedCount的值渲染到页面中:



9.3.4 动态渲染全选按钮的选中状态
- 使用
mapGetters辅助函数,将商品的总数量 映射到当前组件中使用,并定义一个叫做isFullCheck的计算属性:

- 为 radio 组件动态绑定
checked属性的值:




9.3.5 实现商品的全选/反选功能
- 在
store/cart.js模块中,定义一个叫做updateAllGoodsState的 mutations 方法,用来修改所有商品的勾选状态:

- 在
my-settle组件中,通过mapMutations辅助函数,将需要的 mutations 方法映射到当前组件中使用:

- 为 UI 中的
label组件绑定click事件处理函数:

- 在
my-settle组件的 methods 节点中,声明changeAllState事件处理函数:





9.3.6 动态渲染已勾选商品的总价格
- 在
store/cart.js模块中,定义一个叫做checkedGoodsAmount的 getters,用来统计已勾选商品的总价格:

- 在
my-settle组件中,使用mapGetters辅助函数,把需要的checkedGoodsAmount映射到当前组件中使用:

- 在组件的 UI 结构中,渲染已勾选的商品的总价:



9.3.7 动态计算购物车徽标的数值
-
问题说明:当修改购物车中商品的数量之后,tabBar 上的数字徽标不会自动更新。
-
解决方案 :改造
mixins/tabbar-badge.js中的代码,使用watch侦听器,监听total总数量的变化,从而动态为 tabBar 的徽标赋值:


9.3.8 渲染购物车为空时的页面结构
-
将
资料目录中的cart_empty@2x.png图片复制到项目的/static/目录中 -
改造
cart.vue页面的 UI 结构,使用v-if和v-else控制购物车区域 和空白购物车区域的按需展示:

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





9.4 分支的合并与提交
- 将
cart分支进行本地提交:
bash
git add .
git commit -m "完成了购物车的开发"


- 将本地的
cart分支推送到码云:
bash
git push -u origin cart

- 将本地
cart分支中的代码合并到master分支:
bash
git checkout master
git merge cart
git push

- 删除本地的
cart分支:
bash
git branch -d cart

