seimin hai 1 mes
pai
achega
369f331cfd

+ 3 - 0
src/app/app.module.ts

@@ -1,3 +1,4 @@
1
+import { CustomReuseStrategy } from './custom-route-reuse-strategy';
1 2
 import { BrowserModule } from '@angular/platform-browser';
2 3
 import { NgModule } from '@angular/core';
3 4
 import { CommonModule } from '@angular/common';
@@ -10,6 +11,7 @@ import { HttpInterceptorService } from './services/httpInterceptor.service';//
10 11
 import { ShareModule } from './share/share.module';
11 12
 import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
12 13
 import { NZ_I18N, zh_CN, NzConfig, NZ_CONFIG } from 'ng-zorro-antd';
14
+import { RouteReuseStrategy } from '@angular/router';
13 15
 const ngZorroConfig: NzConfig = {
14 16
   // 注意组件名称没有 nz 前缀
15 17
   notification: { nzTop: 100 }
@@ -32,6 +34,7 @@ registerLocaleData(zh);
32 34
     { provide: NZ_CONFIG, useValue: ngZorroConfig },
33 35
     { provide: LocationStrategy, useClass: HashLocationStrategy },
34 36
     { provide: HTTP_INTERCEPTORS, useClass: HttpInterceptorService, multi: true },
37
+    { provide: RouteReuseStrategy, useClass: CustomReuseStrategy },
35 38
   ],
36 39
   bootstrap: [AppComponent]
37 40
 })

+ 48 - 0
src/app/custom-route-reuse-strategy.ts

@@ -0,0 +1,48 @@
1
+import { ActivatedRouteSnapshot, DetachedRouteHandle, RouteReuseStrategy } from '@angular/router';
2
+
3
+export class CustomReuseStrategy implements RouteReuseStrategy {
4
+  private storedHandles = new Map<string, DetachedRouteHandle>();
5
+
6
+  shouldDetach(route: ActivatedRouteSnapshot): boolean {
7
+    return route.data.reuse || false;
8
+  }
9
+
10
+  store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
11
+    if (route.data.reuse) {
12
+      const key = this.getRouteKey(route);
13
+      this.storedHandles.set(key, handle);
14
+    }
15
+  }
16
+
17
+  shouldAttach(route: ActivatedRouteSnapshot): boolean {
18
+    const key = this.getRouteKey(route);
19
+    // console.log('shouldAttach:', this.storedHandles.has(key))
20
+    return this.storedHandles.has(key);
21
+  }
22
+
23
+  retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null {
24
+    const key = this.getRouteKey(route);
25
+    return this.storedHandles.get(key) || null;
26
+  }
27
+
28
+  shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
29
+    // 精确匹配路由复用
30
+    return future.routeConfig === curr.routeConfig &&
31
+           JSON.stringify(future.params) === JSON.stringify(curr.params);
32
+  }
33
+
34
+  private getRouteKey(route: ActivatedRouteSnapshot): string {
35
+    let str = route.pathFromRoot
36
+    .filter(v => v.routeConfig)
37
+    .map(v => v.routeConfig!.path + JSON.stringify(v.params))
38
+    .join('/');
39
+    return str.endsWith('/{}') ? str.slice(0, -3) : str;
40
+  }
41
+
42
+  public deleteRouteSnapshot(url: string): void {
43
+    const key = url.replace(/\//g, '_');
44
+    if (this.storedHandles.has(key)) {
45
+      this.storedHandles.delete(key);
46
+    }
47
+  }
48
+}

+ 0 - 66
src/app/route/simple-reuse-strategy.ts

