uni-app--》如何实现网上购物小程序(中下)?

news2025/1/23 10:25:29

🏍️作者简介:大家好,我是亦世凡华、渴望知识储备自己的一名在校大学生

🛵个人主页:亦世凡华、

🛺系列专栏:uni-app

🚲座右铭:人生亦可燃烧,亦可腐败,我愿燃烧,耗尽所有光芒。

👀引言

        ⚓经过web前端的学习,相信大家对于前端开发有了一定深入的了解,今天我开设了uni-app专栏,主要想从移动端开发方向进一步发展,而对于我来说写移动端博文的第二站就是uni-app开发,希望看到我文章的朋友能对你有所帮助。

目录

商品列表界面

实现上拉加载和下拉刷新

商品详情界面

实现轮播图区域

实现商品信息区域

渲染商品导航区域

实现加入购物车

购物车界面

实现收货地址区域

实现结算区域


商品列表界面

接下来实现商品列表界面,展现的是商品的图片信息和价格,在分包的商品列表中进行展示,通过在主页和分类点击的商品图片传递的参数,决定商品要展现的内容,具体实现过程如下:

通过调用接口,将获取到的数据转存到data中进行保存:

将获取到的数据进行v-for遍历,渲染到页面上如下:

给出如下样式:

如果想设置价格存在小数点,可以通过以下方式:

当然如果后期如果写很多类似这种界面的话,可以将item单独抽离出组件, 将单独抽离的组件存放到components组件当中去,后期如何想调用这种组件进行页面渲染的话传递数据即可,如下:

<template>
	<view class="goods-item">
		<!-- 左侧的盒子 -->
		<view class="goods-item-left">
			<image :src="item.goods_small_logo || defaultPic" class="goods-pic"></image>
		</view>
		<!-- 右侧的盒子 -->
		<view class="goods-item-right">
			<!-- 商品名称 -->
			<view class="goods-name">{{item.goods_name}}</view>
			<!-- 商品价格 -->
			<view class="goods-info-box">
				<view class="goods-price">¥{{item.goods_price}}</view>
			</view>
		</view>
	</view>
</template>

<script>
	export default {
		name:"my-goods",
		props:{
			item:{
				type:Object,
				default:{}
			}
		},
		data() {
			return {
				// 默认的空图片
				defaultPic: 'https://img3.doubanio.com/f/movie/8dd0c794499fe925ae2ae89ee30cd225750457b4/pics/movie/celebrity-default-medium.png',
			};
		}
	}
</script>

<style lang="scss">
.goods-item{
	display: flex;
	padding: 10px 5px;
	border-bottom: 1px solid #f0f0f0;
	.goods-item-left{
		margin-right: 5px;
		.goods-pic{
			width: 100px;
			height: 100px;
			display: block;
		}
	}
	.goods-item-right{
		display: flex;
		flex-direction: column;
		justify-content: space-between;
		.goods-name{
			font-size: 13px;
		}
		.goods-info-box{
			.goods-price{
				color: #c00000;
				font-size: 16px;
			}
		}

	}
}
</style>

自定义好my-goods组件之后,我们可以在goods-list组件中调用我们自定义的组件,通过props传递参数即可,如下:

实现上拉加载和下拉刷新

接下来实现商品列表的上拉加载更多的功能,当页面向下滑动时触发上拉触底函数,关系page页数,具体实现如下:

在项目的pages.json文件中,设置如下的上拉触底的距离。

接下来调用页面上拉触底函数进行页码值+1操作,并在调用接口赋值data数据时,将原本数据和现在获取到的数据进行一个数组的拼接,如下:

因为在进行上来触底的时候如果频繁的进行上拉下拉会导致频繁的请求页码值加+1,所以要设置一个节流阀,在下一条数据加载完成后,才去调用上拉触底函数,现在好像这个bug修复了,我目前是不需要设置节流阀也能实现效果,但是如果遇到老项目还是需要设置节流阀的,这里简单提一下设置节流阀的过程思路,如下:

判断数据是否加载完成,如果数据渲染完成,再次触发上拉触底函数时,就让page不再自增加1,如果下面的公式成立,则证明没有下一页数据了,如下:

