react umi/max 封装页签组件

news2024/11/27 21:07:18

1. models/tabs

// 全局共享数据示例
import { useState } from 'react';

const useUser = () => {
  const [items, setItems] = useState<any[]>([]);  // 页签的全局Item数据
  const [key, setKey] = useState<string>('/home');  // 页签的高亮Key

  return {
    items,
    setItems,
    key,
    setKey,
  };
};

export default useUser;

2. components/PageHeadTabs

import { Home } from '@/pages/Home';
import { useLocation, useModel } from '@umijs/max';
import { Dropdown, Tabs } from 'antd';
import { useEffect } from 'react';

type PageHeadTabsProps = {
  children: any;
  title: string;
};

const PageHeadTabs = (props: PageHeadTabsProps) => {
  const { children, title } = props; // Props获取元素、页面名称
  const { items, setItems, key, setKey } = useModel('tabs'); // 获取全局Item和Key
  const { pathname } = useLocation(); // 获取当前页的Pathname

  // 页签点击事件
  const onTabClick = (value: any) => {
    setKey(value); // 设置高亮的Key
    history.replaceState(null, '', value); // 拼接URL路径、但不执行跳转
  };

  // 关闭页签
  const onEdit = (targetKey: any, action: 'add' | 'remove') => {
    if (action === 'remove') {
      let newActiveKey = key;
      const lastIndex = items.findIndex((item) => item.key === targetKey);
      const newPanes = items.filter((item) => item.key !== targetKey);

      if (newPanes.length && newActiveKey === targetKey) {
        if (lastIndex - 1 >= 0) {
          newActiveKey = newPanes[lastIndex - 1].key;
        } else {
          newActiveKey = newPanes[0].key;
        }
      }

      setItems(newPanes);
      setKey(newActiveKey);
      history.replaceState(null, '', newActiveKey);
    }
  };

  // 关闭当前页
  const onCurrent = (e: any) => {
    e.domEvent.stopPropagation();

    let targetKey = JSON.parse(e?.key).name;
    let newActiveKey = key;
    const lastIndex = items.findIndex((item) => item.key === targetKey);
    const newPanes = items.filter((item) => item.key !== targetKey);

    if (newPanes.length && newActiveKey === targetKey) {
      if (lastIndex - 1 >= 0) {
        newActiveKey = newPanes[lastIndex - 1].key;
      } else {
        newActiveKey = newPanes[0].key;
      }
    }

    setItems(newPanes);
    setKey(newActiveKey);
    history.replaceState(null, '', newActiveKey);
  };

  // 关闭其他
  const onOther = (e: any) => {
    e.domEvent.stopPropagation();

    let targetKey = JSON.parse(e?.key).name;
    const newPanes = items.filter(
      (item) => item.key === targetKey || item.key === '/home',
    );

    setItems(newPanes);
    setKey(targetKey);
    history.replaceState(null, '', targetKey);
  };

  //关闭左侧
  const onLeft = (e: any) => {
    e.domEvent.stopPropagation();

    let targetKey = JSON.parse(e?.key).name;
    const lastIndex = items.findIndex((item) => item.key === targetKey);
    const newPanes = items
      .splice(0, lastIndex + 1)
      .filter((item) => item.key === targetKey || item.key === '/home');
    const oldIndex = newPanes.findIndex((item) => item.key === key);

    setItems(newPanes);
    if (oldIndex) {
      setKey(targetKey);
      history.replaceState(null, '', targetKey);
    }
  };

  // 关闭右侧
  const onRight = (e: any) => {
    e.domEvent.stopPropagation();
    let targetKey = JSON.parse(e?.key).name;
    const lastIndex = items.findIndex((item) => item.key === targetKey);
    const newPanes = items.splice(0, lastIndex + 1);
    const oldIndex = newPanes.findIndex((item) => item.key === key);

    setItems(newPanes);
    if (oldIndex) {
      setKey(targetKey);
      history.replaceState(null, '', targetKey);
    }
  };

  // 关闭全部
  const onAll = (e: any) => {
    e.domEvent.stopPropagation();

    const newPanes = items.splice(0, 1);

    setItems(newPanes);
    setKey('/home');
    history.replaceState(null, '', '/home');
  };

  const labelDropdown = (name: string, label: string) => {
    const lastIndex = items.findIndex((item) => item.key === name);
    return (
      <Dropdown
        menu={{
          items: [
            {
              label: '关闭当前',
              key: JSON.stringify({ name, key: 'current' }),
              disabled: name === '/home',
              onClick: onCurrent,
            },
            {
              label: '关闭其他',
              key: JSON.stringify({ name, key: 'other' }),
              disabled:
                (name === '/home' && items.length <= 1) ||
                (name !== '/home' && items.length <= 2),
              onClick: onOther,
            },
            {
              label: '关闭左侧',
              key: JSON.stringify({ name, key: 'left' }),
              disabled: lastIndex < 2,
              onClick: onLeft,
            },
            {
              label: '关闭右侧',
              key: JSON.stringify({ name, key: 'right' }),
              disabled:
                (name === '/home' && items.length <= 1) ||
                (name !== '/home' && items.length - lastIndex < 2),
              onClick: onRight,
            },
            {
              label: '全部关闭',
              key: JSON.stringify({ name, key: 'all' }),
              onClick: onAll,
              disabled: name === '/home' && items.length <= 1,
            },
          ],
        }}
        trigger={['contextMenu']}
      >
        <span>{label}</span>
      </Dropdown>
    );
  };

  useEffect(() => {
    const index = !items.find(({ key }) => key === pathname);
    const indexHome = !items.find(({ key }) => key === '/home');
    // 如果用户部署从主页进入,引入主页组件作为默认页签
    if (indexHome && pathname !== '/home') {
      const arr = {
        key: '/home',
        label: '首页',
        title: '首页',
        closable: false,
        children: <Home />,
      };
      setItems((item) => item?.concat([arr]));
    }
    // 添加当前页面到页签
    if (index) {
      const arr = {
        key: pathname,
        label: title,
        title: title,
        closable: pathname !== '/home',
        children: children,
      };
      setItems((item) => item?.concat([arr]));
    }
    setKey(pathname);
  }, []);

  useEffect(() => {
    // 页签长度发生变化时,塞入、更新所有标签右键下拉菜单
    setItems((items) =>
      items.map((item) => {
        return { ...item, label: labelDropdown(item.key, item.title) };
      }),
    );
  }, [items.length]);

  return (
    <Tabs
      hideAdd
      size="small"
      type="editable-card"
      activeKey={key}
      onEdit={onEdit}
      onTabClick={onTabClick}
      items={items}
    />
  );
};
export default PageHeadTabs;

3. pages/Home

import PageHeadTabs from '@/components/PageHeadTabs';
import React from 'react';

// *因为首页是默认页面所以有两种进入方式
// *第一种是通过/home进入,正常加载HomePage;
// *第二种是通过其他页面进入,加载Home即可。

export const Home: React.FC = () => {
  return <div>Home</div>;
};

const HomePage: React.FC = () => {
  return (
    <PageHeadTabs title="首页">
      <Home />
    </PageHeadTabs>
  );
};

export default HomePage;

4. 其他页面 

import PageHeadTabs from '@/components/PageHeadTabs';
import { Button } from 'antd';

// *除了Home页面,其他的包裹一层PageHeadTabs即可实现。

const AccessPage: React.FC = () => {

  return (
    <PageHeadTabs title="权限演示">
        <Button>按钮</Button>
    </PageHeadTabs>
  );
};

export default AccessPage;

5. 效果 

自己临时封装的一个小组件,功能如上图。

缺点:没有刷新和拖拽功能。

优点:可以缓存页面。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1392918.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

leetcode热题100.路径总和 III

Problem: 437. 路径总和 III 文章目录 题目思路1复杂度1Code1思路2复杂度2Code2 题目 给定一个二叉树的根节点 root &#xff0c;和一个整数 targetSum &#xff0c;求该二叉树里节点值之和等于 targetSum 的 路径 的数目。 路径 不需要从根节点开始&#xff0c;也不需要在叶…

Leetcode:128. 最长连续序列

128. 最长连续序列 乍一看感觉很简单&#xff0c;一看要用O(n)??? 因为我觉得题目很难而且题目看起来很简单&#xff0c;感觉以后会用到&#x1f606;&#xff0c;做个记录 1.朴素做法 思路 答:任何一段连续的数都有一个左端点&#xff1a;比如&#xff08;1&#xff0c;…

TCP 拥塞控制对数据延迟的影响

哈喽大家好&#xff0c;我是咸鱼 今天分享一篇文章&#xff0c;是关于 TCP 拥塞控制对数据延迟产生的影响的。作者在服务延迟变高之后进行抓包分析&#xff0c;结果发现时间花在了 TCP 本身的机制上面&#xff1a;客户端并不是将请求一股脑发送给服务端&#xff0c;而是只发送…

web架构师编辑器内容-编辑器组件图层面板功能开发01-锁定隐藏功能的开发

我们这一部分主要是对最右侧图层面板功能进行剖析&#xff0c;完成对应的功能的开发: 每个图层都对应编辑器上面的元素&#xff0c;有多少个元素就对应多少个图层&#xff0c;主要的功能如下&#xff1a; 锁定功能&#xff1a;点击锁定&#xff0c;在编辑器中没法编辑对应的组…

git 常规操作及设置

git 常规操作及设置 Git是一个分布式版本控制系统&#xff0c;可以用来跟踪文件的修改历史并与其他人进行协作开发。下面是一些常见的Git操作及设置&#xff1a; 初始化仓库&#xff1a;使用命令git init在当前目录创建一个新的Git仓库。 克隆仓库&#xff1a;使用命令git clo…

web terminal - 如何在mac os上运行gotty

gotty可以让你使用web terminal的方式与环境进行交互&#xff0c;实现终端效果 假设你已经配置好了go环境&#xff0c;首先使用go get github.com/yudai/gotty命令获取可执行文件&#xff0c;默认会安装在$GOPATH/bin这个目录下&#xff0c;注意如果你的go版本比较高&#xff…

C++设计模式(李建忠)笔记3

C设计模式&#xff08;李建忠&#xff09; 本文是学习笔记&#xff0c;如有侵权&#xff0c;请联系删除。 参考链接 Youtube: C设计模式 Gtihub源码与PPT&#xff1a;https://github.com/ZachL1/Bilibili-plus 豆瓣: 设计模式–可复用面向对象软件的基础 文章目录 C设计模…

线性代数的学习和整理23:用EXCEL计算 向量/向量组的点乘 (内积) (建设ing)

目录 前言&#xff1a;EXCEL里的的向量相关计算公式 0.1 EXCEL里相关公式 0.2 先说结论&#xff1a;向量组的点乘公式和 向量组的点乘公式不一样 1 向量的点乘 (内积) 1.1 向量的点乘公式 1.2 EXCEL里向量点乘的计算 ​编辑 1.3 向量点乘的性质 1.3.1 内积的公式…

docker环境下mongo副本集的部署及异常修复

最近更换了办公地点。部署在本地docker环境里的mongo数据库不能使用了。原因是本地的ip地址变更。以前的mongo副本集的配置需要更新。处理完后&#xff0c;索性重新记录一下mongo副本集在docker中的部署流程。 mongo的事务及副本集 我们先了解一下什么是事务&#xff0c;事务…

LangChain新发布v0.1.0稳定版本有哪些新功能?

▼最近直播超级多&#xff0c;预约保你有收获 今晚直播&#xff1a;《LangChain 框架案例实战》 —1— 首个稳定版本 LangChain v0.1.0 LangChain v0.1.0 发布后&#xff0c;就不仅仅是一个开发框架了&#xff0c;而是一个开发者平台&#xff08;LLMOps&#xff09;&#xff0c…

css-盒子等样式学习

盒子居中&#xff0c;继承外层盒子的宽高 兼容性&#xff08;border-box&#xff09;将边框收到盒子内部 初始化div 不用管box-setting content-box 还原 创建为一个类 &#xff0c;让所有需要还原的类 进行继承 padding 用法表示margin上下左右边距 body 外边距&…

