黑马 小兔鲜儿 uniapp 小程序开发- 分类模块- day04

news2025/1/22 13:11:19

 黑马 小兔鲜儿 uniapp 小程序开发- 推荐模块- day03_软工菜鸡的博客-CSDN博客

本课程是全网首套用 vue3 加 TS 写的 uniapp 项目, 里面大量封装自己的组件库,课程从 uni-app 基础入手,按照9大电商业务模块逐步实现完整的电商购物流程业务;涵盖了猜你喜欢、热门推荐、商品分类、商品详情、微信登录、用户管理、地址管理、购物车管理、订单管理等功能。包含微信登录,微信支付等业务。一套代码多端全面覆盖微信小程序端、H5端、APP端。

学完本课程能够收获:使用 uni-app + Vue3 开发中型项目的能力

小兔鲜儿 - 分类模块- day04

用户点击左菜单的一级分类,切换右侧对应的二级分类和商品。

准备工作

参考效果

商品分类页中的广告位,可复用之前定义的轮播图组件 XtxSwiper

静态结构

商品分类页静态结构: src/pages/category/category.vue

<script setup lang="ts">
//
</script>

<template>
  <view class="viewport">
    <!-- 搜索框 -->
    <view class="search">
      <view class="input">
        <text class="icon-search">女靴</text>
      </view>
    </view>
    <!-- 分类 -->
    <view class="categories">
      <!-- 左侧:一级分类 -->
      <scroll-view class="primary" scroll-y>
        <view
          v-for="(item, index) in 10"
          :key="item"
          class="item"
          :class="{ active: index === 0 }"
        >
          <text class="name"> 居家 </text>
        </view>
      </scroll-view>
      <!-- 右侧:二级分类 -->
      <scroll-view class="secondary" scroll-y>
        <!-- 焦点图 -->
        <XtxSwiper class="banner" :list="[]" />
        <!-- 内容区域 -->
        <view class="panel" v-for="item in 3" :key="item">
          <view class="title">
            <text class="name">宠物用品</text>
            <navigator class="more" hover-class="none">全部</navigator>
          </view>
          <view class="section">
            <navigator
              v-for="goods in 4"
              :key="goods"
              class="goods"
              hover-class="none"
              :url="`/pages/goods/goods?id=`"
            >
              <image
                class="image"
                src="https://yanxuan-item.nosdn.127.net/674ec7a88de58a026304983dd049ea69.jpg"
              ></image>
              <view class="name ellipsis">木天蓼逗猫棍</view>
              <view class="price">
                <text class="symbol">¥</text>
                <text class="number">16.00</text>
              </view>
            </navigator>
          </view>
        </view>
      </scroll-view>
    </view>
  </view>
</template>

<style lang="scss">
page {
  height: 100%;
  overflow: hidden;
}
.viewport {
  height: 100%;
  display: flex;
  flex-direction: column;
}
.search {
  padding: 0 30rpx 20rpx;
  background-color: #fff;
  .input {
    display: flex;
    align-items: center;
    justify-content: space-between;
    height: 64rpx;
    padding-left: 26rpx;
    color: #8b8b8b;
    font-size: 28rpx;
    border-radius: 32rpx;
    background-color: #f3f4f4;
  }
}
.icon-search {
  &::before {
    margin-right: 10rpx;
  }
}
/* 分类 */
.categories {
  flex: 1;
  min-height: 400rpx;
  display: flex;
}
/* 一级分类 */
.primary {
  overflow: hidden;
  width: 180rpx;
  flex: none;
  background-color: #f6f6f6;
  .item {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 96rpx;
    font-size: 26rpx;
    color: #595c63;
    position: relative;
    &::after {
      content: '';
      position: absolute;
      left: 42rpx;
      bottom: 0;
      width: 96rpx;
      border-top: 1rpx solid #e3e4e7;
    }
  }
  .active {
    background-color: #fff;
    &::before {
      content: '';
      position: absolute;
      left: 0;
      top: 0;
      width: 8rpx;
      height: 100%;
      background-color: #27ba9b;
    }
  }
}
.primary .item:last-child::after,
.primary .active::after {
  display: none;
}
/* 二级分类 */
.secondary {
  background-color: #fff;
  .carousel {
    height: 200rpx;
    margin: 0 30rpx 20rpx;
    border-radius: 4rpx;
    overflow: hidden;
  }
  .panel {
    margin: 0 30rpx 0rpx;
  }
  .title {
    height: 60rpx;
    line-height: 60rpx;
    color: #333;
    font-size: 28rpx;
    border-bottom: 1rpx solid #f7f7f8;
    .more {
      float: right;
      padding-left: 20rpx;
      font-size: 24rpx;
      color: #999;
    }
  }
  .more {
    &::after {
      font-family: 'erabbit' !important;
      content: '\e6c2';
    }
  }
  .section {
    width: 100%;
    display: flex;
    flex-wrap: wrap;
    padding: 20rpx 0;
    .goods {
      width: 150rpx;
      margin: 0rpx 30rpx 20rpx 0;
      &:nth-child(3n) {
        margin-right: 0;
      }
      image {
        width: 150rpx;
        height: 150rpx;
      }
      .name {
        padding: 5rpx;
        font-size: 22rpx;
        color: #333;
      }
      .price {
        padding: 5rpx;
        font-size: 18rpx;
        color: #cf4444;
      }
      .number {
        font-size: 24rpx;
        margin-left: 2rpx;
      }
    }
  }
}
</style>

渲染轮播图

接口调用

渲染轮播图数据业务功能对于前端来说比较简单,只需调用后端提供的接口将获得的数据展现。

注意:传递参数 2 标识获取商品分类页广告。

接口地址:/home/banner

请求方式:GET

请求参数:

Query:

字段名称

是否必须

默认值

备注

distributionSite

1

活动 banner 位置,1 代表首页,2 代表商品分类页,默认为 1

一级分类

获取数据

该接口同时包含一级分类和二级分类数据,二级分类数据需要先对数据进行处理,再进行渲染。

接口调用

接口地址:/category/top

请求方式:GET

请求参数:无

请求封装

// src/services/category.ts
/**
 * 分类列表-小程序
 */
export const getCategoryTopAPI = () => {
  return http<CategoryTopItem[]>({
    method: 'GET',
    url: '/category/top',
  })
}

类型声明

// src/types/category.d.ts
import type { GoodsItem } from './global'

/** 一级分类项 */
export type CategoryTopItem = {
  /** 二级分类集合[ 二级分类项 ] */
  children: CategoryChildItem[]
  /** 一级分类id */
  id: string
  /** 一级分类图片集[ 一级分类图片项 ] */
  imageBanners: string[]
  /** 一级分类名称 */
  name: string
  /** 一级分类图片 */
  picture: string
}

/** 二级分类项 */
export type CategoryChildItem = {
  /** 商品集合[ 商品项 ] */
  goods: GoodsItem[]
  /** 二级分类id */
  id: string
  /** 二级分类名称 */
  name: string
  /** 二级分类图片 */
  picture: string
}

接下来,先把一级分类数据结合模板语法渲染到页面中。

Tab 交互

当用户点击一级分类时,需要高亮显示,即给它添加 .active 类名即可。

<script setup lang="ts">
import { getCategoryTopAPI } from '@/services/category'
import type { CategoryTopItem } from '@/types/category'
import { onLoad } from '@dcloudio/uni-app'
import { ref } from 'vue'

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

// 高亮下标
const activeIndex = ref(0)

// 页面加载
onLoad(() => {
  getCategoryTopData()
})
</script>

<template>
  <view class="viewport">
    <!-- 分类 -->
    <view class="categories">
      <!-- 左侧:一级分类 -->
      <scroll-view class="primary" scroll-y>
        <view
          class="item"
          v-for="(item, index) in categoryList"
          :key="item.id"
          :class="{ active: index === activeIndex }"
          @tap="activeIndex = index"
        >
          {{ item.name }}
        </view>
      </scroll-view>
    </view>
  </view>
</template>

二级分类

商品二级分类是从属于某个一级分类的,通过 computed 配合高亮下标提取当前二级分类数据。

参考代码

<script setup lang="ts">
import { computed } from 'vue'

// ...省略

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

<template>
  <view class="viewport">
      <!-- ...省略 -->
      <!-- 右侧:二级分类 -->
      <scroll-view class="secondary" scroll-y>
        <!-- 焦点图 -->
        <XtxSwiper class="banner" :list="bannerList" />
        <!-- 内容区域 -->
        <view class="panel" v-for="item in subCategoryList" :key="item.id">
          <view class="title">
            <text class="name">{{ item.name }}</text>
            <navigator class="more" hover-class="none">全部</navigator>
          </view>
          <view class="section">
            <navigator
              v-for="goods in item.goods"
              :key="goods.id"
              class="goods"
              hover-class="none"
              :url="`/pages/goods/goods?id=${goods.id}`"
            >
              <image class="image" :src="goods.picture"></image>
              <view class="name ellipsis">{{ goods.name }}</view>
              <view class="price">
                <text class="symbol">¥</text>
                <text class="number">{{ goods.price }}</text>
              </view>
            </navigator>
          </view>
        </view>
      </scroll-view>
    </view>
  </view>
</template>

提取当前二级分类数据后,剩下的就是列表渲染。

骨架屏

参考效果

实现步骤参考首页的骨架屏。

代码参考(总)

商品分类页

<script setup lang="ts">
import { getCategoryTopAPI } from '@/services/category'
import { getHomeBannerAPI } from '@/services/home'
import type { CategoryTopItem } from '@/types/category'
import type { BannerItem } from '@/types/home'
import { onLoad } from '@dcloudio/uni-app'
import { computed, ref } from 'vue'
import PageSkeleton from './components/PageSkeleton.vue'

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

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

// 是否数据加载完毕
const isFinish = ref(false)
// 页面加载
onLoad(async () => {
  await Promise.all([getBannerData(), getCategoryTopData()])
  isFinish.value = true
})

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

<template>
  <view class="viewport" v-if="isFinish">
    <!-- 搜索框 -->
    <view class="search">
      <view class="input">
        <text class="icon-search">女靴</text>
      </view>
    </view>
    <!-- 分类 -->
    <view class="categories">
      <!-- 左侧:一级分类 -->
      <scroll-view class="primary" scroll-y>
        <view
          v-for="(item, index) in categoryList"
          :key="item.id"
          class="item"
          :class="{ active: index === activeIndex }"
          @tap="activeIndex = index"
        >
          <text class="name">
            {{ item.name }}
          </text>
        </view>
      </scroll-view>
      <!-- 右侧:二级分类 -->
      <scroll-view class="secondary" scroll-y>
        <!-- 焦点图 -->
        <XtxSwiper class="banner" :list="bannerList" />
        <!-- 内容区域 -->
        <view class="panel" v-for="item in subCategoryList" :key="item.id">
          <view class="title">
            <text class="name">{{ item.name }}</text>
            <navigator class="more" hover-class="none">全部</navigator>
          </view>
          <view class="section">
            <navigator
              v-for="goods in item.goods"
              :key="goods.id"
              class="goods"
              hover-class="none"
              :url="`/pages/goods/goods?id=${goods.id}`"
            >
              <image class="image" :src="goods.picture"></image>
              <view class="name ellipsis">{{ goods.name }}</view>
              <view class="price">
                <text class="symbol">¥</text>
                <text class="number">{{ goods.price }}</text>
              </view>
            </navigator>
          </view>
        </view>
      </scroll-view>
    </view>
  </view>
  <!-- 骨架屏 -->
  <PageSkeleton v-else />
</template>

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

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

相关文章

技术人员应该使用那种搜索引擎?

built-in.o是Linux内核中的组件 下面是三种主流搜索引擎的搜索结果&#xff0c;请参考&#xff0c;一切尽在不言中。

CSS - 快速实现悬浮吸顶,当页面滑动一定距离时固定吸附在顶部(position: sticky)

效果图 如下图所示&#xff0c;利用 position: sticky 属性轻松实现。 示例代码 新建一个 *.html 文件&#xff0c;一键复制运行起来。 <body><section class"content"><div class"item">我是悬浮吸顶区域</div><h1>我是…

【leetcode 力扣刷题】栈和队列的基础知识 + 栈的经典应用—匹配

栈和队列的基础知识 栈的经典应用—匹配 栈和队列基础知识232. 用栈实现队列225. 用队列实现栈 20. 有效的括号1047. 删除字符串中的所有相邻重复项 栈和队列基础知识 数据结构课程介绍线性结构的时候&#xff0c;介绍有线性表、链表、栈和队列。线性表&#xff0c;比如array…

室内探索无人机,解决复杂环境下的任务挑战!

前言 室内探索无人机是一种专为在室内环境中进行任务的无人机系统。相比传统的人员部署&#xff0c;室内探索无人机具有更高的灵活性和机动性&#xff0c;能够在复杂的室内环境中执行任务&#xff0c;用于未知环境的探索和特定目标的搜索。 为完成无人机室内搜索与识别等复杂…

无缝数据传输:StreamSet安装部署的最佳实践

文章目录 概要安装方法尝试安装部署方案1. 下载datacollector镜像2. 启动容器3. 访问streamsets小结 概要 StreamSets是一款流数据集成平台&#xff0c;旨在帮助用户轻松地收集、处理和传输大规模数据流。它提供了直观的界面和强大的功能&#xff0c;可用于实时数据流的提取、…

无线测温系统在运行时怎么判断配电设备出现故障?

现如今&#xff0c;电力测温方面使用无线测温系统的使用范围越来越大&#xff0c;比较熟悉的人都了解&#xff0c;传统的温度测量方式周期长、施工复杂&#xff0c;效率低&#xff0c;不便于管理&#xff0c;发生故障时要耗费大量的人力物理排查和重新铺设线缆。而在特定场合下…

若依+lodop+jasperreports+ireport 设计打印票据格式(一)

若依lodopjasperreportsireport 设计打印票据格式 一、设计表格 官网下载ireport5.6&#xff0c;解压安装&#xff0c;jdk 1.7适配ireport5.6&#xff0c;jdk1.8不适配 安装好jdk1.7(不用配置path&#xff0c;安装好就行)&#xff0c;进入ireport5.6安装目录&#xff0c;找到…

异步FIFO设计的仿真与综合技术(2)

概述 本文主体翻译自C. E. Cummings and S. Design, “Simulation and Synthesis Techniques for Asynchronous FIFO Design 一文&#xff0c;添加了笔者的个人理解与注释&#xff0c;文中蓝色部分为笔者注或意译。前文链接&#xff1a;异步FIFO设计的仿真与综合技术&#xff0…

模态分析的概念。C++减振器设计。

一、说明 模态分析是工程和物理学中用于研究系统或结构动态特性的技术。它涉及分析系统的振动或振荡的自然模式以及相应的频率、阻尼系数和振型。 在模态分析中&#xff0c;所研究的系统通常表示为一组质量、刚度和阻尼元件&#xff08;在下面的文章中忽略了阻尼&#xff09;。…

ARTS 打卡 第一周,初试ARTS

前言 认识三掌柜的想必都知道&#xff0c;我持续创作技术博客已经有6年时间了&#xff0c;固定每个月发布不少于6篇博文。同时&#xff0c;自己作为一名热爱分享的开发者&#xff0c;像ARTS这样的活动自然少不了我。由于我是打算挤在一起分享&#xff0c;之前都是做了本地文档记…

大数据Flink(七十九):SQL 的容错(Checkpoint)

文章目录 SQL 的容错(Checkpoint) 一、Checkpoint介绍

华为云云耀云服务器L实例评测|云耀云服务器L实例部署SpaceHuggers网页小游戏

华为云云耀云服务器L实例评测&#xff5c;云云耀云服务器L实例部署SpaceHuggers网页小游戏 一、云耀云服务器L实例介绍1.1 云耀云服务器L实例简介1.2 云耀云服务器L实例特点 二、SpaceHuggers小游戏介绍2.1 SpaceHuggers简介2.2 SpaceHuggers游戏玩法 三、实践环境介绍3.1 本次…

在UOS/Deepin下安装 Python 3.11.5 图文详解

01 先把操作系统更新一下 在开始菜单中&#xff0c;找到“终端”&#xff0c;点击启动&#xff0c;并依次输入以下两条命令即可&#xff1a; sudo apt update sudo apt upgrade 特别说明&#xff1a;Uos/Deepin 系统&#xff0c;要先进入“开发者模式”才行。“ 设置 — 通用…

如何用Polygon ID来证明你不是机器人?

1. 引言 喜剧演员约翰穆拉尼在最近的一个单口相声特辑中说&#xff1a;“世界是由机器人管理的&#xff0c;我们一天中的大部分时间都在告诉他们&#xff0c;我们不是一个仅仅登录并查看自己东西的机器人。” 这种经历很常见&#xff0c;从乏味的&#xff08;“找到所有的停车…

软件设计师--考点小总结

成绩展示 在这里插入图片描述 口诀篇 普密网–【图算法,普利姆算法,适合密网,所以与边无光;另外一个图算法就是有关了】 D-AES, 56-128–【DES,AES是对称加密的,而56就是对称加密的算法位数(三重DES,是112,恰好是56倍数),128则是非对称的位数】 排序 快一样,堆占1,…

入行IC| 数字IC设计和验证选哪个好?

很多初入IC行业的新人不知道选择验证还是设计&#xff0c;下面IC修真院就从技能&#xff0c;门槛等方面来为大家分析一下。 数字前端设计工程师是什么&#xff1f; 集成电路设计&#xff08;Integrated Circuit&#xff0c;简称IC&#xff09;一般分为数字IC设计、模拟IC设计…

macOS - 使用VLC

文章目录 关于 VLC安装查看帮助流媒体 MRL 语法:URL 语法:主程序 (core)音频视频截图:窗口属性: 子画面屏幕显示&#xff08;OSD&#xff09;:字幕:覆盖:轨道设置:播放控制:默认设备:高级: 输入播放列表性能选项: 热键跳跃大小: 关于 VLC VLC media player VLC 是一款自由、开…

5个鲜有人知的爬虫技巧

几点鲜有人知的爬虫技巧 技巧一 换个角度&#xff0c;解锁新姿势 在爬取某些 web 网站的时候&#xff0c;被各种反爬弄得哭天喊地。 什么几把 css 字体加密&#xff0c;什么几把 js 的 MD5 等&#xff0c;各种乱七八糟的加密&#xff0c;什么各种飞的验证 这时候&#xff0…

【汇编】计算机系统组成

【汇编】计算机系统组成 文章目录 【汇编】计算机系统组成冯诺依曼结构1. 总线2. 程序存储3. 存储器3.1地址线与字节3.2 读写逻辑 冯诺依曼结构 冯诺伊曼结构&#xff08;Von Neumann Architecture&#xff09;&#xff0c;又称存储程序计算机结构&#xff0c;是计算机体系结构…

VB:二分法查找

VB&#xff1a;二分法查找 二分查找算法 Private Sub Command1_Click()Dim i%, m%, n%Dim x(1 To 10) As SingleFor i 1 To 10x(i) Val(InputBox("请输入"))Next iCall bubbleSort(x)For i LBound(x) To UBound(x) LBound(x)和UBound(x)是用于获取数组x的下界和上…