uniapp 小兔鲜儿 - 首页模块(2)

目录

热门推荐

[首页 -- 热门推荐组件](#首页 – 热门推荐组件)

[首页 -- 获取热门推荐数据](#首页 – 获取热门推荐数据)

[首页 -- 热门推荐数据类型并渲染](#首页 – 热门推荐数据类型并渲染)

猜你喜欢

[首页 -- 猜你喜欢组件](#首页 – 猜你喜欢组件)

[首页 -- 获取猜你喜欢数据](#首页 – 获取猜你喜欢数据)

[首页 -- 猜你喜欢数据类型和渲染](#首页 – 猜你喜欢数据类型和渲染)

[首页 -- 猜你喜欢分页准备](#首页 – 猜你喜欢分页准备)

[首页 -- 猜你喜欢分页加载](#首页 – 猜你喜欢分页加载)

[首页 -- 猜你喜欢分页条件](#首页 – 猜你喜欢分页条件)

[首页 -- 下拉刷新](#首页 – 下拉刷新)

首页触发下拉刷新

猜你喜欢组件定义重置数据的方法

骨架屏


热门推荐

热门推荐功能,后端根据用户的消费习惯等信息向用户推荐的一系列商品,前端负责展示这些商品展示给用户。

参考效果

首页 -- 热门推荐组件

  1. 准备组件(只有首页使用)

热门推荐布局为独立的组件 HotPanel,属于首页的业务组件,存放到首页的 components 目录中。

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

<template>
  <!-- 推荐专区 -->
  <view class="panel hot">
    <view class="item" v-for="item in 4" :key="item">
      <view class="title">
        <text class="title-text">特惠推荐</text>
        <text class="title-desc">精选全攻略</text>
      </view>
      <navigator hover-class="none" url="/pages/hot/hot" class="cards">
        <image
          class="image"
          mode="aspectFit"
          src="https://pcapi-xiaotuxian-front-devtest.itheima.net/miniapp/uploads/goods_small_1.jpg"
        ></image>
        <image
          class="image"
          mode="aspectFit"
          src="https://pcapi-xiaotuxian-front-devtest.itheima.net/miniapp/uploads/goods_small_2.jpg"
        ></image>
      </navigator>
    </view>
  </view>
</template>

<style lang="scss">
/* 热门推荐 */
.hot {
  display: flex;
  flex-wrap: wrap;
  min-height: 508rpx;
  margin: 20rpx 20rpx 0;
  border-radius: 10rpx;
  background-color: #fff;

  .title {
    display: flex;
    align-items: center;
    padding: 24rpx 24rpx 0;
    font-size: 32rpx;
    color: #262626;
    position: relative;
    .title-desc {
      font-size: 24rpx;
      color: #7f7f7f;
      margin-left: 18rpx;
    }
  }

  .item {
    display: flex;
    flex-direction: column;
    width: 50%;
    height: 254rpx;
    border-right: 1rpx solid #eee;
    border-top: 1rpx solid #eee;
    .title {
      justify-content: start;
    }
    &:nth-child(2n) {
      border-right: 0 none;
    }
    &:nth-child(-n + 2) {
      border-top: 0 none;
    }
    .image {
      width: 150rpx;
      height: 150rpx;
    }
  }
  .cards {
    flex: 1;
    padding: 15rpx 20rpx;
    display: flex;
    justify-content: space-between;
    align-items: center;
  }
}
</style>
  1. 导入并使用组件

    import HotPanel from "./componets/HotPanel.vue";
    <HotPanel></HotPanel>

首页 -- 获取热门推荐数据

  1. 封装获取热门推荐数据API

    export const getHomeHotAPI = () => {
    return http<HotItem[]>({
    method: 'GET',
    url: '/home/hot/mutli',
    })
    }

  2. 页面初始化调用API

    import { onLoad } from "@dcloudio/uni-app";
    import { getHomeHotAPI } from "@/services/home";
    import type { HotItem } from "@/types/home";
    import { ref } from "vue";
    const getHomeHotData = async () => {
    const res = await getHomeHotAPI();
    hotList.value = res.result;
    };
    onLoad(() => {
    getHomeHotData();
    });

首页 -- 热门推荐数据类型并渲染

  1. 定义热门推荐数据类型

    /** 首页-热门推荐数据类型 /
    export type HotItem = {
    /
    * 说明 /
    alt: string
    /
    * id /
    id: string
    /
    * 图片集合[ 图片路径 ] /
    pictures: string[]
    /
    * 跳转地址 /
    target: string
    /
    * 标题 /
    title: string
    /
    * 推荐类型 */
    type: string
    }

  2. 指定类型并传值给子组件

    const hotList = ref<HotItem[]>([])
    <HotPanel :list="hotList" />

    import type { HotItem } from "@/types/home";
    defineProps<{
    list: HotItem[];
    }>();

  3. 渲染热门推荐数据

    <view class="panel hot"> <view class="item" v-for="item in list" :key="item.id"> <view class="title"> <text class="title-text">{{ item.title }}</text> <text class="title-desc">{{ item.alt }}</text> </view> <navigator hover-class="none" url="/pages/recommend/recommend" class="cards"> <image v-for="src in item.pictures" :key="src" :src="src"></image> </navigator> </view> </view>

猜你喜欢

参考效果

猜你喜欢功能,后端根据用户的浏览记录等信息向用户随机推荐的一系列商品,前端负责把商品在多个页面中展示

首页 -- 猜你喜欢组件

  1. 准备组件(通用组件)

猜你喜欢是一个通用组件 XtxGuess,多个页面会用到该组件,存放到 src/components 目录中。

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

<template>
  <!-- 猜你喜欢 -->
  <view class="caption">
    <text class="text">猜你喜欢</text>
  </view>
  <view class="guess">
    <navigator
      class="guess-item"
      v-for="item in 10"
      :key="item"
      :url="`/pages/goods/goods?id=4007498`"
    >
      <image
        class="image"
        mode="aspectFill"
        src="https://pcapi-xiaotuxian-front-devtest.itheima.net/miniapp/uploads/goods_big_1.jpg"
      ></image>
      <view class="name"> 德国THORE男表 超薄手表男士休闲简约夜光石英防水直径40毫米 </view>
      <view class="price">
        <text class="small">¥</text>
        <text>899.00</text>
      </view>
    </navigator>
  </view>
  <view class="loading-text"> 正在加载... </view>
</template>

<style lang="scss">
:host {
  display: block;
}
/* 分类标题 */
.caption {
  display: flex;
  justify-content: center;
  line-height: 1;
  padding: 36rpx 0 40rpx;
  font-size: 32rpx;
  color: #262626;
  .text {
    display: flex;
    justify-content: center;
    align-items: center;
    padding: 0 28rpx 0 30rpx;

    &::before,
    &::after {
      content: '';
      width: 20rpx;
      height: 20rpx;
      background-image: url(@/static/images/bubble.png);
      background-size: contain;
      margin: 0 10rpx;
    }
  }
}

/* 猜你喜欢 */
.guess {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  padding: 0 20rpx;
  .guess-item {
    width: 345rpx;
    padding: 24rpx 20rpx 20rpx;
    margin-bottom: 20rpx;
    border-radius: 10rpx;
    overflow: hidden;
    background-color: #fff;
  }
  .image {
    width: 304rpx;
    height: 304rpx;
  }
  .name {
    height: 75rpx;
    margin: 10rpx 0;
    font-size: 26rpx;
    color: #262626;
    overflow: hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
  }
  .price {
    line-height: 1;
    padding-top: 4rpx;
    color: #cf4444;
    font-size: 26rpx;
  }
  .small {
    font-size: 80%;
  }
}
// 加载提示文字
.loading-text {
  text-align: center;
  font-size: 28rpx;
  color: #666;
  padding: 20rpx 0;
}
</style>
  1. 定义组件类型、

    // types/components.d.ts
    import XtxGuess from '@/components/XtxGuess.vue'
    declare module '@vue/runtime-core' {
    export interface GlobalComponents {
    XtxGuess: typeof XtxGuess
    }
    }

  2. 准备 scroll-view 滚动容器

    <scroll-view scroll-y class="scroll"> <XtxSwiper :list="bannerList" /> <CategoryPanel :list="categoryList" /> <HotPanel :list="hotList"></HotPanel> <XtxGuess></XtxGuess> <view class="index"> </view ></scroll-view>
  3. 设置 page 和 scroll-view 样式

    <style lang="scss"> page { background-color: #f7f7f7; height: 100%; display: flex; flex-direction: column; } .scroll { flex: 1; } </style>

首页 -- 获取猜你喜欢数据

  1. 封装获取猜你喜欢数据API、

    /**

    • 猜你喜欢-小程序
      */
      export const getHomeGoodsGuessLikeAPI = (data?: PageParams) => {
      return http<PageResult<GuessItem>>({
      method: 'GET',
      url: '/home/goods/guessLike',
      data,
      })
      }
  2. 组件挂载完毕调用API

    // 获取猜你喜欢列表数据
    const getHomeGoodsGuessLikeData = async () => {
    const res = await getHomeGoodsGuessLikeAPI()
    console.log(res)
    }
    // 组件挂载完毕
    onMounted(() => {
    getHomeGoodsGuessLikeData()
    })

首页 -- 猜你喜欢数据类型和渲染

/** 通用分页结果类型 */
export type PageResult<T> = {
  /** 列表数据 */
  items: T[]
  /** 总条数 */
  counts: number
  /** 当前页数 */
  page: number
  /** 总页数 */
  pages: number
  /** 每页条数 */
  pageSize: number
}
/** 猜你喜欢-商品类型 */
export type GuessItem = {
  /** 商品描述 */
  desc: string
  /** 商品折扣 */
  discount: number
  /** id */
  id: string
  /** 商品名称 */
  name: string
  /** 商品已下单数量 */
  orderNum: number
  /** 商品图片 */
  picture: string
  /** 商品价格 */
  price: number
}
/** 通用分页参数类型 */
export type PageParams = {
  /** 页码:默认值为 1 */
  page?: number
  /** 页大小:默认值为 10 */
  pageSize?: number
}

export const getHomeGoodsGuessLikeAPI = (data?: PageParams) => {
  return http<PageResult<GuessItem>>({
    method: 'GET',
    url: '/home/goods/guessLike',
    data,
  })
}
const guessList = ref<GuessItem[]>([])
const getHomeGoodsGuessLikeData = async () => {
    const res = await getHomeGoodsGuessLikeAPI()
    guessList.value = res.result.items
}

  <view class="guess">
    <navigator class="guess-item" v-for="item in guessList" :key="item.id">
      <image class="image" mode="aspectFill" :src="item.picture"></image>
      <view class="name">{{ item.name }}</view>
      <view class="price">
        <text class="small">¥</text>
        <text>{{ item.price }}</text>
      </view>
    </navigator>
  </view>

首页 -- 猜你喜欢分页准备

滚动触底 和 模板ref

// pages/index/index.vue
<script setup lang="ts">
import type { XtxGuessInstance } from '@/types/components'
import { ref } from 'vue'
// 获取猜你喜欢组件实例
const guessRef = ref<XtxGuessInstance>()

// 滚动触底事件
const onScrolltolower = () => {
  guessRef.value?.getMore()
}
</script>

<template>
  <!-- 滚动容器 -->
  <scroll-view scroll-y @scrolltolower="onScrolltolower">
    <!-- 猜你喜欢 -->
    <XtxGuess ref="guessRef" />
  </scroll-view>
</template>

组件实例类型

// 组件实例类型
export type XtxGuessInstance = InstanceType<typeof XtxGuess>

暴露方法

// 暴露方法
defineExpose({
  getMore: getHomeGoodsGuessLikeData,
});

首页 -- 猜你喜欢分页加载

/**
 * 猜你喜欢-小程序
 */
export const getHomeGoodsGuessLikeAPI = (data?: PageParams) => {
  return http<PageResult<GuessItem>>({
    method: 'GET',
    url: '/home/goods/guessLike',
    data,
  })
}

/** 通用分页参数类型 */
export type PageParams = {
  /** 页码:默认值为 1 */
  page?: number
  /** 页大小:默认值为 10 */
  pageSize?: number
}

// 分页参数
const pageParams: Required < PageParams > = {
	page: 1,
	pageSize: 10,
}
const getHomeGoodsGuessLikeData = async () => {
	const res = await getHomeGoodsGuessLikeAPI(pageParams)
	// 数组追加
	guessList.value.push(...res.result.items)
	// 页码累加
	pageParams.page++
}

首页 -- 猜你喜欢分页条件

// src/components/XtxGuess.vue
<script setup lang="ts">
import { getHomeGoodsGuessLikeAPI } from '@/services/home'
import type { PageParams } from '@/types/global'
import type { GuessItem } from '@/types/home'
import { onMounted, ref } from 'vue'

// 分页参数
const pageParams: Required<PageParams> = {
  page: 1,
  pageSize: 10,
}
// 猜你喜欢的列表
const guessList = ref<GuessItem[]>([])
// 已结束标记
const finish = ref(false)
// 获取猜你喜欢数据
const getHomeGoodsGuessLikeData = async () => {
  // 退出分页判断
  if (finish.value === true) {
    return uni.showToast({ icon: 'none', title: '没有更多数据~' })
  }
  const res = await getHomeGoodsGuessLikeAPI(pageParams)
  // 数组追加
  guessList.value.push(...res.result.items)
  // 分页条件
  if (pageParams.page < res.result.pages) {
    // 页码累加
    pageParams.page++
  } else {
    finish.value = true
  }
}
// 重置数据
const resetData = () => {
  pageParams.page = 1
  guessList.value = []
  finish.value = false
}
// 组件挂载完毕
onMounted(() => {
  getHomeGoodsGuessLikeData()
})
// 暴露方法
defineExpose({
  resetData,
  getMore: getHomeGoodsGuessLikeData,
})
</script>

<template>
  <!-- 猜你喜欢 -->
  <view class="caption">
    <text class="text">猜你喜欢</text>
  </view>
  <view class="guess">
    <navigator
      class="guess-item"
      v-for="item in guessList"
      :key="item.id"
      :url="`/pages/goods/goods`"
    >
      <image class="image" mode="aspectFill" :src="item.picture"></image>
      <view class="name"> {{ item.name }} </view>
      <view class="price">
        <text class="small">¥</text>
        <text>{{ item.price }}</text>
      </view>
    </navigator>
  </view>
  <view class="loading-text">
    {{ finish ? '没有更多数据~' : '正在加载...' }}
  </view>
</template>

首页 -- 下拉刷新

首页触发下拉刷新

下拉刷新实际上是在用户操作下拉交互时重新调用接口 ,然后将新获取的数据再次渲染到页面中。

操作步骤

基于 scroll-view 组件实现下拉刷新,需要通过以下方式来实现下拉刷新的功能。

  • 配置 refresher-enabled 属性,开启下拉刷新交互

  • 监听 @refresherrefresh 事件,判断用户是否执行了下拉操作

  • 配置 refresher-triggered 属性,关闭下拉状态

    // src/pages/index/index.vue

    <script setup lang="ts"> // 下拉刷新状态 const isTriggered = ref(false) // 自定义下拉刷新被触发 const onRefresherrefresh = async () => { // 开启动画 isTriggered.value = true // 重置猜你喜欢组件数据 guessRef.value?.resetData() // 加载数据 await Promise.all([getHomeBannerData(), getHomeCategoryData(), getHomeHotData()]) // 关闭动画 isTriggered.value = false } </script>

    <scroll-view
    refresher-enabled
    @refresherrefresh="onRefresherrefresh"
    :refresher-triggered="isTriggered"
    class="scroll-view"
    scroll-y

    ...省略
    
    </scroll-view>

猜你喜欢组件定义重置数据的方法

// src/components/XtxGuess.vue
// 重置数据
const resetData = () => {
  pageParams.page = 1
  guessList.value = []
  finish.value = false
}
// 暴露方法
defineExpose({
  resetData,
})

骨架屏

骨架屏是页面的一个空白版本,通常会在页面完全渲染之前,通过一些灰色的区块大致勾勒出轮廓,待数据加载完成后,再替换成真实的内容。

参考效果

骨架屏作用是缓解用户等待时的焦虑情绪,属于用户体验优化方案。

相关推荐
用户48062260414156 小时前
使用uniapp开发微信小程序-框架搭建
微信小程序·uni-app
TttHhhYy8 小时前
uniapp+vue开发app,蓝牙连接,蓝牙接收文件保存到手机特定文件夹,从手机特定目录(可自定义),读取文件内容,这篇首先说如何读取,手机目录如何寻找
开发语言·前端·javascript·vue.js·uni-app
Funky_oaNiu8 小时前
uniapp实现按钮防重复点击(防抖)完整解决方案
uni-app
原克技术8 小时前
uniapp验证码
uni-app
web150850966411 天前
在uniapp Vue3版本中如何解决webH5网页浏览器跨域的问题
前端·uni-app
何极光2 天前
uniapp小程序样式穿透
前端·小程序·uni-app
User_undefined2 天前
uniapp Native.js 调用安卓arr原生service
android·javascript·uni-app
流氓也是种气质 _Cookie2 天前
uniapp blob格式转换为video .mp4文件使用ffmpeg工具
ffmpeg·uni-app
爱笑的眼睛112 天前
uniapp 极速上手鸿蒙开发
华为·uni-app·harmonyos
鱼樱前端2 天前
uni-app框架核心/常用API梳理一(数据缓存)
前端·uni-app