01.购物车-页面分析
- 入口: 点击底部tab菜单购物车时显示购物车页面;商详点击购物车跳转购物车页面
- 主要模块
- 商品列表
- 每一个商品包括选中状态,商品图片,商品名称,商品单价,商品数量
- 全选,总价,总数量
- 商品列表
- 其他说明
- 收货地址放在支付页面
- 商品列表每一项目可以调整选中状态及数量
- 点击结算跳转到支付
02.购物车数据存哪里?(补充 )
-
描述:购物车的数据的改变是发请求还是存本地,还是存Vuex?发请求即可
-
对比以上三个方案
-
购物车添加商品,刷新页面数据依然在
- vuex刷新后就没有
-
手机端添加购物车,PC端可以看到添加的商品
- 只存本地也不行
-
登录前加入购物车,登录后商品在
-
注意点:
- 实际项目中请求一般在100ms以内
03.购物车-静态页面
- copy
- iconfont引入
- 找到icon,加入购物车,生成新的.css文件
- copy内容更新项目里面iconfont.css
- 在cart页面使用iconfont
04.商详跳转购物车
- 商详购物车点击@click: toCart
- 跳转逻辑uni.switchTab
05.购物车-接口分析
-
url : https://www.uinav.com/api/public/v1/goods/goodslist?goods_ids=140,395,291
-
参数id以逗号分隔
-
返回数据
[ { 商品id, 商品名称, 商品价格 商品图片 } ]
问题:
- 那商品列表里面第一个商品的勾选状态和数量来自哪里呢?
- 参数里面goods_ids从哪里来呢?
06.购物车数据结构
商品勾选状态,数量还有goodsId存storage
那么storage购物车的数据结构是什么样的?
- 数组
- 取值比较麻烦
- 有序的
- 对象
- 取值比较方便
//数组
[
{
goodsId:542
num:99,
checked:true
},
....
]
//对象
{
542:{
num:99,
checked:true
},
291:{
num:88,
checked:false
}
}
07.商详加入购物车
点击商详加入购物车按钮加入购物车
1. 点击@click: addCart
- 商品加入购物车
1. 把当前商品存储到storage里面
1. goodsId就是当前商品的id
2. num: 第一次为1,后续++
3. checked始终为true
2. 步骤
1. 取storage
2. 更新
3. 存storage
注意点:
- 商品加入购物车,如果在购物车中去勾选,再添加也勾选状态
- 如何区分是第一次,还是非第一次?
- 对象
cart[542]
- 数组find
- 对象
08.购物车-非首次添加优化
-
添加成功购物车提示用户
-
非首次添加,商品移到数组最前面
-
findIndx的使用
注意点:
-
git stash储藏,相当于是游戏存档
git stash -u #保存当前工作区代码,-u包括新增文件 git stash pop #弹出最近一次的储藏
09.购物车-基本渲染
-
发请求
-
请求参数:取storage cart数组里面每一项的goodsId以逗号分隔拼成字符串
-
forEach
-
方案2,返回一个goodsId的数组,然后join
let goodsIdArr = cart.map(item=>{ return item.goodsId }) let ids = goodsIdArr.join(',')
-
-
什么时机发请求??
- onShow
- tabbar页面,后续不会再执行onLoad
- 方法名:queryGoodsList
- onShow
-
拿到请求数据,设置给data属性goodsList
-
在结构中vfor渲染
-
10.购物车-渲染选中状态及数量
接口返回的数组并不是按传参goodsId的顺序
-
问题:
- 应该以cart为主,遍历
- 数量渲染:去goodslist里面找一项goodsId为当goodsid相等的那一项的商品名称。在结构写不了
-
解决方案
-
在请求结束后,把storage cart和goodsList融合在一起,以cart为主,返回一个新的属性
this.goodsList = this.cart.map(cartItem => { // 到_goodsList里面找一项goodsId相等的,组成新的对象 let targetGoods = _goodsList.find(goodsItem => { return goodsItem.goods_id === cartItem.goodsId; }); return {...targetGoods, ...cartItem}; });
-
渲染
-
注意点:
- cart添加为this的属性,相当于是全局变量
11.购物车-商品勾选与不勾选
- 商品列表的每一项点击时,checked状态取反
- 点击@click = toggleCheck
toggleCheck(item){
item.checked = !item.checked
},
12.购物车-商品跳转商详
点击商品(不包括勾选,还有数量±按钮)跳转商详
- 点击@click= toItem
- toItem,跳转传参goodsId
补充:
- 不包括勾选,还有数量±按钮需要事件阻止冒泡
- .stop
- uniapp里面navigator跳转用绝对路径
13.购物车-商品数量
-
点击+,数量++
- 点击@click.stop: addNum
-
点击-,数量–
- 点击@click.stop: subNum
-
如果为1,提示删除goodsList
this.goodsList.splice(从哪一项开始删除,删除多少项)
14.购物车-全选状态
- 所有商品都选中,全选就勾选
- 计算属性isAll
- 如何判断所有商品都选中呢?
- 1.选中的商品数量和数组总长度相等
- 2.如果有一个不选中,那么false
- 3.所有项都满足指定条件就返回true, 数组方法every
isAll() {
// 所有商品都选中
//1.选中的商品数量和数组总长度相等
// let sum =0
// this.goodsList.forEach(item=>{
// if(item.checked){
// sum++
// }
// })
// return sum ===this.goodsList.length
//2.如果有一个不选中,那么false
// for (let item of this.goodsList) {
// if (!item.checked) {
// return false;
// }
// }
// return true
// 3.所有项都满足指定条件就返回true
return this.goodsList.every(item=>{
return item.checked
})
}
15.购物车-全选点击
-
点击全选,全选状态切换, 勾选/不勾选
-
点击@click: toggleAll
-
在isAll的计算属性里面把isAll改变后的值,设置给它的依赖(goodsList每一项的checked)
set(status){ // 把isAll改变状态和每个商品状态同步 this.goodsList.forEach(item=>{ item.checked= status }) }
-
16.购物车-总价与总数量
-
总数量是选中的商品数量之和
-
计算属性totalNum
-
如何计算
- reduce
return this.goodsList.reduce((sum,item)=>{ return sum+(item.checked?item.num:0) },0)
-
-
总价:选中的商品数量*商品价格之和
- 计算属性totalPrice
- 如何计算
- reduce
17.购物车-状态存storage
- 现象:在界面上改变(勾选的状态,数量改变),data属性goodsList改变,但并没有改变storage.重新编译后,界面状态没有了。
- 解决方案:界面的改变应该同时存到storage
- isAll点击,商品勾选点击,数量++,数量–,商品删除都需要更新到storage
- watch goodsList即可
// isAll点击,商品勾选点击,数量++,数量--会改变goodsList
goodsList:{
handler(){
console.log(this.goodsList)
// 不用从storage取,然后存. 因为goodsList已经有存到storage cart里面所有的数据
let cart = this.goodsList.map(item=>{
return{
goodsId:item.goodsId,
num:item.num,
checked:item.checked
}
})
// 设置到storage
uni.setStorageSync('cart',cart)
},
deep:true
}
注意点:
- 深度监听
- goodsList已经有构造cart的所有的数据,所有没必要取cart
Array的方法总结
-
find
- 概念:在数组里面找一项符合条件的元素
- 如何作用的:遍历数组,对每一项执行指定函数,如果为ture,返回这一项,并中断遍历;如果一直没找到,返回undefined
-
findIndex
- 概念:在数组里面找一项符合条件的元素的下标
-
forEach
- 遍历数组
- 注意点:
- 不能中断遍历,而且不能修改元素
-
map
- 概念:一般用来返回一个新的数组,新数组和原数组结构不一样
- 如何作用的:返回新数组,遍历原数组,每一项执行指定函数,返回值作为新数组的项
-
for …of
- 遍历
- for(let item of arr), item是指元素并不是指下标。遍历可以叫断
-
every
- 遍历数组,每一项执行指定函数,所有结果为true,就返回true
-
some
- 遍历数组,每一项执行指定函数,只要有一项结果为true,就返回true
-
reduce
- 如何作用
- 第一遍历时,中间变量被第2个参数赋值
- 中间变量和元素的计算都会赋值给中间变量
- 遍历结束后,reduce返回中间变量
- 应用场景:累加,和阶乘
数组.reduce((中间变量,元素)=>{ return 中间变量和元素的计算 },中间变量的初始值)
- 如何作用