徐福静0235668 4 өдөр өмнө
parent
commit
6fd1e6db35
36 өөрчлөгдсөн 4424 нэмэгдсэн , 752 устгасан
  1. 6 1
      angular.json
  2. 7 0
      package-lock.json
  3. 1 0
      package.json
  4. 11 1
      src/app/app.routes.ts
  5. 96 6
      src/app/pages/customer-service/consultation-order/consultation-order.html
  6. 192 64
      src/app/pages/customer-service/consultation-order/consultation-order.scss
  7. 165 8
      src/app/pages/customer-service/consultation-order/consultation-order.ts
  8. 155 0
      src/app/pages/customer-service/consultation-order/project-group-dialog.component.ts
  9. 4 4
      src/app/pages/customer-service/customer-service-layout/customer-service-layout.scss
  10. 31 0
      src/app/pages/customer-service/customer-service.routes.ts
  11. 174 1
      src/app/pages/customer-service/dashboard/dashboard.html
  12. 29 0
      src/app/pages/customer-service/dashboard/dashboard.routes.ts
  13. 889 436
      src/app/pages/customer-service/dashboard/dashboard.scss
  14. 308 28
      src/app/pages/customer-service/dashboard/dashboard.ts
  15. 42 0
      src/app/pages/customer-service/dashboard/pages/assignment-list/assignment-list.component.html
  16. 148 0
      src/app/pages/customer-service/dashboard/pages/assignment-list/assignment-list.component.scss
  17. 105 0
      src/app/pages/customer-service/dashboard/pages/assignment-list/assignment-list.component.ts
  18. 42 0
      src/app/pages/customer-service/dashboard/pages/consultation-list/consultation-list.component.html
  19. 166 0
      src/app/pages/customer-service/dashboard/pages/consultation-list/consultation-list.component.scss
  20. 90 0
      src/app/pages/customer-service/dashboard/pages/consultation-list/consultation-list.component.ts
  21. 38 0
      src/app/pages/customer-service/dashboard/pages/exception-list/exception-list.component.html
  22. 158 0
      src/app/pages/customer-service/dashboard/pages/exception-list/exception-list.component.scss
  23. 110 0
      src/app/pages/customer-service/dashboard/pages/exception-list/exception-list.component.ts
  24. 56 0
      src/app/pages/customer-service/dashboard/pages/revenue-detail/revenue-detail.component.html
  25. 161 0
      src/app/pages/customer-service/dashboard/pages/revenue-detail/revenue-detail.component.scss
  26. 120 0
      src/app/pages/customer-service/dashboard/pages/revenue-detail/revenue-detail.component.ts
  27. 170 0
      src/app/pages/customer-service/project-detail/complaint-warning-dialog.ts
  28. 162 0
      src/app/pages/customer-service/project-detail/modification-request-dialog.ts
  29. 121 136
      src/app/pages/customer-service/project-detail/project-detail.html
  30. 201 23
      src/app/pages/customer-service/project-detail/project-detail.scss
  31. 197 16
      src/app/pages/customer-service/project-detail/project-detail.ts
  32. 185 0
      src/app/pages/customer-service/project-detail/refund-request-dialog.ts
  33. 42 26
      src/app/pages/customer-service/project-list/project-list.scss
  34. 1 1
      src/app/pages/customer-service/project-list/project-list.ts
  35. 34 0
      src/app/services/project.service.ts
  36. 7 1
      src/styles.scss

+ 6 - 1
angular.json

@@ -50,7 +50,12 @@
                   "maximumError": "100kB"
                 }
               ],
-              "outputHashing": "all"
+              "outputHashing": "all",
+              "optimization": {
+                "fonts": {
+                  "inline": false
+                }
+              }
             },
             "development": {
               "optimization": false,

+ 7 - 0
package-lock.json

@@ -18,6 +18,7 @@
         "@angular/router": "^20.1.0",
         "chart.js": "^4.5.0",
         "echarts": "^6.0.0",
+        "roboto-fontface": "^0.10.0",
         "rxjs": "~7.8.0",
         "tslib": "^2.3.0",
         "zone.js": "~0.15.0"
@@ -10260,6 +10261,12 @@
         "url": "https://github.com/sponsors/isaacs"
       }
     },
+    "node_modules/roboto-fontface": {
+      "version": "0.10.0",
+      "resolved": "https://registry.npmjs.org/roboto-fontface/-/roboto-fontface-0.10.0.tgz",
+      "integrity": "sha512-OlwfYEgA2RdboZohpldlvJ1xngOins5d7ejqnIBWr9KaMxsnBqotpptRXTyfNRLnFpqzX6sTDt+X+a+6udnU8g==",
+      "license": "Apache-2.0"
+    },
     "node_modules/rolldown": {
       "version": "1.0.0-beta.32",
       "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-beta.32.tgz",

+ 1 - 0
package.json

@@ -31,6 +31,7 @@
     "@angular/router": "^20.1.0",
     "chart.js": "^4.5.0",
     "echarts": "^6.0.0",
+    "roboto-fontface": "^0.10.0",
     "rxjs": "~7.8.0",
     "tslib": "^2.3.0",
     "zone.js": "~0.15.0"

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

@@ -8,6 +8,11 @@ import { ConsultationOrder } from './pages/customer-service/consultation-order/c
 import { ProjectList } from './pages/customer-service/project-list/project-list';
 import { ProjectDetail } from './pages/customer-service/project-detail/project-detail';
 import { CaseLibrary } from './pages/customer-service/case-library/case-library';
+// 客服工作台子页面
+import { ConsultationListComponent } from './pages/customer-service/dashboard/pages/consultation-list/consultation-list.component';
+import { AssignmentListComponent } from './pages/customer-service/dashboard/pages/assignment-list/assignment-list.component';
+import { ExceptionListComponent } from './pages/customer-service/dashboard/pages/exception-list/exception-list.component';
+import { RevenueDetailComponent } from './pages/customer-service/dashboard/pages/revenue-detail/revenue-detail.component';
 
 // 设计师页面
 import { Dashboard as DesignerDashboard } from './pages/designer/dashboard/dashboard';
@@ -53,7 +58,12 @@ export const routes: Routes = [
       { path: 'consultation-order', component: ConsultationOrder, title: '客户咨询与下单' },
       { path: 'project-list', component: ProjectList, title: '项目列表' },
       { path: 'project-detail/:id', component: ProjectDetail, title: '项目详情' },
-      { path: 'case-library', component: CaseLibrary, title: '案例库' }
+      { path: 'case-library', component: CaseLibrary, title: '案例库' },
+      // 工作台子页面路由
+      { path: 'consultation-list', component: ConsultationListComponent, title: '咨询列表' },
+      { path: 'assignment-list', component: AssignmentListComponent, title: '待派单列表' },
+      { path: 'exception-list', component: ExceptionListComponent, title: '异常项目列表' },
+      { path: 'revenue-detail', component: RevenueDetailComponent, title: '今日成交详情' }
     ]
   },
 

+ 96 - 6
src/app/pages/customer-service/consultation-order/consultation-order.html

@@ -1,7 +1,12 @@
 <div class="consultation-order-container">
   <!-- 页面标题 -->
   <div class="page-header">
-    <h1>客户咨询与下单</h1>
+    <h1>
+      <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+        <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"></path>
+      </svg>
+      客户咨询与下单
+    </h1>
     <p>记录客户需求,快速生成报价,一键创建项目</p>
   </div>
 
@@ -17,10 +22,18 @@
   <!-- 客户信息区域 -->
   <section class="customer-info-section">
     <div class="section-header">
-      <h2>客户信息</h2>
-      <button *ngIf="selectedCustomer()" class="clear-customer-btn" (click)="clearSelectedCustomer()">
-        清除客户信息
-      </button>
+      <h2>
+        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+          <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
+          <circle cx="12" cy="7" r="4"></circle>
+        </svg>
+        客户信息
+      </h2>
+      <div class="section-actions">
+        <button *ngIf="selectedCustomer()" class="clear-customer-btn" (click)="clearSelectedCustomer()">
+          清除客户信息
+        </button>
+      </div>
     </div>
     
     <!-- 客户搜索 -->
@@ -87,7 +100,64 @@
           <label for="source">来源渠道</label>
           <input type="text" id="source" formControlName="source" placeholder="如:官网咨询、推荐介绍等">
         </div>
+        <div class="form-group">
+          <label for="demandType">需求类型</label>
+          <select id="demandType" formControlName="demandType">
+            <option value="">请选择需求类型</option>
+            <option *ngFor="let type of demandTypes" [value]="type.value">{{ type.label }}</option>
+          </select>
+        </div>
+      </div>
+      <div class="form-row">
+        <div class="form-group">
+          <label for="followUpStatus">跟进状态</label>
+          <select id="followUpStatus" formControlName="followUpStatus">
+            <option value="">请选择跟进状态</option>
+            <option *ngFor="let status of followUpStatus" [value]="status.value">{{ status.label }}</option>
+          </select>
+        </div>
       </div>
+      
+      <!-- 偏好标签 -->
+      <div class="form-group full-width">
+        <label>偏好标签</label>
+        <div class="tags-container">
+          <mat-chip-grid #chipList aria-label="偏好标签">
+              <mat-chip
+                *ngFor="let tag of preferenceTags"
+                removable
+                (removed)="removePreferenceTag(tag)"
+                class="preference-tag"
+              >
+                {{ tag }}
+                <mat-icon matChipRemove>cancel</mat-icon>
+              </mat-chip>
+              <input
+                placeholder="添加自定义标签..."
+                [matChipInputFor]="chipList"
+                [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
+                [matChipInputAddOnBlur]="addOnBlur"
+                (matChipInputTokenEnd)="addPreferenceTag($event)"
+                class="tag-input"
+              />
+            </mat-chip-grid>
+          
+          <!-- 预设标签选项 -->
+          <div class="preset-tags">
+            <span class="preset-label">快速添加:</span>
+            <button
+              *ngFor="let tag of preferenceTagOptions"
+              type="button"
+              class="preset-tag-btn"
+              (click)="addFromPreset(tag)"
+              [class.active]="preferenceTags.includes(tag)"
+            >
+              {{ tag }}
+            </button>
+          </div>
+        </div>
+      </div>
+      
       <div class="form-group full-width">
         <label for="remark">备注</label>
         <textarea id="remark" formControlName="remark" rows="2" placeholder="请输入其他备注信息"></textarea>
@@ -98,7 +168,16 @@
   <!-- 需求信息区域 -->
   <section class="requirement-section">
     <div class="section-header">
-      <h2>需求信息</h2>
+      <h2>
+        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+          <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
+          <polyline points="14 2 14 8 20 8"></polyline>
+          <line x1="16" y1="13" x2="8" y2="13"></line>
+          <line x1="16" y1="17" x2="8" y2="17"></line>
+          <polyline points="10 9 9 9 8 9"></polyline>
+        </svg>
+        需求信息
+      </h2>
     </div>
     
     <form [formGroup]="requirementForm" class="requirement-form">
@@ -161,6 +240,17 @@
 
   <!-- 报价与案例匹配区域 -->
   <section class="price-match-section">
+    <div class="section-header">
+      <h2>
+        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+          <rect x="2" y="6" width="20" height="12" rx="2"></rect>
+          <path d="M12 6v4"></path>
+          <path d="M2 10h20"></path>
+        </svg>
+        报价与案例匹配
+      </h2>
+    </div>
+    
     <div class="price-info">
       <div class="price-label">预估报价范围</div>
       <div class="price-value">{{ estimatedPriceRange() || '请填写需求信息以获取报价' }}</div>

+ 192 - 64
src/app/pages/customer-service/consultation-order/consultation-order.scss

@@ -1,44 +1,68 @@
-// 全局变量定义
-$primary-color: #165DFF;
-$primary-dark: #0E42CB;
-$secondary-color: #4E5BA6;
-$success-color: #00B42A;
-$warning-color: #FF7D00;
-$danger-color: #F53F3F;
-$text-primary: #1D2129;
-$text-secondary: #4E5969;
-$text-tertiary: #86909C;
-$border-color: #E5E6EB;
+// 全局变量定义 - 财务风格配色
+$primary-color: #1A73E8;
+$primary-dark: #1557B0;
+$secondary-color: #34A853;
+$success-color: #34A853;
+$warning-color: #FBBC05;
+$danger-color: #EA4335;
+$text-primary: #202124;
+$text-secondary: #5F6368;
+$text-tertiary: #80868B;
+$border-color: #DADCE0;
 $background-primary: #FFFFFF;
-$background-secondary: #F2F3F5;
-$background-tertiary: #F7F8FA;
-$shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
-$shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08);
-$shadow-lg: 0 10px 30px rgba(0, 0, 0, 0.1);
+$background-secondary: #F8F9FA;
+$background-tertiary: #F1F3F4;
+$shadow-sm: 0 1px 2px rgba(60,64,67,0.1);
+$shadow-md: 0 2px 8px rgba(60,64,67,0.15);
+$shadow-lg: 0 4px 12px rgba(60,64,67,0.2);
 $border-radius: 8px;
-$transition: all 0.3s ease;
+$transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+$main-font-family: 'Google Sans', 'PingFang SC', 'Microsoft YaHei', sans-serif;
+$heading-font-family: 'Google Sans Display', 'Segoe UI', Roboto, sans-serif;
+$grid-gap: 24px;
+$card-padding: 24px;
 
-// 主容器
+// 主容器 - 财务风格布局
 .consultation-order-container {
-  max-width: 1200px;
+  max-width: 1440px;
   margin: 0 auto;
-  padding: 24px;
+  padding: 32px;
   color: $text-primary;
-  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+  font-family: $main-font-family;
+  font-size: 14px;
+  line-height: 1.5;
+  min-height: 100vh;
+  display: grid;
+  grid-template-columns: 1fr;
+  gap: $grid-gap;
+  
+  @media (min-width: 1024px) {
+    grid-template-columns: 300px 1fr;
+  }
 }
 
-// 页面标题
+// 页面标题 - 财务风格
 .page-header {
-  margin-bottom: 32px;
+  grid-column: 1 / -1;
+  margin-bottom: $grid-gap;
+  padding-bottom: 16px;
+  border-bottom: 1px solid $border-color;
+  
   h1 {
-    font-size: 28px;
-    font-weight: 600;
-    margin-bottom: 8px;
+    font-size: 24px;
+    font-weight: 500;
+    margin: 0 0 8px 0;
     color: $text-primary;
+    font-family: $heading-font-family;
+    display: flex;
+    align-items: center;
+    gap: 12px;
   }
+  
   p {
     font-size: 14px;
     color: $text-secondary;
+    margin: 0;
   }
 }
 
@@ -67,26 +91,52 @@ $transition: all 0.3s ease;
   }
 }
 
-// 通用区域样式
+// 通用区域样式 - 财务风格
 .section-header {
   display: flex;
   justify-content: space-between;
   align-items: center;
-  margin-bottom: 20px;
+  margin-bottom: 16px;
+  
   h2 {
-    font-size: 20px;
-    font-weight: 600;
+    font-size: 18px;
+    font-weight: 500;
     color: $text-primary;
+    font-family: $heading-font-family;
+    margin: 0;
+    display: flex;
+    align-items: center;
+    gap: 8px;
+  }
+  
+  .section-actions {
+    display: flex;
+    gap: 8px;
   }
 }
 
