tree.tsx 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. import { Input, Tree } from 'antd';
  2. import { SearchOutlined } from '@ant-design/icons';
  3. import DragItem from '@/pages/system/Menu/Setting/dragItem';
  4. import { useDrop } from 'react-dnd';
  5. import {useEffect, useState} from 'react';
  6. import type { TreeProps } from 'antd';
  7. import { cloneDeep, debounce } from 'lodash';
  8. import './DragItem.less';
  9. interface TreeBodyProps {
  10. treeData: any[];
  11. droppableId: string;
  12. notDragKeys?: (string | number)[];
  13. onDrop?: (item: any, dropIndex: string, type?: string) => void;
  14. onTreeDrop?: TreeProps['onDrop'];
  15. removeDragItem?: (id: string | number) => void;
  16. className?: string;
  17. }
  18. export const DragType = 'DragBox';
  19. const { TreeNode } = Tree;
  20. const defaultExpandedKeys = ['iot', 'media', 'system', 'device', 'link', 'link/Channel', 'rule-engine/Alarm', 'Northbound', 'rule-engine']
  21. export default (props: TreeBodyProps) => {
  22. const [newData, setNewData] = useState(props.treeData);
  23. const [searchKeys, setSearchKeys] = useState<(string | number)[]>([]);
  24. const [expandedKeys, setExpandedKeys] = useState<(string | number)[]>([]);
  25. const [autoExpandParent, setAutoExpandParent] = useState(true);
  26. useEffect(() => {
  27. setNewData(cloneDeep(props.treeData));
  28. }, [props.treeData]);
  29. useEffect(() => {
  30. setTimeout(() => {
  31. setExpandedKeys(defaultExpandedKeys)
  32. }, 300)
  33. }, [])
  34. const [, drop] = useDrop(() => ({
  35. accept: DragType,
  36. drop(item: any, monitor) {
  37. const result = monitor.getDropResult();
  38. if (!result && props.onDrop) {
  39. props.onDrop(item.data, '');
  40. }
  41. return undefined;
  42. },
  43. collect: (monitor) => {
  44. return {
  45. isOver: monitor.isOver(),
  46. canDrop: monitor.canDrop(),
  47. draggingColor: monitor.getItemType(),
  48. };
  49. },
  50. }));
  51. const createTreeNode = (data: any[], type: string): React.ReactNode => {
  52. return data.map((item: any) => {
  53. const isCanDrag = !props.notDragKeys?.includes(item.code);
  54. if (item.children) {
  55. return (
  56. <TreeNode
  57. selectable={false}
  58. key={item.code}
  59. title={
  60. <DragItem
  61. onDrop={props.onDrop}
  62. data={item}
  63. type={type}
  64. canDrag={isCanDrag}
  65. removeDragItem={props.removeDragItem}
  66. isSearch={searchKeys.includes(item.code)}
  67. />
  68. }
  69. >
  70. {createTreeNode(item.children, type)}
  71. </TreeNode>
  72. );
  73. }
  74. return (
  75. <TreeNode
  76. selectable={false}
  77. key={item.code}
  78. title={
  79. <DragItem
  80. onDrop={props.onDrop}
  81. data={item}
  82. type={type}
  83. canDrag={isCanDrag}
  84. removeDragItem={props.removeDragItem}
  85. isSearch={searchKeys.includes(item.code)}
  86. />
  87. }
  88. />
  89. );
  90. });
  91. };
  92. const getParentKey = (key: string | number, data: any): string => {
  93. let parentKey: string;
  94. data.forEach((item: any) => {
  95. if (item.children) {
  96. if (item.children.some((cItem: any) => cItem.code === key)) {
  97. parentKey = item.code
  98. } else if (!!getParentKey(key, item.children)) {
  99. parentKey = getParentKey(key, item.children)
  100. }
  101. }
  102. })
  103. // @ts-ignore
  104. return parentKey
  105. }
  106. const findAllItem = (data: any[], value: string): string[] => {
  107. return data.reduce((pre, next) => {
  108. const childrenKeys = next.children ? findAllItem(next.children, value) : [];
  109. return next.name.includes(value)
  110. ? [...pre, next.code, ...childrenKeys]
  111. : [...pre, ...childrenKeys];
  112. }, []);
  113. };
  114. const searchValue = (e: any) => {
  115. const value = e.target.value;
  116. if (value) {
  117. const newKeys = findAllItem(props.treeData, value);
  118. const newExpandedKeys = newKeys.map(key => {
  119. return getParentKey(key, props.treeData)
  120. })
  121. setSearchKeys(newKeys);
  122. setExpandedKeys(newExpandedKeys);
  123. } else {
  124. setSearchKeys([]);
  125. }
  126. setAutoExpandParent(true)
  127. };
  128. return (
  129. <div className={'tree-content'}>
  130. <div style={{ width: '75%' }}>
  131. <Input
  132. allowClear
  133. prefix={<SearchOutlined style={{ color: '#B3B3B3' }} />}
  134. placeholder={'请输入菜单名称'}
  135. onChange={debounce(searchValue, 500)}
  136. />
  137. </div>
  138. {props.droppableId === 'source' ? (
  139. <div className={'tree-body'}>
  140. <Tree
  141. expandedKeys={expandedKeys}
  142. onExpand={(_expandedKeys) => {
  143. setExpandedKeys(_expandedKeys);
  144. setAutoExpandParent(false)
  145. }}
  146. autoExpandParent={autoExpandParent}
  147. >
  148. {createTreeNode(newData, props.droppableId)}
  149. </Tree>
  150. </div>
  151. ) : (
  152. <div className={'tree-body'} ref={drop}>
  153. <Tree
  154. expandedKeys={expandedKeys}
  155. onExpand={(_expandedKeys) => {
  156. setExpandedKeys(_expandedKeys);
  157. setAutoExpandParent(false)
  158. }}
  159. autoExpandParent={autoExpandParent}
  160. draggable={{
  161. icon: false,
  162. }}
  163. onDrop={props.onTreeDrop}
  164. className="menu-setting-drag-tree"
  165. >
  166. {createTreeNode(props.treeData, props.droppableId)}
  167. </Tree>
  168. </div>
  169. )}
  170. </div>
  171. );
  172. };