Kaynağa Gözat

feet:customer22

徐福静0235668 16 saat önce
ebeveyn
işleme
0d3f7b651b

+ 85 - 24
src/app/shared/components/global-prompt/global-prompt.component.html

@@ -1,31 +1,92 @@
-@if (visible) {
-  <!-- 全屏遮罩模式 -->
-  @if (mode === 'fullscreen') {
-    <div class="gp-backdrop" role="presentation" (click)="onClose()"></div>
-    <div class="gp-modal" role="dialog" aria-modal="true" aria-label="全局提示">
+<div class="gp-container" [class]="customClass" *ngIf="visible">
+  <!-- 全屏模式 -->
+  <div *ngIf="mode === 'fullscreen'" class="gp-backdrop" [class.gp-backdrop-visible]="backdrop" (click)="onBackdropClick()">
+    <div class="gp-modal" [ngClass]="getSizeClass()" (click)="$event.stopPropagation()">
+      <div class="gp-header" *ngIf="title">
+        <h3 class="gp-title">{{ title }}</h3>
+        <button *ngIf="showCloseButton" class="gp-close-btn" (click)="onClose()" aria-label="关闭">
+          <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
+            <path d="M12 4L4 12M4 4L12 12" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
+          </svg>
+        </button>
+      </div>
+      
+      <div class="gp-content">
+        <div class="gp-icon" [ngClass]="getIconColorClass()">
+          <svg *ngIf="icon === 'success'" width="24" height="24" viewBox="0 0 24 24" fill="none">
+            <path d="M9 12L11 14L15 10M21 12C21 16.9706 16.9706 21 12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3C16.9706 3 21 7.02944 21 12Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+          </svg>
+          <svg *ngIf="icon === 'info'" width="24" height="24" viewBox="0 0 24 24" fill="none">
+            <path d="M12 16V12M12 8H12.01M22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+          </svg>
+          <svg *ngIf="icon === 'warning'" width="24" height="24" viewBox="0 0 24 24" fill="none">
+            <path d="M12 9V13M12 17H12.01M10.29 3.86L1.82 18A2 2 0 003.54 21H20.46A2 2 0 0022.18 18L13.71 3.86A2 2 0 0010.29 3.86Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+          </svg>
+          <svg *ngIf="icon === 'error'" width="24" height="24" viewBox="0 0 24 24" fill="none">
+            <path d="M12 8V12M12 16H12.01M22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+          </svg>
+        </div>
+        
+        <div class="gp-message" *ngIf="message">{{ message }}</div>
+      </div>
+      
+      <div class="gp-actions" *ngIf="showActionButton">
+        <button class="gp-action-btn" (click)="onAction()">{{ actionButtonText }}</button>
+      </div>
+    </div>
+  </div>
+
+  <!-- 模态框模式 -->
+  <div *ngIf="mode === 'modal'" class="gp-backdrop gp-modal-backdrop" [class.gp-backdrop-visible]="backdrop" (click)="onBackdropClick()">
+    <div class="gp-modal gp-modal-dialog" [ngClass]="getSizeClass()" (click)="$event.stopPropagation()">
       <div class="gp-header">
-        <div class="gp-icon" [class.success]="icon==='success'" [class.info]="icon==='info'" [class.warning]="icon==='warning'"></div>
-        <div class="gp-title">{{ title }}</div>
-        <button class="gp-close" (click)="onClose()" aria-label="关闭">×</button>
+        <h3 class="gp-title" *ngIf="title">{{ title }}</h3>
+        <button *ngIf="showCloseButton" class="gp-close-btn" (click)="onClose()" aria-label="关闭">
+          <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
+            <path d="M12 4L4 12M4 4L12 12" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
+          </svg>
+        </button>
+      </div>
+      
+      <div class="gp-content gp-modal-content">
+        <div class="gp-message" *ngIf="message">{{ message }}</div>
       </div>
-      <div class="gp-content">{{ message }}</div>
-      <div class="gp-actions">
-        <button class="btn-primary" (click)="onAction()">确定</button>
+      
+      <div class="gp-actions" *ngIf="showActionButton || showCloseButton">
+        <button *ngIf="showActionButton" class="gp-action-btn gp-btn-primary" (click)="onAction()">{{ actionButtonText }}</button>
+        <button *ngIf="showCloseButton" class="gp-action-btn gp-btn-secondary" (click)="onClose()">取消</button>
       </div>
     </div>
-  }
+  </div>
 
-  <!-- 角落提示模式(无遮罩) -->
-  @if (mode === 'corner') {
-    <div class="gp-toast" [class.top-right]="position==='top-right'" [class.bottom-right]="position==='bottom-right'" role="status" aria-live="polite">
-      <div class="gp-toast-inner">
-        <div class="gp-icon" [class.success]="icon==='success'" [class.info]="icon==='info'" [class.warning]="icon==='warning'"></div>
-        <div class="gp-texts">
-          <div class="gp-title">{{ title }}</div>
-          <div class="gp-content">{{ message }}</div>
-        </div>
-        <button class="gp-close" (click)="onClose()" aria-label="关闭">×</button>
+  <!-- 角落提示模式 -->
+  <div *ngIf="mode === 'corner'" class="gp-toast" [ngClass]="[getSizeClass(), getPositionClass()]">
+    <div class="gp-toast-content">
+      <div class="gp-icon gp-toast-icon" [ngClass]="getIconColorClass()">
+        <svg *ngIf="icon === 'success'" width="20" height="20" viewBox="0 0 24 24" fill="none">
+          <path d="M9 12L11 14L15 10M21 12C21 16.9706 16.9706 21 12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3C16.9706 3 21 7.02944 21 12Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+        </svg>
+        <svg *ngIf="icon === 'info'" width="20" height="20" viewBox="0 0 24 24" fill="none">
+          <path d="M12 16V12M12 8H12.01M22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+        </svg>
+        <svg *ngIf="icon === 'warning'" width="20" height="20" viewBox="0 0 24 24" fill="none">
+          <path d="M12 9V13M12 17H12.01M10.29 3.86L1.82 18A2 2 0 003.54 21H20.46A2 2 0 0022.18 18L13.71 3.86A2 2 0 0010.29 3.86Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+        </svg>
+        <svg *ngIf="icon === 'error'" width="20" height="20" viewBox="0 0 24 24" fill="none">
+          <path d="M12 8V12M12 16H12.01M22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+        </svg>
+      </div>
+      
+      <div class="gp-toast-text">
+        <div class="gp-toast-title" *ngIf="title">{{ title }}</div>
+        <div class="gp-toast-message" *ngIf="message">{{ message }}</div>
       </div>
+      
+      <button *ngIf="showCloseButton" class="gp-toast-close" (click)="onClose()" aria-label="关闭">
+        <svg width="14" height="14" viewBox="0 0 16 16" fill="none">
+          <path d="M12 4L4 12M4 4L12 12" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
+        </svg>
+      </button>
     </div>
-  }
-}
+  </div>
+</div>

+ 300 - 98
src/app/shared/components/global-prompt/global-prompt.component.scss

@@ -1,113 +1,315 @@
-// 层级与断点
-$z-index-backdrop: 9990;
-$z-index-modal: 9991;
-$z-index-toast: 1050;
-
-$mobile: 768px;
-$tablet: 1024px;
-
-.gp-backdrop {
-  position: fixed;
-  inset: 0;
-  background: rgba(0,0,0,0.5);
-  backdrop-filter: blur(2px);
-  z-index: $z-index-backdrop;
-}
+// 全局提示组件样式
+.gp-container {
+  // 基础层级定义
+  $z-index-backdrop: 10000;
+  $z-index-modal: 10001;
+  $z-index-toast: 10002;
 
-.gp-modal {
-  position: fixed;
-  top: 50%;
-  left: 50%;
-  transform: translate(-50%, -50%);
-  z-index: $z-index-modal;
-  width: min(560px, 90vw);
-  background: #fff;
-  border-radius: 16px;
-  box-shadow: 0 24px 64px rgba(0,0,0,0.18);
-  overflow: hidden;
-}
+  // 全屏模式背景遮罩
+  .gp-backdrop {
+    position: fixed;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    z-index: $z-index-backdrop;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    transition: all 0.3s ease;
+    
+    &.gp-backdrop-visible {
+      background: rgba(0, 0, 0, 0.5);
+      backdrop-filter: blur(4px);
+    }
+  }
 
-.gp-header {
-  display: flex;
-  align-items: center;
-  gap: 12px;
-  padding: 16px 20px;
-  border-bottom: 1px solid #eee;
-}
+  // 模态框背景遮罩
+  .gp-modal-backdrop {
+    background: rgba(0, 0, 0, 0.6);
+    backdrop-filter: blur(2px);
+  }
 
-.gp-icon {
-  width: 24px;
-  height: 24px;
-  border-radius: 50%;
-  &.success { background: #10B981; }
-  &.info { background: #3B82F6; }
-  &.warning { background: #F59E0B; }
-}
+  // 全屏模式模态框
+  .gp-modal {
+    position: relative;
+    background: white;
+    border-radius: 12px;
+    box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+    max-width: 90vw;
+    max-height: 90vh;
+    overflow: hidden;
+    animation: modalSlideIn 0.3s ease-out;
+    
+    &.gp-modal-dialog {
+      min-width: 320px;
+    }
 
-.gp-title {
-  font-size: 16px;
-  font-weight: 600;
-  color: #1f2937;
-}
+    .gp-header {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      padding: 20px 24px 16px;
+      border-bottom: 1px solid #f1f5f9;
 
-.gp-close {
-  margin-left: auto;
-  border: none;
-  background: transparent;
-  font-size: 18px;
-  line-height: 1;
-  cursor: pointer;
-  color: #6b7280;
-}
+      .gp-title {
+        margin: 0;
+        font-size: 18px;
+        font-weight: 600;
+        color: #1e293b;
+      }
 
-.gp-content {
-  padding: 16px 20px;
-  color: #374151;
-  font-size: 14px;
-}
+      .gp-close-btn {
+        background: none;
+        border: none;
+        padding: 4px;
+        cursor: pointer;
+        color: #64748b;
+        border-radius: 4px;
+        transition: all 0.2s ease;
 
-.gp-actions {
-  display: flex;
-  justify-content: flex-end;
-  gap: 8px;
-  padding: 0 20px 16px 20px;
-}
+        &:hover {
+          background: #f1f5f9;
+          color: #334155;
+        }
+      }
+    }
 
-.btn-primary {
-  padding: 8px 14px;
-  border-radius: 8px;
-  border: none;
-  background: #007aff;
-  color: #fff;
-  cursor: pointer;
-}
+    .gp-content {
+      padding: 20px 24px;
+      text-align: center;
 
-.gp-toast {
-  position: fixed;
-  z-index: $z-index-toast;
-  &.top-right { top: 16px; right: 16px; }
-  &.bottom-right { bottom: 16px; right: 16px; }
-}
+      &.gp-modal-content {
+        text-align: left;
+        min-height: 60px;
+      }
+
+      .gp-icon {
+        margin-bottom: 16px;
+        
+        &.text-green-500 { color: #22c55e; }
+        &.text-blue-500 { color: #3b82f6; }
+        &.text-orange-500 { color: #f97316; }
+        &.text-red-500 { color: #ef4444; }
+      }
+
+      .gp-message {
+        font-size: 16px;
+        line-height: 1.5;
+        color: #475569;
+        margin: 0;
+      }
+    }
+
+    .gp-actions {
+      padding: 16px 24px 20px;
+      display: flex;
+      gap: 12px;
+      justify-content: flex-end;
+
+      .gp-action-btn {
+        padding: 10px 20px;
+        border-radius: 6px;
+        font-size: 14px;
+        font-weight: 500;
+        cursor: pointer;
+        transition: all 0.2s ease;
+        border: 1px solid transparent;
+
+        &.gp-btn-primary {
+          background: #3b82f6;
+          color: white;
+          
+          &:hover {
+            background: #2563eb;
+          }
+        }
+
+        &.gp-btn-secondary {
+          background: #f8fafc;
+          color: #64748b;
+          border-color: #e2e8f0;
+          
+          &:hover {
+            background: #f1f5f9;
+            color: #475569;
+          }
+        }
+      }
+    }
+  }
+
+  // 角落提示模式
+  .gp-toast {
+    position: fixed;
+    z-index: $z-index-toast;
+    background: white;
+    border-radius: 8px;
+    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+    border: 1px solid #e2e8f0;
+    overflow: hidden;
+    animation: toastSlideIn 0.3s ease-out;
+    max-width: 400px;
+    min-width: 300px;
+
+    .gp-toast-content {
+      display: flex;
+      align-items: flex-start;
+      padding: 16px;
+      gap: 12px;
+
+      .gp-toast-icon {
+        flex-shrink: 0;
+        margin-top: 2px;
+        
+        &.text-green-500 { color: #22c55e; }
+        &.text-blue-500 { color: #3b82f6; }
+        &.text-orange-500 { color: #f97316; }
+        &.text-red-500 { color: #ef4444; }
+      }
+
+      .gp-toast-text {
+        flex: 1;
+        min-width: 0;
+
+        .gp-toast-title {
+          font-size: 14px;
+          font-weight: 600;
+          color: #1e293b;
+          margin-bottom: 4px;
+        }
+
+        .gp-toast-message {
+          font-size: 13px;
+          color: #64748b;
+          line-height: 1.4;
+        }
+      }
+
+      .gp-toast-close {
+        background: none;
+        border: none;
+        padding: 2px;
+        cursor: pointer;
+        color: #94a3b8;
+        border-radius: 4px;
+        transition: all 0.2s ease;
+        flex-shrink: 0;
+
+        &:hover {
+          background: #f1f5f9;
+          color: #64748b;
+        }
+      }
+    }
+  }
+
+  // 尺寸变体
+  .gp-size-small {
+    &.gp-modal {
+      .gp-header { padding: 16px 20px 12px; }
+      .gp-content { padding: 16px 20px; }
+      .gp-actions { padding: 12px 20px 16px; }
+      .gp-title { font-size: 16px; }
+      .gp-message { font-size: 14px; }
+    }
+    
+    &.gp-toast {
+      min-width: 250px;
+      .gp-toast-content { padding: 12px; }
+    }
+  }
+
+  .gp-size-medium {
+    // 默认尺寸,已在上面定义
+  }
+
+  .gp-size-large {
+    &.gp-modal {
+      .gp-header { padding: 24px 28px 20px; }
+      .gp-content { padding: 24px 28px; }
+      .gp-actions { padding: 20px 28px 24px; }
+      .gp-title { font-size: 20px; }
+      .gp-message { font-size: 18px; }
+    }
+    
+    &.gp-toast {
+      min-width: 350px;
+      .gp-toast-content { padding: 20px; }
+    }
+  }
+
+  // 位置变体
+  .gp-position-top-right {
+    top: 20px;
+    right: 20px;
+  }
+
+  .gp-position-bottom-right {
+    bottom: 20px;
+    right: 20px;
+  }
+
+  .gp-position-top-left {
+    top: 20px;
+    left: 20px;
+  }
+
+  .gp-position-bottom-left {
+    bottom: 20px;
+    left: 20px;
+  }
+
+  // 响应式调整
+  @media (max-width: 640px) {
+    .gp-modal {
+      margin: 16px;
+      max-width: calc(100vw - 32px);
+      
+      .gp-header, .gp-content, .gp-actions {
+        padding-left: 20px;
+        padding-right: 20px;
+      }
+    }
 
-.gp-toast-inner {
-  display: flex;
-  align-items: center;
-  gap: 12px;
-  background: #fff;
-  border: 1px solid #e5e7eb;
-  box-shadow: 0 8px 24px rgba(0,0,0,0.12);
-  border-radius: 12px;
-  padding: 12px 14px;
-  min-width: 280px;
+    .gp-toast {
+      left: 16px !important;
+      right: 16px !important;
+      max-width: none;
+      min-width: 0;
+      
+      &.gp-position-top-right,
+      &.gp-position-top-left {
+        top: 16px;
+      }
+      
+      &.gp-position-bottom-right,
+      &.gp-position-bottom-left {
+        bottom: 16px;
+      }
+    }
+  }
 }
 
-.gp-texts {
-  display: flex;
-  flex-direction: column;
-  .gp-title { font-size: 14px; font-weight: 600; color: #111827; }
-  .gp-content { font-size: 12px; color: #4b5563; padding: 0; }
+// 动画定义
+@keyframes modalSlideIn {
+  from {
+    opacity: 0;
+    transform: scale(0.95) translateY(-10px);
+  }
+  to {
+    opacity: 1;
+    transform: scale(1) translateY(0);
+  }
 }
 
-@media (max-width: $mobile) {
-  .gp-toast-inner { min-width: 240px; }
+@keyframes toastSlideIn {
+  from {
+    opacity: 0;
+    transform: translateX(100%);
+  }
+  to {
+    opacity: 1;
+    transform: translateX(0);
+  }
 }

+ 74 - 7
src/app/shared/components/global-prompt/global-prompt.component.ts

@@ -1,8 +1,10 @@
-import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
+import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, OnDestroy } from '@angular/core';
 import { CommonModule } from '@angular/common';
 
-type PromptMode = 'fullscreen' | 'corner';
-type CornerPosition = 'top-right' | 'bottom-right';
+type PromptMode = 'fullscreen' | 'corner' | 'modal';
+type CornerPosition = 'top-right' | 'bottom-right' | 'top-left' | 'bottom-left';
+type PromptType = 'success' | 'info' | 'warning' | 'error';
+type PromptSize = 'small' | 'medium' | 'large';
 
 @Component({
   selector: 'app-global-prompt',
@@ -12,32 +14,97 @@ type CornerPosition = 'top-right' | 'bottom-right';
   styleUrls: ['./global-prompt.component.scss'],
   changeDetection: ChangeDetectionStrategy.OnPush
 })
-export class GlobalPromptComponent {
+export class GlobalPromptComponent implements OnDestroy {
   @Input() visible = false;
   @Input() title = '上传成功!';
   @Input() message = '';
   @Input() mode: PromptMode = 'fullscreen';
   @Input() position: CornerPosition = 'bottom-right';
-  @Input() icon: 'success' | 'info' | 'warning' = 'success';
+  @Input() icon: PromptType = 'success';
+  @Input() size: PromptSize = 'medium';
   @Input() autoDismissMs = 0; // 0 表示不自动关闭
+  @Input() showCloseButton = true;
+  @Input() showActionButton = false;
+  @Input() actionButtonText = '确定';
+  @Input() backdrop = true; // 是否显示背景遮罩
+  @Input() backdropDismiss = true; // 点击背景是否关闭
+  @Input() customClass = ''; // 自定义CSS类名
 
   @Output() closed = new EventEmitter<void>();
   @Output() action = new EventEmitter<void>();
+  @Output() backdropClick = new EventEmitter<void>();
+
+  private _dismissTimer?: number;
 
   ngOnChanges() {
     if (this.visible && this.autoDismissMs > 0) {
-      window.clearTimeout((this as any)._dismissTimer);
-      (this as any)._dismissTimer = window.setTimeout(() => {
+      this.clearDismissTimer();
+      this._dismissTimer = window.setTimeout(() => {
         this.onClose();
       }, this.autoDismissMs);
+    } else if (!this.visible) {
+      this.clearDismissTimer();
+    }
+  }
+
+  ngOnDestroy() {
+    this.clearDismissTimer();
+  }
+
+  private clearDismissTimer() {
+    if (this._dismissTimer) {
+      window.clearTimeout(this._dismissTimer);
+      this._dismissTimer = undefined;
     }
   }
 
   onClose() {
+    this.clearDismissTimer();
     this.closed.emit();
   }
 
   onAction() {
     this.action.emit();
   }
+
+  onBackdropClick() {
+    this.backdropClick.emit();
+    if (this.backdropDismiss) {
+      this.onClose();
+    }
+  }
+
+  // 获取图标类型对应的颜色类
+  getIconColorClass(): string {
+    const colorMap = {
+      success: 'text-green-500',
+      info: 'text-blue-500', 
+      warning: 'text-orange-500',
+      error: 'text-red-500'
+    };
+    return colorMap[this.icon] || colorMap.success;
+  }
+
+  // 获取尺寸对应的CSS类
+  getSizeClass(): string {
+    const sizeMap = {
+      small: 'gp-size-small',
+      medium: 'gp-size-medium',
+      large: 'gp-size-large'
+    };
+    return sizeMap[this.size] || sizeMap.medium;
+  }
+
+  // 获取位置对应的CSS类
+  getPositionClass(): string {
+    if (this.mode !== 'corner') return '';
+    
+    const positionMap = {
+      'top-right': 'gp-position-top-right',
+      'bottom-right': 'gp-position-bottom-right',
+      'top-left': 'gp-position-top-left',
+      'bottom-left': 'gp-position-bottom-left'
+    };
+    return positionMap[this.position] || positionMap['bottom-right'];
+  }
 }

+ 35 - 1
src/app/shared/components/requirements-confirm-card/requirements-confirm-card.scss

@@ -5,6 +5,7 @@
 :host { 
   display: block; 
   height: 100%; 
+  position: relative; // 确保子元素定位基准
 }
 
 // 新增:文本输入区域独立样式
@@ -56,16 +57,38 @@
   }
 }
 
-// 新增:参考图片和CAD图纸并排布局
+// 新增:参考图片和CAD图纸并排布局 - 优化弹窗出现时的布局稳定性
 .file-upload-grid {
   display: grid;
   grid-template-columns: 1fr 1fr;
   gap: 1rem;
+  position: relative;
+  z-index: 1; // 确保在正常文档流中
   
   // 移动端响应式
   @media (max-width: 768px) {
     grid-template-columns: 1fr;
   }
+  
+  // 当弹窗出现时保持布局稳定
+  .upload-item {
+    position: relative;
+    background: white;
+    border: 1px solid #e0e0e0;
+    border-radius: 8px;
+    padding: 1rem;
+    transition: transform 0.2s ease, box-shadow 0.2s ease;
+    
+    &:hover {
+      transform: translateY(-2px);
+      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+    }
+    
+    // 确保在弹窗出现时不会被遮挡或错位
+    &.image-item, &.cad-item {
+      z-index: 2;
+    }
+  }
 }
 
 .requirements-confirm-card {
@@ -465,6 +488,9 @@
     }
     
     .materials-list {
+      position: relative;
+      z-index: 3; // 确保素材列表在弹窗层级之下但高于其他内容
+      
       h5 {
         margin: 0 0 $ios-spacing-sm 0;
         font-size: $ios-font-size-xs;
@@ -482,6 +508,14 @@
           border-radius: 6px;
           padding: $ios-spacing-sm;
           background: white;
+          position: relative;
+          transition: transform 0.2s ease, box-shadow 0.2s ease;
+          
+          // 防止弹窗出现时布局错乱
+          &:hover {
+            transform: translateY(-1px);
+            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+          }
           
           &.material-text {
             border-left: 3px solid #34C759;

+ 1 - 0
src/app/shared/components/requirements-confirm-card/requirements-confirm-card.ts

@@ -79,6 +79,7 @@ interface RequirementItem {
   selector: 'app-requirements-confirm-card',
   standalone: true,
   imports: [CommonModule, FormsModule, ReactiveFormsModule, UploadSuccessModalComponent, GlobalPromptComponent, FullReportOverlayComponent],
+  providers: [CadAnalysisService],
   templateUrl: './requirements-confirm-card.html',
   styleUrls: ['./requirements-confirm-card.scss'],
   changeDetection: ChangeDetectionStrategy.Default // 确保使用默认变更检测策略

+ 1 - 8
src/app/shared/components/upload-success-modal/upload-success-modal.component.scss

@@ -21,7 +21,7 @@ $animation-easing: cubic-bezier(0.25, 0.8, 0.25, 1);
   height: 100vh;
   background: rgba(0, 0, 0, 0.6);
   backdrop-filter: blur(4px);
-  z-index: 9999;
+  z-index: 10000; // 统一z-index层级管理
   display: flex;
   align-items: center;
   justify-content: center;
@@ -30,14 +30,10 @@ $animation-easing: cubic-bezier(0.25, 0.8, 0.25, 1);
   // 强制所有设备都使用相同的居中布局
   @media (max-width: $mobile-breakpoint) {
     padding: 1rem;
-    align-items: center;
-    justify-content: center;
   }
   
   @media (min-width: $mobile-breakpoint) and (max-width: $tablet-breakpoint) {
     padding: 1rem;
-    align-items: center;
-    justify-content: center;
   }
 }
 
@@ -61,19 +57,16 @@ $animation-easing: cubic-bezier(0.25, 0.8, 0.25, 1);
     max-width: calc(100% - 2rem); // 保持左右边距
     border-radius: 16px;
     max-height: 75vh;
-    margin: 0; // 确保没有边距
   }
   
   @media (min-width: $mobile-breakpoint) and (max-width: $tablet-breakpoint) {
     max-width: 90%;
     max-height: 78vh;
-    margin: 0; // 确保没有边距
   }
   
   @media (min-width: $tablet-breakpoint) {
     max-width: 600px;
     max-height: 80vh;
-    margin: 0; // 确保没有边距
   }
   
   // 深色模式适配