07.智慧商城——商品详情页、加入购物车、拦截器封装token

01. 商品详情 - 静态布局

在这里插入图片描述

静态结构 和 样式

<template><div class="prodetail"><van-nav-bar fixed title="商品详情页" left-arrow @click-left="$router.go(-1)" /><van-swipe :autoplay="3000" @change="onChange"><van-swipe-item v-for="(image, index) in images" :key="index"><img :src="image" /></van-swipe-item><template #indicator><div class="custom-indicator">{{ current + 1 }} / {{ images.length }}</div></template></van-swipe><!-- 商品说明 --><div class="info"><div class="title"><div class="price"><span class="now">¥0.01</span><span class="oldprice">¥6699.00</span></div><div class="sellcount">已售1001件</div></div><div class="msg text-ellipsis-2">三星手机 SAMSUNG Galaxy S23 8GB+256GB 超视觉夜拍系统 超清夜景 悠雾紫 5G手机 游戏拍照旗舰机s23</div><div class="service"><div class="left-words"><span><van-icon name="passed" />七天无理由退货</span><span><van-icon name="passed" />48小时发货</span></div><div class="right-icon"><van-icon name="arrow" /></div></div></div><!-- 商品评价 --><div class="comment"><div class="comment-title"><div class="left">商品评价 (5条)</div><div class="right">查看更多 <van-icon name="arrow" /> </div></div><div class="comment-list"><div class="comment-item" v-for="item in 3" :key="item"><div class="top"><img src="http://cba.itlike.com/public/uploads/10001/20230321/a0db9adb2e666a65bc8dd133fbed7834.png" alt=""><div class="name">神雕大侠</div><van-rate :size="16" :value="5" color="#ffd21e" void-icon="star" void-color="#eee"/></div><div class="content">质量很不错 挺喜欢的</div><div class="time">2023-03-21 15:01:35</div></div></div></div><!-- 商品描述 --><div class="desc"><img src="https://uimgproxy.suning.cn/uimg1/sop/commodity/kHgx21fZMWwqirkMhawkAw.jpg" alt=""><img src="https://uimgproxy.suning.cn/uimg1/sop/commodity/0rRMmncfF0kGjuK5cvLolg.jpg" alt=""><img src="https://uimgproxy.suning.cn/uimg1/sop/commodity/2P04A4Jn0HKxbKYSHc17kw.jpg" alt=""><img src="https://uimgproxy.suning.cn/uimg1/sop/commodity/MT4k-mPd0veQXWPPO5yTIw.jpg" alt=""></div><!-- 底部 --><div class="footer"><div class="icon-home"><van-icon name="wap-home-o" /><span>首页</span></div><div class="icon-cart"><van-icon name="shopping-cart-o" /><span>购物车</span></div><div class="btn-add">加入购物车</div><div class="btn-buy">立刻购买</div></div></div>
</template><script>
export default {name: 'ProDetail',data () {return {images: ['https://img01.yzcdn.cn/vant/apple-1.jpg','https://img01.yzcdn.cn/vant/apple-2.jpg'],current: 0}},methods: {onChange (index) {this.current = index}}
}
</script><style lang="less" scoped>
.prodetail {padding-top: 46px;::v-deep .van-icon-arrow-left {color: #333;}img {display: block;width: 100%;}.custom-indicator {position: absolute;right: 10px;bottom: 10px;padding: 5px 10px;font-size: 12px;background: rgba(0, 0, 0, 0.1);border-radius: 15px;}.desc {width: 100%;overflow: scroll;::v-deep img {display: block;width: 100%!important;}}.info {padding: 10px;}.title {display: flex;justify-content: space-between;.now {color: #fa2209;font-size: 20px;}.oldprice {color: #959595;font-size: 16px;text-decoration: line-through;margin-left: 5px;}.sellcount {color: #959595;font-size: 16px;position: relative;top: 4px;}}.msg {font-size: 16px;line-height: 24px;margin-top: 5px;}.service {display: flex;justify-content: space-between;line-height: 40px;margin-top: 10px;font-size: 16px;background-color: #fafafa;.left-words {span {margin-right: 10px;}.van-icon {margin-right: 4px;color: #fa2209;}}}.comment {padding: 10px;}.comment-title {display: flex;justify-content: space-between;.right {color: #959595;}}.comment-item {font-size: 16px;line-height: 30px;.top {height: 30px;display: flex;align-items: center;margin-top: 20px;img {width: 20px;height: 20px;}.name {margin: 0 10px;}}.time {color: #999;}}.footer {position: fixed;left: 0;bottom: 0;width: 100%;height: 55px;background-color: #fff;border-top: 1px solid #ccc;display: flex;justify-content: space-evenly;align-items: center;.icon-home, .icon-cart {display: flex;flex-direction: column;align-items: center;justify-content: center;font-size: 14px;.van-icon {font-size: 24px;}}.btn-add,.btn-buy {height: 36px;line-height: 36px;width: 120px;border-radius: 18px;background-color: #ffa900;text-align: center;color: #fff;font-size: 14px;}.btn-buy {background-color: #fe5630;}}
}.tips {padding: 10px;
}
</style>

LazyloadVue 指令,使用前需要对指令进行注册。

import { Lazyload } from 'vant'
Vue.use(Lazyload)

02. 商品详情 - 动态渲染介绍

  1. 动态路由参数,获取商品 id
computed: {goodsId () {return this.$route.params.id}
},
  1. 封装 api 接口 api/product.js
// 获取商品详情数据
export const getProDetail = (goodsId) => {return request.get('/goods/detail', {params: {goodsId}})
}
  1. 一进入页面发送请求,获取商品详情数据
data () {return {images: ['https://img01.yzcdn.cn/vant/apple-1.jpg','https://img01.yzcdn.cn/vant/apple-2.jpg'],current: 0,detail: {},}
},async created () {this.getDetail()
},methods: {...async getDetail () {const { data: { detail } } = await getProDetail(this.goodsId)this.detail = detailthis.images = detail.goods_images}
}
  1. 动态渲染
<div class="prodetail" v-if="detail.goods_name"><van-swipe :autoplay="3000" @change="onChange"><van-swipe-item v-for="(image, index) in images" :key="index"><img v-lazy="image.external_url" /></van-swipe-item><template #indicator><div class="custom-indicator">{{ current + 1 }} / {{ images.length }}</div></template>
</van-swipe><!-- 商品说明 -->
<div class="info"><div class="title"><div class="price"><span class="now">¥{{ detail.goods_price_min }}</span><span class="oldprice">¥{{ detail.goods_price_max }}</span></div><div class="sellcount">已售{{ detail.goods_sales }}件</div></div><div class="msg text-ellipsis-2">{{ detail.goods_name }}</div><div class="service"><div class="left-words"><span><van-icon name="passed" />七天无理由退货</span><span><van-icon name="passed" />48小时发货</span></div><div class="right-icon"><van-icon name="arrow" /></div></div>
</div><!-- 商品描述 -->
<div class="tips">商品描述</div>
<div class="desc" v-html="detail.content"></div>

03. 商品详情 - 动态渲染评价

  1. 封装接口 api/product.js
// 获取商品评价
export const getProComments = (goodsId, limit) => {return request.get('/comment/listRows', {params: {goodsId,limit}})
}
  1. 页面调用获取数据
import defaultImg from '@/assets/default-avatar.png'data () {return {...total: 0,commentList: [],defaultImg
},async created () {...this.getComments()
},async getComments () {const { data: { list, total } } = await getProComments(this.goodsId, 3)this.commentList = listthis.total = total
},
  1. 动态渲染评价
<!-- 商品评价 -->
<div class="comment" v-if="total > 0"><div class="comment-title"><div class="left">商品评价 ({{ total }})</div><div class="right">查看更多 <van-icon name="arrow" /> </div></div><div class="comment-list"><div class="comment-item" v-for="item in commentList" :key="item.comment_id"><div class="top"><img :src="item.user.avatar_url || defaultImg" alt=""><div class="name">{{ item.user.nick_name }}</div><van-rate :size="16" :value="item.score / 2" color="#ffd21e" void-icon="star" void-color="#eee"/></div><div class="content">{{ item.content }}</div><div class="time">{{ item.create_time }}</div></div> </div>
</div>

04. 加入购物车 - 唤起弹窗

在这里插入图片描述

  1. 按需导入 van-action-sheet
import { ActionSheet } from 'vant'
Vue.use(ActionSheet)
  1. 准备 van-action-sheet 基本结构
<van-action-sheet v-model="showPannel" :title="mode === 'cart' ? '加入购物车' : '立刻购买'">弹窗
</van-action-sheet>data () {return {...mode: 'cart'showPannel: false}
},
  1. 注册点击事件,点击时唤起弹窗
<div class="btn-add" @click="addFn">加入购物车</div>
<div class="btn-buy" @click="buyFn">立刻购买</div>addFn () {this.mode = 'cart'this.showPannel = true
},
buyFn () {this.mode = 'buyNow'this.showPannel = true
}
  1. 完善结构
<van-action-sheet v-model="showPannel" :title="mode === 'cart' ? '加入购物车' : '立刻购买'"><div class="product"><div class="product-title"><div class="left"><img src="http://cba.itlike.com/public/uploads/10001/20230321/8f505c6c437fc3d4b4310b57b1567544.jpg" alt=""></div><div class="right"><div class="price"><span>¥</span><span class="nowprice">9.99</span></div><div class="count"><span>库存</span><span>55</span></div></div></div><div class="num-box"><span>数量</span>数字框占位</div><div class="showbtn" v-if="true"><div class="btn" v-if="true">加入购物车</div><div class="btn now" v-else>立刻购买</div></div><div class="btn-none" v-else>该商品已抢完</div></div>
</van-action-sheet>
.product {.product-title {display: flex;.left {img {width: 90px;height: 90px;}margin: 10px;}.right {flex: 1;padding: 10px;.price {font-size: 14px;color: #fe560a;.nowprice {font-size: 24px;margin: 0 5px;}}}}.num-box {display: flex;justify-content: space-between;padding: 10px;align-items: center;}.btn, .btn-none {height: 40px;line-height: 40px;margin: 20px;border-radius: 20px;text-align: center;color: rgb(255, 255, 255);background-color: rgb(255, 148, 2);}.btn.now {background-color: #fe5630;}.btn-none {background-color: #cccccc;}
}
  1. 动态渲染
<van-action-sheet v-model="showPannel" :title="mode === 'cart' ? '加入购物车' : '立刻购买'"><div class="product"><div class="product-title"><div class="left"><img :src="detail.goods_image" alt=""></div><div class="right"><div class="price"><span>¥</span><span class="nowprice">{{ detail.goods_price_min }}</span></div><div class="count"><span>库存</span><span>{{ detail.stock_total }}</span></div></div></div><div class="num-box"><span>数量</span>数字框组件</div><div class="showbtn" v-if="detail.stock_total > 0"><div class="btn" v-if="mode === 'cart'">加入购物车</div><div class="btn now" v-if="mode === 'buyNow'">立刻购买</div></div><div class="btn-none" v-else>该商品已抢完</div></div>
</van-action-sheet>

05. 加入购物车 - 封装数字框组件

在这里插入图片描述

  1. 封装组件 components/CountBox.vue
<template><div class="count-box"><button @click="handleSub" class="minus">-</button><input :value="value" @change="handleChange" class="inp" type="text"><button @click="handleAdd" class="add">+</button></div>
</template><script>
export default {props: {value: {type: Number,default: 1}},methods: {handleSub () {if (this.value <= 1) {return}this.$emit('input', this.value - 1)},handleAdd () {this.$emit('input', this.value + 1)},handleChange (e) {// console.log(e.target.value)const num = +e.target.value // 转数字处理 (1) 数字 (2) NaN// 输入了不合法的文本 或 输入了负值,回退成原来的 value 值if (isNaN(num) || num < 1) {e.target.value = this.valuereturn}this.$emit('input', num)}}
}
</script><style lang="less" scoped>
.count-box {width: 110px;display: flex;.add, .minus {width: 30px;height: 30px;outline: none;border: none;background-color: #efefef;}.inp {width: 40px;height: 30px;outline: none;border: none;margin: 0 5px;background-color: #efefef;text-align: center;}
}
</style>
  1. 使用组件
import CountBox from '@/components/CountBox.vue'export default {name: 'ProDetail',components: {CountBox},data () {return {addCount: 1...}},
}<div class="num-box"><span>数量</span><CountBox v-model="addCount"></CountBox>
</div>

06. 加入购物车 - 判断 token 登录提示

说明:加入购物车,是一个登录后的用户才能进行的操作,所以需要进行鉴权判断,判断用户 token 是否存在

  1. 若存在:继续加入购物车操作
  2. 不存在:提示用户未登录,引导到登录页

1. 按需注册 dialog 组件
import { Dialog } from 'vant'
Vue.use(Dialog)
  1. 按钮注册点击事件
<div class="btn" v-if="mode === 'cart'" @click="addCart">加入购物车</div>
  1. 添加 token 鉴权判断,跳转携带回跳地址
async addCart () {// 判断用户是否有登录,因为这里要根据用户的选择判断是否要跳转到登录页面,我们之前在路由前置守卫中设置过拦截路径为:['/pay', '/myorder'],//这里是用户访问这两个页面直接拦截前往登录页面,并没有询问用户的意见,所以不能直接在前置守卫中进行拦截。if (!this.$store.getters.token) {this.$dialog.confirm({title: '温馨提示',message: '此时需要先登录才能继续操作哦',confirmButtonText: '去登录',cancelButtonText: '再逛逛'}).then(() => {//注意这里使用replace而不是使用push,push会新增浏览记录,而replace则是替换浏览记录,A页面replaceB时,A的浏览纪律会被B替换this.$router.replace({path: '/login',query: {backUrl: this.$route.fullPath}})}).catch(() => {})return}console.log('进行加入购物车操作')
}
  1. 登录后,若有回跳地址,则回跳页面
// 判断有无回跳地址
const url = this.$route.query.backUrl || '/'
this.$router.replace(url)

07. 加入购物车 - 封装接口进行请求

在这里插入图片描述

  1. 封装接口 api/cart.js
// 加入购物车,goodSkulId为商品的规格,例如颜色,大小
export const addCart = (goodsId, goodsNum, goodsSkuId) => {return request.post('/cart/add', {goodsId,goodsNum,goodsSkuId})
}
  1. 页面中调用请求
data () {return {cartTotal: 0}  
},async addCart () {...const { data } = await addCart(this.goodsId, this.addCount, this.detail.skuList[0].goods_sku_id)this.cartTotal = data.cartTotalthis.$toast('加入购物车成功')this.showPannel = false
},

在这里插入图片描述

  1. 请求拦截器中,统一携带 token
// 自定义配置 - 请求/响应 拦截器
// 添加请求拦截器
instance.interceptors.request.use(function (config) {...const token = store.getters.tokenif (token) {config.headers['Access-Token'] = tokenconfig.headers.platform = 'H5'}return config
}, function (error) {// 对请求错误做些什么return Promise.reject(error)
})
  1. 准备小图标
<div class="icon-cart"><span v-if="cartTotal > 0" class="num">{{ cartTotal }}</span><van-icon name="shopping-cart-o" /><span>购物车</span>
</div>
  1. 定制样式
.footer .icon-cart {position: relative;padding: 0 6px;.num {z-index: 999;position: absolute;top: -2px;right: 0;min-width: 16px;padding: 0 4px;color: #fff;text-align: center;background-color: #ee0a24;border-radius: 50%;}
}

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

结合 Django 和 Vue.js 打造现代 Web 应用

概要 在 Web 开发的世界里&#xff0c;Django 和 Vue.js 分别是后端和前端两个非常流行的框架。Django 以其强大的后端能力、快速开发以及安全性而著称&#xff0c;而 Vue.js 因其简洁、灵活和易于上手在前端开发领域广受欢迎。 本篇文章将详细介绍如何将 Django 与 Vue.js 结…

双点双向重发布

配置IP [R1-GigabitEthernet0/0/0]ip ad 192.168.12.1 24 [R1-GigabitEthernet0/0/1]ip ad 192.168.13.1 24 [R1-LoopBack0]ip address 1.1.1.1 24 [R2-GigabitEthernet0/0/0]ip address 192.168.12.2 24 [R2-GigabitEthernet0/0/1]ip address 192.168.24.2 24 [R2-LoopBack0…

多媒体领域顶会ACM MM 2023 获奖论文一览

ACM 国际多媒体会议是计算机科学领域中多媒体领域的顶级会议&#xff0c;属于CCF A类。今年的ACM MM 2023 已于2023年10月29日至11月2日在加拿大渥太华举行。 ACM MM会议专注于推动多媒体研究和应用&#xff0c;其研究领域广泛涉及触觉、视频、VR/AR、音频、语音、音乐、传感器…

解析 Python requests 库 POST 请求中的参数顺序问题

在这篇文章中&#xff0c;我们将探讨一个用户在使用Python的requests库进行POST请求时遇到的问题&#xff0c;即参数顺序的不一致。用户通过Fiddler进行网络抓包&#xff0c;发现请求体中的参数顺序与他设置的顺序不符。我们将深入了解POST请求的工作原理&#xff0c;并提供解决…

Mysql超详细安装配置教程(保姆级)

目录 一、下载Mysql 二、安装Mysql 三、配置Mysql 四、连接Mysql 五、部分疑难问题 一、下载Mysql 从官网下载MySQL&#xff0c;这里我选用的是Mysql8.0.34版本 二、安装Mysql 下载完成后直接双击进行安装&#xff0c;打开后的页面如下所示&#xff1a; “Developer Defa…

echarts双轴刻度线y轴刻度线对齐

splitNumber属性主要用于设置坐标轴分割的段数。例如&#xff0c;在类目轴&#xff08;category&#xff09;中&#xff0c;可以通过设置splitNumber属性来控制坐标轴被分割成的段数。需要注意的是&#xff0c;这个分割段数只是一个预估值&#xff0c;最终实际显示的段数会在这…

C#调用C++ dll教程

文章目录 一、创建C dll项目二、C#程序员调用C dll三、C与C#数据类型对应基本数据类型对应表C指针类型与C#类型 在使用C#开发客户端时&#xff0c;有时需要调用C dll&#xff0c;本篇博客来介绍C#程序如何调用C dll。 一、创建C dll项目 首先使用VS2022创建C dll项目&#xf…

【MySQL】聚合函数:汇总、分组数据

文章目录 学习目标MAX()、MIN()、AVG()、SUM()、COUNT()COUNT(*) 得到所有记录条目DISTINCT去重练习1&#xff08;使用UNION &#xff0c; SUM&#xff0c; BETEEN AND&#xff09;GROUP BY子句练习2&#xff08;使用sum&#xff0c;group by&#xff0c; join on&#xff0c; …

一文带你了解docker技术

什么是Docker Docker是一种虚拟技术&#xff0c;诞生于2013年&#xff0c;是dotCloud公司研发的开源项目&#xff0c;因为docker这个公司后来改名docker inc&#xff0c;docker的目标是实现轻量级的操作系统虚拟化解决方案。通俗点说&#xff0c;我们想在一台机器上运行多个系…

redis集群-主从复制

目录 一、主从复制概念二、单机安装Redis2.1、安装 Redis 需要的软件 gcc 和 tcl2.2、上传Redis压缩包2.3、编辑 redis.conf 文件2.4、执行安装 Redis 命令2.5、注意防火墙配置 三、主从复制 - 环境搭建3.1、配置一个 master 节点&#xff0c;两个 slave 节点3.2、配置 redis63…

数据结构-哈希表(C语言)

哈希表的概念 哈希表就是&#xff1a; “将记录的存储位置与它的关键字之间建立一个对应关系&#xff0c;使每个关键字和一个唯一的存储位置对 应。” 哈希表又称&#xff1a;“散列法”、“杂凑法”、“关键字&#xff1a;地址法”。 哈希表思想 基本思想是在关键字和存…

kibana8.10.4简单使用

1.创建discovery里的日志项目 点击stack management 选择kibana里的数据视图&#xff0c;右上角创建数据视图&#xff0c;输入名称。索引范围。例子 example-* ,匹配以example-开头的所有index。 然后点击 保存数据视图到kibana&#xff0c; 2.Kibana多用户创建及角色权限控…