Vue2项目前台开发:第五章

  • 一、加入购物车
    • 1.路由跳转前先发请求把商品数据给服务器
      • (1)观察接口文档
      • (2)写接口
      • (3)dispatch调用接口传数据
      • (4)判断服务器是否已经收到商品数据
    • 2.请求成功后进行路由跳转
      • (1)创建路由并配置路由规则
      • (2)路由跳转并传参(练习本地存储)
  • 二、完成购物车页面的业务
    • 1.时间戳生成游客id
    • 2.获取相应的购物车数据
    • 3.计算打勾商品总价
    • 4.全选和商品的打勾联动
      • (1)全选按钮是否选中
      • (2)修改单个产品的选中状态
      • (3)点击全选时所以商品状态跟着切换
    • 5.删除购物车数据
      • (1)删除单个商品
      • (2)删除选中的所有商品
    • 6.购物车商品数量(难点)
      • (1)分析一下要做什么
      • (2)配置请求的方法

一、加入购物车

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,默认是成功的Promisetry {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.购物车商品数量(难点)

(1)分析一下要做什么

这个修改商品数量主要有三个地方,加号、减号、用户输入,那么每改一次实际上都要重新发送请求。这个修改商品数量的接口和加入购物车的接口是一样的。

参数的规则就是传正数代表增加,传负数代表减少,也就是说传的数量skuNum是和原来数量的差值。(盲猜后端写加入购物车时向数据库添加数据默认也是这个规则,只不过初始数量为0,要不然怎么能这俩是一个接口呢?明显改的是一个数据库表)

(2)配置请求的方法

<li class="cart-list-con5">
<!-- ---------------------------------------------------------------- --><!-- 1.减号 --><a href="javascript:void(0)" class="mins" @click="changeSkuNum('sub', good, -1)">-</a><!-- 2.输入任意值 --><input autocomplete="off" type="text" :value="good.skuNum" minnum="1" class="itxt"@blur="changeSkuNum('custom', good, $event.target.value)"><!-- 3.加号 --><a href="javascript:void(0)" class="plus" @click="changeSkuNum('add', good, 1)">+</a>
</li>......<script>changeSkuNum: throttle(async function(type, good, disnum) {console.log(type, good, disnum);switch(type) {//如果是减号,那么不能减到0case 'sub'://有个bug,如果连续点减号,那么可能会变成负数//这是因为连续快速点击,请求还来不及发送,数据没改,所以每次disnum都是-1//解决办法就是节流,给服务器一些缓冲的时间,防止数据不同步出现上述bugif(good.skuNum > 1) {disnum = -1;}else {disnum = 0;}break;case 'add':disnum = 1;break;case 'custom':let checkNum = disnum * 1  //任何非数值字符串乘以数值都是NaNif(isNaN(checkNum) || disnum < 1) {disnum = 0;}else {//避免用户输入小数点,所以转化为整形disnum = parseInt(disnum) - good.skuNum; //需要传的是它们的差值(接口规定)}break;}//发请求,修改购物车商品的数量。try {await this.$store.dispatch('detail/sendShopCarMsg',{skuId: good.skuId, skuNum: disnum})//如果成功,就再次请求数据刷新页面this.getData();}catch(err) {alert('修改数量出现了错误!',err);}},800)},
</script>

这里说复杂也不复杂,三个按钮全部指向一个函数,该函数发送的都是同一个请求,比较麻烦的是你要控制每个按钮发送什么样的数字。

这里用到了节流,其实我感觉不太好,我感觉用防抖比较好,主要思路是点击加减先改变输入框的数字,watch监视改变,最后一次停止点击时再发送请求,这样用户体验更好,但是没搞出来……主要问题是input框值的变化可视化不知道怎么搞,每个li的值不一样,怎么存到data中?难道是加入购物车的时候传过来嘛,针对于这个项目来说,那就太麻烦了

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

  1. 小兔鲜儿电商前台项目(一) —— 项目初始化

    一.创建项目 1. vue-cli 创建项目 打开控制台,输入 vue create 项目名 ,创建项目 选择配置,然后回车 vue版本选择3 路由是否使用history模式?选择 n 如果项目中存在 ...

  2. 订单支付和评论——基于Django框架的天天生鲜电商网站项目系列博客(十五)

    系列文章目录 需求分析--基于Django框架的天天生鲜电商网站项目系列博客(一) 网站框架搭建--基于Django框架的天天生鲜电商网站项目系列博客(二) 用户注册模块--基于Django框架的天天 ...

  3. 网站框架搭建——基于Django框架的天天生鲜电商网站项目系列博客(二)

    系列文章目录 需求分析--基于Django框架的天天生鲜电商网站项目系列博客(一) 网站框架搭建--基于Django框架的天天生鲜电商网站项目系列博客(二) 用户注册模块--基于Django框架的天天 ...

  4. 最新《Laravel5.4电商实战项目-联想商城》

    Laravel5.4电商实战项目-联想商城 官方付费地址:http://edu.51cto.com/course/10500.html 课程目标 本课程学习的目标是利用laravel框架做出一个电商项 ...

  5. [项目实战] 云知梦Laravel5.4电商实战项目VIP视频教程

    laravel电商项目实战,主要教大家如何利用laravel框架做出电商项目 课程目录 第1课时 网站的分析 第2课时 数据库的分析 第3课时 后台页面搭建 第4课时 前台页面搭建和后台无刷新添加 第 ...

  6. estore电商网站项目

    estore电商网站项目 项目背景: 电子商务的发展为网络购物的发展开拓了更广阔的市场.所以,我实现了这个项目,诣在模拟网络购物,从登陆,到页面浏览再到购物车结账的全过程. 使用技术和代码流程: js ...

  7. 01-Flutter移动电商实战-项目学习记录

    01-Flutter移动电商实战-项目学习记录 一直想系统性的学习一下 Flutter,正好看到该课程<Flutter移动电商实战>的百度云资源,共 69 课时,由于怕自己坚持不下去(经常 ...

  8. Django电商网站项目(7)-部署与总结

    将前述4个模块完成后项目的全部功能就完成了,在本地已经可以实现网站的基本功能(从用户注册到订单评论),但开启服务器时使用的是Django自带的测试用服务器,因此仍需要将其部署到真正的可用于工程的服务器 ...

  9. 基于Vue开发的电商APP项目——蘑菇街app

    基于Vue开发的电商APP项目--蘑菇街 项目源码:https://github.com/Limna777/Shopmall.git 1.项目描述 2.使用的插件或第三方库 3.页面主要实现的功能 1 ...

最新文章

  1. ubuntu 安装 python3.6.8
  2. EGO首次线下活动分享:Facebook研发团队的高效秘诀
  3. 台式计算机固定资产属于哪类,电脑属于固定资产的哪一类
  4. 修改附图中文字的办法
  5. 【oracle】除数为0
  6. 操作系统学习笔记 002 安装NASM
  7. hadoop入门-centos7.2安装hadoop2.8
  8. 我什么都没有,只剩下坚持了!!!
  9. 按键精灵手机助手之字符串处理
  10. 刘宇凡:罗永浩的锤子情怀只能拿去喂狗
  11. 可道云kodexplorer修改小记
  12. 使用gpg来加密数据
  13. 使用 Python 和可视化编程控制树莓派机械臂myCobot
  14. Python找最大数及位置
  15. 牛客竞赛小白试炼(20201205 怕npy的牛牛)
  16. Codepage的定义和历史
  17. 脑波和眼动连通性分析 python-(2)
  18. matlab实现一种改进的差分进化算法处理约束优化问题(DyHF)
  19. leetcod--Missing Number
  20. WARNING:not built warning

热门文章

  1. 计算CSV Python中有多少行?
  2. Flex布局 - 仿携程网移动端首页案列练习
  3. 数据库经典基础练习题45道
  4. OKR落地四部曲:vika维格表帮你快速养成OKR思维
  5. win7计算机电源设置在哪里设置,win7系统高级电源管理怎么打开?win7系统设置高级电源管理的方法...
  6. 谷歌翻译停服后,chrome无法自动翻译?解决办法来了~ (最新)
  7. Word插入NoteExpress插件时“运行错误429:ActiveX部件不能创建对象“解决方案
  8. 【PowerDesign】制作数据流图
  9. SAP BOM详细解析
  10. 一文搞懂 神经辐射场(Neural Radiance Fields,NeRF)