Angular 利用路由快照实现tab

1、定义路由快照

  新建文件SimpleReuseStrategy.ts

import { RouteReuseStrategy, DefaultUrlSerializer, ActivatedRouteSnapshot, DetachedRouteHandle, Route } from '@angular/router';

export class SimpleReuseStrategy implements RouteReuseStrategy {
  static _cacheRouters: { [key: string]: any } = {};
  private static _donotWriteKey: string = ''   //用来避免关掉自定义tab重新写入

  /**是否允许复用路由 */
  shouldDetach(route: ActivatedRouteSnapshot): boolean {
    if (!route.routeConfig || route.routeConfig.loadChildren) {
      return false;
    }
    let key = this.getPathNotParams(route);
    switch (key) {
      case '/master/pripowerdc0240/home': return true;
      case '/master/pripowerdc0240/list': return true;
      case '/master/pripowerdc0240/detail/:id': return true;
      default: return false;
    }
  }

  /**当路由离开时会触发 按path作为key存储路由快照&组件当前实例对象*/
  store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
    let key = this.getCompleteKey(route);
    if (SimpleReuseStrategy._donotWriteKey != '' && SimpleReuseStrategy._donotWriteKey == this.getCompleteKey(route)) {
      console.log('不存储快照' + key)
      SimpleReuseStrategy._donotWriteKey = ''
      return;
    }
    SimpleReuseStrategy._cacheRouters[key] = {
      snapshot: route,
      handle: handle
    };
  }

  /**是否允许还原路由 */
  shouldAttach(route: ActivatedRouteSnapshot): boolean {
    // 在缓存中有的都认为允许还原路由
    let key = this.getCompleteKey(route);
    return !!route.routeConfig && !!SimpleReuseStrategy._cacheRouters[key];
  }

  /**从缓存中获取快照 */
  retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null {
    // 从缓存中获取快照,若无则返回null
    let key = this.getCompleteKey(route);
    if (!route.routeConfig || !SimpleReuseStrategy._cacheRouters[key]) return null;
    return SimpleReuseStrategy._cacheRouters[key].handle;
  }

  /** 进入路由触发,判断是否同一路由 */
  shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
    // 同一路由时复用路由
    return this.getCompleteKey(future) === this.getCompleteKey(curr);
  }

  public static clearCacheRoutersBy(key: string, donotWrite = false) {
    delete SimpleReuseStrategy._cacheRouters[key];
    if (donotWrite) {
      //不跳转写入
      SimpleReuseStrategy._donotWriteKey = key;
    }
  }

  private getCompleteKey(route: ActivatedRouteSnapshot): string {
    let keys: string[] = []
    route.pathFromRoot.map(v =>
      v.url.map(segment => {
        keys.push(segment.toString());
      })
    );
    return '/' + keys.join('/');
  }

  private getPathNotParams(route: ActivatedRouteSnapshot): string {
    let key = '';
    route.pathFromRoot.map(x => {
      if (x.routeConfig?.path) {
        key += '/' + x.routeConfig?.path;
      }
    });
    return key;
  }

}

  在app.module.ts添加providers

  providers: [ { provide: RouteReuseStrategy, useClass: SimpleReuseStrategy }],

  在需要缓存快照的子路由添加providers,如

import { NgModule } from '@angular/core';
import { Routes, RouterModule, RouteReuseStrategy } from '@angular/router';
import { HomeComponent } from './home/home.component'
import { DetailComponent } from './detail/detail.component'
import { ListComponent } from './list/list.component'
import { SimpleReuseStrategy } from 'src/app/SimpleReuseStrategy';

const routes: Routes = [
  {
    path: 'home',
    component: HomeComponent,
    data: { title: '首页' }
  },
  {
    path: 'detail/:id',
    component: DetailComponent,
    data: { title: '详情' }
  },
  {
    path: 'list',
    component: ListComponent,
    data: { title: '列表' }
  }
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule],
  providers: [
    { provide: RouteReuseStrategy, useClass: SimpleReuseStrategy }
  ],
})
export class Pripowerdc0240RoutingModule { }

2、制作tab

   在母版页定义tab,先上截图

  

  ts

import { Component, OnInit, ViewChild } from '@angular/core';
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
import { of, pipe } from 'rxjs';
import { filter, map, mergeMap } from 'rxjs/operators';
import { FoodNode, TreeService } from 'src/app/services/common/tree.service';
import { SimpleReuseStrategy } from 'src/app/SimpleReuseStrategy';

@Component({
  selector: 'app-master',
  templateUrl: './master.component.html',
  styleUrls: ['./master.component.scss']
})
export class MasterComponent implements OnInit {

  show = true;
  @ViewChild('asideleft') asideleft: any;
  @ViewChild('asideright') asideright: any;


  menuList: CusRouterTabInfo[] = [];
  selectedIndex = 0;
  constructor(
    public treeService: TreeService,
    private router: Router,
    private activatedRoute: ActivatedRoute
  ) {
    this.iniMenu();
  }

  ngOnInit(): void {
    this.treeService.dataSource.data = this.TREE_DATA;
  }

  iniMenu() {
    this.router.events.pipe(
      filter(event => {
        return event instanceof NavigationEnd
      }),
      map(event => {
        let path = (event as NavigationEnd).url
        return { route: this.activatedRoute, path: path }
      }),
      map(model => {
        while (model.route.firstChild) model.route = model.route.firstChild;
        return model;
      }),
      filter(model => model.route.outlet === 'primary'),
    ).subscribe(model => {
      model.route.data.subscribe(data => {
        if(!data['title']){
          return;//没有配置title的默认不添加到menuList
        }
        let hasIndex = this.menuList.findIndex(x => x.path == model.path);
        if (hasIndex != -1) {//menuList已存在
          this.selectedIndex = hasIndex;
          return;
        }
        let menu: CusRouterTabInfo = { title: data['title'], path: model.path, isSelect: true };
        this.menuList.push(menu);
        this.selectedIndex = this.menuList.findIndex(x => x.path == model.path)
      })
    })
  }

  showleft() {
    this.asideleft.nativeElement.style.transform = 'translate(0, 0)';
    this.asideright.nativeElement.style.transform = 'translate(0, 0)';
    this.asideright.nativeElement.style.width = 'calc(100% - 12vw)';
    this.asideright.nativeElement.style.marginleft = '12vw';
    this.show = true;
  }
  hideleft() {
    this.asideleft.nativeElement.style.transform = 'translate(-100%, 0)';
    this.asideright.nativeElement.style.transform = 'translate(-12vw, 0)';
    this.asideright.nativeElement.style.width = '100%';
    this.asideright.nativeElement.style.marginleft = 0;
    this.show = false;
  }


  TREE_DATA: FoodNode[] = [
    {
      id: '0120',
      name: 'UPS电源设备',
      children: [
        { id: '/manager_specialist/partsearch/list', name: '80-NET系列' },
      ]
    },
    {
      id: '0240',
      name: '一次电源配电柜',
      children: [
        { id: '/master/pripowerdc0240/home', name: 'home' },
        { id: '/master/pripowerdc0240/detail/111', name: 'detail' },
        { id: '/master/pripowerdc0240/detail/222', name: 'detail' },
        { id: '/master/pripowerdc0240/list', name: 'list' },
      ],
    }
  ]



  deleteMenuList(path: string) {
    if (this.menuList.length <= 1) { //只有一个不删
      return;
    }
    let delIndex = this.menuList.findIndex(x => x.path == path);
    this.menuList.splice(delIndex, 1);//删除
    SimpleReuseStrategy.clearCacheRoutersBy(path, true);
    if (delIndex == 0) { //关的是第一个
      this.router.navigate([this.menuList[0].path]);
      return;
    }
    //关的其他
    this.router.navigate([this.menuList[delIndex - 1].path]);
  }
  gotoPath(path: string) {
    console.log('gotoPath:' + path);
    this.router.navigate([path]);
  }
}


interface CusRouterTabInfo {
  title: string
  path: string
  isSelect: boolean
}

  html

<div class="left" #asideleft>
  <div class="logobox">
    <div class="logo">
      <img src="/assets/images/vlogo.png">
    </div>
  </div>


  <div class="tree">
    <mat-tree [dataSource]="treeService.dataSource" [treeControl]="treeService.treeControl">
      <mat-tree-node *matTreeNodeDef="let node" matTreeNodePadding>
        <button mat-icon-button disabled></button>
        <span [routerLink]="node.id" routerLinkActive="active">&nbsp;&nbsp;{{node.name}}</span>
        <!-- <span routerLink="/plm/productconfig/list/{{node.id}}" routerLinkActive="active">{{node.name}}</span> -->
      </mat-tree-node>
      <mat-tree-node *matTreeNodeDef="let node;when: treeService.hasChild" matTreeNodeToggle>
        <button mat-icon-button [attr.aria-label]="'Toggle ' + node.name">
          <mat-icon class="mat-icon-rtl-mirror">
            {{treeService.treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right'}}
          </mat-icon>
        </button>
        <span>{{node.name}}</span>
      </mat-tree-node>
    </mat-tree>
  </div>



</div>


<div class="content" #asideright>
  <div class="contenttop">
    <div class="contenttopleft">
      <div (click)="showleft()" *ngIf="!show">
        <mat-icon>reorder</mat-icon>
      </div>
      <div (click)="hideleft()" *ngIf="show">
        <mat-icon>reorder</mat-icon>
      </div>
    </div>
    <div class="contenttopcenter">
      <b>专家库后台管理</b>
    </div>
    <div class="contenttopright">
      Dear:<b style="color: #fff;">admin</b>
      <button mat-button style="color: #fff;margin-right: 2vw;">退出<mat-icon>logout</mat-icon></button>
    </div>
  </div>

  <div class="router">
    <mat-tab-group mat-align-tabs="start" [selectedIndex]="selectedIndex" *ngIf="menuList.length!=0">
      <mat-tab *ngFor="let menu of menuList" class="mattab">
        <div style="width: 100%">
          <ng-template mat-tab-label>
            <input type="text" [value]="menu.title" readonly='true' (click)="gotoPath(menu.path)">
            <mat-icon class="closeIcon" (click)="deleteMenuList(menu.path)">close</mat-icon>
          </ng-template>
        </div>
      </mat-tab>
    </mat-tab-group>
    <!-- <button mat-button (click)="loadCacheRouters()">load</button> -->
  </div>

  <div class="contentrouting">
    <router-outlet></router-outlet>
  </div>
</div>

  scss

.left{
  float: left;
  background-color: #607B8B;
  height: 100vh;
  width: 12vw;
  transition: all 1s;
  z-index: 2;
  position:absolute;
  .logobox{
    background-color: #68838B;
    border-radius: 7px;
    width: 100%;
    height: 4vw;
    .logo{
      padding: 1vh  1vw;
      img{
        width: 100%;
        height: 100%;
      }
    }
  }
}



.content{
  z-index: 1;
  position:absolute;
  margin-left: 12vw;
  background-color: #DCDCDC;
  height: 100vh;
  width: 88vw;
  transition: all 1s;
  // transition: all 1s;
}

.contenttop{
  height: 6vh;
  width: 100%;
  line-height: 5.5vh;
  background-color: #B5B5B5;
  position: relative;
  .contenttopleft{
    float: left;
    line-height: 100%;
    .mat-icon{
      cursor: pointer;
    }
  }
  .contenttopcenter{
    float: left;
    // width: 30vw;
    margin-left: 35vw;
    b{
      font-size: 1.3rem;
      color: #DCDCDC;
    }
  }
  .contenttopright{
    float: right;
  }
}

.contentrouting{
  height: calc(94vh - 30px);
  width: 100%;
  line-height: unset;
}

::ng-deep{
  .mat-tree{
    background:none;
    mat-tree-node{
      padding-left: 0 !important;
    }
    .mat-tree-node{
      color: #DCDCDC;
      // color: red;
      min-height: 20px;
      height: auto;
      padding-left: 10px;
      button{
        width: 20px;
        height: 30px;
        line-height: 30px;
      }
      span{
        cursor: pointer;
        width: 100%;
        height: auto;
        user-select: none;
      }
    }
  }
}

.router{
  input{
    border: 0;
    // background: none;
    cursor: pointer;
    height: 28px;
    width: 100%;
  }
  input:focus{
    outline: none;
  }
  .closeIcon{
    margin-left: 2px;
    font-size: 12px;
    height: 100%;
    background: red;
    border-radius: 50%;
    width: 12px;
    height: 12px;
    position: absolute;
    right: 5px;
  }

}


.active{
  color: #CD950C;
}


:host ::ng-deep{
  .mat-tab-label{
    height: 30px !important;
    border-top-left-radius: 15px;
    border-top-right-radius: 15px;
    .mat-tab-label-content{
      position: absolute;
      left: 0;
    }
  }
  .mat-tab-group{
    height: 30px !important;
    .mat-tab-labels{
      height: 30px !important;
    }
  }
}

  完毕!!!

 

posted @ 2021-10-27 17:53  wskxy  阅读(161)  评论(0编辑  收藏  举报