antd右键时候并没有弹出Menu,但是右键后点击左键,弹了一下立即就消失了,非常的奇怪。 请问下,这里的原因是什么呢?

我有一个Tree,想要添加右键弹出Menu的功能:
我已经写好代码如下:
https://codesandbox.io/s/hcx7nh

import React, { useEffect, useMemo, useState } from 'react';
import { Input, Tree, Menu, Popover, Button } from 'antd';
import type { TreeDataNode, TreeProps } from 'antd';
import { DownOutlined, AppstoreOutlined, MailOutlined, SettingOutlined } from '@ant-design/icons';

const { Search } = Input;

// 获取父节点 Key
const getParentKey = (key: React.Key, tree: TreeDataNode[]): React.Key | null => {
  for (const node of tree) {
    if (node.children) {
      if (node.children.some((item) => item.key === key)) {
        return node.key;
      }
      const parentKey = getParentKey(key, node.children);
      if (parentKey !== null) {
        return parentKey;
      }
    }
  }
  return null;
};

const isDirectory = (node: TreeDataNode): boolean => {
  // 检查 node 是否有 children 属性
  if (node.children && node.children.length > 0) {
    return true;
  }

  // 检查 node.key 是否有后缀
  const keyParts = (node.key as string).split('/');
  const lastPart = keyParts[keyParts.length - 1];
  if (lastPart.includes('.')) {
    return false;
  }

  return true;
};

// 给定的 treeData
const givenTreeData: TreeDataNode[] = [
  {
    title: 'Folder 1',
    key: '1',
    children: [
      {
        title: 'File 1',
        key: '1-1'
      },
      {
        title: 'File 2',
        key: '1-2'
      }
    ]
  },
  {
    title: 'Folder 2',
    key: '2',
    children: [
      {
        title: 'File 3',
        key: '2-1'
      }
    ]
  }
];

// 定义菜单项
const menuItems = [
  {
    key: '1',
    label: (
      <span>
        <MailOutlined />
        Option 1
      </span>
    ),
  },
  {
    key: '2',
    label: (
      <span>
        <AppstoreOutlined />
        Option 2
      </span>
    ),
  },
  {
    key: 'submenu',
    label: 'Submenu',
    icon: <SettingOutlined />,
    children: [
      {
        key: '3',
        label: 'Option 3',
      },
      {
        key: '4',
        label: 'Option 4',
      },
    ],
  },
];


const App: React.FC = () => {
  const [expandedKeys, setExpandedKeys] = useState<React.Key[]>([]);
  const [checkedKeys, setCheckedKeys] = useState<React.Key[]>([]);
  const [searchValue, setSearchValue] = useState('');
  const [autoExpandParent, setAutoExpandParent] = useState(true);
  const [treeData] = useState<TreeDataNode[]>(givenTreeData);
  const [open, setOpen] = useState(false);
  const [contextMenuPosition, setContextMenuPosition] = useState({ x: 0, y: 0 });
  const [selectedNode, setSelectedNode] = useState<TreeDataNode | null>(null);

  // 搜索框变化事件
  const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { value } = e.target;
    const newExpandedKeys = treeData
      .flatMap((item) => findMatchingKeys(item, value))
      .filter((key, i, self): key is React.Key => !!(key && self.indexOf(key) === i));
    setExpandedKeys(newExpandedKeys);
    setSearchValue(value);
    setAutoExpandParent(true);
  };

  const findMatchingKeys = (node: TreeDataNode, value: string): React.Key[] => {
    const matchingKeys: React.Key[] = [];
    const strTitle = String(node.title);

    // 如果当前节点匹配,添加当前节点的 key
    if (strTitle.toLowerCase().includes(value.toLowerCase())) {
      matchingKeys.push(node.key);

      // 添加父节点 key 以确保节点可见
      let parentKey = getParentKey(node.key, treeData);
      while (parentKey) {
        matchingKeys.push(parentKey);
        parentKey = getParentKey(parentKey, treeData);
      }
    }

    // 递归搜索子节点
    if (node.children) {
      node.children.forEach((child) => {
        matchingKeys.push(...findMatchingKeys(child, value));
      });
    }

    return matchingKeys;
  };

  // 动态生成过滤后的树数据
  const filteredTreeData = useMemo(() => {
    const loop = (data: TreeDataNode[]): TreeDataNode[] => {
      return data.filter((item) => {
        const strTitle = String(item.title);
        const isMatch = strTitle.toLowerCase().includes(searchValue.toLowerCase());
        const hasMatchingChildren = item.children && loop(item.children).length > 0;

        return isMatch || hasMatchingChildren;
      }).map((item) => {
        const strTitle = item.title as string;
        const index = strTitle.indexOf(searchValue);
        const beforeStr = strTitle.substring(0, index);
        const afterStr = strTitle.slice(index + searchValue.length);
        const displayTitle =
          index > -1 ? (
            <span key={item.key}>
              {beforeStr}
              <span className="site-tree-search-value">{searchValue}</span>
              {afterStr}
            </span>
          ) : (
            <span key={item.key}>{strTitle}</span>
          );
        if (item.children) {
          const children = loop(item.children);
          return { title: strTitle, key: item.key, displayTitle, children };
        }
        return { title: strTitle, key: item.key, displayTitle };
      });
    };

    return loop(treeData);
  }, [searchValue, treeData]);

  // 勾选事件
  const onCheck: TreeProps['onCheck'] = (checkedKeysValue) => {
    console.log('onCheck', checkedKeysValue);
    setCheckedKeys(checkedKeysValue as React.Key[]);
  };

  // 展开事件
  const onExpand: TreeProps['onExpand'] = (newExpandedKeys) => {
    console.log('onExpand', newExpandedKeys);
    setExpandedKeys(newExpandedKeys);
    setAutoExpandParent(false);
  };

  // 处理 Tree 节点右键点击事件
  // @ts-ignore
  const handleRigthClick = ( e )=> {
    
    const { event, node } = e
    
    event.preventDefault();
    setOpen(true);
    setContextMenuPosition({ x: event.clientX, y: event.clientY });
    setSelectedNode(node);


    const isDir = isDirectory(node);
    if (isDir) {
      console.log('isDir:', node);
      // 这里需要进行弹出`右键Menu`


    } else {
      console.log('isFile:', node);
    }

    
  };

  // 处理菜单项点击事件
  const handleMenuItemClick = (e: { key: string }) => {
    setOpen(false);
    if (selectedNode) {
      console.log('Selected node:', selectedNode);
      console.log('Selected menu item key:', e.key);
      // 根据菜单项和节点信息执行相应操作
      switch (e.key) {
        case '1':
          console.log('Option 1 clicked on node:', selectedNode);
          break;
        case '2':
          console.log('Option 2 clicked on node:', selectedNode);
          break;
        case '3':
          console.log('Option 3 clicked on node:', selectedNode);
          break;
        case '4':
          console.log('Option 4 clicked on node:', selectedNode);
          break;
        default:
          console.log('Unknown option clicked');
      }
    }
  };

  return (
    <div>
      <Search style={{ marginBottom: 8 }} placeholder="Search" onChange={onChange} />
      <Tree
        showLine
        checkable
        switcherIcon={<DownOutlined />}
        onExpand={onExpand}
        expandedKeys={expandedKeys}
        autoExpandParent={autoExpandParent}
        onCheck={onCheck}
        checkedKeys={checkedKeys}
        treeData={filteredTreeData}
        //@ts-ignore
        titleRender={(nodeData) => nodeData.displayTitle}
        //@ts-ignore
        onRightClick={handleRigthClick}
      />

      <Popover
        content={
          <Menu items={menuItems} onClick={handleMenuItemClick} />
        }
        trigger="contextMenu"
        open={open}
        onOpenChange={(newOpen) => setOpen(newOpen)}
        placement="bottomLeft"
        getPopupContainer={() => document.body}
        style={{ left: contextMenuPosition.x, top: contextMenuPosition.y }}
      >
        {/* 这里的 Button 只是占位,实际不会显示 */}
        <Button style={{ display: 'none' }} />
      </Popover>
    </div>
  );
};

export default App;

但是效果不尽人意:右键时候并没有弹出,但是右键后点击左键,弹了一下立即就消失了,非常的奇怪。
请问下,这里的原因是什么呢?一直没有找到具体的原因。

image.png

阅读 397
1 个回答

可以直接给tree添加:titleRender进行实现:

import { Tree, Dropdown, Menu } from 'antd';
import type { TreeDataNode, TreeProps } from 'antd';
import { DataNode } from 'antd/es/tree';

// 给定的 treeData
const givenTreeData: TreeDataNode[] = [
  {
    title: 'Folder 1',
    key: '1',
    children: [
      {
        title: 'File 1',
        key: '1-1'
      },
      {
        title: 'File 2',
        key: '1-2'
      }
    ]
  },
  {
    title: 'Folder 2',
    key: '2',
    children: [
      {
        title: 'File 3',
        key: '2-1'
      }
    ]
  }
];

const App: React.FC = () => {
  const menu = (
    <Menu
      items={[
        {
          key: 'add',
          label: <span>新增</span>,
        },
        {
          key: 'delete',
          label: <span>删除</span>,
        },
        {
          key: 'update',
          label: <span>编辑</span>,
        },
      ]}
    />
  );

  const titleRender = (nodeData: DataNode) => {
    // 确保 title 是 ReactNode 类型
    const getTitle = (): React.ReactNode => {
      if (typeof nodeData.title === 'function') {
        return (nodeData.title as (data: DataNode) => React.ReactNode)(nodeData);
      }
      return nodeData.title;
    };

    return (
      <Dropdown menu={{ items: menu.props.items }} trigger={['contextMenu']}>
        <div>{getTitle()}</div>
      </Dropdown>
    );
  };

  return (
    <div>
      <Tree
        defaultExpandedKeys={['0-0-0', '0-0-1']}
        titleRender={titleRender}
        treeData={givenTreeData}
      />
    </div>
  );
};

export default App;

效果:
image.png

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题