原文参考我的公众号文章 微信小程序自定义动静tabBar
总结了一下小程序官网举荐的动静
tabBar
应用。通过本人的实际和落地施行,对应用动静tabBar
时须要解决的问题(多角色不同tabBar
动静渲染,页面加载后tabBar
主动选中,操作权限判断,页面逻辑和tabBar
初始化的先后关系管制。。。)进行了逻辑整合。
前置需要
- 多角色
- 动静tabBar
- 操作权限管制
tabBar
残缺代码和页面内的应用手法在文末,这里先分步看看这些问题过后都是如何解决的吧。
tabBar组件封装
组件构造的封装可参考官网代码,之后须要在组件js文件内对动静tabBar做解决即可;
import { geneAuthMenus, updateAppGlobalAuthData} from './tabBarCtrl'import { userLogin} from '../models/UserApi'import { showToast} from '../utils/WxApi'Component({ data: { selected: 0, list: [], }, methods: { switchTab(e) { const data = e.currentTarget.dataset const url = data.path wx.switchTab({ url }) this.setData({ selected: data.index }) } }, lifetimes: { attached() { let appAuthMenus = [...getApp().globalAuth.menus]; if (!getApp().globalAuth.received) { /**登录后App内记录 可拜访页面、可操作动作 */ userLogin({ useCache: false }).then(res => { let { auth_list = [], action_list = [], role_id = 0, role_name = '小白' } = res?.data?.role_auth || {}; let authList = geneAuthMenus(auth_list); updateAppGlobalAuthData({ received: true, menus: authList, actions: action_list, roleId: role_id, roleName: role_name, }); this.setData({ list: authList }) }).catch(err => { showToast(err.msg || '登陆失败') updateAppGlobalAuthData({ received: true, menus: geneAuthMenus([]), actions: [], roleId: 0, roleName: '小白', }); }) } else { this.setData({ list: appAuthMenus }) } } }})
tabBar
数据全局数据拜访
App({ globalAuth: { received: false, // 当状态变为true,代表auth相干数据曾经拿到,与auth相干的逻辑当初能够执行了 menus: [], actions: [], role_id: 0, role_name: '普通用户', roleHash: { 0: '普通用户', 1: '管理员', 2: '经营', 3: '培修徒弟', } }})
tabBar
数据来自【权限&menu】
接口
在custom-tab-bar
的js
里,在lifetimes
的attached
里调用和【权限&menu】
相干接口,接口调用完结(无论胜利或失败)后把getApp().globalAuth.received
设为true
lifetimes: { attached() { let appAuthMenus = [...getApp().globalAuth.menus]; if (!getApp().globalAuth.received) { /**登录后App内记录 可拜访页面、可操作动作 */ userLogin({ useCache: false }).then(res => { let { auth_list = [], action_list = [], role_id = 0, role_name = '小白' } = res?.data?.role_auth || {}; let authList = geneAuthMenus(auth_list); updateAppGlobalAuthData({ received: true, menus: authList, actions: action_list, roleId: role_id, roleName: role_name, }); this.setData({ list: authList }) }).catch(err => { showToast(err.msg || '登陆失败') updateAppGlobalAuthData({ received: true, menus: geneAuthMenus([]), actions: [], roleId: 0, roleName: '普通用户', }); }) } else { this.setData({ list: appAuthMenus }) } } }
解决动静tabBar带来的问题
如何保障页面逻辑在【权限&menu】
接口申请完结之后执行:通过拜访全局状态字段getApp().globalAuth.received
状态来判断;
// 通过有限轮询globalAuth.received状态的变动,监测到变动后再执行后续export function doSthAfterDependentChangedPromise(computed = () => {}) { let loopTicker = null; const dependentTargetChanged = (resolver) => { if (getAppGlobalAuthData('received')) { console.log('doSthAfterDependentChangedPromise=>' + computed.getName()) clearTimeout(loopTicker); resolver(computed()); } else { loopTicker = setTimeout(() => { dependentTargetChanged(resolver) }, 200); } } return new Promise(resolve => { dependentTargetChanged(resolve) })}
2.默认页面问题;
因为下面曾经能够做到在【权限&menu】
接口完结后再执行其余逻辑,那么就能够做到拿到 menu
数据后调用 wx.redirectTo
到角色默认的页面;(因为我的我的项目默认页面是一个所有角色私有的页面,所以就没做这方面解决)
含tabBar
页面自动更新 tab
索引
在应用tabBar
的页面调用selectTabBar
办法,因为办法外围是通过Page实例调用getTabBar
办法,所以把this
传递进去了。参考官网说法:如需实现tab
选中态,要在以后页面下,通过getTabBar
接口获取组件实例,并调用setData
更新选中态。
onShow: function () { selectTabBar(this);},
export function selectTabBar(context) { const computed = () => { let authMenus = [...getAppGlobalAuthData('menus')]; let currentPath = getCurrentRoute(); let pageIndex = authMenus.findIndex(item => item.pagePath.includes(currentPath)); pageIndex = pageIndex == -1 ? 0 : pageIndex; if (typeof context.getTabBar === 'function' && context.getTabBar()) { context.getTabBar().setData({ selected: pageIndex }) console.log('select current path:', currentPath) } return 1; } return doSthAfterDependentChangedPromise(computed)}
3.含tabBar
的页面超过了五个:小程序在app.json
内通过"tabBar"
字段定义了list
只能有五项,所以能够把这些页面作为某一个tabBar
页面的二级入口。
对代码进行整合
上述实现中,将tabBar相干数据挂载在App实例中,为了让tabBar组件相干性能更加严密,逻辑更加清晰,所以把性能和数据整合到了一起,造成了 custom-tab-bar/model.js
文件。
function getCurrentRoute() { let route = '/pages/home/home' let pages = getCurrentPages(); if (pages.length) { route = pages[pages.length - 1].route; } return route;}const PAGE_ENVIRONMENT = { "pagePath": "/pages/pkgStoreInspection/Environment/Environment", "text": "页面0", "iconPath": "/resources/icon/lubanya/tabbar/Env.png", "selectedIconPath": "/resources/icon/lubanya/tabbar/Env_cur.png"}const PAGE_NG = { "pagePath": "/pages/pkgStoreInspection/NG/NG", "text": "页面1", "iconPath": "/resources/icon/lubanya/tabbar/Nogood.png", "selectedIconPath": "/resources/icon/lubanya/tabbar/Nogood_cur.png"}const PAGE_INSPECTION = { "pagePath": "/pages/pkgStoreInspection/inspection/inspection", "text": "页面2", "iconPath": "/resources/icon/lubanya/tabbar/Store.png", "selectedIconPath": "/resources/icon/lubanya/tabbar/Store_cur.png"}const PAGE_RECORD = { "pagePath": "/pages/pkgStoreInspection/record/record", "text": "页面3", "iconPath": "/resources/icon/lubanya/tabbar/History.png", "selectedIconPath": "/resources/icon/lubanya/tabbar/History_cur.png"}const PAGE_MACHINE_EMULATOR = { "pagePath": "/pkgElse/pages/machineEmulator/machineEmulator", "text": "页面4", "iconPath": "/resources/icon/lubanya/tabbar/History.png", "selectedIconPath": "/resources/icon/lubanya/tabbar/History_cur.png"}const PAGE_USER = { "pagePath": "/pages/me/me", "text": "页面5", "iconPath": "/resources/images/tabbar/mine.png", "selectedIconPath": "/resources/images/tabbar/mine_active.png"}const AUTH_PAGE_HASH = { 'PAGE_ENVIRONMENT': PAGE_ENVIRONMENT, 'PAGE_NG': PAGE_NG, 'PAGE_INSPECTION': PAGE_INSPECTION, 'PAGE_RECORD': PAGE_RECORD, 'PAGE_MACHINE_EMULATOR': PAGE_MACHINE_EMULATOR, 'PAGE_USER': PAGE_USER,}/** * TabBar数据和行为管制的单例类 */let CreateSingletonTabBar = (function () { let instance = null; return function (roleId) { if (instance) { return instance } this.index = 0; this.roleNameHash = { 0: '普通用户', 1: '管理员', 2: '经营', 3: '培修徒弟', } this.authData = { received: false, pages: [], actions: [], roleId: roleId, roleName: this.roleNameHash[roleId], } return instance = this; }})()/**记录auth接口申请是否曾经完结 */CreateSingletonTabBar.prototype.getReceive = function () { return this.authData.received;}/**获取有权限的pages */CreateSingletonTabBar.prototype.getAuthPages = function () { return this.authData.pages;}/**获取有权限的actions */CreateSingletonTabBar.prototype.getAuthActions = function () { return this.authData.actions;}/**通过AUTH_CODE生成合乎小程序tabBar数据格式的authPages */CreateSingletonTabBar.prototype.geneAuthPage = function (auth_list = []) { console.log('got auth_list:',auth_list) let pages = []; if (auth_list && auth_list.length) { auth_list.map((item, index) => { pages.push({ index, ...AUTH_PAGE_HASH[item] }); }) } else { pages = [AUTH_PAGE_HASH['PAGE_ENVIRONMENT'], AUTH_PAGE_HASH['PAGE_USER']]; } return pages;}/**更新外部tabBar相干数据 */CreateSingletonTabBar.prototype.updateAuthData = function (objData = {}) { this.authData = { ...this.authData, ...objData };}/**选中tabBar:在含tabBar的页面内调用 selectTabBar(this) */CreateSingletonTabBar.prototype.selectTabBar = function (context) { let that = this; const computed = () => { let authMenus = [...that.getAuthPages()]; let currentPath = getCurrentRoute(); let pageIndex = authMenus.findIndex(item => item.pagePath.includes(currentPath)); pageIndex = pageIndex == -1 ? 0 : pageIndex; that.index = pageIndex; if (typeof context.getTabBar === 'function' && context.getTabBar()) { context.getTabBar().setData({ selected: pageIndex }) } return 1; } return that.doSthAfterDependentChangedPromise(computed)}/**判断角色是否领有某个action权限 */CreateSingletonTabBar.prototype.checkAuthAction = function (act_code) { let that = this; let computedCheckAuthAction = () => { return that.authData.actions.includes(act_code) } return that.doSthAfterDependentChangedPromise(computedCheckAuthAction)}/**获取角色role_id */CreateSingletonTabBar.prototype.getRoleId = function () { let that = this; let computedGetRoleId = () => { return that.authData.roleId } return that.doSthAfterDependentChangedPromise(computedGetRoleId)}/**如果某些逻辑须要在auth接口申请完结后执行,能够用此办法包装调用 */CreateSingletonTabBar.prototype.doSthAfterDependentChangedPromise = function (computed = () => {}) { let loopTicker = null; let that = this; const dependentTargetChanged = (resolver) => { if (that.authData.received) { clearTimeout(loopTicker); resolver(computed()); } else { loopTicker = setTimeout(() => { dependentTargetChanged(resolver) }, 200); } } return new Promise(resolve => { dependentTargetChanged(resolve) })}export const TBInstance = new CreateSingletonTabBar(0)
轻松应用!
在 custom-tab-bar
内实现动静 tabBar
,次要代码在 lifetimes
中
import { userLogin} from '../models/UserApi'import { showToast} from '../utils/WxApi'import { TBInstance} from './model'Component({ data: { selected: 0, list: [], }, methods: { switchTab(e) { const data = e.currentTarget.dataset const url = data.path wx.switchTab({ url }) this.setData({ selected: data.index }) } }, /**以上代码为官网示例所有 */ lifetimes: { /**这里是动静tabBar的要害代码 */ attached() { let appAuthMenus = [...TBInstance.getAuthPages()]; if (!TBInstance.getReceive() || !appAuthMenus.length) { /**登录后TBInstance内记录tabBar相干数据,如:可拜访页面、可操作动作... */ userLogin({ useCache: false }).then(res => { let { auth_list = [], action_list = [], role_id = 0, role_name = '普通用户' } = res?.data?.role_auth || {}; let authList = TBInstance.geneAuthPage(auth_list); TBInstance.updateAuthData({ received: true, pages: authList, actions: action_list, roleId: role_id, roleName: role_name, }) this.setData({ list: authList }) }).catch(err => { console.log(err) showToast(err.msg || '登陆失败') TBInstance.updateAuthData({ received: true, menus: TBInstance.geneAuthPage([]), actions: [], roleId: 0, roleName: '普通用户', }); }) } else { this.setData({ list: appAuthMenus }) } } }})
实现 tab
选中
调用selectTabBar
选中当前页面对应的tab
,不须要传递index
,因为不同角色即使领有的雷同页面,对应的索引也可能是不一样的,所以这个动静的索引放到了selectTabBar
外部实现
import { TBInstance} from '../../../custom-tab-bar/model'Page({ data: {}, onShow: function () { // 选中当前页面对应的tabBar,不须要传递index,因为不同角色即使领有的雷同页面,对应的索引也可能是不一样的,所以这个动静的索引放到了selectTabBar外部实现 TBInstance.selectTabBar(this); }})
实现判断是以后角色否有某个 action
的权限
import { TBInstance} from '../../../custom-tab-bar/model'Page({ data: { showAddNgBtn: false }, onShow: function () { // 判断是否有ACT_ADD_NG操作权限 TBInstance.checkAuthAction('ACT_ADD_NG').then(res => { this.setData({ showAddNgBtn: res }) }) }})
实现 tabBar
的初始化与页面逻辑同步执行
封装了doSthAfterDependentChangedPromise
办法自动检测tabBar
逻辑的执行状况,完结后才执行传入的代码逻辑
import { fetchShopsEnv} from '../../../models/InspectionApi'import { TBInstance} from '../../../custom-tab-bar/model'Page({ data: { list: [], }, onLoad: function () { TBInstance.doSthAfterDependentChangedPromise(this.getShopEnv) }, onShow: function () { TBInstance.selectTabBar(this); }, getShopEnv: function () { fetchShopsEnv().then(res => { this.setData({ list: res.data }) }).catch(err => {}) }})