-// 客户信息区域
-.customer-info-section {
+// 卡片基础样式
+.card {
   background-color: $background-primary;
   border-radius: $border-radius;
-  padding: 24px;
+  padding: $card-padding;
   box-shadow: $shadow-sm;
-  margin-bottom: 24px;
+  margin-bottom: $grid-gap;
+  transition: $transition;
+  
+  &:hover {
+    box-shadow: $shadow-md;
+  }
+  
+  &.highlight {
+    border-left: 4px solid $primary-color;
+  }
+}
+
+// 客户信息区域
+.customer-info-section {
+  @extend .card;
+  @extend .highlight;
 }
 
 // 客户搜索
@@ -202,13 +252,85 @@ $transition: all 0.3s ease;
   color: $secondary-color;
 }
 
+// 偏好标签样式
+.tags-container {
+  margin-top: 12px;
+}
+
+.preference-tag {
+  background-color: color-mix(in srgb, $primary-color 15%, transparent);
+  color: $primary-color;
+  font-size: 16px;
+  padding: 8px 16px;
+  margin-right: 12px;
+  margin-bottom: 12px;
+  font-weight: 500;
+  border-radius: 24px;
+  transition: all 0.3s ease;
+  &:hover {
+    background-color: color-mix(in srgb, $primary-color 30%, transparent);
+  }
+}
+
+.tag-input {
+  font-size: 16px;
+  padding: 8px;
+  margin-top: 8px;
+  width: 100%;
+  border: 2px solid $border-color;
+  border-radius: 8px;
+  transition: border-color 0.3s ease;
+  &:focus {
+    outline: none;
+    border-color: $primary-color;
+  }
+}
+
+// 预设标签样式
+.preset-tags {
+  margin-top: 16px;
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  gap: 12px;
+}
+
+.preset-label {
+  font-size: 16px;
+  font-weight: 600;
+  color: $text-primary;
+  min-width: 100px;
+}
+
+.preset-tag-btn {
+  padding: 8px 16px;
+  background-color: $background-tertiary;
+  color: $text-secondary;
+  border: 2px solid $border-color;
+  border-radius: 24px;
+  font-size: 14px;
+  cursor: pointer;
+  transition: all 0.3s ease;
+  &:hover {
+    background-color: color-mix(in srgb, $primary-color 10%, transparent);
+    color: $primary-color;
+    border-color: $primary-color;
+    transform: translateY(-2px);
+  }
+  &.active {
+    background-color: $primary-color;
+    color: white;
+    border-color: $primary-color;
+  }
+}
+
 // 表单样式
 .customer-form,
 .requirement-form {
   .form-row {
     display: flex;
-    gap: 16px;
-    margin-bottom: 16px;
+    gap: 24px; // 增加行内表单元素间距防止重合
+    margin-bottom: 24px;
     flex-wrap: wrap;
   }
   
@@ -217,19 +339,19 @@ $transition: all 0.3s ease;
     min-width: 200px;
     label {
       display: block;
-      font-size: 14px;
+      font-size: 18px; // 放大表单标签
       font-weight: 500;
       color: $text-primary;
-      margin-bottom: 6px;
+      margin-bottom: 8px;
     }
     input,
     select,
     textarea {
       width: 100%;
-      padding: 10px 12px;
+      padding: 12px 16px; // 增加内边距
       border: 1px solid $border-color;
       border-radius: $border-radius;
-      font-size: 14px;
+      font-size: 16px; // 放大输入元素字体
       transition: $transition;
       &:focus {
         outline: none;
@@ -239,7 +361,8 @@ $transition: all 0.3s ease;
     }
     textarea {
       resize: vertical;
-      min-height: 60px;
+      min-height: 80px;
+      line-height: 1.6;
     }
   }
   
@@ -271,20 +394,12 @@ $transition: all 0.3s ease;
 
 // 需求信息区域
 .requirement-section {
-  background-color: $background-primary;
-  border-radius: $border-radius;
-  padding: 24px;
-  box-shadow: $shadow-sm;
-  margin-bottom: 24px;
+  @extend .card;
 }
 
 // 报价与案例匹配区域
 .price-match-section {
-  background-color: $background-primary;
-  border-radius: $border-radius;
-  padding: 24px;
-  box-shadow: $shadow-sm;
-  margin-bottom: 24px;
+  @extend .card;
 }
 
 // 价格信息
@@ -292,21 +407,24 @@ $transition: all 0.3s ease;
   display: flex;
   justify-content: space-between;
   align-items: center;
-  padding: 16px 20px;
+  padding: 20px 24px;
   background-color: color-mix(in srgb, $primary-color 5%, transparent);
   border-radius: $border-radius;
   margin-bottom: 24px;
+  box-shadow: $shadow-sm;
 }
 
 .price-label {
-  font-size: 14px;
+  font-size: 18px; // 放大价格标签
   color: $text-secondary;
+  font-weight: 500;
 }
 
 .price-value {
-  font-size: 20px;
-  font-weight: 600;
+  font-size: 24px; // 放大价格值
+  font-weight: 700;
   color: $primary-color;
+  letter-spacing: 0.5px;
 }
 
 // 匹配案例
@@ -418,26 +536,36 @@ $transition: all 0.3s ease;
   }
 }
 