@@ -1,66 +0,0 @@
1
-// 创建重用策略
2
-import {ActivatedRouteSnapshot, DetachedRouteHandle, RouteReuseStrategy} from '@angular/router';
3
-
4
-/**
5
- * 路由重用策略
6
- */
7
-export class SimpleReuseStrategy implements RouteReuseStrategy {
8
-
9
-  // 保存路由快照
10
-  // [key:string] 键为字符串类型
11
-  // DetachedRouteHandle 值为路由处理器
12
-  public static snapshots: { [key: string]: DetachedRouteHandle } = {};
13
-
14
-  /**
15
-   * 从缓存中获取快照
16
-   * @param {ActivatedRouteSnapshot} route
17
-   * @return {DetachedRouteHandle | null}
18
-   */
19
-  retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null {
20
-    return route.routeConfig ? SimpleReuseStrategy.snapshots[route.routeConfig.path] : null;
21
-  }
22
-
23
-  /**
24
-   * 是否允许还原
25
-   * @param {ActivatedRouteSnapshot} route
26
-   * @return {boolean} true-允许还原
27
-   */
28
-  shouldAttach(route: ActivatedRouteSnapshot): any {
29
-    return route.routeConfig && SimpleReuseStrategy.snapshots[route.routeConfig.path];
30
-  }
31
-
32
-  /**
33
-   * 确定是否应该分离此路由(及其子树)以便以后重用
34
-   * @param {ActivatedRouteSnapshot} route
35
-   * @return {boolean}
36
-   */
37
-  shouldDetach(route: ActivatedRouteSnapshot): boolean {
38
-    // useCache 为自定义数据
39
-    return route.routeConfig && route.routeConfig.data && route.routeConfig.data.useCache;
40
-  }
41
-
42
-  /**
43
-   * 进入路由触发, 判断是否为同一路由
44
-   * @param {ActivatedRouteSnapshot} future
45
-   * @param {ActivatedRouteSnapshot} curr
46
-   * @return {boolean}
47
-   */
48
-  shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
49
-    // future - 未来的(下一个)路由快照
50
-    return future.routeConfig === curr.routeConfig;
51
-  }
52
-
53
-  /**
54
-   * 保存路由
55
-   * @param {ActivatedRouteSnapshot} route
56
-   * @param {DetachedRouteHandle | null} handle
57
-   */
58
-  store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle | null): void {
59
-    // 通过 Route.path 映射路由快照, 一定要确保它的唯一性
60
-    // 也可以通过 route.routeConfig.data.uid 或其他可以确定唯一性的数据作为映射key
61
-    // 作者这里能够确保 path 的唯一性
62
-    SimpleReuseStrategy.snapshots[route.routeConfig.path] = handle;
63
-  }
64
-
65
-}
66
-

+ 14 - 0
src/app/views/new-statistics/components/custom-tabs/custom-tabs.component.html

@@ -0,0 +1,14 @@
1
+<nz-tabset class="tabs" nzType="card" [nzSelectedIndex]="selectedIndex">
2
+  <nz-tab *ngFor="let tab of tabService.tabs" [nzTitle]="tabTemplate"  (nzClick)="selectTab(tab.path)">
3
+    <ng-template #tabTemplate>
4
+      <div (contextmenu)="onRightClick($event, tab)"><span>{{ tab.title }}</span><i *ngIf="tab.closable && tabService.tabs.length > 1" nz-icon nzType="close" class="ant-tabs-close-x" (click)="closeTab(tab.path, $event)"></i></div>
5
+    </ng-template>
6
+  </nz-tab>
7
+</nz-tabset>
8
+<app-tab-context-menu
9
+  *ngIf="showContextMenu"
10
+  [x]="contextMenuPosition.x"
11
+  [y]="contextMenuPosition.y"
12
+  [event]="event"
13
+  (closeOthers)="closeOtherTabs()">
14
+</app-tab-context-menu>

+ 7 - 0
src/app/views/new-statistics/components/custom-tabs/custom-tabs.component.less

@@ -0,0 +1,7 @@
1
+:host{
2
+  .tabs{
3
+    top: -36px;
4
+    width: calc(100vw - 183px);
5
+    user-select: none;
6
+  }
7
+}

