问答

React 树型菜单,如何保存展开节点,使同一时间,只展开一级内容

作者:admin 2021-09-16 我要评论

如题。基于一个React组件改造的树型菜单(仅是加了一个ItemPanel结构)。 目标:保存展开节点,打开来一个节点时,收起上一个打开节点。 如何实现 ? 改造如下: ....

在说正事之前,我要推荐一个福利:你还在原价购买阿里云、腾讯云、华为云服务器吗?那太亏啦!来这里,新购、升级、续费都打折,能够为您省60%的钱呢!2核4G企业级云服务器低至69元/年,点击进去看看吧>>>)

如题。基于一个React组件改造的树型菜单(仅是加了一个<ItemPanel结构)。

目标:保存展开节点,打开来一个节点时,收起上一个打开节点。

如何实现 ?

改造如下:

... ...
interface ItemNode {
  parentId: string;
  color: string;
  label: string;
  id: string;

  desDatabase: string;
  desSchema: string;
  desTableName: string;
}
interface TreeNode {
  isQueryChildren: boolean;
  id: string;
  parentId: string;
  topicName: string;
  describeInfo: string;
  //null表示有子元素,只是需要请求
  children: (TreeNode | ItemNode)[] | null;
}

//API每次请求的时候需要将数据保存在data和TreeNode中

const needAPI = (id: string): (TreeNode | ItemNode)[] => {
  if (id === "ROOT") {
    return [
      {
        isQueryChildren: false,
        id: "1",
        topic: "topic1",
        parentId: "ROOT",
        describeInfo: "sad",
        children: null,
        topicName: "主题集标题1"
      },
      {
        isQueryChildren: false,
        id: "2",
        topic: "topic2",
        parentId: "ROOT",
        describeInfo: "sad",
        children: null,
        topicName: "主题集标题2"
      },
      {
        isQueryChildren: false,
        id: "3",
        topic: "topic3",
        parentId: "ROOT",
        describeInfo: "sad",
        children: null,
        topicName: "主题集标题3"
      }
    ];
  } else if (id === "1") {
    return [
      {
        parentId: "1",
        color: "#FA8C16",
        label: "表名称1",
        id: "31"
      },
      {
        parentId: "1",
        color: "#FA8C16",
        label: "表名称2",
        id: "32"
      },
      {
        parentId: "1",
        color: "#FA8C16",
        label: "表名称3",
        id: "31"
      },
      {
        parentId: "1",
        color: "#FA8C16",
        label: "表名称4",
        id: "32"
      },
      {
        parentId: "1",
        color: "#FA8C16",
        label: "表名称5",
        id: "31"
      },
      {
        parentId: "1",
        color: "#FA8C16",
        label: "表名称6",
        id: "32"
      }
    ];
  } else if (id === "2") {
    return [
      {
        parentId: "1111",
        color: "#00BFFF",
        label: "表名称3",
        id: "33"
      },
      {
        isQueryChildren: false,
        id: "33",
        topic: "topic2",
        parentId: "root",
        describeInfo: "sad",
        children: null,
        topicName: "sadsa"
      }
    ];
  }
  return [];
};

interface ResponseInterface {
  isSuccess: boolean;
  message: string;
  result: {
    topicElementList: [
      {
        dataSetId: string;
        desDatabase: string;
        desSchema: string;
        desTableName: string;
        id: string;
        topicId: string;
      }
    ];
    topicElementPages: 0;
    topicList: [
      {
        describeInfo: string;
        groupId: string;
        id: string;
        parentId: string;
        topicName: string;
        userId: string;
      }
    ];
  };
}

//------------------------------------------
//添加参数,更改原needAPI(测试数据),标记
const queryData = async (id: "ROOT"): Promise<(TreeNode | ItemNode)[]> => {
  const param = {
    parent_id: id,
    current_page: 1
  };

  const resData = await new Promise<(TreeNode | ItemNode)[]>(
    (resolve, reject) => {
      NetUitl.get(
        "/topic/get/all",
        param,
        (res: ResponseInterface) => {
          if (res.isSuccess === false) {
            reject(res.message);
          }
          const mergeListAndElement: (TreeNode | ItemNode)[] = [];
          const { topicElementList, topicList } = res.result;
          topicList.forEach(item => {
            mergeListAndElement.push({
              isQueryChildren: false,
              id: item.id,
              topicName: item.topicName,
              parentId: id,
              describeInfo: item.describeInfo,
              children: null
            });
          });
          topicElementList.forEach(item => {
            mergeListAndElement.push({
              parentId: item.topicId,
              color: "#00BFFF",
              label: item.desTableName,
              id: item.id,

              desDatabase: item.desDatabase,
              desSchema: item.desSchema,
              desTableName: item.desTableName
            });
          });
          resolve(mergeListAndElement);
        },
        (res: any) => {
          reject();
        }
      );
    }
  );
  return resData;
};

//------------------------------------------

const queryDataById = async (id: string, update: () => void) => {
  let res = await needAPI(id);
  let node = treeNodeMap[id];
  if ("children" in node) {
    //建立树关系
    node.children = res;
    //将节点添加到字典,保存其数据
    res.forEach(item => {
      treeNodeMap[item.id] = item;
    });
    update();
  }
};

//用来保存node的数据的引用
//使用id和节点对应
//会在渲染树的时候加上去的
const treeNodeMap: {
  [index: string]: TreeNode | ItemNode;
} = {
  ROOT: {
    isQueryChildren: false,
    id: "ROOT",
    topicName: "ROOT",
    parentId: "",
    describeInfo: "根元素",
    //null表示有子元素,只是需要请求
    children: []
  }
};

class FlowItemPanel extends React.Component<
  {},
  {
    count: number;
  }
> {
  // const FlowItemPanel = () => (
  state = {
    count: 0
  };
  root: TreeNode = treeNodeMap["ROOT"] as any;

  handleUpdate = () => {
    this.setState(v => {
      return {
        count: v.count + 1
      };
    });
  };

  renderTree = (data: (TreeNode | ItemNode)[]) => {
    let eles: React.ReactNode[] = [];
    data.map(item => {
      if (!("children" in item)) {
        eles.push(
          <ItemPanel className={styles.itemPanel}>
            <Item
              type="node"
              size="72*72"
              shape="flow-circle"
              model={{
                label: item.label,
                id: item.id,
              }}
              src={""}
            >
              <span
                style={{
                  "user-select": "none"
                }}
              >
                {item.label}
              </span>
            </Item>
          </ItemPanel>
        );
      } else {
        let children = item.children;
        //随便搞一个符号表示这个展开后是需要加载的
        let childelems: React.ReactNodeArray = [<div>....</div>];
        if (children !== null) {
          childelems = this.renderTree(children);
        }
        eles.push(
          <Tree
            content={item.topicName}
            // type="ITEM"
            // canHide
            onChange={() => {
              let node = treeNodeMap[item.id] as TreeNode;
              if (node.children === null && node.isQueryChildren === false) {
                //表示请求过了
                node.isQueryChildren = true;
                queryDataById(item.id, this.handleUpdate);
              }
            }}
          >
            {childelems.length === 0 ? null : childelems}
          </Tree>
        );
      }
    });
    return eles;
  };

  componentDidMount() {
    queryDataById("ROOT", this.handleUpdate);
  }


  render() {
    const newNode = this.state.newNode;
    return this.renderTree(this.root.children);
  }

  // );
}

export default FlowItemPanel;

原版:https://codesandbox.io/embed/...

###

已经实现,详见如下地址:

https://codesandbox.io/s/vigorous-meitner-ob7g8

版权声明:本文转载自网络,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。本站转载出于传播更多优秀技术知识之目的,如有侵权请联系QQ/微信:153890879删除

相关文章
  • React 树型菜单,如何保存展开节点,使

    React 树型菜单,如何保存展开节点,使

  • React 菜单同级只允许展开一级,实现思

    React 菜单同级只允许展开一级,实现思

  • 问一个redux的问题

    问一个redux的问题

  • 用idea开发多模块maven项目,用maven bu

    用idea开发多模块maven项目,用maven bu

腾讯云代理商
海外云服务器