一般菜单组件接收到的菜单数据是有父组件传递下来的。如果对展开对菜单进行标记,那么菜单组件就会和父组件有交互。

本案里通过递归的方式直接在菜单组件进行数组push id 的方式进行数组对比进行判断菜单的展开状态。

菜单结构树

menu: [
        {
          name: "1",
          id: "1",
          children: [
            {
              name: "1-1",
              id: "1-1",
              children: [
              ],
            },
            { name: "2", id: "2", children: [] },
            { name: "3", id: "3", children: [] },
          ],
        },
        { name: "2", id: "2", children: [] },
        { name: "3", id: "3", children: [] },
      ]

菜单组件

import React, { useState } from "react";
import css from "./index.module.less";

export type MenuItem = {
  id: string;
  name: string;
  children: Array<MenuItem>;
  [propName: string]: any;
};

export type MenuProps = {
  menu: Array<MenuItem>;
};

function checkArr(arr1: Array<string>, arr2: Array<string>) {
  // 请确保菜单ID是唯一的,否则建议数组转字符串进行比较
  const arr3: Array<string> = [];
  if (arr2.length === 0) {
    return false;
  }
  for (var i = 0; i < arr2.length; i++) {
    if (arr1.indexOf(arr2[i]) > -1) {
      arr3.push(arr2[i]);
    }
  }
  if (arr3.length === arr1.length) {
    return true;
  } else {
    return false;
  }
}

function Menu(props: MenuProps) {
  const { menu } = props;
  const [thisPaths, setPaths] = useState([] as Array<string>);
  function List(menu: Array<MenuItem>, paths: Array<string>) {
    return menu.map((v) => {
      return Item(v, paths);
    });
  }

  function Item(item: MenuItem, paths: Array<string>) {
    const newPaths: Array<string> = paths.concat(item.id);
    const isOPen = checkArr(newPaths, thisPaths);
    const hasChildren = Array.isArray(item.children) && item.children.length;
    return (
      <div
        key={item.id}
        className={`${css.item} ${isOPen ? css.open : ""} ${
          hasChildren ? css["has-children"] : ""
        }`}
      >
        <div
          className={css.title}
          onClick={() => {
            setPaths(newPaths);
          }}
        >
          {item.name}
        </div>
        <div className={css.children}>{List(item.children, newPaths)}</div>
      </div>
    );
  }
  return (
    <div>
      <div className={css.menu}>{List(menu, [])}</div>
    </div>
  );
}

export default Menu;

less

.menu {
  padding: 24px;
  font-size: 18px;
}

.item .item {
  padding: 6px 0 6px 24px;
}

.title {
  position: relative;
  padding-left: 24px;
  line-height: 40px;
  color: #5a4d40;
  cursor: pointer;
  .active {
    background-color: #ffdbb9;
  }
  &:hover {
    background-color: #f9e4d1;
  }
  .has-children > &::before {
    position: absolute;
    top: 10px;
    left: 7px;
    content: "";
    border-width: 10px;
    border-style: solid;
    border-color: transparent transparent transparent #ebb684;
    transition: ease-out all 100ms;
  }
}

.children {
  display: none;
}

.open > .title {
  // background-color: #ffdbb9;
  &::before {
    top: 15px;
    left: 2px;
    transform: rotate(90deg);
    //border-color: #ebb684 transparent transparent transparent;
  }
}

.open > .children {
  display: block;
}

树行菜单展开效果图


现刻
173 声望0 粉丝

勤思笃行