Python ❀ 使用代码实现API接口调用详解

文章目录 1. 工具准备1.1. requests代码包1.2. BurpSuite抓包工具 2. 操作过程2.1. 一个简单的请求2.1.1. Burp获取响应2.1.2. 转发获取响应 2.2. 构造GET类型URL参数2.3. 构造请求头部2.4. 构造POST类型payload数据 本文主要讲解常用API接口如何使用python实现。 API&#xff…

【白话机器学习的数学】读书笔记(3)学习分类(感知机、逻辑回归)

三、学习分类 1.分类的目的 找到一条线把白点和黑点分开。这条直线是使权重向量成为法线向量的直线。(解释见下图) 直线的表达式为&#xff1a; ω ⋅ x ∑ i 1 n ω i ⋅ x i 0 \omegax \sum_{i1}^n\omega_i x_i 0 ω⋅xi1∑n​ωi​⋅xi​0 ω \omega ω是权重向量权…

IIS 缓存, 更新后前端资源不能更新问题

解决办法: 通常只需要index.html 不缓存即可, 其他文件都是根据index.html 中的引用去加载; 正确的做法是在 站点下增加 web.config 文件, 内容如下: 我这个是因为目录下有个config.js 配置文件, 也不能缓存, 所以加了两个 <?xml version"1.0" encoding&quo…

2018年认证杯SPSSPRO杯数学建模D题(第一阶段)投篮的最佳出手点全过程文档及程序

2018年认证杯SPSSPRO杯数学建模 对于投篮最佳出手点的探究 D题 投篮的最佳出手点 原题再现&#xff1a; 影响投篮命中率的因素不仅仅有出手角度、球感、出手速度&#xff0c;还有出手点的选择。规范的投篮动作包含两膝微屈、重心落在两脚掌上、下肢蹬地发力、身体随之向前上…

迅为RK3588开发板编译 Buildroot单独编译图形化界面(打包镜像)

上面 Kernel/U-Boot/Recovery/Rootfs 各个部分的编译后&#xff0c;将打包要用到的所有固件移动到 rockdev 目录下&#xff0c;然后打包为完整的 update.img 镜像。 首先在 linux 源码目录下输入以下命令进入编译的 UI 界面&#xff0c;进入之后如下所示&#xff1a; ./buil…

nginx+lua配置,一个域名配置https,docker集群使用

没安装kua的先安装lua 没有resty.http模块的&#xff0c;许配置 nginxlua配置&#xff0c;一个域名配置https&#xff0c;docker集群使用&#xff0c;一个域名配置https管理整个集群 lua做转发&#xff08;方向代理&#xff09; 1、ad_load.lua文件 ngx.header.content_typ…

如何录制屏幕视频?让视频制作更简单!

随着数字化时代的来临&#xff0c;录制屏幕视频成为一种常见的传播和教学方式。无论是制作演示文稿、教学视频&#xff0c;还是记录游戏操作&#xff0c;屏幕录制为用户提供了强大而灵活的工具。可是您知道如何录制屏幕视频吗&#xff1f;本文将深入介绍两种常见的屏幕录制方法…

蓝桥杯备赛 day 3 —— 高精度(C/C++,零基础,配图)

目录 &#x1f308;前言&#xff1a; &#x1f4c1; 高精度的概念 &#x1f4c1; 高精度加法和其模板 &#x1f4c1; 高精度减法和其模板 &#x1f4c1; 高精度乘法和其模板 &#x1f4c1; 高精度除法和其模板 &#x1f4c1; 总结 &#x1f308;前言&#xff1a; 这篇文…

web开发学习笔记(8.java web后端开发基础知识)

1.使用spring开发的优势&#xff0c;spring发展到今天已经形成了一种开发生态圈&#xff0c;提供了若干个子项目&#xff0c;每个项目用于完成特定的功能。使用spring全家桶&#xff0c;可以做到很多事情&#xff0c;可以很方便的套用很多的组件。 2.pom构成 指定父工程 <p…