Vue 实现商品分类、列表、详情、购物车、搜索(vant)

2023-11-15

商品分类Classify:

<template>
	<div>
		
		
		<div style="display: flex;">
			
			<div class="oneNav">
				<!-- vant侧边导航 -->
				<van-sidebar style="height: 617px; white-space: nowrap; overflow-y: scroll;" v-model="activeKey">
					<van-sidebar-item 
					v-for="(item,index) in oneClass" 
					:title="item.cname" 
					@click="oneNav(index)"/>
				</van-sidebar>
				
			</div>
			
			
			<div class="twoNav">
				<!-- vant宫格 -->
				<van-grid :gutter="10" column-num="3" :border="false" icon-size="60px">
				  <van-grid-item  
					v-for="(item,index) in twoClass" 
					:icon="item.scpic" 
					:text="item.subcname" 
					@click="onList(item.subcid)"/>
				</van-grid>
			</div>
			
			
		</div>
		
		
	</div>
</template>

<script>
	export default{
		data(){
			return{
				oneClass:[], //一级分类数据
				activeKey:0,
				twoClass:[] //二级分类数据
			}
		},
		mounted() {
			this.$axios.get('http://api.kudesoft.cn/tdk/category').then(res=>{
				// console.log(res.data.data.data)
				this.oneClass = res.data.data.data
				this.twoClass = this.oneClass[0].subcategories
			})
		},
		methods:{
			oneNav(index){ //点击一级导航切换
				this.$axios.get('http://api.kudesoft.cn/tdk/category').then(res=>{
					// console.log(res.data.data.data)
					this.oneClass = res.data.data.data
					this.twoClass = this.oneClass[index].subcategories
				})
			},
			onList(id){ //点击二级导航跳转列表页
				this.$router.push({
					path:'/list',
					query:{
						id
					}
				})
			}
		}
	}
</script>

<style scoped>
	.van-sidebar::-webkit-scrollbar {
		display: none;
	}
	
</style>

商品列表List:

<template>
	<div>
		
		<!-- vant搜索 -->
		<div class="list-nav">
			<van-search @click="toSearch" v-model="kw" shape="round" placeholder="请输入搜索关键词">
				
				<template #left>
					<van-icon @click="back" style="margin-right: 5px;" size="22px" name="arrow-left" />
				</template>
				
			</van-search>
		</div>
		
		<!-- 排序 -->
		<div class="list-sort">
			<div @click="onSort" :class="{'active':isActive==0}">综合排序</div>
			
			<div @click="saleSort" :class="{'active':isActive==1}">销量</div>
			
			<div @click="priceSort" style="display: flex;">
				<div>价格</div>
				<div style="margin-left: 5px;">
					<div :class="{'active':isActive==2}" class="iconfont icon-jiantou"></div>
					<div :class="{'active':isActive==3}" class="iconfont icon-jiantouxia"></div>
				</div>
			</div>
			
			<div><!-- 下拉菜单 -->
				<van-dropdown-menu>
				  <van-dropdown-item v-model="value1" :options="option1" @change="screen" />
				</van-dropdown-menu>
			</div>
			
		</div>
		
		
		<!-- vant骨架屏 -->
		<van-skeleton v-for="i in 18" title :row="3" :loading="list.length<=0" style="background-color: white;"/>
		
		<div style="margin-top: 96px;">
			<!-- 商品卡片 -->
			<van-card
				v-for="(item,index) in list"
				:tag="item.shopType == 1 ? '天猫':'淘宝'"
				:price="item.actualPrice"
				:title="item.title"
				:thumb="item.mainPic"
				@click="toDetails(item.id)">
				
				<template #num>
					<span>30天销量:{{item.monthSales}}</span>
				</template>
			</van-card>
		</div>
		
	</div>
</template>