当前的页码值 * 每页显示多少条数据 >= 总数条数
pagenum * pagesize >= total

修改上拉触底函数如下:

接下来实现上拉刷新的效果,首先进行如下配置:

设置下拉刷新函数,需要重置数据如下:

接下来通过传递的参数设置调用阻止下拉刷新的函数,如下:

接下来给goods_list设置点击事件,进行路由跳转到详情页界面,如下:

商品详情界面

接下来实现商品详情,通过点击页面列表的商品数据从而跳转到商品详情界面,并传递相关query参数来标识当前商品详情页面代表的是哪个商品数据,所以接下来通过调用get接口来获取到当前商品的相关数据,如下:

实现轮播图区域

接下来实现轮播图区域的内容,将我们get接口获取到的相关图片数据进行轮播图的实现,如下:

实现商品信息区域

接下来实现商品的信息区域,定义如下的商品文字信息结构:

// 商品文字信息区域
.goods-info-box{
	padding: 10px;
	padding-right: 0;
	// 商品价格
	.price{
		color: #c00000;
		font-size: 18px;
		margin: 10px 0;
	}
	// 商品信息主体区域
	.goods-info-body{
		display: flex;
		justify-content: space-between;
		// 商品名字
		.goods_name{
			font-size: 13px;
			margin-right: 10px;
		}
		// 收藏
		.favi{
			width: 120px;
			font-size: 12px;
			display: flex;
			flex-direction: column;
			align-items: center;
			justify-content: center;
			border-left: 1px solid #efefef;
			color: gray;
		}
	}
	// 运费区域
	.yf{
		font-size: 12px;
		color: gray;
		margin: 10px 0;
	}
}

接下来实现商品的图文区域,使用uni-app的API来识别后端传递过来的html标签,如下:

渲染商品导航区域

接下来实现渲染商品的导航区域,这里使用的uni-app的一个插件需要自行下载,如下:

使用官方提供给我们的一个基础案例,复制到项目代码中,得到如下界面

接下来给其设置固定定位,使其固定在最下方:

因为是固定定位,会覆盖掉页面的一部分内容,所以我们要设置内容的下内边距为导航区域的高度

给导航区域设置点击事件,当点击购物车时,跳转到购物车界面,如下:

实现加入购物车

接下来实现商品详情界面将商品添加到购物车的功能,因为有页面有好多都需要该数据,所以可以设置将数据存放到store仓库里面,那么这里的话就需要使用vuex来进行集中式状态管理了,具体的实现过程如下:

首先先创建一个新的仓库,作为存放整合各个数据和方法的容器:

import Vue from 'vue'
import Vuex from 'vuex'
import muduleCart from '@/store/cart.js'

Vue.use(Vuex)

const store = new Vuex.Store({
    // 挂载 store 模块
	modules:{
		'm_cart':muduleCart
	}
})

export default store

将创建好的容器在main.js入口文件上进行挂载到Vue实例上,如下:

在store文件夹下新建一个cart.js模块,封装方法来实现加入购物车的功能,代码如下:

export default {
	namespaced:true,
	
	state:()=>({
		// 购物车的数组,用来存放购物车中每个商品的信息对象
		cart:[]
	}),
	mutations:{
		addToCart(state,goods){
			// 根据提交的商品的Id,查询购物车中是否存在这件商品
		    // 如果不存在,则 findResult 为 undefined;否则,为查找到的商品信息对象
			const findResult = state.cart.find(x=>x.goods_id === goods.goods_id)
			if(!findResult){
				// 如果购物车中没有这件商品,则直接 push
				state.cart.push(goods)
			}else{
				// 如果购物车中有这件商品,则只更新数量即可
				findResult.goods_count++
			}
 		}
	},
	getters:{
		// 统计购物车中商品的总数量
		total(state){
			let c = 0
			state.cart.forEach(x => c += x.goods_count)
			return c
		}
	}
}

给加入购物车按钮设置点击事件,如下:

通过以下方法调用store中的数据

虽然我们点击加入购物车按钮,购物车会出现商品数量,但是我们一旦刷新编译器的话数据就会消失,这里需要我们设置一下本地存储,如下:

