- 下载拖拽功能的依赖
npm i react-dnd-html5-backend react-dnd
- 编写拖拽功能
import HTML5Backend from 'react-dnd-html5-backend';
import {useDrag, DndProvider, useDrop} from 'react-dnd';
import {Tree} from 'antd'
import useSearchTree from '../hooks/useSearchTree'
const [value,setValue] = useState();
const [dropDataList,setDropDataList] = useState([]);
const [expandedKeys, setExpandedKeys] = useState([]);
const [autoExpandParent,setAutoExpandParent] = useState(false);
const {onChange, newTreeData, initialize, searchExpandedKeys} = useSearchTree()
const renderTreeNodes = data =>
data && data.map(item => ({
...item,
_title: item.title,
title: <Title {...item} />,
children: item.children ? renderTreeNodes(item.children) : [],
}))
const Title = (props) => {
const {title} = props;
const [{}, dragRef] = useDrag({
item: {props, type: 'box'},
collect: () => ({}),
});
if (!_.isEmpty(props.children)) {
return <span>{title}</span>
}
return <span className='treeSearchData' ref={dragRef}>{title}</span>
}
const DropTarget = ({dragHandler, children}) => {
const dropCon = useRef();
const [_, dropRef] = useDrop({
accept: 'box',
drop: item => dragHandler(item)
});
return <div ref={dropRef(dropCon)}>{children}</div>;
};
const onExpand = (e) => {
setExpandedKeys(e)
setAutoExpandParent(false)
}
<div>
<Input value={value} onChange={e=>{
const {value} = e.target
setValue(value)
}}} />
<Button onClick={_=>onChange(value)}>查询</Button>
</div>
<DndProvider backend={HTML5Backend}>
<div>
<div className="left">
<Tree
treeData={newTreeData}
autoExpandParent={autoExpandParent}
expandedKeys={expandedKeys}
onExpand={onExpand}
/>
</div>
<div className='right'>
<DropTarget dragHandler={(props)=>{
setDropDataList(props.concat(dropDataList))
}}>
{
dropDataList && dropDataList.map((item)=>{
<div>{item.title}</div>
})
}
</DropTarget>
</div>
</div>
</DndProvider>
- 搜索逻辑钩子 useSearchTree.jsx
import React, {useRef, useState} from 'react'
import {message} from 'antd'
import _ from 'lodash'
const expandedCount = 50;
const freezeData = []
// 将树形结构转成扁平结构
const generateData = (treeData = []) => treeData.reduce((pre, cur) => {
const {children = [], ...i} = cur;
return pre.concat([{...i}], generateData(children))
}, [])
const getParentKey = (key, tree) => {
let parentKey;
for (let i = 0; i < tree.length; i++) {
const node = tree[i];
if (node.children) {
if (node.children.some((item) => item.key === key)) {
parentKey = node.key;
} else if (getParentKey(key, node.children)) {
parentKey = getParentKey(key, node.children);
}
}
}
return parentKey;
};
const useSearchTree = () => {
const dataList = useRef([])
const [newTreeData, setNewTreeData] = useState([])
const [searchExpandedKeys, setExpandedKeys] = useState([])
const initialize = (data) => {
setNewTreeData(data)
const cloneData = _.cloneDeep(data)
freezeData.push(...cloneData)
dataList.current = generateData(data)
}
const onChange = (text) => {
const value = text?.toLowerCase();
if (value) {
const newExpandedKeys = dataList.current.map((item) => {
if (item._title.indexOf(value) > -1) {
return getParentKey(item.key, freezeData);
}
return null;
}).filter((item, i, self) => item && self.indexOf(item) === i);
if (newExpandedKeys.length > expandedCount) {
message.warn('数据量过大,不自动展开')
} else {
setExpandedKeys(newExpandedKeys)
}
if (!newExpandedKeys.length) {
message.warn('没有搜索结果')
}
const canSearch = (n) => new RegExp(value, 'i').test(n)
const filterData = (nodes) => {
if (!(nodes && nodes.length)) {
return false;
}
const newNodes = []
for (let i = 0; i < nodes.length; i++) {
if (canSearch(nodes[i]._title)) {
newNodes.push(nodes[i])
nodes[i].children = filterData(nodes[i].children)
} else {
const subs = filterData(nodes[i].children);
if ((subs && subs.length) || canSearch(nodes[i].title)) {
nodes[i].children = subs;
newNodes.push(nodes[i])
}
}
}
return newNodes
}
titleLoop(filterData(newTreeData), value)
} else {
setNewTreeData(titleLoop(freezeData, value))
}
}
const titleLoop = (data, searchValue) => {
return data.map((item) => {
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>);
item.title = React.cloneElement(item.title, {...item.title.props, title, _title: item._title})
if (item.children) {
return {...item, children: titleLoop(item.children, searchValue)};
}
})
}
return {onChange, newTreeData, initialize, searchExpandedKeys}
}
4.添加 css
.site-tree-search-value {
color: #f50;
}