tree.ts 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727
  1. import { NodeContext } from './nodeCtx';
  2. export interface TreeRaw {
  3. ID: string;
  4. parentID: string;
  5. seq: number;
  6. [props: string]: any;
  7. }
  8. interface Node<T extends TreeRaw = TreeRaw> extends TreeRaw {
  9. getCtx: () => NodeContext<T>; // ctx对象改为getCtx(),因为handsontable数据循环引用会报错
  10. }
  11. export type TreeNode<T extends TreeRaw = TreeRaw> = Node<T> & T;
  12. export interface TreeIDMap<T extends TreeRaw = TreeRaw> {
  13. [propName: string]: TreeNode<T>;
  14. }
  15. export interface CtxIDMap<T extends TreeRaw = TreeRaw> {
  16. [propName: string]: NodeContext<T>;
  17. }
  18. export interface TreeParentMap<T extends TreeRaw = TreeRaw> {
  19. [propName: string]: TreeNode<T>[];
  20. }
  21. export interface ParentMap<T extends TreeRaw = TreeRaw> {
  22. [propName: string]: (TreeRaw | TreeNode<T>)[];
  23. }
  24. export interface UpdateItem {
  25. parentID?: string;
  26. seq?: number;
  27. [propName: string]: any;
  28. }
  29. export interface UpdateData {
  30. ID: string;
  31. update: UpdateItem;
  32. }
  33. export type None = null | undefined;
  34. export class Tree<T extends TreeRaw = TreeRaw> {
  35. // 原始数据,不经过排序的数据
  36. rawData: TreeNode<T>[];
  37. // 按照树结构拼装好、排好序的数据。实际只是原始数据进行排序,内部元素跟原始数据内部元素的引用是一致的
  38. data: TreeNode<T>[];
  39. rootID: string;
  40. // 默认初始顺序号。在对树结构进行操作后,没必要保证seq连号,只需保证正确排序就行,以减少需要更新的节点。
  41. readonly seqStartIndex = 0;
  42. // ID与原始数据条目映射
  43. IDMap: TreeIDMap<T>;
  44. // parentID与多条同parentID的原始数据条目映射,parentMap内部数组要始终保持正确排序
  45. parentMap: TreeParentMap<T>;
  46. // 节点上下文与节点ID映射
  47. ctxMap: CtxIDMap<T>;
  48. constructor(rawData: TreeRaw[], rootID = '-1', expanded?: boolean) {
  49. this.rootID = rootID;
  50. this.rawData = this.genNodeContext(rawData);
  51. this.rawData = this.sort(this.rawData);
  52. this.data = [];
  53. this.IDMap = {};
  54. this.parentMap = {};
  55. this.ctxMap = {};
  56. this.genMap(this.rawData, expanded);
  57. this.genData();
  58. }
  59. // 根据seq进行排序
  60. sort(nodes: TreeNode<T>[]): TreeNode<T>[] {
  61. return nodes.sort((a, b) => a.seq - b.seq);
  62. }
  63. // 获取节点的所有parentID
  64. getParentIDList(nodes: TreeNode<T>[]): Set<string> {
  65. const parentIDList: Set<string> = new Set();
  66. nodes.forEach(node => parentIDList.add(node.parentID));
  67. return parentIDList;
  68. }
  69. // 生成节点getCtx方法(由于handsontable循环引用bug,将ctx对象改为getCtx方法),用于获取节点上下文,挂载相关节点方法,TreeRaw转换为TreeNode
  70. private genNodeContext(rawData: TreeRaw[]): TreeNode<T>[] {
  71. rawData.forEach(item => {
  72. item.getCtx = () => this.ctxMap[item.ID];
  73. });
  74. return rawData as TreeNode<T>[];
  75. }
  76. // 生成映射表
  77. private genMap(data: TreeNode<T>[], expanded?: boolean): void {
  78. data.forEach(item => {
  79. this.IDMap[item.ID] = item;
  80. this.ctxMap[item.ID] = new NodeContext(item, this, expanded);
  81. (
  82. this.parentMap[item.parentID] || (this.parentMap[item.parentID] = [])
  83. ).push(item);
  84. });
  85. }
  86. // 获取顶层原始数据
  87. getRoots(): TreeNode<T>[] {
  88. return this.parentMap[this.rootID] || [];
  89. }
  90. // 生成按照树结构排好序的数据
  91. private genData(): void {
  92. // genData时不需要排序,因为rawData已经排好序
  93. const roots = this.getRoots();
  94. const pushNodesToData = (nodes: TreeNode<T>[]): void => {
  95. nodes.forEach(node => {
  96. this.data.push(node);
  97. const children = this.parentMap[node.ID];
  98. if (children && children.length) {
  99. pushNodesToData(children);
  100. }
  101. });
  102. };
  103. pushNodesToData(roots);
  104. }
  105. // 重新生成排序序好的数据,不改变原引用
  106. private reGenData(): void {
  107. this.data.splice(0, this.data.length);
  108. this.genData();
  109. }
  110. // 将相关parentMap内的数据、data进行重新排序。新增、删除等操作进行完后,数据需要重新排序
  111. reSortData(nodes: TreeNode<T>[]): void {
  112. const toSortList = this.getParentIDList(nodes);
  113. toSortList.forEach(parentID => {
  114. if (this.parentMap[parentID] && this.parentMap[parentID].length) {
  115. this.sort(this.parentMap[parentID]);
  116. }
  117. });
  118. this.reGenData();
  119. }
  120. // 根据parentID对parentMap进行重新排序,并重新排序生成data
  121. resortDataByID(parentIDList: string[]): void {
  122. parentIDList.forEach(parentID => {
  123. if (this.parentMap[parentID] && this.parentMap[parentID].length) {
  124. this.sort(this.parentMap[parentID]);
  125. }
  126. });
  127. this.reGenData();
  128. }
  129. // 根据字段值查找节点
  130. // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
  131. findByField(field: string, value: any): TreeNode<T> | null {
  132. return this.data.find(item => item[field] === value) || null;
  133. }
  134. // 查找ID节点
  135. find(ID: string): TreeNode<T> | null {
  136. return this.IDMap[ID] || null;
  137. }
  138. // 查找ID节点的父节点
  139. findParent(ID: string): TreeNode<T> | null {
  140. const node = this.find(ID);
  141. if (!node) {
  142. return null;
  143. }
  144. return this.find(node.parentID);
  145. }
  146. // 查找ID节点的下一个节点
  147. findNext(ID: string): TreeNode<T> | null {
  148. const node = this.find(ID);
  149. if (!node) {
  150. return null;
  151. }
  152. const nodes = this.parentMap[node.parentID];
  153. const nodeIndex = nodes.findIndex(item => item.ID === node.ID);
  154. if (nodeIndex < 0) {
  155. return null;
  156. }
  157. return nodes[nodeIndex + 1] || null;
  158. }
  159. // 查找ID节点的上一个节点
  160. findPrev(ID: string): TreeNode<T> | null {
  161. const node = this.find(ID);
  162. if (!node) {
  163. return null;
  164. }
  165. const nodes = this.parentMap[node.parentID];
  166. const nodeIndex = nodes.findIndex(item => item.ID === node.ID);
  167. if (nodeIndex < 0) {
  168. return null;
  169. }
  170. return nodes[nodeIndex - 1] || null;
  171. }
  172. // 查询ID节点的子节点
  173. findChildren(ID: string): TreeNode<T>[] {
  174. return this.parentMap[ID] || [];
  175. }
  176. updateValue(updateData: UpdateData[]): void {
  177. if (updateData.length) {
  178. updateData.forEach(updateItem => {
  179. const node = this.find(updateItem.ID);
  180. if (node) {
  181. Object.assign(node, updateItem.update);
  182. }
  183. });
  184. }
  185. }
  186. // 递归获取节点
  187. getNodesPosterity(
  188. treeNodes: TreeNode<T>[],
  189. includeSelf = true
  190. ): TreeNode<T>[] {
  191. const rst: TreeNode<T>[] = [];
  192. const pushNodes = (nodes: TreeNode<T>[]): void => {
  193. nodes.forEach(node => {
  194. if (includeSelf || !treeNodes.includes(node)) {
  195. rst.push(node);
  196. }
  197. const children = this.findChildren(node.ID);
  198. if (children.length) {
  199. pushNodes(children);
  200. }
  201. });
  202. };
  203. pushNodes(treeNodes);
  204. return rst;
  205. }
  206. // 节选中点块是否是可操作的节点块,选中一批节点进行升降、上下移的时候可能需要用到这个判断。
  207. // 以第一个节点为基准,如果后续的节点深度均大于于于等于第一个节点的深度,且节点间是连续的,则此为可操作节点块
  208. isOperable(nodes: TreeNode<T>[]): boolean {
  209. const baseDepth = nodes[0].getCtx().depth();
  210. for (let i = 0; i < nodes.length; i++) {
  211. const node = nodes[i];
  212. const depth = node.getCtx().depth();
  213. if (depth < baseDepth) {
  214. return false;
  215. }
  216. const nextRowNode = nodes[i + 1];
  217. if (
  218. nextRowNode &&
  219. node.getCtx().row() + 1 !== nextRowNode.getCtx().row()
  220. ) {
  221. return false;
  222. }
  223. }
  224. return true;
  225. }
  226. // 从节点块中获取相同深度的节点
  227. sameDepthNodes(nodes: TreeNode<T>[], depth?: number): TreeNode<T>[] {
  228. if (!depth) {
  229. depth = nodes[0].getCtx().depth();
  230. }
  231. return nodes.filter(node => node.getCtx().depth() === depth);
  232. }
  233. // 展开所有节点
  234. expandAll(): void {
  235. this.data.forEach(item => {
  236. item.getCtx().expanded = true;
  237. });
  238. }
  239. // 展开节点
  240. expand(nodes: TreeNode<T>[]): void {
  241. nodes.forEach(item => {
  242. item.getCtx().expanded = true;
  243. });
  244. }
  245. // 折叠所有节点
  246. collapseAll(): void {
  247. this.data.forEach(item => {
  248. item.getCtx().expanded = false;
  249. });
  250. }
  251. // 折叠节点
  252. collapse(nodes: TreeNode<T>[]): void {
  253. nodes.forEach(item => {
  254. item.getCtx().expanded = false;
  255. });
  256. }
  257. // 准备插入节点,插入节点前,计算出需要更新的数据。(可能会直接覆盖插入数据的seq)
  258. // 可调用完此方法后,将需要更新、插入的数据提交至数据库,成功响应后调用插入节点更新缓存的方法
  259. prepareInsert(rawData: TreeRaw[]): UpdateData[] {
  260. const updateData: UpdateData[] = [];
  261. const insertParentMap: ParentMap<T> = {};
  262. // 将相同父项的插入数据和已存在数据进行合并
  263. rawData.forEach(item => {
  264. if (typeof item.seq === 'undefined') {
  265. item.seq = this.seqStartIndex;
  266. }
  267. (
  268. insertParentMap[item.parentID] || (insertParentMap[item.parentID] = [])
  269. ).push(item);
  270. });
  271. const combineMap: ParentMap<T> = {};
  272. Object.entries(insertParentMap).forEach(([parentID, insertItems]) => {
  273. const items = this.parentMap[parentID];
  274. combineMap[parentID] = [...insertParentMap[parentID]];
  275. if (items) {
  276. combineMap[parentID].push(...items);
  277. }
  278. // 重新排序
  279. const combineItems = combineMap[parentID];
  280. this.sort(combineItems as any);
  281. combineItems.forEach((item, index) => {
  282. // 插入数据重新赋值
  283. if (insertItems.includes(item)) {
  284. item.seq = index;
  285. } else if (item.seq !== index) {
  286. // 需要更新的原数据
  287. updateData.push({
  288. ID: item.ID,
  289. update: {
  290. seq: index,
  291. },
  292. });
  293. }
  294. });
  295. });
  296. return updateData;
  297. }
  298. // 插入节点数据
  299. insert(items: TreeRaw[], updateData: UpdateData[] = []): TreeNode<T>[] {
  300. // 更新需要更新的节点(一般为更新seq)
  301. this.updateValue(updateData);
  302. // 建立映射、插入数据
  303. const nodes = this.genNodeContext(items);
  304. this.genMap(nodes);
  305. this.rawData.push(...nodes);
  306. // 排序
  307. this.reSortData(nodes);
  308. return nodes;
  309. }
  310. // 准备删除,返回所有需要删除的节点,包括嵌套节点
  311. prepareDelete(deleteNodes: TreeNode<T>[]): TreeNode<T>[] {
  312. return this.getNodesPosterity(deleteNodes);
  313. }
  314. /**
  315. * 删除节点
  316. * @param treeNodes - 要删除的节点,不需要包含嵌套节点
  317. */
  318. delete(treeNodes: TreeNode<T>[]): TreeNode<T>[] {
  319. const allDeletedNodes: TreeNode<T>[] = [];
  320. // 递归删除节点
  321. const deleteNodes = (nodes: TreeNode<T>[]): void => {
  322. // 删除映射、删除数据
  323. const toDels: { nodes: TreeNode<T>[]; delNode: TreeNode<T> }[] = [];
  324. nodes.forEach(node => {
  325. allDeletedNodes.push(node);
  326. delete this.IDMap[node.ID];
  327. delete this.ctxMap[node.ID];
  328. const children = this.parentMap[node.ID];
  329. delete this.parentMap[node.ID];
  330. const nodesInParentMap = this.parentMap[node.parentID];
  331. if (nodesInParentMap && nodesInParentMap.length) {
  332. const nIndex = nodesInParentMap.findIndex(
  333. item => item.ID === node.ID
  334. );
  335. if (nIndex >= 0) {
  336. toDels.push({ nodes: nodesInParentMap, delNode: node });
  337. }
  338. }
  339. const index = this.rawData.findIndex(item => item.ID === node.ID);
  340. if (index >= 0) {
  341. this.rawData.splice(index, 1);
  342. }
  343. if (children && children.length) {
  344. deleteNodes(children);
  345. }
  346. });
  347. // 删除parentMap的数据
  348. toDels.forEach(delItem => {
  349. const delIndex = delItem.nodes.findIndex(
  350. item => item.ID === delItem.delNode.ID
  351. );
  352. if (delIndex >= 0) {
  353. delItem.nodes.splice(delIndex, 1);
  354. }
  355. });
  356. };
  357. deleteNodes(treeNodes);
  358. // 排序
  359. this.reSortData(allDeletedNodes);
  360. return allDeletedNodes;
  361. }
  362. // IDList 返回所有需要删除的节点,包括嵌套节点
  363. prepareDeleteByID(IDList: string[]): TreeNode<T>[] {
  364. const deleteNodes: TreeNode<T>[] = [];
  365. IDList.forEach(ID => {
  366. const node = this.find(ID);
  367. if (node) {
  368. deleteNodes.push(node);
  369. }
  370. });
  371. return this.prepareDelete(deleteNodes);
  372. }
  373. /**
  374. * 根据ID删除节点
  375. * @param IDList - 要删除的节点的ID列表(不包含嵌套节点ID)
  376. */
  377. deleteByID(IDList: string[]): TreeNode<T>[] {
  378. const deleteNodes: TreeNode<T>[] = [];
  379. IDList.forEach(ID => {
  380. const node = this.find(ID);
  381. if (node) {
  382. deleteNodes.push(node);
  383. }
  384. });
  385. this.delete(deleteNodes);
  386. return deleteNodes;
  387. }
  388. // 检查节点是否有相同顺序号的错误情况(协作的时候可能导致)
  389. checkSeq(nodes: TreeNode<T>[]): boolean {
  390. const seqList: number[] = nodes.map(node => node.seq);
  391. const seqSet = new Set(seqList);
  392. return seqList.length === seqSet.size;
  393. }
  394. // 序号错乱了,移动的时候,只能重新排编号
  395. prepareSpecialMove(
  396. allNodes: TreeNode<T>[],
  397. sourceNodes: TreeNode<T>[],
  398. targetNode: TreeNode<T>
  399. ): UpdateData[] {
  400. const fisrtNodeIdx = allNodes.findIndex(
  401. node => node.ID === sourceNodes[0].ID
  402. ); // 不能用indexOf,因为传入的可能时proxy节点
  403. const targetNodeIdx = allNodes.findIndex(node => node.ID === targetNode.ID);
  404. const position = fisrtNodeIdx > targetNodeIdx ? 'prev' : 'next';
  405. const nodes = []; // 防止改了原始数组顺序
  406. const sourceNodeIDs = sourceNodes.map(node => node.ID); // 不能直接用sourceNodes includes判断,因为传入的可能时proxy节点
  407. for (const node of allNodes) {
  408. // 不能用includes,因为传入的可能时proxy节点
  409. if (!sourceNodeIDs.includes(node.ID)) {
  410. nodes.push(node);
  411. }
  412. }
  413. const targetIdx = nodes.findIndex(node => node.ID === targetNode.ID);
  414. if (position === 'prev') {
  415. // 插到targetNode前面
  416. nodes.splice(targetIdx, 0, ...sourceNodes);
  417. } else {
  418. // 插到targetNode后面
  419. nodes.splice(targetIdx + 1, 0, ...sourceNodes);
  420. }
  421. // 重新编码
  422. return nodes.map((node, index) => ({
  423. ID: node.ID,
  424. update: { seq: index },
  425. }));
  426. }
  427. // 准备上移节点块(连续的兄弟节点),注意节点的seq可能不连号
  428. prepareUpMove(nodes: TreeNode<T>[]): UpdateData[] {
  429. const updateData: UpdateData[] = [];
  430. const firstNode = nodes[0];
  431. const firstNodePrev = this.findPrev(firstNode.ID);
  432. if (!firstNodePrev) {
  433. return [];
  434. }
  435. const brothers = firstNode.getCtx().brothers();
  436. if (!this.checkSeq(brothers)) {
  437. // 存在序号异常情况,需要特殊处理
  438. return this.prepareSpecialMove(brothers, nodes, firstNodePrev);
  439. }
  440. let tempSeq = firstNodePrev.seq;
  441. nodes.forEach(node => {
  442. const orgSeq = node.seq;
  443. updateData.push({
  444. ID: node.ID,
  445. update: { seq: tempSeq },
  446. });
  447. tempSeq = orgSeq;
  448. });
  449. updateData.push({
  450. ID: firstNodePrev.ID,
  451. update: { seq: tempSeq },
  452. });
  453. return updateData;
  454. }
  455. // 准备下移节点块(连续的兄弟节点),注意节点的seq可能不连号
  456. prepareDownMove(nodes: TreeNode<T>[]): UpdateData[] {
  457. const updateData: UpdateData[] = [];
  458. const lastNode = nodes[nodes.length - 1];
  459. const lastNodeNext = this.findNext(lastNode.ID);
  460. if (!lastNodeNext) {
  461. return [];
  462. }
  463. const brothers = lastNode.getCtx().brothers();
  464. if (!this.checkSeq(brothers)) {
  465. // 存在序号异常情况,需要特殊处理
  466. return this.prepareSpecialMove(brothers, nodes, lastNodeNext);
  467. }
  468. let tempSeq = lastNodeNext.seq;
  469. for (let i = nodes.length - 1; i >= 0; i--) {
  470. const node = nodes[i];
  471. const orgSeq = node.seq;
  472. updateData.push({
  473. ID: node.ID,
  474. update: { seq: tempSeq },
  475. });
  476. tempSeq = orgSeq;
  477. }
  478. updateData.push({
  479. ID: lastNodeNext.ID,
  480. update: { seq: tempSeq },
  481. });
  482. return updateData;
  483. }
  484. // 上下移
  485. move(nodes: TreeNode<T>[], updateData: UpdateData[]): void {
  486. this.updateValue(updateData);
  487. this.reSortData(nodes);
  488. }
  489. // 准备升级节点块(连续的兄弟节点),不维护seq连号
  490. prepareUpLevel(nodes: TreeNode<T>[]): UpdateData[] {
  491. const updateData: UpdateData[] = [];
  492. const firstNode = nodes[0];
  493. const lastNode = nodes[nodes.length - 1];
  494. const parent = this.findParent(firstNode.ID);
  495. if (!parent) {
  496. return [];
  497. }
  498. const baseSeq = parent.seq + 1;
  499. nodes.forEach((node, index) => {
  500. updateData.push({
  501. ID: node.ID,
  502. update: { parentID: parent.parentID, seq: baseSeq + index },
  503. });
  504. });
  505. const parentNextBrothers = parent.getCtx().nextBrothers();
  506. // 因为seq可能是不连号的,如果升级块的最末节点seq,小于下一节点的seq,那就不更新所有下兄弟节点的seq,减少更新的数据量
  507. const lastNodeCurSeq = updateData[updateData.length - 1].update.seq;
  508. const firstBrother = parentNextBrothers[0];
  509. if (lastNodeCurSeq && firstBrother && lastNodeCurSeq >= firstBrother.seq) {
  510. parentNextBrothers.forEach((node, index) => {
  511. updateData.push({
  512. ID: node.ID,
  513. update: { seq: baseSeq + index + nodes.length },
  514. });
  515. });
  516. }
  517. // 最末节点的所有后兄弟节点,成为最末节点的子节点
  518. const lastNodeNextBrothers = lastNode.getCtx().nextBrothers();
  519. const lastNodeLastChild = lastNode.getCtx().lastChild();
  520. const lastNodeLastChildSeq = lastNodeLastChild ? lastNodeLastChild.seq : 0;
  521. lastNodeNextBrothers.forEach((node, index) => {
  522. updateData.push({
  523. ID: node.ID,
  524. update: { parentID: lastNode.ID, seq: index + lastNodeLastChildSeq },
  525. });
  526. });
  527. return updateData;
  528. }
  529. upLevel(nodes: TreeNode<T>[], updateData: UpdateData[]): void {
  530. const firstNode = nodes[0];
  531. const lastNode = nodes[nodes.length - 1];
  532. if (!firstNode.getCtx().canUpLevel()) {
  533. return;
  534. }
  535. const orgParentID = firstNode.parentID;
  536. const orgBrothers = this.parentMap[orgParentID];
  537. const lastNodeNextBrothers = lastNode.getCtx().nextBrothers();
  538. const index = orgBrothers.findIndex(item => item.ID === firstNode.ID);
  539. orgBrothers.splice(index, nodes.length + lastNodeNextBrothers.length);
  540. (this.parentMap[lastNode.ID] || (this.parentMap[lastNode.ID] = [])).push(
  541. ...lastNodeNextBrothers
  542. );
  543. this.updateValue(updateData);
  544. const newParentID = firstNode.parentID;
  545. this.parentMap[newParentID].push(...nodes);
  546. this.resortDataByID([orgParentID, newParentID, lastNode.ID]);
  547. }
  548. // 准备降级节点块(连续的兄弟节点),不维护seq连号
  549. prepareDownLevel(nodes: TreeNode<T>[]): UpdateData[] {
  550. const updateData: UpdateData[] = [];
  551. const firstNode = nodes[0];
  552. const prevNode = this.findPrev(firstNode.ID);
  553. if (!prevNode) {
  554. return [];
  555. }
  556. // 节点块成为前节点的子节点
  557. const prevNodeLastChild = prevNode.getCtx().lastChild();
  558. const baseSeq = prevNodeLastChild
  559. ? prevNodeLastChild.seq + 1
  560. : this.seqStartIndex;
  561. nodes.forEach((node, index) => {
  562. updateData.push({
  563. ID: node.ID,
  564. update: { parentID: prevNode.ID, seq: baseSeq + index },
  565. });
  566. });
  567. return updateData;
  568. }
  569. downLevel(nodes: TreeNode<T>[], updateData: UpdateData[]): void {
  570. const firstNode = nodes[0];
  571. if (!firstNode.getCtx().canDownLevel()) {
  572. return;
  573. }
  574. const prevNode = this.findPrev(firstNode.ID);
  575. if (!prevNode) {
  576. return;
  577. }
  578. const orgBrothers = this.parentMap[firstNode.parentID];
  579. const index = orgBrothers.findIndex(item => item.ID === firstNode.ID);
  580. orgBrothers.splice(index, nodes.length);
  581. (this.parentMap[prevNode.ID] || (this.parentMap[prevNode.ID] = [])).push(
  582. ...nodes
  583. );
  584. this.updateValue(updateData);
  585. this.reGenData();
  586. }
  587. // 准备移动节点块(连续的兄弟节点),不维护seq连号
  588. prepareMoveTo(
  589. nodes: TreeNode<T>[],
  590. parent: TreeNode<T> | None,
  591. next: TreeNode<T> | None
  592. ): UpdateData[] {
  593. const updateData: UpdateData[] = [];
  594. let prev: TreeNode<T> | null;
  595. if (next) {
  596. prev = next.getCtx().prev();
  597. } else {
  598. const roots = this.getRoots();
  599. prev = parent
  600. ? parent.getCtx().lastChild()
  601. : roots[roots.length - 1] || null;
  602. }
  603. const baseSeq = prev ? prev.seq + 1 : this.seqStartIndex;
  604. updateData.push(
  605. ...nodes.map((node, index) => ({
  606. ID: node.ID,
  607. update: {
  608. parentID: (parent && parent.ID) || this.rootID,
  609. seq: baseSeq + index,
  610. },
  611. }))
  612. );
  613. const curBaseSeq = baseSeq + nodes.length;
  614. // eslint-disable-next-line no-nested-ternary
  615. const nextBrothers = prev
  616. ? prev.getCtx().nextBrothers()
  617. : next
  618. ? [next, ...next.getCtx().nextBrothers()]
  619. : [];
  620. updateData.push(
  621. ...nextBrothers.map((node, index) => ({
  622. ID: node.ID,
  623. update: {
  624. seq: curBaseSeq + index,
  625. },
  626. }))
  627. );
  628. return updateData;
  629. }
  630. moveTo(
  631. nodes: TreeNode<T>[],
  632. parent: TreeNode<T> | None,
  633. updateData: UpdateData[]
  634. ): void {
  635. const firstNode = nodes[0];
  636. const newParentID = parent ? parent.ID : this.rootID;
  637. const orgParentID = firstNode.parentID;
  638. const orgBrothers = this.parentMap[orgParentID];
  639. const index = orgBrothers.findIndex(item => item.ID === firstNode.ID);
  640. orgBrothers.splice(index, nodes.length);
  641. (this.parentMap[newParentID] || (this.parentMap[newParentID] = [])).push(
  642. ...nodes
  643. );
  644. this.updateValue(updateData);
  645. this.resortDataByID([orgParentID, newParentID]);
  646. }
  647. // 清除所有节点(不变更data引用)
  648. clear(): void {
  649. this.rawData.splice(0, this.rawData.length);
  650. this.data.splice(0, this.data.length);
  651. Object.keys(this.parentMap).forEach(key => delete this.parentMap[key]);
  652. Object.keys(this.IDMap).forEach(key => delete this.IDMap[key]);
  653. Object.keys(this.ctxMap).forEach(key => delete this.ctxMap[key]);
  654. }
  655. // 检测树结构
  656. check(): TreeNode<T>[] {
  657. const errNodes: TreeNode<T>[] = [];
  658. errNodes.push(...this.checkParent());
  659. return errNodes;
  660. }
  661. // 检查找不到父项的(parentID对应的节点不存在),返回有问题的节点
  662. private checkParent(): TreeNode<T>[] {
  663. return this.rawData.filter(
  664. node => node.parentID !== this.rootID && !this.IDMap[node.parentID]
  665. );
  666. }
  667. }