一、分析结构与数据
- 首先,我们我们需要完成的雏形大概是这样子的:
- 结合请求来的数据分析:
- 我们需要将请求来的数据分为左右两部分,分别填充到两侧的滚动页面中。
二、请求数据
- 为了请求数据时url的简化,我们在request文件夹下index.js文件中配置一个公共的路径baseUrl
- 并且在成功回调时,直接传入
result.data.message
- 与data同级定义一个Cates存放请求来的数据,接口的返回数据,不放data里面因为他不与渲染层交互,只是临时存储,省资源
- 封装一个简单的getCates()方法,仅用来获取数据:
request({
url: "/categories"
})
.then(res => {
this.Cates = res;
// 构造左侧的 大菜单数据
let leftMenuList = this.Cates.map(v => v.cat_name);
// 构造右侧的 商品数据
let rightContent = this.Cates[0].children;
this.setData({
leftMenuList,
rightContent
})
})
- 可以直接在onLoad声明周期中调用刚才的getCates()方法,获取数据。但更好的做法是增加本地缓存,避免重复请求数据造成资源浪费。
- 所以,可以在getCates()方法中添加一步将数据添加到缓存中。
// 把接口的数据存入到本地存储中
wx.setStorageSync("cates", {time: Date.now(), data: this.Cates});
- 而更好的办法是使用es7的async await来发送请求,所以getCates()方法最终的代码是这样的:
// 获取分类数据
async getCates() {
// 使用es7的async await来发送请求
const res = await request({ url: "/categories" });
this.Cates = res;
// 把接口的数据存入到本地存储中
wx.setStorageSync("cates", {time: Date.now(), data: this.Cates});
// 构造左侧的 大菜单数据
let leftMenuList = this.Cates.map(v => v.cat_name);
// 构造右侧的 商品数据
let rightContent = this.Cates[0].children;
this.setData({
leftMenuList,
rightContent
})
}
- 在onLoad中执行判断:
onLoad: function (options) {
/*
0. 以前web中的本地存储和小程序的本地存储的区别
1 写代码的方式不一样
web: localStorage.setItem("key", "value") localStorage.getItem("key")
小程序: wx.setStorageSync("key", "value") wx.getStorageSync("key")
2 存的时候 有没有做类型转换
web: 不管存入什么数据,最终都会先调用一下toString(),把数据变成字符串 再存入
小程序: 不存在类型转换这个操作 存什么类型的数据进入,获取的时候就是什么类型
1. 先判断本地存储中有没有旧的数据
{{time:Data.now(),data:[...]}}
2. 没有旧的数据 直接发送新请求
3. 有旧数据 同时 旧的数据没有过期 就是用 本地存储中的旧数据即可
*/
// 1.获取本地存储中的数据(小程序中也存在本地存储技术)
const Cates = wx.getStorageSync("cates");
// 2.判断
if (!Cates) {
// 不存在 发送请求获取数据
this.getCates();
} else {
// 有旧的数据 暂时定义一个过期时间 10s => 5min
if (Date.now() - Cates.time > 1000 * 60 * 5) {
// 重新发送请求
this.getCates();
} else {
// 可以使用旧的数据
this.Cates = Cates.data;
let leftMenuList = this.Cates.map(v => v.cat_name);
let rightContent = this.Cates[0].children;
this.setData({
leftMenuList,
rightContent
})
}
}
}
- 这样就可以判断上一次请求缓存的时间有没有超过5分钟,如果超过5分钟就从新请求,如果没有超过5分钟,就是用本地缓存的数据,避免多次请求。
三、设计分类页面结构
- 首先,分类页面也使用了之前定义的自定义组件SearchInput,所以需要在category/index.json文件中配置自定义组件
- 然后构建页面,顶部搜索栏,下面左侧商品菜单,下面右侧商品列表
- 左侧菜单很简单,获取的直接就是菜单信息了,但是需要考虑点击事件
<!-- 左侧菜单 -->
<scroll-view scroll-y class="left_menu">
<view
class="menu_item {{index === currentIndex? 'active' : ''}}"
wx:for="{{leftMenuList}}"
wx:key="*this"
bindtap="handleItemTap"
data-index="{{index}}">
{{item}}
</view>
</scroll-view>
- 为了完成点击变换的功能,我们在data中新建一个currentIndex数据用来记录当前点击的分类名称在菜单中的序号,如果currentIndex与某个分类对应,则动态绑定active类,改变他的样式。
- 而且此时,可以根据获取到的index,来改变右侧商品的数据,并且每次点击之后将右侧内容从新置顶(需要在data中添加scrollTop并绑定给右侧商品scroll-y属性)
// 左侧菜单的点击事件
handleItemTap(e) {
/*
1. 获取被点击的标签身上的索引
2. 给data中的currentIndex赋值就可以了
3. 根据不同的索引来渲染右侧商品内容
*/
const { index } = e.currentTarget.dataset;
let rightContent = this.Cates[index].children;
this.setData({
currentIndex: index,
rightContent,
// 重新设置 右侧内容的scroll-view标签的距离顶部距离
scrollTop: 0
})
}
- 右侧商品列表需要再进一步的循环获取数据,不过格式依然比较简单:
<!-- 右侧商品内容 -->
<scroll-view scroll-top="{{scrollTop}}" scroll-y class="right_content">
<view
class="goods_group"
wx:for="{{rightContent}}"
wx:for-item="item1"
wx:for-index="index1"
wx:key="*this">
<view class="goods_title">
<text class="delimiter">/</text>
<text class="title">{{item1.cat_name}}</text>
<text class="delimiter">/</text>
</view>
<view class="goods_list">
<navigator
wx:for="{{item1.children}}"
wx:for-item="item2"
wx:for-index="index2"
wx:key="cat_id">
<image mode="widthFix" src="{{item2.cat_icon}}"/>
<view class="goods_name">{{item2.cat_name}}</view>
</navigator>
</view>
</view>
</scroll-view>
四、添加样式并调试
- 调试样式是比较辛苦
index.less:
page {
height: 100%;
}
.cates {
height: 100%;
.cates_container {
// less中使用calc()要注意
height: ~'calc(100vh - 90rpx)';
display: flex;
.left_menu {
// 子项 高度 100% flex
flex: 2;
.menu_item {
height: 80rpx;
display: flex;
justify-content: center;
align-items: center;
font-size: 30rpx;
}
.active {
color: var(--themeColor);
border-left: 5rpx solid currentColor;
}
}
.right_content {
// 子项 高度 100% flex
flex: 5;
.goods_group {
.goods_title {
height: 80rpx;
display: flex;
justify-content: center;
align-items: center;
.delimiter {
color: #ccc;
padding: 0 10rpx;
}
.title {
}
}
.goods_list {
display: flex;
flex-wrap: wrap;
navigator {
width: 33.33%;
text-align: center;
image {
width: 50%;
}
.goods_name {
}
}
}
}
}
}
}