1. 背景
由于rn移动端有一个需求要求通过工作台进入运维中心需要在底部导航栏上展示满足权限过滤的前四个模块。首先我需要把所有路由枚举出来,然后要通过权限过滤得到前四个路由模块,然后在底部导航栏上动态展示出来。那么要怎么在react-navigation4中的createBottomTabNavigator过滤不需要的路由呢?如何在不同页面隐藏底部路由?底部导航如何获取到权限?
2. 如何实现动态路由
由于createBottomTabNavigator
API中有一个tabBarComponent
属性可以自定义导航组件,而自定义导航组件可以用通过引入
react-navigation-tabs
自带的底部自定义组件BottomTabBar
,这个组件作用是实现跳转功能和底部导航按钮的样式展示。那么就可以将BottomTabBar封装为自己的组件,实现权限过滤及隐藏的逻辑,从而实现动态路由。
3. 如何隐藏动态路由导航栏
正常情况下,都会用createStackNavigator包裹createBottomTabNavigator。利用tabBarVisible实现:
const AppRouter = createStackNavigator({
MyTab: MyTab,
BottomNavigator: BottomNavigator,
});
BottomNavigator.navigationOptions = ({ navigation }) => {
let tabBarVisible = true;
if (navigation.state.index > 0) {
tabBarVisible = false;
}
return {
tabBarVisible,
};
};
自定义底部导航栏可以直接将BottomTabBar
样式设置为display:flase
实现:
<BottomTabBar navigation={myNavigation} style={{ display: this.state.isHidden ? 'none' : 'flex' }} />;
可以利用DeviceEventEmitter
监听组件的改变,控制底部导航栏的隐藏显示。
4. 如何获取权限数据
可以通过给路由组件设置screenProps属性来给路由组件传递数据。路由组件中可以通过this.props.screenProps
获取到传递过来的数据,然后进行路由的权限过滤。
screenProps:后代路由都可以通过this.props.screenProps获取到值。
<MyTabRouter screenProps={{ module: this.props.user.menuByUserModule }} />;
5. 动画效果及代码
//router.js
import OperationIndex from './OperationIndex.js';
const RootNavigator = createStackNavigator({
OperationIndex: {
screen: OperationIndex,
navigationOptions: {
headerShown: false, // 自带头部置空
gestureEnabled: false // 左侧滑动返回 ios默认开启,android默认关闭
}
}
})
//OperationIndex.js
import React from 'react';
import { Image, Text, StyleSheet, StatusBar } from 'react-native';
import { createBottomTabNavigator } from 'react-navigation-tabs';
import { BottomTabBar } from 'react-navigation-tabs';
// import { DeviceEventEmitter } from 'react-native';
// 引入页面容器
import OperationCenter from './OperationCenter'
import AssetOperation from './AssetOperation'
import CreateApply from './CreateApply'
import MyApprove from './MyApprove'
import OperationAssist from './OperationAssist'
import WorkorderOperation from './WorkorderOperation'
import SpecialOperation from './SpecialOperation'
import CommandRecheck from './CommandRecheck'
import PasswordTogether from './PasswordTogether'
import ApproveHistory from './ApproveHistory'
import OperationLog from './OperationLog'
import AgentSet from './AgentSet'
import { connect } from "react-redux";
import { bindActionCreators } from 'redux';
import UserRoleProActions from '../../store/actions/user';
const operationInfo = {
'运维中心': { route: 'OperationCenter', on: require('../../images/Icon/operations/OperationCenter_on.png'), off: require('../../images/Icon/operations/OperationCenter.png') },
'资产运维': { route: 'AssetOperation', on: require('../../images/Icon/operations/AssetOperation_on.png'), off: require('../../images/Icon/operations/AssetOperation.png') },
'新建申请': { route: 'CreateApply', on: require('../../images/Icon/operations/CreateApply.png'), off: require('../../images/Icon/operations/CreateApply.png') },
'待我审批': { route: 'MyApprove', on: require('../../images/Icon/operations/MyApprove_on.png'), off: require('../../images/Icon/operations/MyApprove.png') },
'运维协助': { route: 'OperationAssist', on: require('../../images/Icon/operations/OperationAssist_on.png'), off: require('../../images/Icon/operations/OperationAssist.png') },
'工单运维': {
route: 'WorkorderOperation', on: require('../../images/Icon/operations/WorkorderOperation_on.png'), off: require('../../images/Icon/operations/WorkorderOperation.png')
},
'特殊运维': { route: 'SpecialOperation', on: require('../../images/Icon/operations/SpecialOperation_on.png'), off: require('../../images/Icon/operations/SpecialOperation.png') },
'命令复核': { route: 'CommandRecheck', on: require('../../images/Icon/operations/CommandRecheck_on.png'), off: require('../../images/Icon/operations/CommandRecheck.png') },
'密码会同': {
route: 'PasswordTogether', on: require('../../images/Icon/operations/PasswordTogether_on.png'), off: require('../../images/Icon/operations/PasswordTogether.png')
},
'审批历史': { route: 'ApproveHistory', on: require('../../images/Icon/operations/ApproveHistory_on.png'), off: require('../../images/Icon/operations/ApproveHistory.png') },
'运维日志': { route: 'OperationLog', on: require('../../images/Icon/operations/OperationLog_on.png'), off: require('../../images/Icon/operations/OperationLog.png') },
'代理设置': { route: 'AgentSet', on: require('../../images/Icon/operations/AgentSet_on.png'), off: require('../../images/Icon/operations/AgentSet.png') },
}
/* ****************************** tabbar ****************************** */
const getLabel = (label, tintColor) => {
return <Text style={[styles.label, { color: tintColor }]}>{label}</Text>;
};
const getBarIcon = (routeName, focused) => {
let Icon;
if (routeName === '新建申请') {
Icon = <Image style={styles.centerAdd} source={operationInfo[routeName].on} />;
} else {
Icon = focused ? <Image style={styles.icon} source={operationInfo[routeName].on} />
:
<Image style={styles.icon} source={operationInfo[routeName].off} />;
}
return Icon;
};
const getOption = (name) => {
return {
tabBarLabel: ({ focused, tintColor }) => {
return getLabel(name, tintColor);
},
tabBarIcon: ({ focused, tintColor }) => {
return getBarIcon(name, focused);
},
tabBarOnPress: ({ navigation, defaultHandler }) => {
StatusBar.setBarStyle('light-content');
defaultHandler();
}
}
}
/* 路由页面 */
let tabbarRoutes = {
OperationCenter: {
screen: OperationCenter,
navigationOptions: getOption('运维中心')
},
AssetOperation: {
screen: AssetOperation,
navigationOptions: getOption('资产运维')
},
CreateApply: {
screen: CreateApply,
navigationOptions: getOption('新建申请')
},
MyApprove: {
screen: MyApprove,
navigationOptions: getOption('待我审批')
},
OperationAssist: {
screen: OperationAssist,
navigationOptions: getOption('运维协助')
},
WorkorderOperation: {
screen: WorkorderOperation,
navigationOptions: getOption('工单运维')
},
SpecialOperation: {
screen: SpecialOperation,
navigationOptions: getOption('特殊运维')
},
CommandRecheck: {
screen: CommandRecheck,
navigationOptions: getOption('命令复核')
},
PasswordTogether: {
screen: PasswordTogether,
navigationOptions: getOption('密码会同')
},
ApproveHistory: {
screen: ApproveHistory,
navigationOptions: getOption('审批历史')
},
OperationLog: {
screen: OperationLog,
navigationOptions: getOption('运维日志')
},
AgentSet: {
screen: AgentSet,
navigationOptions: getOption('代理设置')
}
}
/* routes */
const originalRoutes = [
{ key: 'OperationCenter', routeName: 'OperationCenter', name: '运维中心', params: undefined },
{ key: 'AssetOperation', routeName: 'AssetOperation', name: '资产运维', params: undefined },
{ key: 'CreateApply', routeName: 'CreateApply', name: '新建申请', params: undefined },
{ key: 'MyApprove', routeName: 'MyApprove', name: '待我审批', params: undefined },
{ key: 'OperationAssist', routeName: 'OperationAssist', name: '运维协助', params: undefined },
{ key: 'WorkorderOperation', routeName: 'WorkorderOperation', name: '工单运维', params: undefined },
{ key: 'SpecialOperation', routeName: 'SpecialOperation', name: '特殊运维', params: undefined },
{ key: 'CommandRecheck', routeName: 'CommandRecheck', name: '命令复核', params: undefined },
{ key: 'PasswordTogether', routeName: 'PasswordTogether', name: '密码会同', params: undefined },
{ key: 'ApproveHistory', routeName: 'ApproveHistory', name: '审批历史', params: undefined },
{ key: 'OperationLog', routeName: 'OperationLog', name: '运维日志', params: undefined },
{ key: 'AgentSet', routeName: 'AgentSet', name: '代理设置', params: undefined },
];
//自定义BottomTabBar
class CustomBottomTabBar extends React.Component {
//这里对navigation进行处理,注意这里不能直接修改props.navigation,会报错,
//所以只需要传入一个自定义的navigation,而BottomTabBar只会用到navigation.state中routes和index,
//所以就构造这么一个虚假的navigation就可以了
state = {
isHidden: false
}
/* 控制底部导航栏显示隐藏 */
// componentDidMount() {
// DeviceEventEmitter.addListener('closeBottomTab', (val) => {
// this.setState({ isHidden: val })
// });
// DeviceEventEmitter.emit('closeBottomTab', false);
// }
// componentWillUnmount() {
// DeviceEventEmitter.removeListener('closeBottomTab');
// }
dealNavigation = () => {
const { routes, index } = this.props.navigation.state;
// 根据是否需要显示商品推荐菜单来决定state中的routes
let finalRoutes = this.filterOperation();
const currentRoute = routes[index];
return {
state: {
index: finalRoutes.findIndex(route => currentRoute.key === route.key), //修正index
routes: finalRoutes
}
};
};
filterOperation = (arr) => {
if (this.props.screenProps.module) {
let operation = this.props.screenProps.module.filter(item => item.menuName === '运维中心')[0].childMenu.slice(0, 4);
let nameArr = operation.map(item => item.menuName);
return originalRoutes.filter(route => route.name === '运维中心' || nameArr.includes(route.name))
} else {
return originalRoutes
}
}
render() {
const { navigation, ...restProps } = this.props;
const myNavigation = this.dealNavigation();
return <BottomTabBar {...restProps} navigation={myNavigation} style={{ display: this.state.isHidden || myNavigation.state.index === -1 ? 'none' : 'flex' }} />;
}
}
const MyTabRouter = createBottomTabNavigator(tabbarRoutes, {
tabBarComponent: CustomBottomTabBar,
backBehavior: 'initialRoute',
swipeEnabled: true,
animationEnabled: true,
initialRouteName: 'OperationCenter',
tabBarOptions: {
// label和icon的背景色 活跃状态下(选中) ios
activeBackgroundColor: 'rgba(0,0,0,0)',
// label和icon的前景色 活跃状态下(选中)
activeTintColor: '#FF9023',
// label和icon的背景色 不活跃状态下(未选中) ios
inactiveBackgroundColor: 'rgba(0,0,0,0)',
// label和icon的前景色 不活跃状态下(未选中)
inactiveTintColor: '#817F7F',
showIcon: true,
showLabel: true,
style: {
borderTopColor: '#E4E7ED',
backgroundColor: 'rgba(250,250,250,0.9)',
}
}
});
class BottomTab extends React.Component {
//这里必须有这个静态属性,表示将这个页面视为一个navigator,这样才能和AppStack共用一套导航系统
static router = MyTabRouter.router;
render() {
//screenProps:后代路由都可以通过this.props.screenProps获取到值
return <MyTabRouter {...this.props} screenProps={{ module: this.props.user.menuByUserModule }} />;
}
}
const mapStateToProps = state => ({ user: state.user });
const mapDispatchToProps = (dispatch) => ({
actions: bindActionCreators(UserRoleProActions, dispatch),
dispatch: dispatch
});
export default connect(mapStateToProps, mapDispatchToProps)(BottomTab);
const styles = StyleSheet.create({
icon: {
},
centerAdd: {
marginTop: -20
},
label: {
fontSize: 11,
textAlign: 'center'
}
});