开启watch深度监听,一旦数据发生变化则立即调用

接下来处理购物车数字徽标的问题,当点击购物车时跳转到购物车页面,在购物车页面的tabBar处会出现你选中商品数量的一个徽标,因为这个徽标不管你从哪个页面点击进入,购物车选中的数量是不会改变的,这里的话可以将设置徽标的功能设置为mixins混入文件,如下:

购物车界面

接下来实现购物车界面,将vuex保存的状态数据来渲染到购物车界面,如下:

<template>
	<view>
		<!-- 商品列表的标题区域 -->
		<view class="cart-title">
			<!-- 左侧的图标 -->
			<uni-icons type="shop" size="18"></uni-icons>
			<!-- 右侧的文本 -->
			<text class="cart-title-text">购物车</text>
		</view>
		<!-- 循环渲染购物车中的商品信息 -->
		<block v-for="(item,index) in cart" :key="index">
			<my-goods :item="item"></my-goods>
		</block>
	</view>
</template>

<script>
	import badgeMix from '@/mixins/tabbar-badge.js'
	import { mapState } from 'vuex'
	export default {
		computed:{
			...mapState('m_cart',['cart'])
		},
		mixins:[badgeMix],
		data() {
			return {
				
			};
		},
	}
</script>

<style lang="scss">
.cart-title{
	height: 40px;
	display: flex;
	align-items: center;
	padding-left: 5px;
	border-bottom: 1px solid #EFEFEF;
	.cart-title-text{
		font-size: 14px;
		margin-left: 10px;
	}
}
</style>

完成购物车商品列表的展示之后,我们需要在购物车界面单独渲染出一个单选框按钮,这里的话可以通过props传参来控制只有购物车界面才出现单选框按钮,实现过程如下:

界面实现如下:

接下来实现将单选框勾选的商品id和勾选状态进行vuex状态管理并存储在本地当中,实现过程如下

父组件调用子组件的方法,并拿到当前商品的id和状态:

在store中定义能更新状态的方法,来根据 goods_id 查询购物车中对应商品的信息对象:

接下来实现数字输入框的样式,可以参看uni-app官网提供给我们的组件,这里就不再赘述,简单的说一下如何调用吧,和单选框功能实现基本一致,通过v-if来动态的创建数字输入框的显示和隐藏,这里也一样,如下:

通过在store中设置更新数量的函数并存储在本地中

接下来实现滑动删除的功能,这里也是借用uni-app官网提供的一个组件,

在store中定义一个删除的方法,如下:

通过mapMutations方法,将store中定义的方法映射出来,然后进行传递参数即可。

实现收货地址区域

接下来实现收获地址区域的创建,收获地址区域我们定义在components组件当中去,然后设置相关样式在购物车页面中调用该组件即可,如下:

接下来开始设置收获地址组件的相关样式,点击增加地址组件的功能,是调用uni-app官方给我们提供的API,这里直接通过await使用即可,给出如下完整代码:

<template>
	<view>
		<!-- 选择收货地址的盒子 -->
		<view class="address-choose-box" v-if="JSON.stringify(address) === '{}'">
			<button type="primary" size="mini" class="bunChooseAddress" @click="chooseAddress">请选择收货地址+</button>
		</view>
		<!-- 渲染收货信息的盒子 -->
		<view class="address-info-box" v-else>
			<view class="row1">
				<view class="row1-left">
					<view class="username">收货人:{{address.userName}}</view>
				</view>
				<view class="row1-right">
					<view class="phone">电话:{{address.telNumber}}</view>
					<uni-icons type="arrowright" size="16"></uni-icons>
				</view>
			</view>
			<view class="row2">
				<view class="row2-left">收货地址:</view>
				<view class="row2-right">{{addStr}}</view>
			</view>
		</view>
		<!-- 底部的边框线 -->
		<image src="../../static/images/cart_border@2x.png" class="address-border"></image>
	</view>
</template>

