当前位置: 首页 > news >正文

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.加入购物车操作(难点)

重难点说明

  1. 用户临时ID的处理

  2. 购物车数据的管理(复杂)

  3. 不使用v-model监控用户输入

  4. async / await / Promise.all() 的使用

思路:

  1. 加入购物车按钮

    • 路由跳转之前发请求

    • 成功路由跳转与参数传递

    • 失败提示失败信息

  2. addCartSuce

    • 查看详情
    • 查看购物车
  3. 购物车

    • 购物车静态组件-需要修改样式结构(删除第三项并调整css让各个项目对齐,比例为:15 35 10 17 10 13)
    • 向服务器发送ajax请求,获取购物车数据
    • UUID临时游客身份
    • 动态展示购物车
  4. 修改购物车产品的数量(需要发请求:参数理解)

  5. 删除某一产品

  6. 修改产品状态

  7. 删除全部选中的商品

  8. 全选操作

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的用法?

答案:

  1. async 函数
    1)函数的返回值为Promise对象
    2)Promise对象的结果由async函数执行的返回值决定
  2. await 表达式
    1)await右侧的表达式一般为promise对象, 但也可以是其它的值
    2)如果表达式是promise对象,await就忙起来了,它会阻塞函数后面的代码,等着Promise对象resolve,然后得到resolve的值,作为await表达式的运算结果。
    3)如果表达式是其它值, 直接将此值作为await的返回值
  3. asyncawait基于promise的。使用async的函数将会始终返回一个 promise 对象。这一点很重要,要记住,可能是你遇到容易犯错的地方。
  4. 在使用await的时候我们只是暂停了函数,而非整段代码。
  5. async和await是非阻塞的
  6. 仍然可以使用 Promise,例如Promise.all().
  7. 注意
    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.加入购物车操作&#xff08;难点&#xff09;29.1加入购物车按钮29.2addCartSuce29.3购物车29.3.1 向服务器发送ajax请求&#xff0c;获取购物车数据29.3.2UUID临时游客身份29.3.3动态展示购物车29.4修改购物车产品的数量&#xff08;需要发请求&#xff1a;参数理解…...

KubeSphere 社区双周报 | 4.8 深圳站 Meetup 火热报名中 | 2023.3.17-3.30

KubeSphere 社区双周报主要整理展示新增的贡献者名单和证书、新增的讲师证书以及两周内提交过 commit 的贡献者&#xff0c;并对近期重要的 PR 进行解析&#xff0c;同时还包含了线上/线下活动和布道推广等一系列社区动态。 本次双周报涵盖时间为&#xff1a;2023.03.17-2023.…...

ChatGPT热炒之前 搜索引擎SEO算法已经悄然改变

2022年4月起&#xff0c;某度算法有了新的调整&#xff0c;这对于靠SEO获得流量的公司简直可以说是灭顶之灾。原本SEO从业者还指望跟之前一样&#xff0c;等算法调整稳定后&#xff0c;网站的自然排名还会再回来&#xff0c;但等到了10月份&#xff0c;仍然没有回暖的迹象&…...

【Linux】Mysql之视图的基本操作

一、什么是视图 MySQL 视图&#xff08;View&#xff09;是一种虚拟存在的表&#xff0c;同真实表一样&#xff0c;视图也由列和行构成&#xff0c; 但视图并不实际存在于数据库中。行和列的数据来自于定义视图的查询中所使用的 表&#xff0c;并且还是在使用视图时动态生成的。…...

《扬帆优配》西藏地震!美史上最严排放新规将出台,美股收涨

当地时间周四&#xff0c;美股遍及收高&#xff0c;科技股领涨。因耶稣受难日&#xff0c;美股4月7日&#xff08;周五&#xff09;休市&#xff0c;周四为美股本周最终一个买卖日&#xff0c;从本周状况来看&#xff0c;纳指与标普500指数均录得跌幅&#xff0c;别离跌1.1%和0…...

Python 小型项目大全 66~70

六十六、简单替换密码 原文&#xff1a;http://inventwithpython.com/bigbookpython/project66.html 简单替换密码用一个字母代替另一个字母。由于字母A有 26 种可能的替换&#xff0c;B有 25 种可能的替换&#xff0c;C有 24 种可能的替换&#xff0c;等等&#xff0c;所以可能…...

Barra模型因子的构建及应用系列八之Earning_Yeild因子

一、摘要 在前期的Barra模型系列文章中&#xff0c;我们构建了Size因子、Beta因子、Momentum因子、Residual Volatility因子、NonLinear Size因子、Book-to-Price因子和Liquidity因子&#xff0c;并分别创建了对应的单因子策略&#xff0c;其中Size因子和NonLinear Siz因子具有…...

2022蓝桥杯省赛——卡片

问题描述 小蓝有 k 种卡片, 一个班有 n 位同学, 小蓝给每位同学发了两张卡片, 一位同学的两张卡片可能是同一种, 也可能是不同种, 两张卡片没有顺序。没有两位同学的卡片都是一样的。 给定 n, 请问小蓝的卡片至少有多少种? 输入格式 输入一行包含一个正整数表示 n 。 输出…...

数据结构-快速排序

一.概要 快速排序是一种基于分治思想的排序算法&#xff0c;其基本思路是选取一个基准值&#xff08;pivot&#xff09;&#xff0c;通过一趟排序将待排序列分成两个部分&#xff0c;其中左半部分都小于基准值&#xff0c;右半部分都大于基准值&#xff0c;然后对左右两部分分…...

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的问题复现及解决 注意&#xff1a;实验环境的IDEA版本&#xff1a;2021.3.1 1、问题描述 1.1、当想看源码时&#xff0c;点击Download Sources 1.2、此时出现了Cannot download sources 2、解决办法 2.1、…...

R+VIC模型融合实践技术应用及未来气候变化模型预测/SWAT/HSPF/HEC-HMS

在气候变化问题日益严重的今天&#xff0c;水文模型在防洪规划&#xff0c;未来预测等方面发挥着不可替代的重要作用。目前&#xff0c;无论是工程实践或是科学研究中都存在很多著名的水文模型如SWAT/HSPF/HEC-HMS等。虽然&#xff0c;这些软件有各自的优点&#xff1b;但是&am…...

Python 02 数据类型(04元组)

一、元组 元组和列表的唯一不同&#xff1a;不能直接对元组的元素进行修改&#xff0c;删除&#xff0c;添加。 不能修改 1.1 创建元组 1.1.1 创建一个空元组 touple1() # ‘() 里面没有元素&#xff0c;表示为空元组 1.1.2 元组可以容纳任意数据类型的数据的有序集合&…...

WMS:入库库作业流程状态定位

系列文章目录 例如&#xff1a;第一章 WMS&#xff1a;入库库作业流程状态定位 目录 系列文章目录 文章目录 前言 一、入库订单作业状态 二、入库任务级作业状态 1.收货作业 2.验收作业 总结 前言 WMS系统在仓储作业的管理中发挥着至关重要的作用&#xff0c;其核心优势在于强大…...

蓝易云:Linux系统【Centos7】如何配置完整的CC攻击防护策略

完整的CC攻击防护策略包括以下步骤&#xff1a; 1. 调整内核参数 在CentOS 7系统中&#xff0c;可以通过修改内核参数来增加系统对CC攻击的抵抗力。具体操作如下&#xff1a; &#xff08;1&#xff09;打开sysctl.conf文件&#xff1a; vim /etc/sysctl.conf &#xff08…...

编解码持续升级,「硬」实力铸就视频云最优解

算力时代&#xff0c;视频云需要怎样的 CPU&#xff1f; 在数据爆发式增长及算法日益精进的大背景下&#xff0c;属于「算力」的时代俨然到来。随着视频成为互联网流量的主角&#xff0c;日趋饱和的音视频场景渗透率、人类对“感官之限”的追求与突破、更多元化的场景探索及技术…...

贵金属技术分析的止损保护

前面说过我们这些小散户&#xff0c;最多也不过十几万或者几万美金的账户&#xff0c;没有必要想国际的一些大基金那样&#xff0c;又锁仓&#xff0c;又对冲什么的&#xff0c;我们资金小的投资者&#xff0c;足够灵活&#xff0c;自然有我们存活的方法。所以我们要注意发挥我…...

Python 进阶指南(编程轻松进阶):三、使用 Black 工具来格式化代码

原文&#xff1a;http://inventwithpython.com/beyond/chapter3.html 代码格式化是将一组规则应用于源代码&#xff0c;从而使得代码风格能够简洁统一。虽然代码格式对解析程序的计算机来说不重要&#xff0c;但代码格式对于可读性是至关重要的&#xff0c;这是维护代码所必需的…...

计算机应用辅导大纲及真题

00019考试 湖北省高等教育自学考试实践&#xff08;技能&#xff09;课程大纲 课程名称&#xff1a;计算机应用基础&#xff08;实践&#xff09; 课程代码&#xff1a;00019 实践能力的培养目标。 计算机应用基础&#xff08;实践&#xff09;是高等教育自学考试多…...

【Go基础】一篇文章带你全面了解学习—切片

目录 1、切片注意点 2、声明切片 3、切片初始化 4、切片的内存布局...

企业高效知识体系:8大核心特征+可落地搭建框架,告别知识散乱

对于企业而言&#xff0c;知识从来不是“文件堆”&#xff0c;而是能支撑业务、培养新人、规避风险的核心资产。很多企业陷入“文档满天飞、新人没人带、老员工离职带跑经验”的困境&#xff0c;本质是没有搭建起高效、完整的知识体系。今天就一次性讲透&#xff1a;一个能真正…...

65R125-ASEMI超结MOS管TO-220封装

编辑&#xff1a;LL65R125-ASEMI超结MOS管TO-220封装型号&#xff1a;65R125品牌&#xff1a;ASEMI沟道&#xff1a;NPN封装&#xff1a;TO-220漏源电流&#xff1a;31A漏源电压&#xff1a;650VRDS(on):125mΩ批号&#xff1a;最新引脚数量&#xff1a;3封装尺寸&#xff1a;如…...

BepInEx游戏插件加载器完全指南:从入门到精通Unity游戏扩展工具

BepInEx游戏插件加载器完全指南&#xff1a;从入门到精通Unity游戏扩展工具 【免费下载链接】BepInEx Unity / XNA game patcher and plugin framework 项目地址: https://gitcode.com/GitHub_Trending/be/BepInEx 如何用BepInEx解锁游戏自定义功能&#xff1f;解决玩家…...

Python tkinter文件对话框实战:5分钟搞定文件选择与保存功能(附完整代码)

Python tkinter文件对话框实战&#xff1a;5分钟搞定文件选择与保存功能&#xff08;附完整代码&#xff09; 在开发桌面应用程序时&#xff0c;文件选择功能几乎是必不可少的。无论是需要用户上传文件、保存处理结果&#xff0c;还是选择工作目录&#xff0c;一个直观的文件对…...

把Camunda流程引擎当SaaS用?多租户与外部任务实战指南(基于RuoYi改造)

基于Camunda构建企业级流程中心的架构设计与实战 在数字化转型浪潮中&#xff0c;业务流程自动化已成为企业提升运营效率的核心手段。当一家企业同时运行CRM、OA、ERP等多个业务系统时&#xff0c;每个系统都需要工作流支持&#xff0c;但为每个系统单独部署和维护Camunda引擎显…...

全网资源嗅探下载神器:轻松获取视频音频资源的终极指南

全网资源嗅探下载神器&#xff1a;轻松获取视频音频资源的终极指南 【免费下载链接】res-downloader 资源下载器、网络资源嗅探&#xff0c;支持微信视频号下载、网页抖音无水印下载、网页快手无水印视频下载、酷狗音乐下载等网络资源拦截下载! 项目地址: https://gitcode.co…...

CTFHub—Web题目解题合集1(超详细)

目录一. HTTP协议&#xff08;web前置技能&#xff09;1. 请求方式题解小知识2. 302跳转3. Cookie题目解法二. 信息泄露2.1 备份文件下载1. 网站源码2. bak文件题目题解小知识3. vim缓存题目小知识题解4. DS_Store题目小知识题解2.2 Git泄露1. Log题目小知识(GitHack与dirsearc…...

从0到1:Java+AI入门实战,看完直接上手项目

文章目录前言环境准备&#xff1a;别急着装Python&#xff0c;先把JDK升到21第一滴血&#xff1a;让Java程序说出"人话"进阶玩法&#xff1a;给AI装上"记忆"和"工具"让AI记住你们聊过啥让AI能查数据库、调接口实战项目&#xff1a;搭建私有知识库…...

计算机网络 之 【自定义协议、序列化与反序列化】(C++使用JSON示例)

目录 1.自定义协议与序列化/反序列化 2.Json简介 Json是什么 第三方库提供&#xff0c;使用时包含头文件 JSON 的数据类型 JSON结构示例 C使用JSON示例 1.自定义协议与序列化/反序列化 协议的必要性 协议是通信双方的约定&#xff0c;它定义了数据的格式和含义&#xff…...

Connect to Oracle Database with JDBC Driver

1. Overview The Oracle Database is one of the most popular relational databases. In this tutorial, we’ll learn how to connect to an Oracle Database using a JDBC Driver. 2. The Database To get us started, we need a database. If we don’t have access to …...