vue尚品汇商城项目-day04【29.加入购物车操作(难点)】

文章目录
- 29.加入购物车操作(难点)
- 29.1加入购物车按钮
- 29.2addCartSuce
- 29.3购物车
- 29.3.1 向服务器发送ajax请求,获取购物车数据
- 29.3.2UUID临时游客身份
- 29.3.3动态展示购物车
- 29.4修改购物车产品的数量(需要发请求:参数理解)
- 29.5删除某一产品
- 29.6修改产品状态
- 29.7删除全部选中的商品
- 29.8全选操作
- 本人其他相关文章链接
29.加入购物车操作(难点)
重难点说明
-
用户临时ID的处理
-
购物车数据的管理(复杂)
-
不使用v-model监控用户输入
-
async / await / Promise.all() 的使用
思路:
-
加入购物车按钮
-
路由跳转之前发请求
-
成功路由跳转与参数传递
-
失败提示失败信息
-
-
addCartSuce
- 查看详情
- 查看购物车
-
购物车
- 购物车静态组件-需要修改样式结构(删除第三项并调整css让各个项目对齐,比例为:15 35 10 17 10 13)
- 向服务器发送ajax请求,获取购物车数据
- UUID临时游客身份
- 动态展示购物车
-
修改购物车产品的数量(需要发请求:参数理解)
-
删除某一产品
-
修改产品状态
-
删除全部选中的商品
-
全选操作
29.1加入购物车按钮
加入购物车按钮
-
路由跳转之前发请求
-
成功路由跳转与参数传递
-
失败提示失败信息
修改代码:
src/pages/Detail/index.vue
<a href="javascript:" @click="addOrUpdateShopCar">加入购物车</a>methods: {//加入购物车async addOrUpdateShopCar() {//1:在点击加入购物车这个按钮的时候,做的第一件事情,将参数带给服务器(发请求),通知服务器加入购车的产品是谁//this.$store.dispatch('addOrUpdateShopCart'),说白了,它是在调用vuex仓库中的这个addOrUpdateShopCart函数。//2:你需要知道这次请求成功还是失败,如果成功进行路由跳转,如果失败,需要给用户提示try {//成功await this.$store.dispatch("addOrUpdateShopCar", {skuId: this.$route.params.skuId, skuNum: this.skuNum});//3:进行路由跳转//4:在路由跳转的时候还需要将产品的信息带给下一级的路由组件//一些简单的数据skuNum,通过query形式给路由组件传递过去//产品信息的数据【比较复杂:skuInfo】,通过会话存储(不持久化,会话结束数据在消失)//本地存储|会话存储,一般存储的是字符串sessionStorage.setItem("SKUINFO", JSON.stringify(this.skuInfo))this.$router.push({name: "addCartSuccess", query:{skuNum: this.skuNum}})} catch (error) {//失败alert(error.message);}}
}
src/api/index.js
//将产品添加到购物车中(获取更新某一个产品的个数)
// /api/cart/addToCart/{ skuId }/{ skuNum } POST
export const addOrUpdateShopCar = (skuId, skuNum)=>requests({url:`/cart/addToCart/${skuId}/${skuNum}`, method:"post"});
src/store/detail/index.js
import {getGoodsInfo, addOrUpdateShopCar} from "@/api";const actions = {//加入购物车的||修改某一个产品的个数,注意一定要用解构方法解构形参对象,不能写成接收两个形参形式,那样会报错接口调不通async addOrUpdateShopCar(context, {skuId, skuNum}) {//发请求:前端带一些参数给服务器【需要存储这些数据】,存储成功了,没有给返回数据//不需要在三连环(仓库存储数据了)//注意:async函数执行返回的结果一定是一个promise【要么成功,要么失败】let response = await addOrUpdateShopCar(skuId, skuNum);if (response.code == 200) {//返回的是成功的标记return "OK";} else {//返回的是失败的标记return Promise.reject(new Error("fail"))}},
}
注意点1:
问题:加入购物车路由跳转跟普通的路由跳转不一样,不一样在哪里?
答案:因为加入购物车功能得先调接口保存加入购物车的数据,然后再跳转路由。而普通的路由跳转直接跳转并传参就行。
注意点2:
问题:点击购物车函数派发定义在详情组件中,但是加入购物车接口调用在仓库中,如何获取调用函数的结果呢?
答案:第一种方案在vuex的state中保存结果,第二种方案在详情组件中采用async+await修饰派发方法获取调用成功失败结果。
注意点3:
问题:如下代码vuex中执行ajax调用接口返回失败,为啥?
src/api/index.js
//将产品添加到购物车中(获取更新某一个产品的个数)
// /api/cart/addToCart/{ skuId }/{ skuNum } POST
export const addOrUpdateShopCar = (skuId, skuNum)=>requests({url:`/cart/addToCart/${skuId}/${skuNum}`, method:"post"});
src/store/detail/index.js
async addOrUpdateShopCar(context, skuId, skuNum) {let response = await addOrUpdateShopCar(skuId, skuNum);console.log("******response:", response) }
报错如下:

答案:vuex中action定义的函数接收形参书写个数不对。
如果是一个参数可以写成:addOrUpdateShopCar(context, skuId)
但如果是传入多个参数则不能这样写:这是错误写法:addOrUpdateShopCar(context, skuId, skuNum)
正确写法是派发时传入一个对象通过解构方式获取属性值,正确写法:addOrUpdateShopCar(context, {skuId, skuNum})
注意点4:
问题:理解异步函数async和await的用法?
答案:
- async 函数
1)函数的返回值为Promise对象
2)Promise对象的结果由async函数执行的返回值决定 - await 表达式
1)await右侧的表达式一般为promise对象, 但也可以是其它的值
2)如果表达式是promise对象,await就忙起来了,它会阻塞函数后面的代码,等着Promise对象resolve,然后得到resolve的值,作为await表达式的运算结果。
3)如果表达式是其它值, 直接将此值作为await的返回值 async和await基于promise的。使用async的函数将会始终返回一个 promise 对象。这一点很重要,要记住,可能是你遇到容易犯错的地方。- 在使用
await的时候我们只是暂停了函数,而非整段代码。 - async和await是
非阻塞的 - 仍然可以使用
Promise,例如Promise.all(). - 注意
1)await必须写在async函数中, 但async函数中可以没有await
2)如果await的promise失败了, 就会抛出异常, 需要通过try…catch来捕获处理
29.2addCartSuce
重难点说明:区别使用sessionStorage与localStorage
addCartSuce
- 查看详情
- 查看购物车
效果如图

修改代码:
拷贝src/pages/AddCartSuccess文件夹
src/router/routes.js
import AddCartSuccess from '@/pages/AddCartSuccess'
import ShopCart from '@/pages/ShopCart'
{name: 'addCartSuccess',path: '/addCartSuccess',component: AddCartSuccess,meta:{"isShow": true} //自定义元数据属性,判断Footer组件在底部是否显示
},
{name: 'shopCart',path: '/shopCart',component: ShopCart,meta:{"isShow": true} //自定义元数据属性,判断Footer组件在底部是否显示
},
src/pages/AddCartSuccess/index.vue
<img :src="skuInfo.skuDefaultImg">
<p class="title">{{skuInfo.skuName}}</p>
<p class="attr">{{skuInfo.skuDesc}} 数量:{{$route.query.skuNum}}</p>
<router-link class="sui-btn btn-xlarge" :to="`/detail/${skuInfo.id}`">查看商品详情</router-link>
<router-link to="/shopCart">去购物车结算</router-link>computed: {skuInfo() {return JSON.parse(sessionStorage.getItem("SKUINFO"))}
}
注意点1:
问题:点击加入购物车按钮后跳转路由需要传参购物详情参数,采用哪种方式?query?params?
答案:其实都可以,但是最优雅的还是采用query方式,具体效果看如下:
- 首先是采用params方式,地址栏显示的是地址字符串,但组件中控制台打印居然也能正确解析,说明能获取到参数,但是URL看起来一大长串东西还是不优雅,万一后续URL一顿追加参数,岂不是越来越长根本不优雅。

- 而采用query方式,地址栏很清爽,所以推荐采用这种方式传参。

注意点2:
问题:参数传递采用本地存储好还是会话存储好?
答案:推荐采用会话存储,localStorage本地存储会在硬盘保留一份需手动清除,而sessionStorage会话存储随着浏览器窗口关闭就清楚了,使用方便。
注意点3:
问题:无论是本地存储还是会话存储,可以存对象吗?
答案:不能,只能存json字符串,直接存对象会无法解析。
- 存储存对象效果,这东西无法解析:

29.3购物车
购物车
- 购物车静态组件-需要修改样式结构(删除第三项并调整css让各个项目对齐,比例为:15 35 10 17 10 13)
- 向服务器发送ajax请求,获取购物车数据
- UUID临时游客身份
- 动态展示购物车
29.3.1 向服务器发送ajax请求,获取购物车数据
修改代码:
src/api/index.js
//获取购物车列表数据接口
//URL:/api/cart/cartList method:get
export const reqCartList = ()=>requests({url:'/cart/cartList ',method:'get'});
src/store/index.js
import shopCar from "@/store/shopCar"modules:{...shopCar}
src/store/shopCar/index.js
import {reqCartList} from "@/api";//shopCar模块的小仓库
//actions代表一系列动作,可以书写自己的业务逻辑,也可以处理异步
const actions = {async reqCartList({commit}) {let response = await reqCartList();if (response.code == 200) {commit("REQ_CART_LIST", response.data)}},}
//mutations代表维护,操作维护的是state中的数据,且state中数据只能在mutations中处理
const mutations = {REQ_CART_LIST(state, carList) {state.carList = carList},
}
//state代表仓库中的数据
const state = {//购物车列表carList: [],
}
//计算属性
//项目当中getters主要的作用是:简化仓库中的数据(简化数据而生)
//可以把我们将来在组件当中需要用的数据简化一下【将来组件在获取数据的时候就方便了】
const getters = {carList(state) {return state.carList[0] || {};}
}//创建并暴露store
export default {actions,mutations,state,getters
}
src/pages/ShopCart/index.vue
methods: {getData() {this.$store.dispatch('reqCartList')},},
mounted() {this.getData()
}
注意点1:如图,这一列多余,删除第三项并调整css让各个项目对齐,比例为:15 35 10 17 10 13

注意点2:
问题:向服务器发送查询购物车ajax请求,获取购物车数据,为啥返回数据是空呢?
答案:因为你得带UUID的东西,后端不知道你谁,也就不知道把那条数据返回给你了。
29.3.2UUID临时游客身份
安装命令:cnpm install --save uuid
修改代码:
src/utils/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);}//切记有返回值,没有返回值undefinedreturn uuid_token;
}
src/api/axios.js
//在当前模块中引入store
import store from '@/store';
if(store.state.detail.uuid_token){//请求头添加一个字段(userTempId):和后台老师商量好了config.headers.userTempId = store.state.detail.uuid_token;
}
src/store/detail/index.js
const state = {//游客临时身份uuid_token: getUUID()
}
注意点1:
问题:假设现在我有UUID了,那如何传过去呢?接口已经定义好了只有2个参数,UUID咋传过去?
答案:可以放在请求头header中传递过去。
注意点2:
问题:我如何使用UUID第三方插件呢?
答案:登录npm官网https://www.npmjs.com/package/uuid搜索uuid,下方就是使用方式。

注意点3:切记UUID不能在vuex中生成,因为vuex是每次触发就会随机生成,会变动这是不行的,正常应该是一次生成有效期内不改变。所以这个uuidv4()不能放在vuex中,最好放在src目录下新建一个文件夹utils,这个文件夹里面经常放一些常用的功能模块,比如正则、临时身份UUID等等。
注意点4:切记src/utils/uuid_token.js下封装的uuid方法一定要有返回值,否则返回值是underfine无效的。
注意点5:
问题:在src/api/axios.js中现在需要给请求头header添加uuid,但是uuid在vuex仓库中,如何获取呢?
答案:在src/api/axios.js中引入import store from ‘@/store’;,通过store 对象可以获取仓库中的state属性。
29.3.3动态展示购物车
修改代码:
src/pages/ShopCart/index.vue
<ul class="cart-list" v-for="(car, index) in cartInfoList" :key="car.id">
<input type="checkbox" name="chk_list" :checked="car.isChecked == 1">
<div class="item-msg">{{car.skuName}}</div>
<span class="price">{{car.skuPrice}}.00</span>
<input autocomplete="off" type="text" :value="car.skuNum" minnum="1" class="itxt">
<span class="sum">{{car.skuNum * car.skuPrice}}</span>
<i class="summoney">{{carTotalPrice}}</i>import {mapGetters} from "vuex";
data() {return {totalPrice: 0}},computed: {...mapGetters(["carList"]),//购物车数据cartInfoList() {return this.carList.cartInfoList || []},//计算购买产品的总价carTotalPrice() {this.cartInfoList.forEach(item => {this.totalPrice += item.skuNum * item.skuPrice;})return this.totalPrice;},//判断底部复选框是否勾选【全部产品都选中,采勾选】isAllChecked() {//遍历数组里面原理,只要全部元素isChecked属性都为1===>真 true//只要有一个不是1======>假falsereturn this.cartInfoList.every(item=> item.isChecked == 1)}}
注意点1:
问题:查询购物车信息的接口方法放在哪里?是放在“去购物车结算”按钮还是放在“购物车”页面加载完毕中定义?
答案:放在“购物车”页面加载完毕中定义,只要购物车页面一初始化,甭管哪个入口跳转进来的都能进行批量查询购物车数据并动态展示。
注意点2:购物车左下角的全选按钮判断是否勾选,推荐适用every而不是foreach方法
isAllChecked() {//遍历数组里面原理,只要全部元素isChecked属性都为1===>真 true//只要有一个不是1======>假falsereturn this.cartInfoList.every(item=> item.isChecked == 1)
}
29.4修改购物车产品的数量(需要发请求:参数理解)
修改代码:
src/pages/ShopCart/index.vue
<li class="cart-list-con5"><a href="javascript:void(0)" class="mins" @click="handle('minus', -1, car)">-</a><input autocomplete="off" type="text" :value="car.skuNum" minnum="1" class="itxt" @change="handle('change', $event.target.value * 1, car)"><a href="javascript:void(0)" class="plus" @click="handle('add', 1, car)">+</a>
</li>//引入lodash:是把lodash全部封装好的函数全都引入进来了
//按需引入:只是引入节流函数,其他的函数没有引入(模块),这样做的好处是,当你打包项目的时候体积会小一些
import throttle from "lodash/throttle";
methods: {//修改某一个产品的个数[节流]handle: throttle(async function(flag, disNum, car) {//type:为了区分这三个元素//disNum形参:+ 变化量(1) -变化量(-1) input最终的个数(并不是变化量)//cart:哪一个产品【身上有id】//向服务器发请求,修改数量switch (flag) {case "add"://加号disNum = 1;break;case "minus"://判断产品的个数大于1,才可以传递给服务器-1//如果出现产品的个数小于等于1,传递给服务器个数0(原封不动)disNum = car.skuNum > 1 ? -1 : 0;break;case "change":/*用户输入进来的最终量,如果非法的(带有汉字|出现负数),带给服务器数字零* 问题:为啥是disNum < 1,而不是disNum <= 1* 答案:哪怕输入1也会走else里面的代码,不影响功能,所以无需加“=”等于号*/if (isNaN(disNum) || disNum < 1) {disNum = 0;} else {//属于正常情况(小数:取证),带给服务器变化的量 用户输入进来的 - 产品的起始个数disNum = parseInt(disNum) - car.skuNum;}break;}try {//派发actionawait this.$store.dispatch('addOrUpdateShopCar', {skuId: car.skuId, skuNum: disNum})//再一次获取服务器最新的数据进行展示this.getData();} catch (error) {//失败alert(error.message);}}, 500),
}
注意点1:如图,购物车中的【+、-、输入值】都应该触发修改购物车接口,且三个函数是同一个函数

注意点2:
问题:如何区分是点击了加号、减号、还是输入值呢?
答案:通过传参字符串flag标识名称进行区分。
注意点3:
问题:实际触发修改购物车接口的传值要注意。
答案:点击“+”号应传值(1),点击“-”号应传值(-1),点击输入值应传值(输入值-原始值)。
注意点4:输入框中事件一定要写成@change,而不是@click,否则效果不对。
注意点5:
问题:对于“minus”情况,为啥是disNum < 1,而不是disNum <= 1
答案:哪怕输入1也会走else里面的代码,不影响功能,所以无需加“=”等于号
注意点6:点击减号“-”要加判断,不能让值小于1。
case "minus"://判断产品的个数大于1,才可以传递给服务器-1//如果出现产品的个数小于等于1,传递给服务器个数0(原封不动)disNum = car.skuNum > 1 ? -1 : 0;break;
注意点7:对于“change”情况,也要加判断
case "change":/*用户输入进来的最终量,如果非法的(带有汉字|出现负数),带给服务器数字零* 问题:为啥是disNum < 1,而不是disNum <= 1* 答案:哪怕输入1也会走else里面的代码,不影响功能,所以无需加“=”等于号*/ if (isNaN(disNum) || disNum < 1) {disNum = 0;} else {//属于正常情况(小数:取证),带给服务器变化的量 用户输入进来的 - 产品的起始个数disNum = parseInt(disNum) - car.skuNum;}break;
注意点8:
问题:连续点击,居然能出现负数,而慢慢点击就不会出问题,为什么?

答案:因为没有加“节流”效果,由于点击过快就会出现这种错误现象。
旧代码:
//修改某一个产品的个数[节流]async handle(flag, disNum, car) {//type:为了区分这三个元素//disNum形参:+ 变化量(1) -变化量(-1) input最终的个数(并不是变化量)//cart:哪一个产品【身上有id】//向服务器发请求,修改数量switch (flag) {case "add"://加号disNum = 1;break;case "minus"://判断产品的个数大于1,才可以传递给服务器-1//如果出现产品的个数小于等于1,传递给服务器个数0(原封不动)disNum = car.skuNum > 1 ? -1 : 0;break;case "change":/*用户输入进来的最终量,如果非法的(带有汉字|出现负数),带给服务器数字零* 问题:为啥是disNum < 1,而不是disNum <= 1* 答案:哪怕输入1也会走else里面的代码,不影响功能,所以无需加“=”等于号*/if (isNaN(disNum) || disNum < 1) {disNum = 0;} else {//属于正常情况(小数:取证),带给服务器变化的量 用户输入进来的 - 产品的起始个数disNum = parseInt(disNum) - car.skuNum;}break;}try {//派发actionawait this.$store.dispatch('addOrUpdateShopCar', {skuId: car.skuId, skuNum: disNum})//再一次获取服务器最新的数据进行展示this.getData();} catch (error) {//失败alert(error.message);}},
修改后代码
//引入lodash:是把lodash全部封装好的函数全都引入进来了
//按需引入:只是引入节流函数,其他的函数没有引入(模块),这样做的好处是,当你打包项目的时候体积会小一些
import throttle from "lodash/throttle";//修改某一个产品的个数[节流]handle: throttle(async function(flag, disNum, car) {//type:为了区分这三个元素//disNum形参:+ 变化量(1) -变化量(-1) input最终的个数(并不是变化量)//cart:哪一个产品【身上有id】//向服务器发请求,修改数量switch (flag) {case "add"://加号disNum = 1;break;case "minus"://判断产品的个数大于1,才可以传递给服务器-1//如果出现产品的个数小于等于1,传递给服务器个数0(原封不动)disNum = car.skuNum > 1 ? -1 : 0;break;case "change":/*用户输入进来的最终量,如果非法的(带有汉字|出现负数),带给服务器数字零* 问题:为啥是disNum < 1,而不是disNum <= 1* 答案:哪怕输入1也会走else里面的代码,不影响功能,所以无需加“=”等于号*/if (isNaN(disNum) || disNum < 1) {disNum = 0;} else {//属于正常情况(小数:取证),带给服务器变化的量 用户输入进来的 - 产品的起始个数disNum = parseInt(disNum) - car.skuNum;}break;}try {//派发actionawait this.$store.dispatch('addOrUpdateShopCar', {skuId: car.skuId, skuNum: disNum})//再一次获取服务器最新的数据进行展示this.getData();} catch (error) {//失败alert(error.message);}}, 500),
29.5删除某一产品
修改代码:
app/src/api/index.js
//删除购物产品的接口
//URL:/api/cart/deleteCart/{skuId} method:DELETE
export const reqDeleteCartById = (skuId)=>requests({url:`/cart/deleteCart/${skuId}`,method:'delete'});
src/store/shopCar/index.js
import {reqDeleteCartById} from "@/api";const actions = {//删除购物车某一个产品async reqDeleteCartById({commit}, skuId) {let response = await reqDeleteCartById(skuId);if (response.code == 200) {//返回的是成功的标记return "OK";} else {//返回的是失败的标记return Promise.reject(new Error("fail"))}}
}
src/pages/ShopCart/index.vue
<a class="sindelet" @click="reqDeleteCartById(car.skuId)">删除</a>//删除某一个产品的操作
async reqDeleteCartById(skuId) {try {//派发actionawait this.$store.dispatch('reqDeleteCartById', skuId)this.getData();} catch (error) {alert(error.message)}
}
29.6修改产品状态
修改代码:
app/src/api/index.js
//修改商品的选中状态
//URL:/api/cart/checkCart/{skuId}/{isChecked} method:get
export const reqUpdateCheckedByid = (skuId,isChecked)=>requests({url:`/cart/checkCart/${skuId}/${isChecked}`,method:'get'});
src/store/shopCar/index.js
import {reqUpdateCheckedByid} from "@/api";const actions = {//修改购物车某一个产品的选中状态async reqUpdateCheckedByid({commit}, {skuId,isChecked}) {let response = await reqUpdateCheckedByid(skuId,isChecked);if (response.code == 200) {//返回的是成功的标记return "OK";} else {//返回的是失败的标记return Promise.reject(new Error("fail"))}},
}
src/pages/ShopCart/index.vue
<input type="checkbox" name="chk_list" :checked="car.isChecked == 1" @change="reqUpdateCheckedByid(car.skuId, $event)">//修改某个产品的勾选状态async reqUpdateCheckedByid(skuId, event) {try {//如果修改数据成功,再次获取服务器数据(购物车)let isChecked = event.target.checked ? "1" : "0";//派发actionawait this.$store.dispatch('reqUpdateCheckedByid', {skuId: skuId, isChecked})this.getData();} catch (error) {alert(error.message)}},
29.7删除全部选中的商品
修改代码:
src/pages/ShopCart/index.vue
<a @click="deleteAllCheckedCar">删除选中的商品</a>//删除全部选中的产品
//这个回调函数咱门没办法手机到一些有用数据
async deleteAllCheckedCar() {try {//成功//派发一个actionawait this.$store.dispatch('deleteAllCheckedCar')//再发请求获取购物车列表this.getData()} catch (error) {//失败alert(error.message)}
}
src/store/shopCar/index.js
//删除全部勾选的产品
async deleteAllCheckedCar({dispatch, getters}) {//context:小仓库,commit【提交mutations修改state】 getters【计算属性】 dispatch【派发action】 state【当前仓库数据】//获取购物车中全部的产品(是一个数组)let primoseAll = [];getters.carList.cartInfoList.forEach(item => {let promise = item.isChecked == 1 ? dispatch('reqDeleteCartById', item.skuId) : '';//将每一次返回的Promise添加到数组当中primoseAll.push(promise);})//只要全部的p1|p2....都成功,返回结果即为成功//如果有一个失败,返回即为失败结果return Promise.all(primoseAll);}
注意点1:接口API未提供选中删除的功能,只提供了单独删除的,所以可以采用批量循环删除的方式。
注意点2:dispatch派发不仅可以存在于vue中使用,也可以在vuex仓库中使用
注意点3:vuex仓库中的context包含很多东西,包括【commit、dispatch、getters、state…】,具体如图

注意点4:Promise.all用法,all里面的是个数组,如果其中的子项promise有一个失败,返回即为失败结果。具体用法如下:
let primoseAll = [];
let promise = item.isChecked == 1 ? dispatch('reqDeleteCartById', item.skuId) : '';
//将每一次返回的Promise添加到数组当中
primoseAll.push(promise);
//只要全部的p1|p2....都成功,返回结果即为成功
//如果有一个失败,返回即为失败结果
return Promise.all(primoseAll);
29.8全选操作
修改代码:
src/pages/ShopCart/index.vue
<input class="chooseAll" type="checkbox" :checked="isAllChecked && cartInfoList.length > 0" @click="updateAllCheckedCar($event.target.checked)">//修改全部产品的选中状态
async updateAllCheckedCar(checked) {try {//成功//派发一个actionlet isChecked = checked ? "1" : "0";await this.$store.dispatch('updateAllCheckedCar', isChecked)//再发请求获取购物车列表this.getData()} catch (error) {//失败alert(error.message)}
},
src/store/shopCar/index.js
//修改全部产品的状态
async updateAllCheckedCar({dispatch, getters}, isChecked) {//context:小仓库,commit【提交mutations修改state】 getters【计算属性】 dispatch【派发action】 state【当前仓库数据】//获取购物车中全部的产品(是一个数组)let primoseAll = [];getters.carList.cartInfoList.forEach(item => {let promise = dispatch('reqUpdateCheckedByid', {skuId:item.skuId, isChecked});//将每一次返回的Promise添加到数组当中primoseAll.push(promise);})//只要全部的p1|p2....都成功,返回结果即为成功//如果有一个失败,返回即为失败结果return Promise.all(primoseAll);}
注意点1:全选或全不选后的正确效果如图

注意点2:
问题:全部删除后,左下角的勾选按钮居然还是选中状态,需要优化
答案:在后面多补充个cartInfoList.length > 0判断条件即可
:checked="isAllChecked && cartInfoList.length > 0
本人其他相关文章链接
1.vue尚品汇商城项目-day04【24.点击搜索按钮跳转后的页面商品列表、平台售卖属性动态展示(开发Search组件)】
2.vue尚品汇商城项目-day04【25.面包屑处理关键字】
3.vue尚品汇商城项目-day04【26.排序操作(难点)】
4.vue尚品汇商城项目-day04【27.分页器静态组件(难点)】
5.vue尚品汇商城项目-day04【28.详情页面Detail】
6.vue尚品汇商城项目-day04【29.加入购物车操作(难点)】
相关文章:
vue尚品汇商城项目-day04【29.加入购物车操作(难点)】
文章目录29.加入购物车操作(难点)29.1加入购物车按钮29.2addCartSuce29.3购物车29.3.1 向服务器发送ajax请求,获取购物车数据29.3.2UUID临时游客身份29.3.3动态展示购物车29.4修改购物车产品的数量(需要发请求:参数理解…...
KubeSphere 社区双周报 | 4.8 深圳站 Meetup 火热报名中 | 2023.3.17-3.30
KubeSphere 社区双周报主要整理展示新增的贡献者名单和证书、新增的讲师证书以及两周内提交过 commit 的贡献者,并对近期重要的 PR 进行解析,同时还包含了线上/线下活动和布道推广等一系列社区动态。 本次双周报涵盖时间为:2023.03.17-2023.…...
ChatGPT热炒之前 搜索引擎SEO算法已经悄然改变
2022年4月起,某度算法有了新的调整,这对于靠SEO获得流量的公司简直可以说是灭顶之灾。原本SEO从业者还指望跟之前一样,等算法调整稳定后,网站的自然排名还会再回来,但等到了10月份,仍然没有回暖的迹象&…...
【Linux】Mysql之视图的基本操作
一、什么是视图 MySQL 视图(View)是一种虚拟存在的表,同真实表一样,视图也由列和行构成, 但视图并不实际存在于数据库中。行和列的数据来自于定义视图的查询中所使用的 表,并且还是在使用视图时动态生成的。…...
《扬帆优配》西藏地震!美史上最严排放新规将出台,美股收涨
当地时间周四,美股遍及收高,科技股领涨。因耶稣受难日,美股4月7日(周五)休市,周四为美股本周最终一个买卖日,从本周状况来看,纳指与标普500指数均录得跌幅,别离跌1.1%和0…...
Python 小型项目大全 66~70
六十六、简单替换密码 原文:http://inventwithpython.com/bigbookpython/project66.html 简单替换密码用一个字母代替另一个字母。由于字母A有 26 种可能的替换,B有 25 种可能的替换,C有 24 种可能的替换,等等,所以可能…...
Barra模型因子的构建及应用系列八之Earning_Yeild因子
一、摘要 在前期的Barra模型系列文章中,我们构建了Size因子、Beta因子、Momentum因子、Residual Volatility因子、NonLinear Size因子、Book-to-Price因子和Liquidity因子,并分别创建了对应的单因子策略,其中Size因子和NonLinear Siz因子具有…...
2022蓝桥杯省赛——卡片
问题描述 小蓝有 k 种卡片, 一个班有 n 位同学, 小蓝给每位同学发了两张卡片, 一位同学的两张卡片可能是同一种, 也可能是不同种, 两张卡片没有顺序。没有两位同学的卡片都是一样的。 给定 n, 请问小蓝的卡片至少有多少种? 输入格式 输入一行包含一个正整数表示 n 。 输出…...
数据结构-快速排序
一.概要 快速排序是一种基于分治思想的排序算法,其基本思路是选取一个基准值(pivot),通过一趟排序将待排序列分成两个部分,其中左半部分都小于基准值,右半部分都大于基准值,然后对左右两部分分…...
WuThreat身份安全云-TVD每日漏洞情报-2023-04-10
漏洞名称:Apple iOS/iPadOS 越界写入 漏洞级别:高危 漏洞编号:CVE-2023-28206 相关涉及:Apple iOS <16.4.0 漏洞状态:在野 参考链接:https://tvd.wuthreat.com/#/listDetail?TVD_IDTVD-2023-08810 漏洞名称:PHPGurukul Bank Locker Management System SQL 注入 漏洞级别:高…...
IDEA中查看源码点击Download Sources时出现Cannot download sources的问题复现及解决
IDEA中查看源码点击Download Sources时出现Cannot download sources的问题复现及解决 注意:实验环境的IDEA版本:2021.3.1 1、问题描述 1.1、当想看源码时,点击Download Sources 1.2、此时出现了Cannot download sources 2、解决办法 2.1、…...
R+VIC模型融合实践技术应用及未来气候变化模型预测/SWAT/HSPF/HEC-HMS
在气候变化问题日益严重的今天,水文模型在防洪规划,未来预测等方面发挥着不可替代的重要作用。目前,无论是工程实践或是科学研究中都存在很多著名的水文模型如SWAT/HSPF/HEC-HMS等。虽然,这些软件有各自的优点;但是&am…...
Python 02 数据类型(04元组)
一、元组 元组和列表的唯一不同:不能直接对元组的元素进行修改,删除,添加。 不能修改 1.1 创建元组 1.1.1 创建一个空元组 touple1() # ‘() 里面没有元素,表示为空元组 1.1.2 元组可以容纳任意数据类型的数据的有序集合&…...
WMS:入库库作业流程状态定位
系列文章目录 例如:第一章 WMS:入库库作业流程状态定位 目录 系列文章目录 文章目录 前言 一、入库订单作业状态 二、入库任务级作业状态 1.收货作业 2.验收作业 总结 前言 WMS系统在仓储作业的管理中发挥着至关重要的作用,其核心优势在于强大…...
蓝易云:Linux系统【Centos7】如何配置完整的CC攻击防护策略
完整的CC攻击防护策略包括以下步骤: 1. 调整内核参数 在CentOS 7系统中,可以通过修改内核参数来增加系统对CC攻击的抵抗力。具体操作如下: (1)打开sysctl.conf文件: vim /etc/sysctl.conf (…...
编解码持续升级,「硬」实力铸就视频云最优解
算力时代,视频云需要怎样的 CPU? 在数据爆发式增长及算法日益精进的大背景下,属于「算力」的时代俨然到来。随着视频成为互联网流量的主角,日趋饱和的音视频场景渗透率、人类对“感官之限”的追求与突破、更多元化的场景探索及技术…...
贵金属技术分析的止损保护
前面说过我们这些小散户,最多也不过十几万或者几万美金的账户,没有必要想国际的一些大基金那样,又锁仓,又对冲什么的,我们资金小的投资者,足够灵活,自然有我们存活的方法。所以我们要注意发挥我…...
Python 进阶指南(编程轻松进阶):三、使用 Black 工具来格式化代码
原文:http://inventwithpython.com/beyond/chapter3.html 代码格式化是将一组规则应用于源代码,从而使得代码风格能够简洁统一。虽然代码格式对解析程序的计算机来说不重要,但代码格式对于可读性是至关重要的,这是维护代码所必需的…...
计算机应用辅导大纲及真题
00019考试 湖北省高等教育自学考试实践(技能)课程大纲 课程名称:计算机应用基础(实践) 课程代码:00019 实践能力的培养目标。 计算机应用基础(实践)是高等教育自学考试多…...
【Go基础】一篇文章带你全面了解学习—切片
目录 1、切片注意点 2、声明切片 3、切片初始化 4、切片的内存布局...
基于Uniapp开发HarmonyOS 5.0旅游应用技术实践
一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架,支持"一次开发,多端部署",可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务,为旅游应用带来…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...
P3 QT项目----记事本(3.8)
3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...
C++中string流知识详解和示例
一、概览与类体系 C 提供三种基于内存字符串的流,定义在 <sstream> 中: std::istringstream:输入流,从已有字符串中读取并解析。std::ostringstream:输出流,向内部缓冲区写入内容,最终取…...
【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)
骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术,它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton):由层级结构的骨头组成,类似于人体骨骼蒙皮 (Mesh Skinning):将模型网格顶点绑定到骨骼上,使骨骼移动…...
UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)
UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中,UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化…...
06 Deep learning神经网络编程基础 激活函数 --吴恩达
深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...
Linux --进程控制
本文从以下五个方面来初步认识进程控制: 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程,创建出来的进程就是子进程,原来的进程为父进程。…...
数学建模-滑翔伞伞翼面积的设计,运动状态计算和优化 !
我们考虑滑翔伞的伞翼面积设计问题以及运动状态描述。滑翔伞的性能主要取决于伞翼面积、气动特性以及飞行员的重量。我们的目标是建立数学模型来描述滑翔伞的运动状态,并优化伞翼面积的设计。 一、问题分析 滑翔伞在飞行过程中受到重力、升力和阻力的作用。升力和阻力与伞翼面…...
