徐福静0235668 12 цаг өмнө
parent
commit
a7095da144
26 өөрчлөгдсөн 2408 нэмэгдсэн , 6165 устгасан
  1. 1 0
      src/app/app.routes.ts
  2. 3 0
      src/app/models/project.model.ts
  3. 8 4
      src/app/pages/customer-service/consultation-order/consultation-order.ts
  4. 27 2
      src/app/pages/customer-service/customer-service-layout/customer-service-layout.ts
  5. 0 170
      src/app/pages/customer-service/project-detail/complaint-warning-dialog.ts
  6. 0 162
      src/app/pages/customer-service/project-detail/modification-request-dialog.ts
  7. 0 867
      src/app/pages/customer-service/project-detail/project-detail.html
  8. 0 3516
      src/app/pages/customer-service/project-detail/project-detail.scss
  9. 0 1148
      src/app/pages/customer-service/project-detail/project-detail.ts
  10. 0 187
      src/app/pages/customer-service/project-detail/refund-request-dialog.ts
  11. 29 7
      src/app/pages/customer-service/project-list/project-list.ts
  12. 13 13
      src/app/pages/designer/project-detail/project-detail.html
  13. 29 0
      src/app/pages/designer/project-detail/project-detail.ts
  14. 17 2
      src/app/pages/team-leader/dashboard/dashboard.ts
  15. 3 0
      src/app/services/project.service.ts
  16. 208 20
      src/app/shared/components/complaint-card/complaint-card.html
  17. 524 10
      src/app/shared/components/complaint-card/complaint-card.scss
  18. 204 2
      src/app/shared/components/complaint-card/complaint-card.ts
  19. 171 17
      src/app/shared/components/customer-review-card/customer-review-card.html
  20. 419 9
      src/app/shared/components/customer-review-card/customer-review-card.scss
  21. 163 2
      src/app/shared/components/customer-review-card/customer-review-card.ts
  22. 110 16
      src/app/shared/components/settlement-card/settlement-card.html
  23. 325 9
      src/app/shared/components/settlement-card/settlement-card.scss
  24. 106 2
      src/app/shared/components/settlement-card/settlement-card.ts
  25. 3 0
      src/styles.scss
  26. 45 0
      src/styles/_variables.scss

+ 1 - 0
src/app/app.routes.ts

@@ -18,6 +18,7 @@ import { AssignmentListComponent } from './pages/customer-service/dashboard/page
 import { ExceptionListComponent } from './pages/customer-service/dashboard/pages/exception-list/exception-list.component';
 
 
+
 // 设计师页面
 import { Dashboard as DesignerDashboard } from './pages/designer/dashboard/dashboard';
 import { ProjectDetail as DesignerProjectDetail } from './pages/designer/project-detail/project-detail';

+ 3 - 0
src/app/models/project.model.ts

@@ -7,6 +7,7 @@ export interface Project {
   highPriorityNeeds: string[];
   status: ProjectStatus;
   currentStage: ProjectStage;
+  stage: ProjectStage; // 添加stage属性,与currentStage保持一致
   createdAt: Date;
   deadline: Date;
   assigneeId: string;
@@ -84,6 +85,7 @@ export interface RenderProgress {
   completionRate: number; // 百分比
   estimatedTimeRemaining: number; // 小时
   status: '进行中' | '已完成' | '已失败';
+  stage?: string; // 添加渲染阶段属性
   updatedAt: Date;
 }
 
@@ -126,6 +128,7 @@ export interface DesignerChange {
   acceptanceTime?: Date;
   historicalAchievements: string[];
   completedWorkload: number; // 百分比
+  reason?: string; // 添加变更原因属性
 }
 
 // 结算记录

+ 8 - 4
src/app/pages/customer-service/consultation-order/consultation-order.ts

@@ -414,8 +414,10 @@ export class ConsultationOrder {
           };
           this.orderCreated.emit(orderData);
           
-          // 跳转到设计师项目详情页面
-          this.router.navigate(['/designer/project-detail/mock-9']);
+          // 跳转到设计师项目详情页面,传递客服角色标识
+          this.router.navigate(['/designer/project-detail/mock-9'], { 
+            queryParams: { role: 'customer-service' } 
+          });
         }, 1000);
       }, 1500);
     }
@@ -488,8 +490,10 @@ export class ConsultationOrder {
             };
             this.orderCreated.emit(orderData);
             
-            // 跳转到设计师项目详情页面
-            this.router.navigate(['/designer/project-detail/mock-9']);
+            // 跳转到设计师项目详情页面,传递客服角色标识
+            this.router.navigate(['/designer/project-detail/mock-9'], { 
+              queryParams: { role: 'customer-service' } 
+            });
           }, 1000);
         } else {
           this.snackBar.open('创建失败,请稍后重试', '关闭', { duration: 3000 });

+ 27 - 2
src/app/pages/customer-service/customer-service-layout/customer-service-layout.ts

@@ -1,4 +1,4 @@
-import { Component, signal } from '@angular/core';
+import { Component, signal, OnInit, OnDestroy } from '@angular/core';
 import { CommonModule } from '@angular/common';
 import { Router, RouterOutlet, RouterLinkActive, RouterLink } from '@angular/router';
 import { FormsModule } from '@angular/forms';
@@ -10,7 +10,7 @@ import { FormsModule } from '@angular/forms';
   templateUrl: './customer-service-layout.html',
   styleUrl: './customer-service-layout.scss'
 }) 
