目录
[首页 -- 热门推荐组件](#首页 – 热门推荐组件)
[首页 -- 获取热门推荐数据](#首页 – 获取热门推荐数据)
[首页 -- 热门推荐数据类型并渲染](#首页 – 热门推荐数据类型并渲染)
[首页 -- 猜你喜欢组件](#首页 – 猜你喜欢组件)
[首页 -- 获取猜你喜欢数据](#首页 – 获取猜你喜欢数据)
[首页 -- 猜你喜欢数据类型和渲染](#首页 – 猜你喜欢数据类型和渲染)
[首页 -- 猜你喜欢分页准备](#首页 – 猜你喜欢分页准备)
[首页 -- 猜你喜欢分页加载](#首页 – 猜你喜欢分页加载)
[首页 -- 猜你喜欢分页条件](#首页 – 猜你喜欢分页条件)
[首页 -- 下拉刷新](#首页 – 下拉刷新)
热门推荐
热门推荐功能,后端根据用户的消费习惯等信息向用户推荐的一系列商品,前端负责展示这些商品展示给用户。
参考效果
首页 -- 热门推荐组件
- 准备组件(只有首页使用)
热门推荐布局为独立的组件 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>
-
导入并使用组件
import HotPanel from "./componets/HotPanel.vue";
<HotPanel></HotPanel>
首页 -- 获取热门推荐数据
-
封装获取热门推荐数据API
export const getHomeHotAPI = () => {
return http<HotItem[]>({
method: 'GET',
url: '/home/hot/mutli',
})
} -
页面初始化调用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();
});
首页 -- 热门推荐数据类型并渲染
-
定义热门推荐数据类型
/** 首页-热门推荐数据类型 /
export type HotItem = {
/* 说明 /
alt: string
/* id /
id: string
/* 图片集合[ 图片路径 ] /
pictures: string[]
/* 跳转地址 /
target: string
/* 标题 /
title: string
/* 推荐类型 */
type: string
} -
指定类型并传值给子组件
const hotList = ref<HotItem[]>([])
<HotPanel :list="hotList" />import type { HotItem } from "@/types/home";
defineProps<{
list: HotItem[];
}>(); -
渲染热门推荐数据
<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>
猜你喜欢
参考效果
猜你喜欢功能,后端根据用户的浏览记录等信息向用户随机推荐的一系列商品,前端负责把商品在多个页面中展示。
首页 -- 猜你喜欢组件
- 准备组件(通用组件)
猜你喜欢是一个通用组件 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>
-
定义组件类型、
// types/components.d.ts
import XtxGuess from '@/components/XtxGuess.vue'
declare module '@vue/runtime-core' {
export interface GlobalComponents {
XtxGuess: typeof XtxGuess
}
} -
准备 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> -
设置 page 和 scroll-view 样式
<style lang="scss"> page { background-color: #f7f7f7; height: 100%; display: flex; flex-direction: column; } .scroll { flex: 1; } </style>
首页 -- 获取猜你喜欢数据
-
封装获取猜你喜欢数据API、
/**
- 猜你喜欢-小程序
*/
export const getHomeGoodsGuessLikeAPI = (data?: PageParams) => {
return http<PageResult<GuessItem>>({
method: 'GET',
url: '/home/goods/guessLike',
data,
})
}
- 猜你喜欢-小程序
-
组件挂载完毕调用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,
})
骨架屏
骨架屏是页面的一个空白版本,通常会在页面完全渲染之前,通过一些灰色的区块大致勾勒出轮廓,待数据加载完成后,再替换成真实的内容。
参考效果
骨架屏作用是缓解用户等待时的焦虑情绪,属于用户体验优化方案。