<script>
	export default {
		name:"my-address",
		data() {
			return {
				// 收获地址
				address:{}
			};
		},
		methods:{
			async chooseAddress(){
				const res = await uni.chooseAddress()
				if(res.errMsg === "chooseAddress:ok"){
					// 为data里面的收获地址对象赋值
					this.address = res
				}
			}
		},
		computed:{
			addStr(){
				if (!this.address.provinceName) return ''
				// 拼接 省,市,区,详细地址 的字符串并返回给用户
				return this.address.provinceName + this.address.cityName + this.address.countyName + this.address.detailInfo
			}
		}
	}
</script>

<style lang="scss">
.address-choose-box{
	display: flex;
	height: 90px;
	justify-content: center;
	align-items: center;
}
.address-border{
	display: block;
	width: 100%;
	height: 5px;
}
// 渲染收货信息的盒子
.address-info-box {
	font-size: 12px;
	height: 90px;
	display: flex;
	flex-direction: column;
	justify-content: center;
	padding: 0 5px;
	// 第一行
	.row1 {
		display: flex;
		justify-content: space-between;
		.row1-right {
			display: flex;
			align-items: center;
			.phone {
				margin-right: 5px;
			}
		}
	}
	// 第二行
	.row2 {
		display: flex;
		align-items: center;
		margin-top: 10px;
		.row2-left {
			white-space: nowrap;
		}
	}
}
</style>

因为数据是暂时性的,并没有存储到浏览器缓存中,一刷新界面数据就消失了,这里需要我们使用vuex进行数据的状态管理,如下:

在store文件夹中重新建一个user.js文件,用于存放收获地址的数据和方法:

export default {
	// 开启命名空间
	namespaced: true,
	// state数据
	state:()=>({
		// 收获地址
		address:JSON.parse(uni.getStorageSync('address') || '{}'),
	}),
	// 方法
	mutations:{
		// 更新收获地址
		updateAddress(state,address){
			state.address = address
			this.commit('m_user/saveAddressToStorage')
		},
		// 持久化存储address
		saveAddressToStorage(state){
			uni.setStorageSync('address',JSON.stringify(state.address))
		}
	},
	// 数据包装器
	getters:{
		addStr(state){
			if (!state.address.provinceName) return ''
			// 拼接 省,市,区,详细地址 的字符串并返回给用户
			return state.address.provinceName + state.address.cityName + state.address.countyName + state.address.detailInfo
		}
	},
}

在store.js文件中进行挂载:

挂载完成之后,在收获地址组件中进行数据和方法的调用:

实现结算区域

接下来实现结算区域,需要在购物车界面底部建立一个导航按钮用于显示购买的价格,全选和结算按钮的功能实现,具体操作如下:

<template>
	<view class="my-settle-container">
		<!-- 全选 -->
		<label class="radio" @click="changeAllState">
			<radio color="#C00000" :checked="isFullChecked" /><text>全选</text>
		</label>
		<!-- 合计 -->
		<view class="amount-box">
			合计:<text class="amount">¥{{checkedGoodsAmount}}</text>
		</view>
		<!-- 结算按钮 -->
		<view class="btn-settle">结算({{checkedCount}})</view>
	</view>
</template>

<script>
	import { mapGetters,mapMutations } from 'vuex'
	export default {
		name:"my-settle",
		data() {
			return {
				
			};
		},
		computed:{
			...mapGetters('m_cart',['checkedCount','total','checkedGoodsAmount']),
			isFullChecked(){
				return this.total === this.checkedCount
			}
		},
		methods:{
			...mapMutations('m_cart',['updateAllGoodsState']),
			changeAllState(){
				this.updateAllGoodsState(!this.isFullChecked)
			}
		}
	}
</script>

<style lang="scss">
.my-settle-container{
	position: fixed;
	z-index: 999;
	bottom: 0;
	left: 0;
	width: 100%;
	height: 60px;
	background-color: white;
	display: flex;
	justify-content: space-between;
	align-items: center;
	font-size: 14px;
	padding-left: 5px;
	.radio{
		display: flex;
		align-items: center;
	}
	.amount-box{
		.amount{
			color: #C00000;
			font-weight: bold;
		}
	}
	.btn-settle{
		background-color: #C00000;
		height: 60px;
		color: white;
		line-height: 60px;
		padding: 0 10px;
		min-width: 100px;
		text-align: center;
	}
}
</style>

在vuex中调用数据和方法,具体操作如下:

实现的功能为:

当然如果购物车界面一件商品都没有的话,展示个图片显示无数据即可,这里使用v-if和v-else即可

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STM32 W25QXX芯片

W25QXX芯片介绍 W25QXX芯片是华邦公司推出的大容量SPI FLASH产品&#xff0c;该系列有W25Q16/32/62/128等。本例程使用W25Q64&#xff0c;W25Q64容量为64Mbits&#xff08;8M字节&#xff09;&#xff1a;8MB的容量分为128个块(Block)&#xff08;块大小为64KB&#xff09;&…

Python每日一练(20230413)

目录 1. 最后一个单词的长度 ※ 2. 全排列 &#x1f31f;&#x1f31f; 3. 计数质数 ※ &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日一练 专栏 1. 最后一个单词的长度 给你一个字符串 s&…

clickhouse布隆过滤器跳数索引最佳实践

背景 本文来聊一下clickhouse的这个列式存储数据库的布隆过滤器的跳数索引类型,来了解它的数据结构&#xff0c;它可以为那些查询类型提供查询优化。 跳数索引-布隆过滤器 首先布隆过滤器家族的跳数索引分成三种类型&#xff1a; ngrambf_v1,tokenbf_v1,bloom_filter其原理是…

Visual Studio 2022如何安装和使用MSDN

我是荔园微风&#xff0c;作为一名在IT界整整25年的老兵&#xff0c;在后台收到提问&#xff0c;问我Visual Studio 2022如何安装和使用MSDN&#xff0c;这个我之前也没有在这个版本上装过MSDN&#xff0c;我之前是在Visual Studio 2017版上装过MSDN&#xff0c;那既然有人问了…

蓝桥杯【第14届省赛】Python B组

测试链接&#xff1a;https://www.dotcpp.com/oj/train/1093/ 测试结果还是蛮惨烈&#xff0c;主要原因有几个&#xff1a; C 语言网的时间限制是 3 s&#xff1a;但实际比赛的时间限制都是 10 s 起步&#xff0c;甚至有 30 s 的莫名其妙的运行报错&#xff1a;我不知道 D 题…

浅谈ChatGPT的关键技术与落地发展

分享嘉宾 | 刘焕勇 文稿整理 | William 1、从大规模语言模型看ChatGPT的起源与本质 ChatGPT可以拆开分为Chat和GPT去理解&#xff0c;前一个表示一种应用形式&#xff0c;后一个是生成式的模型。在百度百科里面定义为ChatGPT是人工智能技术驱动的自然语言处理工具&#xff0c…

数组与字符串C语言代码总结

《array》 arr.c #include <stdio.h>int main(int argc, char *argv[]) {int a[2][3]; int i, j;for (i 0; i < 2; i) {for (j 0; j < 3; j) printf("%p ", &a[i][j]); //验证二维数组连续性}printf("%p %…

d2l Markov序列模型

本节的任务是使用Markov模型对后续序列进行预测&#xff0c;使用sin函数&#xff0b;噪声绘制1000个样本点&#xff0c;取tau为4&#xff0c;即利用后四个的信息预测第五个。 目录 1.构造样本点 2.抽取iter 3.构造网络 4.训练 5.预测 5.1单步 5.1多步 1.构造样本点 T …

【程序人生】5个月从职场打杂到月薪14000的女测试工程师逆袭之路

大家好&#xff0c;我是来自湖南的一位辣妹子&#xff0c;毕业于一所工业大学&#xff0c;大学的专业是软件与工程&#xff0c;其实也算是本专业&#xff0c;大学期间掌握的知识也算比较广&#xff0c;各个方面都会一丢丢&#xff0c;就是不是特别深入。 之所以这么说&#xf…

nginx配置文件介绍

nginx配置文件介绍 nginx默认的配置文件是在安装目录下的 conf目录下&#xff0c;后续对 nginx 的使用基本上都是对此配置文件进行相应的修改。 配置文件中用#符号表示注释内容。 配置文件主要包括三部分&#xff0c;main、events和http main 用于进行nginx全局信息的配置…

