Vue2电商平台(五)、加入购物车,购物车页面

Vue2电商平台(五)、加入购物车,购物车页面进入购物车页面时 应将用户的 id 传给后端 后端根据用户 id 检索出该用户的购物车商品信息 传递给前端用于展示及进行其他操作

大家好,欢迎来到IT知识分享网。

一、加入购物车

思路分析:点击加入购物车按钮后,需要进行如下的几个操作
(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> 

解决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')就是在调用这个函数addOrUpdateShopCart try { 
    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) { 
    // 生成用户id uuid_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' }) } 

3. 计算打勾商品总价

 computed: { 
    ...mapState('shopCart', ['cartInfoList']), // 计算总价 totalPrice () { 
    let sum = 0 this.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 或false let isChecked = e.target.checked ? 1 : 0 await 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 : 0 await 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) } } 
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. 处理购物车的产品数量(难)

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 input disNum: 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) 

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/117972.html

(0)
上一篇 2025-11-16 19:20
下一篇 2025-11-16 19:33

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信