uni-app--》基于小程序开发的电商平台项目实战(四)

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

🛵个人主页:亦世凡华、

🛺系列专栏:uni-app

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

👀引言

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

今天开始使用 vue3 + uni-app 搭建一个电商购物的小程序,因为文章会将项目的每一个地方代码的书写都会讲解到,所以本项目会分成好几篇文章进行讲解,我会在最后一篇文章中会将项目代码开源到我的GitHub上,大家可以自行去进行下载运行,希望本文章对有帮助的朋友们能多多关注本专栏,学习更多前端uni-app知识。然后开篇先简单介绍一下本项目用到的技术栈都有哪几个方面(阅读此次项目实践文章能够学习到的技术):

uni-app:跨平台的应用开发框架,基于vue.js可以一套代码同时构建运行在多个平台。

pnpm:高性能、轻量级npm替代品,帮助开发人员更加高效地处理应用程序的依赖关系。

vue3:vue.js最新版本的用于构建用户界面的渐进式JavaScript框架。

typescript:JavaScript的超集,提供了静态类型检查,使得代码更加健壮。

pinia:vue3构建的Vuex替代品,具有响应式能力,提供非常简单的 API,进行状态管理。

uni-ui:基于vue.js和uni-app的前端UI组件库,开发人员可以快速地构建跨平台应用程序。

如果是第一次接触uni-app并且想学习uni-app的朋友,我是不建议直接从此次实战项目开始看起,可以先阅读一下我以前的基础文章:什么是uniapp?如何开发uniapp?按部就班的学习可以让学习变得更轻松更容易上手哦,闲话少说我们直接开始今天的uni-app实战篇。

目录

实现商品分类页面

实现商品详情页面

实现登录模块的功能


实现商品分类页面

接下来我们开始实现商品分类页面的静态搭建,首先我们先编写相应的接口函数来获取分类列表的数据:

// 分类页面的相关api函数
import type { CategoryTopItem } from '@/types/category'
import { http } from '@/utils/http'
/* ** 分类列表数据函数*/
export const getCategoryTopAPI = () => {return http<CategoryTopItem[]>({method: 'GET',url: '/category/top',})
}

编写完接口函数之后,接下来我们在分类组件页面中调用该接口函数,将获取到的数据存储在响应式数据ref当中:

// 获取分类列表的数据
let categoryList = ref<CategoryTopItem[]>([])
const getCategoryTopData = async () => {const res = await getCategoryTopAPI()categoryList.value = res.result
}

这里我们设置了一个滚动容器用来放置左侧的导航栏当中,通过动态设置其class属性来设置其点击之后索引值的改变,从而改变其点击激活之后的样式,所以这里我们一开始也需要先设置一下初始的响应式ref的数据:

let activeIndex = ref(0)

接下来我们在导航栏左侧也放置一个滚动容器,用于存放相应的轮播图和对应的产品内容,轮播图我们一开始就设置了全局组件,这里我们直接调用该轮播图的全局组件即可

<Swiper class="banner" :list="bannerList" />

因为我们设置了全局组件需要我们手动去传递相应的props值,所以这里我们仍然需要再次调用之前首页定义好的获取轮播图数据的函数:

// 获取轮播图数据
let bannerList = ref<BannerItem[]>([])
const getBannerData = async () => {const res = await getHomeBanner(2)bannerList.value = res.result
}

轮播图处理完成之后,接下来我们开始处理轮播图下面的内容区域,这里我们通过计算属性来提取我们分类列表下的children属性,该分类列表的数据数组下标是我们通过手动点击来动态改变其相应的下标的:

// 提取当前二级分类数据
const subCategoryList = computed(() => {return categoryList.value[activeIndex.value]?.children || []
})

然后将我们获取到的相应数据通过v-for来展示即可,具体的页面排版及其相应布局如下:

最终呈现的结果如下所示:

细心的朋友可以会发现,分类页面的一级分类和二级分类的数据都是在一个接口里面,所以这里的请求数据的时间会比较长,所以这里我们制作一个骨架屏用来展示数据未完全加载出来时的一个页面状态的展示,防止整个页面因为数据没有完全加载出来时出现白屏的一个状态,关于制作骨架屏我们在上文讲解首页功能实现的时候已经讲解过了,这里再简单的讲解一下吧。

