Vue2电商平台(五)、加入购物车,购物车页面
文章目录
- 一、加入购物车
- 1. 添加到购物车的接口
- 2. 点击按钮的回调函数
- 3. 请求成功后进行路由跳转
- (1)、创建路由并配置路由规则
- (2)、路由跳转并传参(本地存储)
- 二、购物车页面的业务
- 1. uuid生成用户id
- 2. 获取购物车数据
- 3. 计算打勾商品总价
- 4. 全选与商品打勾
- (1)、商品全部打勾,自动勾全选(every方法)
- (2)、打勾单个商品,更新数据
- (3)、点击全选框,对每个商品的单选框进行操作
- 5. 删除购物车
- (1)、删除单个商品
- (2)、删除选中的商品
- 6. 处理购物车的产品数量(难)
一、加入购物车
思路分析:点击加入购物车按钮
后,需要进行如下的几个操作
(1)、向服务器发送请求,将产品信息(产品Id,购买的产品数量)发给服务器进行存储。
(2)、服务器存储成功,进行路由跳转,跳转到加入购物车成功的界面。
(3)、服务器存储失败,给用户提示。
1. 添加到购物车的接口
写发送请求的接口:api/index.js
// 添加到购物车(对已有物品进行数量改动) url: /api/cart/addToCart/{ skuId }/{ skuNum } 请求方式post
export const reqAddOrUpdateShopCart = (skuId, skuNum) => {return requests({ url: `/cart/addToCart/${skuId}/${skuNum} `, method: 'post' })
}
2. 点击按钮的回调函数
<!-- 加入购物车 --><div class="add"><a href="javascript:" @click="addShopCar">加入购物车</a></div><script>addShopCar () {// 1. 派发action请求,将数据给服务器进行存储this.$store.dispatch('detail/addOrUpdateShopCart', { skuId: this.$route.params.goodsId, skuNum: this.skuNum })// 2. 服务器存储成功---进行路由跳转并携带参数 // 3. 服务器存储失败----给用户提示}</script>
这里需要注意的是
(1)、这里发请求不需要服务器返回数据,所以拿到服务器的返回结果时并不需要存储。服务器的返回结果只会告知请求是发送成功or发送失败。
(2)、问题: 请求的返回结果在仓库的actions里,组件中如何拿到这个返回结果以判断是请求成功还是请求失败。
解决:async
函数返回的是一个Promise对象,组件中派发请求相当于调用了这个async
函数,应该得到一个Promise对象。那我们就在这个async
函数里判断请求成功或失败来实例化一个Promise对象,返回给组件。
// detail小仓库里:async addOrUpdateShopCart (context, { skuId, skuNum }) {let result = await reqAddOrUpdateShopCart(skuId, skuNum)if (result.code == 200) {// 加入购物车成功return 'ok'} else {// 加入购物车失败return Promise.reject(new Error('faile'))}}
回调函数中接收到这个
addShopCar () {// 1. 派发action请求,将数据给服务器进行存储:this.$store.dispatch('detail/addOrUpdateShopCart')就是在调用这个函数addOrUpdateShopCarttry {this.$store.dispatch('detail/addOrUpdateShopCart', { skuId: this.$route.params.goodsId, skuNum: this.skuNum })// 2. 服务器存储成功---进行路由跳转并携带参数this.$router.push({ name: 'addCartSuccess', })} catch (error) {// 3. 存储失败----给用户提示alert(error.message)}
}
3. 请求成功后进行路由跳转
(1)、创建路由并配置路由规则
1. 创建路由
将路由组件添加到pages
文件夹
2. 配置路由规则
(2)、路由跳转并传参(本地存储)
如果路由跳转的时候不携带产品信息参数,则需要在跳转页面之后根据产品Id再次发送请求。这样做没必要。直接携带就好了。但是之前路由跳转传参都是简单的数字之类的。这里的产品信息(产品名称,产品属性)涉及到对象,还要传递购买的产品数量。
二、购物车页面的业务
1. uuid生成用户id
进入购物车页面时,应将用户的id传给后端,后端根据用户id检索出该用户的购物车商品信息,传递给前端用于展示及进行其他操作。在这个系统中,我们用uuid
库生成一个用户Id,在向后台发送请求时传递给后端。
1、创建文件utils/uuid_token.js
:
需要注意:一个用户只能有一个Id,所以这里采用本地存储来存储uuid生成的用户id。每次先读取本地存储中是否有用户id,有的话直接用,没有就生成一个新的。
// 用于生成用户的临时身份Id
import { v4 as uuidv4 } from 'uuid'
/*
- 先检查本地存储中有没有这个用户Id,
- 有就直接返回,
- 没有则生成新的
*/
export const getUUID = () => {let uuid_token = localStorage.getItem('UUID_TOKEN')// 如果没有if (!uuid_token) {// 生成用户iduuid_token = uuidv4();// 存储到localStorage.setItem('UUID_TOKEN', uuid_token)}return uuid_token
}
2、在仓库里调用函数,得到userTempId
.
3、将userTempId
配置在请求头里
查看文档里的接口会发现:请求中并没有让携带参数,所以除了通过参数能将数据传递给后台,还能用什么方式呢?答:请求头。可以将用户Id放在请求头中。
2. 获取购物车数据
1、写接口
api/index.js
// 获取购物车列表 url: /api/cart/cartList 请求方式 get
export const reqShopCart = () => {return requests({ url: '/cart/cartList ', method: 'get' })
}
2、vuex三连环
3、组件发请求
因为在购物车页面,其他的操作也会重新向服务器发请求,所以这里将放请求的操作包装成一个函数。
根据获取到的数据渲染界面
3. 计算打勾商品总价
computed: {...mapState('shopCart', ['cartInfoList']),// 计算总价totalPrice () {let sum = 0this.cartInfoList.forEach((el) => {if (el.isChecked === 1) { // 商品是否被选中sum += (el.skuPrice * el.skuNum)}})return sum},
}
4. 全选与商品打勾
(1)、商品全部打勾,自动勾全选(every方法)
<div class="select-all"><input class="chooseAll" type="checkbox" :checked="isAllChecked"/><span>全选</span>
</div>
<script>
computed:{isAllChecked () {// 统计checked的数量,是否等于数组长度let flag = this.cartInfoList.every((item) => {return item.isChecked == 1})return flag}
}
</script>
关于every方法,回顾博客:
(2)、打勾单个商品,更新数据
选中某个商品后,商品的isChecked
属性发生变化,需要将更新后的商品信息发给后台保存。(我感觉这个业务在实际中没必要,每次勾选或取消勾选都发送请求,服务器压力增大,没必要。但是这里是为了练习一些知识点)。
接口
// 切换商品选中状态 url: /api/cart/checkCart/{skuID}/{isChecked} 请求方式 get
export const changeIsChecked = (skuID, isChecked) => {return requests({ url: `/cart/checkCart/${skuID}/${isChecked}`, method: 'get' })
}
仓库Vuex:
// 改变商品选中的状态async changeChecked (context, { skuID, isChecked }) {let res = await changeIsChecked(skuID, isChecked)if (res.code === 200) {return 'ok'} else {return Promise.reject(new Error('faile'))}},
组件:
<!-- 选择框 --><li class="cart-list-con1"><input type="checkbox" name="chk_list" :checked="good.isChecked == 1"@change="handleChange(good.skuId, $event)" /></li><script>// 切换商品选中的状态async handleChange (skuId, e) {try {// 接口中的参数是1或0,不是true 或falselet isChecked = e.target.checked ? 1 : 0await this.$store.dispatch('shopCart/changeChecked', { skuID: skuId, isChecked: isChecked })this.getCartData()// 成功重新发请求} catch (e) {alert(e.message)}}</script>
(3)、点击全选框,对每个商品的单选框进行操作
由于没有修改多个商品的接口,所以思路是多次调用打勾单个商品的接口,把每个商品的勾选状态改为当前全选框的状态。
此处需要注意在actions
里,如何通过dispatch
调用其他actions
的函数
// 改变商品选中的状态async changeChecked (context, { skuID, isChecked }) {let res = await changeIsChecked(skuID, isChecked)if (res.code === 200) {return 'ok'} else {return Promise.reject(new Error('faile'))}},// 改变所有商品的状态// 第一个参数是context,包含:state,dispatch,getters登,通过解构将需要的解构出来async changeAllState ({ dispatch, state }, stateFlag) {let promiseAll = []state.cartInfoList.forEach(el => {if (el.isChecked !== stateFlag) {// 通过dispatch调用其他的actions函数let promise = dispatch('changeChecked', { skuID: el.skuId, isChecked: stateFlag })promiseAll.push(promise)}});return Promise.all(promiseAll)},
给组件添加的全选点击事件
<div class="select-all">
<!--&& cartInfoList.length > 0 是考虑到处于当商品全部被删除时,全选框不应该处于选中状态--><input class="chooseAll" type="checkbox":checked="isAllChecked && cartInfoList.length > 0"@change="handleAllChange" /><span>全选</span>
</div>
<script>async handleAllChange (e) {try {// e.target.checked获取全选框的值,let stateFlag = e.target.checked ? 1 : 0await this.$store.dispatch('shopCart/changeAllState', stateFlag)// 成功则重新发送请求this.getCartData()} catch (error) {alert(error.message);}}
</script>
5. 删除购物车
文档接口:
(1)、删除单个商品
接口:
// 删除购物车商品 url:/api/cart/deleteCart/{skuId} 请求方式:delete
export const reqDeleteGoodById = (skuId) => {return requests({ url: `/cart/deleteCart/${skuId}`, method: 'delete' })
}
Vuex (这里也不需要三连环)
// 删除商品async deleteOneGood (context, skuId) {let res = await reqDeleteGoodById(skuId)if (res.code === 200) {return 'ok'} else {return Promise.reject(new Error('failed'))}}
组件:
<a class="sindelet" @click="deleteOneGood(good.skuId)">删除</a>
<script>// 删除一个商品async deleteOneGood (skuId) {try {await this.$store.dispatch('shopCart/deleteOneGood', skuId)this.getCartData() // 重新发送请求 } catch (error) {alert(error.message)}}
</script>
注意这里有一个bug!
(2)、删除选中的商品
没有删除多个商品的接口,所以思路是多次调用删除单个商品的接口,将选中的商品全都删除。
组件:
// 删除已选中的商品;async deleteAllChecked () {try {await this.$store.dispatch('shopCart/deleteAllSelected')// 重新发请求获取购物车列表this.getCartData()} catch (error) {alert(error.message)}}
重点是Vuex中:
如果有一个删除失败,则这个操作就是失败了。采用Promise.all()
方法给组件反馈成功或失败
actions:{// 删除全部商品deleteAllSelected ({ dispatch, state }) {let promiseAll = []// 获取购物车的全部商品state.cartInfoList.forEach((el) => {// 如果被该商品被勾选if (el.isChecked === 1) {let promise = dispatch('deleteOneGood', el.skuId)// 将每一次返回的Promise添加到数组当中promiseAll.push(promise)}})//Promise.all([p1,p2,p3]) p1,p2,p3都是Promise对象,其中有一个失败则全失败return Promise.all(promiseAll)}
}
6. 处理购物车的产品数量(难)
这里用到的接口之前写过,注意参数的描述!
根据这个文档描述,猜测这个skuNum
的值是商品数量的变化量。
这个接口可以用在两个地方,添加到购物车和对已有物品进行数量改动。在添加到购物车的场景中,skuNum
的值一直都是正的值。
HTML结构:
<!--修改产品数量 --><li class="cart-list-con5"><!-----减号------><a class="mins" @click="handler('sub', -1, good)">-</a><!-----文本框输入,注意事件是失去焦点事件blur。$event.target.value * 1 是因为 非数值字符串*1 的值为NaN ------><input autocomplete="off"type="text":value="good.skuNum"minnum="1"class="itxt"@blur="handler('input', $event.target.value * 1, good)"/><!----加号------><a class="plus" @click="handler('add', 1, good)">+</a></li>
回调函数:
/*type:操作的类型 add sub inputdisNum: 1,-1,输入框的最终数good:产品*/async handler (type, disNum, good) {// 判断不同的情况switch (type) {case 'add': // 加disNum = 1;break;case 'sub': // 减if (good.skuNum > 1) {disNum = -1} else {disNum = 0}break;case 'input':if (isNaN(disNum) || disNum < 0) {disNum = 0} else {// 增量disNum = parseInt(disNum) - good.skuNum}break;}// 发送请求 try {await this.$store.dispatch('detail/addOrUpdateShopCart', { skuId: good.skuId, skuNum: disNum })this.getCartData() // 请求成功,重新获取数据} catch (e) {alert(e.message)}}
但是这样做,有个小bug。当用户频繁点击-
是,数量可能会出现负值
这是因为连续快速点击,请求还来不及发送,数据没改,所以每次disnum都是-1。解决办法就是节流,给服务器一些缓冲的时间,防止数据不同步出现上述bug。
// 按需引入
import throttle from 'lodash/throttle'
handler: throttle(async function (type, disNum, good) {// 判断不同的情况switch (type) { ... }try {await this.$store.dispatch('detail/addOrUpdateShopCart', { skuId: good.skuId, skuNum: disNum })this.getCartData()} catch (e) {alert(e.message)}
}, 800)
最后分析一下,这个功能为什么要设计的这么麻烦,
(1) 为什么每+
或-
或输入一次都要发送一次请求,
(2) 为什么不能直接把输入框的商品最终数量传给后端,还要计算差值。
答:
(1) 每+
或-
或输入一次都是对数据进行了修改。购物车的商品数量改变后,刷新页面时,数量应该保持修改后的样子。所以每次操作完都要保存一下数据。
(2) 为什么要计算差值?因为这个接口还用来实现将商品添加到购物车。
我用淘宝试了一下,这个业务逻辑是当在页面详情页,多次重复的将商品添加到购物车时。购物车里该商品的数量应该是这几次重复添加的数量的和。而不是最后一次添加时选择的商品数量。
所以如果只将输入框里的最终数量传过去的话,商品数量只会是最后一次将该商品添加到购物车里时的选择的数量。
相关文章:

Vue2电商平台(五)、加入购物车,购物车页面
文章目录 一、加入购物车1. 添加到购物车的接口2. 点击按钮的回调函数3. 请求成功后进行路由跳转(1)、创建路由并配置路由规则(2)、路由跳转并传参(本地存储) 二、购物车页面的业务1. uuid生成用户id2. 获取购物车数据3. 计算打勾商品总价4. 全选与商品打勾(1)、商品全部打勾&a…...

众数信科 AI智能体政务服务解决方案——寻知智能笔录系统
政务服务解决方案 寻知智能笔录方案 融合民警口供录入与笔录生成需求 2分钟内生成笔录并提醒错漏 助办案人员二次询问 提升笔录质量和效率 寻知智能笔录系统 众数信科AI智能体 产品亮点 分析、理解行业知识和校验规则 AI实时提醒用户文书需注意部分 全文校验格式、内容…...

Redis篇(面试题 - 连环16炮)(持续更新迭代)
目录 目录 目录 (第一炮)一、Redis?常用数据结构? 1. 项目里面到了Redis,为什么选用Redis? 2. Redis 是什么? 3. Redis和关系型数据库的本质区别有哪些? 4. Redis 的线程模型…...

selenium元素定位
find_element和find_elements 元素定位有两个表达式,分别为find_element()和find_elements(),它们的不同点如下: find_element():找出的为单个元素,若有多个元素为同一表达式,则默认定位第一个元素&#…...

美畅物联丨视频汇聚从“设”开始:海康威视摄像机设置详解
在运用畅联云平台进行视频汇聚与监控管理时,海康威视的安防摄像机凭借其卓越的性能与广泛的应用兼容性,成为了众多用户的首选产品。海康威视摄像机参数设置与调试对于实现高效的安防监控至关重要。今天,让我们一同深入学习海康摄像机的参数设…...

聊天机器人羲和的代码04
进一步完善和优化聊天机器人GUI,使其更加丰富和美观,采取了以下措施: 添加图标:为应用程序添加一个图标。 调整布局:进一步优化布局,使其更加美观。 增加样式:使用更多的样式和主题来提升视觉效果。 添加动画:增加加载动画以提高用户体验。 优化控件:使用更现代的控件…...

Linux安装配置Jupyter Lab并开机自启
文章目录 1、安装配置jupyter lab首先需要使用pip3安装:生成配置文件和密码: 2、设置开机自启首先通过which jupyter查询到可执行文件路径:设置自启服务: 1、安装配置jupyter lab 首先需要使用pip3安装: pip3 instal…...

Java基础——`UUID.randomUUID()` 方法详细介绍
这里写自定义目录标题 UUID.randomUUID() 方法详细介绍1. 概述2. UUID 的结构与格式UUID 的 128 位结构划分: 3. UUID.randomUUID() 方法详解3.1 方法签名3.2 使用示例3.3 生成原理3.4 随机数生成的范围3.5 随机字符的取值范围 4. UUID 的版本与特性4.1 UUID 版本 4…...

前端面试常见手写代码题【详细篇】
文章目录 前言:防抖节流函数柯里化函数组合instanceof 实现实现new操作符的行为深拷贝继承实现:手写Promise数组中常见函数的实现 前言: 在前端面试中,经常会遇到要求手写的代码的题目,主要是考察我们的编程能力、和对…...

当代最厉害的哲学家改名大师颜廷利:北京、上海、广州和深圳房价精准预测
在2024年国庆节期间,北京、上海、广州和深圳的房地产市场异常活跃。作为山东济南籍的国际易学权威颜廷利教授,连续收到了这些大城市客户的感谢信和电话。 来自北京的王先生在信中写道:“非常感谢颜廷利教授这几年来对我们的鼓励和支持。在经历…...

MySQL常用指令码
本文精心挑选了一系列MySQL指令码,助你提升资料库效率、解决常见问题,让你的资料储存体验更加高效、可靠。 常用功能指令码 1.汇出整个资料库 mysqldump - u 使用者名称- p – default - character - set latin1 资料库名>汇出的档名(资料库预设编…...

OpenHarmony(鸿蒙南向开发)——轻量系统内核(LiteOS-M)【扩展组件】
往期知识点记录: 鸿蒙(HarmonyOS)应用层开发(北向)知识点汇总 鸿蒙(OpenHarmony)南向开发保姆级知识点汇总~ 持续更新中…… C支持 基本概念 C作为目前使用最广泛的编程语言之一,…...

官方ROM 免费下载! 王者归来! 华为秘盒media Q M310(续)
最近在捣鼓电视盒子, 前帖讨论了如何拯救华为华为秘盒media Q M310, 详情请点击这里! https://blog.csdn.net/weixin_62598385/article/details/142658048 CSDN上有精简版的M310 ROM下载, 但是我点不进去, 要收年费&am…...

【Docker】05-Docker部署前端项目
1. nginx.conf worker_processes 1;events {worker_connections 1024; }http {include mime.types;default_type application/json;sendfile on;keepalive_timeout 65;server {listen 18080;# 指定前端项目所在的位置location / {root /usr/share/nginx…...

SQL进阶技巧:如何优化NULL值引发的数据倾斜问题?
目录 0 场景描述 1 问题分析 1.1 问题剖析 1.2 解决方案 2 小结 0 场景描述 实际业务中有些大量的null值或者一些无意义的数据参与到计算作业中,表中有大量的null值,如果表之间进行join操作,就会有shuffle产生,这样所有的null值都会被分配到一个reduce中,必然产生数…...

【09】纯血鸿蒙HarmonyOS NEXT星河版开发0基础学习笔记-Class类基础全解(属性、方法、继承复用、判断)
序言: 本文详细讲解了关于我们在程序设计中所用到的class类的各种参数及语法。 笔者也是跟着B站黑马的课程一步步学习,学习的过程中添加部分自己的想法整理为笔记分享出来,如有代码错误或笔误,欢迎指正。 B站黑马的课程链接&am…...

快速提升波段交易技能:4种实用策略分享
每个交易员的交易偏好是各不相同的,有人偏爱短线交易的迅速反应,有人钟情于中长线的稳健布局,还有人则热衷于波段交易的灵活操作。我们经常探讨短线与中长线的策略,但你了解波段交易的策略吗? 波段交易是什么…...

LeetCode 11 Container with Most Water 解题思路和python代码
题目: You are given an integer array height of length n. There are n vertical lines drawn such that the two endpoints of the ith line are (i, 0) and (i, height[i]). Find two lines that together with the x-axis form a container, such that the co…...

【深度学习】损失函数
损失函数(Loss Function)是机器学习和深度学习模型中的一个核心概念,它用于衡量模型的预测输出与真实标签之间的差异。通过优化(最小化)损失函数,模型可以不断调整其内部参数,提升预测性能。不同…...

力扣 中等 46.全排列
文章目录 题目介绍题解 题目介绍 题解 代码如下: class Solution {List<List<Integer>> res new ArrayList<>();// 存放符合条件结果的集合List<Integer> path new ArrayList<>();// 用来存放符合条件结果boolean[] used; // 标记…...

LabVIEW机床加工监控系统
随着制造业的快速发展,机床加工的效率与稳定性成为企业核心竞争力的关键。传统的机床监控方式存在效率低、无法远程监控的问题。为了解决这些问题,开发了一种基于LabVIEW的机床加工监控系统,通过实时监控机床状态,改进生产流程&am…...

第五届智能设计国际会议(ICID 2024)
文章目录 一、会议详情二、重要信息三、大会介绍四、出席嘉宾五、征稿主题六、咨询 一、会议详情 二、重要信息 大会官网:https://ais.cn/u/vEbMBz提交检索:EI Compendex、IEEE Xplore、Scopus大会时间:2024年10月25-27日大会地点࿱…...

厨房用品分割系统源码&数据集分享
厨房用品分割系统源码&数据集分享 [yolov8-seg-C2f-DCNV3&yolov8-seg-AFPN-P345等50全套改进创新点发刊_一键训练教程_Web前端展示] 1.研究背景与意义 项目参考ILSVRC ImageNet Large Scale Visual Recognition Challenge 项目来源AAAI Global Al ln…...

【HTTPS】深入解析 https
我的主页:2的n次方_ 1. 背景介绍 在使用 http 协议的时候是不安全的,可能会出现运营商劫持等安全问题,运营商通过劫持 http 流量,篡改返回的网页内容,例如广告业务,可能会通过 Referer 字段 来统计是…...

Axios 快速入门
什么是Ajax Ajax 是一种通过 JavaScript 发送异步请求的技术,它的核心是使用 XMLHttpRequest 对象来与服务器交换数据。这种方式较为繁琐,因为需要手动处理请求状态和响应,并且编写的代码往往比较冗长。 相较之下,Axios 是一个基于…...

LabVIEW提高开发效率技巧----调度器设计模式
在LabVIEW开发中,针对多任务并行的需求,使用调度器设计模式(Scheduler Pattern)可以有效地管理多个任务,确保它们根据优先级或时间间隔合理执行。这种模式在需要多任务并发执行时特别有用,尤其是在实时系统…...

python之认识变量
1、变量 1.1、定义 字面意思来看,会发生改变的量称为变量。 相反的,如果有一个不会发生改变的量,它应该称为不变量,即常量。 1.2、引入变量的原因 主要是为了方便程序员动态的管理、操控数据。 1.3、变量的三要素 名称 类型…...

c++应用网络编程之十Linux下的Poll模式
一、Poll模式 在上一篇文章中提到了Select模式的缺点。既然有缺点,就要改正。但是直接在Select模式上修改不太现实,那么就推出一个新的模式不更香么?poll模式就应运而生了。不过,罗马不是一天建成的,poll模式也只是对…...

[C++][第三方库][RabbitMq]详细讲解
目录 1.介绍2.安装1.RabbitMq2.客户端库 3.AMQP-CPP 简单使用1.介绍2.使用 4.类与接口1.Channel2.ev 5.使用1.publish.cc2.consume.cc3.makefile 1.介绍 RabbitMQ:消息队列组件,实现两个客户端主机之间消息传输的功能(发布&订阅)核心概念࿱…...

Next.js 详解
Next.js是一个基于React的开源JavaScript框架,由Vercel(原Zeit)公司开发。它旨在简化React应用的构建过程,并提供了一系列强大的功能来优化性能和开发体验。以下是对Next.js的详细解析: 一、核心特性 服务器端渲染&…...