+ 80 - 0
src/app/views/new-statistics/components/custom-tabs/custom-tabs.component.ts

@@ -0,0 +1,80 @@
1
+import { ChangeDetectorRef, Component, HostListener, OnInit, Output } from '@angular/core';
2
+import { EventEmitter } from '@angular/core';
3
+import { Tab, TabService } from '../../services/tab.service';
4
+import { Router } from '@angular/router';
5
+
6
+@Component({
7
+  selector: 'app-custom-tabs',
8
+  templateUrl: './custom-tabs.component.html',
9
+  styleUrls: ['./custom-tabs.component.less']
10
+})
11
+export class CustomTabsComponent implements OnInit {
12
+  @Output() selectTabEmit = new EventEmitter();
13
+  constructor(
14
+    public tabService: TabService,
15
+    public router: Router,
16
+    private cdr: ChangeDetectorRef,
17
+  ) { }
18
+
19
+  ngOnInit() {}
20
+  get selectedIndex(): number {
21
+    return this.tabService.tabs.findIndex(t => t.active);
22
+  }
23
+
24
+  closeTab(path: string, event: MouseEvent): void {
25
+    event.stopPropagation();
26
+    this.tabService.closeTab(path);
27
+  }
28
+
29
+  selectTab(path: string): void {
30
+    const [routePath, params] = this.tabService.getFullPath(path);
31
+    // console.log('params:', params)
32
+    // console.log('routePath:', routePath)
33
+    this.selectTabEmit.emit(routePath.split('/').reverse()[0])
34
+    this.router.navigate([routePath], params).catch(err => {
35
+      console.error('导航失败:', err);
36
+      // 处理无效页签
37
+      this.tabService.closeTab(path);
38
+    });
39
+  }
40
+
41
+  showContextMenu = false;
42
+  contextMenuPosition = { x: 0, y: 0 };
43
+  rightClickedTab;
44
+  event:MouseEvent;
45
+  // 右键点击处理
46
+  onRightClick(event: MouseEvent, tab: Tab): void {
47
+    this.event = event;
48
+    event.preventDefault();
49
+    event.stopPropagation();
50
+
51
+    // 无论是否激活,都允许操作
52
+    this.rightClickedTab = tab; // 存储当前右键页签
53
+    this.contextMenuPosition = {
54
+      x: event.clientX + 5,
55
+      y: event.clientY + 5
56
+    };
57
+    this.showContextMenu = true;
58
+    this.cdr.detectChanges();
59
+  }
60
+
61
+  // 关闭其他页签
62
+  closeOtherTabs(): void {
63
+    if (!this.rightClickedTab) return;
64
+
65
+    // 关闭其他前强制激活当前右键页签
66
+    this.tabService.tabs.forEach(t => t.active = false);
67
+    this.rightClickedTab.active = true;
68
+
69
+    this.tabService.closeOtherTabs(this.rightClickedTab);
70
+    this.selectTabEmit.emit(this.rightClickedTab.path.split('/').reverse()[0])
71
+
72
+    this.showContextMenu = false;
73
+  }
74
+
75
+  // 点击其他地方关闭菜单
76
+  @HostListener('document:click')
77
+  closeContextMenu(): void {
78
+    this.showContextMenu = false;
79
+  }
80
+}

+ 23 - 0
src/app/views/new-statistics/components/custom-tabs/custom-tabs.module.ts

@@ -0,0 +1,23 @@
1
+import { TabContextMenuComponent } from './../tab-context-menu/tab-context-menu.component';
2
+import { CustomTabsComponent } from './custom-tabs.component';
3
+import { NgModule } from '@angular/core';
4
+import { CommonModule } from '@angular/common';
5
+
6
+import { ShareModule } from 'src/app/share/share.module';
7
+
8
+
9
+@NgModule({
10
+  declarations: [
11
+    CustomTabsComponent,
12
+    TabContextMenuComponent,
13
+  ],
14
+  imports: [
15
+    CommonModule,
16
+    ShareModule,
17
+  ],
18
+  exports: [
19
+    CustomTabsComponent,
20
+    TabContextMenuComponent,
21
+  ]
22
+})
23
+export class CustomTabsModule { }

+ 30 - 0
src/app/views/new-statistics/components/tab-context-menu/tab-context-menu.component.ts

@@ -0,0 +1,30 @@
1
+import { Component, Input, Output, EventEmitter, OnInit, ViewChild } from '@angular/core';
2
+import { NzContextMenuService, NzDropdownMenuComponent } from 'ng-zorro-antd';
3
+
4
+@Component({
5
+  selector: 'app-tab-context-menu',
6
+  template: `
7
+    <div class="context-menu" [ngStyle]="{left: x + 'px', top: y + 'px'}">
8
+      <nz-dropdown-menu #menu="nzDropdownMenu">
9
+        <ul nz-menu class="context-menu-list">
10
+          <li nz-menu-item (click)="closeOthers.emit()">关闭其他</li>
11
+        </ul>
12
+      </nz-dropdown-menu>
13
+    </div>
14
+  `,
15
+  styles: [`.ant-dropdown-menu-item, .ant-dropdown-menu-submenu-title{font-size:12px;line-height:1;width: 120px;}`]
16
+})
17
+export class TabContextMenuComponent implements OnInit {
18
+  @Input() x = 0;
19
+  @Input() y = 0;
20
+  @Input() event:MouseEvent;
21
+  @Output() closeOthers = new EventEmitter<void>();
22
+  @ViewChild('menu', { static: true}) menu: NzDropdownMenuComponent;
23
+  constructor(
24
+    private nzContextMenuService: NzContextMenuService,
25
+  ){}
26
+
27
+  ngOnInit(): void {
28
+    this.nzContextMenuService.create(this.event, this.menu);
29
+  }
30
+}

+ 52 - 13
src/app/views/new-statistics/maintenance-statistics/maintenance-statistics-routing.module.ts

@@ -12,79 +12,118 @@ const routes: Routes = [
12 12
         // 故障工单统计
13 13
         path: 'incidentStatistics',
14 14
         loadChildren: () => import('./incident-statistics/incident-statistics.module').then(m => m.IncidentStatisticsModule),
15
-        data: { useCache: true },
15
+        data: {
16
+          reuse: true,
17
+          title: '故障工单统计'
18
+        }
16 19
       },
17 20
       {
18 21
         // 一级故障现象
19 22
         path: 'categoryOneStatistics',
20 23
         loadChildren: () => import('./category-one-statistics/category-one-statistics.module').then(m => m.CategoryOneStatisticsModule),
21
-        data: { useCache: true },
24
+        data: {
25
+          reuse: true,
26
+          title: '一级故障现象'
27
+        }
22 28
       },
23 29
       {
24 30
         // 二级故障现象
25 31
         path: 'categoryTwoStatistics',
26 32
         loadChildren: () => import('./category-two-statistics/category-two-statistics.module').then(m => m.CategoryTwoStatisticsModule),
27
-        data: { useCache: true },
33
+        data: {
34
+          reuse: true,
35
+          title: '二级故障现象'
36
+        }
28 37
       },
29 38
       {
30 39
         // 三级故障现象
31 40
         path: 'categoryThreeStatistics',
32 41
         loadChildren: () => import('./category-three-statistics/category-three-statistics.module').then(m => m.CategoryThreeStatisticsModule),
33
-        data: { useCache: true },
42
+        data: {
43
+          reuse: true,
44
+          title: '三级故障现象'
45
+        }
34 46
       },
35 47
       {
36 48
         // 处理组统计
37 49
         path: 'groupStatistics',
38 50
         loadChildren: () => import('./group-statistics/group-statistics.module').then(m => m.GroupStatisticsModule),
39
-        data: { useCache: true },
51
+        data: {
52
+          reuse: true,
53
+          title: '处理组统计'
54
+        }
40 55
       },
41 56
       {
42 57
         // 处理人统计
43 58
         path: 'userStatistics',
44 59
         loadChildren: () => import('./user-statistics/user-statistics.module').then(m => m.UserStatisticsModule),
45
-        data: { useCache: true },
60
+        data: {
61
+          reuse: true,
62
+          title: '处理人统计'
63
+        }
46 64
       },
47 65
       {
48 66
         // 三方公司统计
49 67
         path: 'tripartiteCompanyStatistics',
50 68
         loadChildren: () => import('./tripartite-company-statistics/tripartite-company-statistics.module').then(m => m.TripartiteCompanyStatisticsModule),
51
-        data: { useCache: true },
69
+        data: {
70
+          reuse: true,
71
+          title: '三方公司统计'
72
+        }
52 73
       },
53 74
       {
54 75
         // 楼栋统计
55 76
         path: 'buildingStatistics',
56 77
         loadChildren: () => import('./building-statistics/building-statistics.module').then(m => m.BuildingStatisticsModule),
57
-        data: { useCache: true },
78
+        data: {
79
+          reuse: true,
80
+          title: '楼栋统计'
81
+        }
58 82
       },
59 83
       {
60 84
         // 楼层统计
61 85
         path: 'floorStatistics',
62 86
         loadChildren: () => import('./floor-statistics/floor-statistics.module').then(m => m.FloorStatisticsModule),
63
-        data: { useCache: true },
87
+        data: {
88
+          reuse: true,
89
+          title: '楼层统计'
90
+        }
64 91
       },
65 92
       {
66 93
         // 科室工单统计
67 94
         path: 'departmentIncidentStatistics',
68 95
         loadChildren: () => import('./department-incident-statistics/department-incident-statistics.module').then(m => m.DepartmentIncidentStatisticsModule),
69
-        data: { useCache: true },
96
+        data: {
97
+          reuse: true,
98
+          title: '科室工单统计'
99
+        }
70 100
       },
71 101
       {
72 102
         // 科室评价统计
73 103
         path: 'departmentEvaluateStatistics',
74 104
         loadChildren: () => import('./department-evaluate-statistics/department-evaluate-statistics.module').then(m => m.DepartmentEvaluateStatisticsModule),
75
-        data: { useCache: true },
105
+        data: {
106
+          reuse: true,
107
+          title: '科室评价统计'
108
+        }
76 109
       },
77 110
       {
78 111
         // 科室来源统计
79 112
         path: 'departmentSourceStatistics',
80 113
         loadChildren: () => import('./department-source-statistics/department-source-statistics.module').then(m => m.DepartmentSourceStatisticsModule),
81
-        data: { useCache: true },
114
+        data: {
115
+          reuse: true,
116
+          title: '科室来源统计'
117
+        }
82 118
       },
83 119
       {
84 120
         // 故障来源统计
85 121
         path: 'categorySourceStatistics',
86 122
         loadChildren: () => import('./category-source-statistics/category-source-statistics.module').then(m => m.CategorySourceStatisticsModule),
87
-        data: { useCache: true },
123
+        data: {
124
+          reuse: true,
125
+          title: '故障来源统计'
126
+        }
88 127
       },
89 128
     ]
90 129
   }

+ 2 - 8
src/app/views/new-statistics/maintenance-statistics/maintenance-statistics.component.html

@@ -12,16 +12,10 @@
12 12
 <!-- content -->
13 13
 <div class="content">
14 14
   <virtual-scroller #osComponentRef1 [items]="secondMenuList" class="secondMenu">
15
-    <div class="secondMenuItem ellipsis-oneline" [ngClass]="{ active: activeSecondMenuId == data.id }" title="故障工单统计" *ngFor="let data of osComponentRef1.viewPortItems" (click)="clickSecondMenu(data)">{{data.title}}</div>
15
+    <div class="secondMenuItem ellipsis-oneline" [ngClass]="{ active: activeSecondMenuLink == data.link }" title="故障工单统计" *ngFor="let data of osComponentRef1.viewPortItems" (click)="clickSecondMenu(data)">{{data.title}}</div>
16 16
   </virtual-scroller>
17 17
   <div class="main">
18
-    <nz-tabset class="tabs" nzType="card" [nzSelectedIndex]="tabIndex" (nzSelectedIndexChange)="clickTab($event)">
19
-      <nz-tab *ngFor="let tab of tabs;trackBy:trackById" [nzTitle]="titleTemplate">
20
-        <ng-template #titleTemplate>
21
-          <div><span>{{ tab.title }}</span><i nz-icon nzType="close" class="ant-tabs-close-x" (click)="closeTab(tab)"></i></div>
22
-        </ng-template>
23
-      </nz-tab>
24
-    </nz-tabset>
18
+    <app-custom-tabs (selectTabEmit)="selectTabEmit($event)"></app-custom-tabs>
25 19
     <router-outlet></router-outlet>
26 20
   </div>
27 21
 </div>

+ 0 - 4
src/app/views/new-statistics/maintenance-statistics/maintenance-statistics.component.less

@@ -88,10 +88,6 @@
88 88
       background-color: #fff;
89 89
       position: relative;
90 90
       top: 36px;
91
-      .tabs{
92
-        top: -36px;
93
-        width: calc(100vw - 183px);
94
-      }
95 91
     }
96 92
   }
97 93
 }

+ 8 - 75
src/app/views/new-statistics/maintenance-statistics/maintenance-statistics.component.ts

@@ -1,5 +1,4 @@
1
-import { filter } from 'rxjs/operators';
2
-import { ActivatedRoute, Router, ActivationEnd } from '@angular/router';
1
+import { ActivatedRoute, Router } from '@angular/router';
3 2
 import { Component, OnInit } from "@angular/core";
4 3
 import { ToolService } from 'src/app/services/tool.service';
5 4
 @Component({
@@ -12,32 +11,7 @@ export class MaintenanceStatisticsComponent implements OnInit {
12 11
     public tool: ToolService,
13 12
     public route: ActivatedRoute,
14 13
     public router: Router,
15
-  ) {
16
-    this.router.events.pipe(filter(e => e instanceof ActivationEnd))
17
-	    .subscribe((e: ActivationEnd) => {
18
-	      const snapshot = e.snapshot;
19
-	      const isSkip = !(snapshot['_routerState'].url && snapshot.routeConfig.data && snapshot.routeConfig.data.useCache);
20
-	      if(isSkip) return;
21
-        console.log(this.tabs,1111);
22
-        // 获取路由配置中自定义的唯一标记
23
-	      const path = snapshot.routeConfig.path;
24
-
25
-		    // 从当前激活的已存在的 tab 缓存中直接激活
26
-	      let exist = false;
27
-        this.tabs.forEach((tab, i) => {
28
-	        if (path === tab.url) {
29
-	          // mvvm 模式直接激活指定 tab
30
-	          this.tabIndex = i;
31
-	          exist = true;
32
-	        }
33
-	      })
34
-        console.log(this.tabIndex)
35
-
36
-		    // 指定路由没有在 tab 缓存中找到(或已经从ui中关闭/删除)
37
-	      // 首次进入没找到 tab, 从menu中获取
38
-	      if (!exist) this.clickTab(this.tabIndex);
39
-	    });
40
-  }
14
+  ) {}
41 15
 
42 16
   menuList: any[] = [];
43 17
   secondMenuList:any[] = [];
@@ -62,56 +36,15 @@ export class MaintenanceStatisticsComponent implements OnInit {
62 36
   }
63 37
 
64 38
   // 点击二级菜单
65
-  activeSecondMenuId:number;
39
+  activeSecondMenuLink:string;
66 40
   clickSecondMenu(data){
67
-    this.activeSecondMenuId = data.id;
68
-    this.addTab(data);
69
-  }
70
-
71
-  tabs = [];
72
-  tabIndex:number = 0;
73
-  closeTab(tab): void {
74
-    let index = this.tabs.findIndex(v => v.id == tab.id);
75
-    this.tabs.splice(index, 1);
76
-
77
-    // 1,2,3,4,5
78
-    // 关闭高亮的tab,则优先显示后一个tab,如果没有后一个tab,则显示前一个tab
79
-    // 最后一个tab无法关闭
80
-    if(this.tabIndex == index){
81
-      if(this.tabs.length){
82
-        if(this.tabIndex == this.tabs.length){
83
-          this.tabIndex--;
84
-        }
85
-        this.clickTab(this.tabIndex);
86
-      }
87
-    }
88
-  }
89
-
90
-  addTab(tab): void {
91
-    let index = this.tabs.findIndex(v => v.id == tab.id);
92
-
93
-    if(index == -1){
94
-      this.tabs.push(tab);
95
-      this.tabIndex = this.tabs.length - 1;
96
-    }else{
97
-      this.tabIndex = index;
98
-    }
99
-    this.clickTab(this.tabIndex);
100
-  }
101
-
102
-  clickTab(tabIndex){
103
-    console.log('tabIndex:', tabIndex);
104
-    this.tabIndex = tabIndex;
105
-    this.activeSecondMenuId = this.tabs[tabIndex].id;
106
-    console.log('this.tabs[tabIndex]:', this.tabs[tabIndex]);
107
-    console.log(`newStatistics/${this.route.parent.snapshot.routeConfig.path}/${this.tabs[tabIndex].link}`)
108
-    // this.router.navigate([`newStatistics/${this.route.parent.snapshot.routeConfig.path}/${this.tabs[tabIndex].link}`], { replaceUrl: true });
109
-    this.router.navigateByUrl(`newStatistics/${this.route.parent.snapshot.routeConfig.path}/${this.tabs[tabIndex].link}`).finally();
41
+    this.activeSecondMenuLink = data.link;
42
+    this.router.navigateByUrl(`newStatistics/${this.route.parent.snapshot.routeConfig.path}/${data.link}`).finally();
110 43
   }
111 44
 
112
-  //trackBy
113
-  trackById(index, item) {
114
-    return item.id;
45
+  // 回显二级菜单
46
+  selectTabEmit(link){
47
+    this.activeSecondMenuLink = link;
115 48
   }
116 49
 
117 50
   // 全院查询-1|院区查询-2|责任部门查询-3|部门垂直查询-4

+ 2 - 5
src/app/views/new-statistics/maintenance-statistics/maintenance-statistics.module.ts

@@ -1,5 +1,4 @@
1
-import { SimpleReuseStrategy } from './../../../route/simple-reuse-strategy';
2
-import { RouteReuseStrategy } from '@angular/router';
1
+import { CustomTabsModule } from './../components/custom-tabs/custom-tabs.module';
3 2
 import { QueryRangeModule } from './../components/query-range/query-range.module';
4 3
 import { NgModule } from '@angular/core';
5 4
 import { CommonModule } from '@angular/common';
@@ -20,9 +19,7 @@ import { VirtualScrollerModule } from 'ngx-virtual-scroller';
20 19
     ShareModule,
21 20
     VirtualScrollerModule,
22 21
     QueryRangeModule,
23
-  ],
24
-  providers: [
25
-    { provide: RouteReuseStrategy, useClass: SimpleReuseStrategy },
22
+    CustomTabsModule,
26 23
   ],
27 24
 })
28 25
 export class MaintenanceStatisticsModule { }

+ 114 - 0
src/app/views/new-statistics/services/tab.service.ts

@@ -0,0 +1,114 @@
1
+import { Injectable } from '@angular/core';
2
+import { Router, NavigationEnd, ActivatedRouteSnapshot } from '@angular/router';
3
+import { filter } from 'rxjs/operators';
4
+
5
+export interface Tab {
6
+  title: string;
7
+  path: string;
8
+  active: boolean;
9
+  closable: boolean; // 保留该属性用于控制显示逻辑
10
+}
11
+
12
+@Injectable({ providedIn: 'root' })
13
+export class TabService {
14
+  tabs: Tab[] = [];
15
+  currentTab: Tab | null = null;
16
+
17
+  constructor(private router: Router) {
18
+    this.router.events
19
+      .pipe(filter(event => event instanceof NavigationEnd))
20
+      .subscribe((event: NavigationEnd) => {
21
+        this.updateTabs(event.urlAfterRedirects);
22
+      });
23
+  }
24
+
25
+  private updateTabs(url: string) {
26
+    const normalizedUrl = this.getNormalizedUrl(url);
27
+    const existingTab = this.tabs.find(t => t.path === normalizedUrl);
28
+
29
+    if (existingTab) {
30
+      this.tabs.forEach(t => t.active = t.path === url);
31
+      this.currentTab = existingTab;
32
+      return;
33
+    }
34
+
35
+    const title = this.getTitleFromUrl(url);
36
+    this.tabs.forEach(t => t.active = false);
37
+
38
+    const newTab: Tab = {
39
+      title: title,
40
+      path: url,
41
+      active: true,
42
+      closable: true
43
+    };
44
+
45
+    this.tabs.push(newTab);
46
+    this.currentTab = newTab;
47
+  }
48
+
49
+  private getNormalizedUrl(url: string): string {
50
+    // 移除哈希片段和查询参数
51
+    return url.split('#')[0].split('?')[0];
52
+  }
53
+
54
+  public getFullPath(path: string): any[] {
55
+    const queryParams = this.router.parseUrl(path).queryParams;
56
+    const pathWithoutParams = path.split('?')[0];
57
+    return [pathWithoutParams, { queryParams }];
58
+  }
59
+
60
+  closeTab(path: string): void {
61
+    const index = this.tabs.findIndex(t => t.path === path);
62
+    if (index === -1) return;
63
+
64
+    // 最后一个页签不关闭
65
+    if (this.tabs.length === 1) {
66
+      return;
67
+    }
68
+
69
+    // 寻找激活目标
70
+    let newActiveTab: Tab | null = null;
71
+
72
+    // 优先查找右侧页签
73
+    if (index < this.tabs.length - 1) {
74
+      newActiveTab = this.tabs[index + 1];
75
+    }
76
+    // 右侧无则查找左侧
77
+    else if (index > 0) {
78
+      newActiveTab = this.tabs[index - 1];
79
+    }
80
+
81
+    // 执行删除
82
+    this.tabs.splice(index, 1);
83
+
84
+    // 设置新激活页签
85
+    if (newActiveTab) {
86
+      this.currentTab = newActiveTab;
87
+      this.tabs.forEach(t => t.active = t.path === newActiveTab!.path);
88
+      this.router.navigate([newActiveTab.path]);
89
+    }
90
+  }
91
+
92
+  private getTitleFromRoute(route: ActivatedRouteSnapshot): string {
93
+    while (route.firstChild) route = route.firstChild;
94
+    return route.data.title || '未命名标签';
95
+  }
96
+
97
+  private getTitleFromUrl(url: string): string {
98
+    const route = this.router.routerState.snapshot.root;
99
+    return this.getTitleFromRoute(route);
100
+  }
101
+
102
+  closeOtherTabs(keepTab: Tab): void {
103
+    // 强制激活当前右键页签
104
+    this.tabs = this.tabs.filter(t => t.path === keepTab.path);
105
+    keepTab.active = true;
106
+    this.currentTab = keepTab;
107
+
108
+    // 触发路由跳转
109
+    this.router.navigate([keepTab.path], {
110
+      replaceUrl: true
111
+    });
112
+  }
113
+}
114
+