ng-alain通过修改样式实现菜单布局切换

一、页面效果

1、默认的左右布局

2、切换后的上下布局


二、代码实现

1、布局切换服务类,用于通知需要改变布局的模块去改变样式
    
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';

@Injectable()
export class LayoutChangeService {
  constructor() {}
  private Source = new Subject<any>();
  Status$ = this.Source.asObservable();

  /**
   *@函数名称:layoutChange
   *@参数:message 字符类型参数
   *@作用:接受布局切换的变化,用户通知各个模块做相应的样式改变
   *@date 2018/5/16
   */
  layoutChange(message: any) {
    const msg = JSON.parse(message);
    this.Source.next(msg);
    this.setLayout(msg);
  }
  /**
   * 获取布局方式的缓存 true为上下布局 false为左右布局
   * @returns {boolean}
   */
  get getLayout() {
    const layout = localStorage.getItem('layoutMode');
    if (layout === 'true') {
      return true;
    } else {
      return false;
    }
  }
  /**
   * 设置默认的布局方式缓存
   * @param val
   */
  setLayout(val) {
    localStorage.setItem('layoutMode', val);
  }
}
2、新增HeaderLayoutComponent ,增加切换的入口,切换布局时通知其他需要修改的模块去改变样式。
import { Component, HostListener } from '@angular/core';
import { LayoutChangeService } from '@core/layout/layoutChange.service';

@Component({
  selector: 'header-layout',
  template: `
    <i class="anticon anticon-{{status? 'shrink' : 'arrows-alt'}}"></i>
    {{(status ? '左右布局' : '上下布局') | translate }}
  `,
  host: {
    '[class.d-block]': 'true',
  },
})
export class HeaderLayoutComponent {
  constructor(public changeService: LayoutChangeService) {}
  status = this.changeService.getLayout;

  @HostListener('window:resize')
  _resize() {
    // this.status = screenfull.isFullscreen;
  }

  @HostListener('click', ['$event'])
  _changeTheme(e) {
    this.changeService.layoutChange(!this.status);
    this.status = !this.status;
  }
}

3、修改需要改变样式的部分(注意事项:通过俩层dom的click事件来动态改变topMenu的高度,达到显示和隐藏topMenu的效果,且解决topMenu遮罩层覆盖其他模块问题

 1)菜单dom,处理自己的样式变化,大部分样式修改都在该模块完成。

 
 
<div class="aside-inner" [ngClass]="{'aside-inner-top' : layoutStatus}">
  <sidebar-nav class="d-block py-lg" [ngClass]="{'ul-nav' : layoutStatus}"
               (click)="onclick()"
               (select)="callDestroy($event)"></sidebar-nav>
</div>
import {
  Component,
  ElementRef,
  AfterViewInit,
  ViewChild,
  Renderer2,
} from '@angular/core';
import { NzMessageService } from 'ng-zorro-antd';
import { SettingsService } from '@delon/theme';
import { TabChangeService } from '@core/tabChange/tabChange.service';
import { LayoutChangeService } from '@core/layout/layoutChange.service';
import { Subscription } from 'rxjs/Subscription';
import { AppUtil } from '@core/util/util.service';
import { StartupService } from '@core/startup/startup.service';

@Component({
  selector: 'app-sidebar',
  templateUrl: './sidebar.component.html',
})
export class SidebarComponent implements AfterViewInit {
  subscription: Subscription;
  layoutStatus = this.layoutService.getLayout;
  @ViewChild('sidebarNav') sidebarNav;
  constructor(
    public settings: SettingsService,
    public msgSrv: NzMessageService,
    public changeService: TabChangeService,
    public layoutService: LayoutChangeService,
    public el: ElementRef,
    public util: AppUtil,
    public renderer2: Renderer2,
    public startupService: StartupService,
  ) {
    this.subscription = layoutService.Status$.subscribe(message => {
      // 获取当前dashboard的名称 和切换的tab页相互校验
      this.layoutStatus = message;
      this.setLayout(message);
    });
  }
  ngAfterViewInit() {
    this.setLayout(this.layoutService.getLayout);
  }
  // 设置样式
  setLayout(direction) {
    // border-left: 3px solid #349ac0;
    this.hideULBorder(direction);
    const el = this.el.nativeElement;
    this.setStyles(el.querySelectorAll('ul'), direction);
    this.setStyles(el.querySelectorAll('li'), direction);
    // 处理小屏模式下 文字隐藏导致切换后头部菜单无法显示
    this.setCollapsed();
    this.setTopMenuShow();
  }
  onclick() {
    setTimeout(() => this.setTopMenuShow());
  }
  // 隐藏ul的有边框
  hideULBorder(direction) {
    const el = this.el.nativeElement;
    const boardRight = direction ? '1px solid #18191d' : '';
    el.querySelector('ul').style['border-right'] = boardRight;
  }
  // 设置屏幕为非pad
  setCollapsed() {
    if (this.settings.layout.collapsed && this.layoutStatus) {
      this.settings.setLayout('collapsed', !this.settings.layout.collapsed);
    }
  }
  // 设置样式属性值
  setStyles(dom, direction) {
    const len = dom.length;
    // 设置默认的样式 切换后改变样式
    let float = 'none';
    let display = '';
    let absolute = '';
    let top = '';
    let backgroundColor = '';
    if (direction) {
      float = 'left';
      display = 'none';
      absolute = 'absolute';
      top = '60px';
      backgroundColor = this.util.getColor('background');
    }
    // 循环改变样式
    for (let i = 0; i < len; i++) {
      const singleItem = dom[i];
      // 设置ui和li的左悬浮样式
      singleItem.style['float'] = float;
      singleItem.style['list-style-type'] = 'none';
      const tagName = singleItem.tagName;
      // 隐藏图标
      const img = singleItem.querySelector('i');
      if (img) {
        // TODO 是否显示图标 && this.settings.layout.collapsed
        img.style['display'] = display;
      }
      if (tagName === 'LI') {
        // 隐藏导航菜单项
        const textDom = singleItem.querySelector('span');
        if (textDom && textDom.innerText === '导航菜单') {
          singleItem.style['display'] = display;
        }
        // 处理有二级菜单节点的样式
        const ULINLI = singleItem.querySelector('ul');
        if (ULINLI) {
          ULINLI.style['position'] = absolute;
          ULINLI.style['top'] = top;
          ULINLI.style['background-color'] = backgroundColor;
        }
      }
    }
  }
  // 隐藏二级菜单
  setTopMenuShow() {
    this.setTreeMenuHeight(true);
  }
  // 二级菜单显示
  setTopMenuHide() {
    this.setTreeMenuHeight(false);
  }
  // 设置topMenu的高度 防止覆盖到模块中
  setTreeMenuHeight(show) {
    const me = this;
    const dom = this.el.nativeElement;
    if (me.layoutStatus) {
      if (!show && !dom.style.height) {
        dom.style.height = '60px';
      } else if (show && dom.style.height) {
        dom.style.height = '';
      }
    } else {
      if (show && dom.style.height) {
        dom.style.height = '';
      }
    }
  }
  /**
   *@函数名称:callDestroy
   *@参数:val 组件传递的参数
   *@作用:给服务传递切换信息
   *@date 2018/5/16
   */
  callDestroy(val) {
    const value = {
      title: val.text,
      url: val.link,
    };
    this.changeService.tabChange(JSON.stringify(value));
    // 切换将topMeun隐藏
    this.setTopMenuHide();
  }
}

2)默认布局LayoutDefaultComponent,点击该层dom时,将topMenu隐藏,防止遮住内容显示区域,

 
 
<div class="wrapper" (click)="onclick($event)">
  <app-sidebar #appSidebar class="aside" [ngClass]="{'aside-top' : layoutStatus}"></app-sidebar>
  <div class="router-progress-bar" *ngIf="isFetching"></div>
  <app-header class="header" [ngClass]="{'header-top' : layoutStatus}"></app-header>
  <section class="content" [ngClass]="{'content-top' : layoutStatus}">
    <reuse-tab (change)="tabChange($event)" [ngClass]="{'reuse-tab-fixed-top' : layoutStatus}"></reuse-tab>
    <router-outlet></router-outlet>
  </section>
</div>

import { Component, OnDestroy, ViewChild } from '@angular/core';
import {
  Router,
  NavigationEnd,
  RouteConfigLoadStart,
  NavigationError,
} from '@angular/router';
import { NzMessageService, NzModalService } from 'ng-zorro-antd';
import { ScrollService, MenuService, SettingsService } from '@delon/theme';
import { TabChangeService } from '@core/tabChange/tabChange.service';
import { LayoutChangeService } from '@core/layout/layoutChange.service';
import { Subscription } from 'rxjs/Subscription';

@Component({
  selector: 'layout-default',
  templateUrl: './default.component.html',
})
export class LayoutDefaultComponent implements OnDestroy {
  isFetching = false;
  subscription: Subscription;
  @ViewChild('appSidebar') appSidebar;
  constructor(
    router: Router,
    scroll: ScrollService,
    private _message: NzMessageService,
    public menuSrv: MenuService,
    public settings: SettingsService,
    public changeService: TabChangeService,
    public layoutService: LayoutChangeService,
  ) {
    // scroll to top in change page
    router.events.subscribe(evt => {
      if (!this.isFetching && evt instanceof RouteConfigLoadStart) {
        this.isFetching = true;
      }
      if (evt instanceof NavigationError) {
        this.isFetching = false;
        _message.error(`无法加载${evt.url}路由`, { nzDuration: 1000 * 3 });
        return;
      }
      if (!(evt instanceof NavigationEnd)) {
        return;
      }
      setTimeout(() => {
        scroll.scrollToTop();
        this.isFetching = false;
      }, 100);
    });
    this.subscription = layoutService.Status$.subscribe(message => {
      // 获取当前dashboard的名称 和切换的tab页相互校验
      this.layoutStatus = message;
    });
  }
  /**
   *@函数名称:tabChange
   *@参数:val获取组件传过来的值,传递下去
   *@作用:给服务传递切换信息
   *@date 2018/5/16
   */
  tabChange(val): void {
    const value = {
      title: val.title,
      url: val.url,
    };
    this.changeService.tabChange(JSON.stringify(value));
  }
  layoutStatus = this.layoutService.getLayout;
  ngOnDestroy() {
    this.subscription.unsubscribe();
  }

  /**
   * 点击将topMenu隐藏
   */
  onclick(e) {
    this.appSidebar.setTopMenuHide();
  }
}
3)需要覆盖的样式
 
 
//处理动态切换menu布局样式覆盖
.aside-top {
  float: left;
  margin-left: 20px;
  margin-top: 0 !important;
  z-index: 30;
  width: @top-menu-width;
  left: 170px;
  background-color: transparent;
}
// 覆盖组件样式,有边框
.aside-top::after {
  border-right: 0 solid @primary-color !important;
}
// 覆盖样式,左边高度处理
.content-top {
  margin-left: 27px;
}
// 动态路由左边距离处理
.reuse-tab-fixed-top {
  left: 27px !important;
}
// 样式覆盖
.aside-inner-top {
  float: left;
  margin-left: 20px;
  width: 100%;
}
// 样式覆盖
.ul-nav {
  padding-top: 12px !important;
}
// 样式覆盖
.header-search-top {
  padding-left: @top-menu-Rightwidth;
}

猜你喜欢

转载自blog.csdn.net/ligaoming_123/article/details/80747324