首页 > 其他分享 >Antd-React-TreeSelect前端搜索过滤

Antd-React-TreeSelect前端搜索过滤

时间:2024-08-20 09:06:32浏览次数:11  
标签:node const children 节点 React item Antd TreeSelect any

在开发过程中,但是antd中的搜索会把多余的也会带出来
就例如下图,我们本想去搜索1但是他会把其子节点都带出来,其实我们的本意是像搜2一样或者当中间隔层处理

但是我们该如何解决这样的问题呢如何做到下面两种情况
(1)搜索过滤掉不匹配的内容只留下匹配的内容
这是没有搜索之前

这是搜索之后,当我们去搜索5的时候我们就会直接把213过滤掉

(2)搜索中当子节点不是搜索内容但是孙节点和祖孙节点中存在要搜索的内容要把该子节点进行保留
这是没有搜索之前

这是搜索之后,我们要保留的结果

那么主要方法如下,antd-treeselect中的filterTreeNode属性,是否根据输入项进行筛选,默认用 treeNodeFilterProp 的值作为要筛选的 TreeNode 的属性值

方法如下使用

//toLowerCase()的方法主要是为了使用不区分大小写使用
 const filterTreeNode = (inputValue: string, treeNode: any) => {
    return treeNode.title.toLowerCase().indexOf(inputValue.toLowerCase()) > -1
  }

接下来就是搜索功能的具体实现方法

 // 此处操作主要用于前端处理搜索树时过滤掉搜索出的父节点下与搜索内容无关的其他子节点
    if (searchValue) {
      const fileData = [...oldfileTree]//主要用于记录tree节点使用的 oldfileTree就是树的节点功能
      // 用户树搜索的功能
      const searchResult = getSearchList([...fileData], searchValue)
      // 将树的列表更具搜索的内容的所有父级节点和搜索到内容id的合集
      let parentKeys
      if (name === 'apiManage') {
        parentKeys = contents
          .map((item: any) => {
            if (item.searchTitle.toLowerCase().indexOf(searchValue.toLowerCase()) > -1) {
              return getParentName(item.id, contents)
            }
            return null
          })
          .filter((item: any, i: any, self: any) => item && self.indexOf(item) === i)
      } else {
        parentKeys = contents
          .map((item: any) => {
            if (item.searchTitle.toLowerCase().indexOf(searchValue.toLowerCase()) > -1) {
              return getParentKey(item.id, contents)
            }
            return null
          })
          .filter((item: any, i: any, self: any) => item && self.indexOf(item) === i)
      }
      //所有需要的id扁平化处理
      const parentIdsList: any = parentKeys
        .flat(2)
        .filter((item: any, i: any, self: any) => item && self.indexOf(item) === i)
      // 获取需要展开的id集合由于过程中可能存在层级丢失,需要使用traverseParent向上寻找所有父级的id序列
      const getExpendKeys = parentIdsList
        .map((item: string) => {
          return traverseParent(searchResult, item)
        })
        .flat(2)
        .filter((item: any, i: any, self: any) => item && self.indexOf(item) === i)
      //设置翻开节点
      setTreeExpandedKeys(getExpendKeys)
      // 将搜索的集合转换成列表形式
      generateList(searchResult)
      // 把集合做转换成Map结构
      const listMap = dataList.reduce((map, item: any) => {
        map.set(item.id, item)
        return map
      }, new Map())
      //将所有展开的key与集合使用Map快速匹配对应值并将Map中存在的标记为true
      getExpendKeys.map((item: string) => {
        if (listMap.has(item)) {
          listMap.set(item, { ...listMap.get(item), hasSearch: true })
        }
      })
      // 将搜索的结果和Map进行匹配,如果匹配成功则将该节点换成Map中该节点的内容
      const result = hasTree(searchResult, listMap)
      // 将融合好的hasSearch tree(是否是搜索的节点)进行去除所有false的节点
      const filterTree = removeFalseNodes(result)
      // 形成所有搜索的结果
      setFileTree([...filterTree] as treeDataNode[])
    } 

getSearchList 就是用于搜索高亮使用的,hasSearch搜索到的值为true,搜索不到的值则为false

  const getSearchList = (data: treeDataNode[], searchValue: string) => {
    const result: treeDataNode[] = data.map(item => {
      const strTitle = item.searchTitle as string
      const index = strTitle.toLowerCase().indexOf(searchValue.toLowerCase())
      const beforeStr = strTitle.substring(0, index)
      const afterStr = strTitle.slice(index + searchValue.length)
      const regExp = new RegExp(searchValue, 'gi')
      const matches = strTitle.match(regExp)
      let value = ''
      if (matches) {
        strTitle.replace(regExp, (match: any) => {
          value = match
          return match
        })
      }

      const alias =
        index > -1 ? (
          <span>
            {beforeStr}
            <span className='site-tree-search-value'>{value}</span> //site-tree-search-value设置css样式,设置你需要的高亮的颜色什么颜色都可以
            {afterStr}
          </span>
        ) : (
          <span>{strTitle}</span>
        )

      if (item.children) {
        return {
          ...item,
          alias,
          value: item.id,
          hasSearch: index > -1 ? true : false, //将所有搜索结果是真的标记为true否则为false
          children: getSearchList(item.children, searchValue)
        }
      }
      return {
        ...item,
        value: item.id,
        hasSearch: index > -1 ? true : false, //将所有搜索结果是真的标记为true否则为false
        alias
      }
    })
    return result
  }

getParentKey 的目的是找到给定 key 所对应的节点的直接父节点,并返回该父节点的 id 和 parentId。
getParentKey 函数没有明确处理未找到父节点的情况,可能会返回意外的结果或 undefined或者空数组。因而要使用.flat(2).filter((item: any, i: any, self: any) => item && self.indexOf(item) === i)来过滤

  const getParentKey = (key: React.Key, tree: any): React.Key => {
    let parentKey: any
    for (let i = 0; i < tree.length; i++) {
      const node = tree[i]
      if (node.children) {
        if (node.children.some((item: any) => item.id === key)) {
          parentKey = [node.id, node.parentId]
        } else if (getParentKey(key, node.children)) {
          parentKey = [getParentKey(key, node.children), node.parentId]
        }
      }
    }
    return parentKey
  }

traverseParent 的目的是递归地查找给定 parentId 的所有祖先节点,并将它们的 id 收集到一个数组中。
traverseParent 在未找到指定 parentId 的情况下会返回一个空数组。因而要使用.flat(2).filter((item: any, i: any, self: any) => item && self.indexOf(item) === i)来过滤

  const traverseParent = (treeData: treeDataNode[], parentId?: string) => {
    let result: string[] = []
    function traverse(nodes: treeDataNode[], parentId: string) {
      for (let i = 0; i < nodes.length; i++) {
        const node = nodes[i]
        if (node.id === parentId) {
          result = [...result, node.id]
          if (node.parentId) {
            traverse(treeData, node.parentId)
          }
          break
        } else if (node.children) {
          traverse(node.children, parentId)
        }
      }
    }
    if (parentId) traverse(treeData, parentId)

    return result
  }

generateList 的目的是用于扁平化树形数据结构并转换每个节点的格式

 const dataList: { key: React.Key; title: string; name: string }[] = []
  const generateList = (data: treeDataNode[]) => {
    for (let i = 0; i < data.length; i++) {
      const node = data[i]
      dataList.push({ ...node, name: node.title })
      if (node.children) {
        generateList(node.children)
      }
    }
  }

hasTree 就是将树重新构建,将树中存在的与Map结构中同样内容的值换成Map结构的信息

  const hasTree = (tree: treeDataNode[], map: any) => {
    return tree.map(node => {
      if (map.has(node.id)) {
        node = map.get(node.id)
      }
      // 如果节点有子节点,递归处理子节点
      if (node.children && node.children.length > 0) {
        node.children = hasTree(node.children, map)
      }
      return node
    })
  }

removeFalseNodes 是删除hasSearch 为false的置换成undefined在将其过滤掉最后剩下的就是搜索出的结果

const removeFalseNodes = (data: treeDataNode[]) => {
    return data
      .map(item => {
        // 递归处理children数组
        item.children = item.children && item.children.filter(child => child.hasSearch)
        if (item.children && item.children.length > 0) {
          removeFalseNodes(item.children)
        }
        // 如果当前对象的hasSearch为false且children为空数组,则返回undefined以从结果中排除
        return item.hasSearch || (item.children && item.children.length > 0) ? item : undefined
      })
      .filter(item => item !== undefined)
  }

总之,在一些时候搜索为了迎合需要不得不这么操作,那么该操作结合了antd官方的搜索操作,在此之前需要保持清醒的头脑

首先我们搜索出来高亮这个操作antd TreeSelect的是可以实现,但是搜索中我们发现实现不了搜索过滤,但是又要解决这个问题,想尝试使用数组方法将不是的部分删除,只能解决节点是的情况,当出现差层,何为差层就是当子节点不是搜索内容但是孙节点和祖孙节点中存在要搜索的内容要把该子节点进行保留的时候发现数据保留不住,不知道该如何解决,翻阅了ES6后发现使用Map做一层数据存储,并结合搜索情况将所有搜索的父节点向上遍历将其hasSearch设置为true,这样在重新构建树的时候可以将所有需要的节点变成true,再最后将所有节点是false的节点进行删除,只保留hasSearch为true的节点。总之该操作中使用了数组的方法,以及ES6的Map结构,当做出来的时候感觉雨过天晴,但是个人觉得这些还是太冗余了,之后会更进方法,如果大家有什么更好的方法请多指教 (´・Д・)」

最后就是如果这种问题可以放在后端在搜索的时候进行请求来减少前端遍历和重组的过程减少渲染次数会更好

标签:node,const,children,节点,React,item,Antd,TreeSelect,any
From: https://www.cnblogs.com/Evisu47/p/18368117

相关文章

  • React 基础学习
    前言这篇博客也是我的学习记录,一边学一边写下的,里面也记录了一些我作为初学者来说看不太懂的个人解释,与之对应的一些比较简单的我也就一笔带过了,比如JSX语法和那些比较简单的Hook。当然我忽略了一些我认为初学者没有必要太过关注的东西,当然这些东西并不代表没有用,或许知识本......
  • react useReducer 的基本用法
    const{createRoot}=ReactDOMconst{useState,useReducer,useEffect}=Reactconstroot=createRoot(document.getElementById('app'))functionApp(){//const[count,setCount]=useState(0)//搜集所有的操作某一个数据的方案//派发其传......
  • 009、Vue3+TypeScript基础,ref和reactive的区别
    01、App.vue代码如下:<template><divclass="app"><h1>好好学习,天天向上</h1><Person/></div></template><script>//JS或TSimportPersonfrom'./view/Person.vue'exportdefault{//......
  • 【vue讲解:vue3介绍、setup、ref、reactive、监听属性、生命周期、toRef、setup写法】
    1vue3介绍#Vue3的变化 -vue3完全兼容vue2---》但是vue3不建议用vue2的写法 -拥抱TypeScript -之前咱们用的JavaScript---》ts完全兼容js-组合式API和配置项API vue2是配置项apivue3组合式api#vue4必须要用2vue3项目......
  • 007、Vue3+TypeScript基础,使用reactive让界面数据变成响应式
    01、App.Vue代码:<template><divclass="app"><h1>好好学习,天天向上</h1><Person/></div></template><script>//JS或TSimportPersonfrom'./view/Person.vue'exportdefault{//Ap......
  • React 的虚拟 DOM 和 Vue 的虚拟 DOM 有什么区别?
    React和Vue都使用虚拟DOM(VirtualDOM)来实现高效的UI渲染。1.引言介绍虚拟DOM的概念和重要性。提到React和Vue都采用了虚拟DOM来优化视图更新过程。2.什么是虚拟DOM?定义虚拟DOM:它是一种用JavaScript对象表示UI结构的技术。解释虚拟DOM如何与......
  • React 中执行的 DOM 操作
    新增/插入对于需要新增/插入的节点,React会对workInProgressfiber.flags执行按位或Placement的操作。表示该fiber节点需要执行插入的操作。执行插入的主要方法是commitPlacement。在该方法中会根据当前Fiber节点是否存在兄弟Fiber节点判断要调用哪种方法。如果......
  • React 高德地图 进京证 路线规划 问题小记
    一、加载问题用高德地图做了个进京证路线规划的功能,官网也是有React代码示例。但是吧,这个Demo有问题,地图是能加载成功,但是其他功能再用map这个变量肯定不行,果不其然是null,处理也简单,把公共变量都管理起来就行了。const[map,setMap]=useState(null);const[AMap,setAMa......
  • windows-g下载js库使用时报错:无法加载文件 D:\code\node\node_global\create-reac
    无法加载文件D:\code\node\node_global\create-react-app.ps1,因为在此系统上禁止运行脚本。有关详细信息,请参阅https:/go.microsoft.com/fwlink/?LinkID=135170中的about_Execution_Policies。当我们在windows上-g(全局)安装一个js库时,执行会报这个错误,然后我们看......
  • 使用 TypeScript 在 React JS 中进行路由
    一.介绍单页应用程序(SPA)中的路由支持在视图之间导航,而无需重新加载应用程序。ReactRouter是React应用程序中路由的标准库。本文简要概述了使用TypeScript设置路由的方法。二.设置项目创建一个新的React项目npxcreate-react-appreact-router-ts--template......