电商前台项目(五):完成加入购物车功能和购物车页面

news2024/12/26 11:27:25

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.购物车商品数量(难点)

未完待续……

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring-相关概念入门

Spring-相关概念&入门 2&#xff0c;Spring相关概念 2.1 初识Spring 在这一节&#xff0c;主要通过以下两个点来了解下Spring: 2.1.1 Spring家族 官网&#xff1a;https://spring.io&#xff0c;从官网我们可以大概了解到&#xff1a; Spring能做什么:用以开发web、微服…

六、附近商户,连续签到,UV统计

文章目录附近商户GEO的基本用法导入店铺数据到GEO实现附近商户功能签到BitMap的基本用法实现签到功能实现连续签到统计功能补充&#xff1a;Java中>>和>>>的区别UV统计HyperLogLog的基本用法测试百万数据的统计官方命令文档&#xff1a;https://redis.io/comman…

OpenGLES(一)——介绍

一、OpenGL介绍 OpenGL&#xff08;全写Open Graphics Library&#xff09;是指定义了一个跨编程语言、跨平台的编程接口规格的专业的图形程序接口。它用于三维图像&#xff08;二维的亦可&#xff09;&#xff0c;是一个功能强大&#xff0c;调用方便的底层图形库。     O…

六、创建Gitee仓库和提交代码

1、创建仓库 1.1、创建远程仓库 (1)登录Gitee.com&#xff0c;点击右上角 号&#xff0c;再点击新建仓库。 (2)填写仓库名称&#xff0c;设置公开(一般指开源项目)或者私有&#xff0c;其他默认(也可以根据自己需要选择) (3)这里要勾选设置模板&#xff0c;Readme文件。(如果…

Java I/O 流详解(Basic I/O)

目录 1、Java Basic I/O 中的字节流&#xff1a;Byte Streams 2、Java Basic I/O 中的字符流&#xff1a;Character Streams 3、Java Basic I/O 中的缓冲流&#xff1a;Buffered Streams 4、Java Basic I/O 中的打印流&#xff1a;PrintStream &#xff08;数据扫描和格式化…

网易二面:CPU狂飙900%,该怎么处理?

说在前面 社群一位小伙伴面试了 网易&#xff0c;遇到了一个 性能类的面试题&#xff1a; CPU飙升900%&#xff0c;该怎么处理&#xff1f; 可惜的是&#xff0c;以上的问题&#xff0c;这个小伙没有回答理想。 最终&#xff0c;导致他网易之路&#xff0c;终止在二面&…

【蓝桥杯】Python字符串处理和应用

前言&#xff1a; 本文侧重于通过实战训练来提高字符串的处理能力&#xff0c;可以先行学习一下我之前的文章&#xff1a;蓝桥杯Python快速入门&#xff08;4&#xff09; &#xff0c;学习完基础知识再来刷题才会事半功倍&#xff01; 字符串处理 # 字符串切片 str1"1…

优秀码农选择对象详细指南,看完记得要实战噢

2023年了&#xff0c;你是否已到了法定年纪&#xff0c;那么这一篇优秀码农选择对象的详细指南&#xff0c;你一定用得到&#xff0c;看完记得感谢狗哥哦&#xff01; 目录 一、对于婚姻先来思考这么几条 1. 太快决定结婚&#xff1f; 2. 一方或双方急于结婚&#xff1f; 3.…

【go语言入门教程】——1. go语言介绍及安装

目 录1. go 语言简介2. go 语言安装2.1 下载安装包2.2 安装 go2.3 验证安装结果3. 使用 VS Code 运行 go 程序1. go 语言简介 go的产生 go 是一个开源的编程语言&#xff0c;它能让构造简单、可靠且高效的软件变得容易。 go 是从 2007 年末由 Robert Griesemer, Rob Pike, Ken…

Linux系统常见问题总结(持续更新)

目录一&#xff0c;vim安装与设置1&#xff0c;安装2&#xff0c;配置二&#xff0c;Found a swap file by the name三&#xff0c;docker启动失败&#xff1a;Job for docker.service failed because the control process exited with error四&#xff0c;docker-compose安装r…

