React 后台管理制作历史导航条

更多文章请访问 深的个人博客

效果图

在这里插入图片描述

项目目录

在这里插入图片描述

分析

1、在Tabs组件里使用 this.props.history.listen 监听路由变化,路由变化时就添加对应的历史记录

2、在Tabs组件和LeftMenu组件里使用事件订阅进行通信(pubsub-js), 当点击Tabs组件里的标签时,leftMenu跳转到相应的菜单栏

Tabs组件注意点

1、因为Tabs组件的标签是有展开和隐藏功能,所以需要获取到相应盒子的高度,使用refs获取盒子高度时,可以先执行this.setState({})更新一下页面,这样获取的高度才是准确的(或许也需要使用定时器延迟获取高度),在网上查阅资料的时候,查到可以使用Mutation Observer API 用来监视 DOM 变动

2、需要使用监听浏览器窗口的大小: window.addEventListener(“resize”, feekback, false),Tabs展开的高度是需要动态改变的

3、点击菜单栏和删除历史记录时都需要重新获取相应盒子的高度

4、通过 this.props.history.listen 监听路由改变,需要在组件卸载的时候时候执行解绑(监听窗口大小的的事件也是需要解绑),解绑this.props.history.listen 的方法是:在 componentWillUnmount 生命周期中执行 this.props.history.listen 事件的返回值 ,示例如下:

import React, { Component } from 'react';
let unListen; // this.props.history.listen 事件的返回值,用于解绑listen事件
class Tabs extends Component {
    componentDidMount () {
        // 监听路由变化
        unListen = this.props.history.listen((route) => {

        });
        // 监听窗口的变化
        window.addEventListener("resize", this.listenerResize, false);
    }
    componentWillUnmount () {
        // 执行解绑this.props.history.listen
        unListen && unListen(); 
        // 解绑窗口监听
        window.removeEventListener("resize", this.listenerResize); 
  	}
    render() {
        return (
            <div>
                
            </div>
        );
    }
}

export default Tabs;

完整代码

LeftMenu.jsx
import React, { Component } from "react";
import { Menu } from "antd";
import PubSub from "pubsub-js"; //引入
import { AppstoreOutlined, MailOutlined } from "@ant-design/icons";
import menuList from "../../config/menusConfig";
import root from "../../config/root"
import "./leftmenu.scss";
const { SubMenu } = Menu;

export default class menu extends Component {

  state = {
    collapsed: false,
    selectedKeys: [],
    openKeys: [],
    menubar: [],
    role: "companyAdmin"
  };
  componentDidMount () {
    let userInfo = localStorage.getItem("userInfo");
    if (userInfo) {
      let { role } = JSON.parse(userInfo);
      this.setState({ role })
    }

    //  点击Tabs组件时触发,进行菜单匹配
    PubSub.subscribe("switch", (_, path) => {
      this.setState({ selectedKeys: [path] });
      this.judgeSubmenu(path);
    }); //订阅
    const menubar = this.creatMenu(menuList);
    this.setState({
      menubar,
    });
  }
  toggleCollapsed = () => {
    this.setState({
      collapsed: !this.state.collapsed,
    });
  };
  goPage = (path) => {
    return () => {
      this.props.goPage(path);
    };
  };
  // 选择菜单
  selectMenu = (obj) => {
    this.setState({ selectedKeys: [obj.key] });
    PubSub.publish("updateList"); //发布消息
  };
  openSubmenu = (obj) => {
    this.setState({ openKeys: obj });
  };
  // 判断当前url对应的菜单,点击Tabs时需要进行匹配
  judgeSubmenu (key) {
    menuList.forEach((item) => {
      if (item.children) {
        item.children.forEach((itemChild) => {
          if (itemChild.key === key) {
            this.setState({ openKeys: [item.key] });
            return item;
          }
        });
      } else {
        if (item.key === key) {
          this.setState({ openKeys: [item.key] });
          return item;
        }
      }
    });
  }
  // 生成menu
  creatMenu = (itemList) => {
    return itemList.map((item) => {
      if (this.judgement(item)) {
        if (item.children) {
          return (
            <SubMenu
              key={item.key}
              title={item.title}
              icon={<AppstoreOutlined />}
            >
              {this.creatMenu(item.children)}
            </SubMenu>
          );
        } else {
          return (
            <Menu.Item
              key={item.key}
              icon={<MailOutlined />}
              onClick={this.goPage(item.key)}
            >
              {item.title}
            </Menu.Item>
          );
        }
      }
    });
  };
  // 判断当前登录的角色是否有权限查看该菜单项
  judgement = (menu) => {
    let { role } = this.state
    if (root[role][menu.key].show) {
      return true;
    } else {
      return false;
    }
  };

