实现ant design tree控件右键菜单功能

最近由于产品功能需求,需要对后端树状数据进行增删改操作。前端使用ant design的tree控件。为了在某个树节点上实现增删改操作,决定采用鼠标右键弹出操作菜单的方式。但是tree控件本身并不支持鼠标右键弹出菜单功能需要自己实现。这里把自己遇到的坑以及最终的方案给分享下。


onRightClick坑

tree控件中有个onRightClick属性,可以设置右键点击的事件处理函数。onRightClick参数有两个属性:{event, node}。但是,通过获取event中的pageX、pageY坐标,并用absolute方式弹出菜单,位置总是不对,离真正点击的位置相差很远。暂时没想到好的解决办法,请路过的高手指教。


react-contexify填坑

为了解决onRightClick弹出菜单位置不正确的问题,采用react-contexify右键菜单组件。原理是,将ant design tree控件中,每个treeNode的title设置为ReactNode,也就是替换成react-contexify中的MenuProvider,从而实现右键弹出,而且位置非常准确。


首先,treeData是从服务器异步拉取下来的。在增删改之后会重新拉取treeData

import { Menu, Item, MenuProvider } from 'react-contexify';
import 'react-contexify/dist/ReactContexify.min.css';

const [treeData, setTreeData] = useState([]);
const [loading, setLoading] = useState<boolean>(false);

const fetchTree = async()=>{
  try{
    const response = await getTreeData({
    });
    if(response && response.status === 0){
      setTreeData(response.tree);
    }else{
      message.error('拉取树失败');
    }
  }catch (e) {
    message.error('拉取树失败');
  }finally {
    setLoading(false);
  }
};

其次,定义右键菜单

// 我使用的菜单定义数据结构是这样的
// { menuId, items: [{ name, handler }]}

// handler函数
const deleteNode= ({event, props}) =>{
  // props是当前点击的树节点,需要在MenuProvider中用属性传进去才能在这里获得
};

const testMenu = {menuId:1,
  items:[
    {name:'添加子节点', handler:addChildNode},
    {name:'添加同级节点', handler:addSiblings},
    {name:'修改节点', handler:updateNode},
    {name:'删除节点', handler:deleteNode},
  ]
};

第三,刷新treeData,把title从字符串格式,包装成MenuProvider

// contextMenu: { menuId, items: [{ name, handler }]} 上下文菜单
// dataSource: 树节点
const TreeWithContextMenu = ({ dataSource, contextMenu }) => {
  // 上下文菜单
  const ContextMenu = () => (
    <Menu id={contextMenu.menuId}>
      {contextMenu.items.map(item => (
        <Item onClick={item.handler}>{item.name}</Item>
      ))}
    </Menu>
  );

  // 渲染树节点,主要目的是为了通过react-contexify包裹上下文菜单
  // 其中 data={item}是把当前数据节点传入MenuProvider会在右键事件回调中使用,方便确定当前点击的数据
  const renderEntityTreeNodes = data => {
    return data.map(item => {
      const title = (
        <MenuProvider id={contextMenu.menuId} data={item}>
          <span>{`${item.title}`}</span>
        </MenuProvider>
      );

      if (item.children) {
        const result = (
          <Tree.TreeNode {...item} key={item.key} title={title}>
            {renderEntityTreeNodes(item.children)}
          </Tree.TreeNode>
        );

        return result;
      }

      return <Tree.TreeNode {...item} key={item.key} title={title} />;
    });
  };

  return (
    <div>
      <Tree autoExpandParent defaultExpandAll defaultExpandParent>{renderEntityTreeNodes(dataSource)}</Tree>
      <ContextMenu/>
    </div>
  );
};

第四,在render中使用TreeWithContextMenu方法

return(<div>
    TreeWithContextMenu({dataSource:treeData, contextMenu:testMenu})
</div>)


参考文献:

github.com/ant-design/a

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值