<script>
	export default{
		data(){
			return{
				kw:'',
				list:[],
				value1: -1,
				option1: [
					{ text: '店铺类型', value: -1 },
					{ text: '淘宝', value: 0 },
					{ text: '天猫', value: 1 }
				],
				isActive:-1 //0综合排序,1销量,2价格升序,3价格降序
			}
		},
		mounted() {
			this.kw = this.$route.query.kw //把搜索页面搜索的值传过来
			
			let id = this.$route.query.id
			this.$axios.get('http://api.kudesoft.cn/tdk/goods',{
				params:{
					pageId:1,
					subcid:id
				}
			}).then(res=>{
				// console.log(res.data.data.data.list)
				let list = res.data.data.data.list
				if(this.kw == '' || this.kw == undefined){
					this.list = list
				}else{
					list.map(item=>{
						if(item.title.includes(this.kw)){
							this.list.push(item)
						}
					})
				}
			})
		},
		methods:{
			back(){ //返回分类页面
				this.$router.push({
					path:'/classify'
				})
			},
			toSearch(){ //跳转到搜索页面
				this.$router.push({
					path:'/search'
				})
			},
			toDetails(id){ //跳转到详情页面
				this.$router.push({
					path:'/details',
					query:{
						id
					}
				})
			},
			priceSort(){ //价格排序
				if(this.isActive < 2){
					this.isActive = 2
					this.list.sort((a,b)=>{
						return a.actualPrice - b.actualPrice
					})
				}else if(this.isActive == 2){
					this.isActive = 3
					this.list.sort((a,b)=>{
						return b.actualPrice - a.actualPrice
					})
				}else if(this.isActive == 3){
					this.isActive = 2
					this.list.sort((a,b)=>{
						return a.actualPrice - b.actualPrice
					})
				}
			},
			onSort(){ //综合排序
				this.isActive = 0
				this.list.sort((a,b)=>{
					return b.shopLevel - a.shopLevel
				})
			},
			saleSort(){ //销量排序
				this.isActive = 1
				this.list.sort((a,b)=>{
					return b.monthSales - a.monthSales
				})
			},
			screen(value){ //筛选
				this.isActive = -1
				this.list = []
				
				if(value == -1){
					this.$axios.get('http://api.kudesoft.cn/tdk/goods').then(res=>{
						this.list = res.data.data.data.list
					})
				}else{
					this.$axios.get("http://api.kudesoft.cn/tdk/goods").then(res=>{
						let list = res.data.data.data.list
						list.map(item=>{
							if(item.shopType == value){
								this.list.push(item)
							}
						})
					})
				}
			}
		}
	}
</script>

<style scoped>
	.list-title{
		 display: -webkit-box;
		 -webkit-box-orient: vertical;
		 -webkit-line-clamp: 2;
		 overflow: hidden;
	}
	.list-nav{
		width: 100%;
		position: fixed;
		top: 0;
		left: 0;
		z-index: 99;
	}
	.iconfont{
		font-size: 8px;
		margin: 0 3px;
	}
	.active{
		color: red;
	}
	.list-sort{
		position: fixed;
		top: 54px;
		left: 0;
		z-index: 99;
		width: 100%;
		background-color: #FFFFFF;
		display: flex;
		justify-content: space-around;
		align-items: center;
		margin-top: -1px;
		font-weight: 300;
	}
</style>


商品详情Details:

<template>
	<div>
		
		<!-- element-ui返回顶部 -->
		<el-backtop :bottom="100" :right="0">
		   <div
		     style="{
		       height: 100%;
		       width: 100%;
		       background-color: #f2f5f6;
		       box-shadow: 0 0 6px rgba(0,0,0, .12);
		       text-align: center;
		       line-height: 40px;
		       color: #000000;
			   border-radius: 50%;
		     }"
			>
		    <span class="el-icon-upload2"></span>
		   </div>
		</el-backtop>
		
		
		<div>
			<!-- vant轮播 -->
			<div style="background-color: #FFFFFF; border-radius: 0 0 15px 15px; margin-bottom: 15px;">
				<van-swipe class="my-swipe" indicator-color="white">
				  <van-swipe-item v-for="(item,index) in details.imgs">
					  <img :src="item" width="100%">
				  </van-swipe-item>
				</van-swipe>
				
				<div style="padding: 10px 10px;">
					<div style="display: flex; justify-content: space-between;">
						<span style="color: #F2270C;"><span style="font-size: 30px;">{{details.actualPrice}}</span></span>
						<img :src="details.shopLogo" width="30px" height="30px">
					</div>
					
					<div>
						<span style="margin-right: 5px; font-size: 12px; background-color: #FF0000; color: white; padding: 2px 2px; border-radius: 2px;">{{details.shopName}}</span>
						<span style="font-weight: 700;">{{details.title}}</span>
					</div>
				</div>
			</div>
			
			<div>
				<div v-for="(item,index) in details.detailPics">
					<img :src="item.img" width="100%">
				</div>
			</div>
			
		</div>
		
		
		<div style="height: 50px;"></div>
		<!-- vant商品导航 -->
		<van-goods-action>
		  <van-goods-action-icon icon="chat-o" text="客服" dot />
		  <van-goods-action-icon icon="cart-o" text="购物车" :badge="this.carts.length" @click="toCarts"/>
		  <van-goods-action-icon icon="shop-o" text="店铺" />
		  <van-goods-action-button @click="addCart" type="warning" text="加入购物车" />
		  <van-goods-action-button type="danger" text="立即购买" />
		</van-goods-action>
		
		
		
	</div>
</template>

<script>
	import Vue from 'vue';
	import { Toast } from 'vant';
	Vue.use(Toast);
	export default{
		data(){
			return{
				details:[],
				carts:[] //购物车商品
			}
		},
		created() {
			let carts = localStorage.carts
			if(carts){
				this.carts = JSON.parse(carts)
			}
		},
		mounted() {
			let id = this.$route.query.id
			this.$axios.get('http://api.kudesoft.cn/tdk/details',{
				params:{
					id:id
				}
			}).then(res=>{
				// console.log(res.data.data.data)
				this.details = res.data.data.data
				this.details.imgs = this.details.imgs.split(',')
				//this.details.detailPics = this.details.detailPics.split(',')
				this.details.detailPics = JSON.parse(this.details.detailPics)
			})
		},
		methods:{
			toCarts(){ //跳转购物车页面
				this.$router.push({
					path:'/carts'
				})
			},
			addCart(){ //添加购物车
				Toast.success('加入购物车成功');
				
				let rel = true
				this.carts.map(item=>{
					if(item.data.id == this.details.id){
						item.num++;
						rel = false;
					}
				})
				
				if(rel){
					this.carts.push({
						data:this.details,
						num:1
					})
				}
				
				localStorage.carts = JSON.stringify(this.carts)
			}
		}
	}
</script>

<style>
</style>

购物车Carts:

<template>
	<div>
		
		<!-- vant空状态 -->
		<van-empty v-show="isShow" description="购物车目前还没有商品" image="https://img.yzcdn.cn/vant/custom-empty-image.png">
		  <van-button round type="danger" class="bottom-button" @click="gotoClassify">
		    去购物
		  </van-button>
		</van-empty>
		
		
		<div v-for="(item,index) in carts">
			<!-- vant复选框组 --> <!-- vant布局 --> <!-- vant滑动单元格 --> 
			<!-- vant商品卡片 --> <!-- vant步进器 -->
			<van-checkbox-group v-model="result" @change="onChecked">
				<van-row style="background-color: white;">
					
				  <van-col span="2" style="margin-top: 35px;">
					
					<van-checkbox :name="item"></van-checkbox>
				  
				  </van-col>
				  
				  <van-col span="22">
					
					  <van-swipe-cell>
					  	
					    <van-card
					      :price="item.data.actualPrice"
					      :desc="item.data.desc"
					      :title="item.data.dtitle"
						  :thumb="item.data.mainPic"
					      class="goods-card"
					  	>
					    
					  	  <template #num>
					  		<van-stepper v-model="item.num" @change="onNum"/>
					  	  </template>
					  	  
					    </van-card>
					    
					    <template #right >
					      <van-button @click="del(item,index)" square text="删除" type="danger" class="delete-button" />
					    </template>
					    
					  </van-swipe-cell>
					  
				  </van-col>
				  
				</van-row>
			</van-checkbox-group>
			
			
		</div>
		
		<!-- vant提交订单栏 -->
		<van-submit-bar :price="total" button-text="提交订单">
		  <van-checkbox v-model="checkedAll" @click="onAll">全选</van-checkbox>
		</van-submit-bar>
		
		
	</div>
</template>

<script>
	export default {
		data(){
			return{
				carts:[], //添加到购物车的商品
				result:[], //所有复选框选中的商品
				checkedAll:false, //全选的状态
				total:0, //总价
				isShow:true
			}
		},
		created() {
			let carts = localStorage.carts
			if(carts){
				this.carts = JSON.parse(carts)
			}
			
			if(this.carts.length > 0){ //显示或隐藏空状态
				this.isShow = false
			}else{
				this.isShow = true
			}
		},
		methods:{
			onNum(){ //商品数量发生变化时触发
				this.onChecked()
				localStorage.carts = JSON.stringify(this.carts)
			},
			onChecked(){ //点击复选框选中商品并计算总价
				this.total = 0
				this.result.map(item=>{
					this.total += item.data.actualPrice*100*item.num
				})
				
				if(this.result.length == this.carts.length){
					this.checkedAll = true
				}else{
					this.checkedAll = false
				}
				
			},
			onAll(){ //点击全选按钮
				if(this.checkedAll){ //如果为true的话全部选中,否则全不选
					this.result = this.carts
				}else{
					this.result = []
				}
			},
			del(item,index){ //删除
				if(this.result.includes(item)){
					this.$notify({ type: 'danger', message: '删除前请取消选中' });
				}else{
					this.carts.splice(index,1)
					this.checkedAll = this.result.length == this.carts.length
					localStorage.carts = JSON.stringify(this.carts)
				}
				
				if(this.carts.length > 0){ //显示或隐藏空状态
					this.isShow = false
				}else{
					this.isShow = true
					this.checkedAll = false
				}
				
			},
			gotoClassify(){ //跳转到分类页
				this.$router.push({
					path:'/classify'
				})
			}
		}
	}
</script>

<style scoped="scoped">
	.goods-card {
	   background-color: white;
	}
	
	.delete-button {
	   height: 100%;
	}
	
	.bottom-button {
	  width: 180px;
	  height: 40px;
	}
</style>

搜索Search:

<template>
	<div>
		
		<!-- vant搜索 -->
		<van-search @input="showSearch" @search="onSearch" autofocus show-action shape="round" v-model="kw" placeholder="请输入搜索关键词">
			
			<template #left>
				<van-icon @click="back" style="margin-right: 5px;" size="22px" name="arrow-left" />
			</template>
			
			<template #action>
				<van-button @click="onSearch" size="small" type="danger" style="border-radius: 5px; font-size: 14px;">搜索</van-button>
			</template>
			
		</van-search>
		
		<!-- 搜索记录 -->
		<div v-show="isShow">
			<div style="display: flex; justify-content: space-between; padding: 5px;">
				<div>
					搜索记录:
				</div>
				
				<div>
					<van-icon @click="onDel" size="18px" name="delete" />
				</div>
			</div>
			
			<div>
				<van-tag style="margin: 5px;" v-for="(item,index) in saveList">{{item}}</van-tag>
			</div>
		</div>
		
		<!-- 搜索提示 -->
		<van-cell-group v-show="!isShow">
		  <van-cell v-for="(item,index) in showList" :title="item.dtitle" />
		</van-cell-group>
		
	</div>
</template>

<script>
	export default{
		data(){
			return{
				kw:'',
				isShow:true,
				list:[], //所有数据
				saveList:[], //搜索记录的数据
				showList:[] //搜索提示的数据
			}
		},
		created() {
			let saveList = localStorage.saveList
			if(saveList){
				this.saveList = JSON.parse(saveList)
			}
		},
		mounted() {
			this.$axios.get(' http://api.kudesoft.cn/tdk/goods').then(res=>{
				// console.log(res.data.data.data.list)
				this.list = res.data.data.data.list
			})
		},
		methods:{
			back(){ //返回上一级
				window.history.back()
			},
			onSearch(val){ //点击enter和点击搜索时触发
			
				if(val.trim() == ''){ //如果搜索的值为空则不会跳转
					return
				}
			
				this.$router.push({ //点击跳转到列表页,并把输入的值传过去
					path:'/list',
					query:{
						kw:val
					}
				})
				
				if(!this.saveList.includes(val.trim())){ //判断是否重复添加相同记录
					this.saveList.push(val) //把搜索的值添加到搜索记录
					localStorage.saveList = JSON.stringify(this.saveList)
				}
				
			},
			showSearch(){ //输入框内容发生变化时触发
				this.showList = []
				
				this.list.map(item=>{
					if(item.dtitle.includes(this.kw)){
						this.showList.push(item)
					}
				})
				
				if(this.kw.trim() == ''){ //判断显示或隐藏搜索提示
					this.isShow = true
				}else{
					this.isShow = false
				}
			},
			onDel(){ //删除
				this.$dialog.confirm({
				  title: '确定要清空搜索记录吗'
				})
				.then(() => {
				    this.saveList = []
					localStorage.saveList = JSON.stringify(this.saveList)
				})
				.catch(() => {
				    // on cancel
				});
			}
		}
	}
</script>

<style>
</style>

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Vue 实现商品分类、列表、详情、购物车、搜索(vant) 的相关文章

随机推荐

  • cass等距离等分线段的命令键_cad等分快捷键(cad等分线段快捷键命令)

    CAD中等分线段分为两种 定数等分和定距等分 1 定数等分 命令是DIVIDE 快捷键是DIV 2 定距等分 命令是MEASURE 快捷键是ME 以线段定数等分为例 1 命令为DIV 以CAD2010操作为例 1 打开CAD的软件 如图所示
  • LeetCode 99. 恢复二叉搜索树

    题目链接 https leetcode cn com problems recover binary search tree 思路如下 如下图所示 正常情况下中序遍历的结果是升序的 11 17 18 22 28 37 42 44 62 如下
  • Hive设置本地模式

    set hive exec mode local auto true
  • 使用EasyPOI导出Excel表格(含多sheet导出以及一对多导出)

    一 前言 官方Api文档地址 http doc wupaas com docs easypoi easypoi 1c0u4mo8p4ro8 常用注解介绍 注解介绍 easypoi起因就是Excel的导入导出 最初的模板是实体和Excel的对
  • SqlServer 关于 datetime 的更新引发的思考

    今天在测试更新 SqlServer 表的 datetime 字段时 突然发现并没有更新成功 同时也没有报错 感觉十分诧异 因此仔细排查了一下 终于发现是和字段本身的精度有关 现象 假设我们现在有一张 SqlServer 表 basic in
  • LC-3 机器语言 指令集

    目录 碎碎念念 LC 3指令 运算类指令 ADD addition AND Bit wise logical AND NOT Bit wise complement 数据搬移类指令 LD load ST store LDI load ind
  • FPN网络结构+源码讲解

    YOLOF前传 特征金字塔 FPN 前言 这几天在读CVPR2021的中稿论文YOLOF You Only Look One level Feature 文章回顾了单阶段的特征金字塔网络 FPN 指出FPN的成功的原因在于它对目标检测中优化
  • c++ map用法 入门必看 超详细

    1 map的作用 可以实现各种类型的映射 可以用数组来类比 都是由下标和值组成 但数组用法很局限 下标不能是负数 而且开数组需要消耗太多的内存 开到10 7就内存超限了 因此 很多时候都用高级的map类或vector类来代替数组 其里面的成
  • “对象创建”模式——抽象工厂

    动机 在软件系统中 经常面临着 一系列相互依赖的对象 的创建工作 同时由于需求的变化 往往存在更多系列对象的创建对象 如何应对这种变化 如何绕过常规的对象创建方法 new 提供一种 封装机制 来避免客户程序和这种 多系列具体对象创建工作 的
  • 互联网编程之基于 TCP 的单线程文件收发程序(CS架构)

    目录 需求 服务端实现 客户端实现 测试 需求 可试着根据java编程课所学到的java socket编程技术 尝试编写一个基于 TCP 的单线程文件收发程序 需满足 服务端程序预先定义好需要发送的文件并等待客户端的连接 客户端连接成功后
  • 非极大值抑制(non-maximum suppression)的理解

    最近在学习RCNN时看到了非极大值抑制 一开始有点不明白 在网上学习了之后记录一下 非极大值抑制就是一个寻找局部最大值的过程 在进行目标检测时一般会采取窗口滑动的方式 在图像上生成很多的候选框 然后把这些候选框进行特征提取后送入分类器 一般
  • MySQL执行update时的[ERROR 1093]处理方法

    版权声明 声明 本文档可以转载 须署名原作者 作者 无为 qq 490073687 周祥兴 zhou xiangxing210 163 com gt update TEST NOIDX set CREATETIME now where ID
  • 使用QFileInfo查看文件相关信息----大小,创建时间,属性

    QString file E workspace Data ColorTransfer txt QFileInfo info file qint64 size info size 文件大小 单位byte QDateTime created
  • [Cocos Creator] Cocos Creator 下拉框(Dropdown)组件的实现

    模仿Unity写了一个Dropdown的组件 使用方法可以见工程 链接见底下 https github com ETRick CocosCreator Dropdown tree master Dropdown
  • opencv-python学习教程【持续更新】

    Opencv python学习系列教程 一 图片的读取 代码 实验结果 二 视频的读取 代码 实验结果 三 颜色通道的读取 代码 实验结果 四 图像边界填充 代码 实验结果 五 图像融合 代码 运行效果 六 图像阈值 代码 实验结果 存疑
  • 《Spring cloud Alibaba全解》阅读笔记

    技术 2022 05 Spring cloud Alibaba全解 创建时间 2022 5 22 15 21 更新时间 2023 2 17 8 52 作者 HelloXF 第一章 微服务的介绍 1 1系统架构的演变 1 1 1单体应用架构
  • ospf综合实验v1

    实验题目 搭建拓扑图 部分配置 r3r4r5r6r7之间的mgre r3中心站点的配置 r5的配置 r6r7的配置以此类推 于r5相似 各个区域之间的ospf 解释 为了方便对不规则区域之间的联络 我采用多进程双点双向重发布来实现其中区域1
  • Swift 之 继承、构造过程与析构过程

    Swift 继承 继承我们可以理解为一个类获取了另外一个类的方法和属性 当一个类继承其它类时 继承类叫子类 被继承类叫超类 或父类 在 Swift 中 类可以调用和访问超类的方法 属性和下标脚本 并且可以重写它们 我们也可以为类中继承来的属
  • STC15单片机实战项目 - PCB打样

    PCB打样 一 设计文件链接 1 PADS9 5格式PCB设计文件 gt Project STC15 V1 0 pcb 2 AltiumDesigner格式PCB设计文件 gt Project STC15 V1 0 PcbDoc 二 获取开
  • Vue 实现商品分类、列表、详情、购物车、搜索(vant)

    商品分类Classify