Browse Source

Merge branch 'master' of http://git.fmode.cn:3000/nkkj/yss-project

0235711 6 hours ago
parent
commit
0e97add46a

+ 101 - 5
src/app/pages/designer/project-detail/project-detail.html

@@ -261,11 +261,30 @@
             <!-- 方案确认卡片 - 优化样式与左侧客户信息卡片保持一致 -->
             <div class="proposal-confirm-card card">
               <div class="card-header">
-                <h2>方案确认</h2>
+                <h2>色彩分析报告</h2>
                 <div class="sync-status">
                   <span class="progress-text">{{ getRequiredStagesProgress() }}% 完成</span>
                 </div>
               </div>
+              <!-- 参考图+色彩报告摘要 -->
+              <div class="report-summary-title">参考图 + 色彩报告</div>
+              <div class="report-summary" *ngIf="colorAnalysisResult; else noColorReportLeft">
+                <div class="summary-left">
+                  <div class="ref-image-wrap" (click)="previewColorRefImage()" title="点击预览参考图">
+                    <img [src]="colorAnalysisResult.originalImage" alt="参考图预览" class="ref-image" />
+                  </div>
+                </div>
+                <div class="summary-right">
+                  <div class="palette">
+                    <div class="swatch" *ngFor="let c of colorAnalysisResult.colors; let i = index" [style.background]="c.hex" [title]="c.hex"></div>
+                  </div>
+                  <div class="main-color">主色:{{ dominantColorHex || (colorAnalysisResult.colors.length > 0 ? colorAnalysisResult.colors[0].hex : '未知') }}</div>
+                  <div class="status">分析状态:{{ isAnalyzing ? '解析中' : '已完成' }}</div>
+                </div>
+              </div>
+              <ng-template #noColorReportLeft>
+                <p class="placeholder-note">暂无色彩分析结果。完成左侧颜色提取后,这里将显示参考图与报告摘要。</p>
+              </ng-template>
               
               <!-- 素材解析状态 -->
               @if (isAnalyzing) {
@@ -639,11 +658,88 @@
                           (stageCompleted)="onRequirementsStageCompleted($event)"
                           (dataUpdated)="onRequirementDataUpdated($event)">
                         </app-requirements-confirm-card>
+                        <!-- 新增:需求确认右侧下方左右分区,滚动展示参考图片与CAD图纸 -->
+                        <div class="materials-two-pane" style="display:flex;gap:16px;margin-top:12px;">
+                          <div class="pane left" style="flex:1;">
+                            <div class="pane-header" style="font-weight:600;margin-bottom:8px;">参考图片</div>
+                            <div class="pane-body scroller" style="max-height:240px;overflow:auto;border:1px solid #eee;border-radius:8px;padding:8px;">
+                              @if (referenceImages.length > 0) {
+                                <div class="images-grid" style="display:grid;grid-template-columns:repeat(auto-fill, minmax(100px, 1fr));gap:8px;">
+                                  @for (img of referenceImages; track img.id) {
+                                    <div class="image-thumb" style="cursor:pointer;border:1px solid #f0f0f0;border-radius:6px;overflow:hidden;" (click)="previewImageFile(img.url)">
+                                      <img [src]="img.url" [alt]="img.name || '参考图'" style="width:100%;height:90px;object-fit:cover;" />
+                                    </div>
+                                  }
+                                </div>
+                              } @else {
+                                <div class="empty-tip" style="color:#888;">暂无参考图片</div>
+                              }
+                            </div>
+                          </div>
+                          <div class="pane right" style="flex:1;">
+                            <div class="pane-header" style="font-weight:600;margin-bottom:8px;">CAD图纸</div>
+                            <div class="pane-body scroller" style="max-height:240px;overflow:auto;border:1px solid #eee;border-radius:8px;padding:8px;">
+                              @if (cadFiles.length > 0) {
+                                <div class="files-grid" style="display:grid;grid-template-columns:repeat(auto-fill, minmax(140px, 1fr));gap:8px;">
+                                  @for (file of cadFiles; track file.id) {
+                                    <div class="file-thumb" style="cursor:pointer;border:1px dashed #e5e5e5;border-radius:6px;padding:8px;background:#fafafa;" (click)="previewCadFile(file.url)">
+                                      <div class="file-icon" style="font-size:12px;color:#555;margin-bottom:6px;">CAD</div>
+                                      <div class="file-name" style="font-size:12px;color:#333;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">{{ file.name }}</div>
+                                    </div>
+                                  }
+                                </div>
+                              } @else {
+                                <div class="empty-tip" style="color:#888;">暂无CAD文件</div>
+                              }
+                            </div>
+                          </div>
+                        </div>
                       } @else if (stage === '方案确认') {
-                        <!-- 方案确认阶段 - 已移动到左侧列 -->
-                        <div class="proposal-confirm-placeholder">
-                          <div class="placeholder-message">
-                            <p>方案确认卡片已移动到左侧区域</p>
+                        <!-- 替换右侧占位为:色彩分析报告(与参考图关联) -->
+                        <div class="card color-report-card" style="margin-top:8px;">
+                          <div class="card-header">
+                            <h4>色彩分析报告</h4>
+                          </div>
+                          <div class="card-body">
+                            @if (colorAnalysisResult) {
+                              <div class="report-summary" style="display:flex;flex-direction:column;gap:16px;">
+                                <div class="report-images">
+                                  <div class="section-title" style="font-weight:600;margin-bottom:6px;">参考图</div>
+                                  @if (referenceImages.length > 0) {
+                                    <div class="images-row" style="display:flex;gap:8px;flex-wrap:wrap;">
+                                      @for (img of referenceImages; track img.id) {
+                                        <img [src]="img.url" [alt]="img.name || '参考图'" class="image-thumb" (click)="previewImageFile(img.url)" style="width:80px;height:80px;object-fit:cover;border-radius:6px;cursor:pointer;border:1px solid #eee;" />
+                                      }
+                                    </div>
+                                  } @else {
+                                    <div class="empty-tip" style="color:#888;">暂无参考图</div>
+                                  }
+                                </div>
+                                <div class="palette">
+                                  <div class="section-title" style="font-weight:600;margin-bottom:6px;">色板</div>
+                                  <div class="palette-row" style="display:flex;gap:8px;flex-wrap:wrap;">
+                                    @for (c of colorAnalysisResult.colors; track c.hex) {
+                                      <div class="palette-item" style="display:flex;align-items:center;gap:6px;padding:6px;border:1px solid #eee;border-radius:6px;">
+                                        <div class="swatch" [style.backgroundColor]="c.hex" style="width:20px;height:20px;border-radius:4px;border:1px solid #ddd;"></div>
+                                        <div class="hex" style="font-size:12px;color:#333;">{{ c.hex }}</div>
+                                        <div class="pct" style="font-size:12px;color:#666;">{{ c.percentage }}%</div>
+                                      </div>
+                                    }
+                                  </div>
+                                </div>
+                                <div class="dominant-color">
+                                  <div class="section-title" style="font-weight:600;margin-bottom:6px;">主色</div>
+                                  <div class="dominant-row" style="display:flex;align-items:center;gap:8px;">
+                                    <div class="swatch" [style.backgroundColor]="dominantColorHex || '#ccc'" style="width:28px;height:28px;border-radius:6px;border:1px solid #ddd;"></div>
+                                    <span class="hex" style="font-size:12px;color:#333;">
+                                      {{ dominantColorHex || (colorAnalysisResult.colors.length > 0 ? colorAnalysisResult.colors[0].hex : '未知') }}
+                                    </span>
+                                  </div>
+                                </div>
+                              </div>
+                            } @else {
+                              <div class="empty-tip" style="color:#888;">暂无色彩分析结果</div>
+                            }
                           </div>
                         </div>
                       } @else if (stage === '建模') {

+ 44 - 0
src/app/pages/designer/project-detail/project-detail.ts

@@ -19,6 +19,7 @@ import { CustomerReviewCardComponent } from '../../../shared/components/customer
 import { ComplaintCardComponent } from '../../../shared/components/complaint-card/complaint-card';
 
 import { VerticalNavComponent } from './components/vertical-nav/vertical-nav.component';
+import { ColorAnalysisResult } from '../../../shared/services/color-analysis.service';
 
 interface ExceptionHistory {
   id: string;
@@ -221,6 +222,12 @@ export class ProjectDetail implements OnInit, OnDestroy {
   proposalAnalysis: ProposalAnalysis | null = null;
   isAnalyzing: boolean = false;
   analysisProgress: number = 0;
+  // 新增:右侧色彩分析结果展示
+  colorAnalysisResult: ColorAnalysisResult | null = null;
+  dominantColorHex: string | null = null;
+  // 新增:区分展示的参考图片与CAD文件(来源于需求确认子组件)
+  referenceImages: any[] = [];
+  cadFiles: any[] = [];
   
   // 新增:10阶段顺序(串式流程)
   stageOrder: ProjectStage[] = ['订单创建', '需求沟通', '方案确认', '建模', '软装', '渲染', '后期', '尾款结算', '客户评价', '投诉处理'];
@@ -2599,6 +2606,24 @@ export class ProjectDetail implements OnInit, OnDestroy {
         }));
       }
       
+      // 接收色彩分析结果并存储用于右侧展示
+      if (data.colorAnalysisResult) {
+        this.colorAnalysisResult = data.colorAnalysisResult as ColorAnalysisResult;
+        // 计算主色:按占比最高的颜色
+        const colors = this.colorAnalysisResult?.colors || [];
+        if (colors.length > 0) {
+          const dominant = colors.reduce((max, cur) => cur.percentage > max.percentage ? cur : max, colors[0]);
+          this.dominantColorHex = dominant.hex;
+        } else {
+          this.dominantColorHex = null;
+        }
+      }
+
+      // 新增:根据上传来源拆分材料文件用于左右区展示
+      const materials = Array.isArray(data?.materials) ? data.materials : [];
+      this.referenceImages = materials.filter((m: any) => m?.type === 'image');
+      this.cadFiles = materials.filter((m: any) => m?.type === 'cad');
+
       // 触发变更检测以更新UI
       this.cdr.detectChanges();
       
@@ -2606,6 +2631,25 @@ export class ProjectDetail implements OnInit, OnDestroy {
     }
   }
 
+  // 预览右侧色彩分析参考图
+  previewColorRefImage(): void {
+    const url = this.colorAnalysisResult?.originalImage;
+    if (url) {
+      window.open(url, '_blank');
+    }
+  }
+
+  // 新增:点击预览参考图片与CAD文件
+  previewImageFile(url?: string): void {
+    if (!url) return;
+    window.open(url, '_blank');
+  }
+
+  previewCadFile(url?: string): void {
+    if (!url) return;
+    window.open(url, '_blank');
+  }
+
   // 新增:上传支付凭证功能
   uploadPaymentProof(): void {
     const input = document.createElement('input');

+ 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>

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

@@ -1,113 +1,394 @@
-// 层级与断点
-$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层级
+  $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;
-}
+  // 全屏模式背景遮罩 - 优化position:fixed布局
+  .gp-backdrop {
+    position: fixed;
+    top: 0;
+    left: 0;
+    width: 100vw;
+    height: 100vh;
+    z-index: $z-index-backdrop;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    padding: 1rem; // 确保在所有设备上都有适当的边距
+    transition: all 0.3s ease;
+    
+    // 强制所有设备都使用相同的居中布局
+    @media (max-width: 640px) {
+      padding: 1rem;
+      align-items: center; // 确保移动端也完全居中
+    }
+    
+    @media (min-width: 641px) and (max-width: 1024px) {
+      padding: 1.5rem;
+      align-items: center; // 确保平板端也完全居中
+    }
+    
+    @media (min-width: 1025px) {
+      padding: 2rem;
+      align-items: center; // 确保桌面端也完全居中
+    }
+    
+    &.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: 80vh; // 限制最大高度,避免内容溢出
+    overflow: hidden;
+    animation: modalSlideIn 0.3s ease-out;
+    margin: 0; // 移除所有边距,完全依赖flex居中
+    transform-origin: center; // 确保动画从中心开始
+    
+    &.gp-modal-dialog {
+      min-width: 320px;
+      width: 100%;
+      max-width: 500px; // 设置合理的最大宽度
+    }
 
-.gp-title {
-  font-size: 16px;
-  font-weight: 600;
-  color: #1f2937;
-}
+    // 响应式调整 - 确保在所有设备上都能正确显示
+    @media (max-width: 640px) {
+      max-width: calc(100vw - 2rem); // 保持左右边距
+      max-height: 75vh; // 移动端稍微降低高度
+      border-radius: 12px;
+      margin: 0; // 确保无边距
+    }
+    
+    @media (min-width: 641px) and (max-width: 1024px) {
+      max-width: 85vw;
+      max-height: 78vh;
+      margin: 0; // 确保无边距
+    }
+    
+    @media (min-width: 1025px) {
+      max-width: 600px;
+      max-height: 80vh;
+      margin: 0; // 确保无边距
+    }
 
-.gp-close {
-  margin-left: auto;
-  border: none;
-  background: transparent;
-  font-size: 18px;
-  line-height: 1;
-  cursor: pointer;
-  color: #6b7280;
-}
+    .gp-header {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      padding: 20px 24px 16px;
+      border-bottom: 1px solid #f1f5f9;
 
-.gp-content {
-  padding: 16px 20px;
-  color: #374151;
-  font-size: 14px;
-}
+      .gp-title {
+        margin: 0;
+        font-size: 18px;
+        font-weight: 600;
+        color: #1e293b;
+        
+        @media (max-width: 640px) {
+          font-size: 16px;
+        }
+      }
 
-.gp-actions {
-  display: flex;
-  justify-content: flex-end;
-  gap: 8px;
-  padding: 0 20px 16px 20px;
-}
+      .gp-close-btn {
+        background: none;
+        border: none;
+        padding: 4px;
+        cursor: pointer;
+        color: #64748b;
+        border-radius: 4px;
+        transition: all 0.2s ease;
+        flex-shrink: 0; // 防止按钮被压缩
 
-.btn-primary {
-  padding: 8px 14px;
-  border-radius: 8px;
-  border: none;
-  background: #007aff;
-  color: #fff;
-  cursor: pointer;
-}
+        &:hover {
+          background: #f1f5f9;
+          color: #334155;
+        }
+      }
+    }
 
-.gp-toast {
-  position: fixed;
-  z-index: $z-index-toast;
-  &.top-right { top: 16px; right: 16px; }
-  &.bottom-right { bottom: 16px; right: 16px; }
-}
+    .gp-content {
+      padding: 20px 24px;
+      text-align: center;
+      overflow-y: auto; // 允许内容滚动
+      max-height: calc(80vh - 140px); // 确保内容区域不会溢出
+      
+      // 自定义滚动条样式
+      &::-webkit-scrollbar {
+        width: 6px;
+      }
+      
+      &::-webkit-scrollbar-track {
+        background: transparent;
+      }
+      
+      &::-webkit-scrollbar-thumb {
+        background: rgba(0, 0, 0, 0.2);
+        border-radius: 3px;
+        
+        &:hover {
+          background: rgba(0, 0, 0, 0.3);
+        }
+      }
+
+      &.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;
+        
+        @media (max-width: 640px) {
+          font-size: 14px;
+        }
+      }
+    }
+
+    .gp-actions {
+      padding: 16px 24px 20px;
+      display: flex;
+      gap: 12px;
+      justify-content: flex-end;
+      flex-shrink: 0; // 防止按钮区域被压缩
+      
+      @media (max-width: 640px) {
+        flex-direction: column-reverse; // 移动端垂直排列按钮
+        gap: 8px;
+      }
+
+      .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;
+        
+        @media (max-width: 640px) {
+          width: 100%; // 移动端按钮全宽
+          justify-content: center;
+        }
+
+        &.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;
+          }
+        }
+      }
+    }
+  }
+
+  // 角落提示模式 - 优化position:fixed定位
+  .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; }
+    }
+  }
+
+  // 位置变体 - 优化fixed定位
+  .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: 0; // 移除边距,完全依赖backdrop的padding
+      max-width: calc(100vw - 2rem);
+      
+      .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'];
+  }
 }

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

@@ -5,6 +5,7 @@
 :host { 
   display: block; 
   height: 100%; 
+  position: relative; // 确保子元素定位基准
 }
 
 // 新增:文本输入区域独立样式
@@ -56,15 +57,88 @@
   }
 }
 
-// 新增:参考图片和CAD图纸并排布局
+// 新增:参考图片和CAD图纸并排布局 - 优化弹窗出现时的布局稳定性
 .file-upload-grid {
   display: grid;
   grid-template-columns: 1fr 1fr;
   gap: 1rem;
+  position: relative;
+  z-index: 1; // 确保在正常文档流中
+  margin-bottom: $ios-spacing-lg;
   
   // 移动端响应式
   @media (max-width: 768px) {
     grid-template-columns: 1fr;
+    gap: $ios-spacing-sm;
+  }
+  
+  // 平板端响应式
+  @media (min-width: 769px) and (max-width: 1024px) {
+    gap: $ios-spacing-md;
+  }
+  
+  // 当弹窗出现时保持布局稳定
+  .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;
+      
+      // 当有弹窗显示时,降低交互优先级
+      &.modal-active {
+        pointer-events: none;
+        opacity: 0.8;
+      }
+    }
+    
+    h5 {
+      margin: 0 0 $ios-spacing-sm 0;
+      font-size: $ios-font-size-xs;
+      font-weight: $ios-font-weight-semibold;
+      color: $ios-text-primary;
+    }
+    
+    .file-upload-zone {
+      border: 2px dashed $ios-border;
+      border-radius: 8px;
+      padding: $ios-spacing-lg;
+      text-align: center;
+      cursor: pointer;
+      transition: all 0.2s ease;
+      
+      &:hover {
+        border-color: $ios-primary;
+        background: rgba(0, 122, 255, 0.02);
+      }
+      
+      svg {
+        color: $ios-text-secondary;
+        margin-bottom: $ios-spacing-sm;
+      }
+      
+      p {
+        margin: 0 0 $ios-spacing-xs 0;
+        font-size: $ios-font-size-xs;
+        color: $ios-text-primary;
+        font-weight: $ios-font-weight-medium;
+      }
+      
+      .hint {
+        font-size: $ios-font-size-xs;
+        color: $ios-text-secondary;
+      }
+    }
   }
 }
 
@@ -465,6 +539,9 @@
     }
     
     .materials-list {
+      position: relative;
+      z-index: 3; // 确保素材列表在弹窗层级之下但高于其他内容
+      
       h5 {
         margin: 0 0 $ios-spacing-sm 0;
         font-size: $ios-font-size-xs;
@@ -477,11 +554,30 @@
         grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
         gap: $ios-spacing-sm;
         
+        // 响应式布局优化
+        @media (max-width: 768px) {
+          grid-template-columns: 1fr;
+          gap: $ios-spacing-xs;
+        }
+        
+        @media (min-width: 769px) and (max-width: 1024px) {
+          grid-template-columns: repeat(2, 1fr);
+        }
+        
         .material-card {
           border: 1px solid $ios-border;
           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);
+            z-index: 4; // 悬停时提升层级
+          }
           
           &.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 // 确保使用默认变更检测策略

+ 20 - 223
src/app/shared/components/upload-success-modal/upload-success-modal.component.scss

@@ -13,6 +13,7 @@ $animation-duration-normal: 0.3s;
 $animation-duration-slow: 0.5s;
 $animation-easing: cubic-bezier(0.25, 0.8, 0.25, 1);
 
+// 弹窗背景遮罩 - 优化全屏居中布局
 .modal-backdrop {
   position: fixed;
   top: 0;
@@ -21,7 +22,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,18 +31,21 @@ $animation-easing: cubic-bezier(0.25, 0.8, 0.25, 1);
   // 强制所有设备都使用相同的居中布局
   @media (max-width: $mobile-breakpoint) {
     padding: 1rem;
-    align-items: center;
-    justify-content: center;
+    align-items: center; // 确保移动端也居中显示
   }
   
   @media (min-width: $mobile-breakpoint) and (max-width: $tablet-breakpoint) {
     padding: 1rem;
-    align-items: center;
-    justify-content: center;
+    align-items: center; // 确保平板端也居中显示
+  }
+  
+  @media (min-width: $tablet-breakpoint) {
+    padding: 2rem;
+    align-items: center; // 确保桌面端也居中显示
   }
 }
 
-// 弹窗主容器
+// 弹窗主容器 - 优化布局稳定性
 .modal-container {
   background: white;
   border-radius: 16px;
@@ -56,24 +60,24 @@ $animation-easing: cubic-bezier(0.25, 0.8, 0.25, 1);
   flex-direction: column;
   margin: 0; // 移除所有边距,完全依赖flex居中
   
-  // 响应式调整 - 统一居中显示
+  // 响应式调整 - 统一居中显示,避免布局冲突
   @media (max-width: $mobile-breakpoint) {
     max-width: calc(100% - 2rem); // 保持左右边距
     border-radius: 16px;
     max-height: 75vh;
-    margin: 0; // 确保没有边距
+    margin: 0; // 确保边距
   }
   
   @media (min-width: $mobile-breakpoint) and (max-width: $tablet-breakpoint) {
     max-width: 90%;
     max-height: 78vh;
-    margin: 0; // 确保没有边距
+    margin: 0; // 确保边距
   }
   
   @media (min-width: $tablet-breakpoint) {
     max-width: 600px;
     max-height: 80vh;
-    margin: 0; // 确保没有边距
+    margin: 0; // 确保边距
   }
   
   // 深色模式适配
@@ -490,6 +494,7 @@ $animation-easing: cubic-bezier(0.25, 0.8, 0.25, 1);
     padding: 24px;
     background: #f9f9f9;
     border-radius: 12px;
+    position: relative; // 确保相对定位
     
     .loading-spinner {
       width: 32px;
@@ -519,6 +524,8 @@ $animation-easing: cubic-bezier(0.25, 0.8, 0.25, 1);
   
   // 分析结果
   .analysis-result {
+    position: relative; // 确保相对定位
+    
     .result-header {
       display: flex;
       align-items: center;
@@ -536,7 +543,7 @@ $animation-easing: cubic-bezier(0.25, 0.8, 0.25, 1);
         }
       }
       
-      .result-badge {
+      .result-count {
         background: #34c759;
         color: white;
         font-size: 11px;
@@ -568,6 +575,7 @@ $animation-easing: cubic-bezier(0.25, 0.8, 0.25, 1);
         border: 1px solid #e5e5ea;
         border-radius: 12px;
         transition: all 0.2s ease;
+        position: relative; // 确保相对定位
         
         @media (max-width: $mobile-breakpoint) {
           padding: 10px;
@@ -645,6 +653,7 @@ $animation-easing: cubic-bezier(0.25, 0.8, 0.25, 1);
         cursor: pointer;
         transition: all 0.2s ease;
         min-width: 140px;
+        position: relative; // 确保相对定位
         
         @media (max-width: $mobile-breakpoint) {
           padding: 10px 16px;
@@ -672,218 +681,6 @@ $animation-easing: cubic-bezier(0.25, 0.8, 0.25, 1);
         }
       }
     }
-    
-    // 颜色描述区域
-    .color-description {
-      margin-top: 24px;
-      padding: 24px;
-      background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
-      border: 1px solid #e5e5ea;
-      border-radius: 16px;
-      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
-      
-      .description-header {
-        margin-bottom: 16px;
-        
-        h6 {
-          margin: 0 0 4px 0;
-          font-size: 18px;
-          font-weight: 700;
-          color: #1d1d1f;
-          display: flex;
-          align-items: center;
-          gap: 8px;
-          
-          &::before {
-            content: '🎨';
-            font-size: 20px;
-          }
-        }
-        
-        p {
-          margin: 0;
-          font-size: 14px;
-          color: #8e8e93;
-          font-weight: 400;
-        }
-      }
-      
-      .description-content {
-        background: #ffffff;
-        border: 2px solid #f0f0f0;
-        border-radius: 12px;
-        padding: 20px;
-        margin-bottom: 16px;
-        font-size: 15px;
-        line-height: 1.7;
-        color: #333333;
-        white-space: pre-line;
-        word-break: break-word;
-        max-height: 180px;
-        overflow-y: auto;
-        font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
-        transition: all 0.2s ease;
-        
-        &:hover {
-          border-color: #007aff;
-          box-shadow: 0 0 0 3px rgba(0, 122, 255, 0.1);
-        }
-        
-        &::-webkit-scrollbar {
-          width: 4px;
-        }
-        
-        &::-webkit-scrollbar-track {
-          background: transparent;
-        }
-        
-        &::-webkit-scrollbar-thumb {
-          background: rgba(0, 122, 255, 0.3);
-          border-radius: 2px;
-          
-          &:hover {
-            background: rgba(0, 122, 255, 0.5);
-          }
-        }
-        
-        &.empty {
-          color: #8e8e93;
-          font-style: italic;
-          text-align: center;
-          padding: 32px 20px;
-          background: #fafafa;
-          border-style: dashed;
-        }
-      }
-      
-      .description-actions {
-        display: flex;
-        align-items: center;
-        justify-content: space-between;
-        gap: 16px;
-        
-        @media (max-width: $mobile-breakpoint) {
-          flex-direction: column;
-          align-items: stretch;
-        }
-        
-        .copy-btn {
-          display: inline-flex;
-          align-items: center;
-          justify-content: center;
-          gap: 8px;
-          padding: 12px 20px;
-          background: linear-gradient(135deg, #007aff 0%, #0056cc 100%);
-          color: #ffffff;
-          border: none;
-          border-radius: 10px;
-          font-size: 14px;
-          font-weight: 600;
-          cursor: pointer;
-          transition: all 0.3s ease;
-          min-width: 100px;
-          box-shadow: 0 4px 12px rgba(0, 122, 255, 0.3);
-          
-          &:hover {
-            transform: translateY(-2px);
-            box-shadow: 0 6px 20px rgba(0, 122, 255, 0.4);
-            background: linear-gradient(135deg, #0056cc 0%, #003d99 100%);
-          }
-          
-          &:active {
-            transform: translateY(0);
-            box-shadow: 0 2px 8px rgba(0, 122, 255, 0.3);
-          }
-          
-          &.copied {
-            background: linear-gradient(135deg, #34c759 0%, #28a745 100%);
-            box-shadow: 0 4px 12px rgba(52, 199, 89, 0.3);
-            
-            &:hover {
-              background: linear-gradient(135deg, #28a745 0%, #1e7e34 100%);
-              box-shadow: 0 6px 20px rgba(52, 199, 89, 0.4);
-            }
-          }
-          
-          svg {
-            width: 14px;
-            height: 14px;
-          }
-        }
-        
-        .description-tip {
-          font-size: 12px;
-          color: #8e8e93;
-          flex: 1;
-          
-          @media (max-width: $mobile-breakpoint) {
-            text-align: center;
-            margin-top: 8px;
-          }
-        }
-      }
-    }
-  }
-  
-  // 分析错误
-  .analysis-error {
-    display: flex;
-    align-items: center;
-    gap: 12px;
-    padding: 16px;
-    background: #fff2f2;
-    border: 1px solid #fecaca;
-    border-radius: 8px;
-    
-    .error-icon {
-      width: 32px;
-      height: 32px;
-      background: #ef4444;
-      border-radius: 50%;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      flex-shrink: 0;
-      
-      svg {
-        color: white;
-        width: 16px;
-        height: 16px;
-      }
-    }
-    
-    .error-text {
-      flex: 1;
-      
-      h5 {
-        margin: 0 0 4px 0;
-        font-size: 14px;
-        font-weight: 600;
-        color: #dc2626;
-      }
-      
-      p {
-        margin: 0;
-        font-size: 12px;
-        color: #991b1b;
-      }
-    }
-    
-    .retry-btn {
-      padding: 6px 12px;
-      background: #ef4444;
-      color: white;
-      border: none;
-      border-radius: 4px;
-      font-size: 12px;
-      font-weight: 500;
-      cursor: pointer;
-      transition: all 0.2s ease;
-      
-      &:hover {
-        background: #dc2626;
-      }
-    }
   }
 }