ソースを参照

feat:customer17

徐福静0235668 10 時間 前
コミット
b1a17999cd

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

@@ -1520,11 +1520,19 @@ export class ProjectDetail implements OnInit, OnDestroy {
 
   // 新增:客户评价阶段确认并自动进入下一阶段(投诉处理)
   confirmCustomerReview(): void {
+    console.log('确认客户评价完成');
+    // 可以在这里添加更多逻辑,比如更新项目状态等
+    // 调用服务更新后端数据
+    // this.projectService.confirmCustomerReview(this.projectId);
     this.advanceToNextStage('客户评价');
   }
 
   // 新增:投诉处理阶段确认并完成项目
   confirmComplaint(): void {
+    console.log('确认投诉处理完成');
+    // 可以在这里添加更多逻辑,比如标记项目完成等
+    // 调用服务更新后端数据
+    // this.projectService.confirmComplaintResolution(this.projectId);
     this.advanceToNextStage('投诉处理');
   }
 

+ 131 - 2
src/app/services/project.service.ts

@@ -174,13 +174,142 @@ export class ProjectService {
     {
       id: 'f1',
       projectId: '1',
-      content: '客厅设计不太满意',
+      content: '客厅设计不太满意,沙发的颜色和整体风格不太搭配',
       isSatisfied: false,
       problemLocation: '沙发区域',
       expectedEffect: '更宽敞舒适',
       referenceCase: '提供了参考图片',
       status: '待处理',
-      createdAt: new Date('2025-09-07')
+      createdAt: new Date('2025-09-07'),
+      customerName: '张女士',
+      rating: 2
+    },
+    {
+      id: 'f2',
+      projectId: '1',
+      content: '整体设计很满意,特别是厨房的布局很合理,使用起来很方便',
+      isSatisfied: true,
+      problemLocation: '',
+      expectedEffect: '',
+      referenceCase: '',
+      status: '已解决',
+      createdAt: new Date('2025-09-05'),
+      updatedAt: new Date('2025-09-06'),
+      customerName: '张女士',
+      rating: 5,
+      response: '感谢您的认可,我们会继续努力为您提供更好的设计服务。'
+    },
+    {
+      id: 'f3',
+      projectId: '1',
+      content: '卧室的灯光设计有些暗,希望能增加一些辅助照明',
+      isSatisfied: false,
+      problemLocation: '主卧室',
+      expectedEffect: '更明亮温馨',
+      referenceCase: '希望参考北欧风格的照明设计',
+      status: '处理中',
+      createdAt: new Date('2025-09-06'),
+      customerName: '张女士',
+      rating: 3
+    },
+    {
+      id: 'f4',
+      projectId: '2',
+      content: '餐厅的家具选择很棒,质感和颜色都很满意',
+      isSatisfied: true,
+      problemLocation: '',
+      expectedEffect: '',
+      referenceCase: '',
+      status: '已解决',
+      createdAt: new Date('2025-09-04'),
+      updatedAt: new Date('2025-09-05'),
+      customerName: '李先生',
+      rating: 4,
+      response: '谢谢您的好评,我们精心挑选的家具能得到您的认可很开心。'
+    },
+    {
+      id: 'f5',
+      projectId: '2',
+      content: '阳台的设计需要调整,现在的布局不太实用',
+      isSatisfied: false,
+      problemLocation: '阳台区域',
+      expectedEffect: '更实用的储物和休闲空间',
+      referenceCase: '希望有更多储物柜',
+      status: '待处理',
+      createdAt: new Date('2025-09-08'),
+      customerName: '李先生',
+      rating: 2
+    },
+    {
+      id: 'f6',
+      projectId: '3',
+      content: '书房的设计非常棒,工作环境很舒适,书柜的设计也很实用',
+      isSatisfied: true,
+      problemLocation: '',
+      expectedEffect: '',
+      referenceCase: '',
+      status: '已解决',
+      createdAt: new Date('2025-09-03'),
+      updatedAt: new Date('2025-09-04'),
+      customerName: '王女士',
+      rating: 5,
+      response: '很高兴书房设计能满足您的工作需求,实用性一直是我们设计的重点。'
+    },
+    {
+      id: 'f7',
+      projectId: '3',
+      content: '客厅的材质选择有些问题,地板的纹理和墙面不太协调',
+      isSatisfied: false,
+      problemLocation: '客厅地面',
+      expectedEffect: '更协调的材质搭配',
+      referenceCase: '希望参考现代简约风格',
+      status: '处理中',
+      createdAt: new Date('2025-09-07'),
+      customerName: '王女士',
+      rating: 3
+    },
+    {
+      id: 'f8',
+      projectId: '1',
+      content: '儿童房的设计很用心,孩子很喜欢,安全性也考虑得很周到',
+      isSatisfied: true,
+      problemLocation: '',
+      expectedEffect: '',
+      referenceCase: '',
+      status: '已解决',
+      createdAt: new Date('2025-09-02'),
+      updatedAt: new Date('2025-09-03'),
+      customerName: '张女士',
+      rating: 5,
+      response: '孩子的安全和喜好是我们设计儿童房的首要考虑,很开心能得到小朋友的认可。'
+    },
+    {
+      id: 'f9',
+      projectId: '2',
+      content: '厨房的收纳设计很实用,但是操作台的高度需要调整',
+      isSatisfied: false,
+      problemLocation: '厨房操作台',
+      expectedEffect: '更符合人体工学的高度',
+      referenceCase: '希望按照我的身高定制',
+      status: '待处理',
+      createdAt: new Date('2025-09-09'),
+      customerName: '李先生',
+      rating: 3
+    },
+    {
+      id: 'f10',
+      projectId: '3',
+      content: '整体效果超出预期,设计师很专业,沟通也很顺畅',
+      isSatisfied: true,
+      problemLocation: '',
+      expectedEffect: '',
+      referenceCase: '',
+      status: '已解决',
+      createdAt: new Date('2025-09-01'),
+      updatedAt: new Date('2025-09-02'),
+      customerName: '王女士',
+      rating: 5,
+      response: '感谢您对我们团队的认可,专业和沟通是我们一直坚持的服务标准。'
     }
   ];
 

+ 83 - 35
src/app/shared/components/complaint-card/complaint-card.html

@@ -87,20 +87,20 @@
           </button>
           <button 
             class="filter-btn pending"
-            [class.active]="statusFilter() === '待处理'"
-            (click)="updateStatusFilter('待处理')">
+            [class.active]="statusFilter() === 'pending'"
+            (click)="updateStatusFilter('pending')">
             待处理
           </button>
           <button 
             class="filter-btn processing"
-            [class.active]="statusFilter() === '处理中'"
-            (click)="updateStatusFilter('处理中')">
+            [class.active]="statusFilter() === 'processing'"
+            (click)="updateStatusFilter('processing')">
             处理中
           </button>
           <button 
             class="filter-btn resolved"
-            [class.active]="statusFilter() === '已解决'"
-            (click)="updateStatusFilter('已解决')">
+            [class.active]="statusFilter() === 'resolved'"
+            (click)="updateStatusFilter('resolved')">
             已解决
           </button>
         </div>
@@ -139,22 +139,18 @@
   <!-- 投诉列表 -->
   <div class="complaints-list">
     @if (filteredComplaints() && filteredComplaints().length > 0) {
-      <div class="list-body">
+      <div class="complaints-grid">
         @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="complaint-card-item" [class]="getStatusClass(complaint)" [class.overdue]="isOverdue(complaint)">
+            <!-- 卡片头部 -->
+            <div class="card-header">
+              <div class="header-left">
+                <span class="type-tag" [class]="getComplaintType(complaint)">{{ getTypeLabel(getComplaintType(complaint)) }}</span>
                 <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">
+              <div class="header-right">
                 <span class="status-badge" [class]="getStatusClass(complaint)">
                   {{ complaint.status }}
                 </span>
@@ -164,43 +160,95 @@
               </div>
             </div>
             
-            <div class="complaint-content">
-              <p class="description">{{ complaint.description }}</p>
+            <!-- 卡片主体 -->
+            <div class="card-body">
+              @if (complaint.customerName) {
+                <div class="customer-info">
+                  <span class="customer-label">客户:</span>
+                  <span class="customer-name">{{ complaint.customerName }}</span>
+                </div>
+              }
+              
+              <div class="complaint-description">
+                <h4>投诉内容</h4>
+                <p>{{ complaint.description }}</p>
+              </div>
+              
               @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">
-                  }
+                  <h5>相关图片</h5>
+                  <div class="images-grid">
+                    @for (image of complaint.images; track $index) {
+                      <img [src]="image" [alt]="'投诉图片' + ($index + 1)" class="complaint-image">
+                    }
+                  </div>
                 </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>
+              
+              <div class="time-section">
+                <div class="time-item">
+                  <span class="time-label">提交时间:</span>
+                  <span class="time-value">{{ complaint.submittedAt | date:'yyyy-MM-dd HH:mm' }}</span>
+                </div>
+                <div class="time-item">
+                  <span class="time-label">处理天数:</span>
+                  <span class="time-value">{{ getDaysInProgress(complaint) }} 天</span>
+                </div>
                 @if (complaint.resolvedAt) {
-                  <span class="resolved-time">解决时间:{{ complaint.resolvedAt | date:'yyyy-MM-dd HH:mm' }}</span>
+                  <div class="time-item">
+                    <span class="time-label">解决时间:</span>
+                    <span class="time-value">{{ complaint.resolvedAt | date:'yyyy-MM-dd HH:mm' }}</span>
+                  </div>
                 }
               </div>
               
               @if (complaint.handlerComment) {
-                <div class="handler-response">
-                  <strong>处理意见:</strong>
-                  <p class="response-text">{{ complaint.handlerComment }}</p>
+                <div class="handler-section">
+                  <h5>处理意见</h5>
+                  <p class="handler-comment">{{ complaint.handlerComment }}</p>
                   @if (complaint.handlerName) {
-                    <span class="handler-name">处理人:{{ complaint.handlerName }}</span>
+                    <div class="handler-info">
+                      <span class="handler-label">处理人:</span>
+                      <span class="handler-name">{{ complaint.handlerName }}</span>
+                    </div>
                   }
                 </div>
               }
               
               @if (complaint.solution) {
                 <div class="solution-section">
-                  <strong>解决方案:</strong>
+                  <h5>解决方案</h5>
                   <p class="solution-text">{{ complaint.solution }}</p>
                 </div>
               }
             </div>
+            
+            <!-- 卡片底部操作按钮 -->
+            <div class="card-footer">
+              @if (complaint.status === '待处理') {
+                <button class="action-btn process-btn" (click)="startProcessing(complaint)">
+                  <span class="btn-icon">🔧</span>
+                  开始处理
+                </button>
+              } @else if (complaint.status === '处理中') {
+                <button class="action-btn complete-btn" (click)="completeProcessing(complaint)">
+                  <span class="btn-icon">✅</span>
+                  完成处理
+                </button>
+              } @else if (complaint.status === '已解决') {
+                <div class="completed-status">
+                  <span class="completed-icon">✓</span>
+                  <span class="completed-text">处理完成</span>
+                </div>
+              }
+              
+              @if (complaint.status !== '已解决') {
+                <button class="action-btn detail-btn" (click)="viewDetails(complaint)">
+                  <span class="btn-icon">👁️</span>
+                  查看详情
+                </button>
+              }
+            </div>
           </div>
         }
       </div>
@@ -208,7 +256,7 @@
       <div class="empty-state">
         <div class="empty-icon">📋</div>
         <div class="empty-title">暂无投诉记录</div>
-        <div class="empty-desc">当前筛选条件下没有找到相关的投诉记录</div>
+        <div class="empty-description">当前没有符合筛选条件的投诉记录</div>
       </div>
     }
   </div>

+ 436 - 200
src/app/shared/components/complaint-card/complaint-card.scss

@@ -292,270 +292,506 @@
     }
   }
 
-  // 投诉列表
+  // 投诉列表样式
   .complaints-list {
-    .list-body {
-      .complaint-item {
-        display: flex;
-        align-items: center;
-        gap: ios.$ios-spacing-md;
-        padding: ios.$ios-spacing-md;
-        background: ios.$ios-background;
-        border: 1px solid ios.$ios-border;
-        border-radius: ios.$ios-radius-md;
-        transition: all 0.2s ease;
+    margin-top: 20px;
 
-        &:hover {
-          box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
-        }
+    .complaints-grid {
+      display: grid;
+      grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
+      gap: 16px;
+      padding: 0;
+    }
 
-        &:last-child {
-          margin-bottom: 0;
-        }
+    .complaint-card-item {
+      background: #ffffff;
+      border-radius: 12px;
+      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+      border: 1px solid #e8e8e8;
+      overflow: hidden;
+      transition: all 0.3s ease;
+      display: flex;
+      flex-direction: column;
 
-        &.overdue {
-          border-left: 4px solid ios.$ios-danger;
-          background: rgba(ios.$ios-danger, 0.02);
+      &:hover {
+        box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
+        transform: translateY(-2px);
+      }
+
+      &.overdue {
+        border-left: 4px solid #ff4757;
+        
+        .card-header {
+          background: linear-gradient(135deg, #fff5f5 0%, #ffffff 100%);
         }
+      }
 
-        .complaint-header {
+      // 卡片头部
+        .card-header {
+          padding: 12px 16px;
+          background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
+          border-bottom: 1px solid #e9ecef;
           display: flex;
           justify-content: space-between;
-          align-items: flex-start;
-          margin-bottom: ios.$ios-spacing-md;
-
-          .complaint-info {
-            flex: 1;
-
-            .complaint-title {
-              display: flex;
-              align-items: center;
-              gap: ios.$ios-spacing-sm;
-              margin-bottom: ios.$ios-spacing-xs;
-
-              .type-tag {
-                padding: ios.$ios-spacing-xs ios.$ios-spacing-sm;
-                background: ios.$ios-primary;
-                color: white;
-                border-radius: ios.$ios-radius-sm;
-                font-size: ios.$ios-font-size-xs;
-              }
-
-              .customer-name {
-                font-size: ios.$ios-font-size-md;
-                font-weight: ios.$ios-font-weight-semibold;
-                color: ios.$ios-text-primary;
-              }
-            }
+          align-items: center;
 
-            .priority-badge {
-              display: inline-block;
-              padding: ios.$ios-spacing-xs ios.$ios-spacing-sm;
-              color: white;
-              border-radius: ios.$ios-radius-sm;
-              font-size: ios.$ios-font-size-xs;
-              font-weight: ios.$ios-font-weight-semibold;
+        .header-left {
+          display: flex;
+          align-items: center;
+          gap: 12px;
+          flex: 1;
+
+          .type-tag {
+            background: #007bff;
+            color: white;
+            padding: 4px 12px;
+            border-radius: 16px;
+            font-size: 12px;
+            font-weight: 500;
+            white-space: nowrap;
+
+            &.quality {
+              background: #dc3545;
             }
-          }
 
-          .complaint-meta {
-            display: flex;
-            align-items: center;
-            gap: ios.$ios-spacing-sm;
-            margin-bottom: ios.$ios-spacing-xs;
+            &.service {
+              background: #28a745;
+            }
 
-            .complaint-id {
-              padding: ios.$ios-spacing-xs ios.$ios-spacing-sm;
-              background: ios.$ios-primary;
-              color: white;
-              border-radius: ios.$ios-radius-sm;
-              font-size: ios.$ios-font-size-xs;
+            &.delivery {
+              background: #ffc107;
+              color: #212529;
             }
 
-            .complaint-title {
-              font-size: ios.$ios-font-size-md;
-              font-weight: ios.$ios-font-weight-semibold;
-              color: ios.$ios-text-primary;
+            &.other {
+              background: #6c757d;
             }
           }
 
-          .complaint-status {
-            padding: ios.$ios-spacing-xs ios.$ios-spacing-sm;
+          .priority-badge {
             color: white;
-            border-radius: ios.$ios-radius-sm;
-            font-size: ios.$ios-font-size-xs;
-            font-weight: ios.$ios-font-weight-semibold;
+            padding: 4px 10px;
+            border-radius: 12px;
+            font-size: 11px;
+            font-weight: 500;
+            white-space: nowrap;
+          }
+        }
+
+        .header-right {
+          display: flex;
+          align-items: center;
+          gap: 8px;
+
+          .status-badge {
+            padding: 4px 12px;
+            border-radius: 12px;
+            font-size: 12px;
+            font-weight: 500;
+            white-space: nowrap;
 
             &.pending {
-              background: rgba(ios.$ios-warning, 0.1);
-              color: ios.$ios-warning;
+              background: #fff3cd;
+              color: #856404;
+              border: 1px solid #ffeaa7;
             }
 
             &.processing {
-              background: rgba(ios.$ios-info, 0.1);
-              color: ios.$ios-info;
+              background: #d1ecf1;
+              color: #0c5460;
+              border: 1px solid #bee5eb;
             }
 
             &.resolved {
-              background: rgba(ios.$ios-success, 0.1);
-              color: ios.$ios-success;
+              background: #d4edda;
+              color: #155724;
+              border: 1px solid #c3e6cb;
             }
+          }
+
+          .overdue-badge {
+            background: #ff4757;
+            color: white;
+            padding: 2px 8px;
+            border-radius: 10px;
+            font-size: 10px;
+            font-weight: 600;
+            animation: pulse 2s infinite;
+          }
+        }
+      }
+
+      // 卡片主体
+      .card-body {
+        padding: 16px;
+        flex: 1;
+        display: flex;
+        flex-direction: column;
+        gap: 12px;
+
+        .customer-info {
+          display: flex;
+          align-items: center;
+          padding: 8px 12px;
+          background: #f8f9fa;
+          border-radius: 8px;
+          border-left: 3px solid #007bff;
+
+          .customer-label {
+            font-weight: 500;
+            color: #495057;
+            margin-right: 8px;
+          }
+
+          .customer-name {
+            color: #007bff;
+            font-weight: 600;
+          }
+        }
+
+        .complaint-description {
+          h4 {
+            margin: 0 0 8px 0;
+            font-size: 14px;
+            font-weight: 600;
+            color: #343a40;
+          }
+
+          p {
+            margin: 0;
+            color: #6c757d;
+            line-height: 1.5;
+            font-size: 14px;
+          }
+        }
+
+        .complaint-images {
+          h5 {
+            margin: 0 0 8px 0;
+            font-size: 13px;
+            font-weight: 600;
+            color: #495057;
+          }
+
+          .images-grid {
+            display: grid;
+            grid-template-columns: repeat(auto-fill, minmax(60px, 1fr));
+            gap: 6px;
+            max-width: 100%;
+          }
 
-            &.urgent {
-              padding: ios.$ios-spacing-xs ios.$ios-spacing-sm;
-              background: rgba(ios.$ios-danger, 0.1);
-              color: ios.$ios-danger;
-              border-radius: ios.$ios-radius-sm;
-              font-size: ios.$ios-font-size-xs;
-              font-weight: ios.$ios-font-weight-semibold;
+          .complaint-image {
+            width: 100%;
+            height: 60px;
+            object-fit: cover;
+            border-radius: 4px;
+            border: 1px solid #dee2e6;
+            cursor: pointer;
+            transition: transform 0.2s ease;
+
+            &:hover {
+              transform: scale(1.05);
             }
           }
+        }
+
+        .time-section {
+          background: #f8f9fa;
+          padding: 12px;
+          border-radius: 8px;
+          border: 1px solid #e9ecef;
 
-          .complaint-content {
-            margin-bottom: ios.$ios-spacing-md;
+          .time-item {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            margin-bottom: 6px;
 
-            .complaint-description {
-              margin: 0 0 ios.$ios-spacing-sm 0;
-              color: ios.$ios-text-primary;
+            &:last-child {
+              margin-bottom: 0;
             }
 
-            .complaint-details {
-              display: flex;
-              flex-direction: column;
-              gap: ios.$ios-spacing-sm;
+            .time-label {
+              font-size: 13px;
+              color: #6c757d;
+              font-weight: 500;
             }
 
-            .detail-item {
-              display: flex;
-              justify-content: space-between;
-              padding: ios.$ios-spacing-xs;
-              border-radius: ios.$ios-radius-sm;
-              border: 1px solid ios.$ios-border;
-
-              .detail-label {
-                font-weight: 500;
-              }
-
-              .detail-value {
-                display: flex;
-                align-items: center;
-                gap: ios.$ios-spacing-md;
-                margin-bottom: ios.$ios-spacing-sm;
-
-                .customer-info {
-                  display: flex;
-                  align-items: center;
-                  gap: ios.$ios-spacing-xs;
-
-                  .customer-name {
-                    font-size: ios.$ios-font-size-xs;
-                    color: ios.$ios-text-secondary;
-                  }
-
-                  .customer-id {
-                    color: ios.$ios-primary;
-                    font-weight: ios.$ios-font-weight-semibold;
-                  }
-
-                  .customer-level {
-                    color: ios.$ios-success;
-                  }
-                }
-
-                .complaint-actions {
-                  padding: ios.$ios-spacing-sm;
-                  background: rgba(ios.$ios-primary, 0.05);
-                  border-radius: ios.$ios-radius-sm;
-                  border-left: 3px solid ios.$ios-primary;
-                  margin-bottom: ios.$ios-spacing-sm;
-
-                  .action-title {
-                    color: ios.$ios-primary;
-                    font-size: ios.$ios-font-size-sm;
-                  }
-
-                  .action-description {
-                    margin: ios.$ios-spacing-xs 0;
-                    color: ios.$ios-text-secondary;
-                    font-size: ios.$ios-font-size-sm;
-                  }
-
-                  .action-timestamp {
-                    font-size: ios.$ios-font-size-xs;
-                    color: ios.$ios-text-secondary;
-                  }
-                }
-
-                .resolution-note {
-                  padding: ios.$ios-spacing-sm;
-                  background: rgba(ios.$ios-success, 0.05);
-                  border-radius: ios.$ios-radius-sm;
-                  border-left: 3px solid ios.$ios-success;
-
-                  .resolution-title {
-                    color: ios.$ios-success;
-                    font-size: ios.$ios-font-size-sm;
-                  }
-
-                  .resolution-content {
-                    margin: ios.$ios-spacing-xs 0 0 0;
-                    color: ios.$ios-text-secondary;
-                    font-size: ios.$ios-font-size-sm;
-                  }
-                }
-              }
+            .time-value {
+              font-size: 13px;
+              color: #495057;
+              font-weight: 600;
             }
           }
+        }
 
-          .complaint-footer {
-            text-align: center;
-            padding: ios.$ios-spacing-xl;
-            color: ios.$ios-text-secondary;
+        .handler-section {
+          background: #e3f2fd;
+          padding: 12px;
+          border-radius: 8px;
+          border-left: 3px solid #2196f3;
+
+          h5 {
+            margin: 0 0 8px 0;
+            font-size: 13px;
+            font-weight: 600;
+            color: #1976d2;
+          }
 
-            .no-complaints-message {
-              margin-bottom: ios.$ios-spacing-md;
+          .handler-comment {
+            margin: 0 0 8px 0;
+            color: #424242;
+            font-size: 13px;
+            line-height: 1.4;
+          }
+
+          .handler-info {
+            display: flex;
+            align-items: center;
+
+            .handler-label {
+              font-size: 12px;
+              color: #757575;
+              margin-right: 6px;
             }
 
-            .no-complaints-title {
-              font-size: ios.$ios-font-size-lg;
-              font-weight: ios.$ios-font-weight-semibold;
-              color: ios.$ios-text-primary;
-              margin-bottom: ios.$ios-spacing-sm;
+            .handler-name {
+              font-size: 12px;
+              color: #1976d2;
+              font-weight: 600;
             }
+          }
+        }
 
-            .no-complaints-description {
-              font-size: ios.$ios-font-size-sm;
-              color: ios.$ios-text-secondary;
+        .solution-section {
+          background: #e8f5e8;
+          padding: 12px;
+          border-radius: 8px;
+          border-left: 3px solid #4caf50;
+
+          h5 {
+            margin: 0 0 8px 0;
+            font-size: 13px;
+            font-weight: 600;
+            color: #388e3c;
+          }
+
+          .solution-text {
+            margin: 0;
+            color: #424242;
+            font-size: 13px;
+            line-height: 1.4;
+          }
+        }
+      }
+
+      // 卡片底部
+      .card-footer {
+        padding: 12px 16px;
+        background: #f8f9fa;
+        border-top: 1px solid #e9ecef;
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        gap: 8px;
+
+        .action-btn {
+          display: flex;
+          align-items: center;
+          gap: 6px;
+          padding: 8px 16px;
+          border: none;
+          border-radius: 6px;
+          font-size: 13px;
+          font-weight: 500;
+          cursor: pointer;
+          transition: all 0.2s ease;
+          white-space: nowrap;
+
+          .btn-icon {
+            font-size: 14px;
+          }
+
+          &.process-btn {
+            background: #007bff;
+            color: white;
+
+            &:hover {
+              background: #0056b3;
+              transform: translateY(-1px);
+            }
+          }
+
+          &.complete-btn {
+            background: #28a745;
+            color: white;
+
+            &:hover {
+              background: #1e7e34;
+              transform: translateY(-1px);
             }
           }
+
+          &.detail-btn {
+            background: #6c757d;
+            color: white;
+
+            &:hover {
+              background: #545b62;
+              transform: translateY(-1px);
+            }
+          }
+        }
+
+        .completed-status {
+          display: flex;
+          align-items: center;
+          gap: 6px;
+          color: #28a745;
+          font-weight: 600;
+          font-size: 13px;
+
+          .completed-icon {
+            background: #28a745;
+            color: white;
+            width: 18px;
+            height: 18px;
+            border-radius: 50%;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            font-size: 12px;
+          }
         }
       }
     }
 
+    // 空状态样式
     .empty-state {
       text-align: center;
-      padding: ios.$ios-spacing-xl;
-      color: ios.$ios-text-secondary;
+      padding: 60px 20px;
+      color: #6c757d;
 
       .empty-icon {
         font-size: 48px;
-        margin-bottom: ios.$ios-spacing-md;
+        margin-bottom: 16px;
+        opacity: 0.5;
       }
 
       .empty-title {
-        font-size: ios.$ios-font-size-lg;
-        font-weight: ios.$ios-font-weight-semibold;
-        color: ios.$ios-text-primary;
-        margin-bottom: ios.$ios-spacing-sm;
+        font-size: 18px;
+        font-weight: 600;
+        margin-bottom: 8px;
+        color: #495057;
+      }
+
+      .empty-description {
+        font-size: 14px;
+        color: #6c757d;
       }
+    }
+  }
 
-      .empty-desc {
-        font-size: ios.$ios-font-size-sm;
-        color: ios.$ios-text-secondary;
+  // 响应式设计 - 中等屏幕
+  @media (max-width: 1024px) {
+    .complaints-list {
+      .complaints-grid {
+        grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
+        gap: 14px;
       }
     }
   }
 
+  // 响应式设计 - 小屏幕
+  @media (max-width: 768px) {
+    .complaints-list {
+      .complaints-grid {
+        grid-template-columns: 1fr;
+        gap: 12px;
+      }
+
+      .complaint-card-item {
+        .card-header {
+          padding: 10px 12px;
+          flex-direction: column;
+          align-items: flex-start;
+          gap: 6px;
+
+          .header-left {
+            width: 100%;
+          }
+
+          .header-right {
+            width: 100%;
+            justify-content: flex-end;
+          }
+        }
+
+        .card-body {
+          padding: 12px;
+          gap: 10px;
+        }
+
+        .card-footer {
+          padding: 10px 12px;
+          flex-direction: column;
+          gap: 6px;
+
+          .action-btn {
+            width: 100%;
+            justify-content: center;
+          }
+        }
+      }
+    }
+  }
+
+  // 响应式设计 - 超小屏幕
+  @media (max-width: 480px) {
+    .complaints-list {
+      .complaints-grid {
+        gap: 10px;
+      }
+
+      .complaint-card-item {
+        .card-header {
+          padding: 8px 10px;
+        }
+
+        .card-body {
+          padding: 10px;
+          gap: 8px;
+
+          .complaint-images .images-grid {
+            grid-template-columns: repeat(auto-fill, minmax(50px, 1fr));
+            gap: 4px;
+          }
+
+          .complaint-image {
+            height: 50px;
+          }
+        }
+
+        .card-footer {
+          padding: 8px 10px;
+        }
+      }
+    }
+  }
+
+  // 动画效果
+  @keyframes pulse {
+    0% {
+      opacity: 1;
+    }
+    50% {
+      opacity: 0.5;
+    }
+    100% {
+      opacity: 1;
+    }
+  }
+
   // 响应式设计
   @media (max-width: 768px) {
     .stats-grid {

+ 92 - 8
src/app/shared/components/complaint-card/complaint-card.ts

@@ -113,7 +113,13 @@ export class ComplaintCardComponent {
 
     // 状态筛选
     if (this.statusFilter() !== 'all') {
-      filtered = filtered.filter(complaint => complaint.status === this.statusFilter());
+      const statusMap: { [key: string]: string } = {
+        'pending': '待处理',
+        'processing': '处理中',
+        'resolved': '已解决'
+      };
+      const targetStatus = statusMap[this.statusFilter()] || this.statusFilter();
+      filtered = filtered.filter(complaint => complaint.status === targetStatus);
     }
 
     // 优先级筛选
@@ -186,11 +192,61 @@ export class ComplaintCardComponent {
     }
   }
 
-  // 计算处理天数
-  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));
+  // 获取处理天数(修复NaN问题)
+  getDaysInProgress(complaint: ComplaintItem): number {
+    if (!complaint.submittedAt) {
+      return 0;
+    }
+    
+    const submittedDate = new Date(complaint.submittedAt);
+    const currentDate = complaint.resolvedAt ? new Date(complaint.resolvedAt) : new Date();
+    
+    // 检查日期是否有效
+    if (isNaN(submittedDate.getTime()) || isNaN(currentDate.getTime())) {
+      return 0;
+    }
+    
+    const diffTime = Math.abs(currentDate.getTime() - submittedDate.getTime());
+    const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
+    
+    return diffDays;
+  }
+
+  // 开始处理投诉
+  startProcessing(complaint: ComplaintItem): void {
+    // 更新投诉状态为处理中
+    complaint.status = '处理中';
+    
+    // 可以在这里添加更多逻辑,比如分配处理人员等
+    console.log('开始处理投诉:', complaint.id);
+    
+    // 这里可以调用服务来更新后端数据
+    // this.complaintService.updateComplaintStatus(complaint.id, '处理中');
+  }
+
+  // 完成处理投诉
+  completeProcessing(complaint: ComplaintItem): void {
+    // 更新投诉状态为已解决
+    complaint.status = '已解决';
+    complaint.resolvedAt = new Date();
+    
+    // 可以在这里添加处理结果的逻辑
+    console.log('完成处理投诉:', complaint.id);
+    
+    // 这里可以调用服务来更新后端数据
+    // this.complaintService.completeComplaint(complaint.id, resolvedData);
+  }
+
+  // 查看投诉详情
+  viewDetails(complaint: ComplaintItem): void {
+    // 这里可以打开详情弹窗或跳转到详情页面
+    console.log('查看投诉详情:', complaint.id);
+    
+    // 示例:打开详情弹窗
+    // this.dialog.open(ComplaintDetailComponent, {
+    //   data: complaint,
+    //   width: '800px'
+    // });
   }
 
   // 判断是否超时
@@ -206,8 +262,12 @@ export class ComplaintCardComponent {
   }
 
   // 更新筛选条件
-  updateStatusFilter(event: any) {
-    this.statusFilter.set(event.target.value);
+  updateStatusFilter(status: string | any) {
+    if (typeof status === 'string') {
+      this.statusFilter.set(status);
+    } else {
+      this.statusFilter.set(status.target.value);
+    }
   }
 
   updatePriorityFilter(event: any) {
@@ -221,4 +281,28 @@ export class ComplaintCardComponent {
   updateSearchKeyword(keyword: string) {
     this.searchKeyword.set(keyword);
   }
+
+  // 确认客户评价完成
+  confirmCustomerReviewComplete(complaint: ComplaintItem): void {
+    // 更新投诉状态
+    complaint.status = '已解决';
+    complaint.resolvedAt = new Date();
+    
+    console.log('确认客户评价完成:', complaint.id);
+    
+    // 这里可以调用服务来更新后端数据
+    // this.complaintService.confirmCustomerReview(complaint.id);
+  }
+
+  // 确认投诉处理完成
+  confirmComplaintResolutionComplete(complaint: ComplaintItem): void {
+    // 更新投诉状态为已解决
+    complaint.status = '已解决';
+    complaint.resolvedAt = new Date();
+    
+    console.log('确认投诉处理完成:', complaint.id);
+    
+    // 这里可以调用服务来更新后端数据
+    // this.complaintService.confirmResolution(complaint.id);
+  }
 }

+ 34 - 0
src/app/shared/components/customer-review-card/customer-review-card.html

@@ -152,6 +152,40 @@
                   <span class="updated-time">更新于 {{ feedback.updatedAt | date:'yyyy-MM-dd HH:mm' }}</span>
                 }
               </div>
+              
+              <!-- 处理按钮区域 -->
+              <div class="action-buttons">
+                @if (feedback.status === '待处理') {
+                  <button 
+                    class="action-btn process-btn"
+                    (click)="startProcessing(feedback.id)">
+                    开始处理
+                  </button>
+                  <button 
+                    class="action-btn reply-btn"
+                    (click)="openReplyModal(feedback)">
+                    回复客户
+                  </button>
+                } @else if (feedback.status === '处理中') {
+                  <button 
+                    class="action-btn complete-btn"
+                    (click)="markAsResolved(feedback.id)">
+                    标记完成
+                  </button>
+                  <button 
+                    class="action-btn reply-btn"
+                    (click)="openReplyModal(feedback)">
+                    回复客户
+                  </button>
+                } @else {
+                  <button 
+                    class="action-btn view-btn"
+                    (click)="viewDetails(feedback)">
+                    查看详情
+                  </button>
+                }
+              </div>
+              
               @if (feedback.response) {
                 <div class="response-section">
                   <strong>处理回复:</strong>

+ 70 - 1
src/app/shared/components/customer-review-card/customer-review-card.scss

@@ -250,9 +250,78 @@
       margin-bottom: ios.$ios-spacing-sm;
     }
 
-    .empty-description {
+    .empty-desc {
       font-size: ios.$ios-font-size-sm;
       color: ios.$ios-text-secondary;
     }
   }
+
+  // 操作按钮样式
+  .action-buttons {
+    display: flex;
+    gap: ios.$ios-spacing-sm;
+    margin-top: ios.$ios-spacing-md;
+    flex-wrap: wrap;
+
+    .action-btn {
+      padding: ios.$ios-spacing-xs ios.$ios-spacing-sm;
+      border: none;
+      border-radius: vars.$ios-border-radius-sm;
+      font-size: ios.$ios-font-size-sm;
+      font-weight: ios.$ios-font-weight-medium;
+      cursor: pointer;
+      transition: all 0.2s ease;
+      min-width: 80px;
+
+      &.process-btn {
+        background: ios.$ios-primary;
+        color: white;
+
+        &:hover {
+          background: darken(ios.$ios-primary, 10%);
+        }
+      }
+
+      &.reply-btn {
+        background: ios.$ios-success;
+        color: white;
+
+        &:hover {
+          background: darken(ios.$ios-success, 10%);
+        }
+      }
+
+      &.complete-btn {
+        background: ios.$ios-warning;
+        color: white;
+
+        &:hover {
+          background: darken(ios.$ios-warning, 10%);
+        }
+      }
+
+      &.view-btn {
+        background: ios.$ios-color-system-gray-1-light;
+        color: white;
+
+        &:hover {
+          background: darken(ios.$ios-color-system-gray-1-light, 10%);
+        }
+      }
+
+      &:active {
+        transform: translateY(1px);
+      }
+    }
+  }
+
+  // 响应式设计
+  @media (max-width: 768px) {
+    .action-buttons {
+      .action-btn {
+        flex: 1;
+        min-width: auto;
+      }
+    }
+  }
 }

+ 40 - 0
src/app/shared/components/customer-review-card/customer-review-card.ts

@@ -172,4 +172,44 @@ export class CustomerReviewCardComponent {
   updateScoreFilter(event: any): void {
     this.scoreFilter.set(event.target.value);
   }
+
+  // 处理客户评价的方法
+  startProcessing(feedbackId: string): void {
+    console.log('开始处理客户评价:', feedbackId);
+    // 这里可以调用服务更新状态
+    const feedback = this.feedbacks.find(f => f.id === feedbackId);
+    if (feedback) {
+      feedback.status = '处理中';
+      feedback.updatedAt = new Date();
+    }
+  }
+
+  markAsResolved(feedbackId: string): void {
+    console.log('标记客户评价为已解决:', feedbackId);
+    // 这里可以调用服务更新状态
+    const feedback = this.feedbacks.find(f => f.id === feedbackId);
+    if (feedback) {
+      feedback.status = '已解决';
+      feedback.updatedAt = new Date();
+    }
+  }
+
+  openReplyModal(feedback: CustomerFeedback): void {
+    console.log('打开回复模态框:', feedback);
+    // 这里可以打开一个模态框让用户输入回复内容
+    const reply = prompt('请输入回复内容:');
+    if (reply && reply.trim()) {
+      feedback.response = reply.trim();
+      feedback.updatedAt = new Date();
+      if (feedback.status === '待处理') {
+        feedback.status = '处理中';
+      }
+    }
+  }
+
+  viewDetails(feedback: CustomerFeedback): void {
+    console.log('查看客户评价详情:', feedback);
+    // 这里可以打开详情页面或模态框
+    alert(`客户评价详情:\n\n客户: ${feedback.customerName}\n评分: ${feedback.rating}/5\n内容: ${feedback.content}\n回复: ${feedback.response || '暂无回复'}`);
+  }
 }