1.热门推荐-准备工作

// 用defineProps获取页面参数,query
const query = defineProps<{
type: string
}>()
const currHot = hotMap.find((v) => v.type === query.type)
// 动态设置标题
uni.setNavigationBarTitle({ title: currHot!.title })
</script>
2.获取热门推荐数据
PageParams & { subType: string } 交叉类型,基于原有类型上进行扩展,再作为当前数据类型
-
封装通用接口
import type { PageParams } from '@/types/global'
import { http } from '@/utils/http'// 拓展类型
type HotParams = PageParams & { subType: string }
export const getHotRecommendAPI = (url: string, data: HotParams) => {
return http({
method: 'GET',
url,
data,
})
} -
初始化调用
// 获取热门推荐数据
const getHotRecommendData = async () => {
const res = await getHotRecommendAPI(currHot!.url)
}
// 页面加载时调用
onLoad(() => {
getHotRecommendData()
})
3.类型定义
类型的复用
export type GuessItem = GoodsItem
import type { PageResult, GoodsItem } from './global'
/** 热门推荐 */
export type HotResult = {
/** id信息 */
id: string
/** 活动图片 */
bannerPicture: string
/** 活动标题 */
title: string
/** 子类选项 */
subTypes: SubTypeItem[]
}
/** 热门推荐-子类选项 */
export type SubTypeItem = {
/** 子类id */
id: string
/** 子类标题 */
title: string
/** 子类对应的商品集合 */
goodsItems: PageResult<GoodsItem>
}
type HotParams = PageParams & { subType: string }
export const getHotRecommendAPI = (url: string, data?: HotParams) => {
return http<HotResult>({
method: 'GET',
url,
data,
})
}
4.渲染页面和Tab交互
-
渲染页面
// 推荐封面图
const bannnerPicture = ref('')
// 推荐选项
const subTypes = ref<SubTypeItem[]>([])
// 获取下标
const activeIndex = ref(0)
// 获取热门推荐数据
const getHotRecommendData = async () => {
const res = await getHotRecommendAPI(currHot!.url)
bannnerPicture.value = res.result.bannerPicture
subTypes.value = res.result.subTypes
} -
Tab交互
<text
v-for="(item, index) in subTypes"
:key="item.id"
class="text"
:class="{ active: index === activeIndex }"
@tap="activeIndex = index"
>{{ item.title }}</text >
用v-show反复的切换更好而不用v-if 耗费性能
<!-- 推荐列表 -->
<scroll-view
v-for="(item, index) in subTypes"
:key="item.id"
v-show="activeIndex === index"
scroll-y
class="scroll-view"
>
<view class="goods">
<navigator
hover-class="none"
class="navigator"
v-for="goods in item.goodsItems.items"
:key="goods.id"
:url="`/pages/goods/goods?id=${goods.id}`"
>
<image class="thumb" :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 class="loading-text">正在加载...</view>
</scroll-view>
</view>
</template>
5.分页加载
- 滚动触底
- 获取当前选项
- 当前页码累加
- 调用API传参
- 当前数据追加
实现效果:
// 滚动触底
const onScrolltolower = async () => {
//获取当前选项
const currsubTypes = subTypes.value[activeIndex.value]
// 当前页码累加
currsubTypes.goodsItems.page++
// 调用API传参
const res = await getHotRecommendAPI(currHot!.url, {
subType: currsubTypes.id,
page: currsubTypes.goodsItems.page,
pageSize: currsubTypes.goodsItems.pageSize,
})
// 新的列表选项
const newSubTypes = res.result.subTypes[activeIndex.value]
// 数组追加
currsubTypes.goodsItems.items.push(...newSubTypes.goodsItems.items)
}
6.分页结束条件

分页条件
// 分页条件
if (currsubTypes.goodsItems.page < currsubTypes.goodsItems.pages) {
// 当前页码累加
currsubTypes.goodsItems.page++
} else {
// 标志已结束
currsubTypes.finish = true
return uni.showToast({ title: '已经到底了' })
}
标记已结束
修改一下:
// 给SubTypeItem 再加一个类型,可有可无加?
const subTypes = ref<(SubTypeItem & { finish?: boolean })[]>([])
// 获取热门推荐数据
const getHotRecommendData = async () => {
const res = await getHotRecommendAPI(currHot!.url, {
subType: '912000341',
// 技巧: 环境变量,开发环境,修改初始页面方便测试分页结果
page: import.meta.env.DEV ? 30 : 1,
pageSize: 10,
})
bannnerPicture.value = res.result.bannerPicture
subTypes.value = res.result.subTypes
}
// 标志已结束
currsubTypes.finish = true
return uni.showToast({ title: '已经到底了' })
页面底部提示
<view class="loading-text">{{ item.finish ? '已经到底了' : '正在加载中...' }}</view>
技巧:
环境变量,开发环境,修改初始页面方便测试分页结果
page: import.meta.env.DEV ? 30 : 1,
7.准备工作
静态数据=>获取轮播图数据=>渲染轮播图
<script setup lang="ts">
import { getHomeBannerAPI } from '@/services/home'
import { BannerItem } from '@/types/home'
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
const bannerList = ref<BannerItem[]>([])
//获取轮播图数据
const getHomeBannerData = async () => {
const res = await getHomeBannerAPI()
}
// 页面加载时调用
onLoad(() => {
getHomeBannerData()
})
</script>
8.渲染一级分类和Tab交互
-
封装API
import { http } from '@/utils/http'
import { CategoryTopItem } from '@/types/category'export const getCategoryTopAPI = () => {
return http<CategoryTopItem[]>({
method: 'GET',
url: '/category/top',
})
} -
初始化调用
// 获取分类列表数据
const getCategoryTopData = async () => {
const res = await getCategoryTopAPI()
}// 页面加载时调用
onLoad(() => {
getHomeBannerData()
getCategoryTopData()
}) -
定义类型
// 获取分类列表数据
const cayegoryList = ref<CategoryTopItem[]>([])
const getCategoryTopData = async () => {
const res = await getCategoryTopAPI()
cayegoryList.value = res.result
} -
渲染一级分类
<scroll-view class="primary" scroll-y> <view v-for="(item, index) in cayegoryList" :key="item.id" class="item" :class="{ active: index === activeIndex }" > -
Tab交互
// 获取分类列表数据
<scroll-view class="primary" scroll-y> <view v-for="(item, index) in cayegoryList" :key="item.id" class="item" :class="{ active: index === activeIndex }" @tap="activeIndex = index" >
const cayegoryList = ref<CategoryTopItem[]>([])
const activeIndex = ref(0)
const getCategoryTopData = async () => {
const res = await getCategoryTopAPI()
cayegoryList.value = res.result
}
9.二级分类和商品渲染
-
提取当前二级分类数据
// 提取当前二级分类数据
const subCategoryList = computed(() => {
return cayegoryList.value[activeIndex.value]?.children || []
}) -
渲染二级分类
-
渲染商品
<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>