Ant Design建立一個樹形元件,實現編輯、搜尋和定位功能

2022-01-13 19:00:46
怎麼客製化Ant Design樹形元件實現編輯、搜尋和反向定位功能?下面本篇文章給大家介紹一下建立樹形元件,實現這些功能的方法,希望對大家有所幫助!

這次在做了一個樹形的展示功能,誰知產品意猶未盡,找我談話:

PD: 什麼?只有展開收起功能?這怎麼行,咱們最基礎的要支援編輯,支援搜尋,如果可以的話還可以做個反向定位...

YY: 你咋不早說?需求檔案上也沒有啊...

PD: 你看誰家檔案一次寫到位的?哪家的PD不加需求?

YY: 話是這樣說,可事情不是這麼做的...

PD: 哎呀,別杵著浪費時間了,快去做吧!

YY: ...

以上故事純屬虛構,如有雷同請評論區留言...

樹形資料在開發中算是比較常見了,資料夾、組織架構、生物分類、國家地區等等,世間萬物的大多數結構都是樹形結構。使用樹控制元件可以完整展現其中的層級關係,並具有展開收起選擇等互動功能。

需求分析

  • 編輯:新增/修改/刪除/移動
  • 搜尋功能:名稱/建立人/ owner過濾
  • 定位:tab反向定位

專案倉庫:https://github.com/speakice/editable-tree

1.png

功能實現

能實現以上功能的方法庫和元件有很多種,這裡只講其中一種,都是Ant Design的元件:

  • Tree.DirectoryTree 目錄樹
  • Dropdown 右鍵選單容器
  • Menu 選單內容
  • Tabs 右側Tab頁
  • Input.Search 搜尋方塊
  • Switch 切換關聯狀態
  • shortid 生成唯一id
import { Tree, Dropdown, Menu, Tabs, Input, Switch } from 'antd';import shortid from 'shortid';複製程式碼

遞迴方法

操作樹行資料,最重要的前提是要有一個趁手的遞迴方法:

/**
 * 如果需要修改tree,action就返回修改後的item, 不修改就不返回
 */export const deepTree = (tree = [], action = () => {}) => {  return tree.map((item) => {    const newItem = action({ ...item }) || item;    if (newItem.children) {
      newItem.children = deepTree(newItem.children, action);
    }    return newItem;
  });
};複製程式碼

滑鼠右鍵選單

右鍵選單作用在title上,需要把Dropdown寫入樹形元件的資料來源上:

    <DirectoryTree
          style={{ width: 280 }}
          draggable
          onDrop={onDrop}
          defaultExpandAll
          onRightClick={({ node }) => setRightClickKey(node.key)}
          onSelect={onSelect}
          selectedKeys={rightConnect ? [activeTabKey] : selectedKeys}
          onExpand={onExpand}
          treeData={[
            ...deepTree(treeData, (item) => {              return {
                ...item,                titleWord: item.title,                title: (                  <Dropdown                    trigger="contextMenu"                    visible={rightClickKey === item.key}                    onVisibleChange={() => setRightClickKey()}
                    overlayStyle={{ width: 80 }}
                    overlay={menu(item)}
                  >                    <div                      style={                        searchWord && item.title.includes(searchWord)
                          ? { color: 'red' }                          : {}
                      }
                    >
                      {item.title}                    </div>                  </Dropdown>
                ),
              };
            }),
          ]}
        />複製程式碼

關於右鍵選單有幾點需要補充說明一下:

  • Dropdown 的觸發屬性需要設定成contextMenu;
  • Dropdown 顯示的位置是相對於title而言,需要設定外層容器寬度鋪滿剩餘空間:
.ant-tree-node-content-wrapper {  display: flex;
}.ant-tree-title {  flex: 1;
}複製程式碼
  • Dropdown 的顯示藏是通過右鍵點選記錄的key來判斷的;
  • Dropdown 的選單需要傳遞當前item;
  const menu = (node) => (    <Menu      onClick={({ key, domEvent }) => {
        domEvent.stopPropagation();
        console.log('menuClick', node, key);
        // 如果要新增操作頂層資料夾,可以直接操作
        switch (key) {
          case 'add':
            setTreeData(
              deepTree(treeData, (item) => {
                if (item.children && item.key === node.key) {
                  return {
                    ...item,
                    children: [
                      ...item.children,
                      {
                        title: 'new add',
                        key: shortid.generate(),
                        isLeaf: true,
                      },
                    ],
                  };
                }
              })
            );
            break;
          case 'delete':
            const outer = treeData.find((item) => item.key === node.key);
            if (outer) {
              setTreeData(treeData.filter((item) => item.key !== node.key));
              return;
            }
            setTreeData(
              deepTree(treeData, (item) => {
                if (item.children) {
                  return {
                    ...item,
                    children: item.children.filter(
                      ({ key }) => key !== node.key
                    ),
                  };
                }
                return item;
              })
            );
            break;
          case 'edit':
            setTreeData(
              deepTree(treeData, (item) => {
                if (item.key === node.key) {
                  console.log('editle', {
                    ...item,
                    title: 'new edit',
                  });
                  return {
                    ...item,
                    title: 'new edit',
                  };
                }
                return item;
              })
            );
            break;
        }
      }}
    >      <Menu.Item key="add">新增</Menu.Item>      <Menu.Item key="delete" danger>
        刪除      </Menu.Item>      <Menu.Item key="edit">編輯</Menu.Item>    </Menu>
  );複製程式碼

新增/修改/刪除功能

新增功能預設只能給資料夾新增,通過key值判斷新增,這裡處理的比較簡單,只做核心功能演示,程式碼見上一小節;

1-2.png

修改功能也做了簡單的範例,在正式專案中一般需要彈窗編輯或者在樹元件的title中嵌入輸入框,可以使用變數記錄正在編輯的item, 最後儲存通過遞迴插入到樹形資料中:

1-3.png

刪除功能做了判斷,如果是刪除最外層,則直接通過filter過濾,⚠️否則刪除功能是通過children來過濾的,這裡要特別注意下。

搜尋功能

搜尋功能是通過titile顏色變紅來提示的:

2.png

實現上也只是做了點選搜尋之後搜尋,沒有實時搜尋提示,也沒有做搜尋詞區分,這裡可以再擷取下字串來實現,可以見官方範例注意這個預設開啟父節點的屬性autoExpandParent,否則可能要費些功夫向上遞迴。

3.png

還有一種需求是要過濾資料來源,可以對官方範例簡單改造後實現;

Tab反向定位

4.png

點選Tree元件item,在右側新增Tab,或者啟用Tab,這可以算是正向定位;那反向定位就是當右側Tab頁切換時左側Tree元件選中對應item,核心程式碼也就是指定selectedKeys,相比較而言也不難,難點在預設開啟相關父節點,當然前面說過了控制好autoExpandParent這個屬性,就好了。

5.png

拖拽移動

拖拽移動一是Tree元件本身支援,二是官方已經給出了拖拽移動範例,我也只是在官方範例稍微做了改造,這裡也不多贅述:

6.png

結束

搜尋和反向定位的難點其實是在,開啟關聯資料夾上,不過官方範例中使用了autoExpandParent這個屬性,一下子簡單了很多。

時候也不早了,今天就到這裡了。

更多程式設計相關知識,請存取:!!

以上就是Ant Design建立一個樹形元件,實現編輯、搜尋和定位功能的詳細內容,更多請關注TW511.COM其它相關文章!