Netty应用篇

Netty应用 粘包和半包 服务器代码 public class StudyServer {static final Logger log LoggerFactory.getLogger(StudyServer.class);void start() {NioEventLoopGroup boss new NioEventLoopGroup(1);NioEventLoopGroup worker new NioEventLoopGroup();try {ServerBoo…

【WebRTC技术专题】未来可期,WebRTC的诞生发展的概述介绍(1)

近几年实时音视频通信应用呈现出了大爆发的趋势。在这些实时通信技术的背后&#xff0c;有一项不得不提的技术 ——WebRTC。 前言背景 2021年1月26日&#xff0c;W3C&#xff08;万维网联盟&#xff09; 和 IETF &#xff08;互联网工程任务组&#xff09; 同时宣布 WebRTC&…

企业办公WLAN覆盖方案的设计与实现_kaic

企业办公WLAN覆盖方案的设计与实现 摘要&#xff1a; 无线LAN技术的快速发展已经使它在当今的数字通讯行业中变得越来越重要。它的优点包括易于部署、灵活操作、价格实惠&#xff0c;使它能够在不同的场景中提供支持。无线LAN技术已经被许多不同类型的人所接受&#xff0c;并且…

linux下使用lftp的小结

lftp的功能比较强大&#xff0c;相比原来用ftp&#xff0c;方便了很多。 1、登陆&#xff1a; lftp ftp://yournamesite pwd:***** 或 open ftp://yournamesite 2、基本操作&#xff08;转&#xff09; lftp使用介绍 lftp 是一个功能强大的下载工具&#xff0c;它支持访问…

React-Native 创建App项目

# React-Native 创建App项目 环境搭建 概述 RN的官方网站百度谷歌 安装环境介绍 操作系统&#xff1a;win10系统手机&#xff1a;安卓手机真机一部或夜神模拟器必须安装的依赖有&#xff1a;Node,JDK,Yarn,Android SDK,Python2 Node的安装 先到官网去下载node版本&#…

Cypress触摸芯片自己做的demo 代码

1.前言 &#xff08;1&#xff09;cyprees芯片主要是可以做一些触摸的检测并实现一些IO输出&#xff0c;使用的工具psoc creater &#xff08;2&#xff09;psoc creater 可以i直接通过GUI的方式配置一些GPIO的状态以及集成的功能模块&#xff0c;编译后&#xff0c;我们可直接…

基于深度学习的花卉识别

1、数据集 春天来了&#xff0c;我在公园的小道漫步&#xff0c;看着公园遍野的花朵&#xff0c;看起来真让人心旷神怡&#xff0c;一周工作带来的疲惫感顿时一扫而光。难得一个糙汉子有闲情逸致俯身欣赏这些花朵儿&#xff0c;然而令人尴尬的是&#xff0c;我一朵都也不认识。…

2022蓝桥杯省赛——砍竹子

问题描述 这天, 小明在砍竹子&#xff0c; 他面前有 n 棵竹子排成一排&#xff0c;一开始第 i 棵竹子的 高度为 hi​。 他觉得一棵一棵砍太慢了&#xff0c; 决定使用魔法来砍竹子。魔法可以对连续的一 段相同高度的竹子使用&#xff0c; 假设这一段竹子的高度为 H&#xff0…

UNIX环境高级编程——系统数据文件和信息

6.1 引言 UNIX系统的正常运行需要使用大量与系统有关的数据文件&#xff0c;这些文件都是ASCII文本文件&#xff0c;并且使用标准I/O库读这些文件。 6.2 口令文件 UNIX口令文件是/etc/passwd&#xff0c;每一行包含下图中的各字段&#xff0c;字段之间用冒号分隔&#xff0c…

除了Jira、禅道还有哪些更好的敏捷开发过程管理平台?

无论是从国内的敏捷调研开发调研报告还是从国外的敏捷状态调查&#xff0c;工具支持一直是决定敏捷成功的关键因素之一&#xff0c;它们可以帮助团队提高软件开发的效率、质量、协作和满意度。选择合适的敏捷开发管理工具&#xff0c;并正确地使用它们&#xff0c;是每个敏捷团…