  render () {
    let { selectedKeys, openKeys, menubar } = this.state;
    return (
      <div style={{ width: 200 }} className="menu_box">
        {/* <Button
          type="primary"
          onClick={this.toggleCollapsed}
          style={{ marginBottom: 16 }}
        >
          {React.createElement(
            this.state.collapsed ? MenuUnfoldOutlined : MenuFoldOutlined
          )}
        </Button> */}
        <Menu
          defaultSelectedKeys={["0"]}
          defaultOpenKeys={["sub0"]}
          mode="inline"
          theme="dark"
          selectedKeys={selectedKeys}
          onClick={this.selectMenu}
          onOpenChange={this.openSubmenu}
          openKeys={openKeys}
        >
          {menubar}
        </Menu>
      </div>
    );
  }
}
Tabs.jsx
import React, { Component } from "react";
import { withRouter } from "react-router-dom";
import {
  CloseOutlined,
  HomeOutlined,
  RightSquareOutlined,
} from "@ant-design/icons";
import PubSub from "pubsub-js"; //引入
import "./tabs.scss";
let unListen; // this.props.history.listen 事件的返回值,用于解绑listen事件
class Tabs extends Component {
  historyObj = {
    "/admin/company": "公司管理",
    "/admin/personnel": "人员管理",
    "/admin/warehouse": "仓库管理",
    "/admin/warehouse/detail": "仓库详情",
    "/admin/maintenance": "维修厂管理",
    "/admin/maintenance/detail": "维修厂详情",
    "/admin/mac": "mac地址管理",
    "/admin/steel": "型钢管理",
    "/admin/project": "项目管理",
    "/admin/project/detail": "项目详情",
    "/admin/demand": "需求单管理",
    "/admin/demand/detail": "需求单详情",
    "/admin/clock/size": "规格尺寸",
    "/admin/clock/material": "材料厂商",
    "/admin/clock/manufacturing": "制造厂商",
    "/admin/clock/transport": "运输公司",
    "/admin/clock/price": "型钢单价",
    "/admin/log": "日志管理",
    "/admin/modify": "修改密码",
  };
  state = {
    pathList: [], // 历史记录数组
    activePath: "", // 当前的path
    showFlag: true, // 展开与隐藏的标识
    outHeight: 40, // tab外部盒子的高度,当tab的数量超过一行时会隐藏
    tabHeight: 0, // tab内部的盒子高度,
  };
  componentDidMount () {
    PubSub.subscribe("updateList", (_) => {
      //   console.log("数据更新");
      if (this.tabDiv && this.tabDiv.clientHeight) {
        this.setState({ tabHeight: this.tabDiv.clientHeight });
        setTimeout(() => {
          if (
            !this.state.showFlag &&
            this.outDiv.clientHeight < this.tabDiv.clientHeight
          ) {
            this.setState({ outHeight: this.tabDiv.clientHeight });
          }
        }, 400);
      }
    }); //订阅
    let activePath = localStorage.getItem("activePath");
    this.setState({ activePath });
    // 监听路由变化
    unListen = this.props.history.listen((route) => {
      //   console.log(route.pathname); // 这个route里面有当前路由的各个参数信息
      //   console.log(route.pathname == "/admin/index");
      localStorage.setItem("activePath", route.pathname);
      this.setState({ activePath: route.pathname });
      if (
        route.pathname === "/admin/index" ||
        route.pathname === "/" ||
        route.pathname === "/login" ||
        route.pathname === "/admin/error/not" ||
        route.pathname === "/admin/error/redirect" ||
        route.pathname === "/admin/error/systemerror"
      ) {
        return;
      }
      //   console.log(this.historyObj[route.pathname]);
      let { pathList } = this.state;
      let index = pathList.findIndex((item) => {
        return item === route.pathname;
      });
      if (index === -1) {
        pathList.push(route.pathname);
        this.setState({ pathList });
      }
    });
    window.addEventListener("resize", this.listenerResize, false);
  }
  // 浏览器窗口变化执行的函数
  listenerResize = () => {
    this.setState({});
    if (this.outDiv.clientHeight > this.tabDiv.clientHeight) {
      this.setState({ outHeight: this.tabDiv.clientHeight });
      if (this.tabDiv.clientHeight === 40) {
        let { showFlag } = this.state;
        this.setState({ showFlag: !showFlag });
      }
    }
    if (
      !this.state.showFlag &&
      this.outDiv.clientHeight < this.tabDiv.clientHeight
    ) {
      this.setState({ outHeight: this.tabDiv.clientHeight });
    }
    this.setState({ tabHeight: this.tabDiv.clientHeight });
  };
  // 删除历史记录
  removeHistory = (path) => {
    return () => {
      let { pathList } = this.state;
      let list = pathList.filter((item) => {
        return item !== path;
      });
      setTimeout(() => {
        this.setState({ pathList: list });
        if (this.outDiv.clientHeight > this.tabDiv.clientHeight) {
          this.setState({
            outHeight: this.tabDiv.clientHeight,
            tabHeight: this.tabDiv.clientHeight,
          });
          if (this.tabDiv.clientHeight === 40) {
            let { showFlag } = this.state;
            this.setState({ showFlag: !showFlag });
          }
        }
      }, 100);
    };
  };
  // 页面跳转
  goPage = (path) => {
    return () => {
      this.props.history.push({ pathname: path });
      PubSub.publish("switch", path); //发布消息
    };
  };
  goHome = () => {
    // console.log("执行返回");
    this.props.history.push({ pathname: "/admin/index" });
  };
  // 展示与隐藏
  showFunc = () => {
    let { showFlag } = this.state;
    this.setState({ showFlag: !showFlag });
    if (showFlag) {
      this.setState({ outHeight: this.tabDiv.clientHeight });
    } else {
      this.setState({ outHeight: 40 });
    }
  };
  componentWillUnmount () {
    unListen && unListen(); // 执行解绑this.props.history.listen
    window.removeEventListener("resize", this.listenerResize);
  }
  render () {
    let { pathList, activePath, outHeight, tabHeight, showFlag } = this.state;
    return (
      <div
        className="tab_out_box"
        ref={(outDiv) => (this.outDiv = outDiv)}
        style={{ height: outHeight }}
      >
        <div className="tabs_box" ref={(tabDiv) => (this.tabDiv = tabDiv)}>
          <div
            onClick={this.goHome}
            className={
              activePath === "" || activePath === "/admin/index"
                ? `home_box home_null_box`
                : `home_box home_block_box`
            }
          >
            <HomeOutlined style={{ fontSize: 18 }} />
          </div>
          {pathList.map((item) => {
            return (
              <div
                className={item === activePath ? `tab_active tabs` : "tabs"}
                key={item}
              >
                <div
                  className={
                    item === activePath ? `tabs_text_active tabs_text` : "tabs"
                  }
                  onClick={this.goPage(item)}
                >
                  {this.historyObj[item]}
                </div>
                <CloseOutlined
                  onClick={this.removeHistory(item)}
                  style={{ fontSize: 12 }}
                  className={item === activePath ? `tabs_icon_active` : ""}
                />
              </div>
            );
          })}
        </div>
        <div
          className={tabHeight > 40 ? `spread_icon show_spread` : `spread_icon`}
        >
          <RightSquareOutlined
            style={{ fontSize: 20 }}
            onClick={this.showFunc}
            className={
              showFlag
                ? `square_outlined show_right`
                : `square_outlined show_Down`
            }
          />
          {/* <DownSquareOutlined
            style={{ fontSize: 20 }}
            className= {showFlag ? `square_outlined show_Down` :`square_outlined`}
            onClick={() => {
              this.showFunc(false);
            }}
          /> */}
        </div>
      </div>
    );
  }
}

export default withRouter(Tabs);

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值