antd可搜索可選擇Tree樹形組件,expandedKeys,checkedKeys屬性詳解


最近寫react用的都是antd組件,不得不說,antd組件在使用上會比element-UI復雜點,不知道是因為react的原因不.很多組件的方法是需要自己配置的,在element-ui中可能只需要增加一個屬性,最近也使用也踩了一些坑.今天總結一下Tree樹形組件的使用和一些閉坑指南把.
項目源碼: https://github.com/shengbid/my-react-admin這是一個react hook + antd + ts寫的后台管理系統,
我的需求是: 一個可選擇的tree樹形列表,頁面初始化展示,從后台獲取數據,根據數據設置部分節點選中.頂部有搜索框,可以搜索樹形列表,根據搜索高亮匹配的節點,並且展開節點.
找了下官網的例子,也有可搜索tree,拿過來用了下,套上自己的數據,結果怎么都不生效.折騰了半天,發現是我的數據的原因,后台返給我的key是Number數據,而tree設置展開和選中都需要string數據的key
所以還是要仔細看文檔
 
然后還要注意的是expandedKeys和defaultExpandedKeys 也就是屬性前有default和沒有default的區別,沒有default是完全控制,做任何操作都要重新設置值,否則就不會改變,有default的屬性是只在初始化渲染有效,后期的操作不會影響

以defaultExpandedKeys為例,初始值設置的展開節點會生效,但是在后期搜索節點后重新設置展開節點時就不會生效了,所以如果你之后還需要對節點進行操作,就需要用沒有default的完全可控的expandedKeys屬性
 
實現可搜索的tree的步驟:

1.設置expandedKeys屬性來控制搜索時的節點展開
2.對treeData數據進行處理,設置樣式,對匹配的搜索項高亮
3.增加輸入框,輸入時,調用方法把需要展開的節點返回,重新設置expandedKeys
4.點擊節點的展開收縮是也需要重新設置expandedKeys

貼代碼
import React, { useEffect, useState } from 'react';
import { Tree, Input } from 'antd'
import { getDeptList } from '@/services/dept'
import { 
  // getFathersById,
  getParentKey,
 } from '@/commons/utils'
import { DeleteOutlined } from '@ant-design/icons'
import './style.less'

const Detail = () => {
  const [expandedKeys, setExpandedKeys] = useState(['0-0', '0-0-0'])
  const [treeData, setTreeData] = useState<any[]>([])
  const [selectdKeys, setSelectdKeys] = useState<any[]>([])
  const [checkedKeys, setCheckedKeys] = useState<any[]>([])
  const [autoExpandParent, setAutoExpandParent] = useState(true)
  const [searchValue, setSearchValue] = useState('')

  // 將樹形節點改為一維數組
  const generateList = (data: any, dataList: any[]) => {
    for (let i = 0; i < data.length; i++) {
      const node = data[i];
      const { key, title } = node;
      dataList.push({ key, title, });
      if (node.children) {
        generateList(node.children, dataList);
      }
    }
    return dataList
  }

  // 獲取樹形節點數據
  const getList = async() => {
    const res = await getDeptList()
    console.log(11, res)
    setTreeData(res.data)
    // handleTree(res.data)
  }
  // 搜索節點
  const onChange = (e: any) => {
    
    let { value } = e.target
    value = String(value).trim()
    const dataList: any[] = generateList(treeData, [])
    let expandedKeys: any = dataList
      .map((item: any) => {
        if (item.title.indexOf(value) > -1) {
          // return getFathersById(treeData, item.key, 'key')
          return getParentKey(item.key, treeData)
        }
        return null;
      })
      .filter((item: any, i: number, self: any) => item && self.indexOf(item) === i)

    // expandedKeys = expandedKeys.length ? expandedKeys[0] : []
    console.log(26, expandedKeys)
    setExpandedKeys(expandedKeys)
    setAutoExpandParent(true)
    setSearchValue(value)
  }

  // 樹節點展開/收縮
  const onExpand = (expandedKeys: any) => {
    console.log(22, expandedKeys)
    setExpandedKeys(expandedKeys)
    setAutoExpandParent(false)
  }

  // 選擇節點
  const checkDep = (val: any, data: any) => {
    if (data && data.checkedNodes && data.checkedNodes.length) {
      let checkedNodes = [...data.checkedNodes]
      // console.log(1, val, data)

      setSelectdKeys(checkedNodes)
      setCheckedKeys(
        checkedNodes.map((subItem: any) => {
          return subItem.key
        }),
      )
    } else {
      setSelectdKeys([])
      setCheckedKeys([])
    }
  }

  // 刪除節點
  const removeDep = (i: number) => {
    const checkedNodes = [...selectdKeys]
    checkedNodes.splice(i, 1)
    setSelectdKeys(checkedNodes)
    setCheckedKeys(
      checkedNodes.map((subItem: any) => {
        return subItem.key
      }),
    )
  }

  // 處理搜索時樹形數據高亮
  const loop = (data: any) =>
    data.map((item: any) => {
      const index = item.title.indexOf(searchValue);
      const beforeStr = item.title.substr(0, index);
      const afterStr = item.title.substr(index + searchValue.length);
      const title =
        index > -1 ? (
          <span>
            {beforeStr}
            <span className="site-tree-search-value">{searchValue}</span>
            {afterStr}
          </span>
        ) : (
          <span>{item.title}</span>
        );
      if (item.children) {
        return { title, key: item.key, children: loop(item.children) };
      }

      return {
        title,
        key: item.key,
      };
    })

  useEffect(() => {
    getList()
  }, [])

  return (
      <div>
        <div className="tree-contanier">
          <div className="left-content">
            <span>可搜索可控制選擇的樹形組件</span>
            <Input style={{ marginBottom: 8 }} allowClear placeholder="Search" onChange={onChange} />
            <Tree
              onExpand={onExpand}
              expandedKeys={expandedKeys}
              autoExpandParent={autoExpandParent}
              checkable
              defaultExpandAll
              onCheck={checkDep}
              checkedKeys={checkedKeys}
              checkStrictly
              treeData={loop(treeData)}
            />
          </div>
          <div className="right-content">
            <span>已選擇列表</span>
            <ul>
              {selectdKeys.map((item: any, i: number) => {
                return <li key={item.key} className="select-item">
                  {item.title}
                  <span className="remove" title="刪除" onClick={() => removeDep(i)}><DeleteOutlined /></span>
                </li>
              })}
            </ul>
          </div>
        </div>
      </div>
  )
}

export default Detail
代碼中的getParentKey方法單獨拿出來了
// antd tree樹形匹配方法
export function getParentKey( key: number | string, tree: any): any {
  let parentKey
  for (let i = 0; i < tree.length; i++) {
    const node = tree[i];
    if (node.children) {
      if (node.children.some((item: any) => item.key === key)) {
        parentKey = node.key;
      } else if (getParentKey(key, node.children)) {
        parentKey = getParentKey(key, node.children);
      }
    }
  }
  return parentKey;
}

實現效果

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM