Vue2项目前台开发:第五章
- 一、加入购物车
- 1.路由跳转前先发请求把商品数据给服务器
- (1)观察接口文档
- (2)写接口
- (3)dispatch调用接口传数据
- (4)判断服务器是否已经收到商品数据
- 2.请求成功后进行路由跳转
- (1)创建路由并配置路由规则
- (2)路由跳转并传参(练习本地存储)
- 二、完成购物车页面的业务
- 1.时间戳生成游客id
- 2.获取相应的购物车数据
- 3.计算打勾商品总价
- 4.全选和商品的打勾联动
- (1)全选按钮是否选中
- (2)修改单个产品的选中状态
- (3)点击全选时所以商品状态跟着切换
- 5.删除购物车数据
- (1)删除单个商品
- (2)删除选中的所有商品
- 6.购物车商品数量(难点)
一、加入购物车
1.路由跳转前先发请求把商品数据给服务器
(1)观察接口文档
这里其实只需要把已有物品的id和数量传给后台,但是后台不需要返回数据
(2)写接口
点击加入购物车之后要把商品的id和数量传给服务器(数据库的增操作)
src/api/index.js
6.添加到购物车的接口(对已有物品进行数量改动)
地址:/api/cart/addToCart/{ skuId }/{ skuNum } post 参数skuId和skuNum
这个请求只需要给服务器发数据,不需要返回什么东西
export const reqShopCarMsg = (skuId, skuNum) => {
return requests.post(`/cart/addToCart/${skuId}/${skuNum}`);
}
(3)dispatch调用接口传数据
1、给加入购物车按钮添加点击事件
2、派送actions,然后把数据以对象的形式传过去,第一个键值对是商品id(当从Search到Detail路由跳转时就传过来的params参数),第二个键值对是购物车数量(我们已经存到了Detail组件的data里)
3、actions这边通过解构赋值,调用接口并把数据传给服务器(后端数据库)
actions: {
.....
加入购物车
异步请求,把商品id和数量发送给服务器,参数解构赋值并自己起了个名
async sendShopCarMsg(context, { skuId: id, skuNum: num }) {
let result = await reqShopCarMsg(id, num);
//这里只是把购物车数据给服务器,但是服务器不需要返回什么东西
//所以这里我们不用再三连环了,通过dispatch把数据给服务器就已经欧了
console.log(result);
}
},
(4)判断服务器是否已经收到商品数据
这里要判断请求是否已经成功,也就是服务器是否收到了要加入购物车商品的数据,若成功了就要进行路由跳转并传参,若失败了就要给用户提示。
dispatch
就会调用这个actions
里的函数,调用这个async函数返回一个promise
对象,这个Promise对象的状态和结果值取决于这个async函数的返回值。
1、如果返回一个
非Promise
对象,那么就是成功,值就是返回值;
2、如果返回Promise对象,那么状态和结果值取决于该Promise
3、如果不写返回值,且await后是失败的Promise,那么就会抛出异常,既然抛出异常,那么async函数返回的就是一个失败的Promise。
4、如果不写返回值,且await后是成功的Promise,那么就会返回undefined
,async函数返回的就是一个成功的Promise,值是undefined
。
如果忘了,去复习Promise:关于Promise的使用和源码
actions: {
....
调用async函数返回一个promise对象
//加入购物车
//异步请求,把商品id和数量发送给服务器,参数解构赋值并自己起名
async sendShopCarMsg(context, { skuId: id, skuNum: num }) {
//其实这里不用try-catch,因为那边已经try了,不写return默认返回undefined(成功的Primise)
//如果await后成功则返回undefined(没写return,返回成功的Promise)
//如果失败则抛出异常(返回失败的Promise)
let result = await reqShopCarMsg(id, num);
//这里只是把购物车数据给服务器,但是服务器不需要返回什么东西
//所以这里我们不用再三连环了,通过dispatch把数据给服务器就已经欧了
console.log('zzy',result);
}
},
那么这样的话其实就可以使用try-catch来捕获这个返回结果,大概的逻辑是这样的:
//函数3:点击加入购物车把商品数据传给后台服务器的数据库
async sendDataToSql() {
//1.发请求,把购物车的商品id和数量传给数据库
try {
//2.请求成功,服务器已经存储了数据了,那么就要路由跳转并传参
await this.$store.dispatch('detail/sendShopCarMsg', { skuId: this.$route.params.skuId, skuNum: this.shopCarNum })
//路由跳转并传参
// this.$router.push({
// name:''
// })
} catch (error) {
//3.发送数据(请求)失败,给用户进行提示
console.log('请求失败了baby', error.message);
}
}
2.请求成功后进行路由跳转
路由组件通常存放在
pages
文件夹,一般组件通常存放在components
文件夹。、
(1)创建路由并配置路由规则
1、所以我们先创建路由
2、配置路由规则src/router/routes.js
(2)路由跳转并传参(练习本地存储)
3、写路由跳转和传参的代码
这里要传两个值:skuInfo对象
和商品数量shopCarNum
(其实skuInfo根本不用传,直接去仓库读就行了)。传参的时候最好别用params和query,因为带过去的需要是skuInfo
这个对象,那么对象传过去的话,地址栏可能会是乱码。这里采用的方案是:本地存储(忘了点这先复习)
1、使用query传简单的
商品数量shopCarNum
,因为数字不会乱码
2、使用会话存储(本地存储也行)带skuInfo
过去(其实直接从仓库读就行了;或者你用query也行,无非就是地址栏乱码;这里我们麻烦点,复习复习本地存储)
async sendDataToSql() {
//1.发请求,把购物车的商品id和数量传给数据库
try {
//2.请求成功,服务器已经存储了数据了,那么就要路由跳转并传参
await this.$store.dispatch('detail/sendShopCarMsg', { skuId: this.$route.params.skuId, skuNum: this.shopCarNum })
//路由跳转并传参
this.$router.push({
name: 'jiaruchenggong',
query: { shopCarNum: this.shopCarNum } //这里只带购物数量,skuInfo用会话存储
})
//比较复杂的skuInfo我们用会话存储传过去
sessionStorage.setItem('skuInfo', JSON.stringify(this.skuInfo));
} catch (error) {
//3.发送数据(请求)失败,给用户进行提示
alert('请求失败了baby', error.message);
}
}
本地存储 里面只能存储字符串格式 ,因此需要把对象转换为字符串
JSON.stringify()
获取本地存储数据,需要把里面的字符串转换为对象格式JSON.parse()
我们才能使用里面的数据。
然后把相应的数据放到页面上就欧了
二、完成购物车页面的业务
点击查看商品详情就直接跳回去就行了,数据仓库本来就有不用重新发请求
<router-link class="sui-btn btn-xlarge" to="/detail">查看商品详情</router-link>
接下来是点击去购物车结算,跳到购物车结算页面
把购物车部分的静态搞过来,然后注册一下路由,并写个路由跳转。
<router-link to="/shopcart">去购物车结算 ></router-link>
1.时间戳生成游客id
这里后端应该是写了个逻辑,用一个叫userTempId
的请求头字段来定位你是谁,然后返回给你相应的数据。
一般来说正常的逻辑应该是每个用户有自己的
token
,然后点击加入购物车之后,往用户-商品这个表里添加一行数据;读购物车取数据的时候呢,应该是传用户token参数获取相应的商品列表。而这里为了模拟,后端写好了useTempId
字段,刷新时我们就给他个时间戳(唯一id),它就拿着这个字段直接作为本地浏览器游客,所以请求购物车数据也不用传参。
1、进入页面的时候先随机生成一个时间戳,作为用户的id。
export const getId = () => {
console.log('获取id的函数执行!!!!!')
let userId = localStorage.getItem('userTempId') || '';
if(userId) {
return userId;
} else {
localStorage.setItem('userTempId', Date.now());
}
}
2、请求数据时,在请求拦截器中将该id作为请求中userTempId
的值。
3、这样就欧了,只要本地存储中这个id没有被手动清除,那么每次都可以获取该id的购物车数据
2.获取相应的购物车数据
写接口
//7.获取购物车列表数据的接口
// 地址:/api/cart/cartList GET请求
export const reqShopCartList = () => {
return requests({
url: '/cart/cartList',
method: 'get',
})
}
发请求,这里直接看图,就不啰嗦了
最后把数据展示到页面上,比较简单,但是有几个需要注意的地方,见下文:
3.计算打勾商品总价
这个也好算,利用isChecked属性,只计算选中的价格,forEach循环一下就行了
computed: {
...mapState('shopcart',['cartList']),
totalPrice() {
let totalPrice = 0;
this.cartList.forEach( el => {
if(el.isChecked == 1) {
totalPrice += el.skuNum * el.skuPrice;
}
});
return totalPrice;
}
},
4.全选和商品的打勾联动
(1)全选按钮是否选中
isAllChecked() {
//全选按钮是否选中,取决于每个是否都选中
return this.cartList.every(el => {
return el.isChecked == 1;
})
}
(2)修改单个产品的选中状态
修改单个产品状态需要去发送请求修改isChecked
字段,这是因为总价那里用到了这个字段去计算,我们要实现勾选的计算总价,取消勾选就不计算。
修改产品勾选状态的接口,需要传两个参数
//8.修改产品勾选状态的接口,其实就是拿着id去改isChecked字段
//地址:/api/cart/checkCart/{skuID}/{isChecked} GET
export const changeIsChecked = (skuId, isChecked) => {
return requests.get(`/cart/checkCart/${skuId}/${isChecked}`)
}
vuex调用接口
//2.修改购物车产品的勾选状态
async changeChecked(context, {skuId, state}) {
let result = await changeIsChecked(skuId, state);
console.log(result);
}
每个购物车商品按钮配置一个点击事件(或者切换事件)如果当前勾选状态为1(勾选),那么改成0(取消勾选),反之也一样。这里要注意参数是skuId
,不是id
<ul v-for="good in cartList" :key="good.id" class="cart-list">
<li class="cart-list-con1">
<input type="checkbox" name="chk_list"
@change="changeState(good.skuId, good.isChecked)"
:checked="good.isChecked == 1">
</li>
.....
</ul>
<script>
async changeState(goodId, goodState) {
try {
//点击某个商品勾选框时,就发请求修改它的勾选状态
let stateFlag = goodState == 1 ? 0 : 1;
await this.$store.dispatch('shopcart/changeChecked', {id: goodId, state: stateFlag});
this.getData();
}
catch(err) {
//如果修改失败,那么就提示
alert('修改失败!',err);
}
},
</script>
(3)点击全选时所以商品状态跟着切换
给全选的勾选框添加点击事件。总体来说主要思路就是点击全选时派发请求,这个请求需要把每个商品的勾选状态改成当前全选框的状态。
所以需要在actions
中遍历购物车数据并派发请求,(当然其实在组件中写也一样,就是这样规范点),用try-catch捕获,如果都请求成功,那么就使用Promise.all
获取成功的标志
//2.修改购物车产品的勾选状态
async changeChecked(context, {skuId, state}) {
let result = await changeIsChecked(skuId,state);
console.log('成功',result);
},
//3.点击全选按钮修改其他商品状态
changeAllStates({dispatch, state}, isChecked) {
let promiseArr = [];
state.cartList.forEach( el => {
try {
let promise = dispatch('changeChecked', {skuId: el.skuId, state: isChecked});
promiseArr.push(promise);
} catch(err) {
console.log(`${el.skuNam}修改失败`,err);
}
});
return Promise.all(promiseArr);
}
然后去组件中给全选添加点击事件搞一下就行了,很简单
async selectAllOrNot() {
let stateFlag = this.isAllChecked ? 0 : 1;
try {
let result = await this.$store.dispatch('shopcart/changeAllStates',stateFlag);
console.log(result);//全是undefined,说明那个请求只写了resolve(),没传值
this.getData();
} catch(err) {
console.log('出现错误',err);
}
},
5.删除购物车数据
(1)删除单个商品
这个就比较简单了,不多说了,记得写vuex的时候别忘了第一个参数context
这里要注意,这个项目的接口给的都是params参数,而使用axios发请求,params参数只能拼接在url地址中,而query参数的配置项叫params,很奇怪。
接口
//9.删除购物车商品的接口
// /api/cart/deleteCart/{skuId}
export const reqDeleteGoodById = (skuId) => {
return requests({
url: `/cart/deleteCart/${skuId}`,
method: 'delete',
})
}
vuex
//4.删除购物车某条数据,第一个参数别忘了噢
async deleteOneGood(context,skuId) {
try{
let result = await reqDeleteGoodById(skuId);
console.log('删除成功',result);
}catch(err) {
console.log('错误!!嗷嗷嗷',err)
}
}
组件点击事件
//4.删除某个购物车商品
deleteOneGood(skuId) {
//try-catch捕获一下,如果删除失败,那么就会抛出错误,返回错误的Promise
//如果删除成功,那么没写返回值返回undefined,默认是成功的Promise
try {
this.$store.dispatch('shopcart/deleteOneGood',skuId);
this.getData();
} catch(err) {
console.log('删除失败',err)
}
}
(2)删除选中的所有商品
这个逻辑和点击全选修改每个商品的勾选状态是一样的。
1.vuex中利用遍历购物车数据,查出来哪个是选中的,然后依次发请求删除
删除成功与否的结果利用Promise.all传给组件
//4.删除购物车某条数据,第一个参数别忘了噢
async deleteOneGood(context,skuId) {
try{
let result = await reqDeleteGoodById(skuId);
console.log('删除成功',result);
}catch(err) {
console.log('错误!!嗷嗷嗷',err)
}
},
//5.删除选中的所有商品
deleteAllGoods({dispatch,state}) {
//利用Promise.all来给组件反馈成功或失败
let promiseArr = [];
state.cartList.forEach(el => {
if(el.isChecked == 0) return;
if(el.isChecked == 1) {
let promise = dispatch('deleteOneGood', el.skuId);
promiseArr.push(promise);
}
})
return Promise.all(promiseArr);
}
2.组件里来个方法,点击删除选中商品按钮生效,方法中派发请求
//5.删除选中的所有商品
async deleteAllSelected() {
//Promise.all返回一个Promise,只有全部成功了才是成功
try{
await this.$store.dispatch('shopcart/deleteAllGoods');
this.getData();
}catch(err) {
console.log('删除过程出现了问题',err.message);
}
}
欧了,搞定
6.购物车商品数量(难点)
未完待续……