小程序自定义tabbar开发以及根据角色切换tabbar内容

前言

写这篇文章是因为自己做小程序的时候遇到一个需求,根据登录的角色来展示不同的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的方式来实现我们的需求。


网图-特殊样式的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
image.png
index.wxml内容如下:

我们可以看到,wxml的内容没有很多,主要的逻辑就是把list中的数据进行循环,然后渲染到底部tabbar窗口中。需要注意的是,这里使用了cover-viewcover-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相近

三、根据角色切换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
      })
    }
  },

最后,我们实现的效果如下:


66b872b8442023f1b6ba83b9e970ed86.gif

四、使用自定义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

参考文章:

微信小程序底部实现自定义动态Tabbar
微信小程序动态设置与自定义TabBar

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容