当前位置: 首页 > 工具软件 > ng-sidebar > 使用案例 >

ng-alain对sidebar-nav进行修改实现菜单布局切换

姜磊
2023-12-01

 一、背景

希望菜单项可以放在顶部

之前使用样式修改修改过第一版,后来发现问题较多,而且样式修改起来特别麻烦,因此通过对sidebar-nav的修改满足需求。

二、实现 

1、主要是通过修改sidebar-nav.component.js,实现新的nav组件

// =========LAYOUTS=========
@import './../../../styles/theme';
@import './../../../styles/index';

.ad-nav-head-div {
  float: left;
  margin-left: 20px;
  width: 100%;
  height: 60px;
}
.ad-nav-head {
  float: left;
  list-style-type: none;
  margin-top: -10px;
  border-right: 1px solid @content-heading-border;
  .ad-nav-li-head {
    float: left;
    list-style-type: none;
    border-left: 0 solid transparent;
    border-bottom: 0 solid transparent;
    transition: border-bottom-color 0.4s ease;
    .ad-nav-li-hend-ui {
      float: left;
      list-style-type: none;
      position: absolute;
      top: 48px;
      background-color: @aside-bg;
    }
  }
  .ad-nav__-head-selected {
    background-repeat: no-repeat;
    background-size: 100% 100%;
    height: 100%;
    background-position: center center;
    background-image: url('../../../assets/img/nav/nav-selected-bg.png');
  }
  .ad-nav__-head-selected > a {
    color: @blue-color2;
  }
}
.ad-nav-li-head-hidden {
  display: none !important;
}
import {
  Component,
  OnInit,
  HostListener,
  ChangeDetectorRef,
  ElementRef,
  Renderer2,
  Input,
  Output,
  EventEmitter,
  OnDestroy,
  AfterViewInit,
} from '@angular/core';
import { LayoutChangeService } from '@core/layout/layoutChange.service';
import { Router, NavigationEnd } from '@angular/router';
import { LocationStrategy } from '@angular/common';
import { MenuService, SettingsService, Menu } from '@delon/theme';
import { filter } from 'rxjs/operators';
import { Subscription } from 'rxjs/Subscription';
/** @type {?} */
const SHOWCLS = 'ad-nav__floating-show';
/** @type {?} */
const FLOATINGCLS = 'ad-nav__floating';
/*头部selected样式*/
const HEDNSELECTED = 'ad-nav__-head-selected';
/*左树选中样式*/
const LEFTSELECTED = 'ad-nav__selected';

@Component({
  selector: 'app-header-nav',
  template: `
    <div [ngClass]="{'ad-nav-head-div' : loyout}">
      <ul class="ad-nav" [ngClass]="{'ad-nav-head': loyout}">
        <ng-container *ngFor="let group of list">
          <ng-template [ngIf]="group._hidden !== true">
            <li class="ad-nav__item ad-nav__group-title" [ngClass]="{'ad-nav-li-head': loyout, 
            'ad-nav-li-head-hidden': group.text === '导航菜单' ? true : false}" 
                *ngIf="group.group"><span>{{ group.text }}</span></li>
            <ng-container *ngFor="let child1 of group.children">
              <li *ngIf="child1._hidden !== true" routerLinkActive="{{routerLinkActive}}" 
                  [ngClass]="{'ad-nav-li-head': loyout}"
                  [routerLinkActiveOptions]="{exact: child1.linkExact}"
                  class="ad-nav__item" [class.ad-nav__open]="child1._open">
                <a *ngIf="child1._type === 1"
                      (click)="onSelect(child1)"
                      [routerLink]="child1.link"
                      [target]="child1.target">
                  <i *ngIf="!settings.layout.collapsed" [hidden]="loyout" class="{{ child1.icon }}"></i>
                  <nz-tooltip *ngIf="settings.layout.collapsed" nzPlacement="right" [nzTitle]="child1.text">
                    <span nz-tooltip> <i class="{{ child1.icon }}"></i> </span>
                  </nz-tooltip>
                  <span>{{ child1.text }}</span>
              </a>
                <a *ngIf="child1._type === 2" href="{{ child1.externalLink }}" target="{{child1.target}}"
                   data-type="external">
                  <i *ngIf="!settings.layout.collapsed" [hidden]="loyout" class="{{ child1.icon }}"></i>
                  <nz-tooltip *ngIf="settings.layout.collapsed" nzPlacement="right" [nzTitle]="child1.text">
                    <span nz-tooltip> <i class="{{ child1.icon }}"></i> </span></nz-tooltip>
                  <span>{{ child1.text }}</span> 
                </a>
                <a *ngIf="child1._type === 3" class="ad-nav__sub-title"
                    (mouseleave)="toggleOpen(child1, false)"
                   (click)="toggleOpenFun(child1)" (mouseenter)="showSubMenu($event, child1, true)">                            
                    <i [hidden]="loyout && !settings.layout.collapsed" class="{{ child1.icon }}"></i> 
                  <span>{{ child1.text }}</span>
                </a>
                <div *ngIf="child1.badge" title="{{child1.badge}}" class="badge badge-{{child1.badge_status}}"
                  [class.badge-dot]="child1.badge_dot"><em>{{child1.badge}}</em></div>
                <ul *ngIf="child1._type === 3" [ngClass]="{'ad-nav-li-hend-ui': loyout}"
                    (mouseenter)="checkPosition(child1, true)" (mouseleave)="checkPosition(child1, false)"
                    class="ad-nav ad-nav__sub ad-nav__depth{{child1._depth}}">
                  <ng-container *ngFor="let child2 of child1.children">
                    <li *ngIf="child2._hidden !== true"
                      routerLinkActive="{{routerLinkActive}}" [routerLinkActiveOptions]="{exact: child2.linkExact}"
                        class="ad-nav__item" [class.ad-nav__open]="child2._open">
                    <a *ngIf="child2._type === 1"
                              (click)="onSelect(child2)"
                              [routerLink]="child2.link"
                              [target]="child2.target">{{ child2.text}}
                      </a>
                      <a *ngIf="child2._type === 2" href="{{ child2.externalLink }}"
                         target="{{ child2.target }}" data-type="external">{{ child2.text }}
                    </a>
                      <a *ngIf="child2._type === 3" class="ad-nav__sub-title" 
                         (click)="toggleOpenFun(child2)"> {{ child2.text}}
                      </a>
                      <div *ngIf="child2.badge" title="{{child2.badge}}" class="badge badge-{{child2.badge_status}}"
                      [class.badge-dot]="child2.badge_dot"><em>{{child2.badge}}</em></div>
                      <ul [ngClass]="{'ad-nav-li-hend-ui': loyout}"
                          (mouseenter)="checkPosition(child2, true)" (mouseleave)="checkPosition(child2, false)"
                          *ngIf="child2._type=== 3"class="ad-nav ad-nav__sub ad-nav__depth{{child2._depth}}">
                        <ng-container *ngFor="let child3 of child2.children">
                          <li *ngIf="child3._hidden !== true"
                            routerLinkActive="{{routerLinkActive}}" [routerLinkActiveOptions]="{exact: child3.linkExact}"
                              class="ad-nav__item" [class.ad-nav__open]="child3._open">
                          <a *ngIf="child3._type === 1"
                               (click)="onSelect(child3)"
                               [routerLink]="child3.link"
                               [target]="child3.target">{{ child3.text}}                                                   
                          </a>
                            <a *ngIf="child3._type === 2" href="{{ child3.externalLink }}"
                               target="{{ child3.target }}" data-type="external">{{ child3.text }}</a>
                            <div *ngIf="child3.badge" title="{{child3.badge}}" class="badge badge-{{child3.badge_status}}"
                            [class.badge-dot]="child3.badge_dot"><em>{{child3.badge}}</em></div>
                          </li>
                        </ng-container>
                      </ul>
                    </li>
                  </ng-container>
                </ul>
              </li>
            </ng-container>
          </ng-template>
        </ng-container>
      </ul>
    </div>
  `,
  styleUrls: ['./header.nav.Component.less'],
})

//  extends SidebarNavComponent
export class HeaderNavComponent implements OnInit, OnDestroy, AfterViewInit {
  constructor(
    public menuSrv: MenuService,
    public settings: SettingsService,
    public router: Router,
    public locationStrategy: LocationStrategy,
    public render: Renderer2,
    public cd: ChangeDetectorRef,
    public el: ElementRef,
    public layoutService: LayoutChangeService,
  ) {
    this.subscription = layoutService.Status$.subscribe(message => {
      // 获取当前的布局状态
      this.loyout = message;
      this.initCollapsed();
      this.setRouterLinkActive();
      this.initSidebar('init');
    });
  }
  subscription: Subscription;
  public loyout = this.layoutService.getLayout;
  bodyEl: any;
  list = [];
  doc = document;
  @Input() autoCloseUnderPad = true;
  @Output('select') select = new EventEmitter();
  rootEl = this.el.nativeElement;
  private change$: any;
  floatingEl: HTMLDivElement;
  private routerLinkActive: any = this.initRouterLinkActive();
  private route$;
  private sidebar$;
  // 确定光标是否在下拉菜单上面
  private isOnUl = false;
  // 监听切换ipad按钮事件
  @HostListener('document:click', ['$event.target'])
  onClick() {
    this.hideAll();
  }
  // propDecorators = {
  //   autoCloseUnderPad: [{ type: Input }],
  //   select: [{ type: Output }],
  //   onClick: [{type: HostListener, args: ['document:click', ['$event.target']]}]
  // };
  /**
   * 对选中样式进行初始化
   */
  public initRouterLinkActive() {
    this.routerLinkActive = this.loyout ? HEDNSELECTED : LEFTSELECTED;
  }

  /**
   * 切换时交替设置选中的样式
   */
  private setRouterLinkActive() {
    this.initRouterLinkActive();
    let oldSelectDom;
    let delClassName;
    let addClassName;
    if (this.loyout) {
      addClassName = HEDNSELECTED;
      delClassName = LEFTSELECTED;
      oldSelectDom = this.rootEl.querySelector('.' + LEFTSELECTED);
    } else {
      addClassName = LEFTSELECTED;
      delClassName = HEDNSELECTED;
      oldSelectDom = this.rootEl.querySelector('.' + HEDNSELECTED);
    }
    this.render.addClass(oldSelectDom, addClassName);
    this.render.removeClass(oldSelectDom, delClassName);
  }
  ngOnInit() {
    const me = this;
    this.bodyEl = this.doc.querySelector('body');
    this.menuSrv.openedByUrl(this.router.url);
    this.genFloatingContainer();
    this.change$ = /** @type {?} */ (this.menuSrv.change.subscribe(function(
      res,
    ) {
      me.list = res;
      me.cd.detectChanges();
    }));
    this.installUnderPad();
    this.initRouterLinkActive();
  }

  ngAfterViewInit() {
    this.initCollapsed();
    this.sidebar$ = this.bodyEl.querySelector('app-sidebar');
    this.initSidebar('init');
  }

  /**
   * 处理当ipad场景是 menu在上面需要把ipad按钮隐藏
   */
  initCollapsed() {
    if (this.settings.layout.collapsed && this.loyout) {
      this.settings.setLayout('collapsed', !this.settings.layout.collapsed);
    }
  }
  /**
   * 处理在顶部的高度 避免遮挡tip
   * @param type init show hide
   */
  initSidebar(type) {
    const me = this;
    const dom = me.sidebar$ || this.bodyEl.querySelector('app-sidebar');
    if (me.loyout) {
      switch (type) {
        case 'show':
          if (dom.style.height !== '') {
            dom.style.height = '';
          }
          break;
        case 'hide':
          if (dom.style.height === '') {
            dom.style.height = '60px';
          }
          break;
        default:
          if (dom.style.height === '') {
            dom.style.height = '60px';
          }
      }
    } else {
      if (dom.style.height !== '') {
        dom.style.height = '';
      }
    }
  }

  /**
   * 设置app-sidebar高度 将app-sidebar显示出来
   */
  showSidebar() {
    this.initSidebar('show');
  }
  /**
   * 设置app-sidebar高度 将app-sidebar隐藏
   */
  hideSidebar() {
    this.initSidebar('hide');
  }
  floatingAreaClickHandle(e) {
    e.stopPropagation();
    /** @type {?} */
    const linkNode = /** @type {?} */ (e.target);
    if (linkNode.nodeName !== 'A') {
      return false;
    }
    /** @type {?} */
    let url = linkNode.getAttribute('href');
    if (url && url.startsWith('#')) {
      url = url.slice(1);
    }
    if (linkNode.dataset['type'] === 'external') {
      return true;
    }
    /** @type {?} */
    const baseHerf = this.locationStrategy.getBaseHref();
    if (baseHerf) {
      url = url.slice(baseHerf.length);
    }
    this.router.navigateByUrl(url);
    this.onSelect(this.menuSrv.getPathByUrl(url).pop());
    this.hideAll();
    e.preventDefault();
    return false;
  }

  clearFloatingContainer() {
    if (!this.floatingEl) {
      return;
    }

    this.floatingEl.removeEventListener(
      'click',
      this.floatingAreaClickHandle.bind(this),
    );
    // fix ie: https://github.com/cipchk/delon/issues/52
    if (this.floatingEl.hasOwnProperty('remove')) {
      this.floatingEl.remove();
    } else if (this.floatingEl.parentNode) {
      this.floatingEl.parentNode.removeChild(this.floatingEl);
    }
  }
  genFloatingContainer() {
    this.clearFloatingContainer();
    this.floatingEl = this.render.createElement('div');
    this.floatingEl.classList.add(FLOATINGCLS + '-container');
    this.floatingEl.addEventListener(
      'click',
      this.floatingAreaClickHandle.bind(this),
      false,
    );
    this.bodyEl.appendChild(this.floatingEl);
  }
  genSubNode(linkNode, item) {
    /** @type {?} */
    const id = '_sidebar-nav-' + item['__id'];
    /** @type {?} */
    const node = /** @type {?} */ (linkNode.nextElementSibling.cloneNode(true));
    node.id = id;
    node.classList.add(FLOATINGCLS);
    node.addEventListener(
      'mouseleave',
      function() {
        node.classList.remove(SHOWCLS);
      },
      false,
    );
    this.floatingEl.appendChild(node);
    return node;
  }
  hideAll() {
    /** @type {?} */
    const allNode = this.floatingEl.querySelectorAll('.' + FLOATINGCLS);
    for (let i = 0; i < allNode.length; i++) {
      allNode[i].classList.remove(SHOWCLS);
    }
  }
  calPos(linkNode, node) {
    /** @type {?} */
    const rect = linkNode.getBoundingClientRect();
    /** @type {?} */
    const scrollTop = Math.max(
      this.doc.documentElement.scrollTop,
      this.bodyEl.scrollTop,
    );
    /** @type {?} */
    const docHeight = Math.max(
      this.doc.documentElement.clientHeight,
      this.bodyEl.clientHeight,
    );
    /** @type {?} */
    let offsetHeight = 0;
    if (docHeight < rect.top + node.clientHeight) {
      offsetHeight = rect.top + node.clientHeight - docHeight;
    }
    node.style.top = rect.top + scrollTop - offsetHeight + 'px';
    node.style.left = rect.right + 5 + 'px';
  }

  /**
   * ipad 产品下 鼠标放在图标上显示菜单
   * @param e
   * @param item
   * @param type
   */
  showSubMenu(e, item, type) {
    if (this.loyout) {
      this.toggleOpen(item, type);
      return;
    }
    if (this.settings.layout.collapsed !== true) {
      return;
    }
    e.preventDefault();
    /** @type {?} */
    const linkNode = /** @type {?} */ (e.target);
    this.genFloatingContainer();
    /** @type {?} */
    const subNode = this.genSubNode(/** @type {?} */ (linkNode), item);
    this.hideAll();
    subNode.classList.add(SHOWCLS);
    this.calPos(/** @type {?} */ (linkNode), subNode);
  }

  /**
   * 对外接口
   * @param item
   */
  onSelect(item) {
    this.select.emit(item);
  }

  /**
   * 判断是否在下来菜单上
   */
  checkPosition(item, type) {
    if (this.loyout) {
      this.isOnUl = type;
      if (!type) {
        this.toggleOpen(item, type);
      }
    }
  }
  toggleOpen(item, type) {
    if (this.loyout) {
      // 鼠标在menu上 将menu显示出来
      setTimeout(() => {
        if (this.isOnUl) {
          return;
        }
        this.toggleOpenHead(item, type);
      }, 300);
    }
  }

  /**
   * 处理头部显示隐藏
   * @param item
   * @param type
   */
  toggleOpenHead(item, type) {
    if (type) {
      this.showSidebar();
    } else {
      this.hideSidebar();
    }
    this.menuSrv.visit(function(i, p) {
      if (i !== item) {
        i['_open'] = false;
      }
    });
    /** @type {?} */
    let pItem = item['__parent'];
    while (pItem) {
      pItem._open = true;
      pItem = pItem.__parent;
    }
    item._open = type;
    this.cd.markForCheck();
  }

  /**
   *
   * @param item
   */
  toggleOpenFun(item) {
    if (this.loyout) {
      return;
    }
    this.menuSrv.visit(function(i, p) {
      if (i !== item) {
        i['_open'] = false;
      }
    });
    /** @type {?} */
    let pItem = item['__parent'];
    while (pItem) {
      pItem._open = true;
      pItem = pItem.__parent;
    }
    item._open = !item._open;
    this.cd.markForCheck();
  }

  ngOnDestroy() {
    this.change$.unsubscribe();
    this.subscription.unsubscribe();
    if (this.route$) {
      this.route$.unsubscribe();
    }
    this.clearFloatingContainer();
  }

  installUnderPad() {
    const me = this;
    if (!this.autoCloseUnderPad) return;
    this.route$ = /** @type {?} */ (this.router.events
      .pipe(
        filter(function(e) {
          return e instanceof NavigationEnd;
        }),
      )
      .subscribe(function(s) {
        return me.underPad();
      }));
    this.underPad();
  }
  underPad() {
    const me = this;
    if (window.innerWidth < 992 && !this.settings.layout.collapsed) {
      setTimeout(function() {
        return me.settings.setLayout('collapsed', true);
      });
    }
  }
}

 

 类似资料: