前言
写这篇文章是因为自己做小程序的时候遇到一个需求,根据登录的角色来展示不同的tabbar,中间遇到了tabbar图标闪烁和tabbar切换失败等问题,后面参考了网上一些文章总算是解决了这些问题。所以打算对这部分内容进行一个总结,避免以后再次踩坑。
一、小程序官方标准的Tabbar
Tabbar本质上就是小程序底部的菜单栏,用户可以通过点击底部的菜单栏来进行对应模块功能的操作。小程序目前最多支持5个tabbar,而且使用小程序官方的tabbar也十分简单,只需要在app.json
文件中,定义tarBar
属性并填写跳转的页面即可。
以下面的代码为例,就是在小程序中定义了4个菜单。
-
pagePath
属性表示要跳转的页面 -
text
属性表示底部菜单栏显示的文本内容 -
iconPath
属性表示菜单未选中时显示的图片 -
selectedIconPath
表示菜单未选中时显示的图片
{
"pages": [
"pages/index/index",
"pages/logs/logs",
"pages/search/index",
"pages/member/index",
"pages/admin/index"
],
...
"tabBar": {
"list": [
{
"pagePath": "pages/index/index",
"text": "首页",
"iconPath": "image/index_dark.png",
"selectedIconPath": "image/index_light.png"
},
{
"pagePath": "pages/search/index",
"text": "订单",
"iconPath": "image/search_dark.png",
"selectedIconPath": "image/search_light.png"
},
{
"pagePath": "pages/admin/index",
"text": "管理",
"iconPath": "image/admin_dark.png",
"selectedIconPath": "image/admin_light.png"
},
{
"pagePath": "pages/member/index",
"text": "我的",
"iconPath": "image/member_dark.png",
"selectedIconPath": "image/member_light.png"
}
]
},
"style": "v2",
"sitemapLocation": "sitemap.json"
}
上面这段代码,最后的显示效果如下:
二、自定义Tabbar的使用
官方的Tabbar虽然简单易用好上手,但灵活性不足,难以满足我们一些特殊的需求,比如说我们希望底部的tabbar有更加特别的样式(见下图),又或者说我们希望小程序可以实现根据登录用户的角色不同,底部的tabBar可以显示不同的菜单。这时候,官方的Tabbar就无法满足我们的需求了,我们需要通过自定义Tabbar的方式来实现我们的需求。
其实,在微信小程序官网上,就已经有一个自定义Tabbar的Demo案例,传送门点这里。我们可以自行在上面下载demo案例来查看。下面我们将对自定义的Tabbar使用进行一个简单的介绍:
步骤一:配置app.json
文件
我们需要在app.json
中通过添加custom:true
的方式来显式地声明我们要使用自定义tabbar,且app.json中必须同样将所有用到的菜单栏配在app.json
文件中。
"tabBar": {
"custom": true,
"list": [
{
"pagePath": "pages/index/index",
"text": "首页",
"iconPath": "image/index_dark.png",
"selectedIconPath": "image/index_light.png"
},
{
"pagePath": "pages/search/index",
"text": "订单",
"iconPath": "image/search_dark.png",
"selectedIconPath": "image/search_light.png"
},
{
"pagePath": "pages/admin/index",
"text": "管理",
"iconPath": "image/admin_dark.png",
"selectedIconPath": "image/admin_light.png"
},
{
"pagePath": "pages/member/index",
"text": "我的",
"iconPath": "image/member_dark.png",
"selectedIconPath": "image/member_light.png"
}
]
},
步骤二:在最外层创建一个名为custom-tab-bar
的文件夹(和pages目录同级),里面安装如下格式创建入口文件:
custom-tab-bar/index.js
custom-tab-bar/index.json
custom-tab-bar/index.wxml
custom-tab-bar/index.wxss
index.wxml内容如下:
我们可以看到,wxml的内容没有很多,主要的逻辑就是把list中的数据进行循环,然后渲染到底部tabbar窗口中。需要注意的是,这里使用了cover-view
和cover-image
标签来进行页面的布局,小程序官方的说法是这种方式可以较好地保证 tabBar 层级相对较高,不容易被页面的内容覆盖掉。但这可能会引起另外的一个问题,我们后面会说到。
<!--miniprogram/custom-tab-bar/index.wxml-->
<cover-view class="tab-bar">
<cover-view class="tab-bar-border"></cover-view>
<cover-view wx:for="{{list}}" wx:key="index" class="tab-bar-item" data-path="{{item.pagePath}}" data-index="{{index}}" bindtap="switchTab">
<cover-image src="{{selected === index ? item.selectedIconPath : item.iconPath}}"></cover-image>
<cover-view style="color: {{selected === index ? selectedColor : color}}">{{item.text}}</cover-view>
</cover-view>
</cover-view>
对应的index.js内容如下:
文件的内容其实基本上和app.json
中配置的一样,我们在switchTab
方法中进行url的切换和图片的刷新
Component({
data: {
selected: 0,
color: "#7A7E83",
selectedColor: "#3cc51f",
"list": [
{
"pagePath": "/pages/index/index",
"text": "首页",
"iconPath": "/image/index_dark.png",
"selectedIconPath": "/image/index_light.png"
},
{
"pagePath": "/pages/search/index",
"text": "订单",
"iconPath": "/image/search_dark.png",
"selectedIconPath": "/image/search_light.png"
},
{
"pagePath": "/pages/admin/index",
"text": "管理",
"iconPath": "/image/admin_dark.png",
"selectedIconPath": "/image/admin_light.png"
},
{
"pagePath": "/pages/member/index",
"text": "我的",
"iconPath": "/image/member_dark.png",
"selectedIconPath": "/image/member_light.png"
}
]
},
attached() {
},
methods: {
switchTab(e) {
const data = e.currentTarget.dataset
const url = data.path
wx.switchTab({url})
this.setData({
selected: data.index
})
}
}
})
对应的css样式如下:
.tab-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 48px;
background: white;
display: flex;
padding-bottom: env(safe-area-inset-bottom);
}
.tab-bar-border {
background-color: rgba(0, 0, 0, 0.33);
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 1px;
transform: scaleY(0.5);
}
.tab-bar-item {
flex: 1;
text-align: center;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.tab-bar-item cover-image {
width: 27px;
height: 27px;
}
.tab-bar-item cover-view {
font-size: 10px;
}
对应的index.json内容如下:
{
"component": true
}
步骤三:在每个tab页面的onshow方法中,添加下面的方法:
onShow: function () {
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
this.getTabBar().setData({
selected: 0
})
}
},
根据每个tabbar在底部的排列顺序,这里的selected要填入不同的值。比如首页是第一个菜单,那么这里就填0,“我的”是第四个菜单,这里就填3。注意!这一步一定不能省略,否则会出现底部图标更新不及时等bug!
按照上面的三个步骤来,到这里我们就已经可以实现和官方tabbar功能相近的效果了,如果希望tabbar做得更加好看的话,只需要自己调整样式即可。
三、根据角色切换Tabbar内容思路
根据角色切换tabbar内容,其实不少小程序还是有这个需求的,毕竟为了不同的角色而专门多做一套小程序是很耗时而没必要的。同时网上对于这个需求也有不同的思路,在这里我选择其中一种比较简单清晰的方案来给大家分享一下。
需求如下:
目前我们一共有4个菜单,分别是首页
、订单
、管理
和我的
,我们想要普通用户登录后只能看到首页
、订单
、和我的
这三个菜单,管理员登录后可以看到所有菜单。
步骤一:在配置文件中定义不同角色的菜单列表
这一步不需要固定写在哪个文件,可以根据项目的实际需要写在常用的常量配置文件中即可。
export const USER_PAGE = {
memberTabbarList : [
{
"pagePath": "/pages/index/index",
"text": "首页",
"iconPath": "/image/index_dark.png",
"selectedIconPath": "/image/index_light.png"
},
{
"pagePath": "/pages/search/index",
"text": "订单",
"iconPath": "/image/search_dark.png",
"selectedIconPath": "/image/search_light.png"
},
{
"pagePath": "/pages/member/index",
"text": "我的",
"iconPath": "/image/member_dark.png",
"selectedIconPath": "/image/member_light.png"
}
],
adminTabbarList : [
{
"pagePath": "/pages/index/index",
"text": "首页",
"iconPath": "/image/index_dark.png",
"selectedIconPath": "/image/index_light.png"
},
{
"pagePath": "/pages/search/index",
"text": "订单",
"iconPath": "/image/search_dark.png",
"selectedIconPath": "/image/search_light.png"
},
{
"pagePath": "/pages/admin/index",
"text": "管理",
"iconPath": "/image/admin_dark.png",
"selectedIconPath": "/image/admin_light.png"
},
{
"pagePath": "/pages/member/index",
"text": "我的",
"iconPath": "/image/member_dark.png",
"selectedIconPath": "/image/member_light.png"
}
]
}
步骤二:编辑custom-tab-bar
目录下的index.js
文件
这一步,最关键的逻辑在于在attached方法中,加入了根据角色来赋予list不同的值的逻辑。
import {USER_PAGE} from "../config/common"
Component({
data: {
selected: 0,
color: "#7A7E83",
selectedColor: "#3cc51f",
"list": []
},
attached() {
let roldId = wx.getStorageSync('roldId');
// 0 表示普通用户 1表示管理员
if(roldId == 0){
this.setData({
list: USER_PAGE.memberTabbarList
})
}else if(roldId == 1){
this.setData({
list: USER_PAGE.adminTabbarList
})
}else{
this.setData({
list: USER_PAGE.memberTabbarList
})
}
},
methods: {
switchTab(e) {
...
}
}
})
步骤三:调整tabbar页面的onshow方法
我们在之前已经讲过,自定义tabbar需要手动在各个菜单入口页面中的onshow方法添加跳转方法,由于我们这个需求不同角色显示的菜单不同,所以对应的我的
菜单页的索引也需要调整一下。
onShow: function () {
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
let selected = 2;
let roldId = wx.getStorageSync('roldId');
if(roldId == 1){
selected = 3;
}
this.getTabBar().setData({
selected
})
}
},
最后,我们实现的效果如下:
四、使用自定义Tabbar的注意事项
在第三节中,我们已经知道了如何使用自定义Tabbar来实现根据角色来切换底部菜单栏的内容,但实际上上面的操作还存在可以优化的地方,比如说我们可以观察到当用户首次点击底部的菜单栏时,会出现tabbar闪烁的现象;每次登陆后,都需要自动跳转到首页才行。本节将针对这两个问题给出解决的一些思路。
(一)解决tabbar闪烁问题
方法一:调整custom-tab-bar
组件的switchTab
方法
主要就是要将this.setData()
方法给注释掉,但这个方法我试过没什么效果
switchTab(e) {
const data = e.currentTarget.dataset
const url = data.path
wx.switchTab({url})
// 注释掉官方demo的这段代码
// this.setData({
// selected: data.index
// })
}
方法二:调整custom-tab-bar
组件使用的标签
将cover-view
标签改用成view
标签,将cover-image
标签改用成image
标签,改完后wxml
代码为:
<!--miniprogram/custom-tab-bar/index.wxml-->
<view class="tab-bar">
<view class="tab-bar-border"></view>
<view wx:for="{{list}}" wx:key="index" class="tab-bar-item" data-path="{{item.pagePath}}" data-index="{{index}}" bindtap="switchTab">
<image src="{{selected === index ? item.selectedIconPath : item.iconPath}}"></image>
<view style="color: {{selected === index ? selectedColor : color}}">{{item.text}}</view>
</view>
</view>
PS:这里需要对应将wxss文件的样式也改一下
(二)每次切换用户时都会跳转到首页
这里的问题一般是出现在用户通过分享进入到小程序中的某个页面的,我们希望用户在登录之后能够返回到一开始进入的页面,而不是登录后直接跳转到首页。我们在判断完用户角色后,先获取从上一个页面传入的url,然后重新reLaunch
回去即可。
本篇文章的代码可以从码云上面下载:https://gitee.com/moutory/qiqv-tab-bar