Ver código fonte

折叠工具条正常,按钮正常

qdy 1 mês atrás
pai
commit
d5d5a3ac41

+ 45
- 2
projects/base-core/src/lib/components/sticky-header/sticky-header.component.html Ver arquivo

@@ -1,8 +1,51 @@
1 1
 <div class="sticky-header" role="banner" aria-label="Page header">
2 2
   <div class="toolbar-area">
3
-    <ng-content select="[toolbar]"></ng-content>
3
+    @if (title || buttons.length > 0) {
4
+      <div class="builtin-toolbar">
5
+        @if (title) {
6
+          <div class="title-section">
7
+            <mat-icon class="title-icon">{{ titleIcon }}</mat-icon>
8
+            <h1 class="title-text">{{ title }}</h1>
9
+          </div>
10
+        }
11
+        
12
+        @if (buttons.length > 0) {
13
+          <div class="buttons-area">
14
+            <button *ngFor="let btn of buttons"
15
+                    mat-button
16
+                    [color]="btn.color || 'primary'"
17
+                    [disabled]="btn.disabled"
18
+                    (click)="onButtonClick(btn.name)">
19
+              <mat-icon *ngIf="btn.icon">{{ btn.icon }}</mat-icon>
20
+              <span>{{ btn.title }}</span>
21
+              <mat-spinner *ngIf="btn.loading" diameter="16"></mat-spinner>
22
+            </button>
23
+          </div>
24
+        }
25
+      </div>
26
+    }
27
+    @else {
28
+      <ng-content select="[toolbar]"></ng-content>
29
+    }
4 30
   </div>
31
+  
5 32
   <div class="hint-area">
6
-    <ng-content select="[hint]"></ng-content>
33
+    @if (hintText || dynamicContents.length > 0) {
34
+      <div class="builtin-hint">
35
+        @if (hintText) {
36
+          <div class="hint-text">{{ hintText }}</div>
37
+        }
38
+        
39
+        <div *ngFor="let item of dynamicContents" 
40
+             class="dynamic-content"
41
+             [class]="item.style || 'hint'"
42
+             [class.custom]="item.customClass">
43
+          {{ item.content }}
44
+        </div>
45
+      </div>
46
+    }
47
+    @else {
48
+      <ng-content select="[hint]"></ng-content>
49
+    }
7 50
   </div>
8 51
 </div>

+ 124
- 35
projects/base-core/src/lib/components/sticky-header/sticky-header.component.scss Ver arquivo

@@ -1,35 +1,92 @@
1
-/* Sticky Header 组件样式 - 密封、开箱即用 */
2
-/* 组件分为三部分:.sticky-header(容器)、.toolbar-area(工具条)、.hint-area(提示区) */
1
+// .sticky-header {
2
+//   /* 左右间距 */
3
+//   padding-left: 16px;
4
+//   padding-right: 16px;
5
+// }
6
+/* ==================== 工具条容器 ==================== */
7
+/* 保持原有的容器样式不变,只在内部调整布局 */
8
+.builtin-toolbar {
9
+  display: flex;
10
+  align-items: center;
11
+  width: 100%;
12
+  gap: 16px;
13
+}
3 14
 
4
-/* ==================== 主容器 ==================== */
5
-/* 使用 position: sticky 实现固定效果 */
6
-/* 注意:祖先元素不能有 overflow: hidden、overflow: clip 或 overflow: auto 破坏 sticky 定位 */
7
-.sticky-header {
8
-  position: sticky;
9
-  top: 0;
10
-  z-index: 1000;
11
-  background: white;
12
-  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
15
+/* ==================== 标题区域 ==================== */
16
+/* 标题区域靠左显示,占据可用空间 */
17
+.title-section {
18
+  display: flex;
19
+  align-items: center;
20
+  gap: 8px;
21
+  flex: 1; /* 占据剩余空间 */
22
+  min-width: 0; /* 允许文本截断 */
23
+  
24
+  .title-icon {
25
+    font-size: 20px;
26
+    width: 20px;
27
+    height: 20px;
28
+    color: #666;
29
+    flex-shrink: 0;
30
+  }
31
+  
32
+  .title-text {
33
+    margin: 0;
34
+    font-size: 18px;
35
+    font-weight: 500;
36
+    line-height: 1.2;
37
+    white-space: nowrap;
38
+    overflow: hidden;
39
+    text-overflow: ellipsis;
40
+  }
13 41
 }
14 42
 
15
-/* ==================== 工具条区域 ==================== */
16
-/* 用于放置标题、按钮等主要操作控件 */
17
-/* 使用 min-height 而非固定高度,确保内容灵活 */
43
+/* ==================== 按钮区域 ==================== */
44
+/* 按钮区域靠右显示,不换行 */
45
+.buttons-area {
46
+  display: flex;
47
+  gap: 8px;
48
+  align-items: center;
49
+  flex-shrink: 0; /* 防止按钮区域被压缩 */
50
+  
51
+  button {
52
+    display: flex;
53
+    align-items: center;
54
+    gap: 4px;
55
+    white-space: nowrap; /* 按钮文本不换行 */
56
+    
57
+    mat-spinner {
58
+      margin-left: 4px;
59
+    }
60
+  }
61
+}
62
+
63
+/* ==================== 工具条区域保持原有样式 ==================== */
18 64
 .toolbar-area {
19 65
   min-height: 48px;  /* 最小高度,可根据内容扩展 */
20 66
   display: flex;
21 67
   align-items: center;
22 68
   padding: 0 16px;
69
+  border-left: 1px solid #e0e0e0; /* 边框颜色 */
70
+  border-right: 1px solid #e0e0e0;
23 71
   border-bottom: 1px solid #e0e0e0;
72
+  
73
+  /* 确保内容垂直居中 */
74
+  .builtin-toolbar {
75
+    align-items: center;
76
+  }
24 77
 }
25 78
 
26
-/* ==================== 提示区域 ==================== */
27
-/* 用于放置提示信息、调试信息等次要内容 */
28
-/* 滚动超过阈值时会折叠此区域 */
79
+/* ==================== 提示区域保持原有样式 ==================== */
29 80
 .hint-area {
30
-  height: 60px;  /* 固定高度,用于折叠动画计算 */
81
+  white-space: pre-line; /* 关键:支持 \n 换行符 */
82
+  line-height: 1.5;
83
+  margin: 0;
84
+  /* 移除固定高度,改为自动 */
85
+  display: flex;
86
+  align-items: center; /* 垂直居中 */
87
+  // height: 60px;  /* 固定高度,用于折叠动画计算 */
31 88
   padding: 8px 16px;
32
-  border-bottom: 1px solid #f0f0f0;
89
+  border-bottom: 1px solid #e0e0e0;
33 90
   background: #f8f9fa;
34 91
   overflow: hidden;
35 92
   transition: height 0.3s cubic-bezier(0.4, 0, 0.2, 1),
@@ -37,8 +94,6 @@
37 94
               padding 0.3s cubic-bezier(0.4, 0, 0.2, 1);
38 95
 }
39 96
 
40
-/* ==================== 折叠状态 ==================== */
41
-/* 当滚动超过阈值时应用的样式 */
42 97
 .hint-area.collapsed {
43 98
   height: 0;
44 99
   padding-top: 0;
@@ -46,31 +101,65 @@
46 101
   opacity: 0;
47 102
 }
48 103
 
49
-/* Responsive adjustments */
104
+/* ==================== 响应式调整 ==================== */
50 105
 @media (max-width: 768px) {
51 106
   .toolbar-area {
52
-    height: 40px;
107
+    min-height: 40px;
108
+    padding: 0 12px;
53 109
   }
54 110
   
55 111
   .hint-area {
56 112
     height: 48px;
113
+    padding: 8px 12px;
57 114
   }
58 115
   
59
-  .hint-area.collapsed {
60
-    height: 0;
116
+  .builtin-toolbar {
117
+    gap: 8px;
61 118
   }
62
-}
63
-
64
-/* High contrast mode support */
65
-@media (prefers-contrast: high) {
66
-  .sticky-header {
67
-    border-bottom: 2px solid currentColor;
119
+  
120
+  .title-section {
121
+    .title-text {
122
+      font-size: 16px;
123
+      max-width: 200px; /* 小屏幕时限制标题宽度 */
124
+    }
125
+    
126
+    .title-icon {
127
+      font-size: 18px;
128
+      width: 18px;
129
+      height: 18px;
130
+    }
131
+  }
132
+  
133
+  .buttons-area {
134
+    gap: 4px;
135
+    
136
+    button {
137
+      min-width: auto;
138
+      padding: 0 8px;
139
+      
140
+      span {
141
+        display: inline-block; /* 保持文本显示 */
142
+        font-size: 14px;
143
+      }
144
+    }
68 145
   }
69 146
 }
70 147
 
71
-/* Reduced motion support */
72
-@media (prefers-reduced-motion: reduce) {
73
-  .hint-area {
74
-    transition: none;
148
+/* 超小屏幕的进一步优化 */
149
+@media (max-width: 480px) {
150
+  .title-section {
151
+    .title-text {
152
+      max-width: 150px; /* 更小的标题宽度 */
153
+    }
154
+  }
155
+  
156
+  .buttons-area {
157
+    button {
158
+      padding: 0 4px;
159
+      
160
+      span {
161
+        font-size: 12px;
162
+      }
163
+    }
75 164
   }
76 165
 }

+ 59
- 1
projects/base-core/src/lib/components/sticky-header/sticky-header.component.ts Ver arquivo

@@ -1,10 +1,23 @@
1 1
 import { Component, Input, Output, EventEmitter, OnInit, OnDestroy, ElementRef, AfterViewInit, NgZone } from '@angular/core';
2 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
+export interface StickyHeaderButton {
8
+  title: string;           // 按钮文本
9
+  name: string;           // 按钮标识(用于事件区分)
10
+  icon?: string;          // Material 图标名称
11
+  color?: 'primary' | 'accent' | 'warn'; // 按钮颜色
12
+  disabled?: boolean;     // 禁用状态
13
+  loading?: boolean;      // 加载状态
14
+  action?: () => void;    // 按钮点击处理函数(可选)
15
+}
3 16
 
4 17
 @Component({
5 18
   selector: 'app-sticky-header',
6 19
   standalone: true,
7
-  imports: [CommonModule],
20
+  imports: [CommonModule, MatButtonModule, MatIconModule, MatProgressSpinnerModule],
8 21
   templateUrl: './sticky-header.component.html',
9 22
   styleUrl: './sticky-header.component.scss'
10 23
 })
@@ -18,15 +31,60 @@ export class StickyHeaderComponent implements OnInit, OnDestroy, AfterViewInit {
18 31
   /** Emits scroll ratio (0-1) on scroll */
19 32
   @Output() scrollRatioChange = new EventEmitter<number>();
20 33
 
34
+  /** 标题文本 */
35
+  @Input() title = '';
36
+  /** 标题图标,默认 'description' */
37
+  @Input() titleIcon = 'description';
38
+  /** 提示文本(支持 \\n 换行) */
39
+  @Input() hintText = '';
40
+  /** 按钮配置数组 */
41
+  @Input() buttons: StickyHeaderButton[] = [];
42
+  /** 按钮点击事件 */
43
+  @Output() buttonAction = new EventEmitter<string>();
44
+
21 45
   private scrollListener?: () => void;
22 46
   private scrollContainerElement?: HTMLElement | Window;
23 47
   private isCollapsed = false;
48
+  /** 动态添加的内容(内部存储) */
49
+  private _dynamicContents: Array<{id: number, content: string, style?: string, customClass?: string}> = [];
50
+
51
+  /** 获取动态添加的内容(只读) */
52
+  get dynamicContents() {
53
+    return this._dynamicContents;
54
+  }
24 55
 
25 56
   constructor(
26 57
     private elementRef: ElementRef,
27 58
     private ngZone: NgZone
28 59
   ) {}
29 60
 
61
+  /** 按钮点击处理 */
62
+  onButtonClick(name: string) {
63
+    const button = this.buttons.find(b => b.name === name);
64
+    if (button && !button.disabled && !button.loading) {
65
+      // 优先执行按钮的 action 函数(如果存在)
66
+      if (button.action) {
67
+        button.action();
68
+      }
69
+      // 同时发射事件,保持向后兼容
70
+      this.buttonAction.emit(name);
71
+    }
72
+  }
73
+
74
+  /** 动态添加内容 */
75
+  add(index: number, content: string, style?: string, customClass?: string): void {
76
+    const existingIndex = this._dynamicContents.findIndex(item => item.id === index);
77
+    const newItem = { id: index, content, style, customClass };
78
+    
79
+    if (existingIndex >= 0) {
80
+      this._dynamicContents[existingIndex] = newItem;
81
+    } else {
82
+      this._dynamicContents.push(newItem);
83
+      // 按id排序
84
+      this._dynamicContents.sort((a, b) => a.id - b.id);
85
+    }
86
+  }
87
+
30 88
   ngOnInit() {}
31 89
 
32 90
   ngAfterViewInit() {

Carregando…
Cancelar
Salvar