Browse Source

feet:customer22

徐福静0235668 1 ngày trước cách đây
mục cha
commit
d32a099599

+ 7 - 11
src/app/pages/customer-service/dashboard/pages/after-sales/after-sales.component.html

@@ -287,7 +287,7 @@
       </div>
       
       <!-- 功能模块标签 -->
-      <div class="detail-tabs">
+      <!-- <div class="detail-tabs">
         <button 
           class="tab-button"
           [class.active]="activeDetailTab() === 'payment'"
@@ -309,10 +309,10 @@
           <mat-icon>feedback</mat-icon>
           客户建议收集与处理
         </button>
-      </div>
+      </div> -->
       
       <!-- 尾款管理模块 -->
-      @if (activeDetailTab() === 'payment') {
+      <!-- @if (activeDetailTab() === 'payment') { -->
         <div class="payment-management">
           <div class="module-header">
             <h3>尾款管理</h3>
@@ -395,10 +395,9 @@
             </div>
           }
         </div>
-      }
       
       <!-- 客户评价处理模块 -->
-      @if (activeDetailTab() === 'review') {
+      <!-- @if (activeDetailTab() === 'review') { -->
         <div class="review-management">
           <div class="module-header">
             <h3>客户评价处理</h3>
@@ -593,10 +592,9 @@
             </div>
           }
         </div>
-      }
       
       <!-- 客户建议收集与处理模块 -->
-      @if (activeDetailTab() === 'suggestion') {
+      <!-- @if (activeDetailTab() === 'suggestion') { -->
         <div class="suggestion-management">
           <div class="module-header">
             <h3>客户建议收集与处理</h3>
@@ -768,7 +766,5 @@
             </div>
           }
         </div>
-      }
-    </div>
-  }
-</div>
+      </div>
+    }

+ 200 - 25
src/app/pages/customer-service/dashboard/pages/after-sales/after-sales.component.scss

@@ -599,12 +599,16 @@ $transition: all 0.2s ease;
     justify-content: space-between;
     align-items: center;
     margin-bottom: 20px;
+    flex-wrap: wrap;
+    gap: 12px;
 
     h3 {
       margin: 0;
       color: $text-primary;
       font-size: 18px;
       font-weight: 600;
+      flex: 1;
+      min-width: 120px;
     }
 
     .upload-button,
@@ -620,6 +624,8 @@ $transition: all 0.2s ease;
       cursor: pointer;
       transition: $transition;
       font-size: 14px;
+      white-space: nowrap;
+      flex-shrink: 0;
 
       &:hover:not(:disabled) {
         background-color: $primary-light;
@@ -637,6 +643,23 @@ $transition: all 0.2s ease;
         height: 16px;
       }
     }
+
+    // 响应式处理
+    @media (max-width: 768px) {
+      flex-direction: column;
+      align-items: flex-start;
+      
+      h3 {
+        width: 100%;
+        margin-bottom: 8px;
+      }
+      
+      .upload-button,
+      .add-review-button {
+        width: 100%;
+        justify-content: center;
+      }
+    }
   }
 
   // 尾款管理模块
@@ -762,6 +785,12 @@ $transition: all 0.2s ease;
               &:hover {
                 text-decoration: underline;
               }
+
+              @media (max-width: 768px) {
+                margin-left: 0;
+                margin-top: 8px;
+                display: flex;
+              }
             }
           }
         }
@@ -1094,70 +1123,114 @@ $transition: all 0.2s ease;
       }
 
       .review-record {
-        background: ios.$ios-card-background;
-        border: 1px solid ios.$ios-border;
+        background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
+        border: 1px solid rgba(0, 122, 255, 0.1);
         border-radius: ios.$ios-radius-lg;
         padding: ios.$ios-spacing-lg;
         margin-bottom: ios.$ios-spacing-md;
-        box-shadow: ios.$ios-shadow-sm;
-        transition: all 0.3s ease;
+        box-shadow: 
+          0 4px 20px rgba(0, 0, 0, 0.08),
+          0 1px 4px rgba(0, 0, 0, 0.04);
+        transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
+        position: relative;
+        overflow: hidden;
+
+        &::before {
+          content: '';
+          position: absolute;
+          top: 0;
+          left: 0;
+          right: 0;
+          height: 3px;
+          background: linear-gradient(90deg, #007aff, #5ac8fa);
+          border-radius: ios.$ios-radius-lg ios.$ios-radius-lg 0 0;
+        }
 
         &:hover {
-          transform: translateY(-1px);
-          box-shadow: ios.$ios-shadow-md;
+          transform: translateY(-2px);
+          box-shadow: 
+            0 8px 32px rgba(0, 0, 0, 0.12),
+            0 2px 8px rgba(0, 0, 0, 0.08);
         }
 
         .review-header {
           display: flex;
           align-items: center;
           gap: ios.$ios-spacing-md;
-          margin-bottom: ios.$ios-spacing-sm;
+          margin-bottom: ios.$ios-spacing-md;
+          flex-wrap: wrap;
 
           .review-type,
           .review-channel {
             padding: ios.$ios-spacing-xs ios.$ios-spacing-sm;
-            background-color: rgba(ios.$ios-primary, 0.1);
+            background: linear-gradient(135deg, rgba(0, 122, 255, 0.1) 0%, rgba(90, 200, 250, 0.1) 100%);
             color: ios.$ios-primary;
             border-radius: ios.$ios-radius-md;
             font-size: ios.$ios-font-size-caption-1;
             font-weight: ios.$ios-font-weight-medium;
             font-family: ios.$ios-font-family;
+            border: 1px solid rgba(0, 122, 255, 0.2);
+            flex-shrink: 0;
           }
 
           .review-date {
             color: ios.$ios-text-secondary;
             font-size: ios.$ios-font-size-subhead;
             font-family: ios.$ios-font-family;
+            flex-shrink: 0;
           }
 
           .review-score {
             display: flex;
             gap: 2px;
+            align-items: center;
+            padding: ios.$ios-spacing-xs ios.$ios-spacing-sm;
+            background: rgba(255, 193, 7, 0.1);
+            border-radius: ios.$ios-radius-md;
+            border: 1px solid rgba(255, 193, 7, 0.2);
+            flex-shrink: 0;
 
             mat-icon {
               font-size: 16px;
               width: 16px;
               height: 16px;
-              color: ios.$ios-border;
+              color: #ddd;
+              transition: color 0.2s ease;
 
               &.filled {
-                color: ios.$ios-warning;
+                color: #ffc107;
+                text-shadow: 0 0 4px rgba(255, 193, 7, 0.3);
               }
             }
           }
+
+          @media (max-width: 768px) {
+            flex-direction: column;
+            align-items: flex-start;
+            gap: ios.$ios-spacing-xs;
+          }
         }
 
         .review-content {
-          background: ios.$ios-background-secondary;
+          background: rgba(248, 249, 250, 0.8);
           border-radius: ios.$ios-radius-md;
           padding: ios.$ios-spacing-md;
+          border: 1px solid rgba(0, 122, 255, 0.05);
 
           p {
             margin: ios.$ios-spacing-xs 0;
             color: ios.$ios-text-primary;
             font-size: ios.$ios-font-size-subhead;
             font-family: ios.$ios-font-family;
-            line-height: 1.5;
+            line-height: 1.6;
+
+            &:first-child {
+              margin-top: 0;
+            }
+
+            &:last-child {
+              margin-bottom: 0;
+            }
           }
 
           .review-remark,
@@ -1165,24 +1238,39 @@ $transition: all 0.2s ease;
             color: ios.$ios-text-secondary;
             font-size: ios.$ios-font-size-caption-1;
             font-family: ios.$ios-font-family;
+            padding: ios.$ios-spacing-xs 0;
+            border-top: 1px solid rgba(0, 122, 255, 0.1);
+            margin-top: ios.$ios-spacing-sm;
+
+            strong {
+              color: ios.$ios-primary;
+              font-weight: ios.$ios-font-weight-semibold;
+            }
           }
         }
 
         .follow-up-reminder {
           display: flex;
           align-items: center;
-          gap: 8px;
-          margin-top: 12px;
-          padding: 8px;
-          background-color: rgba($warning-color, 0.1);
-          border-radius: $border-radius;
-          color: $warning-color;
-          font-size: 12px;
+          gap: ios.$ios-spacing-xs;
+          margin-top: ios.$ios-spacing-md;
+          padding: ios.$ios-spacing-sm;
+          background: linear-gradient(135deg, rgba(255, 149, 0, 0.1) 0%, rgba(255, 193, 7, 0.1) 100%);
+          border-radius: ios.$ios-radius-md;
+          border: 1px solid rgba(255, 149, 0, 0.2);
 
           mat-icon {
-            font-size: 16px;
-            width: 16px;
-            height: 16px;
+            color: #ff9500;
+            font-size: 18px;
+            width: 18px;
+            height: 18px;
+          }
+
+          span {
+            color: #ff9500;
+            font-size: ios.$ios-font-size-caption-1;
+            font-weight: ios.$ios-font-weight-medium;
+            font-family: ios.$ios-font-family;
           }
         }
       }
