angular11 - tree-root

慕健
2023-12-01
<div class="tree-content">
        <ng-container *ngIf="nodes && nodes.length">
            <tree-root #tree (initialized)="onInitialized(tree)" (activate)="onActivate($event)" [nodes]="nodes"
                [options]="options" [focused]="false">
                <ng-template #treeNodeTemplate let-node let-index>
                    <div class="has-border tree-item title-row"
                        [ngClass]="{'click-item-show':node.isChooseStyle == 'show'}">
                        <div class="flex-1 title-content">
                            <div *ngIf="!node.data.isEditing" class="title-field" [matTooltipClass]="'toolTip'"
                                [matTooltip]="node.data.name">
                                <ng-container *ngIf="node.data.subDirectories && node.data.subDirectories.length">
                                    <img *ngIf="node.isExpanded" src="../../../../../assets/images/kb/dakaiwenjian.png"
                                        class="icon-img" />
                                    <img *ngIf="!node.isExpanded" src="../../../../../assets/images/kb/wenjianji.png"
                                        class="icon-img" />
                                    <span class="p-l-3">{{node.data.name}}</span>
                                </ng-container>
                                <ng-container *ngIf="!node.data.subDirectories || !node.data.subDirectories.length">
                                    <span class="p-l-8">{{node.data.name}}</span>
                                </ng-container>
                            </div>
                            <input *ngIf="node.data.isEditing" nz-input [(ngModel)]="node.data.tempalteName"
                                placeholder="请输入名称后回车确定..." (keyup)="saveCurrentNode(node, $event)"
                                style="width: 150px;" (blur)="inputBlur(node, $event)">
                        </div>
                        <div class="d-flex align-items-center icon-content" *ngIf="!node.data.isEditing" [ngClass]="{
                        'icon-content-shown': node.data.isEditing == true}">

                            <i class="show-opt iconfont icon-edit" [matTooltipClass]="'toolTip'" matTooltip="编辑"
                                (click)="editNode($event, node)">
                            </i>
                            <i class="show-opt iconfont icon-delete1" [matTooltipClass]="'toolTip'" matTooltip="删除"
                                nzType="delete" (click)="handleWithAction('delete', node, index, $event)">
                            </i>

                            <a nz-dropdown [nzDropdownMenu]="menu">
                                <i class="show-opt iconfont icon-plus" *ngIf="node.level < 5"
                                    [matTooltipClass]="'toolTip'" matTooltip="添加子目录" nzType="plus">
                                </i>
                            </a>
                            <nz-dropdown-menu #menu="nzDropdownMenu">
                                <ul nz-menu nzSelectable>
                                    <li nz-menu-item (click)="handleWithAction('addLevel', node, index, $event)">同级目录
                                    </li>
                                    <li nz-menu-item (click)="handleWithAction('addChild', node, index, $event)">子集目录
                                    </li>
                                </ul>
                            </nz-dropdown-menu>
                        </div>
                    </div>
                </ng-template>
            </tree-root>
        </ng-container>
        <div *ngIf="!nodes || !nodes.length" class="position-relative h-100">
            <app-no-data></app-no-data>
        </div>
    </div>
import { Component, OnInit, Input, Output, EventEmitter, ElementRef, ViewChild } from '@angular/core';
import { ITreeOptions, KEYS, TreeComponent, TreeModel, TreeNode } from '@circlon/angular-tree-component';
import { SnackBarService } from '@core/services/snackbar.service';
import { PersonalKnowledgeService } from '@routes/personal-knowledge/personal-knowledge.service';
import { appUtil } from '@utils';

@Component({
    selector: 'app-directory-select',
    templateUrl: './directory-select.component.html',
    styleUrls: ['./directory-select.component.less'],
})
export class DirectorySelectComponent implements OnInit {
    @Input() nodes: any = []; // 数据源级联数组
    @Input() selfKbId: any; // 可变化的参数
    @Output() clickNode = new EventEmitter(); // 点击节点
    @Output() refreshTree = new EventEmitter(); // 点击节点
    @ViewChild(TreeComponent, { static: false })
    public tree: TreeComponent;

    defaultOptions: ITreeOptions = {
        displayField: 'name',
        isExpandedField: 'expanded',
        idField: 'id',
        childrenField: 'subDirectories',
        actionMapping: {
            mouse: {
                click: (tree, node, $event) => {
                    this.clickCurrentNode(tree, node, $event);
                },
            },
            keys: {
                [KEYS.ENTER]: (tree, node, $event) => {
                    if (node) {
                        node.expandAll();
                    }
                },
            },
        },
        nodeHeight: 30,
        allowDrop: (node) => {
            return false;
        },
        allowDrag: (node) => {
            return false;
        },
        animateExpand: true,
        scrollOnActivate: true,
        animateSpeed: 30,
        animateAcceleration: 1.2,
    };
    options: ITreeOptions;
    // 当前是否有文件正在编辑
    isNodeEditing: any = false;
    preNode; // 上一个选中的节点
    currentSelectNode = {
        isChooseStyle: 'no',
    };

    constructor(private snackBar: SnackBarService, private service: PersonalKnowledgeService) {}

    ngOnInit() {
        this.options = Object.assign({}, this.defaultOptions);
    }

    /**
     * @desc 操作
     * @param action - target
     * @param node - tree node
     * @param index - target
     * @param event - 点击事件
     */
    handleWithAction(action, node, index, event?) {
        if (event) {
            event.preventDefault();
            event.stopPropagation();
        }
        switch (action) {
            case 'addChild': {
                if (this.isNodeEditing) {
                    this.snackBar.warning('请保存数据!');
                    return;
                }
                this.isNodeEditing = true;
                this.addChildDictionary(node);
                break;
            }
            case 'addLevel': {
                if (this.isNodeEditing) {
                    this.snackBar.warning('请保存数据!');
                    return;
                }
                this.isNodeEditing = true;
                this.addLevel(node);
                break;
            }

            case 'delete': {
                this.deleteNode(node);
                break;
            }
        }
    }
    addLevel(node) {
        const { data } = node;
        let { id, path } = data;
        const newNode = {
            name: '',
            tempalteName: '',
            leaf: 0,
            enterpriseId: data.enterpriseId,
            kbId: data.kbId,
            parentId: node.parentId,
            path,
            hasChildren: false,
            isEditing: true,
            isExpanded: true,
            id: Math.random().toString(32).slice(2, 12),
            isNew: true,
            subDirectories: [],
        };

        let saveChild = node.parent.data.subDirectories;
        saveChild.push(newNode);

        node.parent.data.subDirectories = saveChild;

        this.tree.treeModel.update();
    }
    /**
     * 新增子目录
     */
    addChildDictionary(node) {
        const { data } = node;
        let { id, path } = data;
        data.hasChildren = true;
        const newNode = {
            name: '',
            tempalteName: '',
            leaf: 0,
            enterpriseId: data.enterpriseId,
            kbId: data.kbId,
            parentId: id,
            path,
            hasChildren: false,
            isEditing: true,
            isExpanded: true,
            id: Math.random().toString(32).slice(2, 12),
            isNew: true,
            subDirectories: [],
        };

        // 将 children 赋值给当前节点
        let saveChild = data.subDirectories;
        if (saveChild && saveChild.length) {
            saveChild.push(newNode);
        } else {
            saveChild = [newNode];
        }
        node.data.subDirectories = saveChild;

        if (!node.isExpanded) {
            node.toggleExpanded();
        }

        this.tree.treeModel.update();
    }

    /**
     * 创建树模型后触发
     */
    onInitialized(tree: any) {
        // const firstRoot = tree.treeModel.getFirstRoot();
        // if (firstRoot) firstRoot.expandAll();
    }

    /**
     * 点击树节点
     * @param tree
     * @param node
     * @param event
     */
    clickCurrentNode(tree, node, event) {
        if (node && node.data && node.data.isEditing) {
            return;
        }
        if (this.currentSelectNode && this.currentSelectNode.isChooseStyle) {
            this.currentSelectNode.isChooseStyle = 'no';
        }
        node.isChooseStyle = 'show';
        this.currentSelectNode = node;

        this.clickNode.emit(node);
    }
    shownAllKb() {
        let data = {
            showAll: true,
        };
        this.clickNode.emit(data);
    }

    /**
     * 当前节点
     */
    onActivate(event) {
        // 如果之前有编辑的节点,先保存
        if (this.preNode) {
            Object.assign(this.preNode.data, { isEditing: false, tempalteName: this.preNode.data.name });
        }
        const { node } = event;
        if (node && node.data) {
            if (!node.data.isEditing) {
                // this.knowledgeService.selectDirectory = { id: node.data.id, name: node.data.name }
                // this.router.navigate(['directory', node.data.id], { relativeTo: this.activatedRoute })
            }
        }
    }
    /**
     * 编辑当前节点
     * @param node
     */
    editNode(event, node) {
        event.stopPropagation();
        if (this.preNode) {
            Object.assign(this.preNode.data, { isEditing: false, tempalteName: this.preNode.data.name });
        }
        this.preNode = node;
        Object.assign(node.data, { isEditing: true, tempalteName: node.data.name });
    }
    inputBlur(node, event) {
        this.saveCurrentNode(node, event, 'blur');
    }
    /**
     * 保存当前修改部分
     */
    saveCurrentNode(node, event, action) {
        const keycode = event.keyCode ? event.keyCode : event.which;
        if (keycode === 13 || action === 'blur') {
            if (node && node.data) {
                if (!node.data.tempalteName) {
                    this.snackBar.warning('请输入名称!');
                    return;
                }
                const nullPattern = /^[ ]*$/;
                if (nullPattern.test(node.data.tempalteName)) {
                    this.snackBar.warning('请正确输入名称!');
                    return;
                }
                if (node.data.tempalteName.length > 30) {
                    this.snackBar.warning('名称长度超过限制!');
                    return;
                }
                this.isNodeEditing = false;

                if (node.data.isNew) {
                    // 当前节点为新增节点
                    const { kbId, tempalteName, parentId, leaf } = node.data;
                    this.service.addDirectories(appUtil.filterParams({ kbId, name: tempalteName, parentId, leaf })).subscribe(
                        (res) => {
                            if (res && res.status === 200) {
                                node.data.isNew = false;
                                node.data.id = res.result.result;
                                node.data.name = tempalteName;
                                node.data.isEditing = false;
                                node.data.hasChildren = false;
                                node.treeModel.update();
                            }
                        },
                        (error) => {},
                    );
                } else {
                    // 当前节点为已有节点
                    const { tempalteName, id, parentId, kbId, leaf } = node.data;
                    const params = Object.assign({ name: tempalteName, id, parentId, kbId, leaf });
                    this.service.updateDirectories(id, params).subscribe((res) => {
                        if (res && res.status) {
                            node.data.name = tempalteName;
                            node.data.isEditing = false;
                            node.treeModel.update();
                        }
                    });
                }
            }
        }
    }
    /**
     * 删除当前节点
     * @param node
     */
    deleteNode(node: TreeNode): void {
        if (node && node.data) {
            const { id, isEditing, isNew } = node.data;
            if (isNew) {
                this.isNodeEditing = false;
                // 当前节点为新增节点
                if (node.parent && node.parent.data) {
                    if (node.parent.data.children.length) {
                        node.parent.data.children.pop();
                        this.tree.treeModel.update();
                    }
                }
                return;
            }

            if (!isNew && isEditing) {
                this.snackBar.warning('当前节点正在编辑,无法删除!');
                return;
            }
            if (this.nodes.length === 1) {
                this.snackBar.warning('当前只剩下最后一个目录,删除后会导致无法使用,删除失败');
                return;
            }

            this.service.deleteDirectories(id).subscribe(
                (res) => {
                    if (res && res.status === 200) {
                        if (node.parent != null) {
                            node.parent.data.subDirectories.splice(node.parent.data.subDirectories.indexOf(node.data), 1);
                            this.tree.treeModel.update();
                        }
                    }
                },
                (error) => {},
            );
        }
    }
}

 类似资料: