|
|
@@ -45,6 +45,7 @@ export class Tree {
|
|
|
|
|
|
readonly rootID: string = '-1';
|
|
|
|
|
|
+ // 默认初始顺序号。在对树结构进行操作后,没必要保证seq连号,只需保证正确排序就行,以减少需要更新的节点。
|
|
|
readonly seqStartIndex = 0;
|
|
|
|
|
|
// ID与原始数据条目映射
|
|
|
@@ -199,7 +200,51 @@ export class Tree {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 准备插入节点,插入节点前,计算出需要更新的数据。
|
|
|
+ // 递归获取节点
|
|
|
+ getNodesPosterity(treeNodes: TreeNode[], includeSelf = true): TreeNode[] {
|
|
|
+ const rst: TreeNode[] = [];
|
|
|
+ const pushNodes = (nodes: TreeNode[]): void => {
|
|
|
+ nodes.forEach(node => {
|
|
|
+ if (includeSelf || !treeNodes.includes(node)) {
|
|
|
+ rst.push(node);
|
|
|
+ }
|
|
|
+ const children = this.findChilren(node.ID);
|
|
|
+ if (children.length) {
|
|
|
+ pushNodes(children);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ };
|
|
|
+ pushNodes(treeNodes);
|
|
|
+ return rst;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 节选中点块是否是可操作的节点块,选中一批节点进行升降、上下移的时候可能需要用到这个判断。
|
|
|
+ // 以第一个节点为基准,如果后续的节点深度均大于于于等于第一个节点的深度,且节点间是连续的,则此为可操作节点块
|
|
|
+ isOperable(nodes: TreeNode[]): boolean {
|
|
|
+ const baseDepth = nodes[0].ctx.depth();
|
|
|
+ for (let i = 0; i < nodes.length; i++) {
|
|
|
+ const node = nodes[i];
|
|
|
+ const depth = node.ctx.depth();
|
|
|
+ if (depth < baseDepth) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ const nextRowNode = nodes[i + 1];
|
|
|
+ if (nextRowNode && node.ctx.row() + 1 !== nextRowNode.ctx.row()) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 从节点块中获取相同深度的节点
|
|
|
+ sameDepthNodes(nodes: TreeNode[], depth: number | None): TreeNode[] {
|
|
|
+ if (!depth) {
|
|
|
+ depth = nodes[0].ctx.depth();
|
|
|
+ }
|
|
|
+ return nodes.filter(node => node.ctx.depth() === depth);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 准备插入节点,插入节点前,计算出需要更新的数据。(可能会直接覆盖插入数据的seq)
|
|
|
// 可调用完此方法后,将需要更新、插入的数据提交至数据库,成功响应后调用插入节点更新缓存的方法
|
|
|
prepareInsert(rawData: TreeRaw[]): UpdateData[] {
|
|
|
const updateData: UpdateData[] = [];
|
|
|
@@ -257,35 +302,17 @@ export class Tree {
|
|
|
return this.data;
|
|
|
}
|
|
|
|
|
|
- // 准备删除,获取删除时,需要更新的节点
|
|
|
- prepareDelete(deleteNodes: TreeNode[]): UpdateData[] {
|
|
|
- const updateData: UpdateData[] = [];
|
|
|
- // 获取去掉删除节点后,未删除同层节点需要更新seq的数据
|
|
|
- const parentIDList = Tree.getParentIDList(deleteNodes);
|
|
|
- parentIDList.forEach(parentID => {
|
|
|
- const brothers = this.parentMap[parentID];
|
|
|
- const tempBrothers = brothers.filter(node => !deleteNodes.includes(node));
|
|
|
- Tree.sort(tempBrothers);
|
|
|
- tempBrothers.forEach((node, index) => {
|
|
|
- if (node.seq !== index) {
|
|
|
- updateData.push({
|
|
|
- ID: node.ID,
|
|
|
- update: { seq: index },
|
|
|
- });
|
|
|
- }
|
|
|
- });
|
|
|
- });
|
|
|
- return updateData;
|
|
|
+ // 准备删除,返回所有需要删除的节点,包括嵌套节点
|
|
|
+ prepareDelete(deleteNodes: TreeNode[]): TreeNode[] {
|
|
|
+ return this.getNodesPosterity(deleteNodes);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 删除节点
|
|
|
* @param {TreeNode[]} treeNodes - 要删除的节点,不需要包含嵌套节点
|
|
|
- * @param {UpdateData[]} updateData - 要更新的数据
|
|
|
* @return {TreeNode[]} 返回删除节点后,树的data
|
|
|
*/
|
|
|
- delete(treeNodes: TreeNode[], updateData: UpdateData[]): TreeNode[] {
|
|
|
- this.updateValue(updateData);
|
|
|
+ delete(treeNodes: TreeNode[]): TreeNode[] {
|
|
|
const allDeletedNodes: TreeNode[] = [];
|
|
|
// 递归删除节点
|
|
|
const deleteNodes = (nodes: TreeNode[]): void => {
|
|
|
@@ -317,7 +344,8 @@ export class Tree {
|
|
|
return this.data;
|
|
|
}
|
|
|
|
|
|
- prepareDeleteByID(IDList: string[]): UpdateData[] {
|
|
|
+ // IDList 返回所有需要删除的节点,包括嵌套节点
|
|
|
+ prepareDeleteByID(IDList: string[]): TreeNode[] {
|
|
|
const deleteNodes: TreeNode[] = [];
|
|
|
IDList.forEach(ID => {
|
|
|
const node = this.find(ID);
|
|
|
@@ -330,11 +358,10 @@ export class Tree {
|
|
|
|
|
|
/**
|
|
|
* 根据ID删除节点
|
|
|
- * @param {string[]} IDList - 要删除的节点的ID列表
|
|
|
- * @param {UpdateData[]} updateData - 要更新的数据
|
|
|
+ * @param {string[]} IDList - 要删除的节点的ID列表(不包含嵌套节点ID)
|
|
|
* @return {TreeNode[]} - 返回被删除的所有节点
|
|
|
*/
|
|
|
- deleteByID(IDList: string[], updateData: UpdateData[]): TreeNode[] {
|
|
|
+ deleteByID(IDList: string[]): TreeNode[] {
|
|
|
const deleteNodes: TreeNode[] = [];
|
|
|
IDList.forEach(ID => {
|
|
|
const node = this.find(ID);
|
|
|
@@ -342,11 +369,11 @@ export class Tree {
|
|
|
deleteNodes.push(node);
|
|
|
}
|
|
|
});
|
|
|
- this.delete(deleteNodes, updateData);
|
|
|
+ this.delete(deleteNodes);
|
|
|
return deleteNodes;
|
|
|
}
|
|
|
|
|
|
- // 准备上移节点块(连续的兄弟节点)
|
|
|
+ // 准备上移节点块(连续的兄弟节点),注意节点的seq可能不连号
|
|
|
prepareUpMove(nodes: TreeNode[]): UpdateData[] {
|
|
|
const updateData: UpdateData[] = [];
|
|
|
const firstNode = nodes[0];
|
|
|
@@ -354,20 +381,23 @@ export class Tree {
|
|
|
if (!firstNodePrev) {
|
|
|
return [];
|
|
|
}
|
|
|
+ let tempSeq = firstNodePrev.seq;
|
|
|
nodes.forEach(node => {
|
|
|
+ const orgSeq = node.seq;
|
|
|
updateData.push({
|
|
|
ID: node.ID,
|
|
|
- update: { seq: node.seq - 1 },
|
|
|
+ update: { seq: tempSeq },
|
|
|
});
|
|
|
+ tempSeq = orgSeq;
|
|
|
});
|
|
|
updateData.push({
|
|
|
ID: firstNodePrev.ID,
|
|
|
- update: { seq: firstNodePrev.seq + nodes.length },
|
|
|
+ update: { seq: tempSeq },
|
|
|
});
|
|
|
return updateData;
|
|
|
}
|
|
|
|
|
|
- // 准备下移节点块(连续的兄弟节点)
|
|
|
+ // 准备下移节点块(连续的兄弟节点),注意节点的seq可能不连号
|
|
|
prepareDownMove(nodes: TreeNode[]): UpdateData[] {
|
|
|
const updateData: UpdateData[] = [];
|
|
|
const lastNode = nodes[nodes.length - 1];
|
|
|
@@ -375,15 +405,19 @@ export class Tree {
|
|
|
if (!lastNodeNext) {
|
|
|
return [];
|
|
|
}
|
|
|
- nodes.forEach(node => {
|
|
|
+ let tempSeq = lastNodeNext.seq;
|
|
|
+ for (let i = nodes.length - 1; i >= 0; i--) {
|
|
|
+ const node = nodes[i];
|
|
|
+ const orgSeq = node.seq;
|
|
|
updateData.push({
|
|
|
ID: node.ID,
|
|
|
- update: { seq: node.seq + 1 },
|
|
|
+ update: { seq: tempSeq },
|
|
|
});
|
|
|
- });
|
|
|
+ tempSeq = orgSeq;
|
|
|
+ }
|
|
|
updateData.push({
|
|
|
ID: lastNodeNext.ID,
|
|
|
- update: { seq: lastNodeNext.seq - nodes.length },
|
|
|
+ update: { seq: tempSeq },
|
|
|
});
|
|
|
return updateData;
|
|
|
}
|
|
|
@@ -394,7 +428,7 @@ export class Tree {
|
|
|
this.reSortData(nodes);
|
|
|
}
|
|
|
|
|
|
- // 准备升级节点块(连续的兄弟节点)
|
|
|
+ // 准备升级节点块(连续的兄弟节点),不维护seq连号
|
|
|
prepareUpLevel(nodes: TreeNode[]): UpdateData[] {
|
|
|
const updateData: UpdateData[] = [];
|
|
|
const firstNode = nodes[0];
|
|
|
@@ -411,12 +445,17 @@ export class Tree {
|
|
|
});
|
|
|
});
|
|
|
const parentNextBrothers = parent.ctx.nextBrothers();
|
|
|
- parentNextBrothers.forEach((node, index) => {
|
|
|
- updateData.push({
|
|
|
- ID: node.ID,
|
|
|
- update: { seq: baseSeq + index + nodes.length },
|
|
|
+ // 因为seq可能是不连号的,如果上移的最末节点seq,小于下一节点的seq,那就不更新所有下兄弟节点的seq,减少更新的数据量
|
|
|
+ const lastNodeCurSeq = updateData[updateData.length - 1].update.seq;
|
|
|
+ const firstBrohter = parentNextBrothers[0];
|
|
|
+ if (lastNodeCurSeq && lastNodeCurSeq >= firstBrohter.seq) {
|
|
|
+ parentNextBrothers.forEach((node, index) => {
|
|
|
+ updateData.push({
|
|
|
+ ID: node.ID,
|
|
|
+ update: { seq: baseSeq + index + nodes.length },
|
|
|
+ });
|
|
|
});
|
|
|
- });
|
|
|
+ }
|
|
|
// 最末节点的所有后兄弟节点,成为最末节点的子节点
|
|
|
const lastNodeNextBrothers = lastNode.ctx.nextBrothers();
|
|
|
lastNodeNextBrothers.forEach((node, index) => {
|
|
|
@@ -450,11 +489,10 @@ export class Tree {
|
|
|
this.resortDataByID([orgParentID, newParentID, lastNode.ID]);
|
|
|
}
|
|
|
|
|
|
- // 准备降级节点块(连续的兄弟节点)
|
|
|
+ // 准备降级节点块(连续的兄弟节点),不维护seq连号
|
|
|
prepareDownLevel(nodes: TreeNode[]): UpdateData[] {
|
|
|
const updateData: UpdateData[] = [];
|
|
|
const firstNode = nodes[0];
|
|
|
- const lastNode = nodes[nodes.length - 1];
|
|
|
const prevNode = this.findPrev(firstNode.ID);
|
|
|
if (!prevNode) {
|
|
|
return [];
|
|
|
@@ -470,14 +508,6 @@ export class Tree {
|
|
|
update: { parentID: prevNode.ID, seq: baseSeq + index },
|
|
|
});
|
|
|
});
|
|
|
- // 最末节点的后兄弟节点们,重新设置seq
|
|
|
- const lastNodeNextBrothers = lastNode.ctx.nextBrothers();
|
|
|
- lastNodeNextBrothers.forEach((node, index) => {
|
|
|
- updateData.push({
|
|
|
- ID: node.ID,
|
|
|
- update: { seq: prevNode.seq + 1 + index },
|
|
|
- });
|
|
|
- });
|
|
|
return updateData;
|
|
|
}
|
|
|
|