react +typescript实现锚点目录+内容,原生方法实现

* {
  box-sizing: border-box;
}
.active {
  color: blue;
}
.content {
  background-color: #fff;
  width: 500px;
}

 效果如上。

import { Layout, Table } from "antd";
import { ColumnsType } from "antd/lib/table";
import React, { useEffect, useState } from "react";
import { Anchor } from "antd";
import "./MyAnchor.less";

const { Link } = Anchor;
const { Content } = Layout;
export default function MyAnchoe() {
  const [active, setActive] = useState(0);
  interface DataType {
    key: React.Key;
    interfaceNm: string;
    resourceNm: number;
    permission: string;
  }
  const columns: ColumnsType<DataType> = [
    {
      title: "接口路径",
      dataIndex: "interfaceNm",
      render: (text: string) => <a>{text}</a>,
    },
    {
      title: "接口名称",
      dataIndex: "resourceNm",
    },
    {
      title: "状态",
      dataIndex: "permission",
    },
  ];
  let list: any = [];
  const rowSelection = {
    onSelectAll: (selected: any, selectedRows: any, changeRows: any) => {
      interfaceList.map((a: any) => {
        a.permissionRestInfList.map((b: any) => {
          changeRows.map((c: any) => {
            if (c.key === b.key) {
              b.isChecked = selected;
            }
            return b;
          });
          return a;
        });
      });
      for (let i = 0; i < interfaceList.length; i++) {
        for (
          let j = 0;
          j < interfaceList[i].permissionRestInfList.length;
          j++
        ) {
          if (interfaceList[i].permissionRestInfList[j].isChecked) {
            list.push(interfaceList[i].permissionRestInfList[j]);
          }
        }
      }
    },
    onSelect: (
      record: any,
      selected: any,
      selectedRows: any,
      nativeEvent: any
    ) => {
      interfaceList.map((i: any) => {
        i.permissionRestInfList.map((j: any) => {
          if (record.key === j.key) {
            j.isChecked = !j.isChecked;
          }
          return j;
        });
        return i;
      });
      for (let i = 0; i < interfaceList.length; i++) {
        for (
          let j = 0;
          j < interfaceList[i].permissionRestInfList.length;
          j++
        ) {
          if (interfaceList[i].permissionRestInfList[j].isChecked) {
            list.push(interfaceList[i].permissionRestInfList[j]);
          }
        }
      }
    },
    getCheckboxProps: (record: DataType) => ({
      disabled: record.permission === "1",
      permission: record.permission,
    }),
  };
  const interfaceList: any = [
    {
      permissionRestInfList: [
        {
          checkCustomAuth: "1",
          checkOffLine: "1",
          customMade: "0",
          interfaceNm: "tax/v2/TXDECINF/申报明细查询",
          key: "tax/v2/TXDECINF/申报明细查询",
          onLineOrNot: "1",
          permission: "0",
          resourceId: 16,
          resourceNm: "个税服务",
          restId: "v2/TTXDECINF",
          restNm: "申报明细查询",
          serviceId: "tax",
        },
        {
          checkCustomAuth: "1",
          checkOffLine: "1",
          customMade: "0",
          interfaceNm: "usermanage/OPPSTUPT/修改岗位",
          key: "usermanage/OPPSTUPT/修改岗位",
          onLineOrNot: "1",
          permission: "0",
          resourceId: 16,
          resourceNm: "个税服务",
          restId: "OPPSTUPT",
          restNm: "修改岗位",
          serviceId: "usermanage",
        },
        {
          checkCustomAuth: "1",
          checkOffLine: "1",
          customMade: "0",
          interfaceNm: "usermanage/OPPSTUPT/增加岗位",
          key: "usermanage/OPPSTUPT/增加岗位",
          onLineOrNot: "1",
          permission: "0",
          resourceId: 16,
          resourceNm: "个税服务",
          restId: "OPPSTUPT",
          restNm: "增加岗位",
          serviceId: "usermanage",
        },
      ],
      resourceNm: "个税服务",
    },
    {
      permissionRestInfList: [
        {
          interfaceNm: "tax/v2/TXDECINF/申报明细查询1",
          key: "tax/v2/TXDECINF/申报明细查询1",
          onLineOrNot: "1",
          permission: "0",
          resourceId: 16,
          resourceNm: "嘻嘻嘻",
          restId: "v2/TTXDECINF",
          restNm: "申报明细查询1",
          serviceId: "tax",
        },
        {
          interfaceNm: "usermanage/OPPSTUPT/修改岗位1",
          key: "usermanage/OPPSTUPT/修改岗位1",
          onLineOrNot: "1",
          permission: "0",
          resourceId: 16,
          resourceNm: "嘻嘻嘻",
          restId: "OPPSTUPT",
          restNm: "修改岗位1",
          serviceId: "usermanage",
        },
        {
          checkCustomAuth: "1",
          checkOffLine: "1",
          customMade: "0",
          interfaceNm: "usermanage/OPPSTUPT/增加岗位2",
          key: "usermanage/OPPSTUPT/增加岗位2",
          onLineOrNot: "1",
          permission: "0",
          resourceId: 16,
          resourceNm: "嘻嘻嘻",
          restId: "OPPSTUPT",
          restNm: "增加岗位2",
          serviceId: "usermanage",
        },
      ],
      resourceNm: "嘻嘻嘻",
    },
    {
      permissionRestInfList: [
        {
          interfaceNm: "tax/v2/TXDECINF/申报明细查询3",
          key: "tax/v2/TXDECINF/申报明细查询3",
          onLineOrNot: "1",
          permission: "0",
          resourceId: 16,
          resourceNm: "琪琪",
          restId: "v2/TTXDECINF",
          restNm: "申报明细查询3",
          serviceId: "tax",
        },
        {
          interfaceNm: "usermanage/OPPSTUPT/修改岗位4",
          key: "usermanage/OPPSTUPT/修改岗位4",
          onLineOrNot: "1",
          permission: "0",
          resourceId: 16,
          resourceNm: "琪琪",
          restId: "OPPSTUPT",
          restNm: "修改岗位4",
          serviceId: "usermanage",
        },
        {
          interfaceNm: "usermanage/OPPSTUPT/增加岗位4",
          key: "usermanage/OPPSTUPT/增加岗位4",
          onLineOrNot: "1",
          permission: "0",
          resourceId: 16,
          resourceNm: "琪琪",
          restId: "OPPSTUPT",
          restNm: "增加岗位4",
          serviceId: "usermanage",
        },
      ],
      resourceNm: "琪琪",
    },
    {
      permissionRestInfList: [
        {
          interfaceNm: "password/OPPSTUPT/哈哈哈查询",
          key: "password/OPPSTUPT/哈哈哈查询",
          onLineOrNot: "1",
          permission: "0",
          resourceId: 16,
          resourceNm: "哈哈哈",
          restId: "OPPSTUPT",
          restNm: "哈哈哈查询",
          serviceId: "password",
        },
        {
          interfaceNm: "password/OPPSTUPT/哈哈哈修改",
          key: "password/OPPSTUPT/哈哈哈修改",
          onLineOrNot: "1",
          permission: "1",
          resourceId: 16,
          resourceNm: "哈哈哈",
          restId: "OPPSTUPT",
          restNm: "哈哈哈修改",
          serviceId: "password",
        },
        {
          interfaceNm: "password/OPPSTUPT/哈哈哈增加",
          key: "password/OPPSTUPT/哈哈哈增加",
          onLineOrNot: "1",
          permission: "0",
          resourceId: 16,
          resourceNm: "哈哈哈",
          restId: "OPPSTUPT",
          restNm: "哈哈哈增加",
          serviceId: "password",
        },
      ],
      resourceNm: "哈哈哈",
    },
  ];
  const catalog = ["个税服务", "嘻嘻嘻", "琪琪", "哈哈哈"];
  useEffect(() => {
    interfaceList.map((el: any) => {
      el.permissionRestInfList.map((item: any) => {
        return (item.isChecked = false);
      });
      return el;
    });
  }, []);

  useEffect(() => {
    window.addEventListener("scroll", onScroll, false);
    return () => window.removeEventListener("scroll", onScroll);
  }, []);

  const onScroll = () => {
    // 获取所有锚点元素
    const navContents = document.querySelectorAll(".content p");
    console.log('na',navContents);
    
    // 所有锚点元素的 offsetTop
    const offsetTopArr: any = [];
    navContents.forEach((item: any) => {
      offsetTopArr.push(item.offsetTop);
    });
    // 获取当前文档流的 scrollTop
    const scrollTop =
      document.documentElement.scrollTop || document.body.scrollTop;
    console.log("scrollTop", scrollTop);
    console.log("offsetTopArr", offsetTopArr);
    // 定义当前点亮的导航下标
    let navIndex = 0;
    for (let n = 0; n < offsetTopArr.length; n++) {
      // 如果 scrollTop 大于等于第n个元素的 offsetTop 则说明 n-1 的内容已经完全不可见
      // 那么此时导航索引就应该是n了
      if (scrollTop >= offsetTopArr[n]) {
        navIndex = n;
      }
      if (
        scrollTop + document.documentElement.clientHeight ===
        document.documentElement.scrollHeight
      ) {
        navIndex = offsetTopArr.length - 1;
      }
    }
    console.log("navIndex", navIndex);

    setActive(navIndex);
  };

  const scrollTo = (index: number, item: any) => {
    // 获取目标的 offsetTop
    // css选择器是从 1 开始计数,我们是从 0 开始,所以要 +1
    setActive(index);
    // const target = document.querySelector(
    //   `.content p:nth-child(${index + 1})`
    // ) as HTMLElement;
    const target = document.getElementById(item) as HTMLElement;
    console.log("target", target);
    const targetOffsetTop = target.offsetTop;
    console.log("aa", targetOffsetTop);

    // 获取当前 offsetTop
    let scrollTop =
      document.documentElement.scrollTop || document.body.scrollTop;
    // 定义一次跳 50 个像素,数字越大跳得越快,但是会有掉帧得感觉,步子迈大了会扯到蛋
    console.log("scrollTop", scrollTop);
    const STEP = 50;
    // 判断是往下滑还是往上滑
    if (scrollTop > targetOffsetTop) {
      // 往上滑
      smoothUp();
    } else {
      // 往下滑
      smoothDown();
    }
    // 定义往下滑函数
    function smoothDown() {
      // 如果当前 scrollTop 小于 targetOffsetTop 说明视口还没滑到指定位置
      if (scrollTop < targetOffsetTop) {
        // 如果和目标相差距离大于等于 STEP 就跳 STEP
        // 否则直接跳到目标点,目标是为了防止跳过了。
        if (targetOffsetTop - scrollTop >= STEP) {
          scrollTop += STEP;
        } else {
          scrollTop = targetOffsetTop;
        }
        document.body.scrollTop = scrollTop;
        document.documentElement.scrollTop = scrollTop;
        // 关于 requestAnimationFrame 可以自己查一下,在这种场景下,相比 setInterval 性价比更高
        requestAnimationFrame(smoothDown);
      }
    }
    // 定义往上滑函数
    function smoothUp() {
      if (scrollTop > targetOffsetTop) {
        if (scrollTop - targetOffsetTop >= STEP) {
          scrollTop -= STEP;
        } else {
          scrollTop = targetOffsetTop;
        }
        document.body.scrollTop = scrollTop;
        document.documentElement.scrollTop = scrollTop;
        requestAnimationFrame(smoothUp);
      }
    }
  };

  //点击锚点跳转方法
  const jump = (item: any, index: any) => {
    setActive(index);
    let doc = document.getElementById(item);
    console.log("doc", doc);
    doc?.scrollIntoView({
      behavior: "smooth",
      block: "center",
      inline: "center",
    });
  };

  return (
    <>
      <div style={{ display: "flex" }}>
        <div
          style={{
            width: "300px",
            margin: "24px",
            position: "fixed",
            top: "0",
            left: "0",
            zIndex: "auto",
          }}
        >
          {catalog.map((j: string, index) => (
            <div
              key={index}
              onClick={() => scrollTo(index, j)}
              className={active === index ? "active" : ""}
              // onClick={() => jump(j, index)}
            >
              {j}
            </div>
          ))}
        </div>
        <div
          style={{ width: "600px", marginLeft: "200px" }}
          className="content"
        >
          {interfaceList.map((i: any, index1: any) => (
            <div key={index1} id={i.resourceNm}>
              <p>{i.resourceNm}</p>
              <Table
                rowSelection={{
                  type: "checkbox",
                  ...rowSelection,
                }}
                columns={columns}
                dataSource={i.permissionRestInfList}
              />
            </div>
          ))}
        </div>
      </div>
    </>
  );
}

* {
  box-sizing: border-box;
}
.active {
  color: blue;
}
.content {
  background-color: #fff;
  width: 500px;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值