Browse Source

tree左边样式ok锁定

qdy 1 month ago
commit
a02e5b3b2e

+ 69
- 0
web/src/app/components/sticky-header/sticky-header.component.html View File

@@ -0,0 +1,69 @@
1
+<div class="sticky-header sticky top-0 z-10 transition-all duration-300 ease-in-out" [class.locked]="isLocked" [class.compact]="isCompact" [ngStyle]="isLocked ? {
2
+  width: headerWidth ? (typeof headerWidth === 'number' ? headerWidth + 'px' : headerWidth) : null,
3
+  left: headerLeft ? (typeof headerLeft === 'number' ? headerLeft + 'px' : headerLeft) : null
4
+} : null">
5
+  <!-- 主标题区域 -->
6
+  <div class="header-content bg-white rounded-lg border border-gray-200 shadow-sm transition-all duration-300">
7
+    <div class="flex justify-between items-center p-4 transition-all duration-300">
8
+      <h1 class="header-title text-2xl font-bold transition-all duration-300">{{ title }}</h1>
9
+      <button 
10
+        mat-raised-button 
11
+        [color]="buttonColor" 
12
+        (click)="onButtonClick()" 
13
+        [disabled]="disabled || loading"
14
+        class="flex items-center gap-2"
15
+      >
16
+        <mat-icon *ngIf="!loading">{{ buttonIcon }}</mat-icon>
17
+        <mat-progress-spinner 
18
+          *ngIf="loading" 
19
+          diameter="20" 
20
+          mode="indeterminate" 
21
+          class="inline-block"
22
+        ></mat-progress-spinner>
23
+        {{ loading ? '处理中...' : buttonText }}
24
+       </button>
25
+    </div>
26
+    
27
+    <!-- 提示信息 -->
28
+    @if (hintText) {
29
+      <div class="hint-section border-t border-gray-100 p-3 bg-blue-50/30 transition-all duration-300 overflow-hidden">
30
+        <p class="text-sm text-gray-600 m-0 transition-all duration-300">{{ hintText }}</p>
31
+      </div>
32
+    }
33
+    
34
+    <!-- 调试信息区域 -->
35
+    @if (showDebugInfo && (debugDataSource || debugRegisterUrl || debugListUrl)) {
36
+      <div class="debug-section border-t border-gray-100 overflow-hidden">
37
+        <!-- 数据源信息 -->
38
+        @if (debugDataSource) {
39
+          <div class="debug-info text-xs text-gray-500 p-2 bg-gray-50 transition-all duration-300">
40
+            <div class="flex gap-4 flex-wrap transition-all duration-300">
41
+              <span>数据源: <span class="font-mono">{{ debugDataSource }}</span></span>
42
+              <span>记录条数: <span class="font-mono">{{ debugRecordCount }}</span></span>
43
+              <span>更新时间: <span class="font-mono">{{ debugLastUpdated }}</span></span>
44
+              @if (debugUseMockData) {
45
+                <span class="text-amber-600 font-medium">⚠ 使用模拟数据</span>
46
+              } @else {
47
+                <span class="text-green-600 font-medium">✓ 使用API数据</span>
48
+              }
49
+            </div>
50
+          </div>
51
+        }
52
+        
53
+        <!-- API URL信息 -->
54
+        @if (debugRegisterUrl || debugListUrl) {
55
+          <div class="debug-urls text-xs text-gray-500 p-2 bg-gray-50 border-t border-gray-100 transition-all duration-300">
56
+            <div class="flex flex-col gap-1 transition-all duration-300">
57
+              @if (debugRegisterUrl) {
58
+                <span>注册API: <span class="font-mono">{{ debugRegisterUrl || '未调用' }}</span></span>
59
+              }
60
+              @if (debugListUrl) {
61
+                <span>列表API: <span class="font-mono">{{ debugListUrl || '未调用' }}</span></span>
62
+              }
63
+            </div>
64
+          </div>
65
+        }
66
+      </div>
67
+    }
68
+  </div>
69
+</div>

+ 162
- 0
web/src/app/components/sticky-header/sticky-header.component.scss View File

@@ -0,0 +1,162 @@
1
+.sticky-header {
2
+  position: sticky;
3
+  top: 0;
4
+  z-index: 1000; /* 进一步提高z-index确保在最顶部 */
5
+  transition: all 0.3s ease-in-out;
6
+  margin-bottom: 1.5rem; /* 为内容区域提供间距 */
7
+  align-self: flex-start; /* 确保在flex容器中sticky生效 */
8
+  background-color: white; /* 确保不透明,防止内容透出 */
9
+  /* 创建新的层叠上下文,确保z-index生效 */
10
+  transform: translateZ(0);
11
+  will-change: transform;
12
+  /* 轻微的内阴影,增强立体感 */
13
+  box-shadow: inset 0 1px 0 0 rgba(255, 255, 255, 0.8);
14
+
15
+  .header-content {
16
+    background: #f8fafc; /* 简洁的灰白色背景 */
17
+    border-radius: 0.5rem;
18
+    border: 1px solid #e2e8f0; /* 柔和的边框 */
19
+    box-shadow: 
20
+      inset 0 2px 4px 0 rgba(0, 0, 0, 0.06), /* 轻微内阴影,创造凹陷感 */
21
+      inset 0 0 0 1px rgba(255, 255, 255, 0.9), /* 内边缘高光,增强立体感 */
22
+      0 1px 2px 0 rgba(0, 0, 0, 0.05); /* 轻微外阴影,保持与页面融合 */
23
+    transition: all 0.3s ease-in-out;
24
+    /* 移除 overflow: hidden,防止内容裁剪 */
25
+  }
26
+
27
+  .header-title {
28
+    font-size: 1.75rem; /* text-3xl */
29
+    font-weight: 800;
30
+    transition: all 0.3s ease-in-out;
31
+    margin: 0;
32
+    color: #1e293b; /* text-slate-800 */
33
+    text-shadow: 0 1px 2px rgba(255, 255, 255, 0.8); /* 轻微文字阴影,增强可读性 */
34
+  }
35
+
36
+  .debug-section {
37
+    transition: all 0.3s ease-in-out;
38
+    max-height: 200px;
39
+    opacity: 1;
40
+    
41
+    .debug-info, .debug-urls {
42
+      &:not(:last-child) {
43
+        border-bottom: 1px solid #e2e8f0; /* border-slate-200 */
44
+      }
45
+    }
46
+  }
47
+
48
+  .hint-section {
49
+    transition: all 0.3s ease-in-out;
50
+    max-height: 100px;
51
+    opacity: 1;
52
+  }
53
+
54
+  /* 锁定状态 - 固定在视口顶部,保持原大小 */
55
+  &.locked {
56
+    position: fixed;
57
+    top: 0;
58
+    z-index: 1100; /* 提高z-index确保在最顶层 */
59
+    margin-bottom: 0; /* 移除margin,因为现在是fixed定位 */
60
+    transform: none; /* 移除transform层叠上下文 */
61
+    will-change: auto;
62
+    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1); /* 外阴影,表示浮动状态 */
63
+    background-color: white; /* 确保背景不透明 */
64
+    
65
+    .header-content {
66
+      border-radius: 0.5rem;
67
+      box-shadow: 
68
+        inset 0 2px 4px 0 rgba(0, 0, 0, 0.06),
69
+        inset 0 0 0 1px rgba(255, 255, 255, 0.9),
70
+        0 2px 4px 0 rgba(0, 0, 0, 0.1); /* 更强外阴影,强调固定状态 */
71
+      background: #f1f5f9;
72
+      border: 1px solid #cbd5e1;
73
+      margin: 1rem; /* 添加外边距,避免贴边 */
74
+      /* 保持原padding大小 */
75
+    }
76
+
77
+    .header-title {
78
+      /* 保持原字体大小:1.75rem */
79
+      font-weight: 800;
80
+      color: #1e293b;
81
+      text-shadow: 0 1px 2px rgba(255, 255, 255, 0.8);
82
+    }
83
+
84
+    .hint-section {
85
+      max-height: 0;
86
+      opacity: 0;
87
+      padding-top: 0;
88
+      padding-bottom: 0;
89
+      border: none;
90
+      
91
+      p {
92
+        font-size: 0.8125rem;
93
+        opacity: 0;
94
+      }
95
+    }
96
+    
97
+    .debug-section {
98
+      max-height: 0;
99
+      opacity: 0;
100
+      
101
+      .debug-info, .debug-urls {
102
+        padding-top: 0;
103
+        padding-bottom: 0;
104
+        font-size: 0.6875rem;
105
+        opacity: 0;
106
+        border: none;
107
+      }
108
+    }
109
+  }
110
+
111
+  /* 缩小状态 - 在锁定基础上进一步缩小 */
112
+  &.compact {
113
+    .header-content {
114
+      border-radius: 0.375rem;
115
+      box-shadow: 
116
+        inset 0 3px 6px 0 rgba(0, 0, 0, 0.08), /* 更强的内阴影,增强凹陷感 */
117
+        inset 0 0 0 1px rgba(255, 255, 255, 0.8), /* 内边缘高光 */
118
+        0 1px 3px 0 rgba(0, 0, 0, 0.1); /* 轻微外阴影 */
119
+      background: #f1f5f9; /* 稍微深一点的背景,强调状态变化 */
120
+      border: 1px solid #cbd5e1; /* 保持一致的边框 */
121
+      
122
+      > div:first-child {
123
+        padding-top: 0.375rem;
124
+        padding-bottom: 0.375rem;
125
+      }
126
+    }
127
+
128
+    .header-title {
129
+      font-size: 1.125rem; /* 更小一点 */
130
+      font-weight: 800;
131
+      color: #0f172a; /* 更深的文字颜色,增强可读性 */
132
+      text-shadow: 0 1px 2px rgba(255, 255, 255, 0.9); /* 保持文字阴影 */
133
+      letter-spacing: -0.025em; /* 紧凑的字母间距 */
134
+    }
135
+
136
+    .hint-section {
137
+      max-height: 0;
138
+      opacity: 0;
139
+      padding-top: 0;
140
+      padding-bottom: 0;
141
+      border: none;
142
+      
143
+      p {
144
+        font-size: 0.8125rem; /* 比text-sm小一点 */
145
+        opacity: 0;
146
+      }
147
+    }
148
+    
149
+    .debug-section {
150
+      max-height: 0;
151
+      opacity: 0;
152
+      
153
+      .debug-info, .debug-urls {
154
+        padding-top: 0;
155
+        padding-bottom: 0;
156
+        font-size: 0.6875rem; /* 比text-xs更小 */
157
+        opacity: 0;
158
+        border: none;
159
+      }
160
+    }
161
+  }
162
+}

+ 83
- 0
web/src/app/components/sticky-header/sticky-header.component.ts View File

@@ -0,0 +1,83 @@
1
+import { Component, Input, Output, EventEmitter } from '@angular/core';
2
+import { CommonModule } from '@angular/common';
3
+import { MatButtonModule } from '@angular/material/button';
4
+import { MatIconModule } from '@angular/material/icon';
5
+import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
6
+
7
+@Component({
8
+  selector: 'app-sticky-header',
9
+  standalone: true,
10
+  imports: [CommonModule, MatButtonModule, MatIconModule, MatProgressSpinnerModule],
11
+  templateUrl: './sticky-header.component.html',
12
+  styleUrl: './sticky-header.component.scss'
13
+})
14
+export class StickyHeaderComponent {
15
+  /** 标题文本 */
16
+  @Input() title = '';
17
+  
18
+  /** 按钮文本 */
19
+  @Input() buttonText = '操作';
20
+  
21
+  /** 按钮图标 */
22
+  @Input() buttonIcon = 'add';
23
+  
24
+  /** 按钮颜色,默认primary */
25
+  @Input() buttonColor: 'primary' | 'accent' | 'warn' = 'primary';
26
+  
27
+  /** 是否禁用按钮 */
28
+  @Input() disabled = false;
29
+  
30
+  /** 是否显示加载状态 */
31
+  @Input() loading = false;
32
+  
33
+  /** 滚动阈值(像素),默认为5 */
34
+  @Input() scrollThreshold = 5;
35
+
36
+  /** 是否显示调试信息框 */
37
+  @Input() showDebugInfo = false;
38
+  
39
+  /** 调试信息数据源 */
40
+  @Input() debugDataSource = '';
41
+  
42
+  /** 调试信息记录数 */
43
+  @Input() debugRecordCount = 0;
44
+  
45
+  /** 调试信息更新时间 */
46
+  @Input() debugLastUpdated = '';
47
+  
48
+  /** 调试信息是否使用模拟数据 */
49
+  @Input() debugUseMockData = false;
50
+  
51
+  /** 调试信息注册API URL */
52
+  @Input() debugRegisterUrl = '';
53
+  
54
+  /** 调试信息列表API URL */
55
+  @Input() debugListUrl = '';
56
+
57
+  /** 提示文本,显示在标题下方 */
58
+  @Input() hintText = '';
59
+
60
+  /** 是否已滚动(由父组件控制,向后兼容) */
61
+  @Input() isScrolled = false;
62
+
63
+  /** 是否锁定在顶部(到达滚动容器顶部) */
64
+  @Input() isLocked = false;
65
+
66
+  /** 是否缩小状态(锁定后进一步缩小) */
67
+  @Input() isCompact = false;
68
+
69
+  /** 锁定时的宽度(像素或百分比) */
70
+  @Input() headerWidth: string | number | null = null;
71
+
72
+  /** 锁定时的左侧偏移(像素) */
73
+  @Input() headerLeft: string | number | null = null;
74
+
75
+  /** 按钮点击事件 */
76
+  @Output() buttonClick = new EventEmitter<void>();
77
+
78
+  onButtonClick() {
79
+    if (!this.disabled && !this.loading) {
80
+      this.buttonClick.emit();
81
+    }
82
+  }
83
+}

+ 24
- 34
web/src/app/pages/service-register-config/service-register-config.component.html View File

@@ -1,40 +1,30 @@
1 1
 <div class="h-full flex flex-col p-4 min-w-0">
2
-  <div class="flex justify-between items-center mb-4">
3
-    <h1 class="text-2xl font-bold">{{ title }}</h1>
4
-    <button mat-raised-button color="primary" (click)="onRegister()" [disabled]="registering">
5
-      <mat-icon *ngIf="!registering">add</mat-icon>
6
-      <mat-progress-spinner *ngIf="registering" diameter="20" mode="indeterminate" class="inline-block"></mat-progress-spinner>
7
-      {{ registering ? '注册中...' : '注册' }}
8
-    </button>
9
-  </div>
2
+  <!-- 顶部标题区域 -->
3
+  <app-sticky-header
4
+    [title]="title"
5
+    [hintText]="hintText"
6
+    [isScrolled]="isScrolled"
7
+    [isLocked]="isLocked"
8
+    [isCompact]="isCompact"
9
+    [buttonText]="'注册'"
10
+    [buttonIcon]="'add'"
11
+    [buttonColor]="'primary'"
12
+    [disabled]="registering"
13
+    [loading]="registering"
14
+    [showDebugInfo]="true"
15
+    [debugDataSource]="debugInfo.dataSource"
16
+    [debugRecordCount]="debugInfo.recordCount"
17
+    [debugLastUpdated]="debugInfo.lastUpdated"
18
+    [debugUseMockData]="debugInfo.useMockData"
19
+    [debugRegisterUrl]="lastRegisterUrl"
20
+    [debugListUrl]="lastListUrl"
21
+    [headerWidth]="headerWidth"
22
+    [headerLeft]="headerLeft"
23
+    (buttonClick)="onRegister()"
24
+  ></app-sticky-header>
10 25
    
11
-   <!-- 调试信息 -->
12
-   @if (debugInfo.dataSource) {
13
-     <div class="debug-info text-xs text-gray-500 mt-1 mb-2 p-2 bg-gray-50 rounded border border-gray-200">
14
-       <div class="flex gap-4">
15
-         <span>数据源: <span class="font-mono">{{ debugInfo.dataSource }}</span></span>
16
-         <span>记录条数: <span class="font-mono">{{ debugInfo.recordCount }}</span></span>
17
-         <span>更新时间: <span class="font-mono">{{ debugInfo.lastUpdated }}</span></span>
18
-         @if (debugInfo.useMockData) {
19
-           <span class="text-amber-600 font-medium">⚠ 使用模拟数据</span>
20
-         } @else {
21
-           <span class="text-green-600 font-medium">✓ 使用API数据</span>
22
-         }
23
-        </div>
24
-      </div>
25
-      <div class="debug-urls text-xs text-gray-500 mt-1 mb-2 p-2 bg-gray-50 rounded border border-gray-200">
26
-        <div class="flex flex-col gap-1">
27
-          <span>注册API: <span class="font-mono">{{ lastRegisterUrl || '未调用' }}</span></span>
28
-          <span>列表API: <span class="font-mono">{{ lastListUrl || '未调用' }}</span></span>
29
-        </div>
30
-      </div>
31
-    }
32
-   
33
-   <mat-card class="flex-1">
26
+    <mat-card #matCard class="flex-1">
34 27
     <mat-card-content>
35
-      <div class="mb-4">
36
-        <p class="text-gray-600">点击"注册"按钮将同步所有配置元信息到数据库,并显示配置列表。</p>
37
-      </div>
38 28
 
39 29
       <div *ngIf="configMetaList.length > 0" class="overflow-auto">
40 30
         <table mat-table [dataSource]="configMetaList" class="w-full">

+ 209
- 6
web/src/app/pages/service-register-config/service-register-config.component.ts View File

@@ -1,8 +1,8 @@
1
-import { Component, OnInit } from '@angular/core';
1
+import { Component, OnInit, HostListener, ChangeDetectorRef, AfterViewInit, ElementRef, Renderer2, OnDestroy, ViewChild } from '@angular/core';
2
+import { StickyHeaderComponent } from '../../components/sticky-header/sticky-header.component';
2 3
 import { CommonModule } from '@angular/common';
3 4
 import { MatCard, MatCardContent } from '@angular/material/card';
4
-import { MatButton } from '@angular/material/button';
5
-import { MatIcon } from '@angular/material/icon';
5
+
6 6
 import { MatTableModule } from '@angular/material/table';
7 7
 import { MatProgressSpinner } from '@angular/material/progress-spinner';
8 8
 import { ConfigMetaService } from '../../services/config-meta.service';
@@ -13,16 +13,26 @@ import { HttpErrorResponse } from '@angular/common/http';
13 13
 
14 14
 @Component({
15 15
   selector: 'app-service-register-config',
16
-  imports: [CommonModule, MatCard, MatCardContent, MatButton, MatIcon, MatTableModule, MatProgressSpinner],
16
+  imports: [CommonModule, MatCard, MatCardContent, MatTableModule, MatProgressSpinner, StickyHeaderComponent],
17 17
   templateUrl: './service-register-config.component.html',
18 18
   styleUrl: './service-register-config.component.scss'
19 19
 })
20
-export class ServiceRegisterConfigComponent implements OnInit {
20
+export class ServiceRegisterConfigComponent implements OnInit, AfterViewInit, OnDestroy {
21 21
   title = '注册服务配置';
22
+  hintText = '点击"注册"按钮将同步所有配置元信息到数据库,并显示配置列表。';
22 23
   displayedColumns: string[] = ['configName', 'fieldName', 'fieldType', 'yamlName', 'fieldDesc'];
23 24
   configMetaList: ConfigMeta[] = [];
24 25
   loading = false;
25 26
   registering = false;
27
+  isScrolled = false; // 向后兼容
28
+  isLocked = false;    // 是否锁定在顶部
29
+  isCompact = false;   // 是否缩小状态
30
+  scrollThreshold = 2; // 锁定阈值
31
+  compactThreshold = 50; // 缩小阈值(锁定后进一步滚动多少像素开始缩小)
32
+
33
+  private scrollContainer: HTMLElement | Window = window;
34
+  private scrollListener: (() => void) | null = null;
35
+  private rafId: number | null = null;
26 36
 
27 37
   debugInfo = {
28 38
     dataSource: '',
@@ -31,19 +41,212 @@ export class ServiceRegisterConfigComponent implements OnInit {
31 41
     useMockData: false
32 42
   };
33 43
 
44
+  @ViewChild('matCard', { read: ElementRef }) matCardRef!: ElementRef;
45
+  headerWidth: number | null = null;
46
+  headerLeft: number | null = null;
47
+  private resizeListener: (() => void) | null = null;
48
+
34 49
   lastRegisterUrl = '';
35 50
   lastListUrl = '';
36 51
 
37 52
   constructor(
38 53
     private configMetaService: ConfigMetaService,
39 54
     private configService: ConfigService,
40
-    private toastService: ToastService
55
+    private toastService: ToastService,
56
+    private cdRef: ChangeDetectorRef,
57
+    private elementRef: ElementRef,
58
+    private renderer: Renderer2
41 59
   ) {}
42 60
 
43 61
   ngOnInit() {
44 62
     this.loadConfigMeta();
45 63
   }
46 64
 
65
+  ngAfterViewInit() {
66
+    this.findScrollContainer();
67
+    this.setupScrollListener();
68
+    this.checkScroll();
69
+    
70
+    // 添加窗口resize监听器
71
+    this.resizeListener = () => this.onResize();
72
+    window.addEventListener('resize', this.resizeListener, { passive: true });
73
+  }
74
+
75
+  private onResize() {
76
+    // 只在锁定状态时更新尺寸
77
+    if (this.isLocked && this.matCardRef?.nativeElement) {
78
+      console.log('窗口大小变化,更新标题栏尺寸');
79
+      this.updateHeaderDimensions();
80
+    }
81
+  }
82
+
83
+  ngOnDestroy() {
84
+    this.cleanupScrollListener();
85
+    
86
+    // 清理resize监听器
87
+    if (this.resizeListener) {
88
+      window.removeEventListener('resize', this.resizeListener);
89
+      this.resizeListener = null;
90
+    }
91
+  }
92
+
93
+  @HostListener('window:scroll', [])
94
+  onWindowScroll() {
95
+    // 保留window滚动监听作为备用
96
+    if (this.scrollContainer === window) {
97
+      this.onScroll();
98
+    }
99
+  }
100
+
101
+  private findScrollContainer() {
102
+    // 向上查找最近的滚动容器
103
+    let element: HTMLElement | null = this.elementRef.nativeElement.parentElement;
104
+    
105
+    while (element && element !== document.body) {
106
+      const style = getComputedStyle(element);
107
+      const hasOverflow = style.overflow === 'auto' || style.overflow === 'scroll' || 
108
+                          style.overflowY === 'auto' || style.overflowY === 'scroll';
109
+      
110
+      if (hasOverflow) {
111
+        console.log('找到滚动容器:', element, 'clientHeight:', element.clientHeight, 'scrollHeight:', element.scrollHeight);
112
+        this.scrollContainer = element;
113
+        return;
114
+      }
115
+      
116
+      element = element.parentElement;
117
+    }
118
+    
119
+    // 未找到合适的容器,使用window
120
+    console.log('未找到滚动容器,使用window');
121
+    this.scrollContainer = window;
122
+  }
123
+  
124
+  private setupScrollListener() {
125
+    if (this.scrollContainer === window) {
126
+      this.scrollListener = () => this.onScroll();
127
+      window.addEventListener('scroll', this.scrollListener, { passive: true });
128
+    } else {
129
+      this.scrollListener = () => this.onScroll();
130
+      (this.scrollContainer as HTMLElement).addEventListener('scroll', this.scrollListener, { passive: true });
131
+    }
132
+  }
133
+  
134
+  private cleanupScrollListener() {
135
+    if (this.scrollListener) {
136
+      if (this.scrollContainer === window) {
137
+        window.removeEventListener('scroll', this.scrollListener);
138
+      } else {
139
+        (this.scrollContainer as HTMLElement).removeEventListener('scroll', this.scrollListener);
140
+      }
141
+      this.scrollListener = null;
142
+    }
143
+    
144
+    if (this.rafId !== null) {
145
+      cancelAnimationFrame(this.rafId);
146
+      this.rafId = null;
147
+    }
148
+  }
149
+  
150
+  private onScroll() {
151
+    if (this.rafId !== null) {
152
+      cancelAnimationFrame(this.rafId);
153
+    }
154
+    
155
+    this.rafId = requestAnimationFrame(() => {
156
+      this.checkScroll();
157
+    });
158
+  }
159
+
160
+  private updateHeaderDimensions() {
161
+    if (!this.matCardRef?.nativeElement) return;
162
+    
163
+    const matCard = this.matCardRef.nativeElement;
164
+    const rect = matCard.getBoundingClientRect();
165
+    
166
+    // 计算mat-card在视口中的位置和宽度
167
+    this.headerWidth = rect.width;
168
+    this.headerLeft = rect.left;
169
+    
170
+    console.log('更新标题栏尺寸:', { 
171
+      width: this.headerWidth, 
172
+      left: this.headerLeft,
173
+      matCardRect: rect 
174
+    });
175
+    
176
+    this.cdRef.detectChanges();
177
+  }
178
+
179
+  private checkScroll() {
180
+    if (!this.scrollContainer) return;
181
+    
182
+    let scrollTop = 0;
183
+    let containerType = 'unknown';
184
+    
185
+    if (this.scrollContainer === window) {
186
+      scrollTop = window.scrollY || document.documentElement.scrollTop;
187
+      containerType = 'window';
188
+    } else {
189
+      scrollTop = (this.scrollContainer as HTMLElement).scrollTop;
190
+      containerType = 'element';
191
+    }
192
+    
193
+    // 向后兼容:保持isScrolled逻辑
194
+    const scrolled = scrollTop > this.scrollThreshold;
195
+    
196
+
197
+    
198
+    // 阶段1: 检测是否锁定(开始滚动就锁定)
199
+    const shouldLock = scrollTop > 0;
200
+    
201
+    // 阶段2: 检测是否缩小(锁定后继续滚动)
202
+    const shouldCompact = shouldLock && scrollTop > this.compactThreshold;
203
+    
204
+    console.log('滚动检测:', { 
205
+      container: containerType, 
206
+      scrollTop, 
207
+      scrollThreshold: this.scrollThreshold,
208
+      compactThreshold: this.compactThreshold,
209
+      shouldLock,
210
+      shouldCompact,
211
+      scrolled, // 向后兼容
212
+      isLocked: this.isLocked,
213
+      isCompact: this.isCompact
214
+    });
215
+    
216
+    // 更新状态
217
+    let needsUpdate = false;
218
+    
219
+    if (this.isScrolled !== scrolled) {
220
+      this.isScrolled = scrolled;
221
+      needsUpdate = true;
222
+    }
223
+    
224
+    if (this.isLocked !== shouldLock) {
225
+      this.isLocked = shouldLock;
226
+      needsUpdate = true;
227
+      console.log('锁定状态变化:', this.isLocked);
228
+      
229
+      if (this.isLocked) {
230
+        // 锁定状态:计算并更新标题栏尺寸
231
+        this.updateHeaderDimensions();
232
+      } else {
233
+        // 解锁状态:清除尺寸,让标题栏恢复原始定位
234
+        this.headerWidth = null;
235
+        this.headerLeft = null;
236
+      }
237
+    }
238
+    
239
+    if (this.isCompact !== shouldCompact) {
240
+      this.isCompact = shouldCompact;
241
+      needsUpdate = true;
242
+      console.log('缩小状态变化:', this.isCompact);
243
+    }
244
+    
245
+    if (needsUpdate) {
246
+      this.cdRef.detectChanges();
247
+    }
248
+  }
249
+
47 250
   onRegister() {
48 251
     this.lastRegisterUrl = this.configMetaService.getInitUrl();
49 252
     console.debug('[注册] 请求URL:', this.lastRegisterUrl);

Loading…
Cancel
Save