ng-zorro-antd,antd-angular树形数据动态显示操作整理及源码,各自分析拿去用吧,就是没时间整理,整体代码比较乱各位看官随意,勿喷,转载请说明出处。

import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { GlobalService } from './../../../../service/global.service';
import { Component, OnInit, ChangeDetectorRef } from '@angular/core';
// export interface TreeNodeInterface {
//   key: number;
//   name: string;
//   age?: number;
//   level?: number;
//   expand?: boolean;
//   address?: string;
//   children?: TreeNodeInterface[];
//   parent?: TreeNodeInterface;
// }
//create by huang.l 2020.2.17 17.28
@Component({
  selector: 'app-menu',
  templateUrl: './menu.component.html',
  styleUrls: ['./menu.component.less']
})

export class MenuComponent implements OnInit {

  public pageSize: number = 15;
  public page: number = 1;
  public listOfMapData: any[];
  public mapOfExpandedData: { [key: string]: any[] } = {};
  public isVisible: boolean = false;

  public codePrefix: string = '';
  public rootId: string;
  public parentMenu: any;
  public currentArray: any[];

  validateForm: FormGroup;
  constructor(
    private globalService: GlobalService,
    private fb: FormBuilder,
  ) {

  }

  async collapse(array: any[], data: any, $event: boolean, rootId: string) {
    if ($event === false) {
      if (data.children) {
        data.children.forEach(d => {
          if (array) {
            const target = array.find(a => a.id === d.id)!;
            if (target) {//如果当前节点还存在
              target.expand = false;
              console.log(target.id);
              this.collapse(array, target, false, rootId);
            }
          }
        });
      } else {
        return;
      }
    } else {
      if (data.children) {
        return;
      }
      const submenus: any[] = await this.getChildren(data.id);
      const submenuArray: any[] = [];
      submenus.forEach(item => {
        submenuArray.push({ ...item, level: data.level + 1, expand: false, haveChild: (item.haveChild > 0 ? true : false), parent: data });
      });


      let target: any = null;
      const index = array.findIndex(item => item.id === data.id);

      target = array[index];

      if (target != null) {
        target.children = submenuArray;
      }
      //顺序调整
      // array = [...array, ...submenuArray];
      array[index] = target;
      array.splice(index + 1, 0, ...submenuArray);
      // this.mapOfExpandedData[rootId] = array;
    }

  }

  async ngOnInit() {

    this.validateForm = this.fb.group({
      id: [null],
      parentId: [null],
      parentName: [null],
      name: [null, [Validators.required, Validators.maxLength(20)]],
      code: [null, [Validators.required, Validators.maxLength(20), Validators.pattern('^[A-Za-z0-9]+$')]],
      router: [null],
      orderNum: [10, [Validators.required]],
      icon: [null],
      leaf: ['0'],
      visible: ['1'],
      openNewPage: ['0']
    });

    const menus: any[] = await this.getChildren(null);
    this.listOfMapData = menus;
    this.reload();
  }

  reload(): void {
    this.listOfMapData.forEach(item => {
      this.mapOfExpandedData[item.id] = this.convertTreeToList(item);
    });
  }


  add(): void {
    this.isVisible = true;
    this.setParentWithRoot();
  }

  convertTreeToList(root: any): any[] {
    const stack: any[] = [];
    const array: any[] = [];
    const hashMap = {};

    stack.push({ ...root, level: 0, expand: false, haveChild: (root.haveChild > 0 ? true : false) });

    while (stack.length !== 0) {
      const node = stack.pop()!;
      this.visitNode(node, hashMap, array);
      if (node.children) {
        for (let i = node.children.length - 1; i >= 0; i--) {
          stack.push({ ...node.children[i], level: node.level! + 1, expand: false, parent: node });
        }
      }
    }
    return array;
  }

  visitNode(node: any, hashMap: { [id: string]: boolean }, array: any[]): void {
    if (!hashMap[node.id]) {
      hashMap[node.id] = true;
      array.push(node);
    }
  }

  async getChildren(parentId: string) {
    let parentIdCondition: string;
    if (parentId === '' || parentId === null || parentId === undefined) {
      parentIdCondition = 'parentId=';
    } else {
      parentIdCondition = 'parentId=' + parentId;
      this.page = 1;
      this.pageSize = 9999;
    }
    const url = `api/menu/list/by/parentId?page=${this.page}&pageSize=${this.pageSize}&${parentIdCondition}`;
    try {
      const res: any = await this.globalService.axios.get(url);
      return res.data.list;
    } catch (e) {
      alert(JSON.stringify(e));
    }
  }


  async submitForm() {
    for (const i in this.validateForm.controls) {
      this.validateForm.controls[i].markAsDirty();
      this.validateForm.controls[i].updateValueAndValidity();
    }

    if (this.validateForm.valid) {

      let submitData: any = this.validateForm.getRawValue();
      submitData = { ...submitData, code: (this.codePrefix + submitData.code) };

      const res = await this.globalService.axios.post('api/menu/edit', submitData);

      if (res.data.success === true) {
        this.isVisible = false;
        this.validateForm.reset();
        //刷新表格数据
        const parentId = submitData.parentId;
        //新增根目录数据

        let editMenu: any = res.data.item;

        if (parentId === null || parentId === '' || parentId === undefined) {
          this.listOfMapData = [editMenu, ...this.listOfMapData];
          this.listOfMapData.forEach(item => {
            if (!this.mapOfExpandedData[item.id]) {
              this.mapOfExpandedData[item.id] = this.convertTreeToList(item);
            }
          });
        } else {
          //新增子目录数据
          editMenu = { ...editMenu, haveChild: false, level: (this.parentMenu.level! + 1), expand: false, parent: this.parentMenu };
          const index = this.currentArray.findIndex((item) => item.id === this.parentMenu.id);
          //设置父节点包含子节点

          //当节点未打开时,打开节点!!这句很重要
          if(!this.parentMenu.expand){
            this.parentMenu = { ...this.currentArray[index], haveChild: true, expand: true };
          }

          const chidlren: any[] = this.parentMenu['children'] ? this.parentMenu['children'] : [];
          chidlren.push(editMenu);

          this.parentMenu['children'] = chidlren;

          editMenu = { ...editMenu, parent: this.parentMenu };
          this.currentArray.splice(index + 1, 0, editMenu);
          this.currentArray[index] = this.parentMenu;
          // this.mapOfExpandedData[this.rootId] = array;
        }
        this.globalService.showSuccess();
      } else {
        this.globalService.showFail();
      }
    }
  }


  handleCancelMiddle(): any {
    this.isVisible = false;
    this.validateForm.reset();
  }

  getParent(): void {
    return;
  }

  setParentWithRoot(): void {
    this.validateForm.controls['parentId'].reset();
    this.validateForm.controls['parentName'].setValue('/');
  }

  setCode(code): void {
    this.validateForm.controls['code'].setValue(code);
  }

  addSubMenu(menu: any, array: any[], rootId: string): void {
    this.isVisible = true;
    this.validateForm.controls['parentName'].setValue(menu.name);
    this.validateForm.controls['parentId'].setValue(menu.id);
    this.codePrefix = menu.code + ':';

    this.rootId = rootId;
    this.parentMenu = menu;
    this.currentArray = array;
  }

  async deleteMenu(menu, rootId) {
    const res = await this.globalService.axios.get('api/menu/delete/' + menu.id);
    if (res.data.success === true) {
      //刷新表格
      if (menu.id === rootId) {
        delete this.mapOfExpandedData[rootId];
        //
        const rootIndex = this.listOfMapData.findIndex(item => item.id === rootId);
        this.listOfMapData.splice(rootIndex, 1);//清除列表中的数据

      } else {
        const array = this.mapOfExpandedData[rootId];
        const index = array.findIndex((item) => item.id === menu.id);
        array.splice(index, 1);

        const parentIndex: number = array.findIndex(item => item.id === menu.parentId);
        let thizParent: any = array[parentIndex];
        const parentChildrenIndex = thizParent.children.findIndex(item => item.id === menu.id);
        thizParent.children.splice(parentChildrenIndex, 1);
        if (thizParent.children.length <= 0) {//设置父节点的可删除条件
          thizParent = { ...thizParent, haveChild: false };
        }
        array[parentIndex] = thizParent;
        this.mapOfExpandedData[rootId] = array;

        //当所有子节点删完之后,更改唯一父节点的是否有子节点状态,让其变得可以删除
        // if (array.length === 1) {
        //   this.mapOfExpandedData[rootId] = [{ ...array[0], haveChild: false }];
        // }
      }
      this.globalService.showSuccess();
    } else {
      this.globalService.showFail();
    }
  }

  editMenu(menu): void {
    const menuCode: string = menu.code;
    this.codePrefix = menuCode.substr(0, menuCode.lastIndexOf(':'));
    this.isVisible = true;
    if (menu['parent']) {
      this.validateForm.controls['parentName'].setValue(menu.parent.name);
      this.validateForm.controls['parentId'].setValue(menu.parent.id);
    } else {
      this.validateForm.controls['parentName'].setValue('/');
    }

    const menuArray: string[] = menuCode.split(':');

    menu = {
      ...menu, leaf: menu.leaf + '', visible: menu.visible + '',
      openNewPage: menu['openNewPage'] ? menu.openNewPage + '' : '0',
      code: menuArray[menuArray.length - 1]
    };

    this.validateForm.patchValue(menu);
  }
}

1,以上是组件代码。

 

<nz-page-header>
    <!-- <nz-page-header-title>Title</nz-page-header-title>
    <nz-page-header-subtitle>This is a subtitle</nz-page-header-subtitle> -->
    <nz-page-header-extra>
        <button nz-button (click)="add($event)">添加根目录</button>
        <button nz-button>Operation</button>
        <button nz-button nzType="primary">Primary</button>
    </nz-page-header-extra>
    <nz-page-header-content>
        <!-- <div class="content">
            <div class="main">
                <nz-descriptions [nzColumn]="2">
                    <nz-descriptions-item nzTitle="Created" [nzSpan]="1">Lili Qu</nz-descriptions-item>
                    <nz-descriptions-item nzTitle="Association" [nzSpan]="1"><a>421421</a></nz-descriptions-item>
                    <nz-descriptions-item nzTitle="Creation Time" [nzSpan]="1">2017-01-10</nz-descriptions-item>
                    <nz-descriptions-item nzTitle="Effective Time" [nzSpan]="1">2017-10-10</nz-descriptions-item>
                    <nz-descriptions-item nzTitle="Remarks" [nzSpan]="2">
                        Gonghu Road, Xihu District, Hangzhou, Zhejiang, China
                    </nz-descriptions-item>
                </nz-descriptions>
            </div>
            <div class="extra">
                <div>
                    <nz-statistic nzTitle="Status" nzValue="Pending"></nz-statistic>
                    <nz-statistic nzTitle="Price" [nzValue]="568.08" nzPrefix="$" style="margin: 0 32px">
                    </nz-statistic>
                </div>
            </div>
        </div> -->
    </nz-page-header-content>
    <nz-page-header-footer>
    </nz-page-header-footer>
</nz-page-header>

<nz-table #expandTable [nzData]="listOfMapData" [nzScroll]="{x:'150',y: '800px' }">
    <thead>
        <tr>
            <th nzWidth="30%">名称</th>
            <th nzWidth="20%">编码</th>
            <th>菜单图标</th>
            <th>是否显示</th>
            <th>排序</th>
            <th nzWidth="20%">操作</th>
        </tr>
    </thead>
    <tbody>
        <ng-container *ngFor="let data of expandTable.data">
            <ng-container *ngFor="let item of mapOfExpandedData[data.id]">
                <tr *ngIf="(item.parent && item.parent.expand) || !item.parent">
                    <td [nzIndentSize]="item.level * 20" [nzShowExpand]="!!item.haveChild" [(nzExpand)]="item.expand" (nzExpandChange)="collapse(mapOfExpandedData[data.id], item, $event,data.id)">
                        {{ item.name }}
                    </td>
                    <td>
                        {{item.id}}
                    </td>
                    <!-- <td>
                        {{item.icon}}
                    </td>
                    <td>
                        {{item.visible === 1?'是':'否'}}
                    </td>
                    <td>
                        {{item.orderNum}}
                    </td> -->

                    <td>
                        <a *ngIf="((item.leaf === 0 && item.expand) || (item.leaf === 0 && !item.haveChild))" (click)="addSubMenu(item,mapOfExpandedData[data.id],data.id)">添加子菜单</a>&nbsp;
                        <a (click)="editMenu(item)">编辑</a>&nbsp;
                        <a *ngIf="item.haveChild === false" nz-popconfirm nzPopconfirmTitle="确定要删除?" nzOkText="是" nzCancelText="否" (nzOnConfirm)="deleteMenu(item,data.id)">删除</a>
                    </td>
                </tr>
            </ng-container>
        </ng-container>
    </tbody>
</nz-table>

<nz-modal nzWidth="800" [(nzVisible)]="isVisible" nzTitle="添加菜单" (nzOnCancel)="handleCancelMiddle()" (nzOnOk)="submitForm()">
    <form nz-form [formGroup]="validateForm" nzLayout="vertical" (ngSubmit)="submitForm()">
        <nz-form-item>
            <nz-form-label nzFor="parentName" nzRequired nzSpan="4">父节点</nz-form-label>
            <nz-form-control nzSpan="15">
                <div nz-row nzGutter="24">
                    <div nz-col nzSpan="14">
                        <input nz-input formControlName="parentName" id="parentName" [disabled]="true" />
                        <input type="hidden" formControlName="parentId" id="parentId" />
                        <input type="hidden" formControlName="id" id="id" />
                    </div>
                    <div nz-col nzSpan="5">
                        <button nz-button (click)="getParent()" type="button">选择父节点</button>
                    </div>
                    <div nz-col nzSpan="5">
                        <button nz-button (click)="setParentWithRoot()" type="button">设置根目录</button>
                    </div>
                </div>
            </nz-form-control>
        </nz-form-item>
        <nz-form-item>
            <nz-form-label nzFor="name" nzRequired nzSpan="4">名称</nz-form-label>
            <nz-form-control [nzErrorTip]="errorTpl" nzSpan="15">
                <input nz-input id="name" formControlName="name" placeholder="名称" />
                <ng-template #errorTpl let-control>
                    <ng-container *ngIf="control.hasError('required')">
                        请输入代码
                    </ng-container>
                    <ng-container *ngIf="control.hasError('maxlength')">
                        长度不能超过20
                    </ng-container>
                </ng-template>
            </nz-form-control>
        </nz-form-item>
        <nz-form-item>
            <nz-form-label nzFor="code" nzRequired nzSpan="4">代码</nz-form-label>
            <nz-form-control [nzErrorTip]="errorTpl" nzSpan="15">
                <div nz-row nzGutter="24">
                    <div nz-col nzSpan="14">
                        <nz-input-group [nzAddOnBefore]="codePrefix">
                            <input nz-input formControlName="code" id="code" placeholder="代码" />
                            <ng-template #errorTpl let-control>
                                <ng-container *ngIf="control.hasError('required')">
                                    请输入代码
                                </ng-container>
                                <ng-container *ngIf="control.hasError('maxlength')">
                                    长度不能超过20
                                </ng-container>
                                <ng-container *ngIf="control.hasError('pattern')">
                                    只能输入字母和数字
                                </ng-container>
                            </ng-template>
                        </nz-input-group>
                    </div>
                    <div nz-col nzSpan="10">
                        <button nz-button nz-dropdown [nzDropdownMenu]="menu" type="button">预设<i nz-icon nzType="down"></i></button>
                        <nz-dropdown-menu #menu="nzDropdownMenu">
                            <ul nz-menu>
                                <li nz-menu-item>
                                    <a (click)="setCode('add')">新增</a>
                                </li>
                                <li nz-menu-item>
                                    <a (click)="setCode('edit')">编辑</a>
                                </li>
                                <li nz-menu-item>
                                    <a (click)="setCode('delete')">删除</a>
                                </li>
                                <li nz-menu-item>
                                    <a (click)="setCode('query')">查询</a>
                                </li>
                                <li nz-menu-item>
                                    <a (click)="setCode('detail')">查看详情</a>
                                </li>
                                <li nz-menu-item>
                                    <a (click)="setCode('')">其他(手动输入)</a>
                                </li>
                            </ul>
                        </nz-dropdown-menu>
                    </div>
                </div>
            </nz-form-control>
        </nz-form-item>
        <nz-form-item>
            <nz-form-label nzFor="orderNum" nzRequired nzSpan="4">排序码</nz-form-label>
            <nz-form-control nzErrorTip="请输入排序码" nzSpan="15">
                <nz-input-number id="orderNum" formControlName="orderNum" placeholder="排序码" [nzMin]="0" [nzMax]="9999" nzStep="10"></nz-input-number>
            </nz-form-control>
        </nz-form-item>
        <nz-form-item>
            <nz-form-label nzFor="router" nzSpan="4">路由</nz-form-label>
            <nz-form-control nzErrorTip="请输入路由地址" nzSpan="15">
                <input nz-input id="router" formControlName="router" placeholder="路由" />
            </nz-form-control>
        </nz-form-item>
        <nz-form-item>
            <nz-form-label nzFor="icon" nzSpan="4">图标名称</nz-form-label>
            <nz-form-control nzErrorTip="请输入图标名称!" nzSpan="15">
                <input nz-input id="icon" formControlName="icon" placeholder="图标名称" />
            </nz-form-control>
        </nz-form-item>
        <nz-form-item>
            <nz-form-label nzFor="leaf" nzRequired nzSpan="4">是否叶子节点</nz-form-label>
            <nz-form-control nzErrorTip="leaf" nzSpan="15">
                <nz-radio-group formControlName="leaf">
                    <label nz-radio nzValue="1">是</label>
                    <label nz-radio nzValue="0">否</label>
                </nz-radio-group>
            </nz-form-control>
        </nz-form-item>
        <nz-form-item>
            <nz-form-label nzFor="visible" nzRequired nzSpan="4">是否可见</nz-form-label>
            <nz-form-control nzErrorTip="是否可见" nzSpan="15">
                <nz-radio-group formControlName="visible">
                    <label nz-radio nzValue="1">是</label>
                    <label nz-radio nzValue="0">否</label>
                </nz-radio-group>
            </nz-form-control>
        </nz-form-item>
        <nz-form-item>
            <nz-form-label nzFor="openNewPage" nzRequired nzSpan="4">是否打开新页面</nz-form-label>
            <nz-form-control nzErrorTip="是否打开新页面" nzSpan="15">
                <nz-radio-group formControlName="openNewPage">
                    <label nz-radio nzValue="1">是</label>
                    <label nz-radio nzValue="0">否</label>
                </nz-radio-group>
            </nz-form-control>
        </nz-form-item>
    </form>

</nz-modal>

 2,以上是html代码

 

3,环境版本 

 

4,描述

整体思路在antd官方文档中map数据的格式,围绕着该格式修改数据即可。public mapOfExpandedData: { [key: string]: any[] } = {};

其他的可自由发挥。另ajax框架我用的是axios,以前搞react的时候用惯了。关于后面我业务上的模块会继续完成,框架调好后迫不及待的发个博客。

 

5,捣鼓这个完全是因为武汉肺炎期间闲的蛋疼。

 

6,效果视频__

 

https://v.youku.com/v_show/id_XNDU1MTc1NTYxMg==.html

Google Chrome 2020

 

 

 

 

 

 

 

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值