-// 操作按钮区域
+// 操作按钮区域 - 财务风格
 .action-section {
+  @extend .card;
   display: flex;
-  gap: 12px;
+  gap: 16px;
   justify-content: flex-end;
+  padding: 16px $card-padding;
+  background-color: $background-tertiary;
+  margin-top: $grid-gap;
+  position: sticky;
+  bottom: 32px;
+  z-index: 10;
 }
 
 .primary-btn,
 .secondary-btn {
-  padding: 12px 24px;
+  padding: 16px 32px; // 增大按钮尺寸
   border-radius: $border-radius;
-  font-size: 14px;
-  font-weight: 500;
+  font-size: 18px; // 放大按钮字体
+  font-weight: 600;
   cursor: pointer;
   transition: $transition;
   border: none;
   display: inline-flex;
   align-items: center;
   justify-content: center;
-  gap: 6px;
+  gap: 8px;
+  min-width: 160px;
+  text-transform: none;
+  letter-spacing: 0.5px;
 }
 
 .primary-btn {
@@ -445,7 +573,7 @@ $transition: all 0.3s ease;
   color: white;
   &:hover:not(:disabled) {
     background-color: $primary-dark;
-    transform: translateY(-1px);
+    transform: translateY(-2px);
     box-shadow: $shadow-md;
   }
   &:active:not(:disabled) {
@@ -460,12 +588,12 @@ $transition: all 0.3s ease;
 .secondary-btn {
   background-color: $background-tertiary;
   color: $text-primary;
-  border: 1px solid $border-color;
+  border: 2px solid $border-color;
   &:hover:not(:disabled) {
     background-color: $background-secondary;
     border-color: $primary-color;
     color: $primary-color;
-    transform: translateY(-1px);
+    transform: translateY(-2px);
     box-shadow: $shadow-md;
   }
   &:active:not(:disabled) {

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

@@ -1,8 +1,15 @@
-import { Component, signal } from '@angular/core';
+import { Component, signal, Inject } from '@angular/core';
 import { CommonModule } from '@angular/common';
 import { FormsModule, ReactiveFormsModule, FormBuilder, FormGroup, Validators } from '@angular/forms';
 import { RouterModule } from '@angular/router';
 import { ProjectService } from '../../../services/project.service';
+import { MatChipInputEvent } from '@angular/material/chips';
+import { COMMA, ENTER } from '@angular/cdk/keycodes';
+import { MatChipsModule } from '@angular/material/chips';
+import { MatIconModule } from '@angular/material/icon';
+import { MatSnackBar } from '@angular/material/snack-bar';
+import { MatDialog, MAT_DIALOG_DATA } from '@angular/material/dialog';
+import { ProjectGroupDialog } from './project-group-dialog.component';
 
 // 定义客户信息接口
 interface Customer {
@@ -14,6 +21,10 @@ interface Customer {
   customerType?: string; // 新客户/老客户/VIP客户
   source?: string; // 来源渠道
   remark?: string;
+  // 客户标签信息
+  demandType?: string;
+  preferenceTags?: string[];
+  followUpStatus?: string;
 }
 
 // 定义需求信息接口
@@ -29,10 +40,35 @@ interface Requirement {
   referenceCases?: string[];
 }
 
+// 标签选项定义
+const DEMAND_TYPES = [
+  { value: 'price', label: '价格敏感' },
+  { value: 'quality', label: '质量敏感' },
+  { value: 'comprehensive', label: '综合要求' }
+];
+
+const FOLLOW_UP_STATUS = [
+  { value: 'quotation', label: '待报价' },
+  { value: 'confirm', label: '待确认需求' },
+  { value: 'lost', label: '已失联' }
+];
+
+// 预设的偏好标签选项
+const PREFERENCE_TAG_OPTIONS = [
+  // 颜色偏好
+  '柔和色系', '明亮色系', '深色系', '中性色系',
+  // 材质偏好
+  '环保材料', '实木', '大理石', '瓷砖', '地板', '墙纸',
+  // 风格偏好
+  '现代简约', '北欧风格', '中式风格', '美式风格', '工业风',
+  // 其他偏好
+  '智能家电', '收纳空间', '开放式厨房', '大窗户'
+];
+
 @Component({
   selector: 'app-consultation-order',
   standalone: true,
-  imports: [CommonModule, FormsModule, ReactiveFormsModule, RouterModule],
+  imports: [CommonModule, FormsModule, ReactiveFormsModule, RouterModule, MatChipsModule, MatIconModule],
   templateUrl: './consultation-order.html',
   styleUrls: ['./consultation-order.scss', '../customer-service-styles.scss']
 })
@@ -72,9 +108,19 @@ export class ConsultationOrder {
     '全包', '半包', '清包', '旧房翻新', '局部改造'
   ];
 
+  // 标签系统
+  demandTypes = DEMAND_TYPES;
+  followUpStatus = FOLLOW_UP_STATUS;
+  preferenceTagOptions = PREFERENCE_TAG_OPTIONS;
+  addOnBlur = true;
+  readonly separatorKeysCodes = [ENTER, COMMA] as const;
+  preferenceTags: string[] = [];
+
   constructor(
     private fb: FormBuilder,
-    private projectService: ProjectService
+    private projectService: ProjectService,
+    private snackBar: MatSnackBar,
+    private dialog: MatDialog
   ) {
     // 初始化需求表单
     this.requirementForm = this.fb.group({
@@ -96,7 +142,9 @@ export class ConsultationOrder {
       wechat: [''],
       customerType: ['新客户'],
       source: [''],
-      remark: ['']
+      remark: [''],
+      demandType: [''],
+      followUpStatus: ['']
     });
 
     // 监听表单值变化,自动计算报价和匹配案例
@@ -106,6 +154,36 @@ export class ConsultationOrder {
     });
   }
 
+  // 添加偏好标签
+  addPreferenceTag(event: MatChipInputEvent): void {
+    const value = (event.value || '').trim();
+
+    // 添加标签,如果它不是空的,并且不在已有标签中
+    if (value && !this.preferenceTags.includes(value)) {
+      this.preferenceTags.push(value);
+    }
+
+    // 清空输入框
+    if (event.input) {
+      event.input.value = '';
+    }
+  }
+
+  // 删除偏好标签
+  removePreferenceTag(tag: string): void {
+    const index = this.preferenceTags.indexOf(tag);
+    if (index >= 0) {
+      this.preferenceTags.splice(index, 1);
+    }
+  }
+
+  // 从预设选项中添加标签
+  addFromPreset(tag: string): void {
+    if (!this.preferenceTags.includes(tag)) {
+      this.preferenceTags.push(tag);
+    }
+  }
+
   // 搜索客户
   searchCustomer() {
     if (this.searchKeyword().length >= 2) {
@@ -265,10 +343,89 @@ export class ConsultationOrder {
     }
   }
 
-  // 一键拉群
+  // 创建项目
+  createProject() {
+    if (!this.selectedCustomer()) {
+      this.snackBar.open('请先选择客户', '关闭', { duration: 3000 });
+      return;
+    }
+
+    if (this.requirementForm.invalid) {
+      this.snackBar.open('请完善需求信息', '关闭', { duration: 3000 });
+      return;
+    }
+
+    const selectedCustomer = this.selectedCustomer()!;
+    const projectData = {
+      customerId: selectedCustomer.id,
+      customerName: selectedCustomer.name,
+      requirement: this.requirementForm.value,
+      referenceCases: this.requirementForm.get('referenceCases')?.value || [],
+      tags: {
+        demandType: this.customerForm.get('demandType')?.value,
+        preferenceTags: this.preferenceTags,
+        followUpStatus: this.customerForm.get('followUpStatus')?.value
+      }
+    };
+
+    this.projectService.createProject(projectData).subscribe(
+      (response: any) => {
+        this.snackBar.open('项目创建成功', '关闭', { duration: 3000 });
+      },
+      (error: any) => {
+        this.snackBar.open('项目创建失败,请重试', '关闭', { duration: 3000 });
+      }
+    );
+  }
+
+  /**
+   * 创建项目群
+   */
   createProjectGroup() {
-    // 模拟拉群功能
-    console.log('创建项目群');
-    alert('项目群已创建,并邀请了相应技术组长!');
+    // 先检查是否选择了客户
+    if (!this.selectedCustomer()) {
+      this.snackBar.open('请先选择客户', '确定', { duration: 2000 });
+      return;
+    }
+    
+    // 显示弹窗
+    this.dialog.open(ProjectGroupDialog, {
+      width: '500px',
+      data: {
+        selectedCustomer: this.selectedCustomer(),
+        demandType: this.customerForm.get('demandType')?.value,
+        preferenceTags: this.preferenceTags,
+        followUpStatus: this.customerForm.get('followUpStatus')?.value
+      }
+    }).afterClosed().subscribe(result => {
+      if (result && result.confirm) {
+
+        const tags = {
+          demandType: result.demandType,
+          preferenceTags: result.preferenceTags,
+          followUpStatus: result.followUpStatus
+        };
+
+        this.isSubmitting.set(true);
+        this.projectService.createProjectGroup({
+          customerId: result.customerId,
+          customerName: result.customerName,
+          tags
+        }).subscribe(
+          (response: any) => {
+            if (response.success) {
+              this.snackBar.open(`项目群创建成功,群ID: ${response.groupId}`, '确定', { duration: 3000 });
+            } else {
+              this.snackBar.open('项目群创建失败,请稍后重试', '确定', { duration: 2000 });
+            }
+            this.isSubmitting.set(false);
+          },
+          (error: any) => {
+            this.snackBar.open('创建项目群时出错,请稍后重试', '确定', { duration: 2000 });
+            this.isSubmitting.set(false);
+          }
+        );
+      }
+    });
   }
 }

+ 155 - 0
src/app/pages/customer-service/consultation-order/project-group-dialog.component.ts

@@ -0,0 +1,155 @@
+import { Component, Inject } from '@angular/core';
+import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
+import { CommonModule } from '@angular/common';
+import { MatButtonModule } from '@angular/material/button';
+import { MatDialogModule } from '@angular/material/dialog';
+import { MatFormFieldModule } from '@angular/material/form-field';
+import { MatInputModule } from '@angular/material/input';
+import { FormsModule } from '@angular/forms';
+import { MatChipGrid, MatChipsModule } from '@angular/material/chips';
+import { MatIconModule } from '@angular/material/icon';
+
+@Component({
+  selector: 'app-project-group-dialog',
+  standalone: true,
+  imports: [
+    CommonModule,
+    MatButtonModule,
+    MatDialogModule,
+    MatFormFieldModule,
+    MatInputModule,
+    FormsModule,
+    MatChipsModule,
+    MatIconModule
+  ],
+  template: `
+    <h2 mat-dialog-title>创建项目群</h2>
+    <mat-dialog-content>
+      <div class="dialog-content">
+        <div class="customer-info" *ngIf="data.selectedCustomer">
+          <h3>客户信息</h3>
+          <p><strong>姓名:</strong> {{ data.selectedCustomer.name }}</p>
+          <p><strong>电话:</strong> {{ data.selectedCustomer.phone }}</p>
+          <p><strong>地址:</strong> {{ data.selectedCustomer.address }}</p>
+        </div>
+        <div class="no-customer" *ngIf="!data.selectedCustomer">
+          <p class="warning-message">请先选择客户</p>
+        </div>
+        
+        <div class="group-details" *ngIf="data.selectedCustomer">
+          <h3>项目群信息</h3>
+          <div class="form-group">
+            <label>需求类型</label>
+            <p>{{ data.demandType || '未设置' }}</p>
+          </div>
+          
+          <div class="form-group" *ngIf="data.preferenceTags && data.preferenceTags.length > 0">
+            <label>偏好标签</label>
+            <mat-chip-grid>
+              <mat-chip *ngFor="let tag of data.preferenceTags">{{ tag }}</mat-chip>
+            </mat-chip-grid>
+          </div>
+          
+          <div class="form-group">
+            <label>跟进状态</label>
+            <p>{{ data.followUpStatus || '未设置' }}</p>
+          </div>
+          
+          <div class="form-group">
+            <label>备注</label>
+            <textarea [(ngModel)]="remarks" rows="3" placeholder="输入群聊备注..."></textarea>
+          </div>
+        </div>
+      </div>
+    </mat-dialog-content>
+    <mat-dialog-actions align="end">
+      <button mat-button (click)="onCancel()">取消</button>
+      <button mat-button color="primary" (click)="onConfirm()" [disabled]="!data.selectedCustomer">
+        确认创建
+      </button>
+    </mat-dialog-actions>
+  `,
+  styles: [`
+    .dialog-content {
+      padding: 10px 0;
+    }
+    
+    h3 {
+      margin: 16px 0 12px;
+      font-size: 16px;
+      font-weight: 600;
+    }
+    
+    .customer-info {
+      padding: 12px;
+      background-color: #f5f5f5;
+      border-radius: 8px;
+      margin-bottom: 16px;
+    }
+    
+    .no-customer {
+      padding: 20px;
+      text-align: center;
+    }
+    
+    .warning-message {
+      color: #f44336;
+      font-size: 16px;
+      margin: 0;
+    }
+    
+    .form-group {
+      margin-bottom: 16px;
+    }
+    
+    label {
+      display: block;
+      margin-bottom: 4px;
+      font-weight: 500;
+      color: rgba(0, 0, 0, 0.87);
+    }
+    
+    p {
+      margin: 0;
+      color: rgba(0, 0, 0, 0.6);
+    }
+    
+    textarea {
+      width: 100%;
+      padding: 8px;
+      border: 1px solid #e0e0e0;
+      border-radius: 4px;
+      resize: vertical;
+      font-family: inherit;
+      font-size: 14px;
+    }
+    
+    mat-chip {
+      margin: 4px;
+    }
+  `]
+})
+export class ProjectGroupDialog {
+  remarks: string = '';
+
+  constructor(
+    public dialogRef: MatDialogRef<ProjectGroupDialog>,
+    @Inject(MAT_DIALOG_DATA) public data: any
+  ) {}
+
+  onCancel(): void {
+    this.dialogRef.close();
+  }
+
+  onConfirm(): void {
+    this.dialogRef.close({
+      confirm: true,
+      customerId: this.data.selectedCustomer?.id,
+      customerName: this.data.selectedCustomer?.name,
+      demandType: this.data.demandType,
+      preferenceTags: this.data.preferenceTags,
+      followUpStatus: this.data.followUpStatus,
+      remarks: this.remarks
+    });
+  }
+}

+ 4 - 4
src/app/pages/customer-service/customer-service-layout/customer-service-layout.scss

@@ -24,10 +24,10 @@ $transition: all 0.3s ease;
   align-items: center;
   justify-content: space-between;
   padding: 0 24px;
-  height: 64px;
+  height: 72px; // 扩大导航栏高度
   background-color: $background-primary;
   border-bottom: 1px solid $border-color;
-  box-shadow: $shadow-sm;
+  box-shadow: $shadow-md; // 增加阴影效果
   position: sticky;
   top: 0;
   z-index: 1000;
@@ -53,8 +53,8 @@ $transition: all 0.3s ease;
   }
 
   .app-title {
-    font-size: 20px;
-    font-weight: 600;
+    font-size: 24px; // 放大标题字体
+    font-weight: 700;
     color: $text-primary;
   }
 

+ 31 - 0
src/app/pages/customer-service/customer-service.routes.ts

@@ -0,0 +1,31 @@
+import { Routes } from '@angular/router';
+import { Dashboard } from './dashboard/dashboard';
+import { ConsultationListComponent } from './dashboard/pages/consultation-list/consultation-list.component';
+import { AssignmentListComponent } from './dashboard/pages/assignment-list/assignment-list.component';
+import { ExceptionListComponent } from './dashboard/pages/exception-list/exception-list.component';
+import { RevenueDetailComponent } from './dashboard/pages/revenue-detail/revenue-detail.component';
+
+export const CUSTOMER_SERVICE_ROUTES: Routes = [
+  {
+    path: '',
+    component: Dashboard,
+    children: [
+      {
+        path: 'consultation-list',
+        component: ConsultationListComponent
+      },
+      {
+        path: 'assignment-list',
+        component: AssignmentListComponent
+      },
+      {
+        path: 'exception-list',
+        component: ExceptionListComponent
+      },
+      {
+        path: 'revenue-detail',
+        component: RevenueDetailComponent
+      }
+    ]
+  }
+];

+ 174 - 1
src/app/pages/customer-service/dashboard/dashboard.html

@@ -82,7 +82,7 @@
       <div style="display: flex; gap: 12px; align-items: center;">
         <button 
           class="btn-primary"
-          (click)="addUrgentTask()"
+          (click)="showTaskForm()"
           style="font-size: 14px; padding: 6px 16px;"
         >
           添加紧急事项
@@ -135,6 +135,179 @@
     </div>
   </section>
 
+  <!-- iOS风格的添加紧急事项面板 -->
+  <div class="ios-modal-overlay" *ngIf="isTaskFormVisible()" (click)="hideTaskForm()">
+    <div class="ios-panel" (click)="$event.stopPropagation()">
+      <div class="ios-panel-header">
+        <h3>添加紧急事项</h3>
+        <button class="ios-close-button" (click)="hideTaskForm()">
+          <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+            <line x1="18" y1="6" x2="6" y2="18"></line>
+            <line x1="6" y1="6" x2="18" y2="18"></line>
+          </svg>
+        </button>
+      </div>
+      
+      <div class="ios-panel-content">
+        <form (ngSubmit)="handleAddTaskSubmit()">
+          <div class="form-group">
+            <label for="taskTitle">任务标题 *</label>
+            <input 
+              type="text" 
+              id="taskTitle" 
+              [(ngModel)]="newTask.title" 
+              [ngModelOptions]="{standalone: true}"
+              placeholder="请输入任务标题"
+              required
+              class="ios-input"
+            >
+          </div>
+          
+          <div class="form-group">
+            <label for="projectName">项目名称 *</label>
+            <input 
+              type="text" 
+              id="projectName" 
+              [(ngModel)]="newTask.projectName" 
+              [ngModelOptions]="{standalone: true}"
+              placeholder="请输入项目名称"
+              required
+              class="ios-input"
+            >
+          </div>
+          
+          <div class="form-group">
+            <label for="projectStage">项目阶段 *</label>
+            <select 
+              id="projectStage" 
+              [(ngModel)]="newTask.stage" 
+              [ngModelOptions]="{standalone: true}"
+              required
+              class="ios-select"
+            >
+              <option value="前期沟通">前期沟通</option>
+              <option value="建模">建模</option>
+              <option value="软装">软装</option>
+              <option value="渲染">渲染</option>
+              <option value="后期">后期</option>
+              <option value="完成">完成</option>
+            </select>
+          </div>
+          
+          <div class="form-group">
+            <label for="taskDeadline">截止时间 *</label>
+            
+            <!-- 显示当前选择的截止时间 -->
+            <div class="deadline-display ios-input" (click)="deadlineDropdownVisible = !deadlineDropdownVisible">
+              {{ getDisplayDeadline() || '请选择截止时间' }}
+              <span class="dropdown-arrow" [class.rotate]="deadlineDropdownVisible">▼</span>
+            </div>
+            
+            <!-- 预设时长下拉选择框 -->
+            <div class="deadline-dropdown" [class.visible]="deadlineDropdownVisible">
+              <div class="dropdown-option" 
+                   *ngFor="let preset of timePresets"
+                   [class.selected]="selectedPreset === preset.hours.toString()"
+                   (click)="handlePresetSelection(preset.hours.toString())">
+                {{ preset.label }}
+              </div>
+              
+              <!-- 当天24:00前选项 -->
+              <div class="dropdown-option" 
+                   [class.selected]="selectedPreset === 'today'"
+                   (click)="handlePresetSelection('today')">
+                今日24:00前
+              </div>
+              
+              <!-- 自定义时间选项 -->
+              <div class="dropdown-divider"></div>
+              <div class="dropdown-option custom-option" (click)="handlePresetSelection('custom')">
+                自定义时间
+              </div>
+            </div>
+            
+            <!-- 错误提示信息 -->
+            <div class="error-message" *ngIf="deadlineError">{{ deadlineError }}</div>
+          </div>
+          
+          <!-- 自定义时间选择弹窗 -->
+          <div class="custom-time-modal" *ngIf="isCustomTimeVisible">
+            <div class="modal-backdrop"></div>
+            <div class="modal-content">
+              <div class="modal-header">
+                <h3>选择自定义时间</h3>
+                <button class="close-button" (click)="closeCustomTimePicker()">×</button>
+              </div>
+              
+              <div class="modal-body">
+                <!-- 日期选择 -->
+                <div class="date-picker">
+                  <label>日期</label>
+                  <input
+                    type="date"
+                    [(ngModel)]="customDate"
+                    min="{{ todayDate }}"
+                    max="{{ sevenDaysLaterDate }}"
+                    class="ios-input"
+                  />
+                </div>
+                
+                <!-- 时间选择 -->
+                <div class="time-picker">
+                  <label>时间</label>
+                  <input
+                    type="time"
+                    [(ngModel)]="customTime"
+                    class="ios-input"
+                  />
+                </div>
+                
+                <!-- 错误提示信息 -->
+                <div class="error-message" *ngIf="deadlineError">{{ deadlineError }}</div>
+              </div>
+              
+              <div class="modal-footer">
+                <button class="cancel-button" (click)="closeCustomTimePicker()">取消</button>
+                <button class="confirm-button" (click)="handleCustomTimeSelection()">确定</button>
+              </div>
+            </div>
+          </div>
+          
+          <div class="form-group">
+            <label for="taskPriority">优先级</label>
+            <select 
+              id="taskPriority" 
+              [(ngModel)]="newTask.priority" 
+              [ngModelOptions]="{standalone: true}"
+              class="ios-select"
+            >
+              <option value="high">高</option>
+              <option value="medium">中</option>
+              <option value="low">低</option>
+            </select>
+          </div>
+          
+          <div class="form-group">
+            <label for="taskDescription">任务描述</label>
+            <textarea 
+              id="taskDescription" 
+              [(ngModel)]="newTask.description" 
+              [ngModelOptions]="{standalone: true}"
+              placeholder="请输入任务描述"
+              rows="3"
+              class="ios-textarea"
+            ></textarea>
+          </div>
+        </form>
+      </div>
+      
+      <div class="ios-panel-footer">
+        <button type="button" class="ios-cancel-button" (click)="hideTaskForm()">取消</button>
+        <button type="button" class="ios-submit-button" (click)="handleAddTaskSubmit()" [disabled]="isSubmitDisabled">确定</button>
+      </div>
+    </div>
+  </div>
+
   <!-- 项目动态流 -->
   <section class="project-updates-section">
     <div class="section-header">

+ 29 - 0
src/app/pages/customer-service/dashboard/dashboard.routes.ts

@@ -0,0 +1,29 @@
+import { Routes } from '@angular/router';
+import { Dashboard } from './dashboard';
+import { ConsultationListComponent } from './pages/consultation-list/consultation-list.component';
+import { AssignmentListComponent } from './pages/assignment-list/assignment-list.component';
+import { ExceptionListComponent } from './pages/exception-list/exception-list.component';
+import { RevenueDetailComponent } from './pages/revenue-detail/revenue-detail.component';
+
+export const DASHBOARD_ROUTES: Routes = [
+  { 
+    path: 'consultation-list', 
+    component: ConsultationListComponent,
+    data: { title: '咨询列表' }
+  },
+  { 
+    path: 'assignment-list', 
+    component: AssignmentListComponent,
+    data: { title: '派单列表' }
+  },
+  { 
+    path: 'exception-list', 
+    component: ExceptionListComponent,
+    data: { title: '异常项目' }
+  },
+  { 
+    path: 'revenue-detail', 
+    component: RevenueDetailComponent,
+    data: { title: '成交详情' }
+  }
+];

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 889 - 436
src/app/pages/customer-service/dashboard/dashboard.scss


+ 308 - 28
src/app/pages/customer-service/dashboard/dashboard.ts

@@ -2,7 +2,7 @@
 import { Component, OnInit, OnDestroy, signal, computed } from '@angular/core';
 import { CommonModule } from '@angular/common';
 import { FormsModule } from '@angular/forms';
-import { RouterModule } from '@angular/router';
+import { RouterModule, Router, ActivatedRoute } from '@angular/router';
 import { ProjectService } from '../../../services/project.service';
 import { Project, Task, CustomerFeedback } from '../../../models/project.model';
 
@@ -11,7 +11,8 @@ import { Project, Task, CustomerFeedback } from '../../../models/project.model';
   standalone: true,
   imports: [CommonModule, FormsModule, RouterModule],
   templateUrl: './dashboard.html',
-  styleUrls: ['./dashboard.scss', '../customer-service-styles.scss']
+  styleUrls: ['./dashboard.scss', '../customer-service-styles.scss'],
+  providers: [ProjectService]
 }) 
 export class Dashboard implements OnInit, OnDestroy {
   // 数据看板统计
@@ -57,7 +58,71 @@ export class Dashboard implements OnInit, OnDestroy {
   // 回到顶部按钮可见性信号
   showBackToTopSignal = signal(false);
 
-  constructor(private projectService: ProjectService) {}
+  // 任务表单可见性
+  isTaskFormVisible = signal(false);
+  
+  // 新任务数据
+  newTask: Task = {
+    id: '',
+    projectId: '',
+    projectName: '',
+    title: '',
+    stage: '前期沟通',
+    deadline: new Date(),
+    isOverdue: false,
+    isCompleted: false,
+    priority: 'high',
+    assignee: '当前用户',
+    description: ''
+  };
+  
+  // 用于日期时间输入的属性
+  deadlineInput = '';
+  
+  // 预设快捷时长选项
+  timePresets = [
+    { label: '1小时内', hours: 1 },
+    { label: '3小时内', hours: 3 },
+    { label: '6小时内', hours: 6 },
+    { label: '12小时内', hours: 12 },
+    { label: '24小时内', hours: 24 }
+  ];
+  
+  // 选中的预设时长
+  selectedPreset = '';
+  
+  // 自定义时间弹窗可见性
+  isCustomTimeVisible = false;
+  
+  // 自定义选择的日期和时间
+  customDate = new Date();
+  customTime = '';
+  
+  // 错误提示信息
+  deadlineError = '';
+  
+  // 提交按钮是否禁用
+  isSubmitDisabled = false;
+  
+  // 下拉框可见性
+  deadlineDropdownVisible = false;
+  
+  // 日期范围限制
+  get todayDate(): string {
+    return new Date().toISOString().split('T')[0];
+  }
+  
+  get sevenDaysLaterDate(): string {
+    const date = new Date();
+    date.setDate(date.getDate() + 7);
+    return date.toISOString().split('T')[0];
+  }
+
+  constructor(
+    private projectService: ProjectService,
+    private router: Router,
+    private activatedRoute: ActivatedRoute
+  ) {}
 
   ngOnInit(): void {
     this.loadUrgentTasks();
@@ -175,52 +240,267 @@ export class Dashboard implements OnInit, OnDestroy {
     this.stats.pendingAssignments.set(this.stats.pendingAssignments() - 1);
   }
 
-  // 添加新的紧急事项
-  addUrgentTask(): void {
-    // 在实际应用中,这里可能会打开一个表单模态框
-    // 这里使用模拟数据直接添加
-    const newTask: Task = {
-      id: `task-${Date.now()}`,
-      projectId: `project-${Math.floor(Math.random() * 1000)}`,
-      title: '新增紧急任务',
-      projectName: '新项目',
-      stage: '前期沟通', // 设置一个有效的ProjectStage值
+  // 显示任务表单
+  showTaskForm(): void {
+    // 重置表单数据
+    this.newTask = {
+      id: '',
+      projectId: '',
+      projectName: '',
+      title: '',
+      stage: '前期沟通',
       deadline: new Date(),
       isOverdue: false,
       isCompleted: false,
-      // Task接口中没有status属性,移除它
-      // 为了符合Task接口要求,添加required的stage字段
-      priority: 'high', // 模拟值
-      assignee: '当前用户', // 模拟值
-      description: '紧急任务描述' // 模拟值
+      priority: 'high',
+      assignee: '当前用户',
+      description: ''
     };
-
-    this.urgentTasks.set([newTask, ...this.urgentTasks()]);
+    
+    // 重置相关状态
+    this.deadlineError = '';
+    this.isSubmitDisabled = false;
+    
+    // 计算并设置默认预设时长
+    this.setDefaultPreset();
+    
+    // 显示表单
+    this.isTaskFormVisible.set(true);
+    
+    // 添加iOS风格的面板显示动画
+    setTimeout(() => {
+      document.querySelector('.ios-panel')?.classList.add('ios-panel-visible');
+    }, 10);
+  }
+  
+  // 设置默认预设时长
+  private setDefaultPreset(): void {
+    const now = new Date();
+    const todayEnd = new Date(now);
+    todayEnd.setHours(23, 59, 59, 999);
+    
+    // 检查3小时后是否超过当天24:00
+    const threeHoursLater = new Date(now.getTime() + 3 * 60 * 60 * 1000);
+    
+    if (threeHoursLater <= todayEnd) {
+      // 3小时后未超过当天24:00,默认选中3小时内
+      this.selectedPreset = '3';
+      this.updatePresetDeadline(3);
+    } else {
+      // 3小时后超过当天24:00,默认选中当天24:00前
+      this.selectedPreset = 'today';
+      this.deadlineInput = todayEnd.toISOString().slice(0, 16);
+      this.newTask.deadline = todayEnd;
+    }
+  }
+  
+  // 处理预设时长选择
+  handlePresetSelection(preset: string): void {
+    this.selectedPreset = preset;
+    this.deadlineError = '';
+    
+    if (preset === 'custom') {
+      // 打开自定义时间选择器
+      this.openCustomTimePicker();
+    } else if (preset === 'today') {
+      // 设置为当天24:00前
+      const now = new Date();
+      const todayEnd = new Date(now);
+      todayEnd.setHours(23, 59, 59, 999);
+      
+      this.deadlineInput = todayEnd.toISOString().slice(0, 16);
+      this.newTask.deadline = todayEnd;
+    } else {
+      // 计算预设时长的截止时间
+      const hours = parseInt(preset);
+      this.updatePresetDeadline(hours);
+    }
+  }
+  
+  // 更新预设时长的截止时间
+  private updatePresetDeadline(hours: number): void {
+    const now = new Date();
+    const deadline = new Date(now.getTime() + hours * 60 * 60 * 1000);
+    
+    this.deadlineInput = deadline.toISOString().slice(0, 16);
+    this.newTask.deadline = deadline;
+  }
+  
+  // 打开自定义时间选择器
+  openCustomTimePicker(): void {
+    // 重置自定义时间
+    this.customDate = new Date();
+    const now = new Date();
+    this.customTime = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}`;
+    
+    // 显示自定义时间弹窗
+    this.isCustomTimeVisible = true;
+    
+    // 添加iOS风格的弹窗动画
+    setTimeout(() => {
+      document.querySelector('.custom-time-modal')?.classList.add('modal-visible');
+    }, 10);
+  }
+  
+  // 关闭自定义时间选择器
+  closeCustomTimePicker(): void {
+    // 添加iOS风格的弹窗关闭动画
+    const modal = document.querySelector('.custom-time-modal');
+    if (modal) {
+      modal.classList.remove('modal-visible');
+      setTimeout(() => {
+        this.isCustomTimeVisible = false;
+      }, 300);
+    } else {
+      this.isCustomTimeVisible = false;
+    }
+  }
+  
+  // 处理自定义时间选择
+  handleCustomTimeSelection(): void {
+    const [hours, minutes] = this.customTime.split(':').map(Number);
+    const selectedDateTime = new Date(this.customDate);
+    selectedDateTime.setHours(hours, minutes, 0, 0);
+    
+    // 验证选择的时间是否有效
+    if (this.validateDeadline(selectedDateTime)) {
+      this.deadlineInput = selectedDateTime.toISOString().slice(0, 16);
+      this.newTask.deadline = selectedDateTime;
+      this.closeCustomTimePicker();
+    }
+  }
+  
+  // 验证截止时间是否有效
+  validateDeadline(deadline: Date): boolean {
+    const now = new Date();
+    
+    if (deadline < now) {
+      this.deadlineError = '截止时间不能早于当前时间,请重新选择';
+      this.isSubmitDisabled = true;
+      return false;
+    }
+    
+    this.deadlineError = '';
+    this.isSubmitDisabled = false;
+    return true;
+  }
+  
+  // 获取显示的截止时间文本
+  getDisplayDeadline(): string {
+    if (!this.deadlineInput) return '';
+    
+    try {
+      const date = new Date(this.deadlineInput);
+      return date.toLocaleString('zh-CN', {
+        year: 'numeric',
+        month: '2-digit',
+        day: '2-digit',
+        hour: '2-digit',
+        minute: '2-digit'
+      });
+    } catch (error) {
+      return '';
+    }
+  }
+  
+  // 隐藏任务表单
+  hideTaskForm(): void {
+    // 添加iOS风格的面板隐藏动画
+    const panel = document.querySelector('.ios-panel');
+    if (panel) {
+      panel.classList.remove('ios-panel-visible');
+      setTimeout(() => {
+        this.isTaskFormVisible.set(false);
+      }, 300);
+    } else {
+      this.isTaskFormVisible.set(false);
+    }
+  }
+  
+  // 处理添加任务表单提交
+  handleAddTaskSubmit(): void {
+    // 验证表单数据
+    if (!this.newTask.title.trim() || !this.newTask.projectName.trim() || !this.deadlineInput || this.isSubmitDisabled) {
+      // 在实际应用中,这里应该显示错误提示
+      alert('请填写必填字段(任务标题、项目名称、截止时间)');
+      return;
+    }
+    
+    // 创建新任务
+    const taskToAdd: Task = {
+      ...this.newTask,
+      id: `task-${Date.now()}`,
+      projectId: `project-${Math.floor(Math.random() * 1000)}`,
+      deadline: new Date(this.deadlineInput),
+      isOverdue: new Date(this.deadlineInput) < new Date()
+    };
+    
+    // 添加到任务列表
+    this.urgentTasks.set([taskToAdd, ...this.urgentTasks()]);
+    
+    // 更新统计数据
     this.stats.pendingAssignments.set(this.stats.pendingAssignments() + 1);
+    
+    // 隐藏表单
+    this.hideTaskForm();
+  }
+  
+  // 添加新的紧急事项
+  addUrgentTask(): void {
+    // 调用显示表单方法
+    this.showTaskForm();
   }
 
   // 新咨询数图标点击处理
   handleNewConsultationsClick(): void {
-    console.log('点击查看新咨询详情');
-    // 在实际应用中,这里会跳转到新咨询列表页面或打开新咨询模态框
+    this.navigateToDetail('consultations');
   }
 
   // 待派单数图标点击处理
   handlePendingAssignmentsClick(): void {
-    console.log('点击查看待派单详情');
-    // 在实际应用中,这里会跳转到待派单列表页面或打开待派单模态框
+    this.navigateToDetail('assignments');
   }
 
   // 异常项目图标点击处理
   handleExceptionProjectsClick(): void {
-    console.log('点击查看异常项目详情');
-    // 在实际应用中,这里会跳转到异常项目列表页面或打开异常项目模态框
+    this.navigateToDetail('exceptions');
   }
 
   // 今日成交额图标点击处理
   handleTodayRevenueClick(): void {
-    console.log('点击查看今日成交额详情');
-    // 在实际应用中,这里会跳转到今日成交额详情页面或打开今日成交额模态框
+    this.navigateToDetail('revenue');
+  }
+
+  // 导航到详情页
+  private navigateToDetail(type: 'consultations' | 'assignments' | 'exceptions' | 'revenue'): void {
+    const routeMap = {
+      consultations: '/customer-service/consultation-list',
+      assignments: '/customer-service/assignment-list',
+      exceptions: '/customer-service/exception-list',
+      revenue: '/customer-service/revenue-detail'
+    };
+    
+    console.log('导航到:', routeMap[type]);
+    console.log('当前路由:', this.router.url);
+    
+    // 添加iOS风格页面过渡动画
+    document.body.classList.add('ios-page-transition');
+    setTimeout(() => {
+      this.router.navigateByUrl(routeMap[type])
+        .then(navResult => {
+          console.log('导航结果:', navResult);
+          if (!navResult) {
+            console.error('导航失败,检查路由配置');
+          }
+        })
+        .catch(err => {
+          console.error('导航错误:', err);
+        });
+      
+      setTimeout(() => {
+        document.body.classList.remove('ios-page-transition');
+      }, 300);
+    }, 100);
   }
 
   // 格式化日期

+ 42 - 0
src/app/pages/customer-service/dashboard/pages/assignment-list/assignment-list.component.html

@@ -0,0 +1,42 @@
+<div class="ios-container">
+  <header class="ios-header">
+    <button class="ios-back-btn" (click)="goBack()">
+      <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+        <path d="M19 12H5M12 19l-7-7 7-7"/>
+      </svg>
+    </button>
+    <h1>待派单任务</h1>
+  </header>
+
+  <div class="ios-content">
+    <div class="stats-bar">
+      <div class="stat-item">
+        <span class="value">{{assignments.length}}</span>
+        <span class="label">总任务数</span>
+      </div>
+      <div class="stat-item">
+        <span class="value">{{getUrgentCount()}}</span>
+        <span class="label">紧急任务</span>
+      </div>
+    </div>
+
+    <div class="assignment-list">
+      <div class="assignment-card" *ngFor="let item of assignments">
+        <div class="card-header">
+          <h3>{{item.project}}</h3>
+          <span class="priority-badge" [class.high]="item.priority === 'high'">
+            {{item.priority === 'high' ? '高优先级' : '普通'}}
+          </span>
+        </div>
+        <p class="description">{{item.description}}</p>
+        <div class="card-footer">
+          <div class="meta">
+            <span class="customer"><svg><use xlink:href="#user-icon"></use></svg> {{item.customer}}</span>
+            <span class="deadline"><svg><use xlink:href="#clock-icon"></use></svg> {{item.deadline}}</span>
+          </div>
+          <button class="ios-btn">立即派单</button>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>

+ 148 - 0
src/app/pages/customer-service/dashboard/pages/assignment-list/assignment-list.component.scss

@@ -0,0 +1,148 @@
+@import "../../../customer-service-styles.scss";
+
+.ios-container {
+  max-width: 100%;
+  height: 100vh;
+  font-family: -apple-system, BlinkMacSystemFont, sans-serif;
+  background-color: $background-tertiary;
+}
+
+.ios-header {
+  display: flex;
+  align-items: center;
+  padding: 16px;
+  background-color: $background-primary;
+  border-bottom: 1px solid $border-color;
+  position: sticky;
+  top: 0;
+  z-index: 10;
+
+  h1 {
+    font-size: 20px;
+    font-weight: 600;
+    margin: 0 auto;
+  }
+}
+
+.ios-back-btn {
+  background: none;
+  border: none;
+  padding: 8px;
+  z-index: 11;
+}
+
+.ios-content {
+  padding: 16px;
+}
+
+.stats-bar {
+  display: flex;
+  justify-content: space-around;
+  background-color: $background-primary;
+  border-radius: 12px;
+  padding: 16px;
+  margin-bottom: 16px;
+  box-shadow: $shadow-sm;
+}
+
+.stat-item {
+  text-align: center;
+
+  .value {
+    display: block;
+    font-size: 24px;
+    font-weight: 700;
+    color: $primary-color;
+    margin-bottom: 4px;
+  }
+
+  .label {
+    font-size: 14px;
+    color: $text-tertiary;
+  }
+}
+
+.assignment-list {
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+}
+
+.assignment-card {
+  background-color: $background-primary;
+  border-radius: 12px;
+  padding: 16px;
+  box-shadow: $shadow-sm;
+  transition: transform 0.2s ease;
+
+  &:active {
+    transform: scale(0.98);
+  }
+}
+
+.card-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 12px;
+
+  h3 {
+    font-size: 16px;
+    margin: 0;
+    color: $text-primary;
+  }
+}
+
+.priority-badge {
+  padding: 4px 8px;
+  border-radius: 4px;
+  font-size: 12px;
+  font-weight: 500;
+
+  &.high {
+    background-color: rgba($danger-color, 0.1);
+    color: $danger-color;
+  }
+
+  &:not(.high) {
+    background-color: rgba($success-color, 0.1);
+    color: $success-color;
+  }
+}
+
+.description {
+  color: $text-secondary;
+  margin: 0 0 16px;
+  line-height: 1.5;
+}
+
+.card-footer {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+
+  .meta {
+    display: flex;
+    gap: 12px;
+    font-size: 14px;
+    color: $text-tertiary;
+
+    svg {
+      width: 14px;
+      height: 14px;
+      margin-right: 4px;
+      vertical-align: middle;
+    }
+  }
+}
+
+.ios-btn {
+  padding: 8px 16px;
+  border-radius: 8px;
+  font-size: 14px;
+  font-weight: 500;
+  background-color: $primary-color;
+  color: white;
+  border: none;
+  transition: all 0.2s ease;
+}

+ 105 - 0
src/app/pages/customer-service/dashboard/pages/assignment-list/assignment-list.component.ts

@@ -0,0 +1,105 @@
+import { Component } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { RouterModule } from '@angular/router';
+
+@Component({
+  selector: 'app-assignment-list',
+  standalone: true,
+  imports: [CommonModule, RouterModule],
+  template: `
+    <div class="ios-container">
+      <header class="ios-header">
+        <h1>待派单列表</h1>
+        <button class="ios-back-btn" (click)="goBack()">
+          <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+            <path d="M19 12H5M12 19l-7-7 7-7"/>
+          </svg>
+        </button>
+      </header>
+      
+      <div class="ios-content">
+        <div class="ios-card" *ngFor="let item of assignments">
+          <div class="ios-card-header">
+            <h3>{{item.project}}</h3>
+            <span class="ios-badge" [class.urgent]="item.priority === 'high'">
+              {{item.priority === 'high' ? '紧急' : '普通'}}
+            </span>
+          </div>
+          <p>{{item.description}}</p>
+          <button class="ios-btn">派单处理</button>
+        </div>
+      </div>
+    </div>
+  `,
+  styles: [`
+    .ios-container {
+      max-width: 800px;
+      margin: 0 auto;
+      font-family: -apple-system, BlinkMacSystemFont, sans-serif;
+    }
+    
+    .ios-header {
+      display: flex;
+      align-items: center;
+      padding: 16px;
+      background: #f2f2f7;
+      border-bottom: 1px solid #d1d1d6;
+    }
+    
+    .ios-back-btn {
+      background: none;
+      border: none;
+      margin-right: 16px;
+    }
+    
+    .ios-content {
+      padding: 16px;
+    }
+    
+    .ios-card {
+      background: white;
+      border-radius: 12px;
+      padding: 16px;
+      margin-bottom: 16px;
+      box-shadow: 0 1px 3px rgba(0,0,0,0.1);
+    }
+    
+    .ios-card-header {
+      display: flex;
+      justify-content: space-between;
+      margin-bottom: 8px;
+    }
+    
+    .ios-badge {
+      background: #34c759;
+      color: white;
+      padding: 4px 8px;
+      border-radius: 10px;
+      font-size: 12px;
+      
+      &.urgent {
+        background: #ff3b30;
+      }
+    }
+    
+    .ios-btn {
+      background: #007aff;
+      color: white;
+      border: none;
+      padding: 8px 16px;
+      border-radius: 8px;
+      margin-top: 12px;
+    }
+  `]
+})
+export class AssignmentListComponent {
+  assignments = [
+    {project: '现代简约客厅设计', priority: 'high', description: '客户急需设计方案确认'},
+    {project: '欧式厨房改造', priority: 'normal', description: '等待设计师确认方案'},
+    {project: '三居室全屋设计', priority: 'normal', description: '等待客户提供户型图'}
+  ];
+  
+  goBack() {
+    history.back();
+  }
+}

+ 42 - 0
src/app/pages/customer-service/dashboard/pages/consultation-list/consultation-list.component.html

@@ -0,0 +1,42 @@
+<div class="ios-container">
+  <header class="ios-header">
+    <button class="ios-back-btn" (click)="goBack()">
+      <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+        <path d="M19 12H5M12 19l-7-7 7-7"/>
+      </svg>
+    </button>
+    <h1>客户咨询记录</h1>
+  </header>
+
+  <div class="ios-content">
+    <div class="search-bar">
+      <input type="text" placeholder="搜索咨询记录...">
+      <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+        <circle cx="11" cy="11" r="8"/>
+        <line x1="21" y1="21" x2="16.65" y2="16.65"/>
+      </svg>
+    </div>
+
+    <div class="consultation-list">
+      <div class="consultation-card" *ngFor="let item of consultations">
+        <div class="card-header">
+          <div class="customer-info">
+            <div class="avatar">{{item.customer.charAt(0)}}</div>
+            <div>
+              <h3>{{item.customer}}</h3>
+              <p class="time">{{item.time}}</p>
+            </div>
+          </div>
+          <span class="status-badge" [class.urgent]="item.priority === 'high'">
+            {{item.priority === 'high' ? '紧急' : '普通'}}
+          </span>
+        </div>
+        <p class="content">{{item.content}}</p>
+        <div class="card-footer">
+          <button class="ios-btn">查看详情</button>
+          <button class="ios-btn outline">分配处理</button>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>

+ 166 - 0
src/app/pages/customer-service/dashboard/pages/consultation-list/consultation-list.component.scss

@@ -0,0 +1,166 @@
+@import "../../../customer-service-styles.scss";
+
+.ios-container {
+  max-width: 100%;
+  height: 100vh;
+  font-family: -apple-system, BlinkMacSystemFont, sans-serif;
+  background-color: $background-tertiary;
+}
+
+.ios-header {
+  display: flex;
+  align-items: center;
+  padding: 16px;
+  background-color: $background-primary;
+  border-bottom: 1px solid $border-color;
+  position: sticky;
+  top: 0;
+  z-index: 10;
+
+  h1 {
+    font-size: 20px;
+    font-weight: 600;
+    margin: 0 auto;
+  }
+}
+
+.ios-back-btn {
+  background: none;
+  border: none;
+  padding: 8px;
+  z-index: 11;
+}
+
+.ios-content {
+  padding: 16px;
+}
+
+.search-bar {
+  position: relative;
+  margin-bottom: 16px;
+
+  input {
+    width: 100%;
+    padding: 12px 16px 12px 40px;
+    border-radius: 10px;
+    border: 1px solid $border-color;
+    background-color: $background-secondary;
+    font-size: 16px;
+
+    &::placeholder {
+      color: $text-tertiary;
+    }
+  }
+
+  svg {
+    position: absolute;
+    left: 12px;
+    top: 50%;
+    transform: translateY(-50%);
+    color: $text-tertiary;
+  }
+}
+
+.consultation-list {
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+}
+
+.consultation-card {
+  background-color: $background-primary;
+  border-radius: 12px;
+  padding: 16px;
+  box-shadow: $shadow-sm;
+  transition: transform 0.2s ease;
+
+  &:active {
+    transform: scale(0.98);
+  }
+}
+
+.card-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 12px;
+}
+
+.customer-info {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+
+  .avatar {
+    width: 40px;
+    height: 40px;
+    border-radius: 20px;
+    background-color: $primary-color;
+    color: white;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    font-weight: 600;
+  }
+
+  h3 {
+    font-size: 16px;
+    margin: 0;
+    color: $text-primary;
+  }
+
+  .time {
+    font-size: 14px;
+    color: $text-tertiary;
+    margin: 4px 0 0;
+  }
+}
+
+.status-badge {
+  padding: 4px 8px;
+  border-radius: 4px;
+  font-size: 12px;
+  font-weight: 500;
+
+  &.urgent {
+    background-color: rgba($danger-color, 0.1);
+    color: $danger-color;
+  }
+
+  &:not(.urgent) {
+    background-color: rgba($success-color, 0.1);
+    color: $success-color;
+  }
+}
+
+.content {
+  color: $text-secondary;
+  margin: 0 0 16px;
+  line-height: 1.5;
+}
+
+.card-footer {
+  display: flex;
+  gap: 8px;
+
+  .ios-btn {
+    flex: 1;
+    padding: 10px;
+    border-radius: 8px;
+    font-size: 14px;
+    font-weight: 500;
+    text-align: center;
+    transition: all 0.2s ease;
+
+    &.outline {
+      background-color: transparent;
+      border: 1px solid $primary-color;
+      color: $primary-color;
+    }
+
+    &:not(.outline) {
+      background-color: $primary-color;
+      color: white;
+    }
+  }
+}

+ 90 - 0
src/app/pages/customer-service/dashboard/pages/consultation-list/consultation-list.component.ts

@@ -0,0 +1,90 @@
+import { Component } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { RouterModule } from '@angular/router';
+
+@Component({
+  selector: 'app-consultation-list',
+  standalone: true,
+  imports: [CommonModule, RouterModule],
+  template: `
+    <div class="ios-container">
+      <header class="ios-header">
+        <h1>新咨询列表</h1>
+        <button class="ios-back-btn" (click)="goBack()">
+          <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+            <path d="M19 12H5M12 19l-7-7 7-7"/>
+          </svg>
+        </button>
+      </header>
+      
+      <div class="ios-content">
+        <!-- 咨询列表内容 -->
+        <div class="ios-card" *ngFor="let item of consultations">
+          <div class="ios-card-header">
+            <h3>{{item.customer}}</h3>
+            <span class="ios-badge">{{item.time}}</span>
+          </div>
+          <p>{{item.content}}</p>
+        </div>
+      </div>
+    </div>
+  `,
+  styles: [`
+    .ios-container {
+      max-width: 800px;
+      margin: 0 auto;
+      font-family: -apple-system, BlinkMacSystemFont, sans-serif;
+    }
+    
+    .ios-header {
+      display: flex;
+      align-items: center;
+      padding: 16px;
+      background: #f2f2f7;
+      border-bottom: 1px solid #d1d1d6;
+    }
+    
+    .ios-back-btn {
+      background: none;
+      border: none;
+      margin-right: 16px;
+    }
+    
+    .ios-content {
+      padding: 16px;
+    }
+    
+    .ios-card {
+      background: white;
+      border-radius: 12px;
+      padding: 16px;
+      margin-bottom: 16px;
+      box-shadow: 0 1px 3px rgba(0,0,0,0.1);
+    }
+    
+    .ios-card-header {
+      display: flex;
+      justify-content: space-between;
+      margin-bottom: 8px;
+    }
+    
+    .ios-badge {
+      background: #007aff;
+      color: white;
+      padding: 4px 8px;
+      border-radius: 10px;
+      font-size: 12px;
+    }
+  `]
+})
+export class ConsultationListComponent {
+  consultations = [
+    {customer: '张先生', time: '10:30', content: '咨询关于厨房改造的预算和工期'},
+    {customer: '李女士', time: '11:45', content: '询问客厅设计风格建议'},
+    {customer: '王先生', time: '14:20', content: '需要全屋设计方案咨询'}
+  ];
+  
+  goBack() {
+    history.back();
+  }
+}

+ 38 - 0
src/app/pages/customer-service/dashboard/pages/exception-list/exception-list.component.html

@@ -0,0 +1,38 @@
+<div class="ios-container">
+  <header class="ios-header">
+    <button class="ios-back-btn" (click)="goBack()">
+      <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+        <path d="M19 12H5M12 19l-7-7 7-7"/>
+      </svg>
+    </button>
+    <h1>异常项目处理</h1>
+  </header>
+
+  <div class="ios-content">
+    <div class="filter-bar">
+      <button class="filter-btn active">全部</button>
+      <button class="filter-btn">未处理</button>
+      <button class="filter-btn">处理中</button>
+    </div>
+
+    <div class="exception-list">
+      <div class="exception-card" *ngFor="let item of exceptions">
+        <div class="card-header">
+          <div class="project-info">
+            <div class="status-indicator" [class]="item.status"></div>
+            <h3>{{item.project}}</h3>
+          </div>
+          <span class="date">{{item.date}}</span>
+        </div>
+        <div class="card-body">
+          <p class="issue"><strong>问题描述:</strong> {{item.issue}}</p>
+          <p class="action" *ngIf="item.action"><strong>处理方案:</strong> {{item.action}}</p>
+        </div>
+        <div class="card-footer">
+          <button class="ios-btn warning">{{item.status === 'resolved' ? '查看详情' : '处理异常'}}</button>
+          <button class="ios-btn">联系客户</button>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>

+ 158 - 0
src/app/pages/customer-service/dashboard/pages/exception-list/exception-list.component.scss

@@ -0,0 +1,158 @@
+@import "../../../customer-service-styles.scss";
+
+.ios-container {
+  max-width: 100%;
+  height: 100vh;
+  font-family: -apple-system, BlinkMacSystemFont, sans-serif;
+  background-color: $background-tertiary;
+}
+
+.ios-header {
+  display: flex;
+  align-items: center;
+  padding: 16px;
+  background-color: $background-primary;
+  border-bottom: 1px solid $border-color;
+  position: sticky;
+  top: 0;
+  z-index: 10;
+
+  h1 {
+    font-size: 20px;
+    font-weight: 600;
+    margin: 0 auto;
+  }
+}
+
+.ios-back-btn {
+  background: none;
+  border: none;
+  padding: 8px;
+  z-index: 11;
+}
+
+.ios-content {
+  padding: 16px;
+}
+
+.filter-bar {
+  display: flex;
+  gap: 8px;
+  margin-bottom: 16px;
+}
+
+.filter-btn {
+  flex: 1;
+  padding: 8px;
+  border-radius: 8px;
+  border: none;
+  background-color: $background-secondary;
+  color: $text-secondary;
+  font-size: 14px;
+  transition: all 0.2s ease;
+
+  &.active {
+    background-color: $primary-color;
+    color: white;
+  }
+}
+
+.exception-list {
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+}
+
+.exception-card {
+  background-color: $background-primary;
+  border-radius: 12px;
+  padding: 16px;
+  box-shadow: $shadow-sm;
+  transition: transform 0.2s ease;
+
+  &:active {
+    transform: scale(0.98);
+  }
+}
+
+.card-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 12px;
+}
+
+.project-info {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+
+  .status-indicator {
+    width: 12px;
+    height: 12px;
+    border-radius: 6px;
+
+    &.pending {
+      background-color: $warning-color;
+    }
+
+    &.processing {
+      background-color: $primary-color;
+    }
+
+    &.resolved {
+      background-color: $success-color;
+    }
+  }
+
+  h3 {
+    font-size: 16px;
+    margin: 0;
+    color: $text-primary;
+  }
+}
+
+.date {
+  font-size: 14px;
+  color: $text-tertiary;
+}
+
+.card-body {
+  margin: 12px 0;
+
+  p {
+    margin: 8px 0;
+    line-height: 1.5;
+    color: $text-secondary;
+
+    strong {
+      color: $text-primary;
+    }
+  }
+}
+
+.card-footer {
+  display: flex;
+  gap: 8px;
+  margin-top: 12px;
+
+  .ios-btn {
+    flex: 1;
+    padding: 10px;
+    border-radius: 8px;
+    font-size: 14px;
+    font-weight: 500;
+    text-align: center;
+    transition: all 0.2s ease;
+
+    &.warning {
+      background-color: $warning-color;
+      color: white;
+    }
+
+    &:not(.warning) {
+      background-color: $primary-color;
+      color: white;
+    }
+  }
+}

+ 110 - 0
src/app/pages/customer-service/dashboard/pages/exception-list/exception-list.component.ts

@@ -0,0 +1,110 @@
+import { Component } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { RouterModule } from '@angular/router';
+
+@Component({
+  selector: 'app-exception-list',
+  standalone: true,
+  imports: [CommonModule, RouterModule],
+  template: `
+    <div class="ios-container">
+      <header class="ios-header">
+        <h1>异常项目列表</h1>
+        <button class="ios-back-btn" (click)="goBack()">
+          <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+            <path d="M19 12H5M12 19l-7-7 7-7"/>
+          </svg>
+        </button>
+      </header>
+      
+      <div class="ios-content">
+        <div class="ios-card" *ngFor="let item of exceptions">
+          <div class="ios-card-header">
+            <h3>{{item.project}}</h3>
+            <span class="ios-badge">{{item.status}}</span>
+          </div>
+          <p>{{item.issue}}</p>
+          <div class="ios-actions">
+            <button class="ios-btn warning">标记处理</button>
+            <button class="ios-btn">查看详情</button>
+          </div>
+        </div>
+      </div>
+    </div>
+  `,
+  styles: [`
+    .ios-container {
+      max-width: 800px;
+      margin: 0 auto;
+      font-family: -apple-system, BlinkMacSystemFont, sans-serif;
+    }
+    
+    .ios-header {
+      display: flex;
+      align-items: center;
+      padding: 16px;
+      background: #f2f2f7;
+      border-bottom: 1px solid #d1d1d6;
+    }
+    
+    .ios-back-btn {
+      background: none;
+      border: none;
+      margin-right: 16px;
+    }
+    
+    .ios-content {
+      padding: 16px;
+    }
+    
+    .ios-card {
+      background: white;
+      border-radius: 12px;
+      padding: 16px;
+      margin-bottom: 16px;
+      box-shadow: 0 1px 3px rgba(0,0,0,0.1);
+    }
+    
+    .ios-card-header {
+      display: flex;
+      justify-content: space-between;
+      margin-bottom: 8px;
+    }
+    
+    .ios-badge {
+      background: #ff9500;
+      color: white;
+      padding: 4px 8px;
+      border-radius: 10px;
+      font-size: 12px;
+    }
+    
+    .ios-actions {
+      display: flex;
+      gap: 8px;
+      margin-top: 12px;
+    }
+    
+    .ios-btn {
+      background: #007aff;
+      color: white;
+      border: none;
+      padding: 8px 16px;
+      border-radius: 8px;
+      
+      &.warning {
+        background: #ff9500;
+      }
+    }
+  `]
+})
+export class ExceptionListComponent {
+  exceptions = [
+    {project: '现代简约客厅设计', status: '材料缺货', issue: '客户选定的大理石材料缺货,需要更换'},
+    {project: '欧式厨房改造', status: '工期延误', issue: '施工队排期冲突,需要调整工期'}
+  ];
+  
+  goBack() {
+    history.back();
+  }
+}

+ 56 - 0
src/app/pages/customer-service/dashboard/pages/revenue-detail/revenue-detail.component.html

@@ -0,0 +1,56 @@
+<div class="ios-container">
+  <header class="ios-header">
+    <button class="ios-back-btn" (click)="goBack()">
+      <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+        <path d="M19 12H5M12 19l-7-7 7-7"/>
+      </svg>
+    </button>
+    <h1>成交详情</h1>
+    <button class="ios-share-btn">
+      <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+        <circle cx="18" cy="5" r="3"/>
+        <circle cx="6" cy="12" r="3"/>
+        <circle cx="18" cy="19" r="3"/>
+        <line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/>
+        <line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/>
+      </svg>
+    </button>
+  </header>
+
+  <div class="ios-content">
+    <div class="summary-card">
+      <div class="amount">¥{{totalRevenue}}</div>
+      <div class="period">今日总成交额</div>
+      <div class="stats">
+        <div class="stat-item">
+          <div class="value">{{transactions.length}}</div>
+          <div class="label">成交项目</div>
+        </div>
+        <div class="stat-item">
+          <div class="value positive">+{{growthRate}}%</div>
+          <div class="label">环比增长</div>
+        </div>
+      </div>
+    </div>
+
+    <div class="transaction-list">
+      <div class="section-header">
+        <h2>交易记录</h2>
+        <button class="filter-btn">筛选</button>
+      </div>
+
+      <div class="transaction-item" *ngFor="let item of transactions">
+        <div class="icon">
+          <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+            <path d="M12 1v3M12 20v3M5 12H2M22 12h-3M6.2 6.2l-1.4 1.4M19.2 19.2l-1.4 1.4M6.2 17.8l-1.4-1.4M19.2 4.8l-1.4-1.4"/>
+          </svg>
+        </div>
+        <div class="details">
+          <div class="project">{{item.project}}</div>
+          <div class="customer">{{item.customer}}</div>
+        </div>
+        <div class="amount">¥{{item.amount}}</div>
+      </div>
+    </div>
+  </div>
+</div>

+ 161 - 0
src/app/pages/customer-service/dashboard/pages/revenue-detail/revenue-detail.component.scss

@@ -0,0 +1,161 @@
+@import "../../../customer-service-styles.scss";
+
+.ios-container {
+  max-width: 100%;
+  height: 100vh;
+  font-family: -apple-system, BlinkMacSystemFont, sans-serif;
+  background-color: $background-tertiary;
+}
+
+.ios-header {
+  display: flex;
+  align-items: center;
+  padding: 16px;
+  background-color: $background-primary;
+  border-bottom: 1px solid $border-color;
+  position: sticky;
+  top: 0;
+  z-index: 10;
+
+  h1 {
+    font-size: 20px;
+    font-weight: 600;
+    margin: 0 auto;
+  }
+}
+
+.ios-back-btn {
+  background: none;
+  border: none;
+  padding: 8px;
+  z-index: 11;
+}
+
+.ios-share-btn {
+  background: none;
+  border: none;
+  padding: 8px;
+  z-index: 11;
+}
+
+.ios-content {
+  padding: 16px;
+}
+
+.summary-card {
+  background: linear-gradient(135deg, $primary-color, $primary-dark);
+  border-radius: 16px;
+  padding: 24px;
+  margin-bottom: 16px;
+  color: white;
+  box-shadow: $shadow-md;
+}
+
+.amount {
+  font-size: 32px;
+  font-weight: 700;
+  margin-bottom: 8px;
+}
+
+.period {
+  font-size: 16px;
+  opacity: 0.9;
+  margin-bottom: 24px;
+}
+
+.stats {
+  display: flex;
+  justify-content: space-between;
+}
+
+.stat-item {
+  text-align: center;
+
+  .value {
+    font-size: 20px;
+    font-weight: 600;
+    margin-bottom: 4px;
+
+    &.positive {
+      color: $success-color;
+    }
+  }
+
+  .label {
+    font-size: 14px;
+    opacity: 0.8;
+  }
+}
+
+.transaction-list {
+  background-color: $background-primary;
+  border-radius: 16px;
+  padding: 16px;
+  box-shadow: $shadow-sm;
+}
+
+.section-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 16px;
+
+  h2 {
+    font-size: 18px;
+    margin: 0;
+    color: $text-primary;
+  }
+}
+
+.filter-btn {
+  background: none;
+  border: none;
+  color: $primary-color;
+  font-size: 14px;
+  font-weight: 500;
+}
+
+.transaction-item {
+  display: flex;
+  align-items: center;
+  padding: 12px 0;
+  border-bottom: 1px solid $border-color;
+
+  &:last-child {
+    border-bottom: none;
+  }
+}
+
+.icon {
+  width: 40px;
+  height: 40px;
+  border-radius: 20px;
+  background-color: rgba($primary-color, 0.1);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-right: 12px;
+  color: $primary-color;
+}
+
+.details {
+  flex: 1;
+
+  .project {
+    font-size: 16px;
+    font-weight: 500;
+    color: $text-primary;
+    margin-bottom: 4px;
+  }
+
+  .customer {
+    font-size: 14px;
+    color: $text-tertiary;
+  }
+}
+
+.amount {
+  font-size: 16px;
+  font-weight: 600;
+  color: $success-color;
+}

+ 120 - 0
src/app/pages/customer-service/dashboard/pages/revenue-detail/revenue-detail.component.ts

@@ -0,0 +1,120 @@
+import { Component } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { RouterModule } from '@angular/router';
+
+@Component({
+  selector: 'app-revenue-detail',
+  standalone: true,
+  imports: [CommonModule, RouterModule],
+  template: `
+    <div class="ios-container">
+      <header class="ios-header">
+        <h1>今日成交详情</h1>
+        <button class="ios-back-btn" (click)="goBack()">
+          <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+            <path d="M19 12H5M12 19l-7-7 7-7"/>
+          </svg>
+        </button>
+      </header>
+      
+      <div class="ios-content">
+        <div class="ios-summary-card">
+          <h2>¥28,500</h2>
+          <p>今日总成交额</p>
+          <div class="ios-stats">
+            <div class="ios-stat-item">
+              <span>5</span>
+              <p>成交项目</p>
+            </div>
+            <div class="ios-stat-item">
+              <span>+28%</span>
+              <p>环比增长</p>
+            </div>
+          </div>
+        </div>
+        
+        <div class="ios-card" *ngFor="let item of transactions">
+          <div class="ios-card-header">
+            <h3>{{item.project}}</h3>
+            <span class="ios-amount">¥{{item.amount}}</span>
+          </div>
+          <p>{{item.customer}} · {{item.time}}</p>
+        </div>
+      </div>
+    </div>
+  `,
+  styles: [`
+    .ios-container {
+      max-width: 800px;
+      margin: 0 auto;
+      font-family: -apple-system, BlinkMacSystemFont, sans-serif;
+    }
+    
+    .ios-header {
+      display: flex;
+      align-items: center;
+      padding: 16px;
+      background: #f2f2f7;
+      border-bottom: 1px solid #d1d1d6;
+    }
+    
+    .ios-back-btn {
+      background: none;
+      border: none;
+      margin-right: 16px;
+    }
+    
+    .ios-content {
+      padding: 16px;
+    }
+    
+    .ios-summary-card {
+      background: linear-gradient(135deg, #007aff, #0040dd);
+      color: white;
+      border-radius: 12px;
+      padding: 24px;
+      margin-bottom: 16px;
+      text-align: center;
+    }
+    
+    .ios-stats {
+      display: flex;
+      justify-content: space-around;
+      margin-top: 16px;
+    }
+    
+    .ios-stat-item {
+      text-align: center;
+    }
+    
+    .ios-card {
+      background: white;
+      border-radius: 12px;
+      padding: 16px;
+      margin-bottom: 16px;
+      box-shadow: 0 1px 3px rgba(0,0,0,0.1);
+    }
+    
+    .ios-card-header {
+      display: flex;
+      justify-content: space-between;
+      margin-bottom: 8px;
+    }
+    
+    .ios-amount {
+      color: #34c759;
+      font-weight: bold;
+    }
+  `]
+})
+export class RevenueDetailComponent {
+  transactions = [
+    {project: '现代简约客厅设计', amount: '12,000', customer: '张先生', time: '10:30'},
+    {project: '欧式厨房改造', amount: '8,500', customer: '李女士', time: '11:45'},
+    {project: '三居室全屋设计', amount: '8,000', customer: '王先生', time: '14:20'}
+  ];
+  
+  goBack() {
+    history.back();
+  }
+}

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

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

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

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

+ 121 - 136
src/app/pages/customer-service/project-detail/project-detail.html

@@ -1,46 +1,46 @@
 <!-- 项目详情页面内容 -->
-<div class="project-detail-container">
+<div class="project-detail-container ios-style">
   <!-- 顶部导航/Header -->
-  <header class="project-header-blue">
-    <div class="header-content">
-      <div class="project-info">
-        <h1 class="project-title">{{ project()?.name || '现代简约风格三居室设计' }}</h1>
-        <div class="project-meta">
-          <span class="project-status {{ getProjectStatusClass(project()?.status) }}">
-            {{ project()?.status || '进行中' }}
-          </span>
-          <span class="project-stage">当前阶段:{{ project()?.currentStage || '方案修改与确认' }}</span>
-          <span class="project-date">最后更新:{{ formatDate(currentDate()) }}</span>
+    <header class="project-header ios-header">
+      <div class="header-content">
+        <div class="project-info">
+          <h1 class="project-title">{{ project()?.name || '现代简约风格三居室设计' }}</h1>
+          <div class="project-meta">
+            <span class="project-status {{ getProjectStatusClass(project()?.status) }}">
+              {{ project()?.status || '进行中' }}
+            </span>
+            <span class="project-stage">当前阶段:{{ project()?.currentStage || '方案修改与确认' }}</span>
+            <span class="project-date">最后更新:{{ formatDate(currentDate()) }}</span>
+          </div>
+        </div>
+        <div class="header-actions">
+          <button class="secondary-btn btn-hover-effect">
+            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+              <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
+              <polyline points="14 2 14 8 20 8"></polyline>
+              <line x1="16" y1="13" x2="8" y2="13"></line>
+              <line x1="16" y1="17" x2="8" y2="17"></line>
+              <polyline points="10 9 9 9 8 9"></polyline>
+            </svg>
+            <span>导出报告</span>
+          </button>
+          <button class="primary-btn btn-hover-effect">
+            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+              <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path>
+              <polyline points="22 4 12 14.01 9 11.01"></polyline>
+            </svg>
+            <span>联系客户</span>
+          </button>
         </div>
       </div>
-      <div class="header-actions">
-        <button class="secondary-btn">
-          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-            <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
-            <polyline points="14 2 14 8 20 8"></polyline>
-            <line x1="16" y1="13" x2="8" y2="13"></line>
-            <line x1="16" y1="17" x2="8" y2="17"></line>
-            <polyline points="10 9 9 9 8 9"></polyline>
-          </svg>
-          <span>导出报告</span>
-        </button>
-        <button class="primary-btn">
-          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-            <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path>
-            <polyline points="22 4 12 14.01 9 11.01"></polyline>
-          </svg>
-          <span>联系客户</span>
-        </button>
-      </div>
-    </div>
-  </header>
+    </header>
 
   <!-- 主要内容区域 -->
-  <div class="main-content-area">
+  <div class="main-content-area ios-content">
     <!-- 主内容区 (居中) -->
-    <div class="project-content-main">
+    <div class="project-content-main ios-main">
       <!-- 项目进度卡片 -->
-      <div class="progress-card">
+      <div class="card progress-card">
         <div class="progress-header">
           <h3>项目进度</h3>
           <span class="progress-percentage">{{ completionProgress() }}%</span>
@@ -53,9 +53,60 @@
           <span>预计完成:{{ formatDate(project()?.deadline || '2023-07-15') }}</span>
         </div>
       </div>
+      
+      <!-- 历史服务记录 -->
+      <div class="card historical-records-card">
+        <div class="records-header">
+          <h3>历史服务记录</h3>
+        </div>
+        
+        <!-- 过往咨询记录 -->
+        <div class="record-section">
+          <h4>过往咨询记录</h4>
+          <div class="consultation-list">
+            <div class="consultation-item" *ngFor="let record of consultationRecords()">
+              <div class="consultation-date">{{ formatDate(record.date) }}</div>
+              <div class="consultation-content">{{ record.content }}</div>
+              <div class="consultation-status">{{ record.status }}</div>
+            </div>
+          </div>
+        </div>
+        
+        <!-- 合作项目 -->
+        <div class="record-section">
+          <h4>合作项目</h4>
+          <div class="projects-list">
+            <div class="project-item" *ngFor="let proj of cooperationProjects()">
+              <div class="project-name">{{ proj.name }}</div>
+              <div class="project-period">{{ formatDate(proj.startDate) }} - {{ formatDate(proj.endDate) }}</div>
+              <div class="project-description">{{ proj.description }}</div>
+              <div class="project-status">{{ proj.status }}</div>
+            </div>
+          </div>
+        </div>
+        
+        <!-- 历史反馈/评价 -->
+        <div class="record-section">
+          <h4>历史反馈/评价</h4>
+          <div class="feedback-list">
+            <div class="feedback-item" *ngFor="let feedback of historicalFeedbacks()">
+              <div class="feedback-date">{{ formatDate(feedback.date) }}</div>
+              <div class="feedback-rating">
+                <span *ngFor="let star of [1,2,3,4,5]">
+                  <i class="fa" [ngClass]="{ 'fa-star': star <= feedback.rating, 'fa-star-o': star > feedback.rating }" style="color: #FFD700;"></i>
+                </span>
+              </div>
+              <div class="feedback-content">{{ feedback.content }}</div>
+              <div class="feedback-response" *ngIf="feedback.response">
+                <strong>回复:</strong>{{ feedback.response }}
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
 
       <!-- 进度时间轴 -->
-      <div class="timeline-card">
+      <div class="card timeline-card">
         <h3 class="card-title">项目阶段时间轴</h3>
         <div class="project-timeline">
           <div *ngFor="let stage of projectStages; index as i" class="timeline-item" [class.stage-completed]="stage.completed" [class.stage-in-progress]="stage.inProgress">
@@ -90,87 +141,33 @@
       </div>
 
       <!-- 项目详情标签页 -->
-      <div class="project-tabs">
-        <div class="tab-header">
-          <button class="tab-btn" [class.active]="activeTab() === 'overview'" (click)="switchTab('overview')">
-            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-              <path d="M5 12h14M12 5l7 7-7 7"></path>
-            </svg>
-            <span>概览</span>
-          </button>
-          <button class="tab-btn" [class.active]="activeTab() === 'milestones'" (click)="switchTab('milestones')">
-            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-              <polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline>
-            </svg>
-            <span>里程碑</span>
-          </button>
-          <button class="tab-btn" [class.active]="activeTab() === 'tasks'" (click)="switchTab('tasks')">
-            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-              <polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline>
-            </svg>
-            <span>任务</span>
-          </button>
-          <button class="tab-btn" [class.active]="activeTab() === 'messages'" (click)="switchTab('messages')">
-            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-              <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"></path>
-            </svg>
-            <span>消息</span>
-          </button>
-          <button class="tab-btn" [class.active]="activeTab() === 'files'" (click)="switchTab('files')">
-            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-              <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
-              <polyline points="14 2 14 8 20 8"></polyline>
-              <line x1="16" y1="13" x2="8" y2="13"></line>
-              <line x1="16" y1="17" x2="8" y2="17"></line>
-              <polyline points="10 9 9 9 8 9"></polyline>
-            </svg>
-            <span>文件</span>
-          </button>
-        </div>
-
-      <!-- 项目进度卡片 -->
-      <div class="progress-card">
-        <div class="progress-header">
-          <h3>项目进度</h3>
-          <span class="progress-percentage">{{ completionProgress() }}%</span>
-        </div>
-        <div class="progress-bar">
-          <div class="progress-fill" [style.width]="progressFillWidth()"></div>
-        </div>
-        <div class="progress-meta">
-          <span>开始时间:{{ formatDate(project()?.createdAt || '2023-06-01') }}</span>
-          <span>预计完成:{{ formatDate(project()?.deadline || '2023-07-15') }}</span>
-        </div>
-      </div>
-
-      <!-- 项目详情标签页 -->
-      <div class="project-tabs">
+      <div class="project-tabs ios-tabs">
         <div class="tab-header">
-          <button class="tab-btn" [class.active]="activeTab() === 'overview'" (click)="switchTab('overview')">
+          <button class="tab-btn btn-hover-effect" [class.active]="activeTab() === 'overview'" (click)="switchTab('overview')">
             <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
               <path d="M5 12h14M12 5l7 7-7 7"></path>
             </svg>
             <span>概览</span>
           </button>
-          <button class="tab-btn" [class.active]="activeTab() === 'milestones'" (click)="switchTab('milestones')">
+          <button class="tab-btn btn-hover-effect" [class.active]="activeTab() === 'milestones'" (click)="switchTab('milestones')">
             <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
               <polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline>
             </svg>
             <span>里程碑</span>
           </button>
-          <button class="tab-btn" [class.active]="activeTab() === 'tasks'" (click)="switchTab('tasks')">
+          <button class="tab-btn btn-hover-effect" [class.active]="activeTab() === 'tasks'" (click)="switchTab('tasks')">
             <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
               <polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline>
             </svg>
             <span>任务</span>
           </button>
-          <button class="tab-btn" [class.active]="activeTab() === 'messages'" (click)="switchTab('messages')">
+          <button class="tab-btn btn-hover-effect" [class.active]="activeTab() === 'messages'" (click)="switchTab('messages')">
             <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
               <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"></path>
             </svg>
             <span>消息</span>
           </button>
-          <button class="tab-btn" [class.active]="activeTab() === 'files'" (click)="switchTab('files')">
+          <button class="tab-btn btn-hover-effect" [class.active]="activeTab() === 'files'" (click)="switchTab('files')">
             <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
               <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
               <polyline points="14 2 14 8 20 8"></polyline>
@@ -182,14 +179,24 @@
           </button>
         </div>
 
-        <!-- 消息标签内容 - 只保留一个消息输入区域 -->
+        <!-- 消息标签内容 -->
         <div *ngIf="activeTab() === 'messages'" class="tab-content">
           <div class="messages-container">
             <div class="messages-list">
-              <!-- 消息列表内容保持不变 -->
+              <div *ngFor="let message of messages()" class="message-item">
+                <div class="message-avatar">
+                  {{ message.sender.charAt(0) }}
+                </div>
+                <div class="message-content">
+                  <div class="message-header">
+                    <span class="message-sender">{{ message.sender }}</span>
+                    <span class="message-time">{{ formatDateTime(message.timestamp) }}</span>
+                  </div>
+                  <div class="message-text">{{ message.content }}</div>
+                </div>
+              </div>
             </div>
             <div class="message-input-area">
-              <!-- 只使用单向绑定 + input事件,移除双向绑定 -->
               <textarea 
                 [value]="newMessage()"
                 (input)="onMessageInput($event)"
@@ -199,14 +206,14 @@
                 (keydown.enter)="sendMessage()"
               ></textarea>
               <div class="message-actions">
-                <button class="secondary-btn">
+                <button class="secondary-btn btn-hover-effect">
                   <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
                     <path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"></path>
                     <polyline points="14 2 14 8 20 8"></polyline>
                   </svg>
                   <span>上传文件</span>
                 </button>
-                <button class="primary-btn" (click)="sendMessage()" [disabled]="!newMessage().trim()">
+                <button class="primary-btn btn-hover-effect" (click)="sendMessage()" [disabled]="!newMessage().trim()">
                   <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
                     <line x1="22" y1="2" x2="11" y2="13"></line>
                     <polygon points="22 2 15 22 11 13 2 9 22 2"></polygon>
@@ -291,29 +298,6 @@
               <h4 class="card-title">客户反馈</h4>
               <div class="feedback-list">
                 <div *ngFor="let feedback of feedbacks()" class="feedback-item">
-                  <!-- 修改前 -->
-                  <div class="feedback-item">
-                    <div class="feedback-header">
-                      <div class="feedback-author">{{ feedback?.customerName || '未知客户' }}</div>
-                      <div class="feedback-rating">
-                        <span class="rating-stars">★★★★☆</span>
-                        <span class="rating-number">{{ feedback?.rating || 0 }}.0</span>
-                      </div>
-                    </div>
-                    <div class="feedback-content">{{ feedback?.content || '' }}</div>
-                    <div class="feedback-response" *ngIf="feedback?.response">
-                      <div class="response-label">客服回复:</div>
-                      <div class="response-text">{{ feedback.response }}</div>
-                    </div>
-                    <div class="feedback-meta">
-                      <span class="feedback-date">{{ formatDate(feedback?.createdAt) }}</span>
-                      <span class="feedback-status" [class.status-processed]="feedback?.status === '已解决'" [class.status-pending]="feedback?.status === '待处理'">
-                        {{ feedback?.status || '未知状态' }}
-                      </span>
-                    </div>
-                  </div>
-                  
-                  <!-- 修改后 -->
                   <div class="feedback-item">
                     <div class="feedback-header">
                       <div class="feedback-author">{{ getFeedbackCustomerName(feedback) }}</div>
@@ -334,7 +318,8 @@
                       </span>
                     </div>
                   </div>
-                  <button class="view-all-btn" *ngIf="feedbacks().length > 0">查看全部反馈</button>
+                </div>
+                <button class="view-all-btn btn-hover-effect" *ngIf="feedbacks().length > 0">查看全部反馈</button>
               </div>
             </div>
           </div>
@@ -365,7 +350,7 @@
                   </div>
                 </div>
                 <div class="milestone-actions" *ngIf="!milestone.isCompleted">
-                  <button class="primary-btn small" (click)="completeMilestone(milestone.id)">
+                  <button class="primary-btn small btn-hover-effect" (click)="completeMilestone(milestone.id)">
                     <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor">
                       <polyline points="20 6 9 17 4 12"></polyline>
                     </svg>
@@ -514,12 +499,12 @@
                 </div>
               </div>
               <div class="file-actions">
-                <button class="action-btn" (click)="previewFile(file)" title="预览">
+                <button class="action-btn btn-hover-effect" (click)="previewFile(file)" title="预览">
                   <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
                     <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
                   </svg>
                 </button>
-                <button class="action-btn" (click)="downloadFile(file)" title="下载">
+                <button class="action-btn btn-hover-effect" (click)="downloadFile(file)" title="下载">
                   <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
                     <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
                     <polyline points="7 10 12 15 17 10"></polyline>
@@ -534,7 +519,7 @@
     </div>
 
     <!-- 右侧边栏 - 企业微信聊天集成 -->
-    <div class="wechat-sidebar">
+    <div class="wechat-sidebar ios-sidebar">
       <div class="wechat-header">
         <h3>项目群聊</h3>
         <div class="wechat-actions">
@@ -572,14 +557,14 @@
       <!-- 消息输入框 -->
       <div class="wechat-input-area">
         <div class="input-actions">
-          <button class="action-btn">
+          <button class="action-btn btn-hover-effect">
             <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
               <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
               <circle cx="8.5" cy="8.5" r="1.5"></circle>
               <polyline points="21 15 16 10 5 21"></polyline>
             </svg>
           </button>
-          <button class="action-btn">
+          <button class="action-btn btn-hover-effect">
             <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
               <path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"></path>
               <polyline points="14 2 14 8 20 8"></polyline>
@@ -593,7 +578,7 @@
           class="wechat-input"
           (keydown.enter)="sendWechatMessage()"
         />
-        <button class="send-btn" (click)="sendWechatMessage()" [disabled]="!wechatInput.trim()">
+        <button class="send-btn btn-hover-effect" (click)="sendWechatMessage()" [disabled]="!wechatInput.trim()">
           发送
         </button>
       </div>
@@ -601,16 +586,16 @@
   </div>
 
   <!-- 售后处理入口 (固定在底部) -->
-  <div class="after-sales-actions">
+  <div class="after-sales-actions ios-actions">
     <div class="actions-container">
-      <button class="action-btn primary" (click)="openModificationRequest()">
+      <button class="action-btn primary btn-hover-effect" (click)="openModificationRequest()">
         <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
           <path d="M12 20h9"></path>
           <path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"></path>
         </svg>
         <span>申请修改</span>
       </button>
-      <button class="action-btn warning" (click)="openComplaintWarning()">
+      <button class="action-btn warning btn-hover-effect" (click)="openComplaintWarning()">
         <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
           <path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path>
           <line x1="12" y1="9" x2="12" y2="13"></line>
@@ -618,7 +603,7 @@
         </svg>
         <span>投诉预警</span>
       </button>
-      <button class="action-btn secondary" (click)="openRefundRequest()">
+      <button class="action-btn secondary btn-hover-effect" (click)="openRefundRequest()">
         <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
           <path d="M22 12h-4l-3 9L9 3l-3 9H2"></path>
         </svg>

+ 201 - 23
src/app/pages/customer-service/project-detail/project-detail.scss

@@ -1,21 +1,111 @@
-// 全局变量
-$primary-color: #165DFF;
-$primary-dark: #0d2f5e;
-$success-color: #00B42A;
-$warning-color: #FF7D00;
-$danger-color: #F53F3F;
-$text-primary: #1D2129;
-$text-secondary: #4E5969;
-$text-tertiary: #86909C;
-$border-color: #E5E6EB;
-$background-primary: #FFFFFF;
-$background-secondary: #F2F3F5;
-$background-tertiary: #F7F8FA;
-$shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
-$shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08);
-$shadow-lg: 0 10px 30px rgba(0, 0, 0, 0.1);
-$border-radius: 8px;
-$transition: all 0.3s ease;
+// iOS风格全局变量
+$primary-color: #007AFF;
+$primary-dark: #0047AB;
+$success-color: #34C759;
+$warning-color: #FF9500;
+$danger-color: #FF3B30;
+$text-primary: #1C1C1E;
+$text-secondary: #636366;
+$text-tertiary: #8E8E93;
+$border-color: #D1D1D6;
+$background-primary: #F2F2F7;
+$background-secondary: #FFFFFF;
+$background-tertiary: #E5E5EA;
+$shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1);
+$shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
+$shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
+$border-radius: 12px;
+$transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
+
+// iOS风格卡片
+.card {
+  background: $background-secondary;
+  border-radius: $border-radius;
+  padding: 16px;
+  margin-bottom: 16px;
+  box-shadow: $shadow-sm;
+  transition: $transition;
+  
+  &:hover {
+    box-shadow: $shadow-md;
+  }
+}
+
+// iOS风格按钮
+.button {
+  border-radius: $border-radius;
+  padding: 12px 20px;
+  font-weight: 500;
+  transition: $transition;
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  
+  &-primary {
+    background: $primary-color;
+    color: white;
+    
+    &:active {
+      background: #0E42CB;
+    }
+  }
+  
+  &-secondary {
+    background: $background-tertiary;
+    color: $text-primary;
+    
+    &:active {
+      background: #D1D1D6;
+    }
+  }
+}
+
+// 按钮点击反馈效果
+.btn-hover-effect {
+  transition: $transition;
+  transform: scale(1);
+  position: relative;
+  overflow: hidden;
+}
+
+.btn-hover-effect:active {
+  transform: scale(0.95);
+  transition: all 0.1s ease;
+}
+
+.btn-hover-effect::after {
+  content: '';
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  width: 0;
+  height: 0;
+  border-radius: 50%;
+  background-color: rgba(255, 255, 255, 0.3);
+  transform: translate(-50%, -50%);
+  transition: width 0.6s, height 0.6s;
+}
+
+.btn-hover-effect:active::after {
+  width: 300px;
+  height: 300px;
+}
+
+// 主要按钮的特定点击效果
+.primary-btn.btn-hover-effect:active {
+  background-color: #0E42CB;
+}
+
+// 次要按钮的特定点击效果
+.secondary-btn.btn-hover-effect:active {
+  background-color: #E5E6EB;
+}
+
+// 操作按钮的特定点击效果
+.action-btn.btn-hover-effect:active {
+  transform: scale(0.9);
+  background-color: color-mix(in srgb, $primary-color 5%, transparent);
+}
 
 // 主容器
 .project-detail-container {
@@ -560,19 +650,81 @@ $transition: all 0.3s ease;
   padding: 20px;
 }
 
-// 动画效果
+// iOS风格动画效果
 @keyframes pulse {
   0% {
-    box-shadow: 0 0 0 0 rgba(22, 93, 255, 0.4);
+    transform: scale(1);
+    opacity: 1;
   }
-  70% {
-    box-shadow: 0 0 0 6px rgba(22, 93, 255, 0);
+  50% {
+    transform: scale(0.95);
+    opacity: 0.8;
   }
   100% {
-    box-shadow: 0 0 0 0 rgba(22, 93, 255, 0);
+    transform: scale(1);
+    opacity: 1;
+  }
+}
+
+@keyframes fadeIn {
+  from {
+    opacity: 0;
+    transform: translateY(10px);
+  }
+  to {
+    opacity: 1;
+    transform: translateY(0);
   }
 }
 
+@keyframes sending {
+  0% {
+    transform: scale(1);
+  }
+  50% {
+    transform: scale(0.9);
+  }
+  100% {
+    transform: scale(1);
+  }
+}
+
+@keyframes completing {
+  0% {
+    transform: translateY(0);
+    opacity: 1;
+  }
+  100% {
+    transform: translateY(-20px);
+    opacity: 0;
+  }
+}
+
+// 按钮发送动画
+.sending {
+  animation: sending 0.3s ease;
+}
+
+// 任务完成动画
+.completing {
+  animation: completing 0.5s ease forwards;
+}
+
+// 消息进入动画
+.message-item {
+  animation: fadeIn 0.3s ease;
+}
+
+// iOS风格弹性动画
+.ios-bounce {
+  transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
+}
+
+// 标签切换动画
+.tab-content {
+  animation: fadeIn 0.3s ease;
+}
+
 // 响应式设计
 @media (max-width: 1200px) {
   .wechat-sidebar {
@@ -1707,6 +1859,32 @@ $text-light: $text-tertiary;
   }
 }
 
+// iOS风格按钮点击效果
+.button {
+  &:active {
+    transform: scale(0.96);
+    transition: transform 0.1s ease;
+  }
+  
+  &-primary:active {
+    background: #0E42CB;
+  }
+  
+  &-secondary:active {
+    background: #D1D1D6;
+  }
+}
+
+// 卡片悬停效果
+.card {
+  transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
+  
+  &:hover {
+    transform: translateY(-2px);
+    box-shadow: $shadow-md;
+  }
+}
+
 // 响应式设计
 @media (max-width: 1024px) {
   .overview-grid {

+ 197 - 16
src/app/pages/customer-service/project-detail/project-detail.ts

@@ -2,8 +2,12 @@ import { Component, OnInit, signal, computed, ViewChild, AfterViewChecked } from
 import { CommonModule } from '@angular/common';
 import { FormsModule, ReactiveFormsModule } from '@angular/forms';
 import { RouterModule, ActivatedRoute } from '@angular/router';
+import { MatDialog, MatDialogModule } from '@angular/material/dialog';
 import { ProjectService } from '../../../services/project.service';
 import { Project, Task, Message, FileItem, CustomerFeedback, Milestone } from '../../../models/project.model';
+import { ModificationRequestDialog } from './modification-request-dialog';
+import { ComplaintWarningDialog } from './complaint-warning-dialog';
+import { RefundRequestDialog } from './refund-request-dialog';
 
 // 定义项目阶段接口
 interface ProjectStage {
@@ -23,10 +27,37 @@ interface WechatMessage {
   timestamp: Date;
 }
 
+// 定义历史咨询记录接口
+interface ConsultationRecord {
+  id: string;
+  date: Date;
+  content: string;
+  status: string;
+}
+
+// 定义合作项目接口
+interface CooperationProject {
+  id: string;
+  name: string;
+  startDate: Date;
+  endDate?: Date;
+  status: string;
+  description: string;
+}
+
+// 定义历史反馈接口
+interface HistoricalFeedback {
+  id: string;
+  date: Date;
+  content: string;
+  rating: number;
+  response?: string;
+}
+
 @Component({
   selector: 'app-project-detail',
   standalone: true,
-  imports: [CommonModule, FormsModule, ReactiveFormsModule, RouterModule],
+  imports: [CommonModule, FormsModule, ReactiveFormsModule, RouterModule, MatDialogModule],
   templateUrl: './project-detail.html',
   styleUrls: ['./project-detail.scss', '../customer-service-styles.scss']
 })
@@ -126,6 +157,11 @@ export class ProjectDetail implements OnInit, AfterViewChecked {
   wechatInput = '';
   scrollToBottom = false;
   
+  // 历史服务记录相关
+  consultationRecords = signal<ConsultationRecord[]>([]);
+  cooperationProjects = signal<CooperationProject[]>([]);
+  historicalFeedbacks = signal<HistoricalFeedback[]>([]);
+  
   // 售后处理弹窗状态
   showModificationRequest = false;
   showComplaintWarning = false;
@@ -149,7 +185,8 @@ export class ProjectDetail implements OnInit, AfterViewChecked {
   
   constructor(
     private route: ActivatedRoute,
-    private projectService: ProjectService
+    private projectService: ProjectService,
+    private dialog: MatDialog
   ) {
     // 获取路由参数中的项目ID
     this.route.paramMap.subscribe(params => {
@@ -180,6 +217,9 @@ export class ProjectDetail implements OnInit, AfterViewChecked {
     // 初始化企业微信聊天
     this.initWechatMessages();
     
+    // 初始化历史服务记录
+    this.initHistoricalServiceRecords();
+    
     // 模拟里程碑数据
     this.milestones.set([
       { 
@@ -417,9 +457,16 @@ export class ProjectDetail implements OnInit, AfterViewChecked {
     this.activeTab.set(tab);
   }
   
-  // 发送消息
+  // 增强版发送消息功能
   sendMessage(): void {
     if (this.newMessage().trim()) {
+      // 添加发送动画效果
+      const sendBtn = document.querySelector('.message-actions .primary-btn');
+      if (sendBtn) {
+        sendBtn.classList.add('sending');
+        setTimeout(() => sendBtn.classList.remove('sending'), 300);
+      }
+
       const newMsg: Message = {
         id: `msg${Date.now()}`,
         sender: '客服小李',
@@ -431,19 +478,36 @@ export class ProjectDetail implements OnInit, AfterViewChecked {
       
       this.messages.set([...this.messages(), newMsg]);
       this.newMessage.set('');
+      
+      // 自动滚动到底部
+      setTimeout(() => {
+        const container = document.querySelector('.messages-list');
+        if (container) {
+          container.scrollTop = container.scrollHeight;
+        }
+      }, 100);
     }
   }
   
-  // 完成任务
-  // 修复 completeTask 方法中的类型问题
+  // 增强版完成任务功能
   completeTask(taskId: string): void {
+    // 添加完成动画效果
+    const taskElement = document.querySelector(`.task-item[data-id="${taskId}"]`);
+    if (taskElement) {
+      taskElement.classList.add('completing');
+      setTimeout(() => taskElement.classList.remove('completing'), 500);
+    }
+
     this.tasks.set(
       this.tasks().map(task => 
         task.id === taskId 
-          ? { ...task, isCompleted: true, completedDate: new Date(), isOverdue: false } // 添加 isOverdue 确保类型安全
+          ? { ...task, isCompleted: true, completedDate: new Date(), isOverdue: false }
           : task
       )
     );
+    
+    // 播放完成音效
+    this.playSound('complete');
   }
   
   // 修复 completeMilestone 方法中的类型问题
@@ -594,10 +658,17 @@ export class ProjectDetail implements OnInit, AfterViewChecked {
     }, 100);
   }
   
-  // 发送企业微信消息
+  // 增强版发送企业微信消息
   sendWechatMessage(): void {
     if (!this.wechatInput.trim()) return;
     
+    // 添加发送动画
+    const sendBtn = document.querySelector('.wechat-input-area .send-btn');
+    if (sendBtn) {
+      sendBtn.classList.add('sending');
+      setTimeout(() => sendBtn.classList.remove('sending'), 300);
+    }
+    
     const newMessage: WechatMessage = {
       sender: '客服小李',
       content: this.wechatInput.trim(),
@@ -608,6 +679,9 @@ export class ProjectDetail implements OnInit, AfterViewChecked {
     this.wechatInput = '';
     this.scrollToBottom = true;
     
+    // 播放发送音效
+    this.playSound('message');
+    
     // 模拟对方回复
     setTimeout(() => {
       const replyMessage: WechatMessage = {
@@ -618,8 +692,80 @@ export class ProjectDetail implements OnInit, AfterViewChecked {
       
       this.wechatMessagesList = [...this.wechatMessagesList, replyMessage];
       this.scrollToBottom = true;
+      
+      // 播放接收音效
+      this.playSound('notification');
     }, 2000);
   }
+
+  // 新增播放音效方法
+  playSound(type: 'message' | 'notification' | 'complete'): void {
+    // 实际项目中这里会播放对应的音效
+    console.log(`播放${type}音效`);
+  }
+  
+  // 初始化历史服务记录
+  initHistoricalServiceRecords(): void {
+    // 模拟过往咨询记录
+    this.consultationRecords.set([
+      {
+        id: 'cons1',
+        date: new Date('2023-01-15'),
+        content: '咨询关于厨房改造的可行性和预算',
+        status: '已解决'
+      },
+      {
+        id: 'cons2',
+        date: new Date('2023-03-20'),
+        content: '询问装修材料环保认证相关问题',
+        status: '已解决'
+      },
+      {
+        id: 'cons3',
+        date: new Date('2023-05-10'),
+        content: '了解装修分期付款方案',
+        status: '已解决'
+      }
+    ]);
+    
+    // 模拟合作项目记录
+    this.cooperationProjects.set([
+      {
+        id: 'proj1',
+        name: '2022年现代简约卧室设计项目',
+        startDate: new Date('2022-08-15'),
+        endDate: new Date('2022-10-30'),
+        status: '已完成',
+        description: '为客户设计并实施了现代简约风格的卧室改造,包括定制衣柜和床头背景墙'
+      },
+      {
+        id: 'proj2',
+        name: '2023年欧式厨房设计项目',
+        startDate: new Date('2023-02-01'),
+        endDate: new Date('2023-04-15'),
+        status: '已完成',
+        description: '设计并安装了全套欧式风格厨房,包括橱柜、台面和电器选型'
+      }
+    ]);
+    
+    // 模拟历史反馈/评价
+    this.historicalFeedbacks.set([
+      {
+        id: 'fb1',
+        date: new Date('2022-11-05'),
+        content: '卧室设计非常满意,空间利用合理,风格符合预期',
+        rating: 5,
+        response: '感谢您的好评,我们会继续努力为您提供优质服务'
+      },
+      {
+        id: 'fb2',
+        date: new Date('2023-04-20'),
+        content: '厨房装修质量很好,但工期比预期稍长',
+        rating: 4,
+        response: '感谢您的反馈,我们已经优化了施工流程,会在后续项目中改进'
+      }
+    ]);
+  }
   
   // 格式化为时间显示
   formatTime(date: Date): string {
@@ -637,20 +783,55 @@ export class ProjectDetail implements OnInit, AfterViewChecked {
   
   // 售后处理入口方法
   openModificationRequest(): void {
-    this.showModificationRequest = true;
-    // 实际项目中这里可以打开申请修改的模态框或抽屉组件
-    console.log('打开申请修改弹窗');
+    const dialogRef = this.dialog.open(ModificationRequestDialog, {
+      width: '500px',
+      data: {
+        projectId: this.projectId,
+        projectName: this.project()?.name || '现代简约风格三居室设计',
+        projectStatus: this.project()?.status || '进行中'
+      }
+    });
+
+    dialogRef.afterClosed().subscribe(result => {
+      if (result) {
+        console.log('申请修改提交成功', result);
+        // 这里可以添加提交成功后的处理逻辑,如刷新数据、显示通知等
+      }
+    });
   }
   
   openComplaintWarning(): void {
-    this.showComplaintWarning = true;
-    // 实际项目中这里可以打开投诉预警的模态框或抽屉组件
-    console.log('打开投诉预警弹窗');
+    const dialogRef = this.dialog.open(ComplaintWarningDialog, {
+      width: '500px',
+      data: {
+        projectId: this.projectId,
+        projectName: this.project()?.name || '现代简约风格三居室设计'
+      }
+    });
+
+    dialogRef.afterClosed().subscribe(result => {
+      if (result) {
+        console.log('投诉预警提交成功', result);
+        // 这里可以添加提交成功后的处理逻辑
+      }
+    });
   }
   
   openRefundRequest(): void {
-    this.showRefundRequest = true;
-    // 实际项目中这里可以打开申请退款的模态框或抽屉组件
-    console.log('打开申请退款弹窗');
+    const dialogRef = this.dialog.open(RefundRequestDialog, {
+      width: '500px',
+      data: {
+        projectId: this.projectId,
+        projectName: this.project()?.name || '现代简约风格三居室设计',
+        projectAmount: 85000 // 模拟项目金额
+      }
+    });
+
+    dialogRef.afterClosed().subscribe(result => {
+      if (result) {
+        console.log('申请退款提交成功', result);
+        // 这里可以添加提交成功后的处理逻辑
+      }
+    });
   }
 }

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

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

+ 42 - 26
src/app/pages/customer-service/project-list/project-list.scss

@@ -611,6 +611,10 @@ $transition: all 0.3s ease;
           &:hover {
             background-color: #1565c0;
           }
+          
+          &:active {
+            background-color: #0E42CB;
+          }
         }
 
         .secondary-btn {
@@ -621,6 +625,10 @@ $transition: all 0.3s ease;
           &:hover {
             background-color: $border-color;
           }
+          
+          &:active {
+            background-color: #D1D1D6;
+          }
         }
 
         .card-action {
@@ -642,36 +650,44 @@ $transition: all 0.3s ease;
     box-shadow: $box-shadow;
 
     .pagination-btn {
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      width: 36px;
-      height: 36px;
-      border: 1px solid $border-color;
-      background-color: $bg-white;
-      color: $text-secondary;
-      border-radius: 4px;
-      cursor: pointer;
-      transition: $transition;
-      font-size: 14px;
-      font-weight: 500;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        width: 36px;
+        height: 36px;
+        border: 1px solid $border-color;
+        background-color: $bg-white;
+        color: $text-secondary;
+        border-radius: 4px;
+        cursor: pointer;
+        transition: $transition;
+        font-size: 14px;
+        font-weight: 500;
 
-      &:hover:not(:disabled) {
-        color: $primary-color;
-        border-color: $primary-color;
-      }
+        &:hover:not(:disabled) {
+          color: $primary-color;
+          border-color: $primary-color;
+        }
 
-      &.active {
-        color: white;
-        background-color: $primary-color;
-        border-color: $primary-color;
-      }
+        &:active:not(:disabled):not(.active) {
+          background-color: $primary-light;
+        }
 
-      &:disabled {
-        opacity: 0.5;
-        cursor: not-allowed;
+        &.active {
+          color: white;
+          background-color: $primary-color;
+          border-color: $primary-color;
+        }
+
+        &.active:active {
+          background-color: #0E42CB;
+        }
+
+        &:disabled {
+          opacity: 0.5;
+          cursor: not-allowed;
+        }
       }
-    }
 
     .pagination-ellipsis {
       font-size: 14px;

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

@@ -286,7 +286,7 @@ export class ProjectList implements OnInit {
   goToPage(page: number): void {
     if (page >= 1 && page <= this.totalPages() && page !== this.currentPage()) {
       this.currentPage.set(page);
-      this.loadProjects();
+      // 不需要重新加载整个项目列表,currentPage变更后computed属性会自动更新
     }
   }
 

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

@@ -446,4 +446,38 @@ export class ProjectService {
     console.log('记录材料下载:', materialId);
     return of(void 0);
   }
+
+  // 创建项目
+  createProject(projectData: {
+    customerId: string;
+    customerName: string;
+    requirement: any;
+    referenceCases: any[];
+    tags: {
+      demandType?: string;
+      preferenceTags?: string[];
+      followUpStatus?: string;
+    };
+  }): Observable<{ success: boolean; projectId: string }> {
+    // 模拟API调用
+    console.log('创建项目:', projectData);
+    // 模拟返回创建的项目ID
+    return of({ success: true, projectId: 'new-project-' + Date.now() });
+  }
+
+  // 创建项目群
+  createProjectGroup(groupData: {
+    customerId: string;
+    customerName: string;
+    tags?: {
+      demandType?: string;
+      preferenceTags?: string[];
+      followUpStatus?: string;
+    };
+  }): Observable<{ success: boolean; groupId: string }> {
+    // 模拟API调用
+    console.log('创建项目群:', groupData);
+    // 模拟返回创建的群组ID
+    return of({ success: true, groupId: 'new-group-' + Date.now() });
+  }
 }

+ 7 - 1
src/styles.scss

@@ -6,13 +6,16 @@
 // custom components at https://material.angular.dev/guide/theming
 @use '@angular/material' as mat;
 
+// 自定义字体配置
+@include mat.core();
+
 html {
   @include mat.theme((
     color: (
       primary: mat.$azure-palette,
       tertiary: mat.$blue-palette,
     ),
-    typography: Roboto,
+    // 不指定typography配置,避免自动加载
     density: 0,
   ));
 }
@@ -33,6 +36,9 @@ body {
   // Reset the user agent margin.
   margin: 0;
 }
+/* 从本地npm包引入Roboto字体,避免网络请求 */
+@import 'roboto-fontface/css/roboto/roboto-fontface.css';
+
 /* You can add global styles to this file, and also import other style files */
 
 html, body { height: 100%; }

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно