react-navigation4.x动态路由和隐藏底部导航实现

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'
  }
});

参考
https://juejin.im/post/5cff4f516fb9a07ea803c0f5

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值