@@ -1324,6 +1412,18 @@ $transition: all 0.2s ease;
 
         .suggestion-actions {
           margin-bottom: ios.$ios-spacing-sm;
+          display: flex;
+          gap: ios.$ios-spacing-xs;
+          flex-wrap: wrap;
+
+          @media (max-width: 768px) {
+            flex-direction: column;
+            
+            .mat-mdc-button {
+              width: 100%;
+              justify-content: center;
+            }
+          }
 
           .mat-mdc-button {
             font-family: ios.$ios-font-family;
@@ -1331,6 +1431,18 @@ $transition: all 0.2s ease;
             padding: ios.$ios-spacing-xs ios.$ios-spacing-md;
             font-weight: ios.$ios-font-weight-medium;
             font-size: ios.$ios-font-size-caption-1;
+            min-width: 100px;
+            white-space: nowrap;
+
+            @media (max-width: 768px) {
+              min-width: unset;
+              padding: ios.$ios-spacing-sm ios.$ios-spacing-md;
+            }
+
+            @media (max-width: 480px) {
+              padding: ios.$ios-spacing-xs ios.$ios-spacing-sm;
+              font-size: ios.$ios-font-size-caption-2;
+            }
 
             &.mat-stroked-button {
               border: 1px solid ios.$ios-border;
@@ -1342,6 +1454,19 @@ $transition: all 0.2s ease;
                 box-shadow: ios.$ios-shadow-sm;
               }
             }
+
+            mat-icon {
+              margin-right: ios.$ios-spacing-xs;
+              font-size: 16px;
+              width: 16px;
+              height: 16px;
+
+              @media (max-width: 480px) {
+                font-size: 14px;
+                width: 14px;
+                height: 14px;
+              }
+            }
           }
         }
 
@@ -1562,12 +1687,43 @@ $transition: all 0.2s ease;
             margin-top: ios.$ios-spacing-md;
             padding-top: ios.$ios-spacing-md;
             border-top: 1px solid ios.$ios-border;
+            flex-wrap: wrap;
+
+            @media (max-width: 768px) {
+              flex-direction: column;
+              gap: ios.$ios-spacing-xs;
+              
+              .mat-mdc-button {
+                width: 100%;
+                justify-content: center;
+              }
+            }
+
+            @media (max-width: 480px) {
+              padding: ios.$ios-spacing-sm;
+              margin-top: ios.$ios-spacing-sm;
+            }
 
             .mat-mdc-button {
               font-family: ios.$ios-font-family;
               border-radius: ios.$ios-radius-md;
               padding: ios.$ios-spacing-sm ios.$ios-spacing-lg;
               font-weight: ios.$ios-font-weight-medium;
+              min-width: 120px;
+              white-space: nowrap;
+              overflow: hidden;
+              text-overflow: ellipsis;
+
+              @media (max-width: 768px) {
+                min-width: unset;
+                padding: ios.$ios-spacing-md ios.$ios-spacing-lg;
+                font-size: ios.$ios-font-size-body;
+              }
+
+              @media (max-width: 480px) {
+                padding: ios.$ios-spacing-sm ios.$ios-spacing-md;
+                font-size: ios.$ios-font-size-caption-1;
+              }
 
               &.mat-primary {
                 background: ios.$ios-primary;
@@ -1578,6 +1734,12 @@ $transition: all 0.2s ease;
                   background: #0056CC;
                   box-shadow: ios.$ios-shadow-sm;
                 }
+
+                &:disabled {
+                  background: ios.$ios-text-tertiary;
+                  color: white;
+                  opacity: 0.6;
+                }
               }
 
               &.mat-stroked-button {
@@ -1594,6 +1756,19 @@ $transition: all 0.2s ease;
               &:active {
                 transform: translateY(1px);
               }
+
+              mat-icon {
+                margin-right: ios.$ios-spacing-xs;
+                font-size: 18px;
+                width: 18px;
+                height: 18px;
+
+                @media (max-width: 480px) {
+                  font-size: 16px;
+                  width: 16px;
+                  height: 16px;
+                }
+              }
             }
           }
         }
@@ -1630,7 +1805,7 @@ $transition: all 0.2s ease;
 
             &::before {
               content: '📋';
-              font-size: 20px;
+              font-size: ios.$ios-font-size-body;
             }
           }
 
@@ -1638,7 +1813,7 @@ $transition: all 0.2s ease;
             display: grid;
             grid-template-columns: auto auto auto 1fr;
             gap: ios.$ios-spacing-md;
-            padding: ios.$ios-spacing-sm 0;
+            padding: ios.$ios-spacing-xs 0;
             font-size: ios.$ios-font-size-caption-1;
             color: ios.$ios-text-secondary;
             font-family: ios.$ios-font-family;
@@ -1762,7 +1937,7 @@ $transition: all 0.2s ease;
     align-items: center;
     
     &:hover {
-      background-color: rgba(0, 122, 255, 0.05);
+      background-color: rgba(0, 122, 255, 0.15);
     }
   }
   

+ 0 - 1
src/app/pages/designer/personal-board/personal-board.ts