JavaScript基础知识点整理(一)——运算符比较、闭包、深浅拷贝、原型、

内容主要涉及JavaScript&#xff1a;运算符比较、闭包、深浅拷贝、原型。 JavaScript闭包、原型、深浅拷贝1、 和 的差异2、JavaScript闭包3、JavaScript深浅拷贝3.1、浅拷贝3.2、深拷贝4、JavaScript原型1、 和 的差异 对于而言&#xff0c;若对比双方的类型不一样的话&…

MicroBlaze系列教程(2):AXI_INTC的使用

文章目录 @[TOC]AXI_INTC简介常用函数使用示例参考资料工程下载本文是Xilinx MicroBlaze系列教程的第1篇文章。 AXI_INTC简介 AXI_INTC中断控制器用于将多路中断信号按照优先级输出一路给处理器,支持AXI4-Lite总线,最多支持32个中断输入,中断输入可配置为边沿触发或电平触发…

【vuex】一.vue.js的基本指令;二.vue.js全家桶开发;三.vuex

目录 一.vue.js的基本指令 1.Vue的作用&#xff1a;快速的构建前端页面&#xff08;封装了html、css、js&#xff09;&#xff0c;以工程化的方式进行前端的开发 2.Vue的核心&#xff1a; &#xff08;1&#xff09;组件化&#xff1a; &#xff08;2&#xff09;数据的双…

shell执行脚本的方法及环境变量

执行脚本的方法 &#xff08;1&#xff09;bash ./filename.sh&#xff08;产生子进程&#xff0c;再运行&#xff0c;使用当前指定的bash shell去运行&#xff09; &#xff08;2&#xff09;./filename.sh&#xff08;产生子进程&#xff0c;再运行&#xff0c;使用脚本里面指…

vue3 手撕日历控件

vue制作日历控件 效果如下&#xff1a; <template><div class"cal_con" style"margin-left:200px"><div class"cal_header"><!-- 顶部左侧 --><div class"cal_header_left"><div class"cal_he…

STL分析(十 hash、function type_traits、cout、move )

hash function 假定存在一个Customer类 class Customer{ public:string fname, lname;int no; };其哈希函数存在三种方式 //方式一&#xff1a;创建可调用类型 class CustomerHash { public:std::size_t operator()(const Customer& c) const{return ......} };unordere…

Android 签名基础知识

目录Android 为什么要签名keystore的生成&#xff1a;keystore信息的查看参数说明&#xff1a;Android 怎么签名使用 jarsigner 签名如何查找 jdk 位置签名时遇到的问题查看 apk 是否签名查看 Apk 的MD5值以及MD5不显示时的解决办法缺少 xx.RSA 文件的原因V1 vs V2Android 为什…

Coolify系列-解决局域网主机突然连不了虚拟机

开始之前&#xff0c;我们需要确保配置一切正常&#xff0c;原始配置参考下文 Coolify系列-手把手教学解决局域网局域网中的其他主机访问虚拟机以及docker服务 如果是之前已经配置好的&#xff0c;突然无法访问了&#xff0c;采取以下方式进行排查操作 在虚拟机执行 ifconf…

【GD32F427开发板试用】基于蓝牙的远程步进电机控制

本篇文章来自极术社区与兆易创新组织的GD32F427开发板评测活动&#xff0c;更多开发板试用活动请关注极术社区网站。作者&#xff1a;寒冰1988 一、前言 接上篇文章【GD32F427开发板试用】基于蓝牙模块的远程点灯演示&#xff0c;本篇是第二篇&#xff0c;基于调通的蓝牙模块添…

Pytest-Allure测试报告

Allure 模块下载 pip install allure-pytest包下载 https://repo.maven.apache.org/maven2/io/qameta/allure/allure-commandline/此处我选择下载最新的&#xff0c;版本上可以选择不是最新的&#xff0c;2.9.0的。下载zip或者tgz后缀格式的都可以。 配置环境变量 找到解压…