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

news2024/12/23 20:45:49

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

🛵个人主页:亦世凡华、

🛺系列专栏: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) => void
  close: () => 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: string
  encryptedData: string
  iv: 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.coloradmin.cn/o/1066819.html

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

相关文章

八大排序详解(默认升序)

一、直接插入排序 直接插入排序&#xff1a;直接插入排序就是像打扑克牌一样&#xff0c;每张牌依次与前面的牌比较&#xff0c;遇到比自己大的就将大的牌挪到后面&#xff0c;遇到比自己小的就把自己放在它后面(如果自己最小就放在第一位)&#xff0c;所有牌排一遍后就完成了排…

PX4仿真添加world模型文件,并使用yolov8进行跟踪

前言 目的:我们是为了在无人机仿真中使用一个汽车模型,然后让仿真的无人机能够识别到这个汽车模型。所以我们需要在无人机仿真的环境中添加汽车模型。 无人机仿真中我们默认使用的empty.world文件,所以只需要将我们需要的模型添加到一起写进这个empty.world文件中去就可以…

电脑多开微信教程,可以多开n个

下载地址 链接&#xff1a;https://pan.baidu.com/s/1uWXIhfTZ-aD0A4RBxrI8bg?pwdy2s5 提取码&#xff1a;y2s5 效果如图&#xff1a;

地下水数值模拟软件如何选择?GMS、Visual MODFLOW Flex、FEFLOW、MODFLOW

强调模块化教学&#xff0c;分为前期数据收集与处理&#xff1b;三维地质结构建模&#xff1b;地下水流动模型构建&#xff1b;地下水溶质运移模型构建和反应性溶质运移构建5个模块&#xff1b;采用全流程模式将地下水数值模拟软件GMS的操作进行详细剖析和案例联系。不仅使学员…

Android中的RxJava入门及常用操作符

文章目录 1.定义2.作用3.特点4.使用4.1创建被观察者&#xff08;Observable&#xff09;4.2创建观察者&#xff08;Observer&#xff09;4.3订阅&#xff08;Subscribe&#xff09;4.4Dispose 5.操作符5.1操作符类型5.2just操作符5.2链式调用5.3 fromArray操作符5.4 fromIterab…

服务器文件备份

服务器上&#xff0c;做好跟应用程序有关的文件备份&#xff08;一般备份到远程的盘符&#xff09;&#xff0c;有助于当服务器发生硬件等故障时&#xff0c;可以对系统进行进行快速恢复。 下面以Windows服务器为例&#xff0c;记录如何做文件的备份操作。 具体操作如下&#…

贷款行业,教你如何直接沟通客户

信贷行业拓展业务的人力与时间成本非常高。如是做小微贷款业务的公司可能在寻找贷款客户、筛选客户资质这两项初始工作上花掉超过50%的精力。 并且由于行业特殊性&#xff0c;金融信贷受政策的影响比较大&#xff0c;没法形成固定的推广渠道&#xff0c;线上营销不好做&#x…

什么是站内搜索引擎?如何在网站中加入站内搜索功能?

在当今数字时代&#xff0c;用户体验对于网站的成功起着至关重要的作用。提升用户体验和改善整体网站性能的一种方法是引入站内搜索引擎。站内搜索引擎是一种强大的工具&#xff0c;它的功能类似于Google或Bing等流行搜索引擎&#xff0c;但它专注于实施自己网站上的内容。用户…

工业路由器项目应用(4g+5g两种工业路由器项目介绍)

引言&#xff1a; 随着工业智能化的不断发展&#xff0c;工业路由器在各个领域的应用越来越广泛。本文将介绍两个工业路由器项目的应用案例&#xff0c;一个是使用SR500 4g工业路由器&#xff0c;另一个是使用SR800 5g工业路由器。 详情&#xff1a;https://www.key-iot.com/i…

IPO观察丨重新启动上市,“小而美”能让科迪乳业再次出圈吗?

如今&#xff0c;乳制品市场俨然是一片红海&#xff0c;尽管市场竞争激烈&#xff0c;但对于一些企业而言&#xff0c;发展机会仍然相当可观。 近日举办的2023年中工作会议上&#xff0c;科迪乳业母公司科迪集团对外表示&#xff0c;要部署好下个阶段的重点工作&#xff0c;为…

Jenkins 添加节点Node报错JNI error has occurred UnsupportedClassVersionError

节点日志 报错信息如下 Error: A JNI error has occurred, please check your installation and try again Exception in thread “main” java.lang.UnsupportedClassVersionError: hudson/remoting/Launcher has been compiled by a more recent version of the Java Runtime…

阿里云服务器通用算力型、经济型、七代云服务器实例、倚天云服务器实例区别参考

目前阿里云服务器的实例规格中&#xff0c;既有五代六代实例规格&#xff0c;也有七代和八代倚天云服务器&#xff0c;同时还有通用算力型及经济型这些刚推出不久的新品云服务器实例&#xff0c;其中第五代实例规格已经不是主推的实例规格了&#xff0c;现在主售的实例规格是七…

windows server 2012 R2的C盘空间满了,但是找不到大文件的两种原因

目录 一、第一种原因&#xff1a;windows server backup备份导致C盘空间耗尽 二、第二种原因&#xff1a;超级桌管软件生成的文件放在C盘被隐藏 最近经历了两次C盘满了&#xff0c;但是又找不到大文件的问题&#xff0c;定位了许久&#xff0c;以下是两种原因。 一、第一种原…

python实现UI自动化配置谷歌浏览器驱动

web端UI自动化执行在哪个浏览器&#xff0c;需要对应哪个浏览器的驱动。以谷歌浏览器为例&#xff0c;进行配置。一、查看谷歌浏览器版本 如下截图&#xff1a;我的谷歌浏览器版本是&#xff1a; 117.0.5938.150 二、下载对应版本谷歌浏览器驱动 首先可以从其他版本驱动地址中…

超详细 | 鲸鱼优化算法原理及其实现(Matlab/Python)

鲸鱼优化算法(whale optimization algorithm,WOA)是由Mirjalili和Lewis[1]于2016年提出的一种新型群体智能优化搜索方法,它源于对自然界中座头鲸群体狩猎行为的模拟&#xff0c;该算法整个过程包含搜索觅食、收缩包围和螺旋更新位置三个阶段。 鲸鱼优化算法的三个种群更新机制…

为什么亚马逊速卖通等跨境卖家都选择自养号测评,有哪些优势

自养号测评是一种对于跨境电商卖家来说非常重要的运营手段。通过自养号测评&#xff0c;卖家可以快速增加产品的销量、评论数量&#xff0c;并提升在平台中的排名&#xff0c;从而促进产品的流量转化和订单增长。自养号测评与传统的机刷方式不同&#xff0c;它通过伪装设备参数…

Linux TCP 通信并发

多进程 客户端 #include <stdio.h> #include <arpa/inet.h> #include <unistd.h> #include <string.h> #include <stdlib.h> int main() {//创建套接字int lfd socket(AF_INET, SOCK_STREAM, 0);if(lfd -1) {perror("socket");exi…

突发,美国再将42家中企列入实体名单 | 百能云芯

2023年10月6日&#xff0c;美国商务部工业和安全局&#xff08;BIS&#xff09;宣布对实体名单进行了更新&#xff0c;涉及到新增了49个实体。这些实体来自七个不同的国家&#xff0c;其中42家位于中国&#xff0c;其余七家分别位于爱沙尼亚、芬兰、德国、印度、土耳其、阿拉伯…

知识图谱系列Paper 1:Open-CyKG: An Open Cyber Threat Intelligence Knowledge Graph

向前进&#xff01; 一、摘要 Instant analysis of cybersecurity reports is a fundamental challenge for security experts as an immeasurable amount of cyber information is generated on a daily basis, which necessitates automated information extraction tools t…