@@ -322,7 +322,6 @@ export class PersonalBoard implements OnInit, AfterViewInit {
       '建模': 30,
       '软装': 0,
       '渲染': 50,
-      '后期': 20,
       '尾款结算': 0,
       '客户评价': 0,
       '投诉处理': 0

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

@@ -1,4 +1,4 @@
-<!-- 只展示修改处,未变更部分用占位注释表示 -->
+<!-- 只展示修改处,未变更部分用占位注释表示 -->
 <div class="project-detail-container designer-page">
   <!-- 项目标题栏 -->
   <div class="project-header card">

+ 67 - 21
src/app/pages/designer/project-detail/project-detail.scss

@@ -143,17 +143,21 @@
   .settlement-automation {
     .automation-features {
       display: grid;
-      grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
-      gap: 16px;
+      grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
+      gap: 12px;
       margin-bottom: 20px;
       
       .feature-card {
         background: rgba(52, 199, 89, 0.05);
         border: 1px solid rgba(52, 199, 89, 0.2);
-        border-radius: 12px;
-        padding: 16px;
+        border-radius: 8px;
+        padding: 12px;
         text-align: center;
         transition: all 0.3s ease;
+        min-height: 100px;
+        display: flex;
+        flex-direction: column;
+        justify-content: center;
         
         &:hover {
           background: rgba(52, 199, 89, 0.1);
@@ -161,23 +165,60 @@
         }
         
         .feature-icon {
-          font-size: 24px;
-          margin-bottom: 8px;
+          font-size: 20px;
+          margin-bottom: 6px;
         }
         
         .feature-content {
           h4 {
-            font-size: 14px;
+            font-size: 12px;
             font-weight: 600;
             color: #2c5530;
-            margin: 0 0 4px 0;
+            margin: 0 0 3px 0;
+            line-height: 1.2;
           }
           
           p {
-            font-size: 12px;
+            font-size: 10px;
             color: #666;
             margin: 0;
-            line-height: 1.4;
+            line-height: 1.3;
+          }
+
+          .feature-status {
+            margin-top: 8px;
+
+            .status-badge {
+              display: inline-block;
+              padding: 3px 8px;
+              border-radius: 12px;
+              font-size: 11px;
+              font-weight: 500;
+              
+              &.success {
+                background: #e8f5e8;
+                color: #2e7d32;
+                border: 1px solid #c8e6c9;
+              }
+              
+              &.pending {
+                background: #fff3e0;
+                color: #f57c00;
+                border: 1px solid #ffcc02;
+              }
+              
+              &.ready {
+                background: #e3f2fd;
+                color: #1976d2;
+                border: 1px solid #bbdefb;
+              }
+              
+              &.inactive {
+                background: #f5f5f5;
+                color: #757575;
+                border: 1px solid #e0e0e0;
+              }
+            }
           }
         }
       }
@@ -3515,17 +3556,21 @@
       .panoramic-features,
       .complaint-features {
         display: grid;
-        grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
-        gap: 16px;
-        margin-bottom: 24px;
+        grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
+        gap: 12px;
+        margin-bottom: 20px;
 
         .feature-card {
           background: white;
           border: 1px solid $ios-border;
-          border-radius: 12px;
-          padding: 20px;
+          border-radius: 8px;
+          padding: 16px;
           text-align: center;
           transition: all 0.3s ease;
+          min-height: 120px;
+          display: flex;
+          flex-direction: column;
+          justify-content: center;
 
           &:hover {
             transform: translateY(-4px);
@@ -3533,22 +3578,23 @@
           }
 
           .feature-icon {
-            font-size: 32px;
-            margin-bottom: 12px;
+            font-size: 24px;
+            margin-bottom: 8px;
           }
 
           .feature-content {
             h4 {
-              font-size: 16px;
+              font-size: 14px;
               font-weight: 600;
               color: $ios-text-primary;
-              margin-bottom: 8px;
+              margin-bottom: 6px;
+              line-height: 1.2;
             }
 
             p {
               color: $ios-text-secondary;
-              font-size: 14px;
-              line-height: 1.4;
+              font-size: 12px;
+              line-height: 1.3;
             }
           }
         }

+ 137 - 23
src/app/pages/designer/project-detail/project-detail.ts

@@ -3,6 +3,7 @@ import { CommonModule } from '@angular/common';
 import { ActivatedRoute, Router } from '@angular/router';
 import { FormsModule, ReactiveFormsModule, FormBuilder, FormGroup, Validators } from '@angular/forms';
 import { ProjectService } from '../../../services/project.service';
+import { PaymentVoucherRecognitionService } from '../../../services/payment-voucher-recognition.service';
 import {
   Project,
   RenderProgress,
@@ -221,6 +222,12 @@ export class ProjectDetail implements OnInit, OnDestroy {
   isSettlementCompleted: boolean = false;
   isConfirmingSettlement: boolean = false;
   isSettlementInitiated: boolean = false;
+
+  // 新增:自动化功能状态跟踪
+  miniprogramPaymentStatus: 'active' | 'inactive' = 'inactive';
+  voucherRecognitionCount: number = 0;
+  notificationsSent: number = 0;
+  isAutoSettling: boolean = false;
   isPaymentVerified: boolean = false;
   
   // 客户信息卡片展开/收起状态
@@ -257,7 +264,7 @@ export class ProjectDetail implements OnInit, OnDestroy {
     { key: 'order', label: '订单创建', stages: ['订单创建'] },
     { key: 'requirements', label: '确认需求', stages: ['需求沟通', '方案确认'] },
     { key: 'delivery', label: '交付执行', stages: ['建模', '软装', '渲染'] },
-    { key: 'aftercare', label: '售后', stages: ['尾款结算', '客户评价', '投诉处理'] }
+    { key: 'aftercare', label: '售后', stages: [] }
   ];
   expandedSection: SectionKey | null = null;
   // 渲染异常反馈相关属性
@@ -315,6 +322,7 @@ export class ProjectDetail implements OnInit, OnDestroy {
     private router: Router,
     private fb: FormBuilder,
     private cdr: ChangeDetectorRef,
+    private paymentVoucherService: PaymentVoucherRecognitionService,
   ) {}
 
   // 切换标签页
@@ -3170,21 +3178,109 @@ export class ProjectDetail implements OnInit, OnDestroy {
     const uploadingMessage = `正在上传支付凭证:${file.name}...`;
     console.log(uploadingMessage);
     
-    // 模拟上传API调用
+    // 使用支付凭证识别服务处理上传
+    const settlementId = this.project?.id || 'default_settlement';
+    
+    this.paymentVoucherService.processPaymentVoucherUpload(file, settlementId).subscribe({
+      next: (result) => {
+        if (result.success && result.recognitionResult) {
+          const recognition = result.recognitionResult;
+          
+          // 更新识别计数
+          this.voucherRecognitionCount++;
+          
+          // 显示识别结果
+          const successMessage = `
+            支付凭证识别成功!
+            支付方式:${recognition.paymentMethod}
+            支付金额:¥${recognition.amount}
+            交易号:${recognition.transactionNumber}
+            置信度:${(recognition.confidence * 100).toFixed(1)}%
+          `;
+          
+          alert(successMessage);
+          console.log('支付凭证识别完成', recognition);
+          
+          // 自动标记验证通过并解锁渲染大图
+          this.isPaymentVerified = true;
+          this.renderLargeImages = this.renderLargeImages.map(img => ({ ...img, locked: false }));
+          
+          // 触发自动通知流程
+          this.triggerPaymentCompletedNotification(recognition);
+          
+        } else {
+          const errorMessage = `支付凭证识别失败:${result.error || '未知错误'}`;
+          alert(errorMessage);
+          console.error('支付凭证识别失败', result.error);
+        }
+      },
+      error: (error) => {
+        const errorMessage = `支付凭证处理出错:${error.message || '网络错误'}`;
+        alert(errorMessage);
+        console.error('支付凭证处理出错', error);
+      }
+    });
+  }
+
+  /**
+   * 触发支付完成通知流程
+   */
+  private triggerPaymentCompletedNotification(recognition: any): void {
+    console.log('触发支付完成自动通知流程...');
+    
+    // 模拟调用通知服务发送多渠道通知
+    this.sendMultiChannelNotifications(recognition);
+    
+    // 模拟发送通知
     setTimeout(() => {
-      // 模拟成功上传
-      const successMessage = `支付凭证上传成功:${file.name}`;
-      alert(successMessage);
-      console.log('支付凭证上传完成', {
-        fileName: file.name,
-        fileSize: file.size,
-        fileType: file.type,
-        uploadTime: new Date().toISOString()
-      });
-      // 自动标记验证通过并解锁渲染大图
-      this.isPaymentVerified = true;
-      this.renderLargeImages = this.renderLargeImages.map(img => ({ ...img, locked: false }));
-    }, 2000);
+      const notificationMessage = `
+        🎉 尾款已到账,大图已解锁!
+        
+        支付信息:
+        • 支付方式:${recognition.paymentMethod}
+        • 支付金额:¥${recognition.amount}
+        • 处理时间:${new Date().toLocaleString()}
+        
+        📱 系统已自动发送通知至:
+        • 短信通知:138****8888
+        • 微信通知:已推送至微信
+        • 邮件通知:customer@example.com
+        
+        🖼️ 高清渲染图下载链接已发送
+        您现在可以下载4K高清渲染图了!
+      `;
+      
+      alert(notificationMessage);
+      console.log('自动通知发送完成');
+    }, 1000);
+  }
+
+  /**
+   * 发送多渠道通知
+   */
+  private sendMultiChannelNotifications(recognition: any): void {
+    console.log('开始发送多渠道通知...');
+    
+    // 更新通知发送计数
+    this.notificationsSent++;
+    
+    // 模拟发送短信通知
+    console.log('📱 发送短信通知: 尾款已到账,大图已解锁');
+    
+    // 模拟发送微信通知
+    console.log('💬 发送微信通知: 支付成功,高清图片已准备就绪');
+    
+    // 模拟发送邮件通知
+    console.log('📧 发送邮件通知: 包含下载链接的详细通知');
+    
+    // 模拟发送应用内通知
+    console.log('🔔 发送应用内通知: 实时推送支付状态更新');
+    
+    // 模拟通知发送结果
+    setTimeout(() => {
+      console.log('✅ 所有渠道通知发送完成');
+      console.log(`通知发送统计: 短信✅ 微信✅ 邮件✅ 应用内✅ (总计: ${this.notificationsSent} 次)`);
+    }, 500);
   }
 
   // 获取当前设计师名称
@@ -3285,20 +3381,38 @@ export class ProjectDetail implements OnInit, OnDestroy {
 
   // ==================== 自动结算相关 ====================
   
-  // 自动结算状态
-  isAutoSettling: boolean = false;
-  
   // 启动自动结算
   initiateAutoSettlement(): void {
+    if (this.isAutoSettling) return;
+    
     this.isAutoSettling = true;
-    console.log('启动自动结算流程...');
+    console.log('启动自动结算流程...');
     
-    // 模拟自动结算过程
+    // 模拟启动各个自动化功能
     setTimeout(() => {
+      // 启动小程序支付监听
+      this.miniprogramPaymentStatus = 'active';
+      this.isSettlementInitiated = true;
+      
+      console.log('✅ 自动化结算已启动');
+      console.log('🟢 小程序支付监听已激活');
+      console.log('🔍 支付凭证智能识别已就绪');
+      console.log('📱 自动通知系统已就绪');
+      
       this.isAutoSettling = false;
-      console.log('自动结算完成');
-      alert('自动结算已完成!');
-    }, 3000);
+      
+      // 显示启动成功消息
+      alert(`🚀 自动化结算已成功启动!
+
+✅ 已启动功能:
+• 小程序支付自动监听
+• 支付凭证智能识别  
+• 多渠道自动通知
+• 大图自动解锁
+
+系统将自动处理后续支付流程。`);
+      
+    }, 2000);
   }
 
   // ==================== 全景图合成相关 ====================

+ 202 - 1
src/app/services/auto-settlement.service.ts

@@ -4,6 +4,8 @@ import { map, switchMap, catchError } from 'rxjs/operators';
 import { Settlement } from '../models/project.model';
 import { ProjectService } from './project.service';
 import { PaymentVoucherRecognitionService, PaymentVoucherRecognitionResult } from './payment-voucher-recognition.service';
+import { MiniprogramPaymentService, MiniprogramPaymentResult } from './miniprogram-payment.service';
+import { NotificationService, NotificationType, NotificationChannel } from './notification.service';
 
 export interface AutoSettlementRule {
   id: string;
@@ -41,8 +43,13 @@ export interface SettlementReminder {
 export class AutoSettlementService {
   private scheduledProcesses = new Map<string, any>();
   private paymentRecognitionService = inject(PaymentVoucherRecognitionService);
+  private miniprogramPaymentService = inject(MiniprogramPaymentService);
+  private notificationService = inject(NotificationService);
   
-  constructor(private projectService: ProjectService) {}
+  constructor(private projectService: ProjectService) {
+    // 启动小程序支付自动化监听
+    this.initializeMiniprogramPaymentAutomation();
+  }
   
   private rules = signal<AutoSettlementRule[]>([
     {
@@ -616,4 +623,198 @@ export class AutoSettlementService {
       error: result.error
     };
   }
+
+  /**
+   * 初始化小程序支付自动化流程
+   */
+  private initializeMiniprogramPaymentAutomation(): void {
+    console.log('初始化小程序支付自动化流程...');
+    
+    // 监听小程序支付完成事件
+    this.miniprogramPaymentService.onPaymentCompleted().subscribe({
+      next: (paymentResult: MiniprogramPaymentResult) => {
+        console.log('检测到小程序支付完成:', paymentResult);
+        this.handleMiniprogramPaymentCompleted(paymentResult);
+      },
+      error: (error) => {
+        console.error('小程序支付监听出错:', error);
+      }
+    });
+
+    // 启动小程序支付自动化监听
+    this.miniprogramPaymentService.startAutomationListener();
+  }
+
+  /**
+   * 处理小程序支付完成事件
+   */
+  private handleMiniprogramPaymentCompleted(paymentResult: MiniprogramPaymentResult): void {
+    if (!paymentResult.success) {
+      console.error('支付失败,跳过自动化处理');
+      return;
+    }
+
+    console.log(`开始处理小程序支付自动化流程: ${paymentResult.transactionId}`);
+
+    // 处理支付完成后的自动化流程
+    this.miniprogramPaymentService.processPaymentCompletedFlow(paymentResult).subscribe({
+      next: (success) => {
+        if (success) {
+          console.log('小程序支付自动化流程处理成功');
+          this.createAutomationLog(paymentResult.settlementId, 'miniprogram_payment_success', {
+            transactionId: paymentResult.transactionId,
+            amount: paymentResult.amount,
+            processedAt: new Date()
+          });
+
+          // 发送支付完成通知
+          this.sendPaymentCompletedNotifications(paymentResult);
+          
+        } else {
+          console.error('小程序支付自动化流程处理失败');
+          this.createAutomationLog(paymentResult.settlementId, 'miniprogram_payment_failed', {
+            transactionId: paymentResult.transactionId,
+            error: '自动化流程处理失败'
+          });
+        }
+      },
+      error: (error) => {
+        console.error('小程序支付自动化流程出错:', error);
+        this.createAutomationLog(paymentResult.settlementId, 'miniprogram_payment_error', {
+          transactionId: paymentResult.transactionId,
+          error: error.message
+        });
+      }
+    });
+  }
+
+  /**
+   * 发送支付完成通知
+   */
+  private sendPaymentCompletedNotifications(paymentResult: MiniprogramPaymentResult): void {
+    console.log('发送支付完成通知...');
+
+    // 获取客户信息(模拟)
+    const customerInfo = this.getCustomerInfo(paymentResult.settlementId);
+    
+    // 发送支付完成通知
+    this.notificationService.sendPaymentCompletedNotification({
+      recipient: customerInfo.phone,
+      paymentMethod: '小程序支付', // 使用固定值而不是paymentResult.paymentMethod
+      amount: paymentResult.amount,
+      customerName: customerInfo.name,
+      projectName: customerInfo.projectName,
+      channels: [NotificationChannel.SMS, NotificationChannel.WECHAT, NotificationChannel.IN_APP]
+    }).subscribe({
+      next: (result) => {
+        if (result.success) {
+          console.log('支付完成通知发送成功:', result);
+          
+          // 发送大图解锁通知
+          this.sendImageUnlockedNotifications(paymentResult, customerInfo);
+        } else {
+          console.error('支付完成通知发送失败:', result.error);
+        }
+      },
+      error: (error) => {
+        console.error('支付完成通知发送出错:', error);
+      }
+    });
+  }
+
+  /**
+   * 发送大图解锁通知
+   */
+  private sendImageUnlockedNotifications(paymentResult: MiniprogramPaymentResult, customerInfo: any): void {
+    console.log('发送大图解锁通知...');
+
+    this.notificationService.sendImageUnlockedNotification({
+      recipient: customerInfo.phone,
+      customerName: customerInfo.name,
+      projectName: customerInfo.projectName,
+      imageCount: customerInfo.imageCount || 8,
+      resolution: '4K高清',
+      validUntil: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toLocaleDateString(), // 30天有效期
+      downloadLink: `https://download.yinsanse.com/project/${paymentResult.settlementId}`,
+      channels: [NotificationChannel.SMS, NotificationChannel.EMAIL, NotificationChannel.IN_APP]
+    }).subscribe({
+      next: (result) => {
+        if (result.success) {
+          console.log('大图解锁通知发送成功:', result);
+        } else {
+          console.error('大图解锁通知发送失败:', result.error);
+        }
+      },
+      error: (error) => {
+        console.error('大图解锁通知发送出错:', error);
+      }
+    });
+  }
+
+  /**
+   * 获取客户信息(模拟)
+   */
+  private getCustomerInfo(settlementId: string): {
+    name: string;
+    phone: string;
+    email: string;
+    projectName: string;
+    imageCount: number;
+  } {
+    // 模拟客户信息
+    return {
+      name: '张先生',
+      phone: '138****8888',
+      email: 'customer@example.com',
+      projectName: '现代简约三居室设计',
+      imageCount: 8
+    };
+  }
+
+  /**
+   * 创建自动化日志
+   */
+  private createAutomationLog(settlementId: string, type: string, data: any): void {
+    const log = {
+      id: `log_${Date.now()}`,
+      settlementId,
+      type,
+      data,
+      timestamp: new Date()
+    };
+
+    console.log('创建自动化日志:', log);
+    // 实际实现中会保存到数据库或日志系统
+  }
+
+  /**
+   * 手动触发小程序支付测试流程
+   */
+  triggerMiniprogramPaymentTest(settlementId: string, amount: number): Observable<boolean> {
+    console.log(`触发小程序支付测试流程: ${settlementId}, 金额: ${amount}`);
+    return this.miniprogramPaymentService.triggerTestPaymentFlow(settlementId, amount);
+  }
+
+  /**
+   * 获取小程序支付自动化状态
+   */
+  getMiniprogramPaymentAutomationStatus(): {
+    isProcessing: boolean;
+    processingQueue: string[];
+    supportedMethods: string[];
+  } {
+    return {
+      isProcessing: this.miniprogramPaymentService.getProcessingStatus(),
+      processingQueue: this.miniprogramPaymentService.getProcessingQueue(),
+      supportedMethods: this.miniprogramPaymentService.getSupportedPaymentMethods()
+    };
+  }
+
+  /**
+   * 停止小程序支付自动化监听
+   */
+  stopMiniprogramPaymentAutomation(): void {
+    console.log('停止小程序支付自动化监听');
+    this.miniprogramPaymentService.stopAutomationListener();
+  }
 }

+ 314 - 0
src/app/services/miniprogram-payment.service.ts

@@ -0,0 +1,314 @@
+import { Injectable, signal } from '@angular/core';
+import { Observable, of, delay, switchMap, catchError } from 'rxjs';
+
+export interface MiniprogramPaymentResult {
+  success: boolean;
+  transactionId: string;
+  amount: number;
+  paymentTime: Date;
+  payerInfo: {
+    openId: string;
+    nickname?: string;
+    avatar?: string;
+  };
+  settlementId: string;
+  error?: string;
+}
+
+export interface ImageDecryptionResult {
+  success: boolean;
+  decryptedImageUrl: string;
+  originalImageId: string;
+  decryptionTime: Date;
+  error?: string;
+}
+
+export interface NotificationResult {
+  success: boolean;
+  messageId: string;
+  sentTime: Date;
+  recipient: string;
+  error?: string;
+}
+
+@Injectable({
+  providedIn: 'root'
+})
+export class MiniprogramPaymentService {
+  private isProcessing = signal(false);
+  private paymentQueue = signal<string[]>([]);
+
+  constructor() {}
+
+  /**
+   * 监听小程序支付完成事件
+   */
+  onPaymentCompleted(): Observable<MiniprogramPaymentResult> {
+    // 模拟支付完成事件监听
+    return new Observable(observer => {
+      // 实际实现中会监听微信小程序支付回调
+      console.log('开始监听小程序支付完成事件...');
+      
+      // 模拟支付完成事件
+      setTimeout(() => {
+        const mockPaymentResult: MiniprogramPaymentResult = {
+          success: true,
+          transactionId: `MP${Date.now()}`,
+          amount: Math.floor(Math.random() * 50000) + 10000,
+          paymentTime: new Date(),
+          payerInfo: {
+            openId: `openid_${Date.now()}`,
+            nickname: '客户用户',
+            avatar: 'https://example.com/avatar.jpg'
+          },
+          settlementId: `settlement_${Date.now()}`
+        };
+        
+        observer.next(mockPaymentResult);
+      }, 2000);
+    });
+  }
+
+  /**
+   * 处理支付完成后的自动化流程
+   */
+  processPaymentCompletedFlow(paymentResult: MiniprogramPaymentResult): Observable<boolean> {
+    if (!paymentResult.success) {
+      return of(false);
+    }
+
+    this.isProcessing.set(true);
+    this.paymentQueue.update(queue => [...queue, paymentResult.settlementId]);
+
+    console.log(`开始处理支付完成流程: ${paymentResult.transactionId}`);
+
+    return this.decryptAndSendImages(paymentResult).pipe(
+      switchMap(decryptResult => {
+        if (decryptResult.success) {
+          return this.sendPaymentCompletedNotification(paymentResult, decryptResult);
+        }
+        return of({ success: false, error: '图片解密失败' });
+      }),
+      switchMap(notificationResult => {
+        if (notificationResult.success) {
+          return this.updateSettlementStatus(paymentResult.settlementId, 'completed');
+        }
+        return of(false);
+      }),
+      catchError(error => {
+        console.error('支付完成流程处理失败:', error);
+        return of(false);
+      }),
+      delay(1000), // 模拟处理时间
+      switchMap(result => {
+        this.isProcessing.set(false);
+        this.paymentQueue.update(queue => 
+          queue.filter(id => id !== paymentResult.settlementId)
+        );
+        return of(result);
+      })
+    );
+  }
+
+  /**
+   * 解密并发送大图给客户
+   */
+  private decryptAndSendImages(paymentResult: MiniprogramPaymentResult): Observable<ImageDecryptionResult> {
+    console.log(`开始解密图片,结算ID: ${paymentResult.settlementId}`);
+
+    return new Observable<ImageDecryptionResult>(observer => {
+      // 模拟图片解密过程
+      setTimeout(() => {
+        const decryptResult: ImageDecryptionResult = {
+          success: true,
+          decryptedImageUrl: `https://example.com/decrypted/${paymentResult.settlementId}/high-res-image.jpg`,
+          originalImageId: `img_${paymentResult.settlementId}`,
+          decryptionTime: new Date()
+        };
+
+        console.log('图片解密完成:', decryptResult);
+        observer.next(decryptResult);
+        observer.complete();
+      }, 1500);
+    }).pipe(
+      catchError(error => {
+        console.error('图片解密失败:', error);
+        return of({
+          success: false,
+          decryptedImageUrl: '',
+          originalImageId: '',
+          decryptionTime: new Date(),
+          error: error.message
+        });
+      })
+    );
+  }
+
+  /**
+   * 发送支付完成通知
+   */
+  private sendPaymentCompletedNotification(
+    paymentResult: MiniprogramPaymentResult, 
+    decryptResult: ImageDecryptionResult
+  ): Observable<NotificationResult> {
+    console.log('发送支付完成通知...');
+
+    const notificationContent = {
+      title: '尾款支付成功',
+      message: `您的尾款 ¥${paymentResult.amount} 已成功支付,大图已解锁并发送至您的微信。`,
+      imageUrl: decryptResult.decryptedImageUrl,
+      paymentInfo: {
+        amount: paymentResult.amount,
+        transactionId: paymentResult.transactionId,
+        paymentTime: paymentResult.paymentTime
+      }
+    };
+
+    return new Observable<NotificationResult>(observer => {
+      // 模拟发送通知
+      setTimeout(() => {
+        const notificationResult: NotificationResult = {
+          success: true,
+          messageId: `msg_${Date.now()}`,
+          sentTime: new Date(),
+          recipient: paymentResult.payerInfo.openId
+        };
+
+        console.log('通知发送成功:', notificationResult);
+        console.log('通知内容:', notificationContent);
+        
+        observer.next(notificationResult);
+        observer.complete();
+      }, 800);
+    }).pipe(
+      catchError(error => {
+        console.error('通知发送失败:', error);
+        return of({
+          success: false,
+          messageId: '',
+          sentTime: new Date(),
+          recipient: paymentResult.payerInfo.openId,
+          error: error.message
+        });
+      })
+    );
+  }
+
+  /**
+   * 更新结算状态
+   */
+  private updateSettlementStatus(settlementId: string, status: string): Observable<boolean> {
+    console.log(`更新结算状态: ${settlementId} -> ${status}`);
+
+    return new Observable<boolean>(observer => {
+      // 模拟更新结算状态
+      setTimeout(() => {
+        console.log('结算状态更新成功');
+        observer.next(true);
+        observer.complete();
+      }, 500);
+    }).pipe(
+      catchError(error => {
+        console.error('结算状态更新失败:', error);
+        return of(false);
+      })
+    );
+  }
+
+  /**
+   * 启动自动化监听
+   */
+  startAutomationListener(): void {
+    console.log('启动小程序支付自动化监听...');
+    
+    this.onPaymentCompleted().subscribe(paymentResult => {
+      console.log('检测到支付完成事件:', paymentResult);
+      
+      this.processPaymentCompletedFlow(paymentResult).subscribe(success => {
+        if (success) {
+          console.log('自动化流程处理成功');
+        } else {
+          console.error('自动化流程处理失败');
+        }
+      });
+    });
+  }
+
+  /**
+   * 停止自动化监听
+   */
+  stopAutomationListener(): void {
+    console.log('停止小程序支付自动化监听');
+    this.isProcessing.set(false);
+    this.paymentQueue.set([]);
+  }
+
+  /**
+   * 获取处理状态
+   */
+  getProcessingStatus(): boolean {
+    return this.isProcessing();
+  }
+
+  /**
+   * 获取处理队列
+   */
+  getProcessingQueue(): string[] {
+    return this.paymentQueue();
+  }
+
+  /**
+   * 手动触发支付完成流程(用于测试)
+   */
+  triggerTestPaymentFlow(settlementId: string, amount: number): Observable<boolean> {
+    const mockPaymentResult: MiniprogramPaymentResult = {
+      success: true,
+      transactionId: `TEST_${Date.now()}`,
+      amount: amount,
+      paymentTime: new Date(),
+      payerInfo: {
+        openId: `test_openid_${Date.now()}`,
+        nickname: '测试用户',
+        avatar: 'https://example.com/test-avatar.jpg'
+      },
+      settlementId: settlementId
+    };
+
+    return this.processPaymentCompletedFlow(mockPaymentResult);
+  }
+
+  /**
+   * 获取支持的支付方式
+   */
+  getSupportedPaymentMethods(): string[] {
+    return ['微信小程序支付', '微信H5支付', '微信扫码支付'];
+  }
+
+  /**
+   * 验证支付结果
+   */
+  validatePaymentResult(paymentResult: MiniprogramPaymentResult): { valid: boolean, errors: string[] } {
+    const errors: string[] = [];
+
+    if (!paymentResult.transactionId) {
+      errors.push('缺少交易ID');
+    }
+
+    if (!paymentResult.amount || paymentResult.amount <= 0) {
+      errors.push('支付金额无效');
+    }
+
+    if (!paymentResult.payerInfo?.openId) {
+      errors.push('缺少支付者信息');
+    }
+
+    if (!paymentResult.settlementId) {
+      errors.push('缺少结算ID');
+    }
+
+    return {
+      valid: errors.length === 0,
+      errors
+    };
+  }
+}

+ 426 - 0
src/app/services/notification.service.ts

@@ -0,0 +1,426 @@
+import { Injectable, inject } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { Observable, of, BehaviorSubject } from 'rxjs';
+import { delay, map, catchError } from 'rxjs/operators';
+
+// 通知类型枚举
+export enum NotificationType {
+  PAYMENT_COMPLETED = 'payment_completed',
+  IMAGE_UNLOCKED = 'image_unlocked',
+  SETTLEMENT_CONFIRMED = 'settlement_confirmed',
+  REMINDER = 'reminder',
+  SYSTEM = 'system'
+}
+
+// 通知渠道枚举
+export enum NotificationChannel {
+  SMS = 'sms',
+  EMAIL = 'email',
+  WECHAT = 'wechat',
+  PUSH = 'push',
+  IN_APP = 'in_app'
+}
+
+// 通知接口
+export interface Notification {
+  id: string;
+  type: NotificationType;
+  title: string;
+  content: string;
+  recipient: string;
+  channels: NotificationChannel[];
+  templateData?: any;
+  scheduledAt?: Date;
+  sentAt?: Date;
+  status: 'pending' | 'sent' | 'failed' | 'cancelled';
+  retryCount?: number;
+  metadata?: any;
+}
+
+// 通知模板接口
+export interface NotificationTemplate {
+  id: string;
+  type: NotificationType;
+  name: string;
+  title: string;
+  content: string;
+  channels: NotificationChannel[];
+  variables: string[];
+}
+
+// 通知发送结果接口
+export interface NotificationResult {
+  success: boolean;
+  notificationId: string;
+  sentChannels: NotificationChannel[];
+  failedChannels: NotificationChannel[];
+  error?: string;
+}
+
+@Injectable({
+  providedIn: 'root'
+})
+export class NotificationService {
+  private http = inject(HttpClient);
+  private notifications$ = new BehaviorSubject<Notification[]>([]);
+  
+  // 预定义通知模板
+  private templates: NotificationTemplate[] = [
+    {
+      id: 'payment_completed',
+      type: NotificationType.PAYMENT_COMPLETED,
+      name: '支付完成通知',
+      title: '🎉 尾款已到账,大图已解锁!',
+      content: `
+        亲爱的客户,您好!
+        
+        您的尾款支付已确认到账:
+        • 支付方式:{{paymentMethod}}
+        • 支付金额:¥{{amount}}
+        • 处理时间:{{processedAt}}
+        
+        🎨 高清渲染图已为您解锁,您现在可以:
+        • 下载所有高清渲染图
+        • 查看全景漫游链接
+        • 获取设计方案文件
+        
+        感谢您选择银三色摄影工作室!
+        如有任何问题,请随时联系我们。
+      `,
+      channels: [NotificationChannel.SMS, NotificationChannel.WECHAT, NotificationChannel.IN_APP],
+      variables: ['paymentMethod', 'amount', 'processedAt', 'customerName', 'projectName']
+    },
+    {
+      id: 'image_unlocked',
+      type: NotificationType.IMAGE_UNLOCKED,
+      name: '大图解锁通知',
+      title: '📸 您的高清渲染图已解锁',
+      content: `
+        {{customerName}},您好!
+        
+        项目"{{projectName}}"的高清渲染图已为您解锁:
+        • 解锁图片数量:{{imageCount}}张
+        • 图片分辨率:{{resolution}}
+        • 下载有效期:{{validUntil}}
+        
+        立即下载:{{downloadLink}}
+        
+        银三色摄影工作室
+      `,
+      channels: [NotificationChannel.SMS, NotificationChannel.EMAIL, NotificationChannel.IN_APP],
+      variables: ['customerName', 'projectName', 'imageCount', 'resolution', 'validUntil', 'downloadLink']
+    },
+    {
+      id: 'settlement_reminder',
+      type: NotificationType.REMINDER,
+      name: '结算提醒',
+      title: '💰 尾款结算提醒',
+      content: `
+        {{customerName}},您好!
+        
+        您的项目"{{projectName}}"已完成,请及时结算尾款:
+        • 应付金额:¥{{amount}}
+        • 截止日期:{{dueDate}}
+        
+        支付完成后,我们将立即为您解锁高清渲染图。
+        
+        银三色摄影工作室
+      `,
+      channels: [NotificationChannel.SMS, NotificationChannel.WECHAT],
+      variables: ['customerName', 'projectName', 'amount', 'dueDate']
+    }
+  ];
+
+  /**
+   * 发送支付完成通知
+   */
+  sendPaymentCompletedNotification(data: {
+    recipient: string;
+    paymentMethod: string;
+    amount: number;
+    customerName: string;
+    projectName: string;
+    channels?: NotificationChannel[];
+  }): Observable<NotificationResult> {
+    console.log('发送支付完成通知:', data);
+
+    const template = this.getTemplate(NotificationType.PAYMENT_COMPLETED);
+    if (!template) {
+      return of({
+        success: false,
+        notificationId: '',
+        sentChannels: [],
+        failedChannels: [],
+        error: '未找到通知模板'
+      });
+    }
+
+    const notification: Notification = {
+      id: `notification_${Date.now()}`,
+      type: NotificationType.PAYMENT_COMPLETED,
+      title: template.title,
+      content: this.renderTemplate(template.content, {
+        paymentMethod: data.paymentMethod,
+        amount: data.amount.toString(),
+        processedAt: new Date().toLocaleString(),
+        customerName: data.customerName,
+        projectName: data.projectName
+      }),
+      recipient: data.recipient,
+      channels: data.channels || template.channels,
+      templateData: data,
+      status: 'pending'
+    };
+
+    return this.sendNotification(notification);
+  }
+
+  /**
+   * 发送大图解锁通知
+   */
+  sendImageUnlockedNotification(data: {
+    recipient: string;
+    customerName: string;
+    projectName: string;
+    imageCount: number;
+    resolution: string;
+    validUntil: string;
+    downloadLink: string;
+    channels?: NotificationChannel[];
+  }): Observable<NotificationResult> {
+    console.log('发送大图解锁通知:', data);
+
+    const template = this.getTemplate(NotificationType.IMAGE_UNLOCKED);
+    if (!template) {
+      return of({
+        success: false,
+        notificationId: '',
+        sentChannels: [],
+        failedChannels: [],
+        error: '未找到通知模板'
+      });
+    }
+
+    const notification: Notification = {
+      id: `notification_${Date.now()}`,
+      type: NotificationType.IMAGE_UNLOCKED,
+      title: template.title,
+      content: this.renderTemplate(template.content, data),
+      recipient: data.recipient,
+      channels: data.channels || template.channels,
+      templateData: data,
+      status: 'pending'
+    };
+
+    return this.sendNotification(notification);
+  }
+
+  /**
+   * 发送结算提醒通知
+   */
+  sendSettlementReminderNotification(data: {
+    recipient: string;
+    customerName: string;
+    projectName: string;
+    amount: number;
+    dueDate: string;
+    channels?: NotificationChannel[];
+  }): Observable<NotificationResult> {
+    console.log('发送结算提醒通知:', data);
+
+    const template = this.getTemplate(NotificationType.REMINDER);
+    if (!template) {
+      return of({
+        success: false,
+        notificationId: '',
+        sentChannels: [],
+        failedChannels: [],
+        error: '未找到通知模板'
+      });
+    }
+
+    const notification: Notification = {
+      id: `notification_${Date.now()}`,
+      type: NotificationType.REMINDER,
+      title: template.title,
+      content: this.renderTemplate(template.content, {
+        customerName: data.customerName,
+        projectName: data.projectName,
+        amount: data.amount.toString(),
+        dueDate: data.dueDate
+      }),
+      recipient: data.recipient,
+      channels: data.channels || template.channels,
+      templateData: data,
+      status: 'pending'
+    };
+
+    return this.sendNotification(notification);
+  }
+
+  /**
+   * 发送通知
+   */
+  private sendNotification(notification: Notification): Observable<NotificationResult> {
+    console.log('发送通知:', notification);
+
+    // 模拟发送过程
+    return of(null).pipe(
+      delay(1500), // 模拟网络延迟
+      map(() => {
+        // 模拟发送结果
+        const successRate = 0.95; // 95%成功率
+        const success = Math.random() < successRate;
+
+        if (success) {
+          notification.status = 'sent';
+          notification.sentAt = new Date();
+
+          // 添加到通知历史
+          this.addToNotificationHistory(notification);
+
+          return {
+            success: true,
+            notificationId: notification.id,
+            sentChannels: notification.channels,
+            failedChannels: [],
+          };
+        } else {
+          notification.status = 'failed';
+          notification.retryCount = (notification.retryCount || 0) + 1;
+
+          return {
+            success: false,
+            notificationId: notification.id,
+            sentChannels: [],
+            failedChannels: notification.channels,
+            error: '网络错误或服务暂时不可用'
+          };
+        }
+      }),
+      catchError(error => {
+        console.error('通知发送失败:', error);
+        notification.status = 'failed';
+        
+        return of({
+          success: false,
+          notificationId: notification.id,
+          sentChannels: [],
+          failedChannels: notification.channels,
+          error: error.message || '发送失败'
+        });
+      })
+    );
+  }
+
+  /**
+   * 获取通知模板
+   */
+  private getTemplate(type: NotificationType): NotificationTemplate | undefined {
+    return this.templates.find(t => t.type === type);
+  }
+
+  /**
+   * 渲染模板内容
+   */
+  private renderTemplate(template: string, data: any): string {
+    let rendered = template;
+    
+    Object.keys(data).forEach(key => {
+      const placeholder = `{{${key}}}`;
+      rendered = rendered.replace(new RegExp(placeholder, 'g'), data[key]);
+    });
+
+    return rendered;
+  }
+
+  /**
+   * 添加到通知历史
+   */
+  private addToNotificationHistory(notification: Notification): void {
+    const current = this.notifications$.value;
+    this.notifications$.next([notification, ...current]);
+  }
+
+  /**
+   * 获取通知历史
+   */
+  getNotificationHistory(): Observable<Notification[]> {
+    return this.notifications$.asObservable();
+  }
+
+  /**
+   * 获取通知统计
+   */
+  getNotificationStatistics(): Observable<{
+    total: number;
+    sent: number;
+    failed: number;
+    pending: number;
+    byType: { [key: string]: number };
+    byChannel: { [key: string]: number };
+  }> {
+    return this.notifications$.pipe(
+      map(notifications => {
+        const total = notifications.length;
+        const sent = notifications.filter(n => n.status === 'sent').length;
+        const failed = notifications.filter(n => n.status === 'failed').length;
+        const pending = notifications.filter(n => n.status === 'pending').length;
+
+        const byType: { [key: string]: number } = {};
+        const byChannel: { [key: string]: number } = {};
+
+        notifications.forEach(n => {
+          byType[n.type] = (byType[n.type] || 0) + 1;
+          n.channels.forEach(channel => {
+            byChannel[channel] = (byChannel[channel] || 0) + 1;
+          });
+        });
+
+        return {
+          total,
+          sent,
+          failed,
+          pending,
+          byType,
+          byChannel
+        };
+      })
+    );
+  }
+
+  /**
+   * 重试失败的通知
+   */
+  retryFailedNotification(notificationId: string): Observable<NotificationResult> {
+    const notifications = this.notifications$.value;
+    const notification = notifications.find(n => n.id === notificationId);
+
+    if (!notification || notification.status !== 'failed') {
+      return of({
+        success: false,
+        notificationId,
+        sentChannels: [],
+        failedChannels: [],
+        error: '通知不存在或状态不正确'
+      });
+    }
+
+    notification.status = 'pending';
+    return this.sendNotification(notification);
+  }
+
+  /**
+   * 获取支持的通知渠道
+   */
+  getSupportedChannels(): NotificationChannel[] {
+    return Object.values(NotificationChannel);
+  }
+
+  /**
+   * 获取通知模板列表
+   */
+  getTemplates(): NotificationTemplate[] {
+    return [...this.templates];
+  }
+}

+ 97 - 0
src/app/services/payment-voucher-recognition.service.ts

@@ -310,4 +310,101 @@ export class PaymentVoucherRecognitionService {
 
     return { isValid: true };
   }
+
+  /**
+   * 处理支付凭证上传并自动识别
+   */
+  processPaymentVoucherUpload(file: File, settlementId: string): Observable<{
+    success: boolean;
+    recognitionResult?: PaymentVoucherRecognitionResult;
+    error?: string;
+  }> {
+    console.log(`开始处理支付凭证上传: ${file.name}, 结算ID: ${settlementId}`);
+
+    // 验证文件
+    const validation = this.validateFile(file);
+    if (!validation.isValid) {
+      return of({
+        success: false,
+        error: validation.error
+      });
+    }
+
+    // 执行识别并返回结果
+    return this.recognizePaymentVoucher(file).pipe(
+      map(recognitionResult => {
+        if (recognitionResult.success) {
+          // 自动更新结算状态
+          this.updateSettlementWithRecognitionResult(settlementId, recognitionResult);
+          
+          return {
+            success: true,
+            recognitionResult
+          };
+        } else {
+          return {
+            success: false,
+            error: recognitionResult.error || '识别失败'
+          };
+        }
+      }),
+      catchError(error => {
+        console.error('支付凭证处理出错:', error);
+        return of({
+          success: false,
+          error: '处理过程中出现错误'
+        });
+      })
+    );
+  }
+
+  /**
+   * 根据识别结果更新结算状态
+   */
+  private updateSettlementWithRecognitionResult(settlementId: string, result: PaymentVoucherRecognitionResult): void {
+    console.log(`更新结算状态: ${settlementId}`, result);
+    
+    // 模拟更新结算记录
+    const updateData = {
+      settlementId,
+      paymentMethod: result.paymentMethod,
+      amount: result.amount,
+      transactionNumber: result.transactionNumber,
+      recognizedAt: new Date(),
+      status: 'payment_confirmed'
+    };
+
+    console.log('结算状态更新数据:', updateData);
+    // 实际实现中会调用后端API更新数据库
+  }
+
+  /**
+   * 获取支持的支付方式列表
+   */
+  getSupportedPaymentMethods(): string[] {
+    return ['微信支付', '支付宝', '银行转账', '现金'];
+  }
+
+  /**
+   * 获取识别统计信息
+   */
+  getRecognitionStatistics(): {
+    totalProcessed: number;
+    successRate: number;
+    methodDistribution: { [key: string]: number };
+    averageProcessingTime: number;
+  } {
+    // 模拟统计数据
+    return {
+      totalProcessed: 156,
+      successRate: 0.94,
+      methodDistribution: {
+        '微信支付': 78,
+        '支付宝': 65,
+        '银行转账': 10,
+        '现金': 3
+      },
+      averageProcessingTime: 1.8 // 秒
+    };
+  }
 }

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

@@ -661,6 +661,12 @@ export class ProjectService {
     return of(this.settlements);
   }
 
+  // 新增:创建结算记录(用于自动发起尾款结算申请)
+  addSettlement(settlement: Settlement): Observable<void> {
+    this.settlements.push(settlement);
+    return of(void 0);
+  }
+
   // 获取技能标签
   getSkillTags(): Observable<SkillTag[]> {
     return of(this.skillTags);

+ 5 - 2
src/app/shared/components/complaint-card/complaint-card.scss

@@ -1181,7 +1181,8 @@
         background: #f8f9fa;
         border-top: 1px solid #e9ecef;
         display: flex;
-        justify-content: space-between;
+        flex-wrap: wrap;
+        justify-content: flex-start;
         align-items: center;
         gap: 8px;
 
@@ -1189,7 +1190,7 @@
           display: flex;
           align-items: center;
           gap: 6px;
-          padding: 8px 16px;
+          padding: 6px 12px;
           border: none;
           border-radius: 6px;
           font-size: 13px;
@@ -1197,6 +1198,8 @@
           cursor: pointer;
           transition: all 0.2s ease;
           white-space: nowrap;
+          flex: 0 1 auto;
+          max-width: 100%;
 
           .btn-icon {
             font-size: 14px;

+ 8 - 1
src/app/shared/components/settlement-card/settlement-card.scss

@@ -187,7 +187,7 @@
     .list-body {
       .settlement-item {
         display: grid;
-        grid-template-columns: 2fr 1fr 1fr 2fr 1fr;
+        grid-template-columns: 2fr 1fr 1fr minmax(200px, 2fr) 1fr;
         gap: 16px;
         padding: 16px;
         border: 1px solid $ios-border;
@@ -298,9 +298,11 @@
             flex-direction: column;
             gap: 8px;
             align-items: center;
+            width: 100%;
 
             button {
               min-width: 100px;
+              width: 100%;
               height: 32px;
               font-size: 12px;
               border-radius: 16px;
@@ -487,6 +489,11 @@
           flex-direction: row;
           justify-content: center;
           flex-wrap: wrap;
+
+          button {
+            width: auto;
+            min-width: 100px;
+          }
         }
       }
     }

+ 46 - 1
src/app/shared/components/settlement-card/settlement-card.ts

@@ -9,6 +9,7 @@ import { MatDialogModule, MatDialog } from '@angular/material/dialog';
 import { MatSnackBarModule, MatSnackBar } from '@angular/material/snack-bar';
 import { Settlement } from '../../../models/project.model';
 import { AutoSettlementService } from '../../../services/auto-settlement.service';
+import { ProjectService } from '../../../services/project.service';
 
 export interface SettlementStats {
   totalAmount: number;
@@ -100,7 +101,8 @@ export class SettlementCardComponent implements OnInit {
   constructor(
     private autoSettlementService: AutoSettlementService,
     private dialog: MatDialog,
-    private snackBar: MatSnackBar
+    private snackBar: MatSnackBar,
+    private projectService: ProjectService
   ) { }
 
   ngOnInit() {
@@ -164,6 +166,9 @@ export class SettlementCardComponent implements OnInit {
 
       // 自动创建客服跟进提醒
       this.createCustomerServiceReminder(settlement, 'acceptance_notification');
+
+      // 验收完成后,自动发起尾款结算申请
+      this.initiateFinalSettlement(settlement);
     }, 1500);
   }
 
@@ -420,4 +425,44 @@ export class SettlementCardComponent implements OnInit {
     const diffTime = today.getTime() - thirtyDaysAgo.getTime();
     return Math.max(0, Math.ceil(diffTime / (1000 * 60 * 60 * 24)));
   }
+  
+  // 验收完成后自动发起尾款结算申请
+  private initiateFinalSettlement(contextSettlement: Settlement): void {
+    // 查找当前项目的“尾款结算”记录
+    this.projectService.getSettlements().subscribe(settlements => {
+      const finalSettlement = settlements.find(s => s.projectId === contextSettlement.projectId && s.stage === '尾款结算');
+  
+      if (finalSettlement) {
+        // 标记为已发起(更新时间)并触发自动化处理
+        finalSettlement.createdAt = new Date();
+        this.snackBar.open(`已自动发起尾款结算申请:${finalSettlement.projectName}`, '关闭', {
+          duration: 3000,
+          horizontalPosition: 'center',
+          verticalPosition: 'top'
+        });
+        // 触发自动化处理(提醒/折扣/自动确认等)
+        this.processSettlementAutomation(finalSettlement);
+      } else {
+        // 若不存在尾款结算记录,则创建一条默认记录并发起
+        const newSettlement: Settlement = {
+          id: `s${Date.now()}`,
+          projectId: contextSettlement.projectId,
+          projectName: contextSettlement.projectName,
+          stage: '尾款结算',
+          amount: 0,
+          percentage: 100,
+          status: '待结算',
+          createdAt: new Date()
+        };
+        this.projectService.addSettlement(newSettlement).subscribe(() => {
+          this.snackBar.open(`已创建并自动发起尾款结算申请:${newSettlement.projectName}`, '关闭', {
+            duration: 3000,
+            horizontalPosition: 'center',
+            verticalPosition: 'top'
+          });
+          this.processSettlementAutomation(newSettlement);
+        });
+      }
+    });
+  }
 }