推荐模块
静态结构 (复制)
src\pages\recommend\index.vue
vue
<template>
<view class="viewport">
<!-- 推荐封面 -->
<view class="cover">
<image
src="http://yjy-xiaotuxian-dev.oss-cn-beijing.aliyuncs.com/picture/2021-04-15/1ba86bcc-ae71-42a3-bc3e-37b662f7f07e.jpg"
/>
</view>
<!-- tab栏 -->
<view class="tabs">
<text class="text active"> 抢先尝鲜 </text>
<text class="text"> 新品预告 </text>
</view>
<!-- 列表内容 -->
</view>
</template>
<script>
export default {};
</script>
<style lang="scss">
page {
height: 100%;
background-color: #f4f4f4;
}
.viewport {
display: flex;
flex-direction: column;
height: 100%;
padding: 180rpx 0 0;
position: relative;
}
.cover {
width: 750rpx;
height: 225rpx;
border-radius: 0 0 40rpx 40rpx;
overflow: hidden;
position: absolute;
left: 0;
top: 0;
}
.tabs {
display: flex;
justify-content: space-evenly;
height: 100rpx;
line-height: 90rpx;
margin: 0 20rpx;
font-size: 28rpx;
border-radius: 10rpx;
box-shadow: 0 4rpx 5rpx rgba(200, 200, 200, 0.3);
color: #333;
background-color: #fff;
position: relative;
z-index: 9;
.text {
margin: 0 20rpx;
position: relative;
}
.active {
&::after {
content: "";
width: 40rpx;
height: 4rpx;
transform: translate(-50%);
background-color: #27ba9b;
position: absolute;
left: 50%;
bottom: 24rpx;
}
}
}
.scroll-view {
flex: 1;
overflow: hidden;
}
.goods {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
padding: 0 20rpx 20rpx;
.navigator {
width: 345rpx;
padding: 20rpx;
margin-top: 20rpx;
border-radius: 10rpx;
background-color: #fff;
}
.thumb {
width: 305rpx;
height: 305rpx;
}
.name {
height: 88rpx;
font-size: 26rpx;
}
.price {
line-height: 1;
color: #cf4444;
font-size: 30rpx;
}
}
.goods .symbol,
.goods .decimal {
font-size: 70%;
}
</style>
动态设置页面标题
目标:实现动态设置推荐页面的标题
思路:从首页跳转到推荐页面时,传来参数type。根据type的值,显示不同的title
步骤:
- 声明对象
urlMap
,保存title与url等信息 - 页面加载后,获取参数type的值
- 根据type的值,从urlMap中取出信息
- 动态设置页面title
js
<script>
// 1. 声明对象`urlMap`,保存title与url等信息
const urlMap = {
1: { title: "特惠推荐", url: "/home/preference/mutli" },
2: { title: "爆款推荐", url: "/home/inVogue/mutli" },
3: { title: "一站买全", url: "/home/oneStop/mutli" },
4: { title: "新鲜好物", url: "/home/new/mutli" }, // 后端未提供,暂用新鲜好物url
5: { title: "新鲜好物", url: "/home/new/mutli" },
};
export default {
// 2. 页面加载后,获取参数type的值
onLoad({ type }) {
// 3. 根据type的值,从urlMap中取出信息
this.currentItem = urlMap[type];
// 4. 动态设置页面title
uni.setNavigationBarTitle({ title: this.currentItem.title });
},
};
</script>
请求数据-渲染界面
目标:实现加载后请求数据
封装API
src\api\home.js
js
// 1. 封装API
export const getRecomendsAPI = (url) => {
return request({ url });
};
请求数据、保存数据
src\pages\recommend\index.vue
js
<script>
import { getRecomendsAPI } from "@/api/home";
export default {
data() {
return {
// 3. 声明数据
bannerPicture: "",
goodsItems: [],
subTypes: [],
};
},
// 2. 加载后触发
async onLoad({ type }) {
this.currentItem = urlMap[type];
uni.setNavigationBarTitle({ title: this.currentItem.title });
const { result } = await getRecomendsAPI(this.currentItem.url);
const { bannerPicture, goodsItems, subTypes } = result;
// 4. 保存数据
this.bannerPicture = bannerPicture;
this.goodsItems = goodsItems;
this.subTypes = subTypes;
},
};
</script>
渲染界面:
vue
<template>
<view class="viewport">
<!-- 推荐封面 -->
<view class="cover">
<!-- 5.1 渲染图片 -->
<image :src="bannerPicture" />
</view>
<!-- tab栏 -->
<view class="tabs">
<!-- 5.2 列表渲染 -->
<text v-for="item in subTypes" :key="item.id" class="text active">
{{ item.title }}
</text>
</view>
</view>
</template>
点谁谁高亮
目标:实现点谁谁高
思路:排它判断
步骤:
- 声明变量
activeId
- 动态class添加类名
active
- 点击事件,修改
activeId
- 加载后设置第一项id为
activeId
代码:
- 声明变量
activeId
diff
data() {
return {
+ // 1. 声明变量activeId
+ activeId: "",
};
},
- 动态class添加类名
active
- 点击事件,修改
activeId
diff
<view class="tabs">
<!-- 2. 动态class添加类名 -->
<!-- 3. 点击事件,修改activeId -->
<text
v-for="item in subTypes"
:key="item.id"
class="text"
+ :class="{ active: activeId === item.id }"
+ @click="activeId === item.id"
>
{{ item.title }}
</text>
</view>
- 加载后设置第一项id为
activeId
diff
async onLoad({ type }) {
const currentItem = urlMap[type];
uni.setNavigationBarTitle({ title: currentItem.title });
const { result } = await getRecomendsAPI(currentItem.url);
const { bannerPicture, goodsItems, subTypes } = result;
this.bannerPicture = bannerPicture;
this.goodsItems = goodsItems;
this.subTypes = subTypes;
+ // 4. 加载后设置第一项id为activeId
+ this.activeId = subTypes[0].id;
},
渲染内容列表
实现渲染内容列表
效果:
步骤:
- 准备静态结构
- 列表渲染
- 条件渲染
代码:
- 准备静态结构
vue
<scroll-view
scroll-y
class="scroll-view"
>
<view class="goods">
<navigator
hover-class="none"
class="navigator"
v-for="goods in []"
:key="goods.id"
:url="`/pages/goods/index?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">正在加载...</view>
</scroll-view>
- 列表渲染
- 条件渲染
diff
<scroll-view
scroll-y
class="scroll-view"
+ v-for="(item, id) in goodsItems"
+ :key="id"
+ v-show="id === activeId"
>
<view class="goods">
<navigator
hover-class="none"
class="navigator"
- v-for="goods in []"
+ v-for="goods in item.items"
:key="goods.id"
:url="`/pages/goods/index?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">正在加载...</view>
</scroll-view>
内容列表-无限滚动
目标:实现下拉分页,从而实现无限滚动
思路:
- 每次滚动到底部时,请求下一页数据。
- 合并每次请求的数据,到数组中。
- 根据当前页page与总页数pages,决定是否继续请求下一页数据
步骤:
- 改造API,支持传subTypesId、page等参数。
- 定义计算属性,根据
activeId
和gooodsItems
获取到当前项的分页信息 - 监听滚动底部事件,请求下一页更多数据
- 改造保存数据为合并数据
代码:
1、改造API,支持传subTypesId、page等参数。
src/api/home.js
js
export const getRecomendsAPI = (
url,
subTypeId = "",
page = 1,
pageSize = 10
) => {
return request({
url,
data: {
subType: subTypeId,
page,
pageSize,
},
});
};
2、定义计算属性,根据activeId
和gooodsItems
获取到当前项的分页信息
src/page/recommend/index.vue
js
computed: {
pageInfo() {
if (!this.activeId) return {};
const { page, pages } = this.goodsItems[this.activeId];
return { page, pages };
},
},
3、监听滚动底部事件,请求下一页更多数据
4、保存数据改为合并数据
diff
<scroll-view
scroll-y
class="scroll-view"
v-for="(item, id) in goodsItems"
:key="id"
v-show="id === activeId"
+ @scrolltolower="loadMore"
>
... 省略其它代码
</scroll-view>
js
async loadMore() {
const { result } = await getRecomendsAPI(
this.currentItem.url,
this.activeId,
// 请求下一页更多数据
this.pageInfo.page + 1
);
const { goodsItems } = result;
const { items, page, pages, pageSize, counts } = goodsItems[this.activeId]
// 4. 改造保存数据为合并数据;
this.goodsItems[this.activeId].items.push(...items);
// 💥💥 要更新page和pages
this.goodsItems[this.activeId].page = page;
this.goodsItems[this.activeId].pages = pages;
},
内容列表-停止加载
目标:实现没有更多数据时,停止请求下一页数据
步骤:
- 根据page和pages,定义计算属性finished
- 分页请求触发前,判断finished,为true则阻止发请求
- 条件渲染-显示
没有更多数据了
提示信息
代码:
1、定义计算属性finished
js
computed: {
// ... 省略其它代码 ...
finished() {
if (!this.activeId) return false;
if (this.pageInfo.page < this.pageInfo.pages) return false;
return true;
},
},
2、分页请求触发前,判断finished,为true则阻止发请求
diff
async loadMore() {
+ // 阻止发请求
+ if (this.finished) return;
// ... 省略其它代码 ...
},
3、 条件渲染-显示没有更多数据了
提示信息
diff
+ <view class="loading" v-if="finished">没有更多多数据了</view>
内容列表-节流
目标:防止在底部频繁触发分页请求
思路:
- 声明标识loading为true或false,
- 每次分页请求前,将loading改为true,表示正在请求,未结束。
- 每次分页请求后,将loading改为false,表示请,已未结束。
- 滚动事件触发时,如果loading为true,通过return阻止代码执行
代码省略...