TocTree.tsx 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. import { Tree, Typography } from "antd";
  2. import { useEffect, useState } from "react";
  3. import type { ListNodeData } from "./EditableTree";
  4. import PaliText from "../template/Wbw/PaliText";
  5. import type { Key } from "antd/lib/table/interface";
  6. import { randomString } from "../../utils";
  7. import type { DataNode, EventDataNode } from "antd/es/tree";
  8. const { Text } = Typography;
  9. interface IIdMap {
  10. key: string;
  11. id: string;
  12. }
  13. export interface TreeNodeData {
  14. key: string;
  15. id: string;
  16. title: string | React.ReactNode;
  17. isLeaf?: boolean;
  18. children?: TreeNodeData[];
  19. level: number;
  20. status?: number;
  21. deletedAt?: string | null;
  22. }
  23. function tocGetTreeData(
  24. listData: ListNodeData[],
  25. active = ""
  26. ): [TreeNodeData[] | undefined, IIdMap[]] {
  27. const treeData: TreeNodeData[] = [];
  28. let tocActivePath: TreeNodeData[] = [];
  29. const treeParents = [];
  30. const rootNode: TreeNodeData = {
  31. key: randomString(),
  32. id: "0",
  33. title: "root",
  34. level: 0,
  35. children: [],
  36. };
  37. const idMap: IIdMap[] = [];
  38. treeData.push(rootNode);
  39. let lastInsNode: TreeNodeData = rootNode;
  40. let iCurrLevel = 0;
  41. for (let index = 0; index < listData.length; index++) {
  42. const element = listData[index];
  43. const newNode: TreeNodeData = {
  44. key: randomString(),
  45. id: element.key,
  46. isLeaf: element.children === 0,
  47. title: element.title,
  48. level: element.level,
  49. status: element.status,
  50. deletedAt: element.deletedAt,
  51. };
  52. idMap.push({
  53. key: newNode.key,
  54. id: newNode.id,
  55. });
  56. if (newNode.level > iCurrLevel) {
  57. //新的层级比较大,为上一个的子目录
  58. treeParents.push(lastInsNode);
  59. if (typeof lastInsNode.children === "undefined") {
  60. lastInsNode.children = [];
  61. }
  62. lastInsNode.children.push(newNode);
  63. } else if (newNode.level === iCurrLevel) {
  64. //目录层级相同,为平级
  65. const parentNode = treeParents[treeParents.length - 1];
  66. if (typeof parentNode !== "undefined") {
  67. if (typeof parentNode.children === "undefined") {
  68. parentNode.children = [];
  69. }
  70. parentNode.children.push(newNode);
  71. }
  72. } else {
  73. // 小于 挂在上一个层级
  74. while (treeParents.length > 1) {
  75. treeParents.pop();
  76. if (treeParents[treeParents.length - 1].level < newNode.level) {
  77. break;
  78. }
  79. }
  80. const parentNode = treeParents[treeParents.length - 1];
  81. if (typeof parentNode !== "undefined") {
  82. if (typeof parentNode.children === "undefined") {
  83. parentNode.children = [];
  84. }
  85. parentNode.children.push(newNode);
  86. }
  87. }
  88. lastInsNode = newNode;
  89. iCurrLevel = newNode.level;
  90. if (active === element.key) {
  91. tocActivePath = [];
  92. for (let index = 1; index < treeParents.length; index++) {
  93. //treeParents[index]["expanded"] = true;
  94. tocActivePath.push(treeParents[index]);
  95. }
  96. }
  97. }
  98. return [treeData[0].children, idMap];
  99. }
  100. // It's just a lazy load simple demo. You can use tree map to optimize update perf.
  101. const updateTreeData = (
  102. list: TreeNodeData[],
  103. key: React.Key,
  104. children: TreeNodeData[]
  105. ): TreeNodeData[] => {
  106. return list.map((node) => {
  107. if (node.key === key) {
  108. console.debug("updateTreeData found", key, node);
  109. node.children = children;
  110. return node;
  111. }
  112. if (node.children) {
  113. return {
  114. ...node,
  115. children: updateTreeData(node.children, key, children),
  116. };
  117. }
  118. return node;
  119. });
  120. };
  121. interface IWidgetTocTree {
  122. treeData?: ListNodeData[];
  123. expandedKeys?: Key[];
  124. selectedKeys?: Key[];
  125. onSelect?: Function;
  126. onClick?: Function;
  127. onLoad?: (key: string) => string;
  128. }
  129. const TocTreeWidget = ({
  130. treeData,
  131. expandedKeys,
  132. selectedKeys,
  133. onSelect,
  134. onClick,
  135. onLoad,
  136. }: IWidgetTocTree) => {
  137. const [tree, setTree] = useState<TreeNodeData[]>();
  138. const [expanded, setExpanded] = useState<Key[]>();
  139. const [selected, setSelected] = useState<Key[]>();
  140. const [keyIdMap, setKeyIdMap] = useState<IIdMap[]>();
  141. useEffect(() => {
  142. if (treeData && treeData.length > 0) {
  143. const [data, idMap] = tocGetTreeData(treeData, "");
  144. setTree(data);
  145. setKeyIdMap(idMap);
  146. console.log(" tree data", data);
  147. } else {
  148. setTree([]);
  149. }
  150. }, [treeData]);
  151. useEffect(() => {
  152. if (!keyIdMap) {
  153. return;
  154. }
  155. const realKey = selectedKeys?.map((item) => {
  156. const mapIndex = keyIdMap?.findIndex((value) => value.id === item);
  157. if (mapIndex !== -1) {
  158. return keyIdMap[mapIndex].key;
  159. } else {
  160. return "";
  161. }
  162. });
  163. console.log("realKey", realKey);
  164. setSelected(realKey);
  165. }, [keyIdMap, selectedKeys]);
  166. useEffect(() => {
  167. if (!keyIdMap) {
  168. return;
  169. }
  170. const realKey = expandedKeys?.map((item) => {
  171. const mapIndex = keyIdMap?.findIndex((value) => value.id === item);
  172. if (mapIndex !== -1) {
  173. return keyIdMap[mapIndex].key;
  174. } else {
  175. return "";
  176. }
  177. });
  178. console.log("realKey", realKey);
  179. setExpanded(realKey);
  180. }, [expandedKeys, keyIdMap]);
  181. const onLoadData = ({ key, children }: any) =>
  182. new Promise<void>((resolve) => {
  183. if (children) {
  184. resolve();
  185. return;
  186. }
  187. if (typeof onLoad === "undefined") {
  188. resolve();
  189. return;
  190. }
  191. setTimeout(() => {
  192. setTree((origin) => {
  193. if (!origin) {
  194. return origin;
  195. }
  196. return updateTreeData(origin, key, [
  197. {
  198. title: "Child Node",
  199. key: randomString(),
  200. id: `${key}-0`,
  201. level: 2,
  202. },
  203. {
  204. title: "Child Node",
  205. key: randomString(),
  206. id: `${key}-1`,
  207. level: 2,
  208. },
  209. ]);
  210. });
  211. resolve();
  212. }, 1000);
  213. });
  214. return (
  215. <Tree
  216. treeData={tree}
  217. selectedKeys={selected}
  218. expandedKeys={expanded}
  219. autoExpandParent
  220. loadData={onLoadData}
  221. onExpand={(expandedKeys: Key[]) => {
  222. setExpanded(expandedKeys);
  223. }}
  224. onClick={(
  225. e: React.MouseEvent<HTMLSpanElement, MouseEvent>,
  226. node: EventDataNode<DataNode>
  227. ) => {
  228. if (typeof onClick !== "undefined") {
  229. const selectedId = keyIdMap?.find(
  230. (value) => node.key === value.key
  231. )?.id;
  232. if (selectedId) {
  233. onClick(selectedId, e);
  234. }
  235. }
  236. }}
  237. onSelect={(selectedKeys: Key[]) => {
  238. setSelected(selectedKeys);
  239. if (typeof onSelect !== "undefined") {
  240. const selectedId = keyIdMap
  241. ?.filter((value) => selectedKeys.includes(value.key))
  242. .map((item) => item.id);
  243. onSelect(selectedId);
  244. }
  245. }}
  246. blockNode
  247. titleRender={(node: TreeNodeData) => {
  248. const currNode =
  249. typeof node.title === "string" ? (
  250. node.title === "" ? (
  251. "[unnamed]"
  252. ) : (
  253. <PaliText
  254. textType={node.status === 10 ? "secondary" : undefined}
  255. text={node.title}
  256. />
  257. )
  258. ) : (
  259. node.title
  260. );
  261. return (
  262. <Text
  263. delete={node.deletedAt ? true : false}
  264. disabled={node.deletedAt ? true : false}
  265. type={node.status === 10 ? "secondary" : undefined}
  266. >
  267. {currNode}
  268. </Text>
  269. );
  270. }}
  271. />
  272. );
  273. };
  274. export default TocTreeWidget;