一、加入购物车
当我们点击加入购物车按钮之后,要返回一个商品个数和购物的商品信息,那我们就需要调用添加到购物车接口
分析一下会发现,我们只是为了返回一个成功或者失败的结果,那用promise不是更好吗,所以我们进行改进
二、导入加入购物车成功页面组件 + 配置路由
三、购物成功页面拿数据
1、路由传递参数
当我们点击加入购物车之后,加入成功页面要拿到商品的信息和购买的个数,然后要体现在url地址栏中,如果按照之前那么写的话,地址栏会非常的丑,信息不明显,所以要改进,这里用到了会话存储的方法
本地存储:持久化
会话存储:并非持久
本地存储|会话存储:一半存储的是字符串
产品信息的数据【比较复杂:skuInfo】,通过会话存储(不持久化,会话结束数据会消失)
购物车添加成功这个页面并非需要一直持久存在,所以选择会话存储
因为传递的是一个对象,所以要用转换为字符串
在AddCartSuccess.js文件中进行接收
AddCartSuccess.js
computed:{
skuInfo(){
return JSON.parse(sessionStorage.getItem('SKUINFO'));
}
}
2、渲染数据
<div class="right-info">
<p class="title">{{skuInfo.skuName}}</p>
<p class="attr">{{ skuInfo.skuDesc }} 数量:{{$route.query.skuNum}}}</p>
</div>
四、添加购物车成功页面两个按钮的动态绑定
给查看商品详情按钮设定声明式导航,回退到商品页面并带上商品id参数,所以使用动态式的:to
五、获取购物车信息接口
获取接口成功,但是我们看到没有数据,这是为什么呢?
用户有非常多,每一个人都应该有自己的购物车,但是系统不能分辨哪一个商品是谁加入购物车的,才会出现没有数据的现象。
解决方案:uuid
六、uuid游客身份获取购物车数据
UUID全称:Universally Unique Identifier,即通用唯一识别码。
uuid_token.js
import {v4 as uuidv4} from 'uuid'
//要生成一个随机字符串,且每次执行不能发生变化,游客身份持久存储
export const getUUID = ()=>{
//先从本地存储获取uuid(看一下本地存储里面是否有)
let uuid_token = localStorage.getItem('UUIDTOKEN');
//如果没有
if(!uuid_token){
//我生成游客临时身份
uuid_token = uuidv4();
//本地存储存储一次
localStorage.setItem('UUIDTOKEN',uuid_token);
}
return uuid_token;
}
切记一定要有返回值,要不然是undefined!!
创建一个uuid_token.js文件,在这个文件里面封装一个生成随机,且能存储的的游客身份
在detail仓库里使用这个函数方法
在request.js中设置请求头
//在当前模块引入store
import store from '@/store'
//请求拦截器:在发请求之前,请求拦截器可以检测到,可以在请求发出去之前做一些事情
requests.interceptors.request.use((config) => {
//config:配置对象,对象里面有一个属性很重要,headers请求头
//进度条开始动
if (store.state.detail.uuid_token) {
//请求头添加一个字段(userTempId):这个字段已经是和后台商量好了
config.headers.userTempId = store.state.detail.uuid_token
}
nprogress.start();
return config;
});
七、动态绑定数据
定义两个函数来计算总价和全选按钮
//计算购买产品的总价
totalPrice() {
let sum = 0;
this.cartInfoList.forEach((item) => {
sum += item.skuNum * item.skuPrice;
});
return sum;
},
//判断底部复选框是否勾选[全部产品都选中,才勾选]
isAllCheck() {
// every 只要全部元素里有一个不是1,则为假,全为真才真
return this.cartInfoList.every((item) => item.isChecked == 1);
},
在这定义了三个形参type,disNum,cart
type
为了区分这三个元素
disNum
形参: + 变化量(1) -变化量(-1) input最终的个数
cart
:哪一个产品
//修改某一个产品的个数
handler(type, disNum, cart) {
//type为了区分这三个元素
//disNum 形参: + 变化量(1) -变化量(-1) input最终的个数
//cart:哪一个产品
//向服务器发请求,修改数量
switch (type) {
//加号
case "add":
disNum = 1;
break;
case "minus":
//判断产品的个数大于1,才可以传递给服务器-1
if (cart.skuNum > 1) {
disNum = -1;
} else {
//产品的个数小于等于1
disNum = 0;
}
break;
// disNum = cart.skuNum > 1 ? -1 : 0; //写成三元表达式
}
这里已经对三个按钮设置了判断,接下来就要通知服务器,把新的价格信息拿到,并渲染到页面上
//修改某一个产品的个数
async handler(type, disNum, cart) {
//type为了区分这三个元素
//disNum 形参: + 变化量(1) -变化量(-1) input最终的个数
//cart:哪一个产品
//向服务器发请求,修改数量
switch (type) {
//加号
case "add":
disNum = 1;
break;
case "minus":
//判断产品的个数大于1,才可以传递给服务器-1
if (cart.skuNum > 1) {
disNum = -1;
} else {
//产品的个数小于等于1
disNum = 0;
}
break;
// disNum = cart.skuNum > 1 ? -1 : 0; //写成三元表达式
case "change":
//用户输入进来的最终量,非法的(带有汉字),带给服务器数字
if (isNaN(disNum) || disNum < 1) {
disNum = 0;
} else {
disNum = parseInt(disNum) - cart.skuNum;
}
break;
}
//派发action
try {
//代表的是成功
await this.$store.dispatch("addOrUpdateShopCart", {
skuId: cart.skuId,
skuNum: disNum,
});
//再一次获取服务器最新的数据进行展示
this.getData();
} catch (error) {}
这一块的代码看一看吧,思路我还有点懵,后面要回来多看看
八、删除商品
这里要用到删除购物车的接口,还是老手法
我们看到接口信息里的data没有数据,说明不需要返回信息,只需要判断是否成功
//删除购物车产品的接口
export const reqDeleteCartById = (skuId) => requests({
url: `/cart/deleteCart/${skuId}`,
method: 'delete'
})
async DeleteCartListById({ commit }, skuId) {
let result = await reqDeleteCartById(skuId);
if (result.code == 200) {
return 'ok'
} else {
return Promise.reject(new Error('faile'))
}
}
给删除按钮定义一个自定义事件
<a class="sindelet" @click="deleteCartById(cart)">删除</a>
点击删除之后要记得更新数据
//删除某一个产品的操作
async deleteCartById(cart){
try {
await this.$store.dispatch('DeleteCartListById',cart.skuId);
this.getData();
} catch (error) {
alert(error.message);
}
九、bug
当用户点减少数量太快,会显示负数,所以这一块我们要用到节流,重新复习一下防抖和节流哈哈
先引入throttle
import throttle from 'lodash/throttle'
handler: throttle(async function (type, disNum, cart) {
//type为了区分这三个元素
//disNum 形参: + 变化量(1) -变化量(-1) input最终的个数
//cart:哪一个产品
//向服务器发请求,修改数量
switch (type) {
//加号
case "add":
disNum = 1;
break;
case "minus":
//判断产品的个数大于1,才可以传递给服务器-1
if (cart.skuNum > 1) {
disNum = -1;
} else {
//产品的个数小于等于1
disNum = 0;
}
break;
// disNum = cart.skuNum > 1 ? -1 : 0; //写成三元表达式
case "change":
//用户输入进来的最终量,非法的(带有汉字),带给服务器数字
if (isNaN(disNum) || disNum < 1) {
disNum = 0;
} else {
disNum = parseInt(disNum) - cart.skuNum;
}
break;
}
//派发action
try {
//代表的是成功
await this.$store.dispatch("addOrUpdateShopCart", {
skuId: cart.skuId,
skuNum: disNum,
});
//再一次获取服务器最新的数据进行展示
this.getData();
} catch (error) {}
}),
十、修改产品状态
第一步还是调用接口,然后定义自定义事件,派发action,更新数据
//修改商品的选中状态
export const reqUpdateCheckedById = (skuId,isChecked) => requests({
url: `/cart/checkCart/${skuId}/${isChecked}`,
method: 'get'
})
async updateCheckedByid({ commit }, skuId, isChecked) {
let result = await reqUpdateCheckedById(skuId, isChecked);
if (result == 200) {
return 'ok'
} else {
return Promise.reject(new Error('false'))
}
}
<input type="checkbox" name="chk_list" :checked="cart.isChecked == 1" @change="updateCheckted(cart, $event)" />
这里我们试着打印一下选中和未选中时,传回的是一个布尔值true和false,但是我们需要的是0或者1来判断,所以我们要进行一个判断
let isChecked = event.target.checked ? "1" : "0";
派发action
this.$store.dispatch("updateCheckedById", {
skuId: cart.skuId,
isChecked,
});
但是这里我们还要判断是否成功,所以用try…catch…
//修改某个产品的勾选状态
async updateCheckted(cart, event) {
//带给服务器的参数isChecked 不是布尔值,应该是1 或者 0
try {
let isChecked = event.target.checked ? "1" : "0";
await this.$store.dispatch("updateCheckedById", {
skuId: cart.skuId,
isChecked,
});
this.getData();
} catch (error) {
//如果失败提示
alert(error.message);
}
},
十一、全部商品的勾选状态修改
给按钮绑定了自定义事件,但是在这个回调函数里我们没办法收集到一些有用的数据,因为没有删除全部选中的商品接口,无法派发action,那怎么办呢
//这个回调函数咱门没办法收集到一些有用数据
async deleteAllCheckedCart() {
try {
await this.$store.dispatch("deleteAllCheckedCart");
// 再次发送请求
this.getData();
} catch (error) {
console.log(error.message);
}
},
回到shopcart仓库中,这里面有我们需要的数据,那一个仓库里应该如何传递数据,从state里把其他的数据捞出来,捞出来每一个商品的isChecked,看是否等于1,等于1就派发action到deleteCartListBySkuId,不为1就为空,然后用promise.all
判断
promise.all(),其中有一个为假就返回false
//删除全部勾选的产品
deleteAllCheckedCart({ dispatch, getters }) {
//context:小仓库,commit【提交mutations修改state】 getters【计算属性】 dispatch【派发action】 state【当前仓库数据】
let PromiseAll = [];
//获取购物车中全部的产品(是一个数组)
getters.carList.cartInfoList.forEach((item) => {
let Promise =
item.isChecked == 1 ?
dispatch("deleteCartListBySkuId", item.skuId) :
"";
//将每一次返回的Promise添加到数组当中
PromiseAll.push(Promise);
});
//只要全部的p1|p2....都成功,返回结果即为成功
//如果有一个失败,返回即为失败结果
return Promise.all(PromiseAll);
},
//修改全部产品的状态
async updateAllCartChecked(event) {
let isChecked = event.target.checked ? "1" : "0";
// console.log(checked);
try {
await this.$store.dispatch("updateAllCartChecked", isChecked);
this.getData();
} catch (error) {
console.log(error.message);
}
},
十二、总结
- UUID
- 修改产品的数量
- 删除某一产品的接口
- 某一个产品的勾选状态切换
- 防抖 节流 复习
- 存储