我们通过微信开发者工具自动生成骨架屏的按钮,生成相应的wxml和wxss文件,我们将其原生的语法复制并改变到我们的uni-app代码上面,然后通过定义一个响应式ref变量用来判断当前的组件是否加载完毕,加载完成返回true:

// 是否组件加载完毕
const isFinish = ref<boolean>(false)
// 页面初始化加载函数
onLoad(async () => {await Promise.all([getBannerData(), getCategoryTopData()])isFinish.value = true
})

然后这里我们导入骨架屏组件通过v-if和v-else来展示相应的组件:

最终呈现的效果如下:

实现商品详情页面

接下来我们实现点击商品图片进入到该商品详情页面的功能,首先我们需要在pages文件夹下新建uniapp页面,接下来我们开始编写相应的接口函数,因为当我们点击商品图片的时候为了区分是哪一件商品我们都会传递相应的id值来作为区分,所以这里的接口函数我们也需要传入形参值id:

import type { GoodsResult } from '@/types/goods'
import { http } from '@/utils/http'/* ** 商品详情* @param id 商品id*/
export const getGoodsByIdAPI = (id: string) => {return http<GoodsResult>({method: 'GET',url: '/goods',data: {id,},})
}

这里的TS类型是根据后端返回给我们的数据嵌套来编写相应的ts类型,这里就不再赘述了。

在分类和首页页面上,凡是涉及到商品图片内容的都是需要通过 navigator 下的 url 属性进行页面的跳转和参数的传递的。

所以接下来我们通过 defineProps 的方式来获取到相应的id值,并作为实参调用获取商品详情数据的接口函数,然后存放到响应式ref当中,接着调用onLoad函数在页面刚加载的使用就调用:

// 接受页面参数
const query = defineProps<{id: string
}>()
// 获取商品详情信息
const goods = ref<GoodsResult>()
const getGoodsByIdData = async () => {const res = await getGoodsByIdAPI(query.id)goods.value = res.result
}
// 页面加载时候调用
onLoad(() => {getGoodsByIdData()
})

将获取到的数据通过v-for指令遍历之后通过插值表达式的方式将数据进行一个动态的呈现:

在用户操作的页面设置模块,我们通过getSystemInfoSync函数获取屏幕边界到安全区域距离,然后根据手机的不同尺寸动态的展示其用户操作界面的内容:

// 获取屏幕边界到安全区域距离
const { safeAreaInsets } = uni.getSystemInfoSync()

最终呈现的结果如下:

这里有个问题就是商品详情页面的轮播图右下角的数字没有随着图片的改变而切换,这里需要我们单独的进行设置一下,还有点击图片要呈现一个预览的效果,这里也要简单的设置一下,这里我们可以分别给轮播组件设置chage事件监听其下标的变化,还有给图片设置点击事件来设置图片预览效果,如下:

轮播图监听其下标的变化将变化的值赋值给响应式变量ref当中:

// 轮播图变化时
const currentIndex = ref<number>(0)
const onChange: UniHelper.SwiperOnChange = (ev) => {currentIndex.value = ev.detail!.current
}

因为下标是从零开始的,所以这里的下标我们要加一才行,然后图片的总数这里不能写死了,所以这里的总数就是设置为图片数组的长度即可:

关于图片预览的效果,这里直接调用uni-app提供给我们的API接口,具体使用的详情请参考官方文档的讲解,这里不再赘述:

// 点击图片时
const onTapImage = (url: string) => {// 大图预览uni.previewImage({current: url,urls: goods.value!.mainPictures})
}

呈现的效果如下:

接下来实现用户点击操作面板后会进行弹出层数据的展示,具体的弹出层相关使用我们可以参考官方文档给我们提供的相关示例及其相应的属性的作用,这里不再赘述:

我们只需要调用弹出层的标签,通过获取弹出层的实例调用其相应的open和close函数即可:

<!-- uni-ui 弹出层 -->
<uni-popup ref="popup" type="bottom" background-color="#fff"><view>内容1</view><view>内容2</view><button @tap="popup?.close()">关闭弹出层</button>
</uni-popup><script>
// 保存弹出层的组件ref
const popup = ref<{open: (type?: UniHelper.UniPopupType) => voidclose: () => void
}>()
</script>

接下来我们开始正式编写相应的弹出层具体数据,这里我们将会该数据内容封装成一个组件,通过调用v-if来实现根据条件让弹出层展示不同的组件,然后通过自定义事件让子组件调用父组件的关闭弹出层的函数:

<!-- uni-ui 弹出层 -->
<uni-popup ref="popup" type="bottom" background-color="#fff"><AddressPanel v-if="popupName === 'address'" @close="popup?.close()" /><ServicePanel v-if="popupName === 'service'" @close="popup?.close()" />
</uni-popup>

最终呈现的效果如下:

实现登录模块的功能

接下来我们开始实现登录模块的功能,首先我们先编写相应的接口函数,这里有两个接口函数,第一个接口函数是企业开发的,个人开发者是使用不了的,所以这里我们编写了个人开发者的模拟登录接口:

import type { LoginResult } from '@/types/member'
import { http } from '@/utils/http'type LoginParams = {code: stringencryptedData: stringiv: string
}
/*** 小程序登录接口* @param data 请求参数*/
export const postLoginWxMin = (data: LoginParams) => {return http<LoginResult>({method: 'POST',url: '/login/wxMin',data,})
}
/*** 小程序内测版* @param phoneNumber 模拟手机号码*/
export const postLoginWxMinSimpkleAPI = (phoneNumber: string) => {return http<LoginResult>({method: 'POST',url: '/login/wxMin/simple',data: {phoneNumber,},})
}

具体的样式搭建如下:

<template><view class="viewport"><view class="logo"><image src="../../static/images/bg.jpg"></image></view><view class="login"><!-- 网页端表单登录 --><!-- <input class="input" type="text" placeholder="请输入用户名/手机号码" /> --><!-- <input class="input" type="text" password placeholder="请输入密码" /> --><!-- <button class="button phone">登录</button> --><!-- 小程序端授权登录 --><button class="button phone" open-type="getPhoneNumber" @getphonenumber="onGetphonenumber"><uni-icons type="personadd-filled" size="25" color="#fff"></uni-icons>手机号快捷登录</button><view class="extra"><view class="caption"><text>其他登录方式</text></view><view class="options"><!-- 通用模拟登录 --><button @tap="onGetphonenumberSimple"><text class="icon icon-phone">模拟快捷登录</text></button></view></view><view class="tips">登录/注册即视为你同意《服务条款》和《小兔鲜儿隐私协议》</view></view></view>
</template>

调用相应的两个接口函数,企业的写法虽然在这使用不了,但这里也是将相应的写法呈现出来了:

<script setup lang="ts">
import { postLoginWxMin, postLoginWxMinSimpkleAPI } from '@/api/login'
import { onLoad } from '@dcloudio/uni-app'// 获取 code 登录凭证
let code = '' // 声明全局变量
onLoad(async () => {const res = await wx.login()code = res.code
})
// 获取用户手机号码
const onGetphonenumber: UniHelper.ButtonOnGetphonenumber = async (ev) => {try {// 企业中的写法,个人开发者无法调用const encryptedData = ev.detail!.encryptedData!const iv = ev.detail!.iv!const res = await postLoginWxMin({code,encryptedData,iv,})console.log(res)} catch (error) {uni.showToast({icon: 'none',title: '登录失败,更换登录方式',})}
}
// 模拟手机号码快捷登录(开发练习)
const onGetphonenumberSimple = async () => {const res = await postLoginWxMinSimpkleAPI('13123456789')console.log(res)uni.showToast({ title: '登录成功', icon: 'success' })
}
</script>

最终呈现的效果如下:

写好初始的登录模块之后,接下面我们需要将获取到的登录信息存储到仓库当中进行持久化,然后再执行登录成功之后进行页面的跳转,关于仓库的设置,在第一篇文章已经讲解过了,这里不再赘述,如下:

我们只需要在登录模块的组件中封装如下函数:

const loginSuccess = (profile: LoginResult) => {// 保存用户信息const memberStore = useMemberStore()memberStore.setProfile(profile)// 成功提示uni.showToast({ icon: 'success', title: '登录成功' })// 页面跳转setTimeout(() => {uni.switchTab({ url: '/pages/my/my' })}, 500)
}

在登录模块组件中的登录函数中调用上面的封装函数,传递相应的用户信息值即可实现用户信息的持久化以及相应的页面跳转:

本项目分类页面、商品详情页面以及登录页面的一些基本功能的搭建就讲解到这,下一篇文章将继续讲解项目其他页面操作,关注博主学习更多前端uni-app知识,您的支持就是博主创作的最大动力!

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

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

相关文章

【juc】countdownlatch实现并发网络请求

目录 一、截图示例二、代码示例2.1 测试代码2.2 接口代码 一、截图示例 二、代码示例 2.1 测试代码 package com.learning.countdownlatch;import lombok.extern.slf4j.Slf4j; import org.springframework.web.client.RestTemplate;import java.util.Arrays; import java.uti…

SpringSecurity源码学习一:过滤器执行原理

目录 1. web过滤器Filter1.1 filter核心类1.2 GenericFilterBean1.3 DelegatingFilterProxy1.3.1 原理1.3.2 DelegatingFilterProxy源码 2. FilterChainProxy源码学习2.1 源码2.1.1 doFilterInternal方法源码2.1.1.1 getFilters()方法源码2.1.1.2 VirtualFilterChain方法源码 3…

Python-Scrapy框架(框架学习)

一、概述 Scrapy是一个用于爬取网站数据的Python框架&#xff0c;可以用来抓取web站点并从页面中提取结构化的数据。 基本组件&#xff1a; 引擎(Engine)&#xff1a;负责控制整个爬虫的流程&#xff0c;包括调度请求、处理请求和响应等。 调度器(Scheduler)&#xff1a;负责…

JVM(八股文)

目录 一、JVM简介 二、JVM中的内存区域划分 三、JVM加载 1.类加载 1.1 加载 1.2 验证 1.3 准备 1.4 解析 1.5 初始 1.6 总结 2.双亲委派模型 四、JVM 垃圾回收&#xff08;GC&#xff09; 1.确认垃圾 1.1 引用计数 1.2 可达性分析&#xff08;Java 采用的方案&a…

Ubuntu Server CLI专业提示

基础 网络 获取所有接口的IP地址 networkctl status 显示主机的所有IP地址 hostname -I 启用/禁用接口 ip link set <interface> up ip link set <interface> down 显示路线 ip route 将使用哪条路线到达主机 ip route get <IP> 安全 显示已登录的用户 w…

mysql面试题10:MySQL中有哪几种锁?表级锁、行级锁、页面锁区别和联系?

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:Mysql中有哪几种锁? 在MySQL中,主要有以下几种类型的锁: 共享锁(Shared Lock):也称为读锁。多个事务可以同时持有共享锁,可以读取但不能修…

基于LADRC自抗扰控制的VSG三相逆变器预同步并网控制策略(Simulink仿真实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

苹果手机的祛魅时刻,国产厂商的颠覆征程

“iPhone翻车了&#xff1f;”有网友如此质疑。 发布未满一个月&#xff0c;iPhone 15系列多次因负面问题登上热搜。 首先曝出钛金属边框容易沾染指纹的情况&#xff0c;尚未涉及功能性方面&#xff0c;但后续接连曝出发热严重、电池循环次数低、外放破音、Wi-Fi链接缓慢的问…

【算法基础】一文掌握十大排序算法,冒泡排序、插入排序、选择排序、归并排序、计数排序、基数排序、希尔排序和堆排序

目录 1 冒泡排序&#xff08;Bubble Sort&#xff09; 2 插入排序&#xff08;Insertion Sort&#xff09; 3 选择排序&#xff08;Selection Sort&#xff09; 4. 快速排序&#xff08;Quick Sort&#xff09; 5. 归并排序&#xff08;Merge Sort&#xff09; 6 堆排序 …

RK3568的CAN驱动适配

目录 背景&#xff1a; 1.内核驱动模块配置 2.设备树配置 3.功能测试 4.bug修复 背景&#xff1a; 某个项目上使用RK3568的芯片&#xff0c;需要用到4路CAN接口进行通信&#xff0c;经过方案评审后决定使用RK3568自带的3路CAN外加一路spi转的CAN实现功能&#xff0c;在这个…

【Java-LangChain:使用 ChatGPT API 搭建系统-2】语言模型,提问范式与 Token

第二章 语言模型&#xff0c;提问范式与 Token 在本章中&#xff0c;我们将和您分享大型语言模型&#xff08;LLM&#xff09;的工作原理、训练方式以及分词器&#xff08;tokenizer&#xff09;等细节对 LLM 输出的影响。我们还将介绍 LLM 的提问范式&#xff08;chat format…

【计算机基础知识】字符的编码表示

欢迎来到我的&#xff1a;世界 希望作者的文章对你有所帮助&#xff0c;有不足的地方还请指正&#xff0c;大家一起学习交流 ! 目录 前言1.西文字符编码2.中文字符编码汉字输入码汉字国标码汉字机内码汉字字形码 总结 前言 计算机处理的数据中&#xff0c;除了数值型数据以外…

unity脚本_Input鼠标键盘 c#

获取鼠标坐标 检测鼠标输入 如果在运行游戏场景中点击一下鼠标左键 检测鼠标抬起 选中即可 检测键盘按下 当前屏幕分辨率 注意&#xff1a;获取的是显示器的分辨率 获取设备屏幕宽高 屏幕休眠模式 窗口/全屏模式 移动设备屏幕转向

10-Node.js入门

01.什么是 Node.js 目标 什么是 Node.js&#xff0c;有什么用&#xff0c;为何能独立执行 JS 代码&#xff0c;演示安装和执行 JS 文件内代码 讲解 Node.js 是一个独立的 JavaScript 运行环境&#xff0c;能独立执行 JS 代码&#xff0c;因为这个特点&#xff0c;它可以用来…

背靠背 HVDC-MMC模块化多电平转换器输电系统-用于无源网络系统的电能质量调节(Simulink仿真实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

《深度不确定条件下的决策:从理论到实践》PDF

制定未来计划时需要预测变化&#xff0c;尤其是制定长期计划或针对罕见事件的计划时。当这些变化存在高度不确定性的时候&#xff0c;这种预期就变得越来越困难。 今天给大家介绍的这本《深度不确定条件下的决策&#xff1a;从理论到实践》正是解决以上问题的良方。完整书籍文…

STM32--人体红外感应开关

本文主要介绍基于STM32F103C8T6和人体红外感应开关实现的控制算法 简介 人体红外模块选用HC-SR501人体红外传感器&#xff0c;人体红外感应的主要器件为人体热释电红外传感器。人体都有恒定的体温&#xff0c;一般在36~37度&#xff0c;所以会发出特定波长的红外线&#xff0…

企业电子招投标采购系统源码之电子招投标的组成

功能模块&#xff1a; 待办消息&#xff0c;招标公告&#xff0c;中标公告&#xff0c;信息发布 描述&#xff1a; 全过程数字化采购管理&#xff0c;打造从供应商管理到采购招投标、采购合同、采购执行的全过程数字化管理。通供应商门户具备内外协同的能力&#xff0c;为外部供…

手把手教你编写LoadRunner脚本

编写 LoadRunner 脚本需要熟悉脚本语言、业务场景、参数化技术、断言和事务等基础知识。 在实际编写时&#xff0c;可以根据具体测试需求&#xff0c;结合实际情况进行合理的配置和调整。 基本步骤 创建脚本 在 LoadRunner 的 Controller 模块中&#xff0c;创建一个新的测试…

数据结构——堆(C语言)

本篇会解决一下几个问题&#xff1a; 1.堆是什么&#xff1f; 2.如何形成一个堆&#xff1f; 3.堆的应用场景 堆是什么&#xff1f; 堆总是一颗完全二叉树堆的某个节点总是不大于或不小于父亲节点 如图&#xff0c;在小堆中&#xff0c;父亲节点总是小于孩子节点的。 如图&a…