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);
    }
    //切记有返回值,没有返回值undefined
    return 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======>假false
        return this.cartInfoList.every(item=> item.isChecked == 1)
      }
    }

注意点1:

问题:查询购物车信息的接口方法放在哪里?是放在“去购物车结算”按钮还是放在“购物车”页面加载完毕中定义?

答案:放在“购物车”页面加载完毕中定义,只要购物车页面一初始化,甭管哪个入口跳转进来的都能进行批量查询购物车数据并动态展示。

注意点2:购物车左下角的全选按钮判断是否勾选,推荐适用every而不是foreach方法

isAllChecked() {
        //遍历数组里面原理,只要全部元素isChecked属性都为1===>真 true
        //只要有一个不是1======>假false
        return 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 {
          //派发action
          await 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 {
          //派发action
          await 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 {
          //派发action
          await 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 {
          //派发action
          await 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";
          //派发action
          await 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 {
          //成功
          //派发一个action
          await 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 {
          //成功
          //派发一个action
          let 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.加入购物车操作(难点)】

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/7866.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

203. 移除链表元素

1、题目 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,6,3,4,5,6], val 6 输出&#xff1a;[1,2,3,4,5]示例 2&#xff1a; 输入&…

File 类的用法和 InputStream, OutputStream,System 类的用法

&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了 博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳,欢迎大佬指点!人生格言&#xff1a;当你的才华撑不起你的野心的时候,你就应该静下心来学习! 欢迎志同道合的朋友一起加油喔&#x1f9be;&am…

Typescript学习笔记(一)

什么是TypeScript? TypeScript 是添加了类型系统的 JavaScript&#xff0c;适用于任何规模的项目。 TypeScript 是一门静态类型、弱类型的语言。 安装TypeScript npm install -g typescript编译 tsc hello.tsTypeScript 只会在编译时对类型进行静态检查&#xff0c;如果发…

iOS 内存管理机制与原理

内存分区 内存一般分为五大区&#xff1a;栈区、堆区、常量区、全局区、代码区。如图 1.栈区 是由编译器自动分配并释放的&#xff0c;主要用来存储局部变量、函数的参数等&#xff0c;是一块连续的内存区域&#xff0c;遵循先进后出&#xff08;FILO&#xff09;原则。一般在…

WebAssembly 助力云原生:APISIX 如何借助 Wasm 插件实现扩展功能?

本文将介绍 Wasm&#xff0c;以及 Apache APISIX 如何实现 Wasm 功能。 作者朱欣欣&#xff0c;API7.ai 技术工程师 原文链接 什么是 Wasm Wasm 是 WebAssembly 的缩写。WebAssembly/Wasm 是一个基于堆栈的虚拟机设计的指令格式。 在 Wasm 未出现之前&#xff0c;浏览器中只能…

Hadoop(伪分布式)+Spark(local模式)搭建Hadoop和Spark组合环境

一、安装Hadoop环境使用Ubuntu 14.04 64位 作为系统环境&#xff08;Ubuntu 12.04&#xff0c;Ubuntu16.04 也行&#xff0c;32位、64位均可&#xff09;&#xff0c;请自行安装系统。Hadoop版本: Hadoop 2.7.4创建hadoop用户如果你安装 Ubuntu 的时候不是用的 "hadoop&qu…

研究的艺术 (The craft of research) 读书笔记

前言 如果你对这篇文章感兴趣&#xff0c;可以点击「【访客必读 - 指引页】一文囊括主页内所有高质量博客」&#xff0c;查看完整博客分类与对应链接。 对于研究者而言&#xff0c;写作是一件很重要的事&#xff0c;好的写作不仅能让更多人愿意读&#xff0c;获得更大影响力&…

Windows系统配置SSH服务

1.安装OpenSSH 打开【设置】-【应用】 选择【管理可选功能】 点击【添加可选功能】 选择【OpenSSH 服务端】&#xff0c;切记不是【OpenSSH 客户端】&#xff08;如果安装一个不行&#xff0c;就都安装&#xff0c;我都安装了可以用&#xff09;&#xff0c;然后点击下载即可 …

ERP系统如何让项目管理更轻松?

项目管理是许多企业的首要任务&#xff0c;通常有一个ERP系统来协助他们。然而&#xff0c;一些企业仍然没有意识到&#xff0c;ERP解决方案可以使他们的项目管理更容易。项目管理需要有一个目标&#xff0c;并在你朝着完成项目前进的过程中控制变量&#xff0c;而ERP系统指导你…

成都北大青鸟怎么样?

对于任何一个培训机构的了解大概的流程是&#xff1a;听说名字——网上搜索&#xff0c;可以看到机构官网&#xff0c;也会看到机构广告&#xff0c;当然也会看到各种有好有坏的评论&#xff0c;到这里会对机构形成初印象&#xff1b;然后如果身边有培训出身的小伙伴会去询问对…

【C语言进阶:自定义类型详解】联合(共用体)

本节重点内容&#xff1a; 联合类型的定义联合的特点联合大小的计算⚡联合类型的定义 联合也是一种特殊的自定义类型这种类型定义的变量也包含一系列的成员&#xff0c;特征是这些成员公用同一块空间&#xff08;所以联合也叫共用体&#xff09;。 为了方便大家理解举个例子…

Java 网络编程之NIO(ByteBuffer)

在 Java NIO 中&#xff0c;ByteBuffer 是用于存储和传输数据的一种数据结构。它提供了高效的数据存储和读取能力&#xff0c;使得 Java NIO 能够高效地处理大量的数据输入输出。 ByteBuffer 的作用包括以下几个方面: 存储数据:ByteBuffer 可以存储任意长度的数据&#xff0c;…

Elasticsearch 8.X 如何基于用户指定 ID 顺序召回数据?

1、实战问题如何根据输入的id 的顺序输出结果&#xff0c;id 个数有500个&#xff0c;还有分页&#xff1f;问题来源&#xff1a;https://t.zsxq.com/0cdyq7tzr2、方案探讨2.1 Elasticsearch 默认排序机制在 Elasticsearch 中&#xff0c;如果未指定排序规则&#xff0c;检索结…

Linux下实现的 HTTP 服务器

项目功能&#xff1a;&#xff08;1&#xff09;能接收客户端的GET请求&#xff1b;&#xff08;2&#xff09;能够解析客户端的请求报文&#xff0c;根据客户端要求找到相应的资源&#xff1b;&#xff08;2&#xff09;能够回复http应答报文&#xff1b;&#xff08;3&#x…

数据结构和算法学习记录——设计循环队列(数组实现循环队列)核心思路、题解过程、完整题解

目录 题目描述 题目示例 核心思路 链表实现 数组实现 重点 题解过程 结构体类型定义 创建一个循环队列并初始化 判断循环队列为空或为满 入队列函数 出队列函数 取队头数据 取队尾数据 销毁循环队列 完整题解 题目来源&#xff1a;力扣 题目描述 设计你的…

Sentinel滑动时间窗限流算法原理及源码解析(下)

文章目录对统计数据如何使用获取之前统计好的数据对统计数据如何使用 流控快速失败 获取之前统计好的数据

SpringBoot 项目的创建与启动

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

iosApplePay

1、Apple Pay 接入工程 - 简书 // 设置币种、国家码及merchant标识符等基本信息 PKPaymentRequest *payRequest [[PKPaymentRequest alloc]init]; payRequest.countryCode "CN"; //国家代码 payRequest.currencyCode "CNY"; //RMB的币种代码 …

“被裁员之前,没得到任何风声,措手不及...” 一个在职6年测试工程师内心独白

前言 一个码农&#xff08;软件测试工程师&#xff09;的自白 小张&#xff1a; 我们用工作五年的积蓄&#xff0c;在这个一线城市买了房子&#xff0c;买了车子&#xff0c;然后领证。我也在6年前进入了一个很多人梦寐以求新的公司 码农的新生活开始了。在这家公司里&…

ChatGPT如何为企业提供帮助?

数字化转型是指利用技术来改变企业的运营方式并为客户提供价值&#xff0c;这不仅仅是关于如何采用新的技术或工具。要想取得成功&#xff0c;就需要从根本上改变文化和心态。 ChatGPT如何为企业提供帮助?从数据分析到知识管理再到客户服务等等&#xff0c;人工智能聊天机器人…