-export class CustomerServiceLayout {
+export class CustomerServiceLayout implements OnInit, OnDestroy {
   sidebarOpen = true;
   searchTerm = '';
   currentDate = new Date();
@@ -25,6 +25,31 @@ export class CustomerServiceLayout {
 
   constructor(private router: Router) {}
 
+  ngOnInit() {
+    // 监听来自iframe的消息
+    window.addEventListener('message', this.handleIframeMessage.bind(this));
+  }
+
+  ngOnDestroy() {
+    // 清理事件监听器
+    window.removeEventListener('message', this.handleIframeMessage.bind(this));
+  }
+
+  // 处理来自iframe的消息
+  private handleIframeMessage(event: MessageEvent) {
+    // 验证消息来源(可以根据需要添加更严格的验证)
+    if (event.origin !== window.location.origin) {
+      return;
+    }
+
+    // 处理导航消息
+    if (event.data && event.data.type === 'navigate') {
+      const { url } = event.data;
+      console.log('收到iframe导航请求:', url);
+      this.router.navigateByUrl(url);
+    }
+  }
+
   toggleSidebar() {
     this.sidebarOpen = !this.sidebarOpen;
   }

+ 0 - 170
src/app/pages/customer-service/project-detail/complaint-warning-dialog.ts

@@ -1,170 +0,0 @@
-import { Component, Inject } from '@angular/core';
-import { CommonModule } from '@angular/common';
-import { FormsModule } from '@angular/forms';
-import { MatButtonModule } from '@angular/material/button';
-import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
-import { MatFormFieldModule } from '@angular/material/form-field';
-import { MatInputModule } from '@angular/material/input';
-import { MatSelectModule } from '@angular/material/select';
-import { MatIconModule } from '@angular/material/icon';
-
-interface ComplaintWarningData {
-  projectId: string;
-  projectName: string;
-}
-
-@Component({
-  selector: 'app-complaint-warning-dialog',
-  standalone: true,
-  imports: [
-    CommonModule,
-    FormsModule,
-    MatButtonModule,
-    MatDialogModule,
-    MatFormFieldModule,
-    MatInputModule,
-    MatSelectModule,
-    MatIconModule
-  ],
-  template: `
-    <h2 mat-dialog-title>投诉预警</h2>
-    <mat-dialog-content>
-      <div class="dialog-content">
-        <div class="project-info">
-          <p><strong>项目名称:</strong> {{ data.projectName }}</p>
-        </div>
-        
-        <div class="form-group">
-          <label for="complaint-level">预警等级 *</label>
-          <mat-form-field appearance="outline" class="form-field">
-            <mat-select [(ngModel)]="complaintLevel" id="complaint-level" name="complaintLevel" required>
-              <mat-option value="low">低级别</mat-option>
-              <mat-option value="medium">中级别</mat-option>
-              <mat-option value="high">高级别</mat-option>
-            </mat-select>
-          </mat-form-field>
-        </div>
-        
-        <div class="form-group">
-          <label for="complaint-content">投诉内容 *</label>
-          <mat-form-field appearance="outline" class="form-field">
-            <textarea 
-              matInput 
-              [(ngModel)]="complaintContent" 
-              id="complaint-content" 
-              name="complaintContent" 
-              rows="4" 
-              placeholder="请详细描述客户投诉的内容..."
-              required
-            ></textarea>
-          </mat-form-field>
-        </div>
-        
-        <div class="form-group">
-          <label for="potential-risk">潜在风险</label>
-          <mat-form-field appearance="outline" class="form-field">
-            <textarea 
-              matInput 
-              [(ngModel)]="potentialRisk" 
-              id="potential-risk" 
-              name="potential-risk" 
-              rows="2" 
-              placeholder="分析可能带来的影响和风险..."
-            ></textarea>
-          </mat-form-field>
-        </div>
-        
-        <div class="form-group">
-          <label for="suggested-action">建议措施</label>
-          <mat-form-field appearance="outline" class="form-field">
-            <textarea 
-              matInput 
-              [(ngModel)]="suggestedAction" 
-              id="suggested-action" 
-              name="suggested-action" 
-              rows="2" 
-              placeholder="建议采取的解决措施..."
-            ></textarea>
-          </mat-form-field>
-        </div>
-      </div>
-    </mat-dialog-content>
-    <mat-dialog-actions align="end">
-      <button mat-button (click)="onCancel()">取消</button>
-      <button mat-button color="primary" (click)="onSubmit()" [disabled]="!isFormValid()">
-        提交预警
-      </button>
-    </mat-dialog-actions>
-  `,
-  styles: [`
-    .dialog-content {
-      padding: 10px 0;
-    }
-    
-    .project-info {
-      padding: 12px;
-      background-color: #fff3e0;
-      border-radius: 8px;
-      margin-bottom: 16px;
-    }
-    
-    .form-group {
-      margin-bottom: 20px;
-    }
-    
-    label {
-      display: block;
-      margin-bottom: 6px;
-      font-weight: 500;
-      color: rgba(0, 0, 0, 0.87);
-    }
-    
-    p {
-      margin: 0;
-      color: rgba(0, 0, 0, 0.6);
-    }
-    
-    .form-field {
-      width: 100%;
-    }
-    
-    .mat-mdc-dialog-actions {
-      padding: 16px 24px;
-    }
-  `]
-}) 
-export class ComplaintWarningDialog {
-  complaintLevel: string = '';
-  complaintContent: string = '';
-  potentialRisk: string = '';
-  suggestedAction: string = '';
-
-  constructor(
-    public dialogRef: MatDialogRef<ComplaintWarningDialog>,
-    @Inject(MAT_DIALOG_DATA) public data: ComplaintWarningData
-  ) {}
-
-  onCancel(): void {
-    this.dialogRef.close();
-  }
-
-  isFormValid(): boolean {
-    return !!this.complaintLevel && !!this.complaintContent.trim();
-  }
-
-  onSubmit(): void {
-    if (!this.isFormValid()) return;
-    
-    const formData = {
-      projectId: this.data.projectId,
-      projectName: this.data.projectName,
-      complaintLevel: this.complaintLevel,
-      complaintContent: this.complaintContent,
-      potentialRisk: this.potentialRisk,
-      suggestedAction: this.suggestedAction,
-      reportedAt: new Date().toISOString()
-    };
-    
-    this.dialogRef.close({ confirmed: true, data: formData });
-  }
-}

+ 0 - 162
src/app/pages/customer-service/project-detail/modification-request-dialog.ts

@@ -1,162 +0,0 @@
-import { Component, Inject } from '@angular/core';
-import { CommonModule } from '@angular/common';
-import { FormsModule } from '@angular/forms';
-import { MatButtonModule } from '@angular/material/button';
-import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
-import { MatFormFieldModule } from '@angular/material/form-field';
-import { MatInputModule } from '@angular/material/input';
-import { MatSelectModule } from '@angular/material/select';
-import { MatIconModule } from '@angular/material/icon';
-
-interface ModificationRequestData {
-  projectId: string;
-  projectName: string;
-  projectStatus?: string;
-}
-
-@Component({
-  selector: 'app-modification-request-dialog',
-  standalone: true,
-  imports: [
-    CommonModule,
-    FormsModule,
-    MatButtonModule,
-    MatDialogModule,
-    MatFormFieldModule,
-    MatInputModule,
-    MatSelectModule,
-    MatIconModule
-  ],
-  template: `
-    <h2 mat-dialog-title>申请修改</h2>
-    <mat-dialog-content>
-      <div class="dialog-content">
-        <div class="project-info">
-          <p><strong>项目名称:</strong> {{ data.projectName }}</p>
-        </div>
-        
-        <div class="form-group">
-          <label for="modification-type">修改类型 *</label>
-          <mat-form-field appearance="outline" class="form-field">
-            <mat-select [(ngModel)]="modificationType" id="modification-type" name="modificationType" required>
-              <mat-option value="content">内容修改</mat-option>
-              <mat-option value="design">设计修改</mat-option>
-              <mat-option value="time">时间调整</mat-option>
-              <mat-option value="other">其他</mat-option>
-            </mat-select>
-          </mat-form-field>
-        </div>
-        
-        <div class="form-group">
-          <label for="description">修改说明 *</label>
-          <mat-form-field appearance="outline" class="form-field">
-            <textarea 
-              matInput 
-              [(ngModel)]="description" 
-              id="description" 
-              name="description" 
-              rows="4" 
-              placeholder="请详细描述需要修改的内容..."
-              required
-            ></textarea>
-          </mat-form-field>
-        </div>
-        
-        <div class="form-group">
-          <label for="expected-time">期望完成时间</label>
-          <mat-form-field appearance="outline" class="form-field">
-            <input 
-              matInput 
-              [(ngModel)]="expectedTime" 
-              id="expected-time" 
-              name="expectedTime" 
-              type="text" 
-              placeholder="如:3天内"
-            >
-          </mat-form-field>
-        </div>
-        
-        <div class="form-group">
-          <label for="contact-info">联系人信息</label>
-          <p>{{ contactInfo }}</p>
-        </div>
-      </div>
-    </mat-dialog-content>
-    <mat-dialog-actions align="end">
-      <button mat-button (click)="onCancel()">取消</button>
-      <button mat-button color="primary" (click)="onSubmit()" [disabled]="!isFormValid()">
-        提交申请
-      </button>
-    </mat-dialog-actions>
-  `,
-  styles: [`
-    .dialog-content {
-      padding: 10px 0;
-    }
-    
-    .project-info {
-      padding: 12px;
-      background-color: #f5f5f5;
-      border-radius: 8px;
-      margin-bottom: 16px;
-    }
-    
-    .form-group {
-      margin-bottom: 20px;
-    }
-    
-    label {
-      display: block;
-      margin-bottom: 6px;
-      font-weight: 500;
-      color: rgba(0, 0, 0, 0.87);
-    }
-    
-    p {
-      margin: 0;
-      color: rgba(0, 0, 0, 0.6);
-    }
-    
-    .form-field {
-      width: 100%;
-    }
-    
-    .mat-mdc-dialog-actions {
-      padding: 16px 24px;
-    }
-  `]
-}) 
-export class ModificationRequestDialog {
-  modificationType: string = '';
-  description: string = '';
-  expectedTime: string = '';
-  contactInfo: string = '客服小李 (电话: 138****6789)';
-
-  constructor(
-    public dialogRef: MatDialogRef<ModificationRequestDialog>,
-    @Inject(MAT_DIALOG_DATA) public data: ModificationRequestData
-  ) {}
-
-  onCancel(): void {
-    this.dialogRef.close();
-  }
-
-  isFormValid(): boolean {
-    return !!this.modificationType && !!this.description.trim();
-  }
-
-  onSubmit(): void {
-    if (!this.isFormValid()) return;
-    
-    const formData = {
-      projectId: this.data.projectId,
-      modificationType: this.modificationType,
-      description: this.description,
-      expectedTime: this.expectedTime,
-      contactInfo: this.contactInfo,
-      submittedAt: new Date().toISOString()
-    };
-    
-    this.dialogRef.close({ confirmed: true, data: formData });
-  }
-}

+ 0 - 867
src/app/pages/customer-service/project-detail/project-detail.html

@@ -1,867 +0,0 @@
-<!-- 项目详情页面内容 -->
-<div class="project-detail-container ios-style">
-  <!-- 顶部导航/Header -->
-    <header class="project-header ios-header">
-      <div class="header-left">
-        <div class="project-info">
-          <h1 class="project-title">{{ project()?.name || '现代简约风格三居室设计' }}</h1>
-          <div class="project-meta">
-            <span class="project-status {{ getProjectStatusClass(project()?.status) }}">
-              {{ project()?.status || '进行中' }}
-            </span>
-            <span class="project-stage">当前阶段:{{ project()?.currentStage || '方案修改与确认' }}</span>
-            <span class="project-date">最后更新:{{ formatDate(currentDate()) }}</span>
-          </div>
-        </div>
-      </div>
-      
-      <div class="header-right">
-        <div class="header-actions">
-          <button class="action-btn secondary-btn btn-hover-effect">
-            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-              <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
-              <polyline points="14 2 14 8 20 8"></polyline>
-              <line x1="16" y1="13" x2="8" y2="13"></line>
-              <line x1="16" y1="17" x2="8" y2="17"></line>
-              <polyline points="10 9 9 9 8 9"></polyline>
-            </svg>
-            <span>导出报告</span>
-          </button>
-          <button class="action-btn primary-btn btn-hover-effect">
-            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-              <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path>
-              <polyline points="22 4 12 14.01 9 11.01"></polyline>
-            </svg>
-            <span>联系客户</span>
-          </button>
-          <button class="action-btn back-btn" routerLink="/customer-service/dashboard">
-            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
-              <path d="M19 12H5"></path>
-              <polyline points="12 19 5 12 12 5"></polyline>
-            </svg>
-            返回工作台
-          </button>
-        </div>
-      </div>
-    </header>
-
-  <!-- 主要内容区域 -->
-  <div class="main-content-area ios-content">
-    <!-- 主内容区 (居中) -->
-    <div class="project-content-main ios-main">
-      <!-- 项目进度卡片 -->
-      <div class="card progress-card">
-        <div class="progress-header">
-          <h3>项目进度</h3>
-          <span class="progress-percentage">{{ completionProgress() }}%</span>
-        </div>
-        <div class="progress-bar">
-          <div class="progress-fill" [style.width]="progressFillWidth()"></div>
-        </div>
-        <div class="progress-meta">
-          <span>开始时间:{{ formatDate(project()?.createdAt || '2023-06-01') }}</span>
-          <span>预计完成:{{ formatDate(project()?.deadline || '2023-07-15') }}</span>
-        </div>
-      </div>
-      
-      <!-- 历史服务记录 -->
-      <div class="card historical-records-card">
-        <div class="records-header">
-          <h3>历史服务记录</h3>
-        </div>
-        
-        <!-- 过往咨询记录 -->
-        <div class="record-section">
-          <h4>过往咨询记录</h4>
-          <div class="consultation-list">
-            @for (record of consultationRecords(); track record.id || $index) {
-              <div class="consultation-item">
-                <div class="consultation-date">{{ formatDate(record.date) }}</div>
-                <div class="consultation-content">{{ record.content }}</div>
-                <div class="consultation-status"
-                     [class.status-processed]="record.status === '已解决' || record.status === '成功'"
-                     [class.status-processing]="record.status === '处理中'"
-                     [class.status-pending]="record.status === '待处理'">
-                  {{ record.status }}
-                </div>
-              </div>
-            }
-            </div>
-          </div>
-        </div>
-        
-        <!-- 合作项目 -->
-        <div class="record-section">
-          <h4>合作项目</h4>
-          <div class="projects-list">
-            @for (proj of cooperationProjects(); track proj.id || $index) {
-              <div class="project-item">
-                <div class="project-name">{{ proj.name }}</div>
-                <div class="project-period">{{ formatDate(proj.startDate) }} - {{ formatDate(proj.endDate) }}</div>
-                <div class="project-description">{{ proj.description }}</div>
-                <div class="project-status">{{ proj.status }}</div>
-              </div>
-            }
-            </div>
-          </div>
-        </div>
-        
-        <!-- 历史反馈/评价 -->
-        <div class="record-section">
-          <h4>历史反馈/评价</h4>
-          <div class="feedback-list">
-            @for (feedback of historicalFeedbacks(); track feedback.id || $index) {
-              <div class="feedback-item">
-                <div class="feedback-date">{{ formatDate(feedback.date) }}</div>
-                <div class="feedback-rating">
-                  @for (star of [1,2,3,4,5]; track star) {
-                    <span>
-                      <i class="fa" [ngClass]="{ 'fa-star': star <= feedback.rating, 'fa-star-o': star > feedback.rating }"></i>
-                    </span>
-                  }
-                </div>
-                <div class="feedback-content">{{ feedback.content }}</div>
-                @if (feedback.response) {
-                  <div class="feedback-response">
-                    <strong>回复:</strong>{{ feedback.response }}
-                  </div>
-                }
-              </div>
-            }
-          </div>
-        </div>
-      </div>
-
-      <!-- 进度时间轴 -->
-      <div class="card timeline-card">
-        <div class="timeline-header">
-          <h3 class="card-title">项目阶段时间轴</h3>
-          
-          <!-- 新的流程状态显示条 - 与标题右对齐 -->
-          <div class="process-status-display">
-            <app-process-status-bar 
-              [stageData]="processStatusStages">
-            </app-process-status-bar>
-          </div>
-        </div>
-        
-        <div class="project-timeline">
-          @for (stage of projectStages; track $index; let i = $index) {
-            <div class="timeline-item" [class.stage-completed]="stage.completed" [class.stage-in-progress]="stage.inProgress">
-              <div class="timeline-icon" [class.icon-completed]="stage.completed" [class.icon-in-progress]="stage.inProgress">
-                <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-                  <circle cx="12" cy="12" r="9"></circle>
-                  @if (stage.completed) {
-                    <path d="m5 12 5 5 10-10" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
-                  }
-                  @if (stage.inProgress) {
-                    <circle cx="12" cy="12" r="5"></circle>
-                  }
-                </svg>
-              </div>
-              @if (i < (projectStages?.length || 0) - 1) {
-                <div class="timeline-line" [class.line-completed]="stage.completed && projectStages?.[i+1]?.completed"></div>
-              }
-              <div class="timeline-content">
-                <div class="timeline-header">
-                  <h4 class="stage-title">{{ stage.name }}</h4>
-                  <span class="stage-status">
-                    {{ stage.completed ? '已完成' : stage.inProgress ? '进行中' : '未开始' }}
-                  </span>
-                </div>
-                <div class="timeline-meta">
-                  <div class="stage-dates">
-                    @if (stage.startDate) {
-                      <span class="date-item">开始:{{ formatDate(stage.startDate) }}</span>
-                    }
-                    @if (stage.endDate) {
-                      <span class="date-item">完成:{{ formatDate(stage.endDate) }}</span>
-                    }
-                  </div>
-                  <div class="stage-responsible">负责人:{{ stage.responsible || '未分配' }}</div>
-                </div>
-                @if (stage.details) {
-                  <div class="stage-details">
-                    <p>{{ stage.details }}</p>
-                  </div>
-                }
-              </div>
-            </div>
-          }
-        </div>
-      </div>
-
-      <!-- 项目详情标签页 -->
-      <div class="project-tabs ios-tabs">
-        <div class="tab-header">
-          <button class="tab-btn btn-hover-effect" [class.active]="activeTab() === 'overview'" (click)="switchTab('overview')">
-            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-              <path d="M5 12h14M12 5l7 7-7 7"></path>
-            </svg>
-            <span>概览</span>
-          </button>
-          <button class="tab-btn btn-hover-effect" [class.active]="activeTab() === 'milestones'" (click)="switchTab('milestones')">
-            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-              <polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline>
-            </svg>
-            <span>里程碑</span>
-          </button>
-          <button class="tab-btn btn-hover-effect" [class.active]="activeTab() === 'tasks'" (click)="switchTab('tasks')">
-            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-              <polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline>
-            </svg>
-            <span>任务</span>
-          </button>
-          <button class="tab-btn btn-hover-effect" [class.active]="activeTab() === 'messages'" (click)="switchTab('messages')">
-            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-              <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"></path>
-            </svg>
-            <span>消息</span>
-          </button>
-          <button class="tab-btn btn-hover-effect" [class.active]="activeTab() === 'files'" (click)="switchTab('files')">
-            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-              <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
-              <polyline points="14 2 14 8 20 8"></polyline>
-              <line x1="16" y1="13" x2="8" y2="13"></line>
-              <line x1="16" y1="17" x2="8" y2="17"></line>
-              <polyline points="10 9 9 9 8 9"></polyline>
-            </svg>
-            <span>文件</span>
-          </button>
-        </div>
-
-        <!-- 消息标签内容 -->
-        @if (activeTab() === 'messages') {
-        }
-          <div class="tab-content">
-          <div class="messages-container">
-            <div class="messages-list">
-              @for (message of messages(); track $index) {
-                <div class="message-item">
-                  <div class="message-avatar">
-                    {{ message.sender.charAt(0) }}
-                  </div>
-                  <div class="message-content">
-                    <div class="message-header">
-                      <span class="message-sender">{{ message.sender }}</span>
-                      <span class="message-time">{{ formatDateTime(message.timestamp) }}</span>
-                    </div>
-                    <div class="message-text">{{ message.content }}</div>
-                  </div>
-                </div>
-              }
-            </div>
-            <div class="message-input-area">
-              <textarea 
-                [value]="newMessage()"
-                (input)="onMessageInput($event)"
-                placeholder="输入消息内容..."
-                rows="3"
-                (keydown.enter.shift)="$event.preventDefault()"
-                (keydown.enter)="sendMessage()"
-              ></textarea>
-              <div class="message-actions">
-                <button class="secondary-btn btn-hover-effect">
-                  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-                    <path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"></path>
-                    <polyline points="14 2 14 8 20 8"></polyline>
-                  </svg>
-                  <span>上传文件</span>
-                </button>
-                <button class="primary-btn btn-hover-effect" (click)="sendMessage()" [disabled]="!newMessage().trim()">
-                  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-                    <line x1="22" y1="2" x2="11" y2="13"></line>
-                    <polygon points="22 2 15 22 11 13 2 9 22 2"></polygon>
-                  </svg>
-                  <span>发送</span>
-                </button>
-              </div>
-            </div>
-          </div>
-        </div>
-
-        <!-- 概览标签内容 -->
-        @if (activeTab() === 'overview') {
-          <div class="tab-content">
-            <div class="overview-grid">
-              <!-- 客户信息卡片 -->
-              <div class="info-card">
-                <h4 class="card-title">客户信息</h4>
-                <div class="customer-info">
-                  <div class="info-item">
-                    <label>客户姓名</label>
-                    <span>{{ project()?.customerName || '王先生' }}</span>
-                  </div>
-                  <div class="info-item">
-                    <label>联系方式</label>
-                    <span>138****5678</span>
-                  </div>
-                  <div class="info-item">
-                    <label>标签</label>
-                    <div class="tag-container">
-                      <span class="tag">朋友圈</span>
-                      <span class="tag">软装</span>
-                      <span class="tag">现代风格</span>
-                    </div>
-                  </div>
-                  <div class="info-item">
-                    <label>高优先级需求</label>
-                    <ul class="need-list">
-                      @for (need of project()?.highPriorityNeeds || ['客厅光线充足', '储物空间充足', '环保材料']; track $index) {
-                        <li>
-                          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-                            <polyline points="20 6 9 17 4 12"></polyline>
-                          </svg>
-                          {{ need }}
-                        </li>
-                      }
-                    </ul>
-                  </div>
-                </div>
-              </div>
-
-              <!-- 项目团队卡片 -->
-              <div class="info-card">
-                <h4 class="card-title">项目团队</h4>
-                <div class="team-info">
-                  <div class="team-member">
-                    <div class="member-avatar" title="客服小李">IMG</div>
-                    <div class="member-details">
-                      <div class="member-name">客服小李</div>
-                      <div class="member-role">客户经理</div>
-                    </div>
-                    <button class="message-btn">
-                      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-                        <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"></path>
-                      </svg>
-                    </button>
-                  </div>
-                  <div class="team-member">
-                    <div class="member-avatar" title="张设计师">IMG</div>
-                    <div class="member-details">
-                      <div class="member-name">张设计师</div>
-                      <div class="member-role">主设计师</div>
-                    </div>
-                    <button class="message-btn">
-                      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-                        <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"></path>
-                      </svg>
-                    </button>
-                  </div>
-                </div>
-              </div>
-
-              <!-- 最近反馈卡片 -->
-              <div class="info-card">
-                <h4 class="card-title">客户反馈</h4>
-                <div class="feedback-list">
-                  @for (feedback of feedbacks(); track feedback.id || $index) {
-                    <div class="feedback-item">
-                      <div class="feedback-header">
-                        <div class="feedback-author">{{ getFeedbackCustomerName(feedback) }}</div>
-                        <div class="feedback-rating">
-                          <span class="rating-stars">★★★★☆</span>
-                          <span class="rating-number">{{ getFeedbackRating(feedback) }}.0</span>
-                        </div>
-                      </div>
-                      <div class="feedback-content">{{ feedback?.content || '' }}</div>
-                      @if (feedback?.response) {
-                        <div class="feedback-response">
-                          <div class="response-label">客服回复:</div>
-                          <div class="response-text">{{ feedback.response }}</div>
-                        </div>
-                      }
-                      <div class="feedback-meta">
-                        <span class="feedback-date">{{ formatDate(feedback?.createdAt) }}</span>
-                        <span class="feedback-status" [class.status-processed]="feedback?.status === '已解决'" [class.status-pending]="feedback?.status === '待处理'" [class.status-processing]="feedback?.status === '处理中'">
-                          {{ feedback?.status || '未知状态' }}
-                        </span>
-                      </div>
-                    </div>
-                  }
-                  @if (feedbacks().length > 0) {
-                    <button class="view-all-btn btn-hover-effect">查看全部反馈</button>
-                  }
-                </div>
-              </div>
-            </div>
-            
-            <!-- 订单创建板块 -->
-            <div class="order-creation-section">
-              <div class="section-header">
-                <h4 class="section-title">
-                  <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
-                    <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
-                    <polyline points="14 2 14 8 20 8"></polyline>
-                    <line x1="16" y1="13" x2="8" y2="13"></line>
-                    <line x1="16" y1="17" x2="8" y2="17"></line>
-                  </svg>
-                  订单创建
-                </h4>
-                <button class="toggle-btn" (click)="toggleOrderCreation()" [class.expanded]="isOrderCreationExpanded()">
-                  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
-                    <polyline points="6 9 12 15 18 9"></polyline>
-                  </svg>
-                </button>
-              </div>
-              
-              <div class="order-creation-content" [class.expanded]="isOrderCreationExpanded()">
-                <div class="order-creation-container">
-                  <div class="scrollable-content">
-                    <!-- 客户信息长条栏 -->
-                    <section class="customer-info-bar">
-                    <div class="customer-bar-container">
-                      <!-- 左侧客户搜索和信息 -->
-                      <div class="customer-search-area">
-                        @if (!selectedOrderCustomer()) {
-                          <div class="search-input-container">
-                            <div class="search-icon">
-                              <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
-                                <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
-                                <circle cx="12" cy="7" r="4"></circle>
-                              </svg>
-                            </div>
-                            <input 
-                              type="text" 
-                              [ngModel]="orderSearchKeyword()" 
-                              (ngModelChange)="orderSearchKeyword.set($event); searchOrderCustomer()"
-                              placeholder="输入客户姓名或手机号快速匹配老客户..."
-                              class="customer-search-input"
-                              autocomplete="off"
-                              (keyup.enter)="quickFillOrderCustomerInfo(orderSearchKeyword())"
-                            />
-                            <button 
-                              class="search-action-btn"
-                              (click)="quickFillOrderCustomerInfo(orderSearchKeyword())"
-                              [disabled]="!orderSearchKeyword().trim()"
-                            >
-                              <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
-                                <path d="M9 11l3 3 8-8"></path>
-                                <path d="M21 12c0 4.97-4.03 9-9 9s-9-4.03-9-9 4.03-9 9-9c1.51 0 2.93.37 4.18 1.03"></path>
-                              </svg>
-                              匹配
-                            </button>
-                          </div>
-                          
-                          <!-- 搜索结果下拉 -->
-                          @if (orderSearchResults().length > 0) {
-                            <div class="search-dropdown">
-                              <div class="dropdown-header">
-                                <span>找到 {{ orderSearchResults().length }} 位客户</span>
-                              </div>
-                              @for (customer of orderSearchResults(); track customer.id) {
-                                <div class="customer-option" (click)="selectOrderCustomer(customer)">
-                                  <div class="customer-avatar-sm">
-                                    @if (customer.avatar) {
-                                      <img [src]="customer.avatar" [alt]="customer.name">
-                                    } @else {
-                                      <span>{{ customer.name.charAt(0) }}</span>
-                                    }
-                                  </div>
-                                  <div class="customer-details">
-                                    <div class="customer-name">{{ customer.name }}</div>
-                                    <div class="customer-phone">{{ customer.phone }}</div>
-                                  </div>
-                                  <div class="customer-tags">
-                                    <span class="tag">{{ customer.customerType }}</span>
-                                  </div>
-                                </div>
-                              }
-                            </div>
-                          }
-                        } @else {
-                          <!-- 已选择客户信息展示 -->
-                          <div class="selected-customer-info">
-                            <div class="customer-avatar-lg">
-                              @if (selectedOrderCustomer()?.avatar) {
-                                <img [src]="selectedOrderCustomer()?.avatar" [alt]="selectedOrderCustomer()?.name">
-                              } @else {
-                                <span>{{ selectedOrderCustomer()?.name?.charAt(0) }}</span>
-                              }
-                            </div>
-                            <div class="customer-main-info">
-                              <div class="customer-name">{{ selectedOrderCustomer()?.name }}</div>
-                              <div class="customer-phone">{{ selectedOrderCustomer()?.phone }}</div>
-                              <div class="customer-type">{{ selectedOrderCustomer()?.customerType }}</div>
-                            </div>
-                            <button class="clear-customer-btn" (click)="clearOrderCustomer()">
-                              <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
-                                <line x1="18" y1="6" x2="6" y2="18"></line>
-                                <line x1="6" y1="6" x2="18" y2="18"></line>
-                              </svg>
-                              重新选择
-                            </button>
-                          </div>
-                        }
-                      </div>
-                    </div>
-                  </section>
-
-                  <!-- 项目需求表单 -->
-                  <section class="requirement-section">
-                    <div class="section-header">
-                      <h3>项目需求</h3>
-                    </div>
-                    <form [formGroup]="orderRequirementForm" class="requirement-form">
-                      <div class="form-row">
-                        <div class="form-group">
-                          <label for="projectType">项目类型 <span class="required">*</span></label>
-                          <select id="projectType" formControlName="projectType" class="field-select">
-                            <option value="">请选择项目类型</option>
-                            <option value="residential">住宅设计</option>
-                            <option value="commercial">商业空间</option>
-                            <option value="office">办公空间</option>
-                            <option value="restaurant">餐饮空间</option>
-                          </select>
-                        </div>
-                        <div class="form-group">
-                          <label for="area">面积 <span class="required">*</span></label>
-                          <div class="input-with-unit">
-                            <input type="number" id="area" formControlName="area" class="field-input" placeholder="请输入面积">
-                            <span class="input-unit">㎡</span>
-                          </div>
-                        </div>
-                      </div>
-                      
-                      <div class="form-row">
-                        <div class="form-group">
-                          <label for="style">装修风格</label>
-                          <select id="style" formControlName="style" class="field-select">
-                            <option value="">请选择装修风格</option>
-                            @for (style of decorationStyles; track style.value) {
-                              <option [value]="style.value">{{ style.label }}</option>
-                            }
-                          </select>
-                        </div>
-                        <div class="form-group">
-                          <label for="budget">预算范围</label>
-                          <select id="budget" formControlName="budget" class="field-select">
-                            <option value="">请选择预算范围</option>
-                            <option value="5-10">5-10万</option>
-                            <option value="10-20">10-20万</option>
-                            <option value="20-50">20-50万</option>
-                            <option value="50+">50万以上</option>
-                          </select>
-                        </div>
-                      </div>
-                      
-                      <div class="form-group full-width">
-                        <label for="requirements">具体需求</label>
-                        <textarea id="requirements" formControlName="requirements" class="field-textarea" placeholder="请详细描述您的设计需求..."></textarea>
-                      </div>
-                    </form>
-                  </section>
-                  </div>
-
-                  <!-- 操作按钮 -->
-                  <div class="order-actions">
-                    <button class="secondary-btn" (click)="resetOrderForm()">
-                      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
-                        <polyline points="1 4 1 10 7 10"></polyline>
-                        <path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"></path>
-                      </svg>
-                      重置
-                    </button>
-                    <button class="primary-btn" (click)="createOrder()" [disabled]="!isOrderFormValid()">
-                      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
-                        <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path>
-                        <polyline points="22 4 12 14.01 9 11.01"></polyline>
-                      </svg>
-                      创建订单
-                    </button>
-                  </div>
-                </div>
-              </div>
-            </div>
-          </div>
-        }
-
-        <!-- 里程碑标签内容 -->
-        @if (activeTab() === 'milestones') {
-          <div class="tab-content">
-            <div class="milestones-timeline">
-              @for (milestone of milestones(); track milestone.id || $index; let i = $index) {
-                <div class="milestone-item">
-                  <div class="milestone-dot" [class.completed]="milestone.isCompleted"></div>
-                  @if (i < milestones().length - 1) {
-                    <div class="milestone-line" [class.completed]="milestone.isCompleted && milestones()[i+1].isCompleted"></div>
-                  }
-                  <div class="milestone-content">
-                    <div class="milestone-header">
-                      <h4 class="milestone-title">{{ milestone.title }}</h4>
-                      <span class="milestone-status" [class.status-completed]="milestone.isCompleted" [class.status-pending]="!milestone.isCompleted">
-                        {{ milestone.isCompleted ? '已完成' : '进行中' }}
-                      </span>
-                    </div>
-                    <p class="milestone-description">{{ milestone.description }}</p>
-                    <div class="milestone-dates">
-                      <div class="date-item">
-                        <label>截止日期</label>
-                        <span>{{ formatDate(milestone.dueDate) }}</span>
-                      </div>
-                      @if (milestone.completedDate) {
-                        <div class="date-item">
-                          <label>完成日期</label>
-                          <span>{{ formatDate(milestone.completedDate) }}</span>
-                        </div>
-                      }
-                    </div>
-                    @if (!milestone.isCompleted) {
-                      <div class="milestone-actions">
-                        <button class="primary-btn small btn-hover-effect" (click)="completeMilestone(milestone.id)">
-                          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-                            <polyline points="20 6 9 17 4 12"></polyline>
-                          </svg>
-                          <span>标记完成</span>
-                        </button>
-                      </div>
-                    }
-                  </div>
-                </div>
-              }
-            </div>
-          </div>
-        }
-
-        <!-- 任务标签内容 -->
-        @if (activeTab() === 'tasks') {
-          <div class="tab-content">
-            <div class="tasks-filter">
-              <div class="filter-options">
-                <button class="filter-btn active">全部任务</button>
-                <button class="filter-btn">进行中</button>
-                <button class="filter-btn">已完成</button>
-                <button class="filter-btn">逾期</button>
-              </div>
-              <div class="search-box">
-                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-                  <circle cx="11" cy="11" r="8"></circle>
-                  <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
-                </svg>
-                <input type="text" placeholder="搜索任务...">
-              </div>
-            </div>
-            <div class="tasks-list">
-              <!-- 修复任务列表中的状态显示,确保安全访问 -->
-              @for (task of tasks(); track task.id || $index) {
-                <div class="task-item">
-                  <div class="task-checkbox">
-                    <input type="checkbox" [checked]="task.isCompleted" (change)="task.isCompleted ? null : completeTask(task.id)">
-                  </div>
-                  <div class="task-content">
-                    <h4 class="task-title" [class.completed]="task.isCompleted">{{ task.title || '未命名任务' }}</h4>
-                    <p class="task-description">{{ task.description || '' }}</p>
-                    <div class="task-meta">
-                      <span class="task-assignee">
-                        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-                          <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
-                          <circle cx="9" cy="7" r="4"></circle>
-                          <path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
-                          <path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
-                        </svg>
-                        {{ task.assignee || '未分配' }}
-                      </span>
-                      <span class="task-deadline" [class.overdue]="task.isOverdue">
-                        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-                          <circle cx="12" cy="12" r="10"></circle>
-                          <polyline points="12 6 12 12 16 14"></polyline>
-                        </svg>
-                        {{ formatDate(task.deadline) }}
-                      </span>
-                      <span class="task-priority" [class.priority-high]="task.priority === 'high'" [class.priority-medium]="task.priority === 'medium'" [class.priority-low]="task.priority === 'low'">
-                        {{ task.priority === 'high' ? '高' : task.priority === 'medium' ? '中' : '低' }}
-                      </span>
-                    </div>
-                  </div>
-                </div>
-              }
-            </div>
-          </div>
-        }
-
-        <!-- 消息标签内容 -->
-        @if (activeTab() === 'messages') {
-          <div class="tab-content">
-          <div class="messages-container">
-            <div class="messages-list">
-              @for (message of messages(); track $index) {
-                <div class="message-item">
-                  <div class="message-avatar">
-                    {{ message.sender.charAt(0) }}
-                  </div>
-                  <div class="message-content">
-                    <div class="message-header">
-                      <span class="message-sender">{{ message.sender }}</span>
-                      <span class="message-time">{{ formatDateTime(message.timestamp) }}</span>
-                    </div>
-                    <div class="message-text">{{ message.content }}</div>
-                  </div>
-                </div>
-              }
-            </div>
-      </div>
-    </div>
-
-    <!-- 右侧边栏 - 企业微信聊天集成 -->
-    <div class="wechat-sidebar ios-sidebar">
-      <div class="wechat-header">
-        <h3>项目群聊</h3>
-        <div class="wechat-actions">
-          <button class="search-btn">
-            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-              <circle cx="11" cy="11" r="8"></circle>
-              <path d="m21 21-4.35-4.35"></path>
-            </svg>
-          </button>
-          <button class="settings-btn">
-            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-              <circle cx="12" cy="12" r="3"></circle>
-              <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path>
-            </svg>
-          </button>
-        </div>
-      </div>
-      
-      <!-- 聊天消息列表 -->
-      <div class="wechat-messages" #wechatMessages>
-        @for (msg of wechatMessagesList; track $index) {
-          <div class="wechat-message-item">
-            <div class="message-avatar">
-              {{ msg.sender.charAt(0) }}
-            </div>
-            <div class="message-content">
-              <div class="message-header">
-                <span class="message-sender">{{ msg.sender }}</span>
-                <span class="message-time">{{ formatTime(msg.timestamp) }}</span>
-              </div>
-              <div class="message-text">{{ msg.content }}</div>
-            </div>
-          </div>
-        }
-      </div>
-      
-      <!-- 消息输入框 -->
-      <div class="wechat-input-area">
-        <div class="input-actions">
-          <button class="action-btn btn-hover-effect">
-            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-              <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
-              <circle cx="8.5" cy="8.5" r="1.5"></circle>
-              <polyline points="21 15 16 10 5 21"></polyline>
-            </svg>
-          </button>
-          <button class="action-btn btn-hover-effect">
-            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-              <path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"></path>
-              <polyline points="14 2 14 8 20 8"></polyline>
-            </svg>
-          </button>
-        </div>
-        <input 
-          type="text" 
-          [(ngModel)]="wechatInput" 
-          placeholder="输入消息..." 
-          class="wechat-input"
-          (keydown.enter)="sendWechatMessage()"
-        />
-        <button class="send-btn btn-hover-effect" (click)="sendWechatMessage()" [disabled]="!wechatInput.trim()">
-          发送
-        </button>
-      </div>
-    </div>
-  }
-
-  <!-- 售后处理入口 (固定在底部) -->
-  <div class="after-sales-actions ios-actions">
-    <div class="actions-container">
-      <button class="action-btn primary btn-hover-effect" (click)="openModificationRequest()">
-        <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-          <path d="M12 20h9"></path>
-          <path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"></path>
-        </svg>
-        <span>申请修改</span>
-      </button>
-      <button class="action-btn warning btn-hover-effect" (click)="openComplaintWarning()">
-        <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-          <path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path>
-          <line x1="12" y1="9" x2="12" y2="13"></line>
-          <line x1="12" y1="17" x2="12.01" y2="17"></line>
-        </svg>
-        <span>投诉预警</span>
-      </button>
-      <button class="action-btn secondary btn-hover-effect" (click)="openRefundRequest()">
-        <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-          <path d="M22 12h-4l-3 9L9 3l-3 9H2"></path>
-        </svg>
-        <span>申请退款</span>
-      </button>
-    </div>
-  </div>
-</div>
-
-<!-- 文件标签内容 -->
-@if (activeTab() === 'files') {
-  <div class="tab-content">
-    <div class="files-header">
-      <h4>项目文件</h4>
-      <div class="files-actions">
-        <button class="primary-btn btn-hover-effect" (click)="openRenderPreview()" [disabled]="renderImages().length === 0">
-          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-            <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
-            <circle cx="8.5" cy="8.5" r="1.5"></circle>
-            <polyline points="21 15 16 10 5 21"></polyline>
-          </svg>
-          <span>查看渲染图</span>
-        </button>
-      </div>
-    </div>
-    <div class="files-list">
-      @for (f of files(); track f.id || $index) {
-        <div class="file-item">
-          <div class="file-icon">{{ f.type === 'image' ? 'IMG' : 'DOC' }}</div>
-          <div class="file-info">
-            <div class="file-name">{{ f.name }}</div>
-            <div class="file-meta">{{ f.size }} · 由 {{ f.uploadedBy }} 于 {{ formatDate(f.uploadedAt) }} 上传</div>
-          </div>
-          <div class="file-actions">
-            <button class="link" (click)="previewFile(f)">预览</button>
-            <button class="link" (click)="downloadFile(f)">下载</button>
-          </div>
-        </div>
-      }
-    </div>
-  </div>
-}
-
-<!-- 只读渲染图预览弹窗 -->
-@if (showRenderPreviewModal) {
-  <div class="modal-backdrop" (click)="closeRenderPreview()"></div>
-  <div class="modal" role="dialog" aria-modal="true">
-    <div class="modal-header">
-      <h3>渲染图预览</h3>
-      <button class="close-button" (click)="closeRenderPreview()" aria-label="关闭">×</button>
-    </div>
-    <div class="modal-body">
-      @if (renderImages().length > 0) {
-        <div class="thumb-list">
-          @for (img of renderImages(); track img.id) {
-            <div class="thumb-item">
-              <img [src]="img.url" [alt]="img.name" />
-              <div class="thumb-meta">
-                <div class="name">{{ img.name }}</div>
-                <div class="sub">{{ img.size }} · {{ formatDate(img.uploadedAt) }}</div>
-              </div>
-            </div>
-          }
-        </div>
-      } @else {
-        <div class="empty">
-          <p>暂无可预览的渲染图</p>
-        </div>
-      }
-    </div>
-    <div class="modal-footer">
-      <button class="secondary-btn" (click)="closeRenderPreview()">关闭</button>
-    </div>
-  </div>
-}

+ 0 - 3516
src/app/pages/customer-service/project-detail/project-detail.scss

@@ -1,3516 +0,0 @@
-@use "sass:color";
-
-// 时间轴卡片头部布局
-.timeline-header {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  margin-bottom: 24px;
-  
-  .card-title {
-    margin: 0;
-    flex: 1;
-  }
-  
-  .process-status-display {
-    flex-shrink: 0;
-    margin-left: 16px;
-    
-    // 确保流程状态显示条可见
-    app-process-status-bar {
-      display: block;
-      min-height: 32px;
-    }
-  }
-}
-
-// 流程进度条样式
-.process-progress-section {
-  margin: 24px 0 32px 0;
-  padding: 0;
-  display: block !important;
-  visibility: visible !important;
-  
-  // 确保与标题左对齐
-  margin-left: 0;
-  padding-left: 0;
-}
-
-// iOS风格全局变量
-$primary-color: #007AFF;
-$primary-dark: #0047AB;
-$success-color: #34C759;
-$warning-color: #FF9500;
-$danger-color: #FF3B30;
-$text-primary: #1C1C1E;
-$text-secondary: #636366;
-$text-tertiary: #8E8E93;
-$border-color: #D1D1D6;
-$background-primary: #F2F2F7;
-$background-secondary: #FFFFFF;
-$background-tertiary: #E5E5EA;
-$shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1);
-$shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
-$shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
-$border-radius: 12px;
-$transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
-
-// 兼容背景变量(别名)
-$bg-light: $background-secondary;
-$bg-white: $background-primary;
-
-// iOS风格卡片
-.card {
-  background: $background-secondary;
-  border-radius: $border-radius;
-  padding: 16px;
-  margin-bottom: 16px;
-  box-shadow: $shadow-sm;
-  transition: $transition;
-  
-  &:hover {
-    box-shadow: $shadow-md;
-  }
-}
-
-// 订单创建板块样式 - iOS风格固定区域设计
-.order-creation-section {
-  background: $background-secondary;
-  border-radius: $border-radius;
-  margin-top: 20px;
-  box-shadow: $shadow-sm;
-  overflow: hidden;
-  transition: $transition;
-  position: relative;
-
-  .section-header {
-    display: flex;
-    align-items: center;
-    justify-content: space-between;
-    padding: 16px 20px;
-    background: linear-gradient(135deg, #f8f9ff 0%, #f0f2ff 100%);
-    border-bottom: 1px solid $border-color;
-    cursor: pointer;
-    transition: $transition;
-    position: sticky;
-    top: 0;
-    z-index: 10;
-
-    &:hover {
-      background: linear-gradient(135deg, #f0f2ff 0%, #e8ebff 100%);
-    }
-
-    .section-title {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      margin: 0;
-      font-size: 16px;
-      font-weight: 600;
-      color: $text-primary;
-
-      svg {
-        color: $primary-color;
-      }
-    }
-
-    .toggle-btn {
-      background: none;
-      border: none;
-      padding: 8px;
-      border-radius: 8px;
-      cursor: pointer;
-      transition: $transition;
-      color: $text-secondary;
-
-      &:hover {
-        background: rgba($primary-color, 0.1);
-        color: $primary-color;
-      }
-
-      &.expanded {
-        transform: rotate(180deg);
-      }
-
-      svg {
-        transition: $transition;
-      }
-    }
-  }
-
-  .order-creation-content {
-    height: 0;
-    overflow: hidden;
-    transition: height 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
-
-    &.expanded {
-      height: 600px; // 固定高度
-      transition: height 0.5s cubic-bezier(0.25, 0.8, 0.25, 1);
-    }
-  }
-
-  .order-creation-container {
-    height: 100%;
-    display: flex;
-    flex-direction: column;
-    background: $background-primary;
-  }
-
-  // 可滚动内容区域
-  .scrollable-content {
-    flex: 1;
-    overflow-y: auto;
-    padding: 20px;
-    
-    // iOS风格滚动条
-    &::-webkit-scrollbar {
-      width: 6px;
-    }
-    
-    &::-webkit-scrollbar-track {
-      background: transparent;
-    }
-    
-    &::-webkit-scrollbar-thumb {
-      background: rgba($text-tertiary, 0.3);
-      border-radius: 3px;
-      
-      &:hover {
-        background: rgba($text-tertiary, 0.5);
-      }
-    }
-  }
-
-  // 客户信息长条栏样式
-  .customer-info-bar {
-    background: $background-secondary;
-    border-radius: $border-radius;
-    padding: 16px;
-    margin-bottom: 20px;
-    border: 1px solid $border-color;
-    box-shadow: $shadow-sm;
-
-    .customer-bar-container {
-      width: 100%;
-    }
-
-    .customer-search-area {
-      position: relative;
-    }
-
-    .search-input-container {
-      display: flex;
-      align-items: center;
-      gap: 12px;
-      background: $background-secondary;
-      border: 1px solid $border-color;
-      border-radius: 10px;
-      padding: 12px 16px;
-      transition: $transition;
-
-      &:focus-within {
-        border-color: $primary-color;
-        box-shadow: 0 0 0 3px rgba($primary-color, 0.1);
-      }
-
-      .search-icon {
-        color: $text-tertiary;
-        flex-shrink: 0;
-      }
-
-      .customer-search-input {
-        flex: 1;
-        border: none;
-        outline: none;
-        background: none;
-        font-size: 14px;
-        color: $text-primary;
-
-        &::placeholder {
-          color: $text-tertiary;
-        }
-      }
-
-      .search-action-btn {
-        background: $primary-color;
-        color: white;
-        border: none;
-        border-radius: 6px;
-        padding: 6px 12px;
-        font-size: 12px;
-        cursor: pointer;
-        transition: $transition;
-        display: flex;
-        align-items: center;
-        gap: 4px;
-
-        &:hover:not(:disabled) {
-          background: $primary-dark;
-        }
-
-        &:disabled {
-          background: $text-tertiary;
-          cursor: not-allowed;
-        }
-      }
-    }
-
-    // 搜索结果下拉
-    .search-dropdown {
-      position: absolute;
-      top: 100%;
-      left: 0;
-      right: 0;
-      background: $background-secondary;
-      border: 1px solid $border-color;
-      border-radius: 10px;
-      margin-top: 4px;
-      box-shadow: $shadow-lg;
-      z-index: 1000;
-      max-height: 300px;
-      overflow-y: auto;
-
-      .dropdown-header {
-        padding: 12px 16px;
-        background: $background-primary;
-        border-bottom: 1px solid $border-color;
-        font-size: 12px;
-        color: $text-secondary;
-        font-weight: 500;
-      }
-
-      .customer-option {
-        display: flex;
-        align-items: center;
-        gap: 12px;
-        padding: 12px 16px;
-        cursor: pointer;
-        transition: $transition;
-
-        &:hover {
-          background: $background-primary;
-        }
-
-        .customer-avatar-sm {
-          width: 36px;
-          height: 36px;
-          border-radius: 18px;
-          background: $primary-color;
-          display: flex;
-          align-items: center;
-          justify-content: center;
-          color: white;
-          font-weight: 600;
-          font-size: 14px;
-          flex-shrink: 0;
-
-          img {
-            width: 100%;
-            height: 100%;
-            border-radius: 18px;
-            object-fit: cover;
-          }
-        }
-
-        .customer-details {
-          flex: 1;
-
-          .customer-name {
-            font-weight: 500;
-            color: $text-primary;
-            margin-bottom: 2px;
-          }
-
-          .customer-phone {
-            font-size: 12px;
-            color: $text-secondary;
-          }
-        }
-
-        .customer-tags {
-          .tag {
-            background: $background-primary;
-            color: $text-secondary;
-            padding: 2px 8px;
-            border-radius: 4px;
-            font-size: 11px;
-            font-weight: 500;
-          }
-        }
-      }
-    }
-
-    // 已选择客户信息展示
-    .selected-customer-info {
-      display: flex;
-      align-items: center;
-      gap: 16px;
-      background: $background-secondary;
-      border: 1px solid $success-color;
-      border-radius: 10px;
-      padding: 16px;
-
-      .customer-avatar-lg {
-        width: 48px;
-        height: 48px;
-        border-radius: 24px;
-        background: $primary-color;
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        color: white;
-        font-weight: 600;
-        font-size: 18px;
-        flex-shrink: 0;
-
-        img {
-          width: 100%;
-          height: 100%;
-          border-radius: 24px;
-          object-fit: cover;
-        }
-      }
-
-      .customer-main-info {
-        flex: 1;
-
-        .customer-name {
-          font-weight: 600;
-          color: $text-primary;
-          margin-bottom: 4px;
-          font-size: 16px;
-        }
-
-        .customer-phone {
-          color: $text-secondary;
-          margin-bottom: 2px;
-          font-size: 14px;
-        }
-
-        .customer-type {
-          background: $success-color;
-          color: white;
-          padding: 2px 8px;
-          border-radius: 4px;
-          font-size: 11px;
-          font-weight: 500;
-          display: inline-block;
-        }
-      }
-
-      .clear-customer-btn {
-        background: $background-primary;
-        color: $text-secondary;
-        border: 1px solid $border-color;
-        border-radius: 6px;
-        padding: 8px 12px;
-        font-size: 12px;
-        cursor: pointer;
-        transition: $transition;
-        display: flex;
-        align-items: center;
-        gap: 4px;
-
-        &:hover {
-          background: $danger-color;
-          color: white;
-          border-color: $danger-color;
-        }
-      }
-    }
-  }
-
-  // 项目需求表单样式
-  .requirement-section {
-    background: $background-secondary;
-    border-radius: $border-radius;
-    padding: 16px;
-    margin-bottom: 20px;
-    border: 1px solid $border-color;
-    box-shadow: $shadow-sm;
-
-    .section-header {
-      margin-bottom: 16px;
-
-      h3 {
-        margin: 0;
-        font-size: 16px;
-        font-weight: 600;
-        color: $text-primary;
-      }
-    }
-
-    .requirement-form {
-      .form-row {
-        display: flex;
-        gap: 16px;
-        margin-bottom: 16px;
-
-        @media (max-width: 768px) {
-          flex-direction: column;
-          gap: 12px;
-        }
-      }
-
-      .form-group {
-        flex: 1;
-
-        &.full-width {
-          width: 100%;
-        }
-
-        label {
-          display: block;
-          margin-bottom: 6px;
-          font-weight: 500;
-          color: $text-primary;
-          font-size: 14px;
-
-          .required {
-            color: $danger-color;
-            margin-left: 2px;
-          }
-        }
-
-        .field-input,
-        .field-select,
-        .field-textarea {
-          width: 100%;
-          padding: 12px 16px;
-          border: 1px solid $border-color;
-          border-radius: 8px;
-          font-size: 14px;
-          color: $text-primary;
-          background: $background-secondary;
-          transition: $transition;
-
-          &:focus {
-            outline: none;
-            border-color: $primary-color;
-            box-shadow: 0 0 0 3px rgba($primary-color, 0.1);
-          }
-
-          &::placeholder {
-            color: $text-tertiary;
-          }
-        }
-
-        .field-textarea {
-          min-height: 80px;
-          resize: vertical;
-        }
-
-        .input-with-unit {
-          position: relative;
-          display: flex;
-          align-items: center;
-
-          .field-input {
-            padding-right: 40px;
-          }
-
-          .input-unit {
-            position: absolute;
-            right: 16px;
-            color: $text-secondary;
-            font-size: 14px;
-            pointer-events: none;
-          }
-        }
-      }
-    }
-  }
-
-  // 底部固定按钮区域
-  .order-actions {
-    position: sticky;
-    bottom: 0;
-    background: $background-secondary;
-    border-top: 1px solid $border-color;
-    padding: 16px 20px;
-    display: flex;
-    gap: 12px;
-    justify-content: flex-end;
-    box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.1);
-    z-index: 5;
-
-    .secondary-btn,
-    .primary-btn {
-      padding: 12px 24px;
-      border-radius: 10px;
-      font-size: 14px;
-      font-weight: 600;
-      cursor: pointer;
-      transition: $transition;
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      border: none;
-      min-width: 120px;
-      justify-content: center;
-
-      svg {
-        width: 16px;
-        height: 16px;
-      }
-    }
-
-    .secondary-btn {
-      background: $background-primary;
-      color: $text-secondary;
-      border: 1px solid $border-color;
-
-      &:hover {
-        background: $background-tertiary;
-        color: $text-primary;
-        transform: translateY(-1px);
-        box-shadow: $shadow-sm;
-      }
-    }
-
-    .primary-btn {
-      background: $primary-color;
-      color: white;
-      box-shadow: $shadow-sm;
-
-      &:hover:not(:disabled) {
-        background: $primary-dark;
-        transform: translateY(-1px);
-        box-shadow: $shadow-md;
-      }
-
-      &:disabled {
-        background: $text-tertiary;
-        cursor: not-allowed;
-        transform: none;
-        box-shadow: none;
-      }
-    }
-  }
-}
-
-// Members & Requirements — iOS styled details and micro-interactions
-.project-detail-container {
-  // Members tab
-  .members-header {
-    display: flex;
-    align-items: center;
-    justify-content: space-between;
-    margin: 12px 0 8px;
-
-    h4 {
-      margin: 0;
-      font-size: 16px;
-      font-weight: 600;
-      color: $text-primary;
-    }
-
-    .actions {
-      display: flex;
-      gap: 8px;
-
-      .primary-btn,
-      .secondary-btn {
-        height: 34px;
-        padding: 0 14px;
-        border-radius: 10px;
-      }
-    }
-  }
-
-  .members-list {
-    background-color: $bg-white;
-    border: 1px solid $border-color;
-    border-radius: 12px;
-    box-shadow: $shadow-sm;
-    padding: 8px;
-  }
-
-  .member-row {
-    display: flex;
-    align-items: center;
-    gap: 12px;
-    padding: 12px 10px;
-    border-bottom: 1px dashed color-mix(in srgb, $border-color 70%, transparent);
-    transition: $transition;
-
-    &:last-child { border-bottom: none; }
-
-    .role {
-      flex: 0 0 120px;
-      font-size: 13px;
-      color: $text-secondary;
-    }
-
-    .name { flex: 1; }
-
-    &.editable:hover { background-color: $bg-light; }
-  }
-
-  .member-input {
-    width: 100%;
-    background: $bg-light;
-    border: 1px solid $border-color;
-    border-radius: 10px;
-    padding: 8px 12px;
-    font-size: 14px;
-    color: $text-primary;
-    outline: none;
-    transition: $transition;
-
-    &::placeholder { color: $text-tertiary; }
-
-    &:focus {
-      background: $bg-white;
-      border-color: $primary-color;
-      box-shadow: 0 0 0 3px color-mix(in srgb, $primary-color 15%, transparent);
-      transform: translateY(-1px);
-    }
-  }
-
-  // Requirements tab
-  .requirements-header {
-    margin: 12px 0 8px;
-
-    h4 {
-      margin: 0;
-      font-size: 16px;
-      font-weight: 600;
-      color: $text-primary;
-    }
-  }
-
-  .requirements-form {
-    background: $bg-white;
-    border: 1px solid $border-color;
-    border-radius: 12px;
-    box-shadow: $shadow-sm;
-    padding: 12px;
-    display: flex;
-    flex-direction: column;
-    gap: 12px;
-
-    .form-row {
-      display: flex;
-      flex-direction: column;
-      gap: 6px;
-
-      label {
-        font-size: 13px;
-        color: $text-secondary;
-      }
-
-      .form-input,
-      .form-textarea {
-        background: $bg-light;
-        border: 1px solid $border-color;
-        border-radius: 12px;
-        padding: 10px 12px;
-        font-size: 14px;
-        color: $text-primary;
-        outline: none;
-        transition: $transition;
-
-        &::placeholder { color: $text-tertiary; }
-
-        &:focus {
-          background: $bg-white;
-          border-color: $primary-color;
-          box-shadow: 0 0 0 3px color-mix(in srgb, $primary-color 15%, transparent);
-          transform: translateY(-1px);
-        }
-      }
-
-      .form-textarea { resize: vertical; min-height: 84px; }
-    }
-
-    .form-actions {
-      display: flex;
-      justify-content: flex-end;
-      gap: 10px;
-      padding-top: 8px;
-      border-top: 1px solid color-mix(in srgb, $border-color 70%, transparent);
-    }
-  }
-}
-
-// Responsive fine-tuning
-@media (max-width: 768px) {
-  .project-detail-container {
-    .members-list { padding: 6px; }
-    .member-row {
-      padding: 10px 8px;
-      .role { flex-basis: 88px; }
-    }
-
-    .requirements-form { padding: 10px; }
-  }
-}
-
-// iOS风格按钮
-.button {
-  border-radius: $border-radius;
-  padding: 12px 20px;
-  font-weight: 500;
-  transition: $transition;
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  
-  &-primary {
-    background: $primary-color;
-    color: white;
-    
-    &:active {
-      background: #0E42CB;
-    }
-  }
-  
-  &-secondary {
-    background: $background-tertiary;
-    color: $text-primary;
-    
-    &:active {
-      background: #D1D1D6;
-    }
-  }
-}
-
-// 按钮点击反馈效果
-.btn-hover-effect {
-  transition: $transition;
-  transform: scale(1);
-  position: relative;
-  overflow: hidden;
-}
-
-.btn-hover-effect:active {
-  transform: scale(0.95);
-  transition: all 0.1s ease;
-}
-
-.btn-hover-effect::after {
-  content: '';
-  position: absolute;
-  top: 50%;
-  left: 50%;
-  width: 0;
-  height: 0;
-  border-radius: 50%;
-  background-color: rgba(255, 255, 255, 0.3);
-  transform: translate(-50%, -50%);
-  transition: width 0.6s, height 0.6s;
-}
-
-.btn-hover-effect:active::after {
-  width: 300px;
-  height: 300px;
-}
-
-// 主要按钮的特定点击效果
-.primary-btn.btn-hover-effect:active {
-  background-color: #0E42CB;
-}
-
-// 次要按钮的特定点击效果
-.secondary-btn.btn-hover-effect:active {
-  background-color: #E5E6EB;
-}
-
-// 操作按钮的特定点击效果
-.action-btn.btn-hover-effect:active {
-  transform: scale(0.9);
-  background-color: color-mix(in srgb, $primary-color 5%, transparent);
-}
-
-// 主容器
-.project-detail-container {
-  display: flex;
-  flex-direction: column;
-  height: 100vh;
-  overflow: hidden;
-}
-
-// 顶部导航/Header
-.project-header-blue {
-  background-color: $primary-dark;
-  color: white;
-  padding: 16px 24px;
-  box-shadow: $shadow-md;
-  z-index: 100;
-
-  .header-content {
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-    max-width: 1400px;
-    margin: 0 auto;
-  }
-
-  .project-info {
-    flex: 1;
-  }
-
-  .project-title {
-    font-size: 24px;
-    font-weight: 600;
-    margin-bottom: 8px;
-  }
-
-  .project-meta {
-    display: flex;
-    gap: 16px;
-    font-size: 14px;
-  }
-
-  .project-status {
-    padding: 4px 8px;
-    border-radius: 4px;
-    font-weight: 500;
-    background-color: rgba(255, 255, 255, 0.2);
-  }
-
-  .project-stage,
-  .project-date {
-    opacity: 0.9;
-  }
-
-  .header-actions {
-    display: flex;
-    gap: 12px;
-  }
-
-  .primary-btn,
-  .secondary-btn {
-    color: white;
-    background-color: rgba(255, 255, 255, 0.2);
-    border: 1px solid rgba(255, 255, 255, 0.3);
-    padding: 8px 16px;
-    border-radius: $border-radius;
-    cursor: pointer;
-    display: flex;
-    align-items: center;
-    gap: 6px;
-    transition: $transition;
-
-    &:hover {
-      background-color: rgba(255, 255, 255, 0.3);
-    }
-  }
-
-  .primary-btn {
-    background-color: $primary-color;
-    border-color: $primary-color;
-
-    &:hover {
-      background-color: #0E42CB;
-    }
-  }
-}
-
-// iOS风格项目头部样式
-.project-header {
-  background: rgba(255, 255, 255, 0.95);
-  backdrop-filter: blur(20px);
-  border-bottom: 1px solid rgba(0, 0, 0, 0.1);
-  padding: 16px 24px;
-  position: sticky;
-  top: 0;
-  z-index: 100;
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  min-height: 80px;
-
-  .header-left {
-    flex: 1;
-    
-    .project-info {
-      .project-title {
-        font-size: 24px;
-        font-weight: 600;
-        color: #1a1a1a;
-        margin: 0 0 8px 0;
-        line-height: 1.2;
-      }
-
-      .project-meta {
-        display: flex;
-        align-items: center;
-        gap: 16px;
-        font-size: 14px;
-        color: #666;
-
-        .project-status {
-          padding: 4px 12px;
-          border-radius: 12px;
-          font-weight: 500;
-          font-size: 12px;
-
-          &.active { background: #e8f5e8; color: #2d7d32; }
-          &.pending { background: #fff3e0; color: #f57c00; }
-          &.completed { background: #e3f2fd; color: #1976d2; }
-        }
-      }
-    }
-  }
-
-  .header-right {
-    .header-actions {
-      display: flex;
-      align-items: center;
-      gap: 12px;
-
-      .action-btn {
-        display: flex;
-        align-items: center;
-        gap: 8px;
-        padding: 10px 16px;
-        border: none;
-        border-radius: 8px;
-        font-size: 14px;
-        font-weight: 500;
-        cursor: pointer;
-        transition: all 0.2s ease;
-        text-decoration: none;
-        white-space: nowrap;
-
-        svg {
-          width: 16px;
-          height: 16px;
-          flex-shrink: 0;
-        }
-
-        &:hover {
-          transform: translateY(-1px);
-          box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
-        }
-
-        &:active {
-          transform: translateY(0);
-        }
-
-        &.primary-btn {
-          background: #007AFF;
-          color: white;
-          
-          &:hover {
-            background: #0056CC;
-          }
-        }
-
-        &.secondary-btn {
-          background: #f8f9fa;
-          color: #495057;
-          border: 1px solid #dee2e6;
-          
-          &:hover {
-            background: #e9ecef;
-            border-color: #adb5bd;
-          }
-        }
-
-        &.back-btn {
-          background: #28a745;
-          color: white;
-          
-          &:hover {
-            background: #218838;
-          }
-        }
-      }
-    }
-  }
-}
-
-// 响应式设计
-@media (max-width: 768px) {
-  .project-header {
-    flex-direction: column;
-    align-items: stretch;
-    gap: 16px;
-    padding: 16px;
-
-    .header-left {
-      .project-info {
-        .project-title {
-          font-size: 20px;
-        }
-
-        .project-meta {
-          flex-wrap: wrap;
-          gap: 8px;
-        }
-      }
-    }
-
-    .header-right {
-      .header-actions {
-        justify-content: flex-end;
-        flex-wrap: wrap;
-
-        .action-btn {
-          padding: 8px 12px;
-          font-size: 13px;
-        }
-      }
-    }
-  }
-}
-
-// 主要内容区域
-.main-content-area {
-  display: flex;
-  flex: 1;
-  overflow: hidden;
-}
-
-// 主内容区 (居中)
-.project-content-main {
-  flex: 1;
-  overflow-y: auto;
-  padding: 24px;
-  background-color: $background-secondary;
-}
-
-// 右侧边栏 - 企业微信聊天集成
-.wechat-sidebar {
-  width: 350px;
-  background-color: $background-tertiary;
-  border-left: 1px solid $border-color;
-  display: flex;
-  flex-direction: column;
-  overflow: hidden;
-}
-
-.wechat-header {
-  padding: 16px 20px;
-  border-bottom: 1px solid $border-color;
-  background-color: $background-primary;
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-}
-
-.wechat-header h3 {
-  font-size: 16px;
-  font-weight: 600;
-  color: $text-primary;
-}
-
-.wechat-actions {
-  display: flex;
-  gap: 8px;
-}
-
-.search-btn,
-.settings-btn {
-  background: none;
-  border: none;
-  padding: 6px;
-  border-radius: 4px;
-  cursor: pointer;
-  color: $text-secondary;
-  transition: $transition;
-
-  &:hover {
-    background-color: $background-tertiary;
-    color: $primary-color;
-  }
-}
-
-.wechat-messages {
-  flex: 1;
-  overflow-y: auto;
-  padding: 16px;
-}
-
-.wechat-message-item {
-  display: flex;
-  gap: 12px;
-  margin-bottom: 16px;
-}
-
-.message-avatar {
-  width: 36px;
-  height: 36px;
-  border-radius: 50%;
-  background-color: $primary-color;
-  color: white;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  font-weight: 600;
-  font-size: 14px;
-  flex-shrink: 0;
-}
-
-.message-content {
-  flex: 1;
-}
-
-.message-header {
-  display: flex;
-  justify-content: space-between;
-  margin-bottom: 4px;
-  font-size: 12px;
-}
-
-.message-sender {
-  font-weight: 600;
-  color: $text-primary;
-}
-
-.message-time {
-  color: $text-tertiary;
-}
-
-.message-text {
-  background-color: $background-primary;
-  padding: 8px 12px;
-  border-radius: $border-radius;
-  color: $text-primary;
-  line-height: 1.5;
-}
-
-.wechat-input-area {
-  padding: 16px;
-  border-top: 1px solid $border-color;
-  background-color: $background-primary;
-  display: flex;
-  gap: 8px;
-  align-items: flex-end;
-}
-
-.input-actions {
-  display: flex;
-  flex-direction: column;
-  gap: 8px;
-}
-
-.action-btn {
-  background: none;
-  border: none;
-  padding: 8px;
-  border-radius: 4px;
-  cursor: pointer;
-  color: $text-secondary;
-  transition: $transition;
-
-  &:hover {
-    background-color: $background-tertiary;
-    color: $primary-color;
-  }
-}
-
-.wechat-input {
-  flex: 1;
-  padding: 10px 12px;
-  border: 1px solid $border-color;
-  border-radius: $border-radius;
-  outline: none;
-  font-size: 14px;
-  transition: $transition;
-
-  &:focus {
-    border-color: $primary-color;
-  }
-}
-
-.send-btn {
-  padding: 10px 16px;
-  background-color: $primary-color;
-  color: white;
-  border: none;
-  border-radius: $border-radius;
-  cursor: pointer;
-  font-size: 14px;
-  transition: $transition;
-
-  &:hover:not(:disabled) {
-    background-color: $primary-dark;
-  }
-
-  &:disabled {
-    background-color: $text-tertiary;
-    cursor: not-allowed;
-  }
-}
-
-// 售后处理入口 (固定在底部)
-.after-sales-actions {
-  position: fixed;
-  bottom: 0;
-  left: 0;
-  right: 350px; /* 与右侧边栏宽度一致 */
-  background-color: $background-primary;
-  border-top: 1px solid $border-color;
-  padding: 16px 24px;
-  box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.05);
-  z-index: 100;
-}
-
-.actions-container {
-  max-width: 1000px;
-  margin: 0 auto;
-  display: flex;
-  justify-content: center;
-  gap: 16px;
-}
-
-.action-btn {
-  display: flex;
-  align-items: center;
-  gap: 8px;
-  padding: 10px 20px;
-  border-radius: $border-radius;
-  border: none;
-  cursor: pointer;
-  font-size: 14px;
-  font-weight: 500;
-  transition: $transition;
-}
-
-.action-btn.primary {
-  background-color: $primary-dark;
-  color: white;
-
-  &:hover {
-    background-color: #0a2340;
-  }
-}
-
-.action-btn.warning {
-  background-color: $warning-color;
-  color: white;
-
-  &:hover {
-    background-color: #e57000;
-  }
-}
-
-.action-btn.secondary {
-  background-color: $background-tertiary;
-  color: $text-primary;
-  border: 1px solid $border-color;
-
-  &:hover {
-    background-color: $background-secondary;
-  }
-}
-
-// 进度卡片
-.progress-card {
-  background-color: $background-primary;
-  border-radius: $border-radius;
-  padding: 20px;
-  margin-bottom: 24px;
-  box-shadow: $shadow-sm;
-}
-
-.progress-header {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  margin-bottom: 12px;
-}
-
-.progress-header h3 {
-  font-size: 16px;
-  font-weight: 600;
-  color: $text-primary;
-}
-
-.progress-percentage {
-  font-size: 24px;
-  font-weight: 600;
-  color: $primary-color;
-}
-
-.progress-bar {
-  height: 8px;
-  background-color: $background-tertiary;
-  border-radius: 4px;
-  overflow: hidden;
-  margin-bottom: 12px;
-}
-
-.progress-fill {
-  height: 100%;
-  background-color: $primary-color;
-  transition: width 0.3s ease;
-}
-
-.progress-meta {
-  display: flex;
-  justify-content: space-between;
-  font-size: 12px;
-  color: $text-tertiary;
-}
-
-// 进度时间轴卡片
-.timeline-card {
-  background-color: $background-primary;
-  border-radius: $border-radius;
-  padding: 20px;
-  margin-bottom: 24px;
-  box-shadow: $shadow-sm;
-}
-
-.card-title {
-  font-size: 16px;
-  font-weight: 600;
-  color: $text-primary;
-  margin-bottom: 20px;
-}
-
-.project-timeline {
-  position: relative;
-  padding-left: 30px;
-}
-
-.timeline-item {
-  position: relative;
-  padding-bottom: 24px;
-  transition: $transition;
-
-  &:hover {
-    background-color: $background-tertiary;
-    border-radius: $border-radius;
-  }
-}
-
-.timeline-icon {
-  position: absolute;
-  left: -30px;
-  top: 4px;
-  width: 24px;
-  height: 24px;
-  border-radius: 50%;
-  background-color: $background-tertiary;
-  border: 2px solid $border-color;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  transition: $transition;
-}
-
-.timeline-icon.icon-completed {
-  background-color: $success-color;
-  border-color: $success-color;
-  color: white;
-}
-
-.timeline-icon.icon-in-progress {
-  background-color: $primary-color;
-  border-color: $primary-color;
-  color: white;
-  animation: pulse 2s infinite;
-}
-
-.timeline-line {
-  position: absolute;
-  left: -24px;
-  top: 28px;
-  width: 2px;
-  height: calc(100% + 4px);
-  background-color: $border-color;
-}
-
-.timeline-line.line-completed {
-  background-color: $success-color;
-}
-
-.timeline-content {
-  padding: 8px 16px;
-}
-
-.timeline-header {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  margin-bottom: 8px;
-}
-
-.stage-title {
-  font-size: 14px;
-  font-weight: 600;
-  color: $text-primary;
-}
-
-.stage-status {
-  font-size: 12px;
-  padding: 2px 8px;
-  border-radius: 12px;
-  background-color: $background-tertiary;
-  color: $text-secondary;
-}
-
-.stage-item.stage-completed .stage-status {
-  background-color: color-mix(in srgb, $success-color 10%, transparent);
-  color: $success-color;
-}
-
-.stage-item.stage-in-progress .stage-status {
-  background-color: color-mix(in srgb, $primary-color 10%, transparent);
-  color: $primary-color;
-}
-
-.timeline-meta {
-  font-size: 12px;
-  color: $text-tertiary;
-  margin-bottom: 8px;
-}
-
-.stage-dates {
-  display: flex;
-  gap: 16px;
-  margin-bottom: 4px;
-}
-
-.stage-details {
-  font-size: 13px;
-  color: $text-secondary;
-  line-height: 1.4;
-}
-
-// 标签页样式
-.project-tabs {
-  background-color: $background-primary;
-  border-radius: $border-radius;
-  box-shadow: $shadow-sm;
-  overflow: hidden;
-}
-
-.tab-header {
-  display: flex;
-  border-bottom: 1px solid $border-color;
-  background-color: $background-tertiary;
-}
-
-.tab-btn {
-  display: flex;
-  align-items: center;
-  gap: 8px;
-  padding: 14px 20px;
-  border: none;
-  background: none;
-  cursor: pointer;
-  font-size: 14px;
-  color: $text-secondary;
-  transition: $transition;
-
-  &:hover {
-    color: $primary-color;
-    background-color: color-mix(in srgb, $primary-color 5%, transparent);
-  }
-
-  &.active {
-    color: $primary-color;
-    background-color: $background-primary;
-    border-bottom: 2px solid $primary-color;
-  }
-}
-
-.tab-content {
-  padding: 20px;
-}
-
-// iOS风格动画效果
-@keyframes pulse {
-  0% {
-    transform: scale(1);
-    opacity: 1;
-  }
-  50% {
-    transform: scale(0.95);
-    opacity: 0.8;
-  }
-  100% {
-    transform: scale(1);
-    opacity: 1;
-  }
-}
-
-@keyframes fadeIn {
-  from {
-    opacity: 0;
-    transform: translateY(10px);
-  }
-  to {
-    opacity: 1;
-    transform: translateY(0);
-  }
-}
-
-@keyframes sending {
-  0% {
-    transform: scale(1);
-  }
-  50% {
-    transform: scale(0.9);
-  }
-  100% {
-    transform: scale(1);
-  }
-}
-
-@keyframes completing {
-  0% {
-    transform: translateY(0);
-    opacity: 1;
-  }
-  100% {
-    transform: translateY(-20px);
-    opacity: 0;
-  }
-}
-
-// 按钮发送动画
-.sending {
-  animation: sending 0.3s ease;
-}
-
-// 任务完成动画
-.completing {
-  animation: completing 0.5s ease forwards;
-}
-
-// 消息进入动画
-.message-item {
-  animation: fadeIn 0.3s ease;
-}
-
-// iOS风格弹性动画
-.ios-bounce {
-  transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
-}
-
-// 标签切换动画
-.tab-content {
-  animation: fadeIn 0.3s ease;
-}
-
-// 响应式设计
-@media (max-width: 1200px) {
-  .wechat-sidebar {
-    width: 300px;
-  }
-  
-  .after-sales-actions {
-    right: 300px;
-  }
-}
-
-@media (max-width: 768px) {
-  .main-content-area {
-    flex-direction: column;
-  }
-  
-  .wechat-sidebar {
-    width: 100%;
-    height: 0;
-    border-left: none;
-    border-top: 1px solid $border-color;
-  }
-  
-  .wechat-sidebar.show {
-    height: 400px;
-  }
-  
-  .after-sales-actions {
-    right: 0;
-    bottom: 0;
-  }
-  
-  .actions-container {
-    flex-wrap: wrap;
-  }
-}
-
-// 原有样式变量兼容
-$bg-light: $background-secondary;
-$bg-white: $background-primary;
-$box-shadow: $shadow-md;
-$text-light: $text-tertiary;
-
-// 顶部导航栏
-.top-navbar {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  padding: 0 24px;
-  height: 64px;
-  background-color: $bg-white;
-  border-bottom: 1px solid $border-color;
-  box-shadow: $box-shadow;
-
-  .navbar-left {
-    display: flex;
-    align-items: center;
-    gap: 16px;
-
-    .menu-toggle {
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      width: 40px;
-      height: 40px;
-      border: none;
-      background: none;
-      color: $text-primary;
-      cursor: pointer;
-      transition: $transition;
-
-      &:hover {
-        color: $primary-color;
-        background-color: $bg-light;
-        border-radius: 4px;
-      }
-    }
-
-    .app-title {
-      font-size: 18px;
-      font-weight: 600;
-      color: $text-primary;
-      margin: 0;
-    }
-  }
-
-  .navbar-right {
-    display: flex;
-    align-items: center;
-    gap: 16px;
-
-    .notification-btn {
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      width: 40px;
-      height: 40px;
-      border: none;
-      background: none;
-      color: $text-primary;
-      cursor: pointer;
-      position: relative;
-      transition: $transition;
-
-      &:hover {
-        color: $primary-color;
-        background-color: $bg-light;
-        border-radius: 4px;
-      }
-
-      .notification-badge {
-        position: absolute;
-        top: 8px;
-        right: 8px;
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        width: 16px;
-        height: 16px;
-        font-size: 10px;
-        font-weight: 600;
-        color: white;
-        background-color: $danger-color;
-        border-radius: 50%;
-      }
-    }
-
-    .user-profile {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-      padding: 8px 12px;
-      border-radius: 6px;
-      transition: $transition;
-
-      &:hover {
-        background-color: $bg-light;
-      }
-
-      .user-avatar {
-        width: 32px;
-        height: 32px;
-        border-radius: 50%;
-        object-fit: cover;
-      }
-
-      .user-name {
-        font-size: 14px;
-        font-weight: 500;
-        color: $text-primary;
-      }
-    }
-  }
-}
-
-// 主要内容区
-.dashboard-content {
-  display: flex;
-  flex: 1;
-  overflow: hidden;
-}
-
-// 左侧侧边栏
-.sidebar {
-  width: 240px;
-  background-color: $bg-white;
-  border-right: 1px solid $border-color;
-  overflow-y: auto;
-  flex-shrink: 0;
-
-  .sidebar-nav {
-    padding: 16px 0;
-
-    .nav-item {
-      display: flex;
-      align-items: center;
-      gap: 12px;
-      padding: 12px 24px;
-      color: $text-secondary;
-      text-decoration: none;
-      transition: $transition;
-
-      &:hover {
-        color: $primary-color;
-        background-color: $bg-light;
-      }
-
-      &.active {
-        color: $primary-color;
-        background-color: #e3f2fd;
-        border-left: 3px solid $primary-color;
-        padding-left: 21px;
-      }
-
-      svg {
-        flex-shrink: 0;
-      }
-
-      span {
-        font-size: 14px;
-        font-weight: 500;
-      }
-    }
-  }
-}
-
-// 右侧主要内容
-.project-content {
-  flex: 1;
-  overflow-y: auto;
-  padding: 24px;
-
-  // 项目头部信息
-  .project-header {
-    display: flex;
-    justify-content: space-between;
-    align-items: flex-start;
-    margin-bottom: 24px;
-
-    .project-title-section {
-      .project-title {
-        font-size: 24px;
-        font-weight: 600;
-        color: $text-primary;
-        margin: 0 0 8px 0;
-      }
-
-      .project-meta {
-        display: flex;
-        align-items: center;
-        gap: 16px;
-
-        .project-status {
-          padding: 4px 12px;
-          border-radius: 16px;
-          font-size: 12px;
-          font-weight: 500;
-          text-align: center;
-
-          &.status-active {
-            color: $primary-color;
-            background-color: #e3f2fd;
-          }
-
-          &.status-pending {
-            color: $warning-color;
-            background-color: #fff3e0;
-          }
-
-          &.status-completed {
-            color: $success-color;
-            background-color: #e8f5e9;
-          }
-        }
-
-        .project-stage {
-          font-size: 14px;
-          color: $text-secondary;
-        }
-      }
-    }
-
-    .project-actions {
-      display: flex;
-      gap: 12px;
-
-      .primary-btn,
-      .secondary-btn {
-        display: flex;
-        align-items: center;
-        gap: 8px;
-        padding: 8px 16px;
-        border: none;
-        border-radius: 6px;
-        font-size: 14px;
-        font-weight: 500;
-        cursor: pointer;
-        transition: $transition;
-      }
-
-      .primary-btn {
-        color: white;
-        background-color: $primary-color;
-
-        &:hover {
-          background-color: #1565c0;
-        }
-
-        &:disabled {
-          background-color: $text-tertiary;
-          cursor: not-allowed;
-        }
-      }
-
-      .secondary-btn {
-        color: $text-primary;
-        background-color: $bg-white;
-        border: 1px solid $border-color;
-
-        &:hover {
-          background-color: $bg-light;
-        }
-      }
-
-      .primary-btn.small {
-        padding: 4px 12px;
-        font-size: 12px;
-      }
-    }
-  }
-
-  // 项目进度卡片
-  .progress-card {
-    padding: 20px;
-    margin-bottom: 24px;
-    background-color: $bg-white;
-    border-radius: 8px;
-    box-shadow: $box-shadow;
-
-    .progress-header {
-      display: flex;
-      justify-content: space-between;
-      align-items: center;
-      margin-bottom: 12px;
-
-      h3 {
-        font-size: 16px;
-        font-weight: 600;
-        color: $text-primary;
-        margin: 0;
-      }
-
-      .progress-percentage {
-        font-size: 20px;
-        font-weight: 700;
-        color: $primary-color;
-      }
-    }
-
-    .progress-bar {
-      height: 8px;
-      margin-bottom: 12px;
-      background-color: $bg-light;
-      border-radius: 4px;
-      overflow: hidden;
-
-      .progress-fill {
-        height: 100%;
-        background-color: $primary-color;
-        border-radius: 4px;
-        transition: $transition;
-      }
-    }
-
-    .progress-meta {
-      display: flex;
-      justify-content: space-between;
-      font-size: 12px;
-      color: $text-tertiary;
-    }
-  }
-
-  // 项目详情标签页
-  .project-tabs {
-    background-color: $bg-white;
-    border-radius: 8px;
-    box-shadow: $box-shadow;
-    overflow: hidden;
-
-    // 标签页头部
-    .tab-header {
-      display: flex;
-      border-bottom: 1px solid $border-color;
-      background-color: #f8f9fa;
-
-      .tab-btn {
-        display: flex;
-        align-items: center;
-        gap: 8px;
-        padding: 16px 24px;
-        border: none;
-        background: none;
-        font-size: 14px;
-        font-weight: 500;
-        color: $text-secondary;
-        cursor: pointer;
-        transition: $transition;
-        border-bottom: 2px solid transparent;
-
-        &:hover {
-          color: $primary-color;
-          background-color: $bg-light;
-        }
-
-        &.active {
-          color: $primary-color;
-          background-color: $bg-white;
-          border-bottom-color: $primary-color;
-        }
-      }
-    }
-
-    // 标签页内容
-    .tab-content {
-      padding: 24px;
-      min-height: 400px;
-    }
-  }
-
-  // 概览网格布局
-  .overview-grid {
-    display: grid;
-    grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
-    gap: 24px;
-
-    // 信息卡片
-    .info-card {
-      padding: 20px;
-      background-color: color-mix(in srgb, $bg-light 80%, $bg-white);
-      border-radius: 10px;
-
-      .card-title {
-        font-size: 16px;
-        font-weight: 700;
-        color: $text-primary;
-        margin: 0 0 14px 0;
-        padding-bottom: 10px;
-        border-bottom: 1px solid color-mix(in srgb, $border-color 80%, transparent);
-        letter-spacing: .2px;
-      }
-
-      // 客户信息
-      .customer-info {
-        .info-item {
-          margin-bottom: 16px;
-
-          label {
-            display: block;
-            font-size: 12px;
-            font-weight: 600;
-            color: $text-secondary;
-            letter-spacing: .2px;
-            margin-bottom: 4px;
-          }
-
-          span {
-            font-size: 14px;
-            font-weight: 500;
-            color: $text-primary;
-            font-variant-numeric: tabular-nums;
-            letter-spacing: .1px;
-          }
-
-          .tag-container {
-            display: flex;
-            gap: 8px;
-            flex-wrap: wrap;
-
-            .tag {
-              padding: 4px 10px;
-              font-size: 12px;
-              font-weight: 600;
-              color: color.mix($primary-color, black, 85%);
-              background-color: color-mix(in srgb, #e3f2fd 80%, white);
-              border-radius: 14px;
-              letter-spacing: .2px;
-            }
-          }
-
-          .need-list {
-            margin: 0;
-            padding-left: 20px;
-
-            li {
-              display: flex;
-              align-items: flex-start;
-              gap: 8px;
-              font-size: 14px;
-              color: $text-primary;
-              margin-bottom: 4px;
-
-              svg {
-                flex-shrink: 0;
-                margin-top: 2px;
-                color: $success-color;
-              }
-            }
-          }
-        }
-      }
-
-      // 团队信息
-      .team-info {
-        .team-member {
-          display: flex;
-          align-items: center;
-          justify-content: space-between;
-          padding: 12px 0;
-          border-bottom: 1px solid $border-color;
-
-          &:last-child {
-            border-bottom: none;
-          }
-
-          .member-avatar {
-            width: 48px;
-            height: 48px;
-            border-radius: 50%;
-            object-fit: cover;
-            display: flex;
-            align-items: center;
-            justify-content: center;
-            background: linear-gradient(180deg, #f5f7fa, #e9edf3);
-            color: $text-secondary;
-            font-weight: 600;
-            font-size: 14px;
-            border: 1px solid $border-color;
-            box-shadow: 0 1px 2px rgba(0,0,0,0.04);
-          }
-
-          .member-details {
-            flex: 1;
-            margin-left: 12px;
-
-            .member-name {
-              font-size: 15px;
-              font-weight: 600;
-              color: $text-primary;
-              margin-bottom: 2px;
-              letter-spacing: .2px;
-            }
-
-            .member-role {
-              font-size: 12px;
-              color: $text-secondary;
-            }
-          }
-
-          .message-btn {
-            display: flex;
-            align-items: center;
-            justify-content: center;
-            width: 36px;
-            height: 36px;
-            border: none;
-            color: $primary-color;
-            background-color: #e3f2fd;
-            border-radius: 50%;
-            cursor: pointer;
-            transition: $transition;
-
-            &:hover {
-              background-color: $primary-color;
-              color: white;
-              box-shadow: 0 8px 16px rgba(0,0,0,0.12);
-              transform: translateY(-1px);
-            }
-
-            &:active {
-              transform: translateY(0);
-              box-shadow: 0 4px 10px rgba(0,0,0,0.08);
-            }
-          }
-        }
-      }
-
-      // 反馈信息
-      .feedback-list {
-        .feedback-item {
-          padding: 16px;
-          background-color: $bg-white;
-          border-radius: 6px;
-          margin-bottom: 12px;
-
-          .feedback-header {
-            display: flex;
-            justify-content: space-between;
-            align-items: center;
-            margin-bottom: 8px;
-
-            .feedback-author {
-              font-size: 14px;
-              font-weight: 500;
-              color: $text-primary;
-            }
-
-            .feedback-rating {
-              display: flex;
-              align-items: center;
-              gap: 8px;
-
-              .fa {
-                color: $warning-color;
-                margin-right: 2px;
-              }
-
-              .rating-stars {
-                font-size: 12px;
-                color: $warning-color;
-              }
-
-              .rating-number {
-                font-size: 14px;
-                font-weight: 600;
-                color: $text-primary;
-              }
-            }
-          }
-
-          .feedback-content {
-            font-size: 14px;
-            color: $text-primary;
-            line-height: 1.6;
-            margin-bottom: 8px;
-          }
-
-          .feedback-response {
-            padding: 12px;
-            background-color: $bg-light;
-            border-radius: 4px;
-            margin-bottom: 8px;
-
-            .response-label {
-              font-size: 12px;
-              font-weight: 500;
-              color: $text-light;
-              margin-bottom: 4px;
-            }
-
-            .response-text {
-              font-size: 13px;
-              color: $text-secondary;
-            }
-          }
-
-          .feedback-meta {
-            display: flex;
-            justify-content: space-between;
-            align-items: center;
-
-            .feedback-date {
-              font-size: 12px;
-              color: $text-light;
-            }
-
-            .feedback-status {
-              padding: 2px 8px;
-              font-size: 11px;
-              font-weight: 500;
-              border-radius: 12px;
-
-              &.status-processed {
-                color: $success-color;
-                background-color: #e8f5e9;
-              }
-
-              &.status-pending {
-                color: $warning-color;
-                background-color: #fff3e0;
-              }
-            }
-          }
-        }
-
-        .view-all-btn {
-          display: block;
-          width: 100%;
-          padding: 8px 16px;
-          border: 1px solid $border-color;
-          background-color: $bg-white;
-          color: $text-secondary;
-          font-size: 13px;
-          text-align: center;
-          cursor: pointer;
-          border-radius: 4px;
-          transition: $transition;
-
-          &:hover {
-            color: $primary-color;
-            border-color: $primary-color;
-          }
-        }
-      }
-    }
-  }
-
-  // 里程碑时间线
-  .milestones-timeline {
-    position: relative;
-    padding-left: 32px;
-    margin-left: 16px;
-
-    .milestone-item {
-      position: relative;
-      margin-bottom: 24px;
-
-      .milestone-dot {
-        position: absolute;
-        left: -48px;
-        top: 4px;
-        width: 16px;
-        height: 16px;
-        background-color: $text-light;
-        border-radius: 50%;
-        border: 2px solid white;
-        box-shadow: 0 0 0 2px $border-color;
-        transition: $transition;
-
-        &.completed {
-          background-color: $success-color;
-          box-shadow: 0 0 0 2px #e8f5e9;
-        }
-      }
-
-      .milestone-line {
-        position: absolute;
-        left: -40px;
-        top: 20px;
-        bottom: -24px;
-        width: 2px;
-        background-color: $border-color;
-        transition: $transition;
-
-        &.completed {
-          background-color: $success-color;
-        }
-      }
-
-      .milestone-content {
-        padding: 16px;
-        background-color: $bg-white;
-        border-radius: 8px;
-        border-left: 3px solid $border-color;
-        transition: $transition;
-
-        &:hover {
-          box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
-        }
-      }
-
-      .milestone-header {
-        display: flex;
-        justify-content: space-between;
-        align-items: center;
-        margin-bottom: 8px;
-
-        .milestone-title {
-          font-size: 16px;
-          font-weight: 600;
-          color: $text-primary;
-          margin: 0;
-        }
-
-        .milestone-status {
-          padding: 4px 12px;
-          border-radius: 16px;
-          font-size: 12px;
-          font-weight: 500;
-
-          &.status-completed {
-            color: $success-color;
-            background-color: #e8f5e9;
-          }
-
-          &.status-pending {
-            color: $warning-color;
-            background-color: #fff3e0;
-          }
-        }
-      }
-
-      .milestone-description {
-        font-size: 14px;
-        color: $text-secondary;
-        line-height: 1.5;
-        margin: 0 0 12px 0;
-      }
-
-      .milestone-dates {
-        display: flex;
-        gap: 24px;
-        margin-bottom: 12px;
-
-        .date-item {
-          display: flex;
-          flex-direction: column;
-
-          label {
-            font-size: 11px;
-            color: $text-light;
-            margin-bottom: 2px;
-          }
-
-          span {
-            font-size: 13px;
-            color: $text-primary;
-            font-weight: 500;
-          }
-        }
-      }
-
-      .milestone-actions {
-        display: flex;
-        gap: 8px;
-      }
-    }
-
-    .milestone-item:last-child {
-      margin-bottom: 0;
-
-      .milestone-line {
-        display: none;
-      }
-    }
-  }
-
-  // 任务过滤和搜索
-  .tasks-filter,
-  .files-filter {
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-    margin-bottom: 24px;
-
-    .filter-options {
-      display: flex;
-      gap: 8px;
-
-      .filter-btn {
-        padding: 6px 16px;
-        border: 1px solid $border-color;
-        background-color: $bg-white;
-        color: $text-secondary;
-        font-size: 13px;
-        font-weight: 500;
-        cursor: pointer;
-        border-radius: 4px;
-        transition: $transition;
-
-        &:hover {
-          color: $primary-color;
-          border-color: $primary-color;
-        }
-
-        &.active {
-          color: white;
-          background-color: $primary-color;
-          border-color: $primary-color;
-        }
-      }
-    }
-
-    .search-box {
-      position: relative;
-      width: 240px;
-
-      svg {
-        position: absolute;
-        left: 12px;
-        top: 50%;
-        transform: translateY(-50%);
-        color: $text-light;
-      }
-
-      input {
-        width: 100%;
-        padding: 8px 12px 8px 40px;
-        border: 1px solid $border-color;
-        border-radius: 4px;
-        font-size: 14px;
-        color: $text-primary;
-        transition: $transition;
-
-        &:focus {
-          outline: none;
-          border-color: $primary-color;
-          box-shadow: 0 0 0 2px rgba(30, 136, 229, 0.1);
-        }
-
-        &::placeholder {
-          color: $text-light;
-        }
-      }
-    }
-  }
-
-  // 任务列表
-  .tasks-list {
-    .task-item {
-      display: flex;
-      gap: 16px;
-      padding: 16px;
-      margin-bottom: 12px;
-      background-color: $bg-white;
-      border-radius: 8px;
-      border-left: 3px solid $border-color;
-      transition: $transition;
-
-      &:hover {
-        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
-      }
-
-      .task-checkbox {
-        flex-shrink: 0;
-
-        input[type="checkbox"] {
-          width: 18px;
-          height: 18px;
-          cursor: pointer;
-        }
-      }
-
-      .task-content {
-        flex: 1;
-
-        .task-title {
-          font-size: 16px;
-          font-weight: 600;
-          color: $text-primary;
-          margin: 0 0 8px 0;
-          transition: $transition;
-
-          &.completed {
-            color: $text-light;
-            text-decoration: line-through;
-          }
-        }
-
-        .task-description {
-          font-size: 14px;
-          color: $text-secondary;
-          line-height: 1.5;
-          margin: 0 0 12px 0;
-        }
-
-        .task-meta {
-          display: flex;
-          gap: 24px;
-
-          span {
-            display: flex;
-            align-items: center;
-            gap: 6px;
-            font-size: 13px;
-            color: $text-secondary;
-          }
-
-          .task-deadline {
-            &.overdue {
-              color: $danger-color;
-            }
-          }
-
-          .task-priority {
-            padding: 2px 8px;
-            border-radius: 12px;
-            font-size: 11px;
-            font-weight: 500;
-
-            &.priority-high {
-              color: $danger-color;
-              background-color: #ffebee;
-            }
-
-            &.priority-medium {
-              color: $warning-color;
-              background-color: #fff3e0;
-            }
-
-            &.priority-low {
-              color: $success-color;
-              background-color: #e8f5e9;
-            }
-          }
-        }
-      }
-    }
-  }
-
-  // 消息容器
-  .messages-container {
-    display: flex;
-    flex-direction: column;
-    height: 500px;
-
-    .messages-list {
-      flex: 1;
-      overflow-y: auto;
-      padding-right: 8px;
-      margin-bottom: 16px;
-
-      .message-item {
-        display: flex;
-        gap: 12px;
-        margin-bottom: 24px;
-
-        .message-avatar {
-          display: flex;
-          align-items: center;
-          justify-content: center;
-          width: 36px;
-          height: 36px;
-          font-size: 14px;
-          font-weight: 600;
-          color: white;
-          background-color: $primary-color;
-          border-radius: 50%;
-          flex-shrink: 0;
-        }
-
-        .message-content {
-          max-width: 70%;
-
-          .message-header {
-            display: flex;
-            justify-content: space-between;
-            align-items: center;
-            margin-bottom: 4px;
-
-            .message-sender {
-              font-size: 13px;
-              font-weight: 600;
-              color: $text-primary;
-            }
-
-            .message-time {
-              font-size: 11px;
-              color: $text-light;
-            }
-          }
-
-          .message-text {
-            font-size: 14px;
-            color: $text-secondary;
-            line-height: 1.5;
-            padding: 12px;
-            background-color: $bg-light;
-            border-radius: 12px;
-          }
-        }
-      }
-    }
-
-    .message-input-area {
-      .message-actions {
-        display: flex;
-        justify-content: flex-end;
-        gap: 12px;
-        margin-top: 12px;
-      }
-
-      textarea {
-        width: 100%;
-        padding: 12px;
-        border: 1px solid $border-color;
-        border-radius: 6px;
-        font-size: 14px;
-        color: $text-primary;
-        resize: vertical;
-        transition: $transition;
-
-        &:focus {
-          outline: none;
-          border-color: $primary-color;
-          box-shadow: 0 0 0 2px rgba(30, 136, 229, 0.1);
-        }
-
-        &::placeholder {
-          color: $text-light;
-        }
-      }
-    }
-  }
-
-  // 文件列表
-  .files-list {
-    .file-item {
-      display: flex;
-      align-items: center;
-      gap: 16px;
-      padding: 16px;
-      margin-bottom: 12px;
-      background-color: $bg-white;
-      border-radius: 8px;
-      transition: $transition;
-
-      &:hover {
-        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
-      }
-
-      .file-icon {
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        width: 48px;
-        height: 48px;
-        border-radius: 8px;
-        flex-shrink: 0;
-
-        &.type-document {
-          color: $primary-color;
-          background-color: #e3f2fd;
-        }
-
-        &.type-image {
-          color: $success-color;
-          background-color: #e8f5e9;
-        }
-
-        &.type-spreadsheet {
-          color: $warning-color;
-          background-color: #fff3e0;
-        }
-      }
-
-      .file-info {
-        flex: 1;
-
-        .file-name {
-          font-size: 14px;
-          font-weight: 500;
-          color: $text-primary;
-          margin: 0 0 8px 0;
-        }
-
-        .file-meta {
-          display: flex;
-          gap: 16px;
-
-          span {
-            font-size: 12px;
-            color: $text-light;
-          }
-        }
-      }
-
-      .file-actions {
-        display: flex;
-        gap: 8px;
-
-        .action-btn {
-          display: flex;
-          align-items: center;
-          justify-content: center;
-          width: 36px;
-          height: 36px;
-          border: 1px solid $border-color;
-          background-color: $bg-white;
-          color: $text-secondary;
-          border-radius: 6px;
-          cursor: pointer;
-          transition: $transition;
-
-          &:hover {
-            color: $primary-color;
-            border-color: $primary-color;
-          }
-        }
-      }
-    }
-  }
-}
-
-// iOS风格按钮点击效果
-.button {
-  &:active {
-    transform: scale(0.96);
-    transition: transform 0.1s ease;
-  }
-  
-  &-primary:active {
-    background: #0E42CB;
-  }
-  
-  &-secondary:active {
-    background: #D1D1D6;
-  }
-}
-
-// 卡片悬停效果
-.card {
-  transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
-  
-  &:hover {
-    transform: translateY(-2px);
-    box-shadow: $shadow-md;
-  }
-}
-
-// 响应式设计
-@media (max-width: 1024px) {
-  .overview-grid {
-    grid-template-columns: 1fr;
-  }
-
-  .project-content {
-    padding: 16px;
-  }
-
-  .sidebar {
-    width: 200px;
-  }
-}
-
-@media (max-width: 768px) {
-  .top-navbar {
-    padding: 0 16px;
-
-    .navbar-right {
-      gap: 8px;
-    }
-
-    .user-name {
-      display: none;
-    }
-  }
-
-  .sidebar {
-    display: none;
-  }
-
-  .project-header {
-    flex-direction: column;
-    gap: 16px;
-    align-items: flex-start !important;
-  }
-
-  .project-actions {
-    width: 100%;
-    justify-content: flex-start;
-  }
-
-  .tab-header {
-    flex-wrap: wrap;
-
-    .tab-btn {
-      flex: 1;
-      min-width: 120px;
-    }
-  }
-
-  .tasks-filter,
-  .files-filter {
-    flex-direction: column;
-    gap: 16px;
-    align-items: flex-start !important;
-
-    .search-box {
-      width: 100%;
-    }
-  }
-
-  .task-meta,
-  .file-meta {
-    flex-wrap: wrap;
-    gap: 12px !important;
-  }
-
-  .messages-container {
-    height: 400px;
-  }
-
-  .message-content {
-    max-width: 85% !important;
-  }
-}
-
-// Project Detail iOS tweaks
-.project-detail-container {
-  // 统一卡片间距(仅作用于本页)
-  .card {
-    padding: 20px;
-    margin-bottom: 24px;
-  }
-
-  // 标签页激活态(底部指示条 + 主色高亮)
-  .project-tabs {
-    .tab-header {
-      position: relative;
-    }
-    .tab-btn {
-      position: relative;
-    }
-    .tab-btn.active {
-      color: $primary-color;
-    }
-    .tab-btn.active::after {
-      content: '';
-      position: absolute;
-      left: 16px;
-      right: 16px;
-      bottom: -1px;
-      height: 2px;
-      background: $primary-color;
-      border-radius: 2px;
-    }
-  }
-
-  // 进度条填充质感(渐变 + 轻微内阴影)
-  .progress-bar {
-    .progress-fill {
-      background: linear-gradient(90deg, color.adjust($primary-color, $lightness: 8%), $primary-color);
-      border-radius: 4px;
-      box-shadow: inset 0 0 2px rgba(0,0,0,0.08);
-    }
-  }
-
-  // 输入框聚焦的环形高亮
-  .wechat-input-area {
-    .wechat-input:focus {
-      box-shadow: 0 0 0 3px color-mix(in srgb, $primary-color 15%, transparent);
-    }
-  }
-}
-
-// 响应式优化:侧栏在中等屏略窄,小屏隐藏
-@media (max-width: 1200px) {
-  .project-detail-container {
-    .wechat-sidebar { width: 300px; }
-  }
-}
-@media (max-width: 992px) {
-  .project-detail-container {
-    .wechat-sidebar { display: none; }
-    .project-content-main { width: 100%; }
-  }
-}
-
-// Project Detail iOS segmented & refinements (ABCD)
-.project-detail-container {
-  // A+B: 胶囊分段标签 + 细化激活态
-  .project-tabs.ios-tabs {
-    background: $background-primary;
-    border-radius: $border-radius;
-    box-shadow: $shadow-sm;
-    padding: 8px;
-
-    .tab-header {
-      background: $background-secondary;
-      border: 1px solid $border-color;
-      border-radius: $border-radius;
-      padding: 4px;
-      gap: 4px;
-    }
-
-    .tab-btn {
-      position: relative;
-      border-radius: $border-radius;
-      padding: 10px 14px;
-      font-size: 14px;
-      font-weight: 500;
-      color: $text-secondary;
-      transition: color .18s ease, background-color .18s ease, transform .18s ease;
-
-      &:hover { background-color: color-mix(in srgb, $primary-color 6%, transparent); }
-      &:active { transform: scale(0.98); }
-
-      &.active {
-        color: $primary-color;
-        font-weight: 600; // B: 激活时略增字重
-      }
-      &.active::before { // A: 胶囊式激活背景
-        content: '';
-        position: absolute;
-        inset: 0;
-        background: $background-primary;
-        border-radius: inherit;
-        box-shadow: 0 1px 2px rgba(0,0,0,0.06);
-        z-index: -1;
-      }
-      &.active::after { // B: 极细底线增强层次
-        content: '';
-        position: absolute;
-        left: 12px;
-        right: 12px;
-        bottom: 6px;
-        height: 2px;
-        border-radius: 2px;
-        background: $primary-color;
-        opacity: .7;
-      }
-    }
-  }
-
-  // C: 输入区细化
-  .wechat-input-area {
-    .wechat-input::placeholder { color: $text-tertiary; }
-
-    .action-btn:hover {
-      background-color: color-mix(in srgb, $text-primary 8%, transparent);
-      color: $text-primary;
-    }
-
-    .send-btn {
-      &:active:not(:disabled) { transform: scale(0.98); }
-      &:disabled {
-        background-color: $background-tertiary;
-        color: $text-tertiary;
-        border: 1px solid $border-color;
-        opacity: 0.9;
-        pointer-events: none;
-      }
-    }
-  }
-
-  // D: 侧栏与卡片阴影/圆角统一
-  .wechat-sidebar { box-shadow: $shadow-sm; }
-}
-
-@media (max-width: 768px) {
-  .project-detail-container {
-    .wechat-sidebar { border-radius: $border-radius; }
-  }
-}
-
-// Project Detail iOS buttons + history + bottom actions
-.project-detail-container {
-  // 顶部右侧两个按钮:导出报告(secondary)、联系客户(primary)
-  .header-actions {
-    .primary-btn,
-    .secondary-btn {
-      height: 36px;
-      padding: 0 14px;
-      border-radius: 10px;
-      font-size: 14px;
-      font-weight: 500;
-      letter-spacing: .2px;
-      display: inline-flex;
-      align-items: center;
-      gap: 8px;
-      transition: background-color .18s ease, color .18s ease, transform .12s ease;
-
-      svg { width: 16px; height: 16px; }
-
-      &:active { transform: scale(0.98); }
-    }
-
-    .secondary-btn {
-      color: $text-primary;
-      background-color: $bg-white;
-      border: 1px solid $border-color;
-
-      &:hover { background-color: $bg-light; }
-      &:focus-visible { box-shadow: 0 0 0 3px color-mix(in srgb, $text-primary 10%, transparent); }
-    }
-
-    .primary-btn {
-      color: #fff;
-      background-color: $primary-color;
-      border: 1px solid $primary-color;
-
-      &:hover { background-color: color.adjust($primary-color, $lightness: -6%); }
-      &:focus-visible { box-shadow: 0 0 0 3px color-mix(in srgb, $primary-color 20%, transparent); }
-      &:disabled { background: $text-tertiary; border-color: $text-tertiary; cursor: not-allowed; }
-    }
-  }
-
-  // 历史服务记录板块字体与排版
-  .historical-records-card {
-    .record-section {
-      .projects-list { display: grid; grid-template-columns: 1fr; gap: 10px; }
-      .project-item { padding: 10px 12px; border-radius: 8px; background: $bg-white; border: 1px solid $border-color; }
-      .project-name { font-size: 14px; font-weight: 600; color: $text-primary; margin-bottom: 4px; }
-      .project-period { font-size: 12px; color: $text-tertiary; margin-bottom: 6px; }
-      .project-description { font-size: 14px; color: $text-primary; line-height: 1.6; margin-bottom: 4px; }
-      .project-status { font-size: 12px; color: $text-secondary; }
-    }
-  }
-
-  // 底部售后操作栏(固定在底部的小界面):现代感与极简
-  .after-sales-actions.ios-actions {
-    position: sticky;
-    bottom: 0;
-    z-index: 5;
-    padding: 12px 0;
-
-    .actions-container {
-      display: flex;
-      gap: 12px;
-      justify-content: center;
-      padding: 10px 16px;
-      background: color-mix(in srgb, $bg-white 70%, transparent);
-      border-top: 1px solid $border-color;
-      backdrop-filter: saturate(160%) blur(10px);
-      -webkit-backdrop-filter: saturate(160%) blur(10px);
-      box-shadow: 0 -2px 8px rgba(0,0,0,0.04);
-      border-radius: 12px;
-    }
-
-    .primary-btn,
-    .secondary-btn,
-    .warning-btn {
-      height: 36px;
-      padding: 0 14px;
-      border-radius: 10px;
-      font-size: 14px;
-      font-weight: 500;
-      letter-spacing: .2px;
-      border: 1px solid transparent;
-      transition: background-color .18s ease, color .18s ease, transform .12s ease;
-      &:active { transform: scale(0.98); }
-    }
-
-    .primary-btn { color: #fff; background: $primary-color; border-color: $primary-color; &:hover { background: color.adjust($primary-color, $lightness: -6%); } }
-    .secondary-btn { color: $text-primary; background: $bg-white; border-color: $border-color; &:hover { background: $bg-light; } }
-    .warning-btn { color: #8a6100; background: #fff8e1; border-color: #ffe0a5; &:hover { background: #ffefc2; } }
-  }
-
-  // 里程碑小界面下方的动作区
-  .milestone-actions {
-    display: flex;
-    gap: 8px;
-    padding-top: 8px;
-    border-top: 1px dashed $border-color;
-
-    .primary-btn.small {
-      border-radius: 10px;
-      padding: 6px 12px;
-      font-weight: 600;
-    }
-  }
-}
-
-// 移动端微调
-@media (max-width: 768px) {
-  .project-detail-container {
-    .after-sales-actions.ios-actions .actions-container { border-radius: 10px; gap: 8px; }
-    .historical-records-card .record-section .consultation-item { padding: 10px; }
-  }
-}
-
-// iOS micro tuning — apply all requested adjustments
-.project-detail-container {
-  // Header actions buttons
-  .header-actions {
-    .primary-btn,
-    .secondary-btn {
-      height: 40px;
-      padding: 0 16px;
-      border-radius: 12px;
-    }
-    .primary-btn:hover {
-      background-color: color.adjust($primary-color, $lightness: -4%);
-    }
-  }
-
-  // Historical records cards — softer borders
-  .historical-records-card .record-section {
-    .consultation-item,
-    .cooperation-item,
-    .feedback-item,
-    .project-item {
-      border-color: color-mix(in srgb, $border-color 70%, transparent);
-    }
-  }
-
-  // Bottom sticky actions — lighter frosted glass and rounded container
-  .after-sales-actions.ios-actions {
-    .actions-container {
-      background: color-mix(in srgb, $bg-white 60%, transparent);
-      backdrop-filter: saturate(160%) blur(8px);
-      -webkit-backdrop-filter: saturate(160%) blur(8px);
-      border-radius: 14px;
-    }
-    .warning-btn {
-      color: #7a5200;
-      background: #fff5d6;
-      border-color: #ffd88a;
-      &:hover { background: #ffedbd; }
-    }
-  }
-
-  // Milestone small actions
-  .milestone-actions .primary-btn.small {
-    border-radius: 12px;
-    font-size: 13px;
-  }
-}
-
-@media (max-width: 768px) {
-  .project-detail-container {
-    .header-actions {
-      .primary-btn,
-      .secondary-btn { height: 38px; padding: 0 14px; }
-    }
-    .after-sales-actions.ios-actions .actions-container { border-radius: 12px; }
-  }
-}
-
-// Override: 历史卡片改为无描边 + 极浅阴影(高优先级覆盖)
-.project-detail-container {
-  .historical-records-card .record-section {
-    .consultation-item,
-    .cooperation-item,
-    .feedback-item,
-    .project-item {
-      border: none !important;
-      box-shadow: 0 1px 3px rgba(0,0,0,0.04);
-    }
-  }
-}
-
-@media (max-width: 768px) {
-  .project-detail-container {
-    .historical-records-card .record-section {
-      .consultation-item,
-      .cooperation-item,
-      .feedback-item,
-      .project-item {
-        box-shadow: 0 1px 2px rgba(0,0,0,0.04);
-      }
-    }
-  }
-}
-
-// Typography & layout improvements for Historical Records — 更直观的数据层级
-.project-detail-container {
-  .historical-records-card {
-    .record-section {
-      h4 { font-size: 16px; font-weight: 600; color: $text-primary; margin-bottom: 10px; }
-
-      // 过往咨询记录:日期-内容-状态 三段式布局
-      .consultation-list {
-        .consultation-item {
-          display: grid;
-          grid-template-columns: 128px 1fr auto; // 日期 | 内容 | 状态
-          gap: 8px 12px;
-          align-items: center;
-
-          .consultation-date {
-            font-size: 12px;
-            color: $text-tertiary;
-            letter-spacing: .2px;
-            font-variant-numeric: tabular-nums;
-          }
-          .consultation-content {
-            font-size: 14px;
-            color: $text-primary;
-            line-height: 1.6;
-          }
-          .consultation-status {
-            justify-self: end;
-            font-size: 12px;
-            font-weight: 600;
-            color: $text-primary;
-            background: $bg-light;
-            padding: 2px 8px;
-            border-radius: 10px;
-
-            // 三色状态:成功/处理中/待处理
-            &.status-processed { color: $success-color; background-color: #e8f5e9; }
-            &.status-processing { color: $primary-color; background-color: #e3f2fd; }
-            &.status-pending { color: $warning-color; background-color: #fff3e0; }
-          }
-        }
-      }
-
-      // 合作项目:名称/周期/描述/状态的网格分区,突出名称与状态
-      .projects-list {
-        .project-item {
-          display: grid;
-          grid-template-columns: 1fr auto;
-          grid-template-areas:
-            'name status'
-            'period status'
-            'desc desc';
-          row-gap: 4px;
-          column-gap: 12px;
-
-          .project-name { grid-area: name; font-size: 15px; font-weight: 600; color: $text-primary; }
-          .project-period { grid-area: period; font-size: 12px; color: $text-tertiary; font-variant-numeric: tabular-nums; }
-          .project-description { grid-area: desc; font-size: 14px; color: $text-secondary; line-height: 1.6; }
-          .project-status { grid-area: status; align-self: start; justify-self: end; font-size: 12px; font-weight: 600; padding: 2px 8px; border-radius: 10px; background: #f2f4f7; color: $text-secondary; }
-        }
-      }
-
-      // 历史反馈:日期等数值统一用等宽数字,正文稍加对比度
-      .feedback-list {
-        .feedback-item {
-          .feedback-date { font-variant-numeric: tabular-nums; letter-spacing: .2px; color: $text-light; }
-          .feedback-content { color: $text-primary; line-height: 1.7; }
-          .rating-number { font-variant-numeric: tabular-nums; }
-        }
-      }
-    }
-  }
-}
-
-// 响应式:在移动端收敛为纵向信息流
-@media (max-width: 768px) {
-  .project-detail-container {
-    .historical-records-card {
-      .record-section {
-        .consultation-list .consultation-item {
-          grid-template-columns: 1fr; // 纵向排列:内容 -> 状态 -> 日期
-          .consultation-status { justify-self: start; margin-top: 4px; }
-          .consultation-date { margin-top: 2px; }
-        }
-        .projects-list .project-item {
-          grid-template-columns: 1fr; grid-template-areas: 'name' 'status' 'period' 'desc';
-          .project-status { justify-self: start; margin: 2px 0 6px; }
-        }
-      }
-    }
-  }
-}
-
-/* 只读渲染图预览弹窗 + 缩略图样式(customer-service/project-detail) */
-.project-detail-container {
-  .modal-backdrop {
-    position: fixed;
-    inset: 0;
-    background: rgba(0,0,0,0.45);
-    backdrop-filter: saturate(160%) blur(6px);
-    -webkit-backdrop-filter: saturate(160%) blur(6px);
-    z-index: 999;
-  }
-
-  .modal {
-    position: fixed;
-    top: 50%;
-    left: 50%;
-    transform: translate(-50%, -50%);
-    width: min(920px, 92vw);
-    max-height: 80vh;
-    background: $bg-white;
-    border: 1px solid $border-color;
-    border-radius: $border-radius;
-    box-shadow: 0 12px 28px rgba(0,0,0,0.15);
-    display: flex;
-    flex-direction: column;
-    z-index: 1000;
-  }
-
-  .modal-header {
-    padding: 14px 16px;
-    border-bottom: 1px solid $border-color;
-    display: flex;
-    align-items: center;
-    justify-content: space-between;
-
-    h3 {
-      margin: 0;
-      font-size: 16px;
-      font-weight: 600;
-      color: $text-primary;
-    }
-  }
-
-  .modal-body {
-    padding: 16px;
-    overflow: auto;
-    background: $bg-white;
-  }
-
-  .modal-footer {
-    padding: 12px 16px;
-    border-top: 1px solid $border-color;
-    display: flex;
-    justify-content: flex-end;
-    gap: 8px;
-    background: $bg-white;
-  }
-
-  .close-button {
-    background: transparent;
-    border: none;
-    font-size: 18px;
-    line-height: 1;
-    cursor: pointer;
-    color: $text-secondary;
-
-    &:hover { color: $text-primary; }
-  }
-
-  /* 缩略图网格 */
-  .thumb-list {
-    display: grid;
-    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
-    gap: 12px;
-  }
-
-  .thumb-item {
-    border: 1px solid $border-color;
-    border-radius: $border-radius;
-    overflow: hidden;
-    background: $bg-white;
-    box-shadow: 0 1px 2px rgba(0,0,0,0.04);
-  }
-
-  .thumb-item img {
-    width: 100%;
-    height: 120px;
-    object-fit: cover;
-    display: block;
-  }
-
-  .thumb-meta {
-    padding: 8px;
-
-    .name {
-      font-size: 13px;
-      color: $text-primary;
-      white-space: nowrap;
-      overflow: hidden;
-      text-overflow: ellipsis;
-    }
-
-    .sub {
-      font-size: 12px;
-      color: $text-secondary;
-      margin-top: 2px;
-    }
-  }
-}
-
-// 优化交付执行模块中的"选择图片"按钮样式
-.project-detail-container {
-  .upload-actions {
-    .secondary-btn {
-      // 增大按钮尺寸
-      padding: 12px 24px !important;
-      min-height: 44px;
-      font-size: 15px !important;
-      font-weight: 600 !important;
-      
-      // 高对比度配色方案
-      background: linear-gradient(135deg, #4A90E2 0%, #357ABD 100%) !important;
-      color: white !important;
-      border: 2px solid #357ABD !important;
-      border-radius: 12px !important;
-      
-      // 视觉层次感
-      box-shadow: 0 4px 12px rgba(74, 144, 226, 0.25), 
-                  0 2px 4px rgba(0, 0, 0, 0.1) !important;
-      
-      // 悬停效果
-      transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
-      position: relative;
-      overflow: hidden;
-      
-      &::before {
-        content: '';
-        position: absolute;
-        top: 0;
-        left: -100%;
-        width: 100%;
-        height: 100%;
-        background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
-        transition: left 0.5s;
-      }
-      
-      &:hover {
-        background: linear-gradient(135deg, #5BA0F2 0%, #4A8ACD 100%) !important;
-        border-color: #4A8ACD !important;
-        box-shadow: 0 6px 20px rgba(74, 144, 226, 0.35), 
-                    0 4px 8px rgba(0, 0, 0, 0.15) !important;
-        transform: translateY(-2px) !important;
-        
-        &::before {
-          left: 100%;
-        }
-      }
-      
-      &:active {
-        transform: translateY(-1px) !important;
-        box-shadow: 0 4px 12px rgba(74, 144, 226, 0.3), 
-                    0 2px 4px rgba(0, 0, 0, 0.1) !important;
-      }
-      
-      // 添加图标效果
-      &::after {
-        content: '📁';
-        margin-left: 8px;
-        font-size: 16px;
-      }
-    }
-  }
-}
-
-// 响应式设计优化
-@media (max-width: 768px) {
-  .project-detail-container {
-    .upload-actions {
-      flex-direction: column;
-      gap: 12px;
-      
-      .secondary-btn {
-        width: 100% !important;
-        padding: 16px 20px !important;
-        font-size: 16px !important;
-        min-height: 48px;
-        
-        &::after {
-          font-size: 18px;
-        }
-      }
-      
-      .primary-btn {
-        width: 100%;
-        padding: 16px 20px;
-        font-size: 16px;
-        min-height: 48px;
-      }
-    }
-  }
-}
-
-@media (max-width: 480px) {
-  .project-detail-container {
-    .upload-actions {
-      .secondary-btn {
-        padding: 18px 24px !important;
-        font-size: 17px !important;
-        min-height: 52px;
-        
-        &::after {
-          font-size: 20px;
-        }
-      }
-    }
-  }
-}

+ 0 - 1148
src/app/pages/customer-service/project-detail/project-detail.ts

@@ -1,1148 +0,0 @@
-import { Component, OnInit, signal, computed, ViewChild, AfterViewChecked } from '@angular/core';
-import { CommonModule } from '@angular/common';
-import { FormsModule, ReactiveFormsModule, FormBuilder, FormGroup, Validators } from '@angular/forms';
-import { RouterModule, ActivatedRoute, Router } from '@angular/router';
-import { MatDialog, MatDialogModule } from '@angular/material/dialog';
-import { ProjectService } from '../../../services/project.service';
-import { Project, Task, Message, FileItem, CustomerFeedback, Milestone } from '../../../models/project.model';
-import { ModificationRequestDialog } from './modification-request-dialog';
-import { ComplaintWarningDialog } from './complaint-warning-dialog';
-import { RefundRequestDialog } from './refund-request-dialog';
-import { ProcessStatusBarComponent, ProcessStatusStage } from '../../../shared/components/process-status-bar/process-status-bar';
-
-// 定义项目阶段接口
-interface ProjectStage {
-  name: string;
-  completed: boolean;
-  inProgress: boolean;
-  startDate?: Date;
-  endDate?: Date;
-  responsible?: string;
-  details?: string;
-}
-
-// 定义企业微信消息接口
-interface WechatMessage {
-  sender: string;
-  content: string;
-  timestamp: Date;
-}
-
-// 定义历史咨询记录接口
-interface ConsultationRecord {
-  id: string;
-  date: Date;
-  content: string;
-  status: string;
-}
-
-// 定义合作项目接口
-interface CooperationProject {
-  id: string;
-  name: string;
-  startDate: Date;
-  endDate?: Date;
-  status: string;
-  description: string;
-}
-
-// 定义历史反馈接口
-interface HistoricalFeedback {
-  id: string;
-  date: Date;
-  content: string;
-  rating: number;
-  response?: string;
-}
-
-// 定义客户接口
-interface Customer {
-  id: string;
-  name: string;
-  phone: string;
-  avatar?: string;
-  customerType: string;
-}
-
-// 定义装修风格选项接口
-interface DecorationStyle {
-  value: string;
-  label: string;
-}
-
-@Component({
-  selector: 'app-project-detail',
-  standalone: true,
-  imports: [CommonModule, FormsModule, ReactiveFormsModule, RouterModule, MatDialogModule, ProcessStatusBarComponent],
-  templateUrl: './project-detail.html',
-  styleUrls: ['./project-detail.scss', '../customer-service-styles.scss']
-})
-export class ProjectDetail implements OnInit, AfterViewChecked {
-  // 项目ID
-  projectId = '';
-  
-  // 项目详情
-  project = signal<Project | null>(null);
-  
-  // 添加 toggleSidebar 方法
-  toggleSidebar(): void {
-    // 这里可以实现侧边栏切换逻辑
-    // 如果需要与全局状态交互,可以注入相应的服务
-    console.log('Toggle sidebar');
-  }
-  
-  // 项目里程碑
-  milestones = signal<Milestone[]>([]);
-  
-  // 项目任务
-  tasks = signal<Task[]>([]);
-  
-  // 项目消息
-  messages = signal<Message[]>([]);
-  
-  // 项目文件
-  files = signal<FileItem[]>([]);
-  
-  // 客户反馈
-  feedbacks = signal<CustomerFeedback[]>([]);
-  
-  // 当前激活的标签页
-  activeTab = signal('overview');
-  
-  // 新消息内容
-  newMessage = signal('');
-  
-  // 项目阶段数据 - 进度时间轴
-  projectStages?: ProjectStage[] = [
-    {      
-      name: '需求沟通',
-      completed: true,
-      inProgress: false,
-      startDate: new Date('2023-06-01'),
-      endDate: new Date('2023-06-08'),
-      responsible: '客服小李',
-      details: '完成客户需求确认、现场量房和初步方案讨论'
-    },
-    {
-      name: '建模',
-      completed: true,
-      inProgress: false,
-      startDate: new Date('2023-06-09'),
-      endDate: new Date('2023-06-18'),
-      responsible: '张设计师',
-      details: '创建3D模型和基础渲染'
-    },
-    {
-      name: '软装',
-      completed: false,
-      inProgress: true,
-      startDate: new Date('2023-06-19'),
-      responsible: '张设计师',
-      details: '选择和搭配家具、灯具和装饰品'
-    },
-    {
-      name: '渲染',
-      completed: false,
-      inProgress: false,
-      startDate: new Date('2023-06-26'),
-      responsible: '张设计师',
-      details: '生成最终渲染图'
-    },
-    {
-      name: '后期',
-      completed: false,
-      inProgress: false,
-      startDate: new Date('2023-07-03'),
-      responsible: '张设计师',
-      details: '后期图像处理和优化'
-    },
-    {
-      name: '投诉处理',
-      completed: false,
-      inProgress: false,
-      startDate: new Date('2023-07-10'),
-      endDate: new Date('2023-07-15'),
-      responsible: '客服小李',
-      details: '项目验收、交付和投诉处理'
-    }
-  ];
-
-  // 流程状态条数据
-  processStatusStages: ProcessStatusStage[] = [
-    { id: 'requirement', name: '需求沟通', status: 'completed' },
-    { id: 'modeling', name: '建模', status: 'completed' },
-    { id: 'soft-decoration', name: '软装', status: 'in-progress' },
-    { id: 'rendering', name: '渲染', status: 'pending' },
-    { id: 'post-processing', name: '后期', status: 'pending' },
-    { id: 'complaint-handling', name: '投诉处理', status: 'pending' }
-  ];
-  
-  // 企业微信聊天相关
-  @ViewChild('wechatMessages') wechatMessagesContainer: any;
-  wechatMessagesList: WechatMessage[] = [];
-  wechatInput = '';
-  scrollToBottom = false;
-  
-  // 历史服务记录相关
-  consultationRecords = signal<ConsultationRecord[]>([]);
-  cooperationProjects = signal<CooperationProject[]>([]);
-  historicalFeedbacks = signal<HistoricalFeedback[]>([]);
-  
-  // 售后处理弹窗状态
-  showModificationRequest = false;
-  showComplaintWarning = false;
-  showRefundRequest = false;
-  
-  // 渲染图只读预览弹窗状态与数据
-  showRenderPreviewModal = false;
-  renderImages = computed(() => this.files().filter(f => f.type === 'image'));
-  
-  // 项目状态颜色映射
-  statusColors = {
-    '进行中': 'primary',
-    '待确认': 'warning',
-    '已完成': 'success',
-    '已暂停': 'secondary',
-    '已取消': 'danger'
-  };
-  
-  // 计算完成进度
-  completionProgress = computed(() => {
-    if (!this.tasks().length) return 0;
-    const completedTasks = this.tasks().filter(task => task.isCompleted).length;
-    return Math.round((completedTasks / this.tasks().length) * 100);
-  });
-  
-  // 订单创建相关属性
-  isOrderCreationExpanded = signal(false);
-  orderSearchKeyword = signal('');
-  orderSearchResults = signal<Customer[]>([]);
-  selectedOrderCustomer = signal<Customer | null>(null);
-  orderRequirementForm: FormGroup;
-  
-  // 装修风格选项
-  decorationStyles: DecorationStyle[] = [
-    { value: 'modern', label: '现代简约' },
-    { value: 'chinese', label: '中式' },
-    { value: 'european', label: '欧式' },
-    { value: 'american', label: '美式' },
-    { value: 'nordic', label: '北欧' },
-    { value: 'industrial', label: '工业风' },
-    { value: 'mediterranean', label: '地中海' },
-    { value: 'japanese', label: '日式' }
-  ];
-
-  constructor(
-    private route: ActivatedRoute,
-    private projectService: ProjectService,
-    private dialog: MatDialog,
-    private fb: FormBuilder,
-    private router: Router
-  ) {
-    // 获取路由参数中的项目ID
-    this.route.params.subscribe(params => {
-      this.projectId = params['id'] || '';
-    });
-    
-    // 初始化订单表单
-    this.orderRequirementForm = this.fb.group({
-      projectType: ['', Validators.required],
-      area: ['', [Validators.required, Validators.min(1)]],
-      style: [''],
-      budget: [''],
-      requirements: ['']
-    });
-  }
-  
-  ngOnInit(): void {
-    this.loadProjectDetails();
-  }
-  
-  // 加载项目详情
-  loadProjectDetails(): void {
-    // 模拟从服务获取项目数据
-    this.projectService.getProjectById(this.projectId).subscribe(project => {
-      if (project) {
-        this.project.set(project);
-      }
-    });
-    
-    // 加载模拟数据
-    this.loadMockData();
-  }
-  
-  // 加载模拟数据
-  // 修复 loadMockData 方法中的任务对象,确保类型一致性
-  loadMockData(): void {
-    // 初始化企业微信聊天
-    this.initWechatMessages();
-    
-    // 初始化历史服务记录
-    this.initHistoricalServiceRecords();
-    
-    // 模拟里程碑数据
-    this.milestones.set([
-      { 
-        id: 'm1',
-        title: '客户需求确认',
-        description: '确认客户的装修需求和风格偏好',
-        dueDate: new Date('2023-06-10'),
-        completedDate: new Date('2023-06-08'),
-        isCompleted: true
-      },
-      {
-        id: 'm2',
-        title: '设计方案提交',
-        description: '提交初步设计方案供客户审阅',
-        dueDate: new Date('2023-06-20'),
-        completedDate: new Date('2023-06-18'),
-        isCompleted: true
-      },
-      {
-        id: 'm3',
-        title: '方案修改与确认',
-        description: '根据客户反馈修改并最终确认方案',
-        dueDate: new Date('2023-06-25'),
-        completedDate: undefined,
-        isCompleted: false
-      },
-      {
-        id: 'm4',
-        title: '施工图制作',
-        description: '制作详细的施工图纸',
-        dueDate: new Date('2023-07-05'),
-        completedDate: undefined,
-        isCompleted: false
-      },
-      {
-        id: 'm5',
-        title: '项目验收',
-        description: '客户验收最终成果',
-        dueDate: new Date('2023-07-15'),
-        completedDate: undefined,
-        isCompleted: false
-      }
-    ]);
-    
-    // 模拟任务数据 - 确保所有任务对象都有完整的必填属性
-    this.tasks.set([
-      { 
-        id: 't1',
-        title: '现场量房',
-        description: '前往客户现场进行房屋测量',
-        projectId: this.projectId,
-        projectName: '现代简约风格三居室设计',
-        assignee: '张设计师',
-        deadline: new Date('2023-06-05'),
-        completedDate: new Date('2023-06-04'),
-        isCompleted: true,
-        isOverdue: false,
-        priority: 'high',
-        stage: '需求沟通' // 添加 stage 属性,确保符合 Task 接口
-      },
-      {
-        id: 't2',
-        title: '设计初稿',
-        description: '完成设计方案初稿',
-        projectId: this.projectId,
-        projectName: '现代简约风格三居室设计',
-        assignee: '张设计师',
-        deadline: new Date('2023-06-15'),
-        completedDate: new Date('2023-06-14'),
-        isCompleted: true,
-        isOverdue: false,
-        priority: 'high',
-        stage: '建模'
-      },
-      {
-        id: 't3',
-        title: '客户沟通',
-        description: '与客户沟通设计方案反馈',
-        projectId: this.projectId,
-        projectName: '现代简约风格三居室设计',
-        assignee: '客服小李',
-        deadline: new Date('2023-06-22'),
-        completedDate: undefined,
-        isCompleted: false,
-        isOverdue: false,
-        priority: 'medium',
-        stage: '需求沟通'
-      },
-      {
-        id: 't4',
-        title: '方案修改',
-        description: '根据客户反馈修改设计方案',
-        projectId: this.projectId,
-        projectName: '现代简约风格三居室设计',
-        assignee: '张设计师',
-        deadline: new Date('2023-06-28'),
-        completedDate: undefined,
-        isCompleted: false,
-        isOverdue: false,
-        priority: 'high',
-        stage: '软装'
-      },
-      {
-        id: 't5',
-        title: '施工图绘制',
-        description: '绘制详细的施工图纸',
-        projectId: this.projectId,
-        projectName: '现代简约风格三居室设计',
-        assignee: '王设计师',
-        deadline: new Date('2023-07-10'),
-        completedDate: undefined,
-        isCompleted: false,
-        isOverdue: false,
-        priority: 'medium',
-        stage: '渲染'
-      }
-    ]);
-    
-    // 模拟消息数据
-    this.messages.set([
-      {
-        id: 'msg1',
-        sender: '客户王先生',
-        content: '设计方案收到了,整体很满意,客厅的颜色可以再调整一下吗?',
-        timestamp: new Date('2023-06-19T10:30:00'),
-        isRead: true,
-        type: 'text'
-      },
-      {
-        id: 'msg2',
-        sender: '客服小李',
-        content: '好的,我们会根据您的需求调整客厅颜色方案,稍后给您发送修改后的效果图。',
-        timestamp: new Date('2023-06-19T10:45:00'),
-        isRead: true,
-        type: 'text'
-      },
-      {
-        id: 'msg3',
-        sender: '张设计师',
-        content: '修改后的客厅效果图已上传,请查收。',
-        timestamp: new Date('2023-06-19T14:20:00'),
-        isRead: true,
-        type: 'text'
-      },
-      {
-        id: 'msg4',
-        sender: '客户王先生',
-        content: '这个效果很好,就按照这个方案进行吧!',
-        timestamp: new Date('2023-06-20T09:15:00'),
-        isRead: true,
-        type: 'text'
-      },
-      {
-        id: 'msg5',
-        sender: '客服小李',
-        content: '收到,我们会尽快开始下一阶段的工作。',
-        timestamp: new Date('2023-06-20T09:30:00'),
-        isRead: true,
-        type: 'text'
-      }
-    ]);
-    
-    // 模拟文件数据
-    this.files.set([
-      {
-        id: 'file1',
-        name: '设计方案初稿.pdf',
-        type: 'document',
-        size: '2.5MB',
-        url: 'https://example.com/files/design-proposal.pdf',
-        uploadedBy: '张设计师',
-        uploadedAt: new Date('2023-06-15'),
-        downloadCount: 12
-      },
-      {
-        id: 'file2',
-        name: '客厅效果图.png',
-        type: 'image',
-        size: '1.8MB',
-        url: 'https://example.com/files/living-room.png',
-        uploadedBy: '张设计师',
-        uploadedAt: new Date('2023-06-18'),
-        downloadCount: 8
-      },
-      {
-        id: 'file3',
-        name: '修改后客厅效果图.png',
-        type: 'image',
-        size: '2.1MB',
-        url: 'https://example.com/files/living-room-revised.png',
-        uploadedBy: '张设计师',
-        uploadedAt: new Date('2023-06-19'),
-        downloadCount: 5
-      },
-      {
-        id: 'file4',
-        name: '客户需求文档.docx',
-        type: 'document',
-        size: '1.2MB',
-        url: 'https://example.com/files/requirements.docx',
-        uploadedBy: '客服小李',
-        uploadedAt: new Date('2023-06-05'),
-        downloadCount: 10
-      },
-      {
-        id: 'file5',
-        name: '房屋测量数据.xlsx',
-        type: 'spreadsheet',
-        size: '0.8MB',
-        url: 'https://example.com/files/measurement.xlsx',
-        uploadedBy: '张设计师',
-        uploadedAt: new Date('2023-06-04'),
-        downloadCount: 7
-      }
-    ]);
-    
-    // 模拟客户反馈
-    this.feedbacks.set([
-      {
-        id: 'fb1',
-        projectId: this.projectId,
-        customerName: '王先生',
-        content: '对设计方案整体满意,但希望客厅颜色能更柔和一些。',
-        rating: 4,
-        createdAt: new Date('2023-06-19'),
-        status: '已解决',
-        response: '已根据您的需求调整了客厅颜色方案,详见最新上传的效果图。',
-        isSatisfied: true
-      }
-    ]);
-  }
-  
-  // 切换标签页
-  switchTab(tab: string): void {
-    this.activeTab.set(tab);
-  }
-  
-  // 增强版发送消息功能
-  sendMessage(): void {
-    if (this.newMessage().trim()) {
-      // 添加发送动画效果
-      const sendBtn = document.querySelector('.message-actions .primary-btn');
-      if (sendBtn) {
-        sendBtn.classList.add('sending');
-        setTimeout(() => sendBtn.classList.remove('sending'), 300);
-      }
-
-      const newMsg: Message = {
-        id: `msg${Date.now()}`,
-        sender: '客服小李',
-        content: this.newMessage().trim(),
-        timestamp: new Date(),
-        isRead: true,
-        type: 'text'
-      };
-      
-      this.messages.set([...this.messages(), newMsg]);
-      this.newMessage.set('');
-      
-      // 自动滚动到底部
-      setTimeout(() => {
-        const container = document.querySelector('.messages-list');
-        if (container) {
-          container.scrollTop = container.scrollHeight;
-        }
-      }, 100);
-    }
-  }
-  
-  // 增强版完成任务功能
-  completeTask(taskId: string): void {
-    // 添加完成动画效果
-    const taskElement = document.querySelector(`.task-item[data-id="${taskId}"]`);
-    if (taskElement) {
-      taskElement.classList.add('completing');
-      setTimeout(() => taskElement.classList.remove('completing'), 500);
-    }
-
-    this.tasks.set(
-      this.tasks().map(task => 
-        task.id === taskId 
-          ? { ...task, isCompleted: true, completedDate: new Date(), isOverdue: false }
-          : task
-      )
-    );
-    
-    // 播放完成音效
-    this.playSound('complete');
-  }
-  
-  // 修复 completeMilestone 方法中的类型问题
-  completeMilestone(milestoneId: string): void {
-    this.milestones.set(
-      this.milestones().map(milestone => 
-        milestone.id === milestoneId 
-          ? { ...milestone, isCompleted: true, completedDate: new Date() }
-          : milestone
-      )
-    );
-  }
-  
-  // 增强 formatDate 和 formatDateTime 方法的类型安全
-  formatDate(date: Date | string | null | undefined): string {
-    if (!date) return '-';
-    try {
-      return new Date(date).toLocaleDateString('zh-CN', {
-        year: 'numeric',
-        month: '2-digit',
-        day: '2-digit'
-      });
-    } catch (error) {
-      console.error('日期格式化错误:', error);
-      return '-';
-    }
-  }
-  
-  formatDateTime(date: Date | string | null | undefined): string {
-    if (!date) return '-';
-    try {
-      return new Date(date).toLocaleString('zh-CN', {
-        year: 'numeric',
-        month: '2-digit',
-        day: '2-digit',
-        hour: '2-digit',
-        minute: '2-digit'
-      });
-    } catch (error) {
-      console.error('日期时间格式化错误:', error);
-      return '-';
-    }
-  }
-  
-  // 下载文件
-  downloadFile(file: FileItem): void {
-    console.log('下载文件:', file.name);
-    // 实际应用中,这里会处理文件下载逻辑
-  }
-  
-  // 查看文件预览
-  previewFile(file: FileItem): void {
-    console.log('预览文件:', file.name);
-    // 实际应用中,这里会打开文件预览
-  }
-  
-  // 打开渲染图只读预览
-  openRenderPreview(): void {
-    this.showRenderPreviewModal = true;
-  }
-
-  // 关闭渲染图只读预览
-  closeRenderPreview(): void {
-    this.showRenderPreviewModal = false;
-  }
-  
-  // 获取项目状态的CSS类名
-  getProjectStatusClass(status: string | null | undefined): string {
-    if (!status) return 'status-default';
-    switch (status) {
-      case '进行中':
-        return 'status-active';
-      case '待确认':
-        return 'status-pending';
-      case '已完成':
-        return 'status-completed';
-      default:
-        return 'status-default';
-    }
-  }
-  
-  // 获取反馈客户名称(带安全检查)
-  getFeedbackCustomerName(feedback: CustomerFeedback | undefined | null): string {
-    if (!feedback) return '未知客户';
-    return feedback.customerName || '未知客户';
-  }
-  
-  // 获取反馈评分(带安全检查)
-  getFeedbackRating(feedback: CustomerFeedback | undefined | null): number {
-    if (!feedback) return 0;
-    return feedback.rating || 0;
-  }
-  
-  // 消息输入事件处理
-  onMessageInput(event: Event): void {
-    const target = event.target as HTMLTextAreaElement;
-    this.newMessage.set(target.value);
-  }
-  
-  // 添加正确的progressFillWidth计算属性
-  progressFillWidth = computed(() => {
-    return `${this.completionProgress()}%`;
-  });
-  
-  // AfterViewChecked 接口实现,用于滚动到聊天窗口底部
-  ngAfterViewChecked(): void {
-    if (this.scrollToBottom && this.wechatMessagesContainer) {
-      this.wechatMessagesContainer.nativeElement.scrollTop = 
-        this.wechatMessagesContainer.nativeElement.scrollHeight;
-      this.scrollToBottom = false;
-    }
-  }
-  
-  // 初始化企业微信聊天
-  initWechatMessages(): void {
-    // 模拟企业微信聊天消息
-    this.wechatMessagesList = [
-      {
-        sender: '客服小李',
-        content: '您好,张先生,我们已经收到您的需求,正在为您制定设计方案。',
-        timestamp: new Date('2023-06-01T10:30:00')
-      },
-      {
-        sender: '张先生',
-        content: '好的,我希望客厅光线充足,储物空间充足,并且使用环保材料。',
-        timestamp: new Date('2023-06-01T10:35:00')
-      },
-      {
-        sender: '客服小李',
-        content: '明白了,我们会重点考虑这些需求。预计3天内可以完成初步方案。',
-        timestamp: new Date('2023-06-01T10:40:00')
-      },
-      {
-        sender: '张设计师',
-        content: '您好,我是负责您项目的设计师小张。今天下午我会去现场量房,请问您方便吗?',
-        timestamp: new Date('2023-06-02T14:00:00')
-      },
-      {
-        sender: '张先生',
-        content: '下午好的,我在小区门口等您。',
-        timestamp: new Date('2023-06-02T14:05:00')
-      },
-      {
-        sender: '张设计师',
-        content: '已完成量房,正在制作初步设计方案。',
-        timestamp: new Date('2023-06-05T18:30:00')
-      },
-      {
-        sender: '客服小李',
-        content: '张先生,初步设计方案已完成,您什么时候方便查看一下?',
-        timestamp: new Date('2023-06-08T10:00:00')
-      }
-    ];
-    
-    // 确保滚动到底部
-    setTimeout(() => {
-      this.scrollToBottom = true;
-    }, 100);
-  }
-  
-  // 增强版发送企业微信消息
-  sendWechatMessage(): void {
-    if (!this.wechatInput.trim()) return;
-    
-    // 添加发送动画
-    const sendBtn = document.querySelector('.wechat-input-area .send-btn');
-    if (sendBtn) {
-      sendBtn.classList.add('sending');
-      setTimeout(() => sendBtn.classList.remove('sending'), 300);
-    }
-    
-    const newMessage: WechatMessage = {
-      sender: '客服小李',
-      content: this.wechatInput.trim(),
-      timestamp: new Date()
-    };
-    
-    this.wechatMessagesList = [...this.wechatMessagesList, newMessage];
-    this.wechatInput = '';
-    this.scrollToBottom = true;
-    
-    // 播放发送音效
-    this.playSound('message');
-    
-    // 模拟对方回复
-    setTimeout(() => {
-      const replyMessage: WechatMessage = {
-        sender: '张先生',
-        content: '收到,我稍后查看。',
-        timestamp: new Date()
-      };
-      
-      this.wechatMessagesList = [...this.wechatMessagesList, replyMessage];
-      this.scrollToBottom = true;
-      
-      // 播放接收音效
-      this.playSound('notification');
-    }, 2000);
-  }
-
-  // 新增播放音效方法
-  playSound(type: 'message' | 'notification' | 'complete'): void {
-    // 实际项目中这里会播放对应的音效
-    console.log(`播放${type}音效`);
-  }
-  
-  // 初始化历史服务记录
-  initHistoricalServiceRecords(): void {
-    // 模拟过往咨询记录
-    this.consultationRecords.set([
-      {
-        id: 'cons1',
-        date: new Date('2023-01-15'),
-        content: '咨询关于厨房改造的可行性和预算',
-        status: '已解决'
-      },
-      {
-        id: 'cons2',
-        date: new Date('2023-03-20'),
-        content: '询问装修材料环保认证相关问题',
-        status: '已解决'
-      },
-      {
-        id: 'cons3',
-        date: new Date('2023-05-10'),
-        content: '了解装修分期付款方案',
-        status: '已解决'
-      }
-    ]);
-    
-    // 模拟合作项目记录
-    this.cooperationProjects.set([
-      {
-        id: 'proj1',
-        name: '2022年现代简约卧室设计项目',
-        startDate: new Date('2022-08-15'),
-        endDate: new Date('2022-10-30'),
-        status: '已完成',
-        description: '为客户设计并实施了现代简约风格的卧室改造,包括定制衣柜和床头背景墙'
-      },
-      {
-        id: 'proj2',
-        name: '2023年欧式厨房设计项目',
-        startDate: new Date('2023-02-01'),
-        endDate: new Date('2023-04-15'),
-        status: '已完成',
-        description: '设计并安装了全套欧式风格厨房,包括橱柜、台面和电器选型'
-      }
-    ]);
-    
-    // 模拟历史反馈/评价
-    this.historicalFeedbacks.set([
-      {
-        id: 'fb1',
-        date: new Date('2022-11-05'),
-        content: '卧室设计非常满意,空间利用合理,风格符合预期',
-        rating: 5,
-        response: '感谢您的好评,我们会继续努力为您提供优质服务'
-      },
-      {
-        id: 'fb2',
-        date: new Date('2023-04-20'),
-        content: '厨房装修质量很好,但工期比预期稍长',
-        rating: 4,
-        response: '感谢您的反馈,我们已经优化了施工流程,会在后续项目中改进'
-      }
-    ]);
-  }
-  
-  // 格式化为时间显示
-  formatTime(date: Date): string {
-    if (!date) return '';
-    return new Date(date).toLocaleTimeString('zh-CN', { 
-      hour: '2-digit', 
-      minute: '2-digit' 
-    });
-  }
-  
-  // 获取当前日期的计算属性
-  currentDate = computed(() => {
-    return new Date();
-  });
-  
-  // 流程阶段点击处理
-  onStageClick(stage: any): void {
-    console.log('Stage clicked:', stage);
-    
-    // 查找对应的项目阶段
-    const stageIndex = this.projectStages?.findIndex(s => s.name === stage.name) ?? -1;
-    
-    if (stageIndex >= 0 && stageIndex < (this.projectStages?.length || 0)) {
-      const projectStage = this.projectStages?.[stageIndex];
-      
-      if (projectStage) {
-        console.log('Project stage details:', projectStage);
-        // 这里可以添加更多的交互逻辑,比如显示详情弹窗
-      }
-    }
-  }
-  
-  // 售后处理入口方法
-  openModificationRequest(): void {
-    const dialogRef = this.dialog.open(ModificationRequestDialog, {
-      width: '500px',
-      data: {
-        projectId: this.projectId,
-        projectName: this.project()?.name || '现代简约风格三居室设计',
-        projectStatus: this.project()?.status || '进行中'
-      }
-    });
-
-    dialogRef.afterClosed().subscribe(result => {
-      if (result) {
-        console.log('申请修改提交成功', result);
-        // 这里可以添加提交成功后的处理逻辑,如刷新数据、显示通知等
-      }
-    });
-  }
-  
-  openComplaintWarning(): void {
-    const dialogRef = this.dialog.open(ComplaintWarningDialog, {
-      width: '500px',
-      data: {
-        projectId: this.projectId,
-        projectName: this.project()?.name || '现代简约风格三居室设计'
-      }
-    });
-
-    dialogRef.afterClosed().subscribe(result => {
-      if (result) {
-        console.log('投诉预警提交成功', result);
-        // 这里可以添加提交成功后的处理逻辑
-      }
-    });
-  }
-  
-  openRefundRequest(): void {
-    const dialogRef = this.dialog.open(RefundRequestDialog, {
-      width: '500px',
-      data: {
-        projectId: this.projectId,
-        projectName: this.project()?.name || '现代简约风格三居室设计',
-        projectAmount: 85000 // 模拟项目金额
-      }
-    });
-
-    dialogRef.afterClosed().subscribe(result => {
-      if (result) {
-        console.log('申请退款提交成功', result);
-        // 这里可以添加提交成功后的处理逻辑
-      }
-    });
-  }
-
-  // 订单创建相关方法
-  toggleOrderCreation(): void {
-    this.isOrderCreationExpanded.set(!this.isOrderCreationExpanded());
-  }
-
-  searchOrderCustomer(): void {
-    const keyword = this.orderSearchKeyword().trim();
-    if (!keyword) {
-      this.orderSearchResults.set([]);
-      return;
-    }
-
-    // 模拟客户搜索
-    const mockCustomers: Customer[] = [
-      {
-        id: '1',
-        name: '张三',
-        phone: '13800138001',
-        avatar: '',
-        customerType: 'VIP客户'
-      },
-      {
-        id: '2',
-        name: '李四',
-        phone: '13800138002',
-        avatar: '',
-        customerType: '普通客户'
-      },
-      {
-        id: '3',
-        name: '王五',
-        phone: '13800138003',
-        avatar: '',
-        customerType: '新客户'
-      }
-    ];
-
-    const results = mockCustomers.filter(customer => 
-      customer.name.includes(keyword) || customer.phone.includes(keyword)
-    );
-    
-    this.orderSearchResults.set(results);
-  }
-
-  selectOrderCustomer(customer: Customer): void {
-    this.selectedOrderCustomer.set(customer);
-    this.orderSearchResults.set([]);
-    this.orderSearchKeyword.set('');
-  }
-
-  clearOrderCustomer(): void {
-    this.selectedOrderCustomer.set(null);
-    this.orderSearchKeyword.set('');
-    this.orderSearchResults.set([]);
-  }
-
-  quickFillOrderCustomerInfo(keyword: string): void {
-    // 快速填充客户信息的逻辑
-    if (keyword.trim()) {
-      const newCustomer: Customer = {
-        id: 'new-' + Date.now(),
-        name: keyword,
-        phone: '',
-        customerType: '新客户'
-      };
-      this.selectedOrderCustomer.set(newCustomer);
-      this.orderSearchKeyword.set('');
-    }
-  }
-
-  isOrderFormValid(): boolean {
-    return this.orderRequirementForm.valid && this.selectedOrderCustomer() !== null;
-  }
-
-  resetOrderForm(): void {
-    this.orderRequirementForm.reset();
-    this.clearOrderCustomer();
-    
-    // 重置创建按钮状态
-    const createBtn = document.querySelector('.primary-btn') as HTMLButtonElement;
-    if (createBtn) {
-      createBtn.disabled = false;
-      createBtn.innerHTML = `
-        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
-          <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path>
-          <polyline points="22 4 12 14.01 9 11.01"></polyline>
-        </svg>
-        创建订单
-      `;
-    }
-  }
-
-  createOrder(): void {
-    if (!this.isOrderFormValid()) {
-      return;
-    }
-
-    const orderData = {
-      customer: this.selectedOrderCustomer(),
-      requirements: this.orderRequirementForm.value,
-      createTime: new Date(),
-      orderId: 'ORD-' + Date.now(),
-      status: 'pending'
-    };
-
-    console.log('创建订单:', orderData);
-
-    // 显示创建中状态
-    const createBtn = document.querySelector('.primary-btn') as HTMLButtonElement;
-    if (createBtn) {
-      createBtn.disabled = true;
-      createBtn.innerHTML = `
-        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
-          <path d="M21 12a9 9 0 11-6.219-8.56"></path>
-        </svg>
-        创建中...
-      `;
-    }
-
-    // 模拟订单创建API调用
-    setTimeout(() => {
-      // 生成新的项目ID
-      const newProjectId = 'PRJ-' + Date.now();
-      
-      // 显示成功提示
-      this.showOrderCreationSuccess(orderData.orderId, newProjectId);
-      
-      // 重置表单
-      this.resetOrderForm();
-      
-      // 收起订单创建板块
-      this.isOrderCreationExpanded.set(false);
-      
-      // 延迟跳转到新创建的项目详情页面
-      setTimeout(() => {
-        this.router.navigate(['/customer-service/project-detail', newProjectId], {
-          queryParams: {
-            role: 'customer_service',
-            activeTab: 'overview',
-            fromOrder: 'true'
-          }
-        });
-      }, 2000);
-    }, 1500);
-  }
-
-  private showOrderCreationSuccess(orderId: string, projectId: string): void {
-    // 创建成功提示元素
-    const successMessage = document.createElement('div');
-    successMessage.className = 'order-success-toast';
-    successMessage.innerHTML = `
-      <div class="success-content">
-        <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
-          <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path>
-          <polyline points="22 4 12 14.01 9 11.01"></polyline>
-        </svg>
-        <div class="success-text">
-          <h4>订单创建成功!</h4>
-          <p>订单号:${orderId}</p>
-          <p>项目ID:${projectId}</p>
-          <p>即将跳转到项目详情页面...</p>
-        </div>
-      </div>
-    `;
-
-    // 添加样式
-    successMessage.style.cssText = `
-      position: fixed;
-      top: 20px;
-      right: 20px;
-      background: #34C759;
-      color: white;
-      padding: 16px 20px;
-      border-radius: 12px;
-      box-shadow: 0 10px 25px rgba(52, 199, 89, 0.3);
-      z-index: 10000;
-      animation: slideInRight 0.3s ease-out;
-    `;
-
-    // 添加动画样式
-    const style = document.createElement('style');
-    style.textContent = `
-      @keyframes slideInRight {
-        from {
-          transform: translateX(100%);
-          opacity: 0;
-        }
-        to {
-          transform: translateX(0);
-          opacity: 1;
-        }
-      }
-      .order-success-toast .success-content {
-        display: flex;
-        align-items: center;
-        gap: 12px;
-      }
-      .order-success-toast .success-text h4 {
-        margin: 0 0 4px 0;
-        font-size: 16px;
-        font-weight: 600;
-      }
-      .order-success-toast .success-text p {
-        margin: 2px 0;
-        font-size: 14px;
-        opacity: 0.9;
-      }
-    `;
-    document.head.appendChild(style);
-
-    // 添加到页面
-    document.body.appendChild(successMessage);
-
-    // 3秒后移除提示
-    setTimeout(() => {
-      if (successMessage.parentNode) {
-        successMessage.parentNode.removeChild(successMessage);
-      }
-      if (style.parentNode) {
-        style.parentNode.removeChild(style);
-      }
-    }, 3000);
-  }
-}

+ 0 - 187
src/app/pages/customer-service/project-detail/refund-request-dialog.ts

@@ -1,187 +0,0 @@
-import { CommonModule } from '@angular/common';
-import { FormsModule } from '@angular/forms';
-import { Component, Inject } from '@angular/core';
-import { MatButtonModule } from '@angular/material/button';
-import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
-import { MatFormFieldModule } from '@angular/material/form-field';
-import { MatInputModule } from '@angular/material/input';
-import { MatSelectModule } from '@angular/material/select';
-import { MatIconModule } from '@angular/material/icon';
-
-interface RefundRequestData {
-  projectId: string;
-  projectName: string;
-  projectAmount: number;
-}
-
-@Component({
-  selector: 'app-refund-request-dialog',
-  standalone: true,
-  imports: [
-    CommonModule,
-    FormsModule,
-    MatButtonModule,
-    MatDialogModule,
-    MatFormFieldModule,
-    MatInputModule,
-    MatSelectModule,
-    MatIconModule
-  ],
-  template: `
-    <h2 mat-dialog-title>申请退款</h2>
-    <mat-dialog-content>
-      <div class="dialog-content">
-        <div class="project-info">
-          <p><strong>项目名称:</strong> {{ data.projectName }}</p>
-          <p><strong>总金额:</strong> ¥{{ data.projectAmount.toLocaleString() }}</p>
-        </div>
-        
-        <div class="form-group">
-          <label for="refund-type">退款类型 *</label>
-          <mat-form-field appearance="outline" class="form-field">
-            <mat-select [(ngModel)]="refundType" id="refund-type" name="refundType" required>
-              <mat-option value="full">全额退款</mat-option>
-              <mat-option value="partial">部分退款</mat-option>
-            </mat-select>
-          </mat-form-field>
-        </div>
-        
-        @if (refundType === 'partial') {
-          <div class="form-group">
-            <label for="refund-amount">退款金额 *</label>
-            <mat-form-field appearance="outline" class="form-field">
-              <input 
-                matInput 
-                [(ngModel)]="refundAmount" 
-                id="refund-amount" 
-                name="refundAmount" 
-                type="number" 
-                min="0" 
-                [max]="data.projectAmount"
-                step="0.01" 
-                placeholder="请输入退款金额"
-                required
-              >
-              <span matTextSuffix>¥</span>
-            </mat-form-field>
-          </div>
-        }
-        
-        <div class="form-group">
-          <label for="refund-reason">退款原因 *</label>
-          <mat-form-field appearance="outline" class="form-field">
-            <textarea 
-              matInput 
-              [(ngModel)]="refundReason" 
-              id="refund-reason" 
-              name="refundReason" 
-              rows="4" 
-              placeholder="请详细描述退款原因..."
-              required
-            ></textarea>
-          </mat-form-field>
-        </div>
-        
-        <div class="form-group">
-          <label for="refund-account">退款账户信息</label>
-          <mat-form-field appearance="outline" class="form-field">
-            <input 
-              matInput 
-              [(ngModel)]="refundAccount" 
-              id="refund-account" 
-              name="refundAccount" 
-              type="text" 
-              placeholder="请提供退款账户信息(如银行卡号/支付宝等)"
-            >
-          </mat-form-field>
-        </div>
-      </div>
-    </mat-dialog-content>
-    <mat-dialog-actions align="end">
-      <button mat-button (click)="onCancel()">取消</button>
-      <button mat-button color="primary" (click)="onSubmit()" [disabled]="!isFormValid()">
-        提交申请
-      </button>
-    </mat-dialog-actions>
-  `,
-  styles: [`
-    .dialog-content {
-      padding: 10px 0;
-    }
-    
-    .project-info {
-      padding: 12px;
-      background-color: #ffebee;
-      border-radius: 8px;
-      margin-bottom: 16px;
-    }
-    
-    .form-group {
-      margin-bottom: 20px;
-    }
-    
-    label {
-      display: block;
-      margin-bottom: 6px;
-      font-weight: 500;
-      color: rgba(0, 0, 0, 0.87);
-    }
-    
-    p {
-      margin: 0;
-      color: rgba(0, 0, 0, 0.6);
-    }
-    
-    .form-field {
-      width: 100%;
-    }
-    
-    .mat-mdc-dialog-actions {
-      padding: 16px 24px;
-    }
-  `]
-}) 
-export class RefundRequestDialog {
-  refundType: string = 'full';
-  refundAmount: number = 0;
-  refundReason: string = '';
-  refundAccount: string = '';
-
-  constructor(
-    public dialogRef: MatDialogRef<RefundRequestDialog>,
-    @Inject(MAT_DIALOG_DATA) public data: RefundRequestData
-  ) {}
-
-  onCancel(): void {
-    this.dialogRef.close();
-  }
-
-  isFormValid(): boolean {
-    if (!this.refundType || !this.refundReason.trim()) {
-      return false;
-    }
-    
-    if (this.refundType === 'partial' && (this.refundAmount <= 0 || this.refundAmount > this.data.projectAmount)) {
-      return false;
-    }
-    
-    return true;
-  }
-
-  onSubmit(): void {
-    if (!this.isFormValid()) return;
-    
-    const formData = {
-      projectId: this.data.projectId,
-      projectName: this.data.projectName,
-      refundType: this.refundType,
-      totalAmount: this.data.projectAmount,
-      refundAmount: this.refundType === 'full' ? this.data.projectAmount : this.refundAmount,
-      refundReason: this.refundReason,
-      refundAccount: this.refundAccount,
-      requestedAt: new Date().toISOString()
-    };
-    
-    this.dialogRef.close({ confirmed: true, data: formData });
-  }
-}

+ 29 - 7
src/app/pages/customer-service/project-list/project-list.ts

@@ -1,4 +1,4 @@
-import { Component, OnInit, signal, computed, Inject } from '@angular/core';
+import { Component, OnInit, OnDestroy, signal, computed, Inject } from '@angular/core';
 import { CommonModule } from '@angular/common';
 import { FormsModule } from '@angular/forms';
 import { Router, RouterModule } from '@angular/router';
@@ -22,7 +22,7 @@ interface ProjectListItem extends Project {
   templateUrl: './project-list.html',
   styleUrls: ['./project-list.scss', '../customer-service-styles.scss']
 })
-export class ProjectList implements OnInit {
+export class ProjectList implements OnInit, OnDestroy {
   // 项目列表数据
   projects = signal<ProjectListItem[]>([]);
   
@@ -43,6 +43,9 @@ export class ProjectList implements OnInit {
   // 基础项目集合(服务端返回 + 本地生成),用于二次处理
   private baseProjects: Project[] = [];
 
+  // 消息监听器
+  private messageListener?: (event: MessageEvent) => void;
+
   // 添加toggleSidebar方法
   toggleSidebar(): void {
     // 侧边栏切换逻辑
@@ -111,8 +114,24 @@ export class ProjectList implements OnInit {
       this.viewMode.set(saved as 'card' | 'list' | 'dashboard');
     }
     this.loadProjects();
+
+    // 添加消息监听器,处理来自iframe的导航请求
+    this.messageListener = (event: MessageEvent) => {
+      // 验证消息来源(可以根据需要添加更严格的验证)
+      if (event.data && event.data.type === 'navigate' && event.data.route) {
+        this.router.navigate([event.data.route]);
+      }
+    };
+    window.addEventListener('message', this.messageListener);
   }
-  
+
+  ngOnDestroy(): void {
+    // 清理消息监听器
+    if (this.messageListener) {
+      window.removeEventListener('message', this.messageListener);
+    }
+  }
+
   // 视图切换
   toggleView(mode: 'card' | 'list' | 'dashboard') {
     if (this.viewMode() !== mode) {
@@ -280,6 +299,7 @@ export class ProjectList implements OnInit {
         highPriorityNeeds: i % 3 === 0 ? ['需快速交付', '重要客户'] : [],
         status: status,
         currentStage: stage,
+        stage: stage,
         createdAt: createdDate,
         deadline: deadlineDate,
         assigneeId: i % 4 === 0 ? '' : `designer${i % 3 + 1}`,
@@ -417,9 +437,11 @@ export class ProjectList implements OnInit {
     return 'req';
   }
 
-  // 详情跳转到设计师项目详情页面
+  // 详情跳转到设计师项目详情页面,传递客服角色标识
   navigateToProject(project: ProjectListItem, columnId: 'pending' | 'req' | 'delivery' | 'done') {
-    this.router.navigate(['/designer/project-detail', project.id]);
+    this.router.navigate(['/designer/project-detail', project.id], { 
+      queryParams: { role: 'customer-service' } 
+    });
   }
 
   // 新增:直接进入沟通管理(消息)标签
@@ -444,11 +466,11 @@ export class ProjectList implements OnInit {
       
       // 跳转到新创建的项目详情页面
       this.router.navigate([
-        '/customer-service/project-detail', 
+        '/designer/project-detail', 
         orderData.orderId
       ], {
         queryParams: { 
-          role: 'customer_service', 
+          role: 'customer-service', 
           activeTab: 'overview'
         }
       });

+ 13 - 13
src/app/pages/designer/project-detail/project-detail.html

@@ -455,7 +455,7 @@
                             <h4>上传白模图片</h4>
                             <span class="hint">支持:JPG/PNG,不强制4K</span>
                           </div>
-                          @if (isDesignerView()) {
+                          @if (canEditSection('delivery')) {
                             <div class="upload-dropzone" 
                                  (click)="whiteModelImages.length === 0 ? triggerFileInput('whiteModel') : null"
                                  (dragover)="whiteModelImages.length === 0 ? onDragOver($event) : null"
@@ -506,11 +506,11 @@
                             </div>
                           }
                           <div class="upload-actions">
-                            @if (isDesignerView()) {
+                            @if (canEditSection('delivery')) {
                               <button class="primary-btn" [disabled]="whiteModelImages.length===0" (click)="confirmWhiteModelUpload()">确认上传</button>
                             }
                             @if (isTeamLeaderView()) { <button class="secondary-btn" (click)="syncUploadedImages('white')">同步图片信息</button> }
-                            @if (isCustomerServiceView()) { <span class="desc">只读</span> }
+                            @if (!canEditSection('delivery')) { <span class="desc">只读</span> }
                           </div>
                         </div>
                         <div class="model-check-section">
@@ -519,7 +519,7 @@
                             @for (item of modelCheckItems; track item.id) {
                               <div class="checklist-item">
                                 <label class="checklist-label">
-                                  <input type="checkbox" class="custom-checkbox" [checked]="item.isPassed" (change)="updateModelCheckItem(item.id, $any($event.target).checked)" [disabled]="!isDesignerView()">
+                                  <input type="checkbox" class="custom-checkbox" [checked]="item.isPassed" (change)="updateModelCheckItem(item.id, $any($event.target).checked)" [disabled]="!canEditSection('delivery')">
                                   <span class="checklist-text">{{ item.name }}</span>
                                 </label>
                                 <span class="check-status">{{ item.isPassed ? '已通过' : '待处理' }}</span>
@@ -535,7 +535,7 @@
                               <h4>上传软装小图</h4>
                               <span class="hint">建议 ≤1MB 的 JPG/PNG 小图</span>
                             </div>
-                            @if (isDesignerView()) {
+                            @if (canEditSection('delivery')) {
                               <div class="upload-dropzone" 
                                    (click)="softDecorImages.length === 0 ? triggerFileInput('softDecor') : null"
                                    (dragover)="softDecorImages.length === 0 ? onDragOver($event) : null"
@@ -586,11 +586,11 @@
                               </div>
                             }
                             <div class="upload-actions">
-                              @if (isDesignerView()) {
+                              @if (canEditSection('delivery')) {
                                 <button class="primary-btn" [disabled]="softDecorImages.length===0" (click)="confirmSoftDecorUpload()">确认上传</button>
                               }
                               @if (isTeamLeaderView()) { <button class="secondary-btn" (click)="syncUploadedImages('soft')">同步图片信息</button> }
-                              @if (isCustomerServiceView()) { <span class="desc">只读</span> }
+                              @if (!canEditSection('delivery')) { <span class="desc">只读</span> }
                             </div>
                           </div>
                         </div>
@@ -620,7 +620,7 @@
                               <h4>上传渲染大图</h4>
                               <span class="hint">需满足4K标准(最长边 ≥ 4000px)</span>
                             </div>
-                            @if (isDesignerView()) {
+                            @if (canEditSection('delivery')) {
                               <div class="upload-dropzone" 
                                    (click)="renderLargeImages.length === 0 ? triggerFileInput('render') : null"
                                    (dragover)="renderLargeImages.length === 0 ? onDragOver($event) : null"
@@ -671,11 +671,11 @@
                               </div>
                             }
                             <div class="upload-actions">
-                              @if (isDesignerView()) {
+                              @if (canEditSection('delivery')) {
                                 <button class="primary-btn" [disabled]="renderLargeImages.length===0" (click)="confirmRenderUpload()">确认上传</button>
                               }
                               @if (isTeamLeaderView()) { <button class="secondary-btn" (click)="syncUploadedImages('render')">同步图片信息</button> }
-                              @if (isCustomerServiceView()) { <span class="desc">只读</span> }
+                              @if (!canEditSection('delivery')) { <span class="desc">只读</span> }
                             </div>
                           </div>
                         </div>
@@ -686,7 +686,7 @@
                               <h4>上传后期处理图片</h4>
                               <span class="hint">包含调色、修图、特效等后期处理结果</span>
                             </div>
-                            @if (isDesignerView()) {
+                            @if (canEditSection('delivery')) {
                               <div class="upload-dropzone" 
                                    (click)="postProcessImages.length === 0 ? triggerFileInput('postProcess') : null"
                                    (dragover)="postProcessImages.length === 0 ? onDragOver($event) : null"
@@ -737,11 +737,11 @@
                               </div>
                             }
                             <div class="upload-actions">
-                              @if (isDesignerView()) {
+                              @if (canEditSection('delivery')) {
                                 <button class="primary-btn" [disabled]="postProcessImages.length===0" (click)="confirmPostProcessUpload()">确认上传</button>
                               }
                               @if (isTeamLeaderView()) { <button class="secondary-btn" (click)="syncUploadedImages('postProcess')">同步图片信息</button> }
-                              @if (isCustomerServiceView()) { <span class="desc">只读</span> }
+                              @if (!canEditSection('delivery')) { <span class="desc">只读</span> }
                             </div>
                           </div>
                         </div>

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

@@ -316,6 +316,15 @@ export class ProjectDetail implements OnInit, OnDestroy {
   // ============ 角色视图与只读控制(新增) ============
   private detectRoleContextFromUrl(): 'customer-service' | 'designer' | 'team-leader' {
     const url = this.router.url || '';
+    
+    // 首先检查查询参数中的role
+    const queryParams = this.route.snapshot.queryParamMap;
+    const roleParam = queryParams.get('role');
+    if (roleParam === 'customer-service') {
+      return 'customer-service';
+    }
+    
+    // 如果没有role查询参数,则根据URL路径判断
     if (url.includes('/customer-service/')) return 'customer-service';
     if (url.includes('/team-leader/')) return 'team-leader';
     return 'designer';
@@ -327,6 +336,26 @@ export class ProjectDetail implements OnInit, OnDestroy {
   // 只读规则:客服视角为只读
   isReadOnly(): boolean { return this.isCustomerServiceView(); }
 
+  // 权限控制:客服只能编辑订单创建、确认需求、售后板块
+  canEditSection(sectionKey: SectionKey): boolean {
+    if (this.isCustomerServiceView()) {
+      return sectionKey === 'order' || sectionKey === 'requirements' || sectionKey === 'aftercare';
+    }
+    return true; // 设计师和组长可以编辑所有板块
+  }
+
+  // 权限控制:客服只能编辑特定阶段
+  canEditStage(stage: ProjectStage): boolean {
+    if (this.isCustomerServiceView()) {
+      const editableStages: ProjectStage[] = [
+        '订单创建', '需求沟通', '方案确认', // 订单创建和确认需求板块
+        '尾款结算', '客户评价', '投诉处理' // 售后板块
+      ];
+      return editableStages.includes(stage);
+    }
+    return true; // 设计师和组长可以编辑所有阶段
+  }
+
   // 计算当前激活板块:优先用户点击的 expandedSection;否则取当前阶段所属板块;再否则回退首个板块
   private getActiveSectionKey(): SectionKey {
     if (this.expandedSection) return this.expandedSection;

+ 17 - 2
src/app/pages/team-leader/dashboard/dashboard.ts

@@ -1233,8 +1233,23 @@ export class Dashboard implements OnInit, OnDestroy {
 
   // 查看项目详情
   viewProjectDetails(projectId: string): void {
-    // 改为跳转到复用的项目详情(组长上下文,具备审核权限)
-    this.router.navigate(['/team-leader/project-detail', projectId]);
+    // 检测是否在iframe中运行(即从客服端访问)
+    const isInIframe = window.self !== window.top;
+    
+    if (isInIframe) {
+      // 如果在iframe中,跳转到设计师端项目详情页面,并传递客服角色标识
+      // 使用parent.window来在父窗口中进行导航
+      const targetUrl = `/designer/project-detail/${projectId}?role=customer-service`;
+      if (window.parent) {
+        window.parent.postMessage({
+          type: 'navigate',
+          url: targetUrl
+        }, '*');
+      }
+    } else {
+      // 正常情况下跳转到组长端项目详情页面
+      this.router.navigate(['/team-leader/project-detail', projectId]);
+    }
   }
 
   // 快速分配项目(增强:加入智能推荐)

+ 3 - 0
src/app/services/project.service.ts

@@ -68,6 +68,7 @@ export class ProjectService {
       highPriorityNeeds: ['需家具购买建议'],
       status: '进行中',
       currentStage: '建模',
+      stage: '建模',
       createdAt: new Date('2025-09-01'),
       deadline: new Date('2025-09-15'),
       assigneeId: 'designer1',
@@ -84,6 +85,7 @@ export class ProjectService {
       highPriorityNeeds: [],
       status: '进行中',
       currentStage: '渲染',
+      stage: '渲染',
       createdAt: new Date('2025-09-02'),
       deadline: new Date('2025-09-20'),
       assigneeId: 'designer1',
@@ -100,6 +102,7 @@ export class ProjectService {
       highPriorityNeeds: ['需快速交付'],
       status: '已完成',
       currentStage: '投诉处理',
+      stage: '投诉处理',
       createdAt: new Date('2025-08-20'),
       deadline: new Date('2025-09-05'),
       assigneeId: 'designer1',

+ 208 - 20
src/app/shared/components/complaint-card/complaint-card.html

@@ -1,22 +1,210 @@
-<div class="info-card complaint-card">
-  <h4>投诉与异常处理</h4>
-  @if (complaints && complaints.length > 0) {
-    <ul class="items">
-      @for (c of complaints; track c.id) {
-        <li class="item">
-          <div class="title">{{ c.type }}</div>
-          <div class="desc">{{ c.description }}</div>
-          <div class="meta">
-            <span class="time">{{ c.submitTime }}</span>
-            <span class="status">{{ c.status }}</span>
-          </div>
-          @if (c.response) {
-            <div class="response">处理意见:{{ c.response }}</div>
-          }
-        </li>
+<div class="complaint-card">
+  <!-- 统计数据概览 -->
+  <div class="stats-overview">
+    <h4>投诉处理概览</h4>
+    <div class="stats-grid">
+      <div class="stat-item total">
+        <div class="stat-value">{{ stats().totalCount }}</div>
+        <div class="stat-label">总投诉数</div>
+      </div>
+      <div class="stat-item pending">
+        <div class="stat-value">{{ stats().pendingCount }}</div>
+        <div class="stat-label">待处理</div>
+      </div>
+      <div class="stat-item processing">
+        <div class="stat-value">{{ stats().processingCount }}</div>
+        <div class="stat-label">处理中</div>
+      </div>
+      <div class="stat-item resolved">
+        <div class="stat-value">{{ stats().resolvedCount }}</div>
+        <div class="stat-label">已解决</div>
+      </div>
+      <div class="stat-item high-priority">
+        <div class="stat-value">{{ stats().highPriorityCount }}</div>
+        <div class="stat-label">高优先级</div>
+      </div>
+      <div class="stat-item resolution-time">
+        <div class="stat-value">{{ stats().averageResolutionTime }}<span class="time-suffix">天</span></div>
+        <div class="stat-label">平均解决时间</div>
+      </div>
+    </div>
+  </div>
+
+  <!-- 优先级统计 -->
+  <div class="priority-stats">
+    <h5>优先级分布</h5>
+    <div class="priority-grid">
+      @for (priority of priorities; track priority.value) {
+        <div class="priority-item" [style.border-left-color]="priority.color">
+          <span class="priority-label">{{ priority.label }}</span>
+          <span class="priority-count">{{ stats().priorityStats[priority.value] || 0 }}</span>
+        </div>
+      }
+    </div>
+  </div>
+
+  <!-- 类型统计 -->
+  <div class="type-stats">
+    <h5>问题类型统计</h5>
+    <div class="type-grid">
+      @for (type of complaintTypes; track type.value) {
+        <div class="type-item">
+          <span class="type-label">{{ type.label }}</span>
+          <span class="type-count">{{ stats().typeStats[type.value] || 0 }}</span>
+        </div>
       }
-    </ul>
-  } @else {
-    <div class="empty">暂无投诉/异常</div>
-  }
+    </div>
+  </div>
+
+  <!-- 筛选区域 -->
+  <div class="filter-section">
+    <div class="search-row">
+      <div class="search-group">
+        <label>搜索:</label>
+        <input 
+          type="text" 
+          class="search-input"
+          placeholder="搜索投诉内容、客户姓名..."
+          [value]="searchKeyword()"
+          (input)="updateSearchKeyword($event.target.value)">
+      </div>
+    </div>
+
+    <div class="filter-row">
+      <div class="filter-group">
+        <label>状态筛选:</label>
+        <div class="filter-buttons">
+          <button 
+            class="filter-btn"
+            [class.active]="statusFilter() === 'all'"
+            (click)="updateStatusFilter('all')">
+            全部
+          </button>
+          <button 
+            class="filter-btn pending"
+            [class.active]="statusFilter() === '待处理'"
+            (click)="updateStatusFilter('待处理')">
+            待处理
+          </button>
+          <button 
+            class="filter-btn processing"
+            [class.active]="statusFilter() === '处理中'"
+            (click)="updateStatusFilter('处理中')">
+            处理中
+          </button>
+          <button 
+            class="filter-btn resolved"
+            [class.active]="statusFilter() === '已解决'"
+            (click)="updateStatusFilter('已解决')">
+            已解决
+          </button>
+        </div>
+      </div>
+    </div>
+    
+    <div class="filter-row">
+      <div class="filter-group">
+        <label>优先级筛选:</label>
+        <select 
+          class="filter-select"
+          [value]="priorityFilter()"
+          (change)="updatePriorityFilter($event)">
+          <option value="all">全部优先级</option>
+          @for (priority of priorities; track priority.value) {
+            <option [value]="priority.value">{{ priority.label }}</option>
+          }
+        </select>
+      </div>
+      
+      <div class="filter-group">
+        <label>类型筛选:</label>
+        <select 
+          class="filter-select"
+          [value]="typeFilter()"
+          (change)="updateTypeFilter($event)">
+          <option value="all">全部类型</option>
+          @for (type of complaintTypes; track type.value) {
+            <option [value]="type.value">{{ type.label }}</option>
+          }
+        </select>
+      </div>
+    </div>
+  </div>
+
+  <!-- 投诉列表 -->
+  <div class="complaints-list">
+    @if (filteredComplaints() && filteredComplaints().length > 0) {
+      <div class="list-body">
+        @for (complaint of filteredComplaints(); track complaint.id) {
+          <div class="complaint-item" [class]="getStatusClass(complaint)" [class.overdue]="isOverdue(complaint)">
+            <div class="complaint-header">
+              <div class="complaint-info">
+                <div class="complaint-title">
+                  <span class="type-tag">{{ getTypeLabel(getComplaintType(complaint)) }}</span>
+                  @if (complaint.customerName) {
+                    <span class="customer-name">{{ complaint.customerName }}</span>
+                  }
+                </div>
+                <div class="priority-badge" [class]="getPriorityClass(complaint.priority || 'low')" [style.background-color]="getPriorityInfo(complaint.priority || 'low').color">
+                  {{ getPriorityInfo(complaint.priority || 'low').label }}优先级
+                </div>
+              </div>
+              <div class="complaint-meta">
+                <span class="status-badge" [class]="getStatusClass(complaint)">
+                  {{ complaint.status }}
+                </span>
+                @if (isOverdue(complaint)) {
+                  <span class="overdue-badge">超时</span>
+                }
+              </div>
+            </div>
+            
+            <div class="complaint-content">
+              <p class="description">{{ complaint.description }}</p>
+              @if (complaint.images && complaint.images.length > 0) {
+                <div class="complaint-images">
+                  @for (image of complaint.images; track $index) {
+                    <img [src]="image" [alt]="'投诉图片' + ($index + 1)" class="complaint-image">
+                  }
+                </div>
+              }
+            </div>
+            
+            <div class="complaint-footer">
+              <div class="time-info">
+                <span class="submitted-time">提交时间:{{ complaint.submittedAt | date:'yyyy-MM-dd HH:mm' }}</span>
+                <span class="days-progress">已处理 {{ getDaysInProgress(complaint) }} 天</span>
+                @if (complaint.resolvedAt) {
+                  <span class="resolved-time">解决时间:{{ complaint.resolvedAt | date:'yyyy-MM-dd HH:mm' }}</span>
+                }
+              </div>
+              
+              @if (complaint.handlerComment) {
+                <div class="handler-response">
+                  <strong>处理意见:</strong>
+                  <p class="response-text">{{ complaint.handlerComment }}</p>
+                  @if (complaint.handlerName) {
+                    <span class="handler-name">处理人:{{ complaint.handlerName }}</span>
+                  }
+                </div>
+              }
+              
+              @if (complaint.solution) {
+                <div class="solution-section">
+                  <strong>解决方案:</strong>
+                  <p class="solution-text">{{ complaint.solution }}</p>
+                </div>
+              }
+            </div>
+          </div>
+        }
+      </div>
+    } @else {
+      <div class="empty-state">
+        <div class="empty-icon">📋</div>
+        <div class="empty-title">暂无投诉记录</div>
+        <div class="empty-desc">当前筛选条件下没有找到相关的投诉记录</div>
+      </div>
+    }
+  </div>
 </div>

+ 524 - 10
src/app/shared/components/complaint-card/complaint-card.scss

@@ -1,16 +1,530 @@
 @use '../../styles/_ios-theme.scss' as *;
+@import '../../../../styles/variables';
 
 :host { display: block; height: 100%; }
 
 .complaint-card {
-  h4 { margin: 0; font-size: $ios-font-size-sm; font-weight: $ios-font-weight-semibold; color: $ios-text-primary; padding-bottom: $ios-spacing-xs; border-bottom: 1px solid $ios-border; }
-  .items { margin: 0; padding-left: 0; list-style: none; }
-  .item { padding: $ios-spacing-sm 0; border-bottom: 1px dashed $ios-border; }
-  .item:last-child { border-bottom: none; }
-  .title { font-weight: $ios-font-weight-semibold; color: $ios-text-primary; }
-  .desc { color: $ios-text-secondary; margin-top: $ios-spacing-xs; }
-  .meta { display: flex; gap: $ios-spacing-sm; color: $ios-text-secondary; font-size: $ios-font-size-xs; margin-top: $ios-spacing-xs; }
-  .status { color: $ios-primary; }
-  .time { color: $ios-text-secondary; }
-  .response { margin-top: $ios-spacing-sm; color: $ios-primary; }
+  padding: $ios-spacing-md;
+  background: $ios-background;
+  border-radius: 12px;
+  border: 1px solid $ios-border;
+
+  // 统计数据概览
+  .stats-overview {
+    margin-bottom: $ios-spacing-lg;
+    
+    h4 {
+      margin: 0 0 $ios-spacing-md 0;
+      font-size: $ios-font-size-lg;
+      font-weight: $ios-font-weight-semibold;
+      color: $ios-text-primary;
+    }
+
+    .stats-grid {
+      display: grid;
+      grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
+      gap: $ios-spacing-md;
+
+      .stat-item {
+        text-align: center;
+        padding: $ios-spacing-md;
+        background: $ios-background-secondary;
+        border-radius: $ios-border-radius-md;
+        border: 1px solid $ios-border;
+
+        .stat-value {
+          font-size: $ios-font-size-xl;
+          font-weight: $ios-font-weight-bold;
+          color: $ios-text-primary;
+          margin-bottom: $ios-spacing-xs;
+
+          .time-suffix {
+            font-size: $ios-font-size-sm;
+            color: $ios-text-secondary;
+          }
+        }
+
+        .stat-label {
+          font-size: $ios-font-size-xs;
+          color: $ios-text-secondary;
+        }
+
+        &.total .stat-value { color: $ios-primary; }
+        &.pending .stat-value { color: $ios-warning; }
+        &.processing .stat-value { color: #1890ff; }
+        &.resolved .stat-value { color: $ios-success; }
+        &.high-priority .stat-value { color: $ios-danger; }
+        &.resolution-time .stat-value { color: #722ed1; }
+      }
+    }
+  }
+
+  // 优先级统计
+  .priority-stats {
+    margin-bottom: $ios-spacing-lg;
+    padding: $ios-spacing-md;
+    background: $ios-background-secondary;
+    border-radius: $ios-border-radius-md;
+    border: 1px solid $ios-border;
+
+    h5 {
+      margin: 0 0 $ios-spacing-md 0;
+      font-size: $ios-font-size-md;
+      font-weight: $ios-font-weight-semibold;
+      color: $ios-text-primary;
+    }
+
+    .priority-grid {
+      display: grid;
+      grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
+      gap: $ios-spacing-sm;
+
+      .priority-item {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        padding: $ios-spacing-sm;
+        background: $ios-background;
+        border-radius: $ios-border-radius-sm;
+        border: 1px solid $ios-border;
+        border-left-width: 4px;
+
+        .priority-label {
+          font-size: $ios-font-size-sm;
+          color: $ios-text-secondary;
+        }
+
+        .priority-count {
+          font-size: $ios-font-size-md;
+          font-weight: $ios-font-weight-semibold;
+          color: $ios-text-primary;
+        }
+      }
+    }
+  }
+
+  // 类型统计
+  .type-stats {
+    margin-bottom: $ios-spacing-lg;
+    padding: $ios-spacing-md;
+    background: $ios-background-secondary;
+    border-radius: $ios-border-radius-md;
+    border: 1px solid $ios-border;
+
+    h5 {
+      margin: 0 0 $ios-spacing-md 0;
+      font-size: $ios-font-size-md;
+      font-weight: $ios-font-weight-semibold;
+      color: $ios-text-primary;
+    }
+
+    .type-grid {
+      display: grid;
+      grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
+      gap: $ios-spacing-sm;
+
+      .type-item {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        padding: $ios-spacing-sm;
+        background: $ios-background;
+        border-radius: $ios-border-radius-sm;
+        border: 1px solid $ios-border;
+
+        .type-label {
+          font-size: $ios-font-size-xs;
+          color: $ios-text-secondary;
+        }
+
+        .type-count {
+          font-size: $ios-font-size-sm;
+          font-weight: $ios-font-weight-semibold;
+          color: $ios-primary;
+        }
+      }
+    }
+  }
+
+  // 筛选区域
+  .filter-section {
+    margin-bottom: $ios-spacing-lg;
+    padding: $ios-spacing-md;
+    background: $ios-background-secondary;
+    border-radius: $ios-border-radius-md;
+    border: 1px solid $ios-border;
+
+    .search-row {
+      margin-bottom: $ios-spacing-md;
+
+      .search-group {
+        display: flex;
+        align-items: center;
+        gap: $ios-spacing-sm;
+
+        label {
+          font-size: $ios-font-size-sm;
+          color: $ios-text-secondary;
+          white-space: nowrap;
+        }
+
+        .search-input {
+          flex: 1;
+          padding: $ios-spacing-sm;
+          border: 1px solid $ios-border;
+          border-radius: $ios-border-radius-sm;
+          background: $ios-background;
+          color: $ios-text-primary;
+          font-size: $ios-font-size-sm;
+
+          &:focus {
+            outline: none;
+            border-color: $ios-primary;
+          }
+
+          &::placeholder {
+            color: $ios-text-secondary;
+          }
+        }
+      }
+    }
+
+    .filter-row {
+      display: flex;
+      flex-wrap: wrap;
+      gap: $ios-spacing-md;
+      margin-bottom: $ios-spacing-md;
+
+      &:last-child {
+        margin-bottom: 0;
+      }
+
+      .filter-group {
+        display: flex;
+        align-items: center;
+        gap: $ios-spacing-sm;
+
+        label {
+          font-size: $ios-font-size-sm;
+          color: $ios-text-secondary;
+          white-space: nowrap;
+        }
+
+        .filter-buttons {
+          display: flex;
+          gap: $ios-spacing-xs;
+
+          .filter-btn {
+            padding: $ios-spacing-xs $ios-spacing-sm;
+            border: 1px solid $ios-border;
+            background: $ios-background;
+            color: $ios-text-secondary;
+            border-radius: $ios-border-radius-sm;
+            font-size: $ios-font-size-xs;
+            cursor: pointer;
+            transition: all 0.2s ease;
+
+            &:hover {
+              background: $ios-background-secondary;
+            }
+
+            &.active {
+              background: $ios-primary;
+              color: white;
+              border-color: $ios-primary;
+            }
+
+            &.pending.active {
+              background: $ios-warning;
+              border-color: $ios-warning;
+            }
+
+            &.processing.active {
+              background: #1890ff;
+              border-color: #1890ff;
+            }
+
+            &.resolved.active {
+              background: $ios-success;
+              border-color: $ios-success;
+            }
+          }
+        }
+
+        .filter-select {
+          padding: $ios-spacing-xs $ios-spacing-sm;
+          border: 1px solid $ios-border;
+          border-radius: $ios-border-radius-sm;
+          background: $ios-background;
+          color: $ios-text-primary;
+          font-size: $ios-font-size-xs;
+          min-width: 120px;
+
+          &:focus {
+            outline: none;
+            border-color: $ios-primary;
+          }
+        }
+      }
+    }
+  }
+
+  // 投诉列表
+  .complaints-list {
+    .list-body {
+      .complaint-item {
+        padding: $ios-spacing-md;
+        margin-bottom: $ios-spacing-md;
+        background: $ios-background;
+        border: 1px solid $ios-border;
+        border-radius: $ios-border-radius-md;
+        transition: all 0.2s ease;
+
+        &:hover {
+          box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+        }
+
+        &:last-child {
+          margin-bottom: 0;
+        }
+
+        &.overdue {
+          border-left: 4px solid $ios-danger;
+          background: rgba($ios-danger, 0.02);
+        }
+
+        .complaint-header {
+          display: flex;
+          justify-content: space-between;
+          align-items: flex-start;
+          margin-bottom: $ios-spacing-md;
+
+          .complaint-info {
+            flex: 1;
+
+            .complaint-title {
+              display: flex;
+              align-items: center;
+              gap: $ios-spacing-sm;
+              margin-bottom: $ios-spacing-xs;
+
+              .type-tag {
+                padding: $ios-spacing-xs $ios-spacing-sm;
+                background: $ios-primary;
+                color: white;
+                border-radius: $ios-border-radius-sm;
+                font-size: $ios-font-size-xs;
+              }
+
+              .customer-name {
+                font-size: $ios-font-size-md;
+                font-weight: $ios-font-weight-semibold;
+                color: $ios-text-primary;
+              }
+            }
+
+            .priority-badge {
+              display: inline-block;
+              padding: $ios-spacing-xs $ios-spacing-sm;
+              color: white;
+              border-radius: $ios-border-radius-sm;
+              font-size: $ios-font-size-xs;
+              font-weight: $ios-font-weight-semibold;
+            }
+          }
+
+          .complaint-meta {
+            display: flex;
+            align-items: center;
+            gap: $ios-spacing-sm;
+
+            .status-badge {
+              padding: $ios-spacing-xs $ios-spacing-sm;
+              border-radius: $ios-border-radius-sm;
+              font-size: $ios-font-size-xs;
+              font-weight: $ios-font-weight-semibold;
+
+              &.pending {
+                background: rgba($ios-warning, 0.1);
+                color: $ios-warning;
+              }
+
+              &.processing {
+                background: rgba(#1890ff, 0.1);
+                color: #1890ff;
+              }
+
+              &.resolved {
+                background: rgba($ios-success, 0.1);
+                color: $ios-success;
+              }
+            }
+
+            .overdue-badge {
+              padding: $ios-spacing-xs $ios-spacing-sm;
+              background: rgba($ios-danger, 0.1);
+              color: $ios-danger;
+              border-radius: $ios-border-radius-sm;
+              font-size: $ios-font-size-xs;
+              font-weight: $ios-font-weight-semibold;
+            }
+          }
+        }
+
+        .complaint-content {
+          margin-bottom: $ios-spacing-md;
+
+          .description {
+            margin: 0 0 $ios-spacing-sm 0;
+            color: $ios-text-primary;
+            line-height: 1.5;
+          }
+
+          .complaint-images {
+            display: flex;
+            gap: $ios-spacing-sm;
+            flex-wrap: wrap;
+
+            .complaint-image {
+              width: 80px;
+              height: 80px;
+              object-fit: cover;
+              border-radius: $ios-border-radius-sm;
+              border: 1px solid $ios-border;
+            }
+          }
+        }
+
+        .complaint-footer {
+          .time-info {
+            display: flex;
+            flex-wrap: wrap;
+            gap: $ios-spacing-md;
+            margin-bottom: $ios-spacing-sm;
+
+            .submitted-time,
+            .days-progress,
+            .resolved-time {
+              font-size: $ios-font-size-xs;
+              color: $ios-text-secondary;
+            }
+
+            .days-progress {
+              color: $ios-primary;
+              font-weight: $ios-font-weight-semibold;
+            }
+
+            .resolved-time {
+              color: $ios-success;
+            }
+          }
+
+          .handler-response {
+            padding: $ios-spacing-sm;
+            background: rgba($ios-primary, 0.05);
+            border-radius: $ios-border-radius-sm;
+            border-left: 3px solid $ios-primary;
+            margin-bottom: $ios-spacing-sm;
+
+            strong {
+              color: $ios-primary;
+              font-size: $ios-font-size-sm;
+            }
+
+            .response-text {
+              margin: $ios-spacing-xs 0;
+              color: $ios-text-secondary;
+              font-size: $ios-font-size-sm;
+              line-height: 1.4;
+            }
+
+            .handler-name {
+              font-size: $ios-font-size-xs;
+              color: $ios-text-secondary;
+            }
+          }
+
+          .solution-section {
+            padding: $ios-spacing-sm;
+            background: rgba($ios-success, 0.05);
+            border-radius: $ios-border-radius-sm;
+            border-left: 3px solid $ios-success;
+
+            strong {
+              color: $ios-success;
+              font-size: $ios-font-size-sm;
+            }
+
+            .solution-text {
+              margin: $ios-spacing-xs 0 0 0;
+              color: $ios-text-secondary;
+              font-size: $ios-font-size-sm;
+              line-height: 1.4;
+            }
+          }
+        }
+      }
+    }
+
+    .empty-state {
+      text-align: center;
+      padding: $ios-spacing-xl;
+      color: $ios-text-secondary;
+
+      .empty-icon {
+        font-size: 48px;
+        margin-bottom: $ios-spacing-md;
+      }
+
+      .empty-title {
+        font-size: $ios-font-size-lg;
+        font-weight: $ios-font-weight-semibold;
+        color: $ios-text-primary;
+        margin-bottom: $ios-spacing-sm;
+      }
+
+      .empty-desc {
+        font-size: $ios-font-size-sm;
+        color: $ios-text-secondary;
+      }
+    }
+  }
+
+  // 响应式设计
+  @media (max-width: 768px) {
+    .stats-overview .stats-grid {
+      grid-template-columns: repeat(2, 1fr);
+    }
+
+    .priority-stats .priority-grid,
+    .type-stats .type-grid {
+      grid-template-columns: 1fr;
+    }
+
+    .filter-section {
+      .search-row .search-group {
+        flex-direction: column;
+        align-items: stretch;
+      }
+
+      .filter-row {
+        flex-direction: column;
+        align-items: stretch;
+
+        .filter-group {
+          flex-direction: column;
+          align-items: stretch;
+
+          .filter-buttons {
+            justify-content: center;
+          }
+        }
+      }
+    }
+
+    .complaints-list .complaint-item .complaint-header {
+      flex-direction: column;
+      gap: $ios-spacing-sm;
+    }
+  }
 }

+ 204 - 2
src/app/shared/components/complaint-card/complaint-card.ts

@@ -1,22 +1,224 @@
-import { Component, Input } from '@angular/core';
+import { Component, Input, computed, signal } from '@angular/core';
 import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
 
 export interface ComplaintItem {
   id: string;
   type: string;
   description: string;
   submitTime: any;
+  submittedAt?: Date;
+  resolvedAt?: Date;
   status: string;
   response?: string;
+  handlerComment?: string;
+  handlerName?: string;
+  solution?: string;
+  customerName?: string;
+  priority?: string;
+  images?: string[];
+}
+
+interface ComplaintStats {
+  totalCount: number;
+  pendingCount: number;
+  processingCount: number;
+  resolvedCount: number;
+  highPriorityCount: number;
+  averageResolutionTime: number;
+  priorityStats: { [key: string]: number };
+  typeStats: { [key: string]: number };
 }
 
 @Component({
   selector: 'app-complaint-card',
   standalone: true,
-  imports: [CommonModule],
+  imports: [CommonModule, FormsModule],
   templateUrl: './complaint-card.html',
   styleUrls: ['./complaint-card.scss']
 })
 export class ComplaintCardComponent {
   @Input() complaints: ComplaintItem[] = [];
+
+  // 筛选条件
+  statusFilter = signal<string>('all');
+  priorityFilter = signal<string>('all');
+  typeFilter = signal<string>('all');
+  searchKeyword = signal<string>('');
+
+  // 投诉类型
+  complaintTypes = [
+    { value: 'quality', label: '质量问题' },
+    { value: 'service', label: '服务态度' },
+    { value: 'delivery', label: '交付延期' },
+    { value: 'communication', label: '沟通问题' },
+    { value: 'design', label: '设计不满意' },
+    { value: 'other', label: '其他问题' }
+  ];
+
+  // 优先级选项
+  priorities = [
+    { value: 'low', label: '低', color: '#52c41a' },
+    { value: 'medium', label: '中', color: '#faad14' },
+    { value: 'high', label: '高', color: '#f5222d' },
+    { value: 'urgent', label: '紧急', color: '#722ed1' }
+  ];
+
+  // 计算统计数据
+  stats = computed<ComplaintStats>(() => {
+    const complaints = this.complaints || [];
+    const totalCount = complaints.length;
+    
+    const pendingCount = complaints.filter(c => c.status === '待处理').length;
+    const processingCount = complaints.filter(c => c.status === '处理中').length;
+    const resolvedCount = complaints.filter(c => c.status === '已解决').length;
+    const highPriorityCount = complaints.filter(c => (c as any).priority === 'high' || (c as any).priority === 'urgent').length;
+
+    // 计算平均解决时间(天)
+    const resolvedComplaints = complaints.filter(c => c.status === '已解决' && (c as any).resolvedAt && (c as any).createdAt);
+    const averageResolutionTime = resolvedComplaints.length > 0 
+      ? resolvedComplaints.reduce((sum, c) => {
+          const days = Math.ceil((new Date((c as any).resolvedAt).getTime() - new Date((c as any).createdAt).getTime()) / (1000 * 60 * 60 * 24));
+          return sum + days;
+        }, 0) / resolvedComplaints.length
+      : 0;
+
+    // 优先级统计
+    const priorityStats: { [key: string]: number } = {};
+    this.priorities.forEach(p => {
+      priorityStats[p.value] = complaints.filter(c => (c as any).priority === p.value).length;
+    });
+
+    // 类型统计
+    const typeStats: { [key: string]: number } = {};
+    this.complaintTypes.forEach(t => {
+      typeStats[t.value] = complaints.filter(c => this.getComplaintType(c) === t.value).length;
+    });
+
+    return {
+      totalCount,
+      pendingCount,
+      processingCount,
+      resolvedCount,
+      highPriorityCount,
+      averageResolutionTime: Math.round(averageResolutionTime * 10) / 10,
+      priorityStats,
+      typeStats
+    };
+  });
+
+  // 筛选后的投诉列表
+  filteredComplaints = computed(() => {
+    let filtered = this.complaints || [];
+
+    // 状态筛选
+    if (this.statusFilter() !== 'all') {
+      filtered = filtered.filter(complaint => complaint.status === this.statusFilter());
+    }
+
+    // 优先级筛选
+    if (this.priorityFilter() !== 'all') {
+      filtered = filtered.filter(complaint => (complaint as any).priority === this.priorityFilter());
+    }
+
+    // 类型筛选
+    if (this.typeFilter() !== 'all') {
+      filtered = filtered.filter(complaint => this.getComplaintType(complaint) === this.typeFilter());
+    }
+
+    // 关键词搜索
+    if (this.searchKeyword()) {
+      const keyword = this.searchKeyword().toLowerCase();
+      filtered = filtered.filter(complaint => 
+        complaint.description?.toLowerCase().includes(keyword) ||
+        (complaint as any).customerName?.toLowerCase().includes(keyword) ||
+        complaint.type?.toLowerCase().includes(keyword)
+      );
+    }
+
+    return filtered;
+  });
+
+  // 获取投诉类型
+  getComplaintType(complaint: any): string {
+    if (complaint.type) return complaint.type;
+    
+    // 根据描述内容推断类型
+    const description = complaint.description?.toLowerCase() || '';
+    if (description.includes('质量') || description.includes('缺陷')) return 'quality';
+    if (description.includes('服务') || description.includes('态度')) return 'service';
+    if (description.includes('延期') || description.includes('时间')) return 'delivery';
+    if (description.includes('沟通') || description.includes('联系')) return 'communication';
+    if (description.includes('设计') || description.includes('效果')) return 'design';
+    
+    return 'other';
+  }
+
+  // 获取类型标签
+  getTypeLabel(type: string): string {
+    const typeObj = this.complaintTypes.find(t => t.value === type);
+    return typeObj ? typeObj.label : '其他';
+  }
+
+  // 获取优先级信息
+  getPriorityInfo(priority: string) {
+    return this.priorities.find(p => p.value === priority) || this.priorities[0];
+  }
+
+  // 获取状态样式类
+  getStatusClass(complaint: any): string {
+    switch (complaint.status) {
+      case '待处理': return 'pending';
+      case '处理中': return 'processing';
+      case '已解决': return 'resolved';
+      default: return 'pending';
+    }
+  }
+
+  // 获取优先级样式类
+  getPriorityClass(priority: string): string {
+    switch (priority) {
+      case 'low': return 'priority-low';
+      case 'medium': return 'priority-medium';
+      case 'high': return 'priority-high';
+      case 'urgent': return 'priority-urgent';
+      default: return 'priority-low';
+    }
+  }
+
+  // 计算处理天数
+  getDaysInProgress(complaint: any): number {
+    const startDate = new Date(complaint.createdAt);
+    const endDate = complaint.resolvedAt ? new Date(complaint.resolvedAt) : new Date();
+    return Math.ceil((endDate.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24));
+  }
+
+  // 判断是否超时
+  isOverdue(complaint: any): boolean {
+    if (complaint.status === '已解决') return false;
+    
+    const daysInProgress = this.getDaysInProgress(complaint);
+    const maxDays = complaint.priority === 'urgent' ? 1 : 
+                   complaint.priority === 'high' ? 3 : 
+                   complaint.priority === 'medium' ? 7 : 14;
+    
+    return daysInProgress > maxDays;
+  }
+
+  // 更新筛选条件
+  updateStatusFilter(event: any) {
+    this.statusFilter.set(event.target.value);
+  }
+
+  updatePriorityFilter(event: any) {
+    this.priorityFilter.set(event.target.value);
+  }
+
+  updateTypeFilter(event: any) {
+    this.typeFilter.set(event.target.value);
+  }
+
+  updateSearchKeyword(keyword: string) {
+    this.searchKeyword.set(keyword);
+  }
 }

+ 171 - 17
src/app/shared/components/customer-review-card/customer-review-card.html

@@ -1,19 +1,173 @@
-<div class="info-card customer-review-card">
-  <h4>客户评价</h4>
-  @if (feedbacks && feedbacks.length > 0) {
-    <ul class="items">
-      @for (f of feedbacks; track f.id) {
-        <li class="item">
-          <div class="title">{{ f.customerName || '客户反馈' }}</div>
-          <div class="desc">{{ f.content }}</div>
-          <div class="meta">
-            <span class="time">{{ (f.updatedAt || f.createdAt) | date:'yyyy-MM-dd HH:mm' }}</span>
-            <span class="status">{{ f.status }}</span>
-          </div>
-        </li>
+<div class="customer-review-card">
+  <!-- 统计数据概览 -->
+  <div class="stats-overview">
+    <h4>客户评价概览</h4>
+    <div class="stats-grid">
+      <div class="stat-item total">
+        <div class="stat-value">{{ stats().totalCount }}</div>
+        <div class="stat-label">总评价数</div>
+      </div>
+      <div class="stat-item score">
+        <div class="stat-value">{{ stats().averageScore }}<span class="score-suffix">/5</span></div>
+        <div class="stat-label">平均评分</div>
+        <div class="star-display">
+          @for (star of getStarRating(stats().averageScore); track $index) {
+            <span class="star">{{ star }}</span>
+          }
+        </div>
+      </div>
+      <div class="stat-item satisfied">
+        <div class="stat-value">{{ stats().satisfiedCount }}</div>
+        <div class="stat-label">满意评价</div>
+      </div>
+      <div class="stat-item pending">
+        <div class="stat-value">{{ stats().pendingCount }}</div>
+        <div class="stat-label">待处理</div>
+      </div>
+    </div>
+  </div>
+
+  <!-- 分类统计 -->
+  <div class="category-stats">
+    <h5>问题分类统计</h5>
+    <div class="category-grid">
+      @for (category of categories; track category.value) {
+        <div class="category-item">
+          <span class="category-label">{{ category.label }}</span>
+          <span class="category-count">{{ stats().categoryStats[category.value] || 0 }}</span>
+        </div>
       }
-    </ul>
-  } @else {
-    <div class="empty">暂无客户评价</div>
-  }
+    </div>
+  </div>
+
+  <!-- 筛选区域 -->
+  <div class="filter-section">
+    <div class="filter-row">
+      <div class="filter-group">
+        <label>状态筛选:</label>
+        <div class="filter-buttons">
+          <button 
+            class="filter-btn"
+            [class.active]="statusFilter() === 'all'"
+            (click)="updateStatusFilter('all')">
+            全部
+          </button>
+          <button 
+            class="filter-btn pending"
+            [class.active]="statusFilter() === '待处理'"
+            (click)="updateStatusFilter('待处理')">
+            待处理
+          </button>
+          <button 
+            class="filter-btn satisfied"
+            [class.active]="statusFilter() === 'satisfied'"
+            (click)="updateStatusFilter('satisfied')">
+            满意
+          </button>
+          <button 
+            class="filter-btn unsatisfied"
+            [class.active]="statusFilter() === 'unsatisfied'"
+            (click)="updateStatusFilter('unsatisfied')">
+            不满意
+          </button>
+        </div>
+      </div>
+    </div>
+    
+    <div class="filter-row">
+      <div class="filter-group">
+        <label>分类筛选:</label>
+        <select 
+          class="filter-select"
+          [value]="categoryFilter()"
+          (change)="updateCategoryFilter($event)">
+          <option value="all">全部分类</option>
+          @for (category of categories; track category.value) {
+            <option [value]="category.value">{{ category.label }}</option>
+          }
+        </select>
+      </div>
+      
+      <div class="filter-group">
+        <label>评分筛选:</label>
+        <select 
+          class="filter-select"
+          [value]="scoreFilter()"
+          (change)="updateScoreFilter($event)">
+          <option value="all">全部评分</option>
+          <option value="high">高分 (4-5分)</option>
+          <option value="medium">中等 (2-4分)</option>
+          <option value="low">低分 (1-2分)</option>
+        </select>
+      </div>
+    </div>
+  </div>
+
+  <!-- 评价列表 -->
+  <div class="reviews-list">
+    @if (filteredFeedbacks() && filteredFeedbacks().length > 0) {
+      <div class="list-body">
+        @for (feedback of filteredFeedbacks(); track feedback.id) {
+          <div class="review-item" [class]="getStatusClass(feedback)">
+            <div class="review-header">
+              <div class="customer-info">
+                <span class="customer-name">{{ feedback.customerName || '客户' }}</span>
+                <span class="category-tag">{{ getCategoryLabel(getFeedbackCategory(feedback)) }}</span>
+              </div>
+              <div class="review-meta">
+                @if (feedback.rating !== undefined) {
+                  <div class="score-display" [class]="getScoreClass(feedback.rating)">
+                    <div class="score-stars">
+                      @for (star of getStarRating(feedback.rating); track $index) {
+                        <span class="star">{{ star }}</span>
+                      }
+                    </div>
+                    <span class="score-number">{{ feedback.rating }}/5</span>
+                  </div>
+                }
+                <span class="status-badge" [class]="getStatusClass(feedback)">
+                  {{ feedback.status }}
+                </span>
+              </div>
+            </div>
+            
+            <div class="review-content">
+              <p class="feedback-text">{{ feedback.content }}</p>
+              @if (feedback.problemLocation) {
+                <div class="problem-location">
+                  <strong>问题位置:</strong>{{ feedback.problemLocation }}
+                </div>
+              }
+              @if (feedback.referenceCase) {
+                <div class="reference-case">
+                  <strong>参考案例:</strong>{{ feedback.referenceCase }}
+                </div>
+              }
+            </div>
+            
+            <div class="review-footer">
+              <div class="time-info">
+                <span class="created-time">{{ feedback.createdAt | date:'yyyy-MM-dd HH:mm' }}</span>
+                @if (feedback.updatedAt && feedback.updatedAt !== feedback.createdAt) {
+                  <span class="updated-time">更新于 {{ feedback.updatedAt | date:'yyyy-MM-dd HH:mm' }}</span>
+                }
+              </div>
+              @if (feedback.response) {
+                <div class="response-section">
+                  <strong>处理回复:</strong>
+                  <p class="response-text">{{ feedback.response }}</p>
+                </div>
+              }
+            </div>
+          </div>
+        }
+      </div>
+    } @else {
+      <div class="empty-state">
+        <div class="empty-icon">💬</div>
+        <div class="empty-title">暂无客户评价</div>
+        <div class="empty-desc">当前筛选条件下没有找到相关的客户评价记录</div>
+      </div>
+    }
+  </div>
 </div>

+ 419 - 9
src/app/shared/components/customer-review-card/customer-review-card.scss

@@ -2,14 +2,424 @@
 
 :host { display: block; height: 100%; }
 
+@import '../../../../styles/variables';
+
 .customer-review-card {
-  h4 { margin: 0; font-size: $ios-font-size-sm; font-weight: $ios-font-weight-semibold; color: $ios-text-primary; padding-bottom: $ios-spacing-xs; border-bottom: 1px solid $ios-border; }
-  .items { margin: 0; padding-left: 0; list-style: none; }
-  .item { padding: $ios-spacing-sm 0; border-bottom: 1px dashed $ios-border; }
-  .item:last-child { border-bottom: none; }
-  .title { font-weight: $ios-font-weight-semibold; color: $ios-text-primary; }
-  .desc { color: $ios-text-secondary; margin-top: $ios-spacing-xs; }
-  .meta { display: flex; gap: $ios-spacing-sm; color: $ios-text-secondary; font-size: $ios-font-size-xs; margin-top: $ios-spacing-xs; }
-  .status { color: $ios-primary; }
-  .time { color: $ios-text-secondary; }
+  padding: $ios-spacing-md;
+  background: $ios-background;
+  border-radius: 12px;
+  border: 1px solid $ios-border;
+
+  // 统计数据概览
+  .stats-overview {
+    margin-bottom: $ios-spacing-lg;
+    
+    h4 {
+      margin: 0 0 $ios-spacing-md 0;
+      font-size: $ios-font-size-lg;
+      font-weight: $ios-font-weight-semibold;
+      color: $ios-text-primary;
+    }
+
+    .stats-grid {
+      display: grid;
+      grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
+      gap: $ios-spacing-md;
+
+      .stat-item {
+        text-align: center;
+        padding: $ios-spacing-md;
+        background: $ios-background-secondary;
+        border-radius: $ios-border-radius-md;
+        border: 1px solid $ios-border;
+
+        .stat-value {
+          font-size: $ios-font-size-xl;
+          font-weight: $ios-font-weight-bold;
+          color: $ios-text-primary;
+          margin-bottom: $ios-spacing-xs;
+
+          .score-suffix {
+            font-size: $ios-font-size-sm;
+            color: $ios-text-secondary;
+          }
+        }
+
+        .stat-label {
+          font-size: $ios-font-size-xs;
+          color: $ios-text-secondary;
+          margin-bottom: $ios-spacing-xs;
+        }
+
+        .star-display {
+          .star {
+            color: #FFD700;
+            font-size: $ios-font-size-sm;
+          }
+        }
+
+        &.total .stat-value { color: $ios-primary; }
+        &.score .stat-value { color: #FF6B35; }
+        &.satisfied .stat-value { color: $ios-success; }
+        &.pending .stat-value { color: $ios-warning; }
+      }
+    }
+  }
+
+  // 分类统计
+  .category-stats {
+    margin-bottom: $ios-spacing-lg;
+    padding: $ios-spacing-md;
+    background: $ios-background-secondary;
+    border-radius: $ios-border-radius-md;
+    border: 1px solid $ios-border;
+
+    h5 {
+      margin: 0 0 $ios-spacing-md 0;
+      font-size: $ios-font-size-md;
+      font-weight: $ios-font-weight-semibold;
+      color: $ios-text-primary;
+    }
+
+    .category-grid {
+      display: grid;
+      grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
+      gap: $ios-spacing-sm;
+
+      .category-item {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        padding: $ios-spacing-sm;
+        background: $ios-background;
+        border-radius: $ios-border-radius-sm;
+        border: 1px solid $ios-border;
+
+        .category-label {
+          font-size: $ios-font-size-xs;
+          color: $ios-text-secondary;
+        }
+
+        .category-count {
+          font-size: $ios-font-size-sm;
+          font-weight: $ios-font-weight-semibold;
+          color: $ios-primary;
+        }
+      }
+    }
+  }
+
+  // 筛选区域
+  .filter-section {
+    margin-bottom: $ios-spacing-lg;
+    padding: $ios-spacing-md;
+    background: $ios-background-secondary;
+    border-radius: $ios-border-radius-md;
+    border: 1px solid $ios-border;
+
+    .filter-row {
+      display: flex;
+      flex-wrap: wrap;
+      gap: $ios-spacing-md;
+      margin-bottom: $ios-spacing-md;
+
+      &:last-child {
+        margin-bottom: 0;
+      }
+
+      .filter-group {
+        display: flex;
+        align-items: center;
+        gap: $ios-spacing-sm;
+
+        label {
+          font-size: $ios-font-size-sm;
+          color: $ios-text-secondary;
+          white-space: nowrap;
+        }
+
+        .filter-buttons {
+          display: flex;
+          gap: $ios-spacing-xs;
+
+          .filter-btn {
+            padding: $ios-spacing-xs $ios-spacing-sm;
+            border: 1px solid $ios-border;
+            background: $ios-background;
+            color: $ios-text-secondary;
+            border-radius: $ios-border-radius-sm;
+            font-size: $ios-font-size-xs;
+            cursor: pointer;
+            transition: all 0.2s ease;
+
+            &:hover {
+              background: $ios-background-secondary;
+            }
+
+            &.active {
+              background: $ios-primary;
+              color: white;
+              border-color: $ios-primary;
+            }
+
+            &.pending.active {
+              background: $ios-warning;
+              border-color: $ios-warning;
+            }
+
+            &.satisfied.active {
+              background: $ios-success;
+              border-color: $ios-success;
+            }
+
+            &.unsatisfied.active {
+              background: $ios-danger;
+              border-color: $ios-danger;
+            }
+          }
+        }
+
+        .filter-select {
+          padding: $ios-spacing-xs $ios-spacing-sm;
+          border: 1px solid $ios-border;
+          border-radius: $ios-border-radius-sm;
+          background: $ios-background;
+          color: $ios-text-primary;
+          font-size: $ios-font-size-xs;
+          min-width: 120px;
+
+          &:focus {
+            outline: none;
+            border-color: $ios-primary;
+          }
+        }
+      }
+    }
+  }
+
+  // 评价列表
+  .reviews-list {
+    .list-body {
+      .review-item {
+        padding: $ios-spacing-md;
+        margin-bottom: $ios-spacing-md;
+        background: $ios-background;
+        border: 1px solid $ios-border;
+        border-radius: $ios-border-radius-md;
+        transition: all 0.2s ease;
+
+        &:hover {
+          box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+        }
+
+        &:last-child {
+          margin-bottom: 0;
+        }
+
+        .review-header {
+          display: flex;
+          justify-content: space-between;
+          align-items: flex-start;
+          margin-bottom: $ios-spacing-md;
+
+          .customer-info {
+            display: flex;
+            align-items: center;
+            gap: $ios-spacing-sm;
+
+            .customer-name {
+              font-size: $ios-font-size-md;
+              font-weight: $ios-font-weight-semibold;
+              color: $ios-text-primary;
+            }
+
+            .category-tag {
+              padding: $ios-spacing-xs $ios-spacing-sm;
+              background: $ios-primary;
+              color: white;
+              border-radius: $ios-border-radius-sm;
+              font-size: $ios-font-size-xs;
+            }
+          }
+
+          .review-meta {
+            display: flex;
+            align-items: center;
+            gap: $ios-spacing-sm;
+
+            .score-display {
+              display: flex;
+              align-items: center;
+              gap: $ios-spacing-xs;
+
+              .score-stars {
+                .star {
+                  color: #FFD700;
+                  font-size: $ios-font-size-sm;
+                }
+              }
+
+              .score-number {
+                font-size: $ios-font-size-xs;
+                color: $ios-text-secondary;
+              }
+
+              &.high .score-number { color: $ios-success; }
+              &.medium .score-number { color: $ios-warning; }
+              &.low .score-number { color: $ios-danger; }
+            }
+
+            .status-badge {
+              padding: $ios-spacing-xs $ios-spacing-sm;
+              border-radius: $ios-border-radius-sm;
+              font-size: $ios-font-size-xs;
+              font-weight: $ios-font-weight-semibold;
+
+              &.satisfied {
+                background: rgba($ios-success, 0.1);
+                color: $ios-success;
+              }
+
+              &.unsatisfied {
+                background: rgba($ios-danger, 0.1);
+                color: $ios-danger;
+              }
+
+              &.pending {
+                background: rgba($ios-warning, 0.1);
+                color: $ios-warning;
+              }
+            }
+          }
+        }
+
+        .review-content {
+          margin-bottom: $ios-spacing-md;
+
+          .feedback-text {
+            margin: 0 0 $ios-spacing-sm 0;
+            color: $ios-text-primary;
+            line-height: 1.5;
+          }
+
+          .problem-location {
+            margin-bottom: $ios-spacing-sm;
+            padding: $ios-spacing-sm;
+            background: rgba($ios-warning, 0.1);
+            border-radius: $ios-border-radius-sm;
+            font-size: $ios-font-size-sm;
+            color: $ios-text-secondary;
+
+            strong {
+              color: $ios-warning;
+            }
+          }
+
+          .feedback-images {
+            display: flex;
+            gap: $ios-spacing-sm;
+            flex-wrap: wrap;
+
+            .feedback-image {
+              width: 80px;
+              height: 80px;
+              object-fit: cover;
+              border-radius: $ios-border-radius-sm;
+              border: 1px solid $ios-border;
+            }
+          }
+        }
+
+        .review-footer {
+          .time-info {
+            display: flex;
+            gap: $ios-spacing-md;
+            margin-bottom: $ios-spacing-sm;
+
+            .created-time,
+            .updated-time {
+              font-size: $ios-font-size-xs;
+              color: $ios-text-secondary;
+            }
+
+            .updated-time {
+              color: $ios-primary;
+            }
+          }
+
+          .response-section {
+            padding: $ios-spacing-sm;
+            background: rgba($ios-primary, 0.05);
+            border-radius: $ios-border-radius-sm;
+            border-left: 3px solid $ios-primary;
+
+            strong {
+              color: $ios-primary;
+              font-size: $ios-font-size-sm;
+            }
+
+            .response-text {
+              margin: $ios-spacing-xs 0 0 0;
+              color: $ios-text-secondary;
+              font-size: $ios-font-size-sm;
+              line-height: 1.4;
+            }
+          }
+        }
+      }
+    }
+
+    .empty-state {
+      text-align: center;
+      padding: $ios-spacing-xl;
+      color: $ios-text-secondary;
+
+      .empty-icon {
+        font-size: 48px;
+        margin-bottom: $ios-spacing-md;
+      }
+
+      .empty-title {
+        font-size: $ios-font-size-lg;
+        font-weight: $ios-font-weight-semibold;
+        color: $ios-text-primary;
+        margin-bottom: $ios-spacing-sm;
+      }
+
+      .empty-desc {
+        font-size: $ios-font-size-sm;
+        color: $ios-text-secondary;
+      }
+    }
+  }
+
+  // 响应式设计
+  @media (max-width: 768px) {
+    .stats-overview .stats-grid {
+      grid-template-columns: repeat(2, 1fr);
+    }
+
+    .category-stats .category-grid {
+      grid-template-columns: 1fr;
+    }
+
+    .filter-section .filter-row {
+      flex-direction: column;
+      align-items: stretch;
+
+      .filter-group {
+        flex-direction: column;
+        align-items: stretch;
+
+        .filter-buttons {
+          justify-content: center;
+        }
+      }
+    }
+
+    .reviews-list .review-item .review-header {
+      flex-direction: column;
+      gap: $ios-spacing-sm;
+    }
+  }
 }

+ 163 - 2
src/app/shared/components/customer-review-card/customer-review-card.ts

@@ -1,14 +1,175 @@
-import { Component, Input } from '@angular/core';
+import { Component, Input, computed, signal } from '@angular/core';
 import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
 import { CustomerFeedback } from '../../../models/project.model';
 
+export interface ReviewStats {
+  totalCount: number;
+  averageScore: number;
+  satisfiedCount: number;
+  unsatisfiedCount: number;
+  pendingCount: number;
+  processedCount: number;
+  categoryStats: { [key: string]: number };
+}
+
 @Component({
   selector: 'app-customer-review-card',
   standalone: true,
-  imports: [CommonModule],
+  imports: [CommonModule, FormsModule],
   templateUrl: './customer-review-card.html',
   styleUrls: ['./customer-review-card.scss']
 })
 export class CustomerReviewCardComponent {
   @Input() feedbacks: CustomerFeedback[] = [];
+  
+  // 筛选条件
+  statusFilter = signal<string>('all');
+  categoryFilter = signal<string>('all');
+  scoreFilter = signal<string>('all');
+  
+  // 评价分类
+  categories = [
+    { value: 'color', label: '色彩问题' },
+    { value: 'furniture', label: '家具款式问题' },
+    { value: 'lighting', label: '光线问题' },
+    { value: 'layout', label: '布局问题' },
+    { value: 'material', label: '材质问题' },
+    { value: 'other', label: '其他问题' }
+  ];
+  
+  // 计算统计数据
+  stats = computed<ReviewStats>(() => {
+    const feedbacks = this.feedbacks || [];
+    
+    const categoryStats: { [key: string]: number } = {};
+    this.categories.forEach(cat => {
+      categoryStats[cat.value] = feedbacks.filter(f => this.getFeedbackCategory(f) === cat.value).length;
+    });
+    
+    const scores = feedbacks.filter(f => f.rating !== undefined).map(f => f.rating || 0);
+    const averageScore = scores.length > 0 ? scores.reduce((sum, score) => sum + score, 0) / scores.length : 0;
+    
+    return {
+      totalCount: feedbacks.length,
+      averageScore: Math.round(averageScore * 10) / 10,
+      satisfiedCount: feedbacks.filter(f => f.isSatisfied).length,
+      unsatisfiedCount: feedbacks.filter(f => !f.isSatisfied).length,
+      pendingCount: feedbacks.filter(f => f.status === '待处理').length,
+      processingCount: feedbacks.filter(f => f.status === '处理中').length,
+      processedCount: feedbacks.filter(f => f.status === '已解决').length,
+      categoryStats
+    };
+  });
+  
+  // 筛选后的评价列表
+  filteredFeedbacks = computed(() => {
+    let filtered = this.feedbacks || [];
+    
+    // 状态筛选
+    const status = this.statusFilter();
+    if (status !== 'all') {
+      if (status === 'satisfied') {
+        filtered = filtered.filter(f => f.isSatisfied);
+      } else if (status === 'unsatisfied') {
+        filtered = filtered.filter(f => !f.isSatisfied);
+      } else {
+        filtered = filtered.filter(f => f.status === status);
+      }
+    }
+    
+    // 分类筛选
+    const category = this.categoryFilter();
+    if (category !== 'all') {
+      filtered = filtered.filter(f => this.getFeedbackCategory(f) === category);
+    }
+    
+    // 评分筛选
+    const score = this.scoreFilter();
+    if (score !== 'all') {
+      if (score === 'high') {
+        filtered = filtered.filter(f => (f.rating || 0) >= 4);
+      } else if (score === 'medium') {
+        filtered = filtered.filter(f => (f.rating || 0) >= 2 && (f.rating || 0) < 4);
+      } else if (score === 'low') {
+        filtered = filtered.filter(f => (f.rating || 0) < 2);
+      }
+    }
+    
+    return filtered.sort((a, b) => {
+      // 按状态排序:待处理 > 已处理,按时间倒序
+      if (a.status !== b.status) {
+        if (a.status === '待处理') return -1;
+        if (b.status === '待处理') return 1;
+      }
+      return new Date(b.updatedAt || b.createdAt).getTime() - new Date(a.updatedAt || a.createdAt).getTime();
+    });
+  });
+  
+  // 获取反馈分类
+  getFeedbackCategory(feedback: CustomerFeedback): string {
+    const content = feedback.content.toLowerCase();
+    const location = (feedback.problemLocation || '').toLowerCase();
+    
+    if (content.includes('色彩') || content.includes('颜色') || location.includes('色彩')) {
+      return 'color';
+    } else if (content.includes('家具') || content.includes('沙发') || content.includes('桌子') || location.includes('家具')) {
+      return 'furniture';
+    } else if (content.includes('光线') || content.includes('照明') || content.includes('灯光')) {
+      return 'lighting';
+    } else if (content.includes('布局') || content.includes('空间') || content.includes('摆放')) {
+      return 'layout';
+    } else if (content.includes('材质') || content.includes('质感') || content.includes('纹理')) {
+      return 'material';
+    }
+    return 'other';
+  }
+  
+  // 获取分类标签
+  getCategoryLabel(category: string): string {
+    const cat = this.categories.find(c => c.value === category);
+    return cat ? cat.label : '其他问题';
+  }
+  
+  // 获取评分星级
+  getStarRating(score: number): string[] {
+    const stars = [];
+    for (let i = 1; i <= 5; i++) {
+      if (i <= score) {
+        stars.push('★');
+      } else if (i - 0.5 <= score) {
+        stars.push('☆');
+      } else {
+        stars.push('☆');
+      }
+    }
+    return stars;
+  }
+  
+  // 获取状态样式类
+  getStatusClass(feedback: CustomerFeedback): string {
+    if (feedback.status === '已解决') return 'processed';
+    if (feedback.status === '处理中') return 'processing';
+    return 'pending';
+  }
+  
+  // 获取评分样式类
+  getScoreClass(score: number): string {
+    if (score >= 4) return 'high';
+    if (score >= 2) return 'medium';
+    return 'low';
+  }
+  
+  // 更新筛选条件
+  updateStatusFilter(status: string): void {
+    this.statusFilter.set(status);
+  }
+
+  updateCategoryFilter(event: any): void {
+    this.categoryFilter.set(event.target.value);
+  }
+
+  updateScoreFilter(event: any): void {
+    this.scoreFilter.set(event.target.value);
+  }
 }

+ 110 - 16
src/app/shared/components/settlement-card/settlement-card.html

@@ -1,17 +1,111 @@
-<div class="info-card settlement-card">
-  <h4>尾款结算</h4>
-  @if (settlements && settlements.length > 0) {
-    <ul class="items">
-      @for (s of settlements; track s.id) {
-        <li class="item">
-          <span class="name">{{ s.projectName || '结算项' }}</span>
-          <span class="amount">¥{{ s.amount | number:'1.0-2' }}</span>
-          <span class="status">{{ s.status }}</span>
-          <span class="date" *ngIf="s.settledAt as t">{{ t | date:'yyyy-MM-dd' }}</span>
-        </li>
-      }
-    </ul>
-  } @else {
-    <div class="empty">暂无结算信息</div>
-  }
+<div class="settlement-card">
+  <!-- 统计数据概览 -->
+  <div class="stats-overview">
+    <h4>尾款结算概览</h4>
+    <div class="stats-grid">
+      <div class="stat-item total">
+        <div class="stat-value">{{ formatAmount(stats().totalAmount) }}</div>
+        <div class="stat-label">总金额 ({{ stats().totalCount }}项)</div>
+      </div>
+      <div class="stat-item pending">
+        <div class="stat-value">{{ formatAmount(stats().pendingAmount) }}</div>
+        <div class="stat-label">待收款 ({{ stats().pendingCount }}项)</div>
+      </div>
+      <div class="stat-item overdue">
+        <div class="stat-value">{{ formatAmount(stats().overdueAmount) }}</div>
+        <div class="stat-label">逾期尾款 ({{ stats().overdueCount }}项)</div>
+      </div>
+      <div class="stat-item completed">
+        <div class="stat-value">{{ formatAmount(stats().completedAmount) }}</div>
+        <div class="stat-label">已收款 ({{ stats().completedCount }}项)</div>
+      </div>
+    </div>
+  </div>
+
+  <!-- 筛选和搜索 -->
+  <div class="filter-section">
+    <div class="search-bar">
+      <input 
+        type="text" 
+        placeholder="搜索项目名称或阶段..."
+        [value]="searchKeyword()"
+        (input)="updateSearchKeyword($event.target.value)"
+        class="search-input">
+    </div>
+    <div class="filter-buttons">
+      <button 
+        class="filter-btn"
+        [class.active]="statusFilter() === 'all'"
+        (click)="updateStatusFilter('all')">
+        全部
+      </button>
+      <button 
+        class="filter-btn pending"
+        [class.active]="statusFilter() === '待结算'"
+        (click)="updateStatusFilter('待结算')">
+        待结算
+      </button>
+      <button 
+        class="filter-btn overdue"
+        [class.active]="statusFilter() === 'overdue'"
+        (click)="updateStatusFilter('overdue')">
+        逾期
+      </button>
+      <button 
+        class="filter-btn completed"
+        [class.active]="statusFilter() === '已结算'"
+        (click)="updateStatusFilter('已结算')">
+        已结算
+      </button>
+    </div>
+  </div>
+
+  <!-- 结算列表 -->
+  <div class="settlements-list">
+    @if (filteredSettlements() && filteredSettlements().length > 0) {
+      <div class="list-header">
+        <span class="col-project">项目信息</span>
+        <span class="col-amount">金额</span>
+        <span class="col-status">状态</span>
+        <span class="col-date">日期</span>
+      </div>
+      <div class="list-body">
+        @for (settlement of filteredSettlements(); track settlement.id) {
+          <div class="settlement-item" [class]="getStatusClass(settlement)">
+            <div class="col-project">
+              <div class="project-name">{{ settlement.projectName || '结算项' }}</div>
+              <div class="stage-name">{{ settlement.stage || '阶段' }}</div>
+            </div>
+            <div class="col-amount">
+              <div class="amount">{{ formatAmount(settlement.amount || 0) }}</div>
+              <div class="percentage">{{ settlement.percentage }}%</div>
+            </div>
+            <div class="col-status">
+              <span class="status-badge" [class]="getStatusClass(settlement)">
+                {{ settlement.status }}
+              </span>
+              @if (isOverdue(settlement)) {
+                <div class="overdue-days">逾期 {{ getDaysOverdue(settlement) }} 天</div>
+              }
+            </div>
+            <div class="col-date">
+              @if (settlement.settledAt) {
+                <div class="settled-date">{{ settlement.settledAt | date:'yyyy-MM-dd' }}</div>
+                <div class="date-label">结算日期</div>
+              } @else {
+                <div class="due-date">{{ settlement.createdAt | date:'yyyy-MM-dd' }}</div>
+                <div class="date-label">创建日期</div>
+              }
+            </div>
+          </div>
+        }
+      </div>
+    } @else {
+      <div class="empty-state">
+        <div class="empty-icon">💰</div>
+        <div class="empty-title">暂无结算信息</div>
+        <div class="empty-desc">当前筛选条件下没有找到相关的结算记录</div>
+      </div>
+    }
+  </div>
 </div>

+ 325 - 9
src/app/shared/components/settlement-card/settlement-card.scss

@@ -1,14 +1,330 @@
 @use '../../styles/_ios-theme.scss' as *;
 
-:host { display: block; height: 100%; }
+:host { 
+  display: block; 
+  height: 100%; 
+}
 
 .settlement-card {
-  h4 { margin: 0; font-size: $ios-font-size-sm; font-weight: $ios-font-weight-semibold; color: $ios-text-primary; padding-bottom: $ios-spacing-xs; border-bottom: 1px solid $ios-border; }
-  .items { margin: 0; padding-left: 0; list-style: none; }
-  .item { display: grid; grid-template-columns: 1fr auto auto auto; gap: $ios-spacing-xs; padding: $ios-spacing-sm 0; border-bottom: 1px dashed $ios-border; }
-  .item:last-child { border-bottom: none; }
-  .name { font-weight: $ios-font-weight-medium; color: $ios-text-primary; }
-  .amount { color: $ios-primary; }
-  .status { font-size: $ios-font-size-xs; color: $ios-text-secondary; }
-  .date { font-size: $ios-font-size-xs; color: $ios-text-secondary; }
+  background: white;
+  border-radius: 12px;
+  padding: 20px;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
+  border: 1px solid $ios-border;
+
+  // 统计数据概览
+  .stats-overview {
+    margin-bottom: 24px;
+
+    h4 {
+      margin: 0 0 16px 0;
+      font-size: 18px;
+      font-weight: $ios-font-weight-semibold;
+      color: $ios-text-primary;
+    }
+
+    .stats-grid {
+      display: grid;
+      grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+      gap: 16px;
+
+      .stat-item {
+        background: #f8f9fa;
+        border-radius: 8px;
+        padding: 16px;
+        text-align: center;
+        border: 1px solid $ios-border;
+
+        &.total {
+          background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+          color: white;
+          border: none;
+        }
+
+        &.pending {
+          background: linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%);
+          color: #8b4513;
+        }
+
+        &.overdue {
+          background: linear-gradient(135deg, #ff9a9e 0%, #fecfef 100%);
+          color: #dc3545;
+        }
+
+        &.completed {
+          background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);
+          color: #28a745;
+        }
+
+        .stat-value {
+          font-size: 24px;
+          font-weight: $ios-font-weight-bold;
+          margin-bottom: 4px;
+        }
+
+        .stat-label {
+          font-size: 12px;
+          opacity: 0.8;
+        }
+      }
+    }
+  }
+
+  // 筛选和搜索区域
+  .filter-section {
+    margin-bottom: 20px;
+    display: flex;
+    flex-direction: column;
+    gap: 12px;
+
+    .search-bar {
+      .search-input {
+        width: 100%;
+        padding: 10px 16px;
+        border: 1px solid $ios-border;
+        border-radius: 8px;
+        font-size: 14px;
+        background: white;
+        transition: border-color 0.2s ease;
+
+        &:focus {
+          outline: none;
+          border-color: $ios-primary;
+          box-shadow: 0 0 0 2px rgba(0, 122, 255, 0.1);
+        }
+
+        &::placeholder {
+          color: $ios-text-secondary;
+        }
+      }
+    }
+
+    .filter-buttons {
+      display: flex;
+      gap: 8px;
+      flex-wrap: wrap;
+
+      .filter-btn {
+        padding: 8px 16px;
+        border: 1px solid $ios-border;
+        border-radius: 20px;
+        background: white;
+        color: $ios-text-secondary;
+        font-size: 14px;
+        cursor: pointer;
+        transition: all 0.2s ease;
+
+        &:hover {
+          background: #f8f9fa;
+        }
+
+        &.active {
+          background: $ios-primary;
+          color: white;
+          border-color: $ios-primary;
+        }
+
+        &.pending.active {
+          background: #ff9500;
+          border-color: #ff9500;
+        }
+
+        &.overdue.active {
+          background: #dc3545;
+          border-color: #dc3545;
+        }
+
+        &.completed.active {
+          background: #28a745;
+          border-color: #28a745;
+        }
+      }
+    }
+  }
+
+  // 结算列表
+  .settlements-list {
+    .list-header {
+      display: grid;
+      grid-template-columns: 2fr 1fr 1fr 1fr;
+      gap: 16px;
+      padding: 12px 16px;
+      background: #f8f9fa;
+      border-radius: 8px;
+      font-weight: $ios-font-weight-semibold;
+      color: $ios-text-secondary;
+      font-size: 12px;
+      text-transform: uppercase;
+      letter-spacing: 0.5px;
+      margin-bottom: 8px;
+    }
+
+    .list-body {
+      .settlement-item {
+        display: grid;
+        grid-template-columns: 2fr 1fr 1fr 1fr;
+        gap: 16px;
+        padding: 16px;
+        border: 1px solid $ios-border;
+        border-radius: 8px;
+        margin-bottom: 8px;
+        background: white;
+        transition: all 0.2s ease;
+
+        &:hover {
+          box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+          transform: translateY(-1px);
+        }
+
+        &.overdue {
+          border-left: 4px solid #dc3545;
+          background: #fff5f5;
+        }
+
+        &.pending {
+          border-left: 4px solid #ff9500;
+        }
+
+        &.completed {
+          border-left: 4px solid #28a745;
+          background: #f8fff9;
+        }
+
+        .col-project {
+          .project-name {
+            font-weight: $ios-font-weight-semibold;
+            color: $ios-text-primary;
+            margin-bottom: 4px;
+          }
+
+          .stage-name {
+            font-size: 12px;
+            color: $ios-text-secondary;
+          }
+        }
+
+        .col-amount {
+          .amount {
+            font-weight: $ios-font-weight-bold;
+            color: $ios-primary;
+            font-size: 16px;
+          }
+
+          .percentage {
+            font-size: 12px;
+            color: $ios-text-secondary;
+            margin-top: 2px;
+          }
+        }
+
+        .col-status {
+          .status-badge {
+            display: inline-block;
+            padding: 4px 8px;
+            border-radius: 12px;
+            font-size: 12px;
+            font-weight: $ios-font-weight-medium;
+
+            &.pending {
+              background: rgba(255, 149, 0, 0.1);
+              color: #ff9500;
+            }
+
+            &.overdue {
+              background: rgba(220, 53, 69, 0.1);
+              color: #dc3545;
+            }
+
+            &.completed {
+              background: rgba(40, 167, 69, 0.1);
+              color: #28a745;
+            }
+          }
+
+          .overdue-days {
+            font-size: 11px;
+            color: #dc3545;
+            margin-top: 4px;
+            font-weight: $ios-font-weight-medium;
+          }
+        }
+
+        .col-date {
+          text-align: right;
+
+          .settled-date,
+          .due-date {
+            font-weight: $ios-font-weight-medium;
+            color: $ios-text-primary;
+          }
+
+          .date-label {
+            font-size: 11px;
+            color: $ios-text-secondary;
+            margin-top: 2px;
+          }
+        }
+      }
+    }
+  }
+
+  // 空状态
+  .empty-state {
+    text-align: center;
+    padding: 40px 20px;
+    color: $ios-text-secondary;
+
+    .empty-icon {
+      font-size: 48px;
+      margin-bottom: 16px;
+      opacity: 0.5;
+    }
+
+    .empty-title {
+      font-size: 16px;
+      font-weight: $ios-font-weight-semibold;
+      color: $ios-text-primary;
+      margin-bottom: 8px;
+    }
+
+    .empty-desc {
+      font-size: 14px;
+      line-height: 1.4;
+    }
+  }
+}
+
+// 响应式设计
+@media (max-width: 768px) {
+  .settlement-card {
+    .stats-overview .stats-grid {
+      grid-template-columns: repeat(2, 1fr);
+    }
+
+    .filter-section {
+      .filter-buttons {
+        justify-content: center;
+      }
+    }
+
+    .settlements-list {
+      .list-header {
+        display: none;
+      }
+
+      .list-body .settlement-item {
+        grid-template-columns: 1fr;
+        gap: 8px;
+
+        .col-project,
+        .col-amount,
+        .col-status,
+        .col-date {
+          display: flex;
+          justify-content: space-between;
+          align-items: center;
+        }
+      }
+    }
+  }
 }

+ 106 - 2
src/app/shared/components/settlement-card/settlement-card.ts

@@ -1,14 +1,118 @@
-import { Component, Input } from '@angular/core';
+import { Component, Input, computed, signal } from '@angular/core';
 import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
 import { Settlement } from '../../../models/project.model';
 
+export interface SettlementStats {
+  totalAmount: number;
+  pendingAmount: number;
+  overdueAmount: number;
+  completedAmount: number;
+  totalCount: number;
+  pendingCount: number;
+  overdueCount: number;
+  completedCount: number;
+}
+
 @Component({
   selector: 'app-settlement-card',
   standalone: true,
-  imports: [CommonModule],
+  imports: [CommonModule, FormsModule],
   templateUrl: './settlement-card.html',
   styleUrls: ['./settlement-card.scss']
 })
 export class SettlementCardComponent {
   @Input() settlements: Settlement[] = [];
+  
+  // 筛选条件
+  statusFilter = signal<string>('all');
+  searchKeyword = signal<string>('');
+  
+  // 计算统计数据
+  stats = computed<SettlementStats>(() => {
+    const settlements = this.settlements || [];
+    
+    return {
+      totalAmount: settlements.reduce((sum, s) => sum + (s.amount || 0), 0),
+      pendingAmount: settlements.filter(s => s.status === '待结算').reduce((sum, s) => sum + (s.amount || 0), 0),
+      overdueAmount: settlements.filter(s => this.isOverdue(s)).reduce((sum, s) => sum + (s.amount || 0), 0),
+      completedAmount: settlements.filter(s => s.status === '已结算').reduce((sum, s) => sum + (s.amount || 0), 0),
+      totalCount: settlements.length,
+      pendingCount: settlements.filter(s => s.status === '待结算').length,
+      overdueCount: settlements.filter(s => this.isOverdue(s)).length,
+      completedCount: settlements.filter(s => s.status === '已结算').length
+    };
+  });
+  
+  // 筛选后的结算列表
+  filteredSettlements = computed(() => {
+    let filtered = this.settlements || [];
+    
+    // 状态筛选
+    const status = this.statusFilter();
+    if (status !== 'all') {
+      if (status === 'overdue') {
+        filtered = filtered.filter(s => this.isOverdue(s));
+      } else {
+        filtered = filtered.filter(s => s.status === status);
+      }
+    }
+    
+    // 关键词搜索
+    const keyword = this.searchKeyword().toLowerCase();
+    if (keyword) {
+      filtered = filtered.filter(s => 
+        (s.projectName || '').toLowerCase().includes(keyword) ||
+        (s.stage || '').toLowerCase().includes(keyword)
+      );
+    }
+    
+    return filtered;
+  });
+  
+  // 判断是否逾期
+  isOverdue(settlement: Settlement): boolean {
+    if (settlement.status === '已结算') return false;
+    // 简化逾期判断:如果是待结算状态且创建时间超过30天,则认为逾期
+    const thirtyDaysAgo = new Date();
+    thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
+    return settlement.createdAt < thirtyDaysAgo;
+  }
+  
+  // 获取状态样式类
+  getStatusClass(settlement: Settlement): string {
+    if (this.isOverdue(settlement)) return 'overdue';
+    return settlement.status === '已结算' ? 'completed' : 'pending';
+  }
+  
+  // 格式化金额
+  formatAmount(amount: number): string {
+    return new Intl.NumberFormat('zh-CN', {
+      style: 'currency',
+      currency: 'CNY',
+      minimumFractionDigits: 0,
+      maximumFractionDigits: 2
+    }).format(amount);
+  }
+  
+  // 更新筛选条件
+  updateStatusFilter(status: string): void {
+    this.statusFilter.set(status);
+  }
+  
+  updateSearchKeyword(keyword: string): void {
+    this.searchKeyword.set(keyword);
+  }
+  
+  // 计算逾期天数
+  getDaysOverdue(settlement: Settlement): number {
+    if (settlement.status === '已结算') return 0;
+    const thirtyDaysAgo = new Date();
+    thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
+    if (settlement.createdAt >= thirtyDaysAgo) return 0;
+    
+    const today = new Date();
+    const diffTime = today.getTime() - thirtyDaysAgo.getTime();
+    return Math.max(0, Math.ceil(diffTime / (1000 * 60 * 60 * 24)));
+  }
 }

+ 3 - 0
src/styles.scss

@@ -6,6 +6,9 @@
 // custom components at https://material.angular.dev/guide/theming
 @use '@angular/material' as mat;
 
+// 导入变量
+@import './styles/variables';
+
 // 自定义字体配置
 @include mat.core();
 

+ 45 - 0
src/styles/_variables.scss

@@ -0,0 +1,45 @@
+// iOS风格变量定义
+
+// 颜色
+$ios-primary: #007AFF;
+$ios-background: #FFFFFF;
+$ios-background-secondary: #F2F2F7;
+$ios-background-tertiary: #FFFFFF;
+$ios-border: #C6C6C8;
+$ios-text-primary: #000000;
+$ios-text-secondary: #8E8E93;
+$ios-text-tertiary: #C7C7CC;
+$ios-error: #FF3B30;
+
+// 字体大小
+$ios-font-size-xs: 12px;
+$ios-font-size-sm: 14px;
+$ios-font-size-md: 16px;
+$ios-font-size-lg: 18px;
+$ios-font-size-xl: 20px;
+
+// 字体粗细
+$ios-font-weight-light: 300;
+$ios-font-weight-normal: 400;
+$ios-font-weight-medium: 500;
+$ios-font-weight-semibold: 600;
+$ios-font-weight-bold: 700;
+
+// 间距
+$ios-spacing-xs: 4px;
+$ios-spacing-sm: 8px;
+$ios-spacing-md: 16px;
+$ios-spacing-lg: 24px;
+$ios-spacing-xl: 32px;
+
+// 圆角
+$ios-radius-sm: 4px;
+$ios-radius-md: 8px;
+$ios-radius-lg: 12px;
+$ios-radius-xl: 16px;
+
+// 兼容旧的变量名
+$ios-border-radius-sm: $ios-radius-sm;
+$ios-border-radius-md: $ios-radius-md;
+$ios-border-radius-lg: $ios-radius-lg;
+$ios-border-radius-xl: $ios-radius-xl;