瀏覽代碼

Merge branch 'master' of http://192.168.1.41:3000/SmartCost/SCCommon

zhangweicheng 5 年之前
父節點
當前提交
50b0a3d30d
共有 3 個文件被更改,包括 78 次插入75 次删除
  1. 1 1
      tree/package.json
  2. 19 19
      tree/src/nodeCtx.ts
  3. 58 55
      tree/src/tree.ts

+ 1 - 1
tree/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@sc/tree",
-  "version": "1.0.12",
+  "version": "1.0.13",
   "description": "a template for npm package coding",
   "main": "./dist/index.cjs.js",
   "module": "./dist/index.esm.js",

+ 19 - 19
tree/src/nodeCtx.ts

@@ -1,15 +1,15 @@
 import { TreeNode, Tree } from './tree';
 
-export class NodeContext {
+export class NodeContext<T = any> {
   // 对树节点数据的引用
-  ref: TreeNode;
+  ref: TreeNode & T;
 
   tree: Tree;
 
   // 展开收起
   expanded = false;
 
-  constructor(node: TreeNode, tree: Tree) {
+  constructor(node: TreeNode & T, tree: Tree) {
     this.ref = node;
     this.tree = tree;
   }
@@ -67,36 +67,36 @@ export class NodeContext {
     return true;
   }
 
-  parent(): TreeNode | null {
+  parent(): (TreeNode & T) | null {
     return this.tree.findParent(this.ID());
   }
 
-  next(): TreeNode | null {
+  next(): (TreeNode & T) | null {
     return this.tree.findNext(this.ID());
   }
 
-  prev(): TreeNode | null {
+  prev(): (TreeNode & T) | null {
     return this.tree.findPrev(this.ID());
   }
 
   // 获取节点子项
-  children(): TreeNode[] {
+  children(): (TreeNode & T)[] {
     return this.tree.parentMap[this.ID()] || [];
   }
 
-  firstChild(): TreeNode | null {
+  firstChild(): (TreeNode & T) | null {
     return this.children()[0] || null;
   }
 
-  lastChild(): TreeNode | null {
+  lastChild(): (TreeNode & T) | null {
     const children = this.children();
     return children[children.length - 1] || null;
   }
 
   // 获取节点后代(包含嵌套子项)
-  posterity(): TreeNode[] {
-    const posterity: TreeNode[] = [];
-    const getChild = (nodes: TreeNode[]): void => {
+  posterity(): (TreeNode & T)[] {
+    const posterity: (TreeNode & T)[] = [];
+    const getChild = (nodes: (TreeNode & T)[]): void => {
       nodes.forEach(node => {
         posterity.push(node);
         const children = node.getCtx().children();
@@ -114,20 +114,20 @@ export class NodeContext {
   }
 
   // 获取节点的起源节点(根节点,若该节点已为根节点,则返回自身)
-  progenitor(): TreeNode {
+  progenitor(): TreeNode & T {
     let parent = this.parent();
     if (!parent) {
       return this.ref;
     }
     while (parent && parent.getCtx().parent()) {
-      parent = parent.getCtx().parent() as TreeNode;
+      parent = parent.getCtx().parent() as TreeNode & T;
     }
     return parent;
   }
 
   // 获取节点所有先代(包含嵌套父项)
-  ancestor(): TreeNode[] {
-    const ancestor: TreeNode[] = [];
+  ancestor(): (TreeNode & T)[] {
+    const ancestor: (TreeNode & T)[] = [];
     let parent = this.parent();
     while (parent) {
       ancestor.push(parent);
@@ -141,7 +141,7 @@ export class NodeContext {
   }
 
   // 获取同层节点
-  brothers(includeSelf = true): TreeNode[] {
+  brothers(includeSelf = true): (TreeNode & T)[] {
     let nodes = this.tree.parentMap[this.parentID()] || [];
     if (!includeSelf) {
       nodes = nodes.filter(node => node !== this.ref);
@@ -154,13 +154,13 @@ export class NodeContext {
   }
 
   // 获取后兄弟节点们
-  nextBrothers(): TreeNode[] {
+  nextBrothers(): (TreeNode & T)[] {
     const nodes = this.tree.parentMap[this.parentID()] || [];
     return nodes.filter(node => node.seq >= this.seq() && node !== this.ref);
   }
 
   // 获取前兄弟节点们
-  prevBrothers(): TreeNode[] {
+  prevBrothers(): (TreeNode & T)[] {
     const nodes = this.tree.parentMap[this.parentID()] || [];
     return nodes.filter(node => node.seq <= this.seq() && node !== this.ref);
   }

+ 58 - 55
tree/src/tree.ts

@@ -11,20 +11,20 @@ export interface TreeNode extends TreeRaw {
   getCtx: () => NodeContext; // ctx对象改为getCtx(),因为handsontable数据循环引用会报错
 }
 
-export interface TreeIDMap {
-  [propName: string]: TreeNode;
+export interface TreeIDMap<T = any> {
+  [propName: string]: TreeNode & T;
 }
 
-export interface CtxIDMap {
-  [propName: string]: NodeContext;
+export interface CtxIDMap<T = any> {
+  [propName: string]: NodeContext<T>;
 }
 
-export interface TreeParentMap {
-  [propName: string]: TreeNode[];
+export interface TreeParentMap<T = any> {
+  [propName: string]: (TreeNode & T)[];
 }
 
-export interface ParentMap {
-  [propName: string]: (TreeRaw | TreeNode)[];
+export interface ParentMap<T = any> {
+  [propName: string]: ((TreeRaw | TreeNode) & T)[];
 }
 
 export interface UpdateItem {
@@ -40,12 +40,12 @@ export interface UpdateData {
 
 export type None = null | undefined;
 
-export class Tree {
+export class Tree<T = any> {
   // 原始数据,不经过排序的数据
-  rawData: TreeNode[];
+  rawData: (TreeNode & T)[];
 
   // 按照树结构拼装好、排好序的数据。实际只是原始数据进行排序,内部元素跟原始数据内部元素的引用是一致的
-  data: TreeNode[];
+  data: (TreeNode & T)[];
 
   rootID: string;
 
@@ -53,13 +53,13 @@ export class Tree {
   readonly seqStartIndex = 0;
 
   // ID与原始数据条目映射
-  IDMap: TreeIDMap;
+  IDMap: TreeIDMap<T>;
 
   // parentID与多条同parentID的原始数据条目映射,parentMap内部数组要始终保持正确排序
-  parentMap: TreeParentMap;
+  parentMap: TreeParentMap<T>;
 
   // 节点上下文与节点ID映射
-  ctxMap: CtxIDMap;
+  ctxMap: CtxIDMap<T>;
 
   constructor(rawData: TreeRaw[], rootID = '-1') {
     this.rootID = rootID;
@@ -74,7 +74,7 @@ export class Tree {
   }
 
   // 根据seq进行排序
-  static sort<T extends TreeNode | TreeRaw>(nodes: T[]): T[] {
+  static sort<U extends TreeNode | TreeRaw>(nodes: U[]): U[] {
     return nodes.sort((a, b) => a.seq - b.seq);
   }
 
@@ -86,15 +86,15 @@ export class Tree {
   }
 
   // 生成节点getCtx方法(由于handsontable循环引用bug,将ctx对象改为getCtx方法),用于获取节点上下文,挂载相关节点方法,TreeRaw转换为TreeNode
-  private genNodeContext(rawData: TreeRaw[]): TreeNode[] {
+  private genNodeContext(rawData: TreeRaw[]): (TreeNode & T)[] {
     rawData.forEach(item => {
       item.getCtx = () => this.ctxMap[item.ID];
     });
-    return rawData as TreeNode[];
+    return rawData as (TreeNode & T)[];
   }
 
   // 生成映射表
-  private genMap(data: TreeNode[]): void {
+  private genMap(data: (TreeNode & T)[]): void {
     data.forEach(item => {
       this.IDMap[item.ID] = item;
       this.ctxMap[item.ID] = new NodeContext(item, this);
@@ -105,7 +105,7 @@ export class Tree {
   }
 
   // 获取顶层原始数据
-  getRoots(): TreeNode[] {
+  getRoots(): (TreeNode & T)[] {
     return this.parentMap[this.rootID] || [];
   }
 
@@ -113,7 +113,7 @@ export class Tree {
   private genData(): void {
     // genData时不需要排序,因为rawData已经排好序
     const roots = this.getRoots();
-    const pushNodesToData = (nodes: TreeNode[]): void => {
+    const pushNodesToData = (nodes: (TreeNode & T)[]): void => {
       nodes.forEach(node => {
         this.data.push(node);
         const children = this.parentMap[node.ID];
@@ -132,7 +132,7 @@ export class Tree {
   }
 
   // 将相关parentMap内的数据、data进行重新排序。新增、删除等操作进行完后,数据需要重新排序
-  reSortData(nodes: TreeNode[]): void {
+  reSortData(nodes: (TreeNode & T)[]): void {
     const toSortList = Tree.getParentIDList(nodes);
     toSortList.forEach(parentID => {
       if (this.parentMap[parentID] && this.parentMap[parentID].length) {
@@ -153,12 +153,12 @@ export class Tree {
   }
 
   // 查找ID节点
-  find(ID: string): TreeNode | null {
+  find(ID: string): (TreeNode & T) | null {
     return this.IDMap[ID] || null;
   }
 
   // 查找ID节点的父节点
-  findParent(ID: string): TreeNode | null {
+  findParent(ID: string): (TreeNode & T) | null {
     const node = this.find(ID);
     if (!node) {
       return null;
@@ -167,7 +167,7 @@ export class Tree {
   }
 
   // 查找ID节点的下一个节点
-  findNext(ID: string): TreeNode | null {
+  findNext(ID: string): (TreeNode & T) | null {
     const node = this.find(ID);
     if (!node) {
       return null;
@@ -181,7 +181,7 @@ export class Tree {
   }
 
   // 查找ID节点的上一个节点
-  findPrev(ID: string): TreeNode | null {
+  findPrev(ID: string): (TreeNode & T) | null {
     const node = this.find(ID);
     if (!node) {
       return null;
@@ -195,7 +195,7 @@ export class Tree {
   }
 
   // 查询ID节点的子节点
-  findChildren(ID: string): TreeNode[] {
+  findChildren(ID: string): (TreeNode & T)[] {
     return this.parentMap[ID] || [];
   }
 
@@ -211,9 +211,12 @@ export class Tree {
   }
 
   // 递归获取节点
-  getNodesPosterity(treeNodes: TreeNode[], includeSelf = true): TreeNode[] {
-    const rst: TreeNode[] = [];
-    const pushNodes = (nodes: TreeNode[]): void => {
+  getNodesPosterity(
+    treeNodes: (TreeNode & T)[],
+    includeSelf = true
+  ): (TreeNode & T)[] {
+    const rst: (TreeNode & T)[] = [];
+    const pushNodes = (nodes: (TreeNode & T)[]): void => {
       nodes.forEach(node => {
         if (includeSelf || !treeNodes.includes(node)) {
           rst.push(node);
@@ -230,7 +233,7 @@ export class Tree {
 
   // 节选中点块是否是可操作的节点块,选中一批节点进行升降、上下移的时候可能需要用到这个判断。
   // 以第一个节点为基准,如果后续的节点深度均大于于于等于第一个节点的深度,且节点间是连续的,则此为可操作节点块
-  isOperable(nodes: TreeNode[]): boolean {
+  isOperable(nodes: (TreeNode & T)[]): boolean {
     const baseDepth = nodes[0].getCtx().depth();
     for (let i = 0; i < nodes.length; i++) {
       const node = nodes[i];
@@ -250,7 +253,7 @@ export class Tree {
   }
 
   // 从节点块中获取相同深度的节点
-  sameDepthNodes(nodes: TreeNode[], depth?: number): TreeNode[] {
+  sameDepthNodes(nodes: (TreeNode & T)[], depth?: number): (TreeNode & T)[] {
     if (!depth) {
       depth = nodes[0].getCtx().depth();
     }
@@ -300,7 +303,7 @@ export class Tree {
   }
 
   // 插入节点数据
-  insert(items: TreeRaw[], updateData: UpdateData[] = []): TreeNode[] {
+  insert(items: TreeRaw[], updateData: UpdateData[] = []): (TreeNode & T)[] {
     // 更新需要更新的节点(一般为更新seq)
     this.updateValue(updateData);
     // 建立映射、插入数据
@@ -313,19 +316,19 @@ export class Tree {
   }
 
   // 准备删除,返回所有需要删除的节点,包括嵌套节点
-  prepareDelete(deleteNodes: TreeNode[]): TreeNode[] {
+  prepareDelete(deleteNodes: (TreeNode & T)[]): (TreeNode & T)[] {
     return this.getNodesPosterity(deleteNodes);
   }
 
   /**
    * 删除节点
-   * @param {TreeNode[]} treeNodes - 要删除的节点,不需要包含嵌套节点
-   * @return {TreeNode[]} 返回被删除的节点
+   * @param {(TreeNode & T)[]} treeNodes - 要删除的节点,不需要包含嵌套节点
+   * @return {(TreeNode & T)[]} 返回被删除的节点
    */
-  delete(treeNodes: TreeNode[]): TreeNode[] {
-    const allDeletedNodes: TreeNode[] = [];
+  delete(treeNodes: (TreeNode & T)[]): (TreeNode & T)[] {
+    const allDeletedNodes: (TreeNode & T)[] = [];
     // 递归删除节点
-    const deleteNodes = (nodes: TreeNode[]): void => {
+    const deleteNodes = (nodes: (TreeNode & T)[]): void => {
       // 删除映射、删除数据
       const toDels: { nodes: TreeNode[]; delNode: TreeNode }[] = [];
       nodes.forEach(node => {
@@ -364,8 +367,8 @@ export class Tree {
   }
 
   // IDList 返回所有需要删除的节点,包括嵌套节点
-  prepareDeleteByID(IDList: string[]): TreeNode[] {
-    const deleteNodes: TreeNode[] = [];
+  prepareDeleteByID(IDList: string[]): (TreeNode & T)[] {
+    const deleteNodes: (TreeNode & T)[] = [];
     IDList.forEach(ID => {
       const node = this.find(ID);
       if (node) {
@@ -380,8 +383,8 @@ export class Tree {
    * @param {string[]} IDList - 要删除的节点的ID列表(不包含嵌套节点ID)
    * @return {TreeNode[]} - 返回被删除的所有节点
    */
-  deleteByID(IDList: string[]): TreeNode[] {
-    const deleteNodes: TreeNode[] = [];
+  deleteByID(IDList: string[]): (TreeNode & T)[] {
+    const deleteNodes: (TreeNode & T)[] = [];
     IDList.forEach(ID => {
       const node = this.find(ID);
       if (node) {
@@ -393,7 +396,7 @@ export class Tree {
   }
 
   // 准备上移节点块(连续的兄弟节点),注意节点的seq可能不连号
-  prepareUpMove(nodes: TreeNode[]): UpdateData[] {
+  prepareUpMove(nodes: (TreeNode & T)[]): UpdateData[] {
     const updateData: UpdateData[] = [];
     const firstNode = nodes[0];
     const firstNodePrev = this.findPrev(firstNode.ID);
@@ -417,7 +420,7 @@ export class Tree {
   }
 
   // 准备下移节点块(连续的兄弟节点),注意节点的seq可能不连号
-  prepareDownMove(nodes: TreeNode[]): UpdateData[] {
+  prepareDownMove(nodes: (TreeNode & T)[]): UpdateData[] {
     const updateData: UpdateData[] = [];
     const lastNode = nodes[nodes.length - 1];
     const lastNodeNext = this.findNext(lastNode.ID);
@@ -442,13 +445,13 @@ export class Tree {
   }
 
   // 上下移
-  move(nodes: TreeNode[], updateData: UpdateData[]): void {
+  move(nodes: (TreeNode & T)[], updateData: UpdateData[]): void {
     this.updateValue(updateData);
     this.reSortData(nodes);
   }
 
   // 准备升级节点块(连续的兄弟节点),不维护seq连号
-  prepareUpLevel(nodes: TreeNode[]): UpdateData[] {
+  prepareUpLevel(nodes: (TreeNode & T)[]): UpdateData[] {
     const updateData: UpdateData[] = [];
     const firstNode = nodes[0];
     const lastNode = nodes[nodes.length - 1];
@@ -488,7 +491,7 @@ export class Tree {
     return updateData;
   }
 
-  upLevel(nodes: TreeNode[], updateData: UpdateData[]): void {
+  upLevel(nodes: (TreeNode & T)[], updateData: UpdateData[]): void {
     const firstNode = nodes[0];
     const lastNode = nodes[nodes.length - 1];
     if (!firstNode.getCtx().canUpLevel()) {
@@ -511,7 +514,7 @@ export class Tree {
   }
 
   // 准备降级节点块(连续的兄弟节点),不维护seq连号
-  prepareDownLevel(nodes: TreeNode[]): UpdateData[] {
+  prepareDownLevel(nodes: (TreeNode & T)[]): UpdateData[] {
     const updateData: UpdateData[] = [];
     const firstNode = nodes[0];
     const prevNode = this.findPrev(firstNode.ID);
@@ -532,7 +535,7 @@ export class Tree {
     return updateData;
   }
 
-  downLevel(nodes: TreeNode[], updateData: UpdateData[]): void {
+  downLevel(nodes: (TreeNode & T)[], updateData: UpdateData[]): void {
     const firstNode = nodes[0];
     if (!firstNode.getCtx().canDownLevel()) {
       return;
@@ -552,12 +555,12 @@ export class Tree {
 
   // 准备移动节点块(连续的兄弟节点),不维护seq连号
   prepareMoveTo(
-    nodes: TreeNode[],
-    parent: TreeNode | None,
-    next: TreeNode | None
+    nodes: (TreeNode & T)[],
+    parent: (TreeNode & T) | None,
+    next: (TreeNode & T) | None
   ): UpdateData[] {
     const updateData: UpdateData[] = [];
-    let prev: TreeNode | null;
+    let prev: (TreeNode & T) | null;
     if (next) {
       prev = next.getCtx().prev();
     } else {
@@ -595,8 +598,8 @@ export class Tree {
   }
 
   moveTo(
-    nodes: TreeNode[],
-    parent: TreeNode | None,
+    nodes: (TreeNode & T)[],
+    parent: (TreeNode & T) | None,
     updateData: UpdateData[]
   ): void {
     const firstNode = nodes[0];