徐福静0235668 6 天之前
父节点
当前提交
d9581ba259
共有 30 个文件被更改,包括 5336 次插入40 次删除
  1. 4 4
      src/app/consumer/booking-recycle/booking-recycle.html
  2. 2 1
      src/app/consumer/booking-recycle/booking-recycle.ts
  3. 11 1
      src/app/consumer/consumer-routing-module.ts
  4. 138 0
      src/app/consumer/home/activities/activities.html
  5. 503 0
      src/app/consumer/home/activities/activities.scss
  6. 220 0
      src/app/consumer/home/activities/activities.ts
  7. 126 0
      src/app/consumer/home/ar-recognition/ar-recognition.html
  8. 736 0
      src/app/consumer/home/ar-recognition/ar-recognition.scss
  9. 127 0
      src/app/consumer/home/ar-recognition/ar-recognition.ts
  10. 146 0
      src/app/consumer/home/collectors/collectors.html
  11. 683 0
      src/app/consumer/home/collectors/collectors.scss
  12. 192 0
      src/app/consumer/home/collectors/collectors.ts
  13. 144 0
      src/app/consumer/home/drop-points/drop-points.html
  14. 551 0
      src/app/consumer/home/drop-points/drop-points.scss
  15. 198 0
      src/app/consumer/home/drop-points/drop-points.ts
  16. 155 18
      src/app/consumer/home/home.scss
  17. 6 13
      src/app/consumer/home/home.ts
  18. 47 0
      src/app/consumer/home/notifications/notifications.html
  19. 425 0
      src/app/consumer/home/notifications/notifications.scss
  20. 87 0
      src/app/consumer/home/notifications/notifications.ts
  21. 15 0
      src/app/consumer/points-mall/points-mall.html
  22. 51 0
      src/app/consumer/points-mall/points-mall.scss
  23. 7 1
      src/app/consumer/points-mall/points-mall.ts
  24. 94 0
      src/app/shared/components/svg-icons/svg-icons.component.ts
  25. 7 0
      src/assets/fonts/iconfont.css
  26. 10 0
      src/assets/fonts/iconfont.eot
  27. 158 2
      src/styles.scss
  28. 301 0
      src/styles/_mixins.scss
  29. 150 0
      src/styles/_variables.scss
  30. 42 0
      src/styles/iconfont.scss

+ 4 - 4
src/app/consumer/booking-recycle/booking-recycle.html

@@ -1,6 +1,6 @@
 <!-- 顶部标题栏 -->
 <div class="header">
-  <div class="back-btn" (click)="goBack()"><i class="fas fa-arrow-left"></i></div>
+  <div class="back-btn" (click)="goBack()"><app-svg-icon name="arrow-left" size="md" color="#2e7d32"></app-svg-icon></div>
   <div class="header-title">预约回收</div>
 </div>
 
@@ -8,7 +8,7 @@
   <!-- 废品信息填写 -->
   <div class="form-section">
     <div class="section-title">
-      <i class="fas fa-trash-alt"></i> 废品信息
+      <app-svg-icon name="trash" size="md" color="#2e7d32"></app-svg-icon> 废品信息
     </div>
     
     <div class="category-select">
@@ -33,7 +33,7 @@
     
     <div class="photo-upload">
       <div class="upload-btn" (click)="uploadPhoto()">
-        <i class="fas fa-camera"></i>
+        <app-svg-icon name="camera" size="md" color="#6c757d"></app-svg-icon>
         <div>上传照片</div>
       </div>
       <div 
@@ -41,7 +41,7 @@
         class="photo-preview">
         <img [src]="photo" alt="废品照片">
         <div class="remove-photo" (click)="removePhoto(i)">
-          <i class="fas fa-times"></i>
+          <app-svg-icon name="times" size="sm" color="#dc3545"></app-svg-icon>
         </div>
       </div>
     </div>

+ 2 - 1
src/app/consumer/booking-recycle/booking-recycle.ts

@@ -3,6 +3,7 @@ import { CommonModule } from '@angular/common';
 import { RouterModule, Router } from '@angular/router';
 import { FormsModule } from '@angular/forms';
 import { BottomNavComponent } from '../../shared/bottom-nav/bottom-nav.component';
+import { SvgIconComponent } from '../../shared/components/svg-icons/svg-icons.component';
 
 interface WasteCategory {
   id: string;
@@ -51,7 +52,7 @@ interface AdditionalService {
 @Component({
   selector: 'app-booking-recycle',
   standalone: true,
-  imports: [CommonModule, RouterModule, FormsModule, BottomNavComponent],
+  imports: [CommonModule, RouterModule, FormsModule, BottomNavComponent, SvgIconComponent],
   templateUrl: './booking-recycle.html',
   styleUrl: './booking-recycle.scss'
 })

+ 11 - 1
src/app/consumer/consumer-routing-module.ts

@@ -7,6 +7,11 @@ import { PointsMall } from './points-mall/points-mall';
 import { EcoKnowledge } from './eco-knowledge/eco-knowledge';
 import { AiAssistant } from './ai-assistant/ai-assistant';
 import { Profile } from './profile/profile';
+import { NotificationsComponent } from './home/notifications/notifications';
+import { ArRecognitionComponent } from './home/ar-recognition/ar-recognition';
+import { DropPointsComponent } from './home/drop-points/drop-points';
+import { CollectorsComponent } from './home/collectors/collectors';
+import { ActivitiesComponent } from './home/activities/activities';
 
 const routes: Routes = [
   { path: '', redirectTo: 'home', pathMatch: 'full' },
@@ -19,7 +24,12 @@ const routes: Routes = [
   { path: 'eco-knowledge', component: EcoKnowledge },
   { path: 'points-mall/eco-knowledge', component: EcoKnowledge },
   { path: 'ai-assistant', component: AiAssistant },
-  { path: 'profile', component: Profile }
+  { path: 'profile', component: Profile },
+  { path: 'notifications', component: NotificationsComponent },
+  { path: 'ar-recognition', component: ArRecognitionComponent },
+  { path: 'drop-points', component: DropPointsComponent },
+  { path: 'collectors', component: CollectorsComponent },
+  { path: 'activities', component: ActivitiesComponent }
 ];
 
 @NgModule({

+ 138 - 0
src/app/consumer/home/activities/activities.html

@@ -0,0 +1,138 @@
+<div class="activities-container">
+  <!-- Header -->
+  <div class="header">
+    <div class="header-left" (click)="goBack()">
+      <app-svg-icon name="arrow-left" class="back-icon"></app-svg-icon>
+    </div>
+    <div class="header-title">环保活动</div>
+    <div class="header-right">
+      <app-svg-icon name="calendar" class="calendar-icon"></app-svg-icon>
+    </div>
+  </div>
+
+  <!-- Tab Navigation -->
+  <div class="tab-navigation">
+    <div 
+      class="tab-item"
+      *ngFor="let tab of ['ongoing', 'upcoming', 'completed']"
+      [class.active]="selectedTab === tab"
+      (click)="switchTab(tab)"
+    >
+      <span class="tab-title">{{ getTabTitle(tab) }}</span>
+      <span class="tab-count">{{ getTabCount(tab) }}</span>
+    </div>
+  </div>
+
+  <!-- Activities List -->
+  <div class="activities-list">
+    <div class="activity-item" *ngFor="let activity of getCurrentActivities()">
+      <div class="activity-header">
+        <div class="activity-image">
+          <div class="image-placeholder">
+            <app-svg-icon [name]="getCategoryIcon(activity.category)" class="category-icon"></app-svg-icon>
+          </div>
+          <div 
+            class="status-badge" 
+            [style.background-color]="getStatusColor(activity.status)"
+          >
+            {{ getStatusText(activity.status) }}
+          </div>
+        </div>
+        
+        <div class="activity-info">
+          <h3 class="activity-title">{{ activity.title }}</h3>
+          <p class="activity-subtitle">{{ activity.subtitle }}</p>
+          <p class="activity-description">{{ activity.description }}</p>
+        </div>
+      </div>
+
+      <div class="activity-meta">
+        <div class="meta-item">
+          <app-svg-icon name="calendar" class="meta-icon"></app-svg-icon>
+          <span>{{ formatDate(activity.startDate) }} - {{ formatDate(activity.endDate) }}</span>
+        </div>
+        <div class="meta-item">
+          <app-svg-icon name="users" class="meta-icon"></app-svg-icon>
+          <span>{{ activity.participants }}/{{ activity.maxParticipants }} 人参与</span>
+        </div>
+      </div>
+
+      <div class="activity-progress" *ngIf="activity.status === 'ongoing'">
+        <div class="progress-info">
+          <span class="progress-label">活动进度</span>
+          <span class="progress-value">{{ activity.progress }}%</span>
+        </div>
+        <div class="progress-bar">
+          <div 
+            class="progress-fill" 
+            [style.width.%]="activity.progress"
+          ></div>
+        </div>
+      </div>
+
+      <div class="participation-rate">
+        <div class="rate-info">
+          <span class="rate-label">参与率</span>
+          <span class="rate-value">{{ getParticipationRate(activity) }}%</span>
+        </div>
+        <div class="rate-bar">
+          <div 
+            class="rate-fill" 
+            [style.width.%]="getParticipationRate(activity)"
+          ></div>
+        </div>
+      </div>
+
+      <div class="activity-tags">
+        <div class="tag" *ngFor="let tag of activity.tags">
+          {{ tag }}
+        </div>
+      </div>
+
+      <div class="activity-rewards">
+        <div class="rewards-title">
+          <app-svg-icon name="gift" class="gift-icon"></app-svg-icon>
+          <span>活动奖励</span>
+        </div>
+        <div class="rewards-list">
+          <div class="reward-item" *ngFor="let reward of activity.rewards">
+            <app-svg-icon name="check" class="check-icon"></app-svg-icon>
+            <span>{{ reward }}</span>
+          </div>
+        </div>
+      </div>
+
+      <div class="activity-actions">
+        <button class="action-btn secondary" (click)="viewActivityDetails(activity)">
+          查看详情
+        </button>
+        <button class="action-btn secondary" (click)="shareActivity(activity)">
+          <app-svg-icon name="share" class="share-icon"></app-svg-icon>
+          分享
+        </button>
+        <button 
+          class="action-btn primary" 
+          *ngIf="activity.status !== 'completed'"
+          [disabled]="activity.participants >= activity.maxParticipants"
+          (click)="joinActivity(activity)"
+        >
+          {{ activity.status === 'ongoing' ? '立即参与' : '预约参与' }}
+        </button>
+        <button 
+          class="action-btn disabled" 
+          *ngIf="activity.status === 'completed'"
+          disabled
+        >
+          活动已结束
+        </button>
+      </div>
+    </div>
+
+    <!-- Empty State -->
+    <div class="empty-state" *ngIf="getCurrentActivities().length === 0">
+      <app-svg-icon name="empty-activity" class="empty-icon"></app-svg-icon>
+      <div class="empty-text">暂无{{ getTabTitle(selectedTab) }}的活动</div>
+      <div class="empty-sub-text">请关注其他类型的活动或稍后再来查看</div>
+    </div>
+  </div>
+</div>

+ 503 - 0
src/app/consumer/home/activities/activities.scss

@@ -0,0 +1,503 @@
+@use '../../../../styles/variables' as *;
+@use '../../../../styles/mixins' as *;
+
+.activities-container {
+  min-height: 100vh;
+  background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
+  padding-bottom: 80px;
+
+  .header {
+    @include header-style();
+    background: rgba(255, 255, 255, 0.95);
+    backdrop-filter: blur(10px);
+    border-bottom: 1px solid rgba(0, 0, 0, 0.1);
+    box-shadow: 0 2px 20px rgba(0, 0, 0, 0.1);
+    position: sticky;
+    top: 0;
+    z-index: 100;
+    
+    .header-title {
+      font-weight: 600;
+      color: var(--text-primary);
+      font-size: 18px;
+    }
+    
+    .back-icon, .calendar-icon {
+      color: var(--primary-color);
+      transition: all 0.3s ease;
+      
+      &:hover {
+        transform: scale(1.1);
+        color: var(--primary-dark);
+      }
+    }
+  }
+
+  .tab-navigation {
+    display: flex;
+    background: rgba(255, 255, 255, 0.9);
+    margin: 20px 16px;
+    border-radius: 16px;
+    padding: 4px;
+    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
+    backdrop-filter: blur(10px);
+
+    .tab-item {
+      flex: 1;
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      padding: 12px 8px;
+      border-radius: 12px;
+      cursor: pointer;
+      transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+      position: relative;
+
+      .tab-title {
+        font-size: 14px;
+        font-weight: 500;
+        color: var(--text-secondary);
+        transition: all 0.3s ease;
+      }
+
+      .tab-count {
+        font-size: 12px;
+        color: var(--text-tertiary);
+        margin-top: 2px;
+        padding: 2px 8px;
+        background: rgba(0, 0, 0, 0.05);
+        border-radius: 10px;
+        transition: all 0.3s ease;
+      }
+
+      &.active {
+        background: linear-gradient(135deg, var(--primary-color), var(--primary-light));
+        transform: translateY(-2px);
+        box-shadow: 0 8px 25px rgba(var(--primary-rgb), 0.3);
+
+        .tab-title {
+          color: white;
+          font-weight: 600;
+        }
+
+        .tab-count {
+          background: rgba(255, 255, 255, 0.2);
+          color: white;
+        }
+      }
+
+      &:hover:not(.active) {
+        background: rgba(var(--primary-rgb), 0.1);
+        transform: translateY(-1px);
+      }
+    }
+  }
+
+  .activities-list {
+    padding: 0 16px;
+
+    .activity-item {
+      background: rgba(255, 255, 255, 0.95);
+      border-radius: 20px;
+      margin-bottom: 20px;
+      padding: 24px;
+      box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
+      backdrop-filter: blur(10px);
+      border: 1px solid rgba(255, 255, 255, 0.2);
+      transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
+      position: relative;
+      overflow: hidden;
+
+      &::before {
+        content: '';
+        position: absolute;
+        top: 0;
+        left: 0;
+        right: 0;
+        height: 4px;
+        background: linear-gradient(90deg, var(--primary-color), var(--secondary-color));
+        border-radius: 20px 20px 0 0;
+      }
+
+      &:hover {
+        transform: translateY(-8px);
+        box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
+      }
+
+      .activity-header {
+        display: flex;
+        gap: 16px;
+        margin-bottom: 20px;
+
+        .activity-image {
+          position: relative;
+          flex-shrink: 0;
+
+          .image-placeholder {
+            width: 80px;
+            height: 80px;
+            background: linear-gradient(135deg, var(--primary-light), var(--primary-color));
+            border-radius: 16px;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            box-shadow: 0 4px 20px rgba(var(--primary-rgb), 0.3);
+
+            .category-icon {
+              color: white;
+              font-size: 32px;
+            }
+          }
+
+          .status-badge {
+            position: absolute;
+            top: -8px;
+            right: -8px;
+            padding: 4px 8px;
+            border-radius: 12px;
+            font-size: 10px;
+            font-weight: 600;
+            color: white;
+            text-transform: uppercase;
+            letter-spacing: 0.5px;
+            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
+          }
+        }
+
+        .activity-info {
+          flex: 1;
+
+          .activity-title {
+            font-size: 18px;
+            font-weight: 700;
+            color: var(--text-primary);
+            margin-bottom: 8px;
+            line-height: 1.3;
+          }
+
+          .activity-subtitle {
+            font-size: 14px;
+            color: var(--primary-color);
+            font-weight: 600;
+            margin-bottom: 8px;
+          }
+
+          .activity-description {
+            font-size: 13px;
+            color: var(--text-secondary);
+            line-height: 1.5;
+            display: -webkit-box;
+          -webkit-line-clamp: 2;
+          line-clamp: 2;
+          -webkit-box-orient: vertical;
+          overflow: hidden;
+          }
+        }
+      }
+
+      .activity-meta {
+        display: flex;
+        gap: 20px;
+        margin-bottom: 16px;
+        flex-wrap: wrap;
+
+        .meta-item {
+          display: flex;
+          align-items: center;
+          gap: 6px;
+          font-size: 12px;
+          color: var(--text-secondary);
+          background: rgba(var(--primary-rgb), 0.1);
+          padding: 6px 12px;
+          border-radius: 20px;
+
+          .meta-icon {
+            color: var(--primary-color);
+            font-size: 14px;
+          }
+        }
+      }
+
+      .activity-progress, .participation-rate {
+        margin-bottom: 16px;
+
+        .progress-info, .rate-info {
+          display: flex;
+          justify-content: space-between;
+          align-items: center;
+          margin-bottom: 8px;
+
+          .progress-label, .rate-label {
+            font-size: 13px;
+            color: var(--text-secondary);
+            font-weight: 500;
+          }
+
+          .progress-value, .rate-value {
+            font-size: 13px;
+            font-weight: 700;
+            color: var(--primary-color);
+          }
+        }
+
+        .progress-bar, .rate-bar {
+          height: 6px;
+          background: rgba(var(--primary-rgb), 0.1);
+          border-radius: 3px;
+          overflow: hidden;
+
+          .progress-fill, .rate-fill {
+            height: 100%;
+            background: linear-gradient(90deg, var(--primary-color), var(--secondary-color));
+            border-radius: 3px;
+            transition: width 0.6s cubic-bezier(0.4, 0, 0.2, 1);
+          }
+        }
+      }
+
+      .activity-tags {
+        display: flex;
+        gap: 8px;
+        margin-bottom: 16px;
+        flex-wrap: wrap;
+
+        .tag {
+          padding: 4px 12px;
+          background: linear-gradient(135deg, rgba(var(--secondary-rgb), 0.1), rgba(var(--primary-rgb), 0.1));
+          color: var(--primary-color);
+          border-radius: 16px;
+          font-size: 11px;
+          font-weight: 600;
+          border: 1px solid rgba(var(--primary-rgb), 0.2);
+        }
+      }
+
+      .activity-rewards {
+        margin-bottom: 20px;
+        padding: 16px;
+        background: linear-gradient(135deg, rgba(var(--success-rgb), 0.05), rgba(var(--primary-rgb), 0.05));
+        border-radius: 12px;
+        border: 1px solid rgba(var(--success-rgb), 0.1);
+
+        .rewards-title {
+          display: flex;
+          align-items: center;
+          gap: 8px;
+          margin-bottom: 12px;
+          font-size: 14px;
+          font-weight: 600;
+          color: var(--success-color);
+
+          .gift-icon {
+            color: var(--success-color);
+          }
+        }
+
+        .rewards-list {
+          .reward-item {
+            display: flex;
+            align-items: center;
+            gap: 8px;
+            margin-bottom: 6px;
+            font-size: 12px;
+            color: var(--text-secondary);
+
+            .check-icon {
+              color: var(--success-color);
+              font-size: 14px;
+            }
+
+            &:last-child {
+              margin-bottom: 0;
+            }
+          }
+        }
+      }
+
+      .activity-actions {
+        display: flex;
+        gap: 12px;
+        flex-wrap: wrap;
+
+        .action-btn {
+          flex: 1;
+          min-width: 100px;
+          padding: 12px 20px;
+          border-radius: 12px;
+          font-size: 14px;
+          font-weight: 600;
+          border: none;
+          cursor: pointer;
+          transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          gap: 6px;
+
+          &.primary {
+            background: linear-gradient(135deg, var(--primary-color), var(--primary-dark));
+            color: white;
+            box-shadow: 0 4px 20px rgba(var(--primary-rgb), 0.3);
+
+            &:hover:not(:disabled) {
+              transform: translateY(-2px);
+              box-shadow: 0 8px 25px rgba(var(--primary-rgb), 0.4);
+            }
+          }
+
+          &.secondary {
+            background: rgba(var(--primary-rgb), 0.1);
+            color: var(--primary-color);
+            border: 1px solid rgba(var(--primary-rgb), 0.2);
+
+            &:hover {
+              background: rgba(var(--primary-rgb), 0.2);
+              transform: translateY(-1px);
+            }
+          }
+
+          &.disabled {
+            background: var(--background-secondary);
+            color: var(--text-tertiary);
+            cursor: not-allowed;
+          }
+
+          &:disabled {
+            opacity: 0.6;
+            cursor: not-allowed;
+            transform: none !important;
+          }
+
+          .share-icon {
+            font-size: 16px;
+          }
+        }
+      }
+    }
+
+    .empty-state {
+      text-align: center;
+      padding: 60px 20px;
+      background: rgba(255, 255, 255, 0.8);
+      border-radius: 20px;
+      margin: 40px 0;
+
+      .empty-icon {
+        font-size: 64px;
+        color: var(--text-tertiary);
+        margin-bottom: 20px;
+        opacity: 0.6;
+      }
+
+      .empty-text {
+        font-size: 16px;
+        font-weight: 600;
+        color: var(--text-secondary);
+        margin-bottom: 8px;
+      }
+
+      .empty-sub-text {
+        font-size: 14px;
+        color: var(--text-tertiary);
+        line-height: 1.5;
+      }
+    }
+  }
+}
+
+// 移动端响应式设计
+@include mobile {
+  .activities-container {
+    .header {
+      padding: 12px 16px;
+      
+      .header-title {
+        font-size: 16px;
+      }
+    }
+    
+    .tab-navigation {
+      margin: 16px 12px;
+      
+      .tab-item {
+        padding: 10px 6px;
+        
+        .tab-title {
+          font-size: 13px;
+        }
+        
+        .tab-count {
+          font-size: 11px;
+        }
+      }
+    }
+    
+    .activities-list {
+      padding: 0 12px;
+      
+      .activity-item {
+        padding: 20px;
+        margin-bottom: 16px;
+        
+        .activity-header {
+          gap: 12px;
+          
+          .activity-image .image-placeholder {
+            width: 60px;
+            height: 60px;
+            
+            .category-icon {
+              font-size: 24px;
+            }
+          }
+          
+          .activity-info {
+            .activity-title {
+              font-size: 16px;
+            }
+            
+            .activity-subtitle {
+              font-size: 13px;
+            }
+            
+            .activity-description {
+              font-size: 12px;
+            }
+          }
+        }
+        
+        .activity-meta {
+          gap: 12px;
+          
+          .meta-item {
+            font-size: 11px;
+            padding: 4px 8px;
+          }
+        }
+        
+        .activity-actions {
+          gap: 8px;
+          
+          .action-btn {
+            padding: 10px 16px;
+            font-size: 13px;
+            min-width: 80px;
+          }
+        }
+      }
+    }
+  }
+}
+
+// 平板端响应式设计
+@include tablet {
+  .activities-container {
+    .activities-list {
+      padding: 0 24px;
+      
+      .activity-item {
+        padding: 28px;
+      }
+    }
+  }
+}

+ 220 - 0
src/app/consumer/home/activities/activities.ts

@@ -0,0 +1,220 @@
+import { Component, OnInit } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { Router } from '@angular/router';
+import { SvgIconComponent } from '../../../shared/components/svg-icons/svg-icons.component';
+
+@Component({
+  selector: 'app-activities',
+  standalone: true,
+  imports: [CommonModule, SvgIconComponent],
+  templateUrl: './activities.html',
+  styleUrls: ['./activities.scss']
+})
+export class ActivitiesComponent implements OnInit {
+  selectedTab = 'ongoing';
+  
+  activities = {
+    ongoing: [
+      {
+        id: 1,
+        title: '绿色生活挑战赛',
+        subtitle: '21天环保习惯养成',
+        description: '参与21天环保挑战,每日完成环保任务,养成绿色生活习惯',
+        image: 'green-challenge.jpg',
+        startDate: '2024-01-01',
+        endDate: '2024-01-31',
+        participants: 1250,
+        maxParticipants: 2000,
+        status: 'ongoing',
+        rewards: ['环保达人徽章', '100积分奖励', '专属头像框'],
+        progress: 65,
+        category: 'challenge',
+        tags: ['环保', '挑战', '习惯养成']
+      },
+      {
+        id: 2,
+        title: '社区回收日',
+        subtitle: '每周六集中回收活动',
+        description: '每周六上午9-11点,社区集中回收活动,专业回收员现场指导',
+        image: 'community-recycle.jpg',
+        startDate: '2024-01-06',
+        endDate: '2024-12-31',
+        participants: 89,
+        maxParticipants: 150,
+        status: 'ongoing',
+        rewards: ['参与证书', '50积分', '环保小礼品'],
+        progress: 59,
+        category: 'community',
+        tags: ['社区', '回收', '定期活动']
+      }
+    ],
+    upcoming: [
+      {
+        id: 3,
+        title: '地球日特别活动',
+        subtitle: '保护地球,从我做起',
+        description: '4月22日地球日特别活动,线上线下结合,共同为地球环保贡献力量',
+        image: 'earth-day.jpg',
+        startDate: '2024-04-22',
+        endDate: '2024-04-22',
+        participants: 0,
+        maxParticipants: 5000,
+        status: 'upcoming',
+        rewards: ['地球守护者徽章', '200积分', '环保纪念品'],
+        progress: 0,
+        category: 'special',
+        tags: ['地球日', '特别活动', '环保']
+      },
+      {
+        id: 4,
+        title: '废物利用创意大赛',
+        subtitle: '变废为宝,创意无限',
+        description: '发挥创意,将废弃物品改造成实用或艺术品,展示环保创意',
+        image: 'creative-contest.jpg',
+        startDate: '2024-02-15',
+        endDate: '2024-03-15',
+        participants: 0,
+        maxParticipants: 500,
+        status: 'upcoming',
+        rewards: ['创意奖金', '作品展示', '媒体报道'],
+        progress: 0,
+        category: 'contest',
+        tags: ['创意', '比赛', '废物利用']
+      }
+    ],
+    completed: [
+      {
+        id: 5,
+        title: '新年环保决心',
+        subtitle: '2024新年环保目标设定',
+        description: '新年伊始,设定个人环保目标,记录环保行动,分享环保心得',
+        image: 'new-year-eco.jpg',
+        startDate: '2024-01-01',
+        endDate: '2024-01-07',
+        participants: 2340,
+        maxParticipants: 3000,
+        status: 'completed',
+        rewards: ['新年徽章', '80积分', '环保日历'],
+        progress: 100,
+        category: 'goal',
+        tags: ['新年', '目标', '环保决心']
+      }
+    ]
+  };
+
+  constructor(private router: Router) {}
+
+  ngOnInit() {}
+
+  goBack() {
+    this.router.navigate(['/consumer/home']);
+  }
+
+  switchTab(tab: string) {
+    this.selectedTab = tab;
+  }
+
+  getTabTitle(tab: string): string {
+    switch (tab) {
+      case 'ongoing':
+        return '进行中';
+      case 'upcoming':
+        return '即将开始';
+      case 'completed':
+        return '已结束';
+      default:
+        return '';
+    }
+  }
+
+  getTabCount(tab: string): number {
+    return this.activities[tab as keyof typeof this.activities]?.length || 0;
+  }
+
+  getCurrentActivities() {
+    return this.activities[this.selectedTab as keyof typeof this.activities] || [];
+  }
+
+  joinActivity(activity: any) {
+    if (activity.status === 'ongoing' || activity.status === 'upcoming') {
+      // 模拟加入活动
+      activity.participants += 1;
+      alert(`成功加入活动: ${activity.title}`);
+    }
+  }
+
+  viewActivityDetails(activity: any) {
+    // 可以导航到活动详情页面
+    console.log('查看活动详情:', activity);
+  }
+
+  shareActivity(activity: any) {
+    // 分享活动功能
+    if (navigator.share) {
+      navigator.share({
+        title: activity.title,
+        text: activity.description,
+        url: window.location.href
+      });
+    } else {
+      // 复制链接到剪贴板
+      navigator.clipboard.writeText(window.location.href);
+      alert('活动链接已复制到剪贴板');
+    }
+  }
+
+  getStatusText(status: string): string {
+    switch (status) {
+      case 'ongoing':
+        return '进行中';
+      case 'upcoming':
+        return '即将开始';
+      case 'completed':
+        return '已结束';
+      default:
+        return '';
+    }
+  }
+
+  getStatusColor(status: string): string {
+    switch (status) {
+      case 'ongoing':
+        return '#4CAF50';
+      case 'upcoming':
+        return '#2196F3';
+      case 'completed':
+        return '#9E9E9E';
+      default:
+        return '#9E9E9E';
+    }
+  }
+
+  getCategoryIcon(category: string): string {
+    switch (category) {
+      case 'challenge':
+        return 'challenge';
+      case 'community':
+        return 'community';
+      case 'special':
+        return 'special';
+      case 'contest':
+        return 'contest';
+      case 'goal':
+        return 'goal';
+      default:
+        return 'activity';
+    }
+  }
+
+  formatDate(dateString: string): string {
+    const date = new Date(dateString);
+    return date.toLocaleDateString('zh-CN', {
+      month: 'short',
+      day: 'numeric'
+    });
+  }
+
+  getParticipationRate(activity: any): number {
+    return Math.round((activity.participants / activity.maxParticipants) * 100);
+  }
+}

+ 126 - 0
src/app/consumer/home/ar-recognition/ar-recognition.html

@@ -0,0 +1,126 @@
+<div class="ar-recognition-container">
+  <!-- Header -->
+  <div class="header">
+    <div class="header-left" (click)="goBack()">
+      <app-svg-icon name="arrow-left" class="back-icon"></app-svg-icon>
+    </div>
+    <div class="header-title">AR智能识别</div>
+    <div class="header-right" (click)="viewHistory()">
+      <app-svg-icon name="history" class="history-icon"></app-svg-icon>
+    </div>
+  </div>
+
+  <!-- Camera View -->
+  <div class="camera-container">
+    <div class="camera-view" [class.scanning]="isScanning">
+      <div class="camera-overlay">
+        <div class="scan-frame">
+          <div class="corner top-left"></div>
+          <div class="corner top-right"></div>
+          <div class="corner bottom-left"></div>
+          <div class="corner bottom-right"></div>
+          
+          <div class="scan-line" *ngIf="isScanning"></div>
+        </div>
+        
+        <div class="scan-tips" *ngIf="!isScanning && !recognitionResult">
+          <app-svg-icon name="camera" class="camera-icon"></app-svg-icon>
+          <p>将垃圾物品放入框内进行识别</p>
+          <p class="sub-tip">支持塑料、纸张、金属、电池等物品识别</p>
+        </div>
+
+        <div class="scanning-tips" *ngIf="isScanning">
+          <div class="loading-spinner"></div>
+          <p>正在识别中...</p>
+          <p class="sub-tip">请保持物品清晰可见</p>
+        </div>
+      </div>
+    </div>
+
+    <!-- Recognition Result -->
+    <div class="recognition-result" *ngIf="recognitionResult">
+      <div class="result-header">
+        <h3>识别结果</h3>
+        <div class="confidence">置信度: {{ recognitionResult.confidence }}%</div>
+      </div>
+      
+      <div class="result-content">
+        <div class="item-info">
+          <div class="item-name">{{ recognitionResult.item }}</div>
+          <div class="item-category" [style.color]="getCategoryColor(recognitionResult.category)">
+            {{ recognitionResult.category }}
+          </div>
+          <div class="item-points">+{{ recognitionResult.points }} 积分</div>
+        </div>
+
+        <div class="recycling-info">
+          <h4>回收信息</h4>
+          <div class="info-item">
+            <span class="label">材质:</span>
+            <span class="value">{{ recognitionResult.recyclingInfo.material }}</span>
+          </div>
+          <div class="info-item">
+            <span class="label">可回收:</span>
+            <span class="value" [class.recyclable]="recognitionResult.recyclingInfo.recyclable">
+              {{ recognitionResult.recyclingInfo.recyclable ? '是' : '否' }}
+            </span>
+          </div>
+          <div class="info-item">
+            <span class="label">处理方式:</span>
+            <span class="value">{{ recognitionResult.recyclingInfo.process }}</span>
+          </div>
+        </div>
+
+        <div class="tips-section">
+          <h4>回收提示</h4>
+          <ul class="tips-list">
+            <li *ngFor="let tip of recognitionResult.tips">{{ tip }}</li>
+          </ul>
+        </div>
+      </div>
+
+      <div class="result-actions">
+        <button class="retry-btn" (click)="retryScanning()">重新识别</button>
+        <button class="confirm-btn" (click)="confirmRecognition()">确认回收</button>
+      </div>
+    </div>
+  </div>
+
+  <!-- Control Buttons -->
+  <div class="control-buttons" *ngIf="!recognitionResult">
+    <button 
+      class="scan-btn" 
+      [class.scanning]="isScanning"
+      (click)="isScanning ? stopScanning() : startScanning()"
+    >
+      <app-svg-icon [name]="isScanning ? 'stop' : 'scan'" class="btn-icon"></app-svg-icon>
+      <span>{{ isScanning ? '停止扫描' : '开始扫描' }}</span>
+    </button>
+  </div>
+
+  <!-- Scan History -->
+  <div class="scan-history" *ngIf="scanHistory.length > 0">
+    <div class="history-header">
+      <h3>扫描历史</h3>
+      <span class="history-count">{{ scanHistory.length }} 条记录</span>
+    </div>
+    
+    <div class="history-list">
+      <div class="history-item" *ngFor="let item of scanHistory.slice(0, 3)">
+        <div class="item-icon">
+          <app-svg-icon name="recycle" class="icon"></app-svg-icon>
+        </div>
+        <div class="item-details">
+          <div class="item-name">{{ item.item }}</div>
+          <div class="item-meta">
+            <span class="category" [style.color]="getCategoryColor(item.category)">
+              {{ item.category }}
+            </span>
+            <span class="time">{{ item.time }}</span>
+          </div>
+        </div>
+        <div class="item-points">+{{ item.points }}</div>
+      </div>
+    </div>
+  </div>
+</div>

+ 736 - 0
src/app/consumer/home/ar-recognition/ar-recognition.scss

@@ -0,0 +1,736 @@
+@use '../../../../styles/variables' as *;
+@use '../../../../styles/mixins' as *;
+
+.ar-recognition-container {
+  min-height: 100vh;
+  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+  padding-bottom: 80px;
+  position: relative;
+
+  .header {
+    @include header-style();
+    background: rgba(255, 255, 255, 0.95);
+    backdrop-filter: blur(15px);
+    border-bottom: 1px solid rgba(255, 255, 255, 0.2);
+    box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
+    position: sticky;
+    top: 0;
+    z-index: 100;
+    
+    .header-title {
+      font-weight: 700;
+      color: var(--text-primary);
+      font-size: 18px;
+      background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
+      -webkit-background-clip: text;
+      -webkit-text-fill-color: transparent;
+      background-clip: text;
+    }
+    
+    .back-icon, .history-icon {
+      color: var(--primary-color);
+      transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+      
+      &:hover {
+        transform: scale(1.2) rotate(5deg);
+        color: var(--primary-dark);
+      }
+    }
+  }
+
+  .camera-container {
+    padding: 20px 16px;
+    position: relative;
+
+    .camera-view {
+      position: relative;
+      width: 100%;
+      height: 400px;
+      background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%);
+      border-radius: 24px;
+      overflow: hidden;
+      box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
+      border: 3px solid rgba(255, 255, 255, 0.2);
+      transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
+
+      &.scanning {
+        box-shadow: 0 0 40px rgba(var(--primary-rgb), 0.6);
+        border-color: var(--primary-color);
+        animation: pulse 2s infinite;
+      }
+
+      .camera-overlay {
+        position: absolute;
+        top: 0;
+        left: 0;
+        right: 0;
+        bottom: 0;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        flex-direction: column;
+
+        .scan-frame {
+          position: relative;
+          width: 250px;
+          height: 250px;
+          border: 2px solid rgba(255, 255, 255, 0.3);
+          border-radius: 16px;
+
+          .corner {
+            position: absolute;
+            width: 30px;
+            height: 30px;
+            border: 3px solid var(--primary-light);
+            border-radius: 4px;
+
+            &.top-left {
+              top: -3px;
+              left: -3px;
+              border-right: none;
+              border-bottom: none;
+            }
+
+            &.top-right {
+              top: -3px;
+              right: -3px;
+              border-left: none;
+              border-bottom: none;
+            }
+
+            &.bottom-left {
+              bottom: -3px;
+              left: -3px;
+              border-right: none;
+              border-top: none;
+            }
+
+            &.bottom-right {
+              bottom: -3px;
+              right: -3px;
+              border-left: none;
+              border-top: none;
+            }
+          }
+
+          .scan-line {
+            position: absolute;
+            top: 0;
+            left: 0;
+            right: 0;
+            height: 3px;
+            background: linear-gradient(90deg, transparent, var(--primary-light), transparent);
+            animation: scanLine 2s linear infinite;
+            border-radius: 2px;
+            box-shadow: 0 0 10px var(--primary-light);
+          }
+        }
+
+        .scan-tips, .scanning-tips {
+          margin-top: 30px;
+          text-align: center;
+          color: white;
+
+          .camera-icon {
+            font-size: 48px;
+            margin-bottom: 16px;
+            opacity: 0.8;
+            animation: float 3s ease-in-out infinite;
+          }
+
+          .loading-spinner {
+            width: 40px;
+            height: 40px;
+            border: 3px solid rgba(255, 255, 255, 0.3);
+            border-top: 3px solid white;
+            border-radius: 50%;
+            animation: spin 1s linear infinite;
+            margin: 0 auto 16px;
+          }
+
+          p {
+            margin: 8px 0;
+            font-size: 16px;
+            font-weight: 600;
+            text-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
+
+            &.sub-tip {
+              font-size: 14px;
+              opacity: 0.8;
+              font-weight: 400;
+            }
+          }
+        }
+      }
+    }
+
+    .recognition-result {
+      margin-top: 24px;
+      background: rgba(255, 255, 255, 0.95);
+      border-radius: 20px;
+      padding: 24px;
+      box-shadow: 0 15px 50px rgba(0, 0, 0, 0.1);
+      backdrop-filter: blur(10px);
+      border: 1px solid rgba(255, 255, 255, 0.2);
+      animation: slideUp 0.5s cubic-bezier(0.4, 0, 0.2, 1);
+
+      .result-header {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        margin-bottom: 20px;
+        padding-bottom: 16px;
+        border-bottom: 2px solid rgba(var(--primary-rgb), 0.1);
+
+        h3 {
+          font-size: 20px;
+          font-weight: 700;
+          color: var(--text-primary);
+          margin: 0;
+        }
+
+        .confidence {
+          background: linear-gradient(135deg, var(--success-color), var(--success-light));
+          color: white;
+          padding: 6px 16px;
+          border-radius: 20px;
+          font-size: 12px;
+          font-weight: 600;
+          box-shadow: 0 4px 15px rgba(var(--success-rgb), 0.3);
+        }
+      }
+
+      .result-content {
+        .item-info {
+          text-align: center;
+          margin-bottom: 24px;
+          padding: 20px;
+          background: linear-gradient(135deg, rgba(var(--primary-rgb), 0.05), rgba(var(--secondary-rgb), 0.05));
+          border-radius: 16px;
+          border: 1px solid rgba(var(--primary-rgb), 0.1);
+
+          .item-name {
+            font-size: 24px;
+            font-weight: 700;
+            color: var(--text-primary);
+            margin-bottom: 8px;
+          }
+
+          .item-category {
+            font-size: 16px;
+            font-weight: 600;
+            margin-bottom: 12px;
+            padding: 6px 16px;
+            background: rgba(255, 255, 255, 0.8);
+            border-radius: 20px;
+            display: inline-block;
+          }
+
+          .item-points {
+            font-size: 18px;
+            font-weight: 700;
+            color: var(--success-color);
+            background: rgba(var(--success-rgb), 0.1);
+            padding: 8px 20px;
+            border-radius: 25px;
+            display: inline-block;
+            border: 2px solid rgba(var(--success-rgb), 0.2);
+          }
+        }
+
+        .recycling-info {
+          margin-bottom: 24px;
+          padding: 20px;
+          background: rgba(var(--info-rgb), 0.05);
+          border-radius: 16px;
+          border: 1px solid rgba(var(--info-rgb), 0.1);
+
+          h4 {
+            font-size: 16px;
+            font-weight: 600;
+            color: var(--info-color);
+            margin-bottom: 16px;
+            display: flex;
+            align-items: center;
+            gap: 8px;
+
+            &::before {
+              content: '♻️';
+              font-size: 18px;
+            }
+          }
+
+          .info-item {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            padding: 12px 0;
+            border-bottom: 1px solid rgba(var(--info-rgb), 0.1);
+
+            &:last-child {
+              border-bottom: none;
+            }
+
+            .label {
+              font-size: 14px;
+              color: var(--text-secondary);
+              font-weight: 500;
+            }
+
+            .value {
+              font-size: 14px;
+              font-weight: 600;
+              color: var(--text-primary);
+
+              &.recyclable {
+                color: var(--success-color);
+                background: rgba(var(--success-rgb), 0.1);
+                padding: 4px 12px;
+                border-radius: 12px;
+              }
+            }
+          }
+        }
+
+        .tips-section {
+          padding: 20px;
+          background: rgba(var(--warning-rgb), 0.05);
+          border-radius: 16px;
+          border: 1px solid rgba(var(--warning-rgb), 0.1);
+
+          h4 {
+            font-size: 16px;
+            font-weight: 600;
+            color: var(--warning-color);
+            margin-bottom: 16px;
+            display: flex;
+            align-items: center;
+            gap: 8px;
+
+            &::before {
+              content: '💡';
+              font-size: 18px;
+            }
+          }
+
+          .tips-list {
+            list-style: none;
+            padding: 0;
+            margin: 0;
+
+            li {
+              padding: 8px 0;
+              font-size: 14px;
+              color: var(--text-secondary);
+              line-height: 1.5;
+              position: relative;
+              padding-left: 20px;
+
+              &::before {
+                content: '•';
+                color: var(--warning-color);
+                font-weight: bold;
+                position: absolute;
+                left: 0;
+              }
+            }
+          }
+        }
+      }
+
+      .result-actions {
+        display: flex;
+        gap: 16px;
+        margin-top: 24px;
+
+        button {
+          flex: 1;
+          padding: 14px 24px;
+          border-radius: 16px;
+          font-size: 16px;
+          font-weight: 600;
+          border: none;
+          cursor: pointer;
+          transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+
+          &.retry-btn {
+            background: rgba(var(--text-rgb), 0.1);
+            color: var(--text-primary);
+            border: 2px solid rgba(var(--text-rgb), 0.2);
+
+            &:hover {
+              background: rgba(var(--text-rgb), 0.2);
+              transform: translateY(-2px);
+            }
+          }
+
+          &.confirm-btn {
+            background: linear-gradient(135deg, var(--success-color), var(--success-dark));
+            color: white;
+            box-shadow: 0 8px 25px rgba(var(--success-rgb), 0.3);
+
+            &:hover {
+              transform: translateY(-3px);
+              box-shadow: 0 12px 35px rgba(var(--success-rgb), 0.4);
+            }
+          }
+        }
+      }
+    }
+  }
+
+  .control-buttons {
+    padding: 0 16px;
+    margin-top: 24px;
+
+    .scan-btn {
+      width: 100%;
+      padding: 18px 24px;
+      background: linear-gradient(135deg, var(--primary-color), var(--primary-dark));
+      color: white;
+      border: none;
+      border-radius: 20px;
+      font-size: 18px;
+      font-weight: 700;
+      cursor: pointer;
+      transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      gap: 12px;
+      box-shadow: 0 10px 30px rgba(var(--primary-rgb), 0.4);
+      position: relative;
+      overflow: hidden;
+
+      &::before {
+        content: '';
+        position: absolute;
+        top: 0;
+        left: -100%;
+        width: 100%;
+        height: 100%;
+        background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
+        transition: left 0.5s;
+      }
+
+      &:hover {
+        transform: translateY(-3px);
+        box-shadow: 0 15px 40px rgba(var(--primary-rgb), 0.5);
+
+        &::before {
+          left: 100%;
+        }
+      }
+
+      &.scanning {
+        background: linear-gradient(135deg, var(--error-color), var(--error-dark));
+        animation: pulse 2s infinite;
+      }
+
+      .btn-icon {
+        font-size: 24px;
+      }
+    }
+  }
+
+  .scan-history {
+    margin: 32px 16px 0;
+    background: rgba(255, 255, 255, 0.95);
+    border-radius: 20px;
+    padding: 24px;
+    box-shadow: 0 15px 50px rgba(0, 0, 0, 0.1);
+    backdrop-filter: blur(10px);
+    border: 1px solid rgba(255, 255, 255, 0.2);
+
+    .history-header {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-bottom: 20px;
+      padding-bottom: 16px;
+      border-bottom: 2px solid rgba(var(--primary-rgb), 0.1);
+
+      h3 {
+        font-size: 18px;
+        font-weight: 700;
+        color: var(--text-primary);
+        margin: 0;
+      }
+
+      .history-count {
+        background: rgba(var(--primary-rgb), 0.1);
+        color: var(--primary-color);
+        padding: 6px 12px;
+        border-radius: 16px;
+        font-size: 12px;
+        font-weight: 600;
+      }
+    }
+
+    .history-list {
+      .history-item {
+        display: flex;
+        align-items: center;
+        gap: 16px;
+        padding: 16px 0;
+        border-bottom: 1px solid rgba(var(--border-rgb), 0.1);
+        transition: all 0.3s ease;
+
+        &:last-child {
+          border-bottom: none;
+        }
+
+        &:hover {
+          background: rgba(var(--primary-rgb), 0.05);
+          border-radius: 12px;
+          padding: 16px 12px;
+        }
+
+        .item-icon {
+          width: 48px;
+          height: 48px;
+          background: linear-gradient(135deg, var(--success-light), var(--success-color));
+          border-radius: 12px;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          box-shadow: 0 4px 15px rgba(var(--success-rgb), 0.3);
+
+          .icon {
+            color: white;
+            font-size: 24px;
+          }
+        }
+
+        .item-details {
+          flex: 1;
+
+          .item-name {
+            font-size: 16px;
+            font-weight: 600;
+            color: var(--text-primary);
+            margin-bottom: 4px;
+          }
+
+          .item-meta {
+            display: flex;
+            gap: 12px;
+            align-items: center;
+
+            .category {
+              font-size: 12px;
+              font-weight: 600;
+              padding: 4px 8px;
+              background: rgba(255, 255, 255, 0.8);
+              border-radius: 8px;
+            }
+
+            .time {
+              font-size: 12px;
+              color: var(--text-tertiary);
+            }
+          }
+        }
+
+        .item-points {
+          font-size: 14px;
+          font-weight: 700;
+          color: var(--success-color);
+          background: rgba(var(--success-rgb), 0.1);
+          padding: 6px 12px;
+          border-radius: 12px;
+        }
+      }
+    }
+  }
+}
+
+// 动画定义
+@keyframes scanLine {
+  0% {
+    top: 0;
+    opacity: 1;
+  }
+  50% {
+    opacity: 0.8;
+  }
+  100% {
+    top: calc(100% - 3px);
+    opacity: 1;
+  }
+}
+
+@keyframes spin {
+  0% {
+    transform: rotate(0deg);
+  }
+  100% {
+    transform: rotate(360deg);
+  }
+}
+
+@keyframes pulse {
+  0%, 100% {
+    transform: scale(1);
+  }
+  50% {
+    transform: scale(1.02);
+  }
+}
+
+@keyframes float {
+  0%, 100% {
+    transform: translateY(0);
+  }
+  50% {
+    transform: translateY(-10px);
+  }
+}
+
+@keyframes slideUp {
+  from {
+    opacity: 0;
+    transform: translateY(30px);
+  }
+  to {
+    opacity: 1;
+    transform: translateY(0);
+  }
+}
+
+// 移动端响应式设计
+@include mobile {
+  .ar-recognition-container {
+    .header {
+      padding: 12px 16px;
+      
+      .header-title {
+        font-size: 16px;
+      }
+    }
+    
+    .camera-container {
+      padding: 16px 12px;
+      
+      .camera-view {
+        height: 300px;
+        border-radius: 16px;
+        
+        .camera-overlay .scan-frame {
+          width: 200px;
+          height: 200px;
+          
+          .corner {
+            width: 24px;
+            height: 24px;
+          }
+        }
+        
+        .scan-tips, .scanning-tips {
+          margin-top: 20px;
+          
+          .camera-icon {
+            font-size: 36px;
+          }
+          
+          p {
+            font-size: 14px;
+            
+            &.sub-tip {
+              font-size: 12px;
+            }
+          }
+        }
+      }
+      
+      .recognition-result {
+        padding: 20px;
+        margin-top: 16px;
+        
+        .result-header h3 {
+          font-size: 18px;
+        }
+        
+        .result-content {
+          .item-info {
+            padding: 16px;
+            
+            .item-name {
+              font-size: 20px;
+            }
+            
+            .item-category {
+              font-size: 14px;
+            }
+            
+            .item-points {
+              font-size: 16px;
+            }
+          }
+          
+          .recycling-info, .tips-section {
+            padding: 16px;
+          }
+        }
+        
+        .result-actions button {
+          padding: 12px 20px;
+          font-size: 14px;
+        }
+      }
+    }
+    
+    .control-buttons .scan-btn {
+      padding: 16px 20px;
+      font-size: 16px;
+      
+      .btn-icon {
+        font-size: 20px;
+      }
+    }
+    
+    .scan-history {
+      margin: 24px 12px 0;
+      padding: 20px;
+      
+      .history-list .history-item {
+        gap: 12px;
+        
+        .item-icon {
+          width: 40px;
+          height: 40px;
+          
+          .icon {
+            font-size: 20px;
+          }
+        }
+        
+        .item-details .item-name {
+          font-size: 14px;
+        }
+      }
+    }
+  }
+}
+
+// 平板端响应式设计
+@include tablet {
+  .ar-recognition-container {
+    .camera-container {
+      padding: 24px 20px;
+      
+      .camera-view {
+        height: 450px;
+      }
+      
+      .recognition-result {
+        padding: 28px;
+      }
+    }
+    
+    .scan-history {
+      margin: 32px 20px 0;
+      padding: 28px;
+    }
+  }
+}

+ 127 - 0
src/app/consumer/home/ar-recognition/ar-recognition.ts

@@ -0,0 +1,127 @@
+import { Component, OnInit } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { Router } from '@angular/router';
+import { SvgIconComponent } from '../../../shared/components/svg-icons/svg-icons.component';
+
+@Component({
+  selector: 'app-ar-recognition',
+  standalone: true,
+  imports: [CommonModule, SvgIconComponent],
+  templateUrl: './ar-recognition.html',
+  styleUrls: ['./ar-recognition.scss']
+})
+export class ArRecognitionComponent implements OnInit {
+  isScanning = false;
+  recognitionResult: any = null;
+  scanHistory = [
+    {
+      id: 1,
+      item: '塑料瓶',
+      category: '可回收垃圾',
+      points: 5,
+      time: '2024-01-15 14:30',
+      image: 'plastic-bottle.jpg'
+    },
+    {
+      id: 2,
+      item: '废纸',
+      category: '可回收垃圾',
+      points: 3,
+      time: '2024-01-15 10:20',
+      image: 'paper.jpg'
+    },
+    {
+      id: 3,
+      item: '电池',
+      category: '有害垃圾',
+      points: 10,
+      time: '2024-01-14 16:45',
+      image: 'battery.jpg'
+    }
+  ];
+
+  constructor(private router: Router) {}
+
+  ngOnInit() {}
+
+  goBack() {
+    this.router.navigate(['/consumer/home']);
+  }
+
+  startScanning() {
+    this.isScanning = true;
+    this.recognitionResult = null;
+    
+    // 模拟扫描过程
+    setTimeout(() => {
+      this.simulateRecognition();
+    }, 3000);
+  }
+
+  stopScanning() {
+    this.isScanning = false;
+  }
+
+  simulateRecognition() {
+    this.isScanning = false;
+    this.recognitionResult = {
+      item: '塑料瓶',
+      category: '可回收垃圾',
+      points: 5,
+      confidence: 95,
+      tips: [
+        '请清洗干净后投放',
+        '可获得 5 积分奖励',
+        '建议压扁后投放节省空间'
+      ],
+      recyclingInfo: {
+        material: 'PET塑料',
+        recyclable: true,
+        process: '可制成纤维、地毯等产品'
+      }
+    };
+  }
+
+  confirmRecognition() {
+    if (this.recognitionResult) {
+      // 添加到历史记录
+      this.scanHistory.unshift({
+        id: Date.now(),
+        item: this.recognitionResult.item,
+        category: this.recognitionResult.category,
+        points: this.recognitionResult.points,
+        time: new Date().toLocaleString('zh-CN'),
+        image: 'scanned-item.jpg'
+      });
+      
+      this.recognitionResult = null;
+      
+      // 显示成功提示
+      alert(`识别成功!获得 ${this.recognitionResult?.points || 0} 积分`);
+    }
+  }
+
+  retryScanning() {
+    this.recognitionResult = null;
+    this.startScanning();
+  }
+
+  viewHistory() {
+    // 可以导航到历史记录页面或展开历史记录
+  }
+
+  getCategoryColor(category: string): string {
+    switch (category) {
+      case '可回收垃圾':
+        return '#4CAF50';
+      case '有害垃圾':
+        return '#F44336';
+      case '厨余垃圾':
+        return '#FF9800';
+      case '其他垃圾':
+        return '#9E9E9E';
+      default:
+        return '#2196F3';
+    }
+  }
+}

+ 146 - 0
src/app/consumer/home/collectors/collectors.html

@@ -0,0 +1,146 @@
+<div class="collectors-container">
+  <!-- Header -->
+  <div class="header">
+    <div class="header-left" (click)="goBack()">
+      <app-svg-icon name="arrow-left" class="back-icon"></app-svg-icon>
+    </div>
+    <div class="header-title">附近回收员</div>
+    <div class="header-right">
+      <app-svg-icon name="filter" class="filter-icon"></app-svg-icon>
+    </div>
+  </div>
+
+  <!-- Filter Tabs -->
+  <div class="filter-tabs">
+    <div class="tabs-container">
+      <div 
+        class="filter-tab"
+        *ngFor="let option of filterOptions"
+        [class.active]="selectedFilter === option.value"
+        (click)="selectFilter(option.value)"
+      >
+        <app-svg-icon [name]="option.icon" class="tab-icon"></app-svg-icon>
+        <span class="tab-label">{{ option.label }}</span>
+      </div>
+    </div>
+  </div>
+
+  <!-- Collectors List -->
+  <div class="collectors-list">
+    <div class="list-header">
+      <span class="result-count">找到 {{ filteredCollectors.length }} 位回收员</span>
+    </div>
+
+    <div class="collector-item" *ngFor="let collector of filteredCollectors">
+      <div class="collector-header">
+        <div class="collector-avatar">
+          <div class="avatar-placeholder">
+            <app-svg-icon name="user" class="avatar-icon"></app-svg-icon>
+          </div>
+          <div 
+            class="status-indicator" 
+            [style.background-color]="getStatusColor(collector.status)"
+          ></div>
+        </div>
+        
+        <div class="collector-info">
+          <div class="collector-name">{{ collector.name }}</div>
+          <div class="collector-meta">
+            <span class="rating">
+              <app-svg-icon name="star" class="star-icon"></app-svg-icon>
+              {{ collector.rating }} ({{ collector.reviewCount }})
+            </span>
+            <span class="distance">{{ collector.distance }}</span>
+            <span 
+              class="status" 
+              [style.color]="getStatusColor(collector.status)"
+            >
+              {{ getStatusText(collector.status) }}
+            </span>
+          </div>
+          <div class="collector-experience">{{ collector.experience }}</div>
+        </div>
+
+        <div class="collector-actions">
+          <button class="action-btn" (click)="callCollector(collector.phone)">
+            <app-svg-icon name="phone" class="action-icon"></app-svg-icon>
+          </button>
+          <button class="action-btn" (click)="chatWithCollector(collector)">
+            <app-svg-icon name="chat" class="action-icon"></app-svg-icon>
+          </button>
+        </div>
+      </div>
+
+      <div class="collector-specialties">
+        <span class="specialties-label">专业领域:</span>
+        <div class="specialties-list">
+          <div 
+            class="specialty-tag"
+            *ngFor="let specialty of collector.specialties"
+            [style.background-color]="getSpecialtyColor(specialty)"
+          >
+            {{ specialty }}
+          </div>
+        </div>
+      </div>
+
+      <div class="collector-details">
+        <div class="detail-row">
+          <div class="detail-item">
+            <app-svg-icon name="orders" class="detail-icon"></app-svg-icon>
+            <span>完成订单: {{ collector.completedOrders }}</span>
+          </div>
+          <div class="detail-item">
+            <app-svg-icon name="clock" class="detail-icon"></app-svg-icon>
+            <span>响应时间: {{ collector.responseTime }}</span>
+          </div>
+        </div>
+        
+        <div class="detail-row">
+          <div class="detail-item">
+            <app-svg-icon name="time" class="detail-icon"></app-svg-icon>
+            <span>工作时间: {{ collector.workingHours }}</span>
+          </div>
+          <div class="detail-item">
+            <app-svg-icon name="location" class="detail-icon"></app-svg-icon>
+            <span>服务区域: {{ collector.serviceArea }}</span>
+          </div>
+        </div>
+
+        <div class="detail-row">
+          <div class="detail-item full-width">
+            <app-svg-icon name="price" class="detail-icon"></app-svg-icon>
+            <span>{{ collector.priceRange }}</span>
+          </div>
+        </div>
+      </div>
+
+      <div class="collector-features">
+        <div class="feature-tag" *ngFor="let feature of collector.features">
+          <app-svg-icon name="check" class="feature-icon"></app-svg-icon>
+          {{ feature }}
+        </div>
+      </div>
+
+      <div class="collector-bottom">
+        <button class="profile-btn" (click)="viewCollectorProfile(collector)">
+          查看详情
+        </button>
+        <button 
+          class="book-btn" 
+          [disabled]="collector.status === 'offline'"
+          (click)="bookCollector(collector)"
+        >
+          立即预约
+        </button>
+      </div>
+    </div>
+
+    <!-- Empty State -->
+    <div class="empty-state" *ngIf="filteredCollectors.length === 0">
+      <app-svg-icon name="empty-user" class="empty-icon"></app-svg-icon>
+      <div class="empty-text">暂无符合条件的回收员</div>
+      <div class="empty-sub-text">请尝试调整筛选条件</div>
+    </div>
+  </div>
+</div>

+ 683 - 0
src/app/consumer/home/collectors/collectors.scss

@@ -0,0 +1,683 @@
+@use '../../../../styles/variables' as *;
+@use '../../../../styles/mixins' as *;
+
+.collectors-container {
+  min-height: 100vh;
+  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+  padding-bottom: 80px;
+  position: relative;
+
+  .header {
+    @include header-style();
+    background: rgba(255, 255, 255, 0.95);
+    backdrop-filter: blur(15px);
+    border-bottom: 1px solid rgba(255, 255, 255, 0.2);
+    box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
+    position: sticky;
+    top: 0;
+    z-index: 100;
+    
+    .header-title {
+      font-weight: 700;
+      color: var(--text-primary);
+      font-size: 18px;
+      background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
+      -webkit-background-clip: text;
+      -webkit-text-fill-color: transparent;
+      background-clip: text;
+    }
+    
+    .back-icon, .filter-icon {
+      color: var(--primary-color);
+      transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+      
+      &:hover {
+        transform: scale(1.2) rotate(5deg);
+        color: var(--primary-dark);
+      }
+    }
+  }
+
+  .filter-tabs {
+    padding: 20px 16px;
+    background: rgba(255, 255, 255, 0.1);
+    backdrop-filter: blur(10px);
+    border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+
+    .tabs-container {
+      display: flex;
+      gap: 12px;
+      overflow-x: auto;
+      padding-bottom: 4px;
+
+      &::-webkit-scrollbar {
+        height: 4px;
+      }
+
+      &::-webkit-scrollbar-track {
+        background: rgba(255, 255, 255, 0.1);
+        border-radius: 2px;
+      }
+
+      &::-webkit-scrollbar-thumb {
+        background: rgba(255, 255, 255, 0.3);
+        border-radius: 2px;
+      }
+
+      .filter-tab {
+        display: flex;
+        align-items: center;
+        gap: 8px;
+        padding: 12px 20px;
+        background: rgba(255, 255, 255, 0.2);
+        border-radius: 25px;
+        border: 2px solid transparent;
+        cursor: pointer;
+        transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+        white-space: nowrap;
+        min-width: fit-content;
+
+        .tab-icon {
+          font-size: 18px;
+          color: rgba(255, 255, 255, 0.8);
+          transition: all 0.3s ease;
+        }
+
+        .tab-label {
+          font-size: 14px;
+          font-weight: 600;
+          color: rgba(255, 255, 255, 0.9);
+          transition: all 0.3s ease;
+        }
+
+        &:hover {
+          background: rgba(255, 255, 255, 0.3);
+          transform: translateY(-2px);
+          box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
+        }
+
+        &.active {
+          background: rgba(255, 255, 255, 0.95);
+          border-color: var(--primary-color);
+          box-shadow: 0 8px 30px rgba(var(--primary-rgb), 0.3);
+
+          .tab-icon {
+            color: var(--primary-color);
+          }
+
+          .tab-label {
+            color: var(--text-primary);
+          }
+        }
+      }
+    }
+  }
+
+  .collectors-list {
+    padding: 20px 16px;
+
+    .list-header {
+      margin-bottom: 20px;
+      padding: 16px 20px;
+      background: rgba(255, 255, 255, 0.95);
+      border-radius: 16px;
+      backdrop-filter: blur(10px);
+      box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
+
+      .result-count {
+        font-size: 16px;
+        font-weight: 600;
+        color: var(--text-primary);
+        background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
+        -webkit-background-clip: text;
+        -webkit-text-fill-color: transparent;
+        background-clip: text;
+      }
+    }
+
+    .collector-item {
+      background: rgba(255, 255, 255, 0.95);
+      border-radius: 20px;
+      padding: 24px;
+      margin-bottom: 20px;
+      box-shadow: 0 15px 50px rgba(0, 0, 0, 0.1);
+      backdrop-filter: blur(10px);
+      border: 1px solid rgba(255, 255, 255, 0.2);
+      transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
+      position: relative;
+      overflow: hidden;
+
+      &::before {
+        content: '';
+        position: absolute;
+        top: 0;
+        left: 0;
+        right: 0;
+        height: 4px;
+        background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
+        border-radius: 20px 20px 0 0;
+      }
+
+      &:hover {
+        transform: translateY(-8px);
+        box-shadow: 0 25px 70px rgba(0, 0, 0, 0.15);
+      }
+
+      .collector-header {
+        display: flex;
+        align-items: flex-start;
+        gap: 16px;
+        margin-bottom: 20px;
+
+        .collector-avatar {
+          position: relative;
+          flex-shrink: 0;
+
+          .avatar-placeholder {
+            width: 60px;
+            height: 60px;
+            background: linear-gradient(135deg, var(--primary-light), var(--primary-color));
+            border-radius: 50%;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            box-shadow: 0 8px 25px rgba(var(--primary-rgb), 0.3);
+
+            .avatar-icon {
+              font-size: 28px;
+              color: white;
+            }
+          }
+
+          .status-indicator {
+            position: absolute;
+            bottom: 2px;
+            right: 2px;
+            width: 18px;
+            height: 18px;
+            border-radius: 50%;
+            border: 3px solid white;
+            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
+          }
+        }
+
+        .collector-info {
+          flex: 1;
+          min-width: 0;
+
+          .collector-name {
+            font-size: 20px;
+            font-weight: 700;
+            color: var(--text-primary);
+            margin-bottom: 8px;
+          }
+
+          .collector-meta {
+            display: flex;
+            flex-wrap: wrap;
+            gap: 16px;
+            margin-bottom: 8px;
+
+            .rating {
+              display: flex;
+              align-items: center;
+              gap: 4px;
+              font-size: 14px;
+              font-weight: 600;
+              color: var(--warning-color);
+
+              .star-icon {
+                font-size: 16px;
+                color: var(--warning-color);
+              }
+            }
+
+            .distance {
+              font-size: 14px;
+              color: var(--text-secondary);
+              font-weight: 500;
+            }
+
+            .status {
+              font-size: 12px;
+              font-weight: 600;
+              padding: 4px 12px;
+              border-radius: 12px;
+              background: rgba(var(--success-rgb), 0.1);
+            }
+          }
+
+          .collector-experience {
+            font-size: 14px;
+            color: var(--text-secondary);
+            font-style: italic;
+          }
+        }
+
+        .collector-actions {
+          display: flex;
+          gap: 8px;
+          flex-shrink: 0;
+
+          .action-btn {
+            width: 44px;
+            height: 44px;
+            border-radius: 12px;
+            border: none;
+            background: rgba(var(--primary-rgb), 0.1);
+            color: var(--primary-color);
+            cursor: pointer;
+            transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+            display: flex;
+            align-items: center;
+            justify-content: center;
+
+            .action-icon {
+              font-size: 20px;
+            }
+
+            &:hover {
+              background: var(--primary-color);
+              color: white;
+              transform: scale(1.1);
+              box-shadow: 0 8px 25px rgba(var(--primary-rgb), 0.3);
+            }
+          }
+        }
+      }
+
+      .collector-specialties {
+        margin-bottom: 20px;
+        padding: 16px;
+        background: rgba(var(--info-rgb), 0.05);
+        border-radius: 12px;
+        border: 1px solid rgba(var(--info-rgb), 0.1);
+
+        .specialties-label {
+          font-size: 14px;
+          font-weight: 600;
+          color: var(--info-color);
+          margin-bottom: 12px;
+          display: block;
+        }
+
+        .specialties-list {
+          display: flex;
+          flex-wrap: wrap;
+          gap: 8px;
+
+          .specialty-tag {
+            padding: 6px 12px;
+            border-radius: 16px;
+            font-size: 12px;
+            font-weight: 600;
+            color: white;
+            background: linear-gradient(135deg, var(--success-color), var(--success-dark));
+            box-shadow: 0 4px 15px rgba(var(--success-rgb), 0.3);
+            transition: all 0.3s ease;
+
+            &:hover {
+              transform: translateY(-2px);
+              box-shadow: 0 6px 20px rgba(var(--success-rgb), 0.4);
+            }
+          }
+        }
+      }
+
+      .collector-details {
+        margin-bottom: 20px;
+
+        .detail-row {
+          display: flex;
+          gap: 16px;
+          margin-bottom: 12px;
+
+          &:last-child {
+            margin-bottom: 0;
+          }
+
+          .detail-item {
+            flex: 1;
+            display: flex;
+            align-items: center;
+            gap: 8px;
+            padding: 12px;
+            background: rgba(var(--background-rgb), 0.5);
+            border-radius: 12px;
+            border: 1px solid rgba(var(--border-rgb), 0.1);
+
+            &.full-width {
+              flex: none;
+              width: 100%;
+            }
+
+            .detail-icon {
+              font-size: 16px;
+              color: var(--primary-color);
+              flex-shrink: 0;
+            }
+
+            span {
+              font-size: 13px;
+              color: var(--text-secondary);
+              font-weight: 500;
+            }
+          }
+        }
+      }
+
+      .collector-features {
+        display: flex;
+        flex-wrap: wrap;
+        gap: 8px;
+        margin-bottom: 20px;
+
+        .feature-tag {
+          display: flex;
+          align-items: center;
+          gap: 6px;
+          padding: 8px 12px;
+          background: rgba(var(--success-rgb), 0.1);
+          border-radius: 20px;
+          font-size: 12px;
+          font-weight: 600;
+          color: var(--success-color);
+          border: 1px solid rgba(var(--success-rgb), 0.2);
+
+          .feature-icon {
+            font-size: 14px;
+          }
+        }
+      }
+
+      .collector-bottom {
+        display: flex;
+        gap: 12px;
+        padding-top: 16px;
+        border-top: 2px solid rgba(var(--border-rgb), 0.1);
+
+        .profile-btn {
+          flex: 1;
+          padding: 14px 20px;
+          background: rgba(var(--text-rgb), 0.1);
+          color: var(--text-primary);
+          border: 2px solid rgba(var(--text-rgb), 0.2);
+          border-radius: 16px;
+          font-size: 14px;
+          font-weight: 600;
+          cursor: pointer;
+          transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+
+          &:hover {
+            background: rgba(var(--text-rgb), 0.2);
+            transform: translateY(-2px);
+            box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
+          }
+        }
+
+        .book-btn {
+          flex: 2;
+          padding: 14px 20px;
+          background: linear-gradient(135deg, var(--primary-color), var(--primary-dark));
+          color: white;
+          border: none;
+          border-radius: 16px;
+          font-size: 14px;
+          font-weight: 700;
+          cursor: pointer;
+          transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+          box-shadow: 0 8px 25px rgba(var(--primary-rgb), 0.3);
+
+          &:hover:not(:disabled) {
+            transform: translateY(-3px);
+            box-shadow: 0 12px 35px rgba(var(--primary-rgb), 0.4);
+          }
+
+          &:disabled {
+            background: rgba(var(--text-rgb), 0.3);
+            cursor: not-allowed;
+            box-shadow: none;
+          }
+        }
+      }
+    }
+
+    .empty-state {
+      text-align: center;
+      padding: 60px 20px;
+      background: rgba(255, 255, 255, 0.95);
+      border-radius: 20px;
+      backdrop-filter: blur(10px);
+      box-shadow: 0 15px 50px rgba(0, 0, 0, 0.1);
+
+      .empty-icon {
+        font-size: 80px;
+        color: var(--text-tertiary);
+        margin-bottom: 24px;
+        opacity: 0.6;
+      }
+
+      .empty-text {
+        font-size: 18px;
+        font-weight: 600;
+        color: var(--text-primary);
+        margin-bottom: 8px;
+      }
+
+      .empty-sub-text {
+        font-size: 14px;
+        color: var(--text-secondary);
+      }
+    }
+  }
+}
+
+// 移动端响应式设计
+@include mobile {
+  .collectors-container {
+    .header {
+      padding: 12px 16px;
+      
+      .header-title {
+        font-size: 16px;
+      }
+    }
+
+    .filter-tabs {
+      padding: 16px 12px;
+
+      .tabs-container {
+        gap: 8px;
+
+        .filter-tab {
+          padding: 10px 16px;
+
+          .tab-icon {
+            font-size: 16px;
+          }
+
+          .tab-label {
+            font-size: 12px;
+          }
+        }
+      }
+    }
+
+    .collectors-list {
+      padding: 16px 12px;
+
+      .list-header {
+        padding: 12px 16px;
+        margin-bottom: 16px;
+
+        .result-count {
+          font-size: 14px;
+        }
+      }
+
+      .collector-item {
+        padding: 20px;
+        margin-bottom: 16px;
+
+        .collector-header {
+          gap: 12px;
+          margin-bottom: 16px;
+
+          .collector-avatar .avatar-placeholder {
+            width: 50px;
+            height: 50px;
+
+            .avatar-icon {
+              font-size: 24px;
+            }
+          }
+
+          .collector-info {
+            .collector-name {
+              font-size: 18px;
+            }
+
+            .collector-meta {
+              gap: 12px;
+
+              .rating, .distance, .status {
+                font-size: 12px;
+              }
+            }
+
+            .collector-experience {
+              font-size: 12px;
+            }
+          }
+
+          .collector-actions {
+            gap: 6px;
+
+            .action-btn {
+              width: 40px;
+              height: 40px;
+
+              .action-icon {
+                font-size: 18px;
+              }
+            }
+          }
+        }
+
+        .collector-specialties {
+          padding: 12px;
+          margin-bottom: 16px;
+
+          .specialties-label {
+            font-size: 12px;
+            margin-bottom: 8px;
+          }
+
+          .specialties-list {
+            gap: 6px;
+
+            .specialty-tag {
+              padding: 4px 8px;
+              font-size: 11px;
+            }
+          }
+        }
+
+        .collector-details {
+          margin-bottom: 16px;
+
+          .detail-row {
+            flex-direction: column;
+            gap: 8px;
+
+            .detail-item {
+              padding: 10px;
+
+              .detail-icon {
+                font-size: 14px;
+              }
+
+              span {
+                font-size: 12px;
+              }
+            }
+          }
+        }
+
+        .collector-features {
+          gap: 6px;
+          margin-bottom: 16px;
+
+          .feature-tag {
+            padding: 6px 10px;
+            font-size: 11px;
+
+            .feature-icon {
+              font-size: 12px;
+            }
+          }
+        }
+
+        .collector-bottom {
+          gap: 8px;
+          padding-top: 12px;
+
+          .profile-btn, .book-btn {
+            padding: 12px 16px;
+            font-size: 12px;
+          }
+        }
+      }
+
+      .empty-state {
+        padding: 40px 16px;
+
+        .empty-icon {
+          font-size: 60px;
+          margin-bottom: 16px;
+        }
+
+        .empty-text {
+          font-size: 16px;
+        }
+
+        .empty-sub-text {
+          font-size: 12px;
+        }
+      }
+    }
+  }
+}
+
+// 平板端响应式设计
+@include tablet {
+  .collectors-container {
+    .filter-tabs {
+      padding: 24px 20px;
+    }
+
+    .collectors-list {
+      padding: 24px 20px;
+
+      .collector-item {
+        padding: 28px;
+        margin-bottom: 24px;
+
+        .collector-header {
+          .collector-avatar .avatar-placeholder {
+            width: 70px;
+            height: 70px;
+
+            .avatar-icon {
+              font-size: 32px;
+            }
+          }
+
+          .collector-info .collector-name {
+            font-size: 22px;
+          }
+        }
+      }
+    }
+  }
+}

+ 192 - 0
src/app/consumer/home/collectors/collectors.ts

@@ -0,0 +1,192 @@
+import { Component, OnInit } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { Router } from '@angular/router';
+import { SvgIconComponent } from '../../../shared/components/svg-icons/svg-icons.component';
+
+@Component({
+  selector: 'app-collectors',
+  standalone: true,
+  imports: [CommonModule, SvgIconComponent],
+  templateUrl: './collectors.html',
+  styleUrls: ['./collectors.scss']
+})
+export class CollectorsComponent implements OnInit {
+  selectedFilter = 'all';
+  
+  collectors = [
+    {
+      id: 1,
+      name: '张师傅',
+      avatar: 'avatar1.jpg',
+      rating: 4.9,
+      reviewCount: 156,
+      distance: '0.8km',
+      status: 'online',
+      specialties: ['电子产品', '金属回收', '家具回收'],
+      experience: '5年经验',
+      phone: '138****1234',
+      completedOrders: 1250,
+      responseTime: '5分钟内',
+      workingHours: '08:00-20:00',
+      serviceArea: '朝阳区、海淀区',
+      priceRange: '上门费: ¥10-20',
+      features: ['免费评估', '上门回收', '当场结算']
+    },
+    {
+      id: 2,
+      name: '李师傅',
+      avatar: 'avatar2.jpg',
+      rating: 4.8,
+      reviewCount: 203,
+      distance: '1.2km',
+      status: 'online',
+      specialties: ['纸张回收', '塑料回收', '玻璃回收'],
+      experience: '8年经验',
+      phone: '139****5678',
+      completedOrders: 1890,
+      responseTime: '10分钟内',
+      workingHours: '07:00-19:00',
+      serviceArea: '东城区、西城区',
+      priceRange: '上门费: ¥15-25',
+      features: ['专业分类', '环保处理', '价格透明']
+    },
+    {
+      id: 3,
+      name: '王师傅',
+      avatar: 'avatar3.jpg',
+      rating: 4.7,
+      reviewCount: 89,
+      distance: '2.1km',
+      status: 'busy',
+      specialties: ['大件回收', '装修垃圾', '办公设备'],
+      experience: '3年经验',
+      phone: '137****9012',
+      completedOrders: 567,
+      responseTime: '15分钟内',
+      workingHours: '09:00-18:00',
+      serviceArea: '丰台区、大兴区',
+      priceRange: '上门费: ¥20-30',
+      features: ['大件搬运', '团队作业', '清理彻底']
+    },
+    {
+      id: 4,
+      name: '陈师傅',
+      avatar: 'avatar4.jpg',
+      rating: 4.6,
+      reviewCount: 134,
+      distance: '3.5km',
+      status: 'offline',
+      specialties: ['有害垃圾', '电池回收', '化学品处理'],
+      experience: '6年经验',
+      phone: '136****3456',
+      completedOrders: 890,
+      responseTime: '20分钟内',
+      workingHours: '08:30-17:30',
+      serviceArea: '石景山区、门头沟区',
+      priceRange: '上门费: ¥25-35',
+      features: ['专业资质', '安全处理', '环保认证']
+    }
+  ];
+
+  filteredCollectors = [...this.collectors];
+
+  filterOptions = [
+    { value: 'all', label: '全部', icon: 'all' },
+    { value: 'online', label: '在线', icon: 'online' },
+    { value: 'nearby', label: '附近', icon: 'location' },
+    { value: 'highRated', label: '高评分', icon: 'star' }
+  ];
+
+  constructor(private router: Router) {}
+
+  ngOnInit() {
+    this.filterCollectors();
+  }
+
+  goBack() {
+    this.router.navigate(['/consumer/home']);
+  }
+
+  selectFilter(filter: string) {
+    this.selectedFilter = filter;
+    this.filterCollectors();
+  }
+
+  filterCollectors() {
+    let filtered = [...this.collectors];
+
+    switch (this.selectedFilter) {
+      case 'online':
+        filtered = filtered.filter(collector => collector.status === 'online');
+        break;
+      case 'nearby':
+        filtered = filtered.sort((a, b) => 
+          parseFloat(a.distance) - parseFloat(b.distance)
+        );
+        break;
+      case 'highRated':
+        filtered = filtered.filter(collector => collector.rating >= 4.8);
+        break;
+      default:
+        // 保持原有顺序
+        break;
+    }
+
+    this.filteredCollectors = filtered;
+  }
+
+  getStatusText(status: string): string {
+    switch (status) {
+      case 'online':
+        return '在线';
+      case 'busy':
+        return '忙碌';
+      case 'offline':
+        return '离线';
+      default:
+        return '未知';
+    }
+  }
+
+  getStatusColor(status: string): string {
+    switch (status) {
+      case 'online':
+        return '#4CAF50';
+      case 'busy':
+        return '#FF9800';
+      case 'offline':
+        return '#9E9E9E';
+      default:
+        return '#9E9E9E';
+    }
+  }
+
+  callCollector(phone: string) {
+    window.open(`tel:${phone}`);
+  }
+
+  bookCollector(collector: any) {
+    this.router.navigate(['/consumer/booking'], { 
+      queryParams: { collectorId: collector.id } 
+    });
+  }
+
+  viewCollectorProfile(collector: any) {
+    // 可以导航到详情页面或显示详情弹窗
+    console.log('查看详情:', collector);
+  }
+
+  chatWithCollector(collector: any) {
+    // 可以集成聊天功能
+    console.log('联系回收员:', collector);
+  }
+
+  getSpecialtyColor(specialty: string): string {
+    const colors = [
+      '#4CAF50', '#2196F3', '#FF9800', '#9C27B0', 
+      '#F44336', '#00BCD4', '#795548', '#607D8B'
+    ];
+    const index = specialty.length % colors.length;
+    return colors[index];
+  }
+}

+ 144 - 0
src/app/consumer/home/drop-points/drop-points.html

@@ -0,0 +1,144 @@
+<div class="drop-points-container">
+  <!-- Header -->
+  <div class="header">
+    <div class="header-left" (click)="goBack()">
+      <app-svg-icon name="arrow-left" class="back-icon"></app-svg-icon>
+    </div>
+    <div class="header-title">附近投放点</div>
+    <div class="header-right">
+      <app-svg-icon name="map" class="map-icon"></app-svg-icon>
+    </div>
+  </div>
+
+  <!-- Search Section -->
+  <div class="search-section">
+    <div class="search-bar">
+      <app-svg-icon name="search" class="search-icon"></app-svg-icon>
+      <input 
+        type="text" 
+        class="search-input"
+        placeholder="搜索投放点名称或地址"
+        [value]="searchQuery"
+        (input)="onSearchChange($event)"
+      >
+    </div>
+
+    <div class="filter-tabs">
+      <div 
+        class="filter-tab" 
+        [class.active]="selectedFilter === 'all'"
+        (click)="selectFilter('all')"
+      >
+        全部
+      </div>
+      <div 
+        class="filter-tab" 
+        [class.active]="selectedFilter === 'nearby'"
+        (click)="selectFilter('nearby')"
+      >
+        附近
+      </div>
+      <div 
+        class="filter-tab" 
+        [class.active]="selectedFilter === 'available'"
+        (click)="selectFilter('available')"
+      >
+        可用
+      </div>
+      <div 
+        class="filter-tab" 
+        [class.active]="selectedFilter === 'full-service'"
+        (click)="selectFilter('full-service')"
+      >
+        全服务
+      </div>
+    </div>
+  </div>
+
+  <!-- Drop Points List -->
+  <div class="drop-points-list">
+    <div class="drop-point-item" *ngFor="let point of filteredDropPoints">
+      <div class="drop-point-header">
+        <div class="drop-point-info">
+          <h3 class="drop-point-name">{{ point.name }}</h3>
+          <div class="drop-point-meta">
+            <div class="meta-item">
+              <app-svg-icon name="location" class="meta-icon"></app-svg-icon>
+              <span>{{ point.distance }}</span>
+            </div>
+            <div class="meta-item">
+              <app-svg-icon name="star" class="meta-icon"></app-svg-icon>
+              <span>{{ point.rating }}</span>
+            </div>
+          </div>
+        </div>
+        <div 
+          class="drop-point-status" 
+          [class.busy]="point.status === 'busy'"
+          [class.closed]="point.status === 'closed'"
+        >
+          {{ getStatusText(point.status) }}
+        </div>
+      </div>
+
+      <div class="drop-point-details">
+        <div class="detail-row">
+          <app-svg-icon name="location" class="detail-icon"></app-svg-icon>
+          <span class="detail-text">{{ point.address }}</span>
+        </div>
+        <div class="detail-row">
+          <app-svg-icon name="clock" class="detail-icon"></app-svg-icon>
+          <span class="detail-text">{{ point.hours }}</span>
+        </div>
+        <div class="detail-row">
+          <app-svg-icon name="phone" class="detail-icon"></app-svg-icon>
+          <span class="detail-text">{{ point.phone }}</span>
+        </div>
+      </div>
+
+      <div class="drop-point-types">
+        <div class="types-title">接收类型</div>
+        <div class="types-list">
+          <span class="type-tag" *ngFor="let type of point.types">
+            {{ getTypeLabel(type) }}
+          </span>
+        </div>
+      </div>
+
+      <div class="drop-point-features">
+        <div class="features-title">服务特色</div>
+        <div class="features-list">
+          <span class="feature-tag" *ngFor="let feature of point.features">
+            {{ feature }}
+          </span>
+        </div>
+      </div>
+
+      <div class="drop-point-actions">
+        <button class="action-btn" (click)="callDropPoint(point.phone)">
+          <app-svg-icon name="phone" class="btn-icon"></app-svg-icon>
+          电话
+        </button>
+        <button class="action-btn" (click)="navigateToDropPoint(point)">
+          <app-svg-icon name="navigation" class="btn-icon"></app-svg-icon>
+          导航
+        </button>
+        <button class="action-btn" (click)="viewDropPointDetails(point)">
+          <app-svg-icon name="info" class="btn-icon"></app-svg-icon>
+          详情
+        </button>
+        <button class="action-btn primary" (click)="bookDropPoint(point)">
+          <app-svg-icon name="calendar" class="btn-icon"></app-svg-icon>
+          预约
+        </button>
+      </div>
+    </div>
+
+    <!-- Empty State -->
+    <div class="empty-state" *ngIf="filteredDropPoints.length === 0">
+      <app-svg-icon name="empty-location" class="empty-icon"></app-svg-icon>
+      <div class="empty-text">未找到符合条件的投放点</div>
+      <div class="empty-sub-text">请尝试调整筛选条件或搜索关键词</div>
+    </div>
+  </div>
+</div>

+ 551 - 0
src/app/consumer/home/drop-points/drop-points.scss

@@ -0,0 +1,551 @@
+@use '../../../../styles/variables' as *;
+@use '../../../../styles/mixins' as *;
+
+.drop-points-container {
+  min-height: 100vh;
+  background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
+  padding-bottom: 80px;
+
+  .header {
+    @include header-style();
+    background: rgba(255, 255, 255, 0.95);
+    backdrop-filter: blur(10px);
+    border-bottom: 1px solid rgba(0, 0, 0, 0.05);
+    box-shadow: 0 2px 20px rgba(0, 0, 0, 0.1);
+    position: sticky;
+    top: 0;
+    z-index: 100;
+
+    .header-left, .header-right {
+      .back-icon, .map-icon {
+        width: 24px;
+        height: 24px;
+        color: var(--primary-color);
+        transition: all 0.3s ease;
+
+        &:hover {
+          transform: scale(1.1);
+          color: var(--primary-dark);
+        }
+      }
+    }
+
+    .header-title {
+      font-size: 18px;
+      font-weight: 600;
+      color: var(--text-primary);
+      background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
+      -webkit-background-clip: text;
+      -webkit-text-fill-color: transparent;
+      background-clip: text;
+    }
+  }
+
+  .search-section {
+    padding: 20px;
+    background: rgba(255, 255, 255, 0.9);
+    backdrop-filter: blur(10px);
+    border-bottom: 1px solid rgba(0, 0, 0, 0.05);
+
+    .search-bar {
+      position: relative;
+      margin-bottom: 20px;
+
+      .search-icon {
+        position: absolute;
+        left: 15px;
+        top: 50%;
+        transform: translateY(-50%);
+        width: 20px;
+        height: 20px;
+        color: var(--text-secondary);
+        z-index: 2;
+      }
+
+      .search-input {
+        width: 100%;
+        padding: 15px 20px 15px 50px;
+        border: 2px solid transparent;
+        border-radius: 25px;
+        background: rgba(255, 255, 255, 0.8);
+        font-size: 16px;
+        color: var(--text-primary);
+        transition: all 0.3s ease;
+        box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
+
+        &:focus {
+          outline: none;
+          border-color: var(--primary-color);
+          background: white;
+          box-shadow: 0 6px 25px rgba(var(--primary-rgb), 0.2);
+          transform: translateY(-2px);
+        }
+
+        &::placeholder {
+          color: var(--text-secondary);
+        }
+      }
+    }
+
+    .filter-tabs {
+      display: flex;
+      gap: 12px;
+      overflow-x: auto;
+      padding-bottom: 5px;
+
+      .filter-tab {
+        flex-shrink: 0;
+        padding: 12px 20px;
+        border-radius: 20px;
+        background: rgba(255, 255, 255, 0.7);
+        color: var(--text-secondary);
+        font-size: 14px;
+        font-weight: 500;
+        cursor: pointer;
+        transition: all 0.3s ease;
+        border: 2px solid transparent;
+        white-space: nowrap;
+
+        &:hover {
+          background: rgba(255, 255, 255, 0.9);
+          transform: translateY(-2px);
+          box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
+        }
+
+        &.active {
+          background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
+          color: white;
+          border-color: var(--primary-light);
+          box-shadow: 0 6px 20px rgba(var(--primary-rgb), 0.3);
+        }
+      }
+    }
+  }
+
+  .drop-points-list {
+    padding: 20px;
+    display: flex;
+    flex-direction: column;
+    gap: 20px;
+
+    .drop-point-item {
+      background: rgba(255, 255, 255, 0.95);
+      border-radius: 20px;
+      padding: 24px;
+      box-shadow: 0 8px 30px rgba(0, 0, 0, 0.1);
+      transition: all 0.3s ease;
+      border: 1px solid rgba(255, 255, 255, 0.2);
+      backdrop-filter: blur(10px);
+
+      &:hover {
+        transform: translateY(-5px);
+        box-shadow: 0 15px 40px rgba(0, 0, 0, 0.15);
+      }
+
+      .drop-point-header {
+        display: flex;
+        justify-content: space-between;
+        align-items: flex-start;
+        margin-bottom: 20px;
+
+        .drop-point-info {
+          flex: 1;
+
+          .drop-point-name {
+            font-size: 20px;
+            font-weight: 700;
+            color: var(--text-primary);
+            margin: 0 0 12px 0;
+            background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
+            -webkit-background-clip: text;
+            -webkit-text-fill-color: transparent;
+            background-clip: text;
+          }
+
+          .drop-point-meta {
+            display: flex;
+            gap: 20px;
+
+            .meta-item {
+              display: flex;
+              align-items: center;
+              gap: 6px;
+              color: var(--text-secondary);
+              font-size: 14px;
+
+              .meta-icon {
+                width: 16px;
+                height: 16px;
+                color: var(--primary-color);
+              }
+            }
+          }
+        }
+
+        .drop-point-status {
+          padding: 8px 16px;
+          border-radius: 20px;
+          font-size: 12px;
+          font-weight: 600;
+          text-transform: uppercase;
+          letter-spacing: 0.5px;
+          background: linear-gradient(135deg, #4CAF50, #45a049);
+          color: white;
+          box-shadow: 0 4px 15px rgba(76, 175, 80, 0.3);
+
+          &.busy {
+            background: linear-gradient(135deg, #FF9800, #F57C00);
+            box-shadow: 0 4px 15px rgba(255, 152, 0, 0.3);
+          }
+
+          &.closed {
+            background: linear-gradient(135deg, #f44336, #d32f2f);
+            box-shadow: 0 4px 15px rgba(244, 67, 54, 0.3);
+          }
+        }
+      }
+
+      .drop-point-details {
+        margin-bottom: 20px;
+
+        .detail-row {
+          display: flex;
+          align-items: center;
+          gap: 12px;
+          margin-bottom: 12px;
+          padding: 8px 0;
+
+          .detail-icon {
+            width: 18px;
+            height: 18px;
+            color: var(--primary-color);
+            flex-shrink: 0;
+          }
+
+          .detail-text {
+            color: var(--text-secondary);
+            font-size: 14px;
+            line-height: 1.5;
+          }
+
+          &:last-child {
+            margin-bottom: 0;
+          }
+        }
+      }
+
+      .drop-point-types {
+        margin-bottom: 20px;
+
+        .types-title {
+          font-size: 14px;
+          font-weight: 600;
+          color: var(--text-primary);
+          margin-bottom: 12px;
+        }
+
+        .types-list {
+          display: flex;
+          flex-wrap: wrap;
+          gap: 8px;
+
+          .type-tag {
+            padding: 6px 12px;
+            background: linear-gradient(135deg, var(--primary-light), var(--secondary-light));
+            color: var(--primary-color);
+            border-radius: 15px;
+            font-size: 12px;
+            font-weight: 500;
+            border: 1px solid var(--primary-light);
+          }
+        }
+      }
+
+      .drop-point-features {
+        margin-bottom: 24px;
+
+        .features-title {
+          font-size: 14px;
+          font-weight: 600;
+          color: var(--text-primary);
+          margin-bottom: 12px;
+        }
+
+        .features-list {
+          display: flex;
+          flex-wrap: wrap;
+          gap: 8px;
+
+          .feature-tag {
+            padding: 6px 12px;
+            background: rgba(var(--secondary-rgb), 0.1);
+            color: var(--secondary-color);
+            border-radius: 15px;
+            font-size: 12px;
+            font-weight: 500;
+            border: 1px solid rgba(var(--secondary-rgb), 0.2);
+          }
+        }
+      }
+
+      .drop-point-actions {
+        display: grid;
+        grid-template-columns: repeat(auto-fit, minmax(80px, 1fr));
+        gap: 12px;
+
+        .action-btn {
+          @include center-flex;
+          flex-direction: column;
+          gap: 6px;
+          padding: 12px 8px;
+          border: 2px solid var(--border-light);
+          border-radius: 15px;
+          background: rgba(255, 255, 255, 0.8);
+          color: var(--text-secondary);
+          font-size: 12px;
+          font-weight: 500;
+          cursor: pointer;
+          transition: all 0.3s ease;
+
+          .btn-icon {
+            width: 20px;
+            height: 20px;
+          }
+
+          &:hover {
+            background: var(--primary-light);
+            border-color: var(--primary-color);
+            color: var(--primary-color);
+            transform: translateY(-2px);
+            box-shadow: 0 4px 15px rgba(var(--primary-rgb), 0.2);
+          }
+
+          &.primary {
+            background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
+            border-color: var(--primary-color);
+            color: white;
+            box-shadow: 0 4px 15px rgba(var(--primary-rgb), 0.3);
+
+            &:hover {
+              background: linear-gradient(135deg, var(--primary-dark), var(--secondary-dark));
+              transform: translateY(-3px);
+              box-shadow: 0 6px 25px rgba(var(--primary-rgb), 0.4);
+            }
+          }
+        }
+      }
+    }
+
+    .empty-state {
+      @include center-flex;
+      flex-direction: column;
+      padding: 60px 20px;
+      text-align: center;
+
+      .empty-icon {
+        width: 80px;
+        height: 80px;
+        color: var(--text-disabled);
+        margin-bottom: 20px;
+        opacity: 0.6;
+      }
+
+      .empty-text {
+        font-size: 18px;
+        font-weight: 600;
+        color: var(--text-secondary);
+        margin-bottom: 8px;
+      }
+
+      .empty-sub-text {
+        font-size: 14px;
+        color: var(--text-disabled);
+        line-height: 1.5;
+      }
+    }
+  }
+}
+
+// 平板响应式设计
+@include tablet {
+  .drop-points-container {
+    .search-section {
+      padding: 24px;
+
+      .filter-tabs {
+        justify-content: center;
+        
+        .filter-tab {
+          min-width: 100px;
+        }
+      }
+    }
+
+    .drop-points-list {
+      padding: 24px;
+      
+      .drop-point-item {
+        .drop-point-actions {
+          grid-template-columns: repeat(4, 1fr);
+        }
+      }
+    }
+  }
+}
+
+// 移动端响应式设计
+@include mobile {
+  .drop-points-container {
+    .header {
+      padding: 12px 16px;
+      
+      .header-title {
+        font-size: 16px;
+      }
+
+      .header-left, .header-right {
+        .back-icon, .map-icon {
+          width: 20px;
+          height: 20px;
+        }
+      }
+    }
+
+    .search-section {
+      padding: 16px;
+
+      .search-bar {
+        margin-bottom: 16px;
+
+        .search-input {
+          padding: 12px 16px 12px 45px;
+          font-size: 14px;
+        }
+
+        .search-icon {
+          width: 18px;
+          height: 18px;
+          left: 12px;
+        }
+      }
+
+      .filter-tabs {
+        gap: 8px;
+        
+        .filter-tab {
+          padding: 10px 16px;
+          font-size: 12px;
+        }
+      }
+    }
+
+    .drop-points-list {
+      padding: 16px;
+      gap: 16px;
+      
+      .drop-point-item {
+        padding: 20px;
+        border-radius: 16px;
+
+        .drop-point-header {
+          margin-bottom: 16px;
+
+          .drop-point-info {
+            .drop-point-name {
+              font-size: 18px;
+              margin-bottom: 10px;
+            }
+
+            .drop-point-meta {
+              gap: 16px;
+
+              .meta-item {
+                font-size: 13px;
+
+                .meta-icon {
+                  width: 14px;
+                  height: 14px;
+                }
+              }
+            }
+          }
+
+          .drop-point-status {
+            padding: 6px 12px;
+            font-size: 11px;
+          }
+        }
+
+        .drop-point-details {
+          margin-bottom: 16px;
+
+          .detail-row {
+            margin-bottom: 10px;
+            padding: 6px 0;
+
+            .detail-icon {
+              width: 16px;
+              height: 16px;
+            }
+
+            .detail-text {
+              font-size: 13px;
+            }
+          }
+        }
+
+        .drop-point-types, .drop-point-features {
+          margin-bottom: 16px;
+
+          .types-title, .features-title {
+            font-size: 13px;
+            margin-bottom: 10px;
+          }
+
+          .types-list, .features-list {
+            gap: 6px;
+
+            .type-tag, .feature-tag {
+              padding: 4px 10px;
+              font-size: 11px;
+            }
+          }
+        }
+
+        .drop-point-actions {
+          grid-template-columns: repeat(2, 1fr);
+          gap: 10px;
+
+          .action-btn {
+            padding: 10px 6px;
+            font-size: 11px;
+
+            .btn-icon {
+              width: 18px;
+              height: 18px;
+            }
+          }
+        }
+      }
+
+      .empty-state {
+        padding: 40px 16px;
+
+        .empty-icon {
+          width: 60px;
+          height: 60px;
+          margin-bottom: 16px;
+        }
+
+        .empty-text {
+          font-size: 16px;
+          margin-bottom: 6px;
+        }
+
+        .empty-sub-text {
+          font-size: 13px;
+        }
+      }
+    }
+  }
+}

+ 198 - 0
src/app/consumer/home/drop-points/drop-points.ts

@@ -0,0 +1,198 @@
+import { Component, OnInit } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { Router } from '@angular/router';
+import { SvgIconComponent } from '../../../shared/components/svg-icons/svg-icons.component';
+
+@Component({
+  selector: 'app-drop-points',
+  standalone: true,
+  imports: [CommonModule, FormsModule, SvgIconComponent],
+  templateUrl: './drop-points.html',
+  styleUrls: ['./drop-points.scss']
+})
+export class DropPointsComponent implements OnInit {
+  selectedFilter = 'all';
+  searchQuery = '';
+  
+  dropPoints = [
+    {
+      id: 1,
+      name: '绿色家园小区回收点',
+      address: '朝阳区建国路88号绿色家园小区',
+      distance: '0.5km',
+      rating: 4.8,
+      status: 'open',
+      types: ['plastic', 'paper', 'metal', 'glass'],
+      hours: '06:00-22:00',
+      phone: '010-12345678',
+      features: ['24小时', '智能分类', '积分奖励'],
+      coordinates: { lat: 39.9042, lng: 116.4074 }
+    },
+    {
+      id: 2,
+      name: '阳光社区环保站',
+      address: '海淀区中关村大街123号阳光社区',
+      distance: '1.2km',
+      rating: 4.6,
+      status: 'open',
+      types: ['plastic', 'paper', 'electronic'],
+      hours: '08:00-20:00',
+      phone: '010-87654321',
+      features: ['专业回收', '环保教育'],
+      coordinates: { lat: 39.9826, lng: 116.3186 }
+    },
+    {
+      id: 3,
+      name: '科技园区回收中心',
+      address: '海淀区科技园区创新大厦B座',
+      distance: '2.1km',
+      rating: 4.9,
+      status: 'busy',
+      types: ['electronic', 'battery', 'metal'],
+      hours: '09:00-18:00',
+      phone: '010-11223344',
+      features: ['专业设备', '数据销毁', '企业服务'],
+      coordinates: { lat: 39.9889, lng: 116.3056 }
+    },
+    {
+      id: 4,
+      name: '市民广场便民点',
+      address: '东城区王府井大街市民广场',
+      distance: '3.5km',
+      rating: 4.3,
+      status: 'closed',
+      types: ['plastic', 'paper', 'glass'],
+      hours: '07:00-19:00',
+      phone: '010-99887766',
+      features: ['便民服务', '快速投放'],
+      coordinates: { lat: 39.9097, lng: 116.4139 }
+    }
+  ];
+
+  filteredDropPoints = [...this.dropPoints];
+
+  filterOptions = [
+    { value: 'all', label: '全部', icon: 'all' },
+    { value: 'plastic', label: '塑料', icon: 'plastic' },
+    { value: 'paper', label: '纸张', icon: 'paper' },
+    { value: 'metal', label: '金属', icon: 'metal' },
+    { value: 'glass', label: '玻璃', icon: 'glass' },
+    { value: 'electronic', label: '电子', icon: 'electronic' },
+    { value: 'battery', label: '电池', icon: 'battery' }
+  ];
+
+  constructor(private router: Router) {}
+
+  ngOnInit() {
+    this.filterDropPoints();
+  }
+
+  goBack() {
+    this.router.navigate(['/consumer/home']);
+  }
+
+  selectFilter(filter: string) {
+    this.selectedFilter = filter;
+    this.filterDropPoints();
+  }
+
+  filterDropPoints() {
+    let filtered = [...this.dropPoints];
+
+    // 按类型筛选
+    if (this.selectedFilter !== 'all') {
+      filtered = filtered.filter(point => 
+        point.types.includes(this.selectedFilter)
+      );
+    }
+
+    // 按搜索关键词筛选
+    if (this.searchQuery.trim()) {
+      const query = this.searchQuery.toLowerCase();
+      filtered = filtered.filter(point =>
+        point.name.toLowerCase().includes(query) ||
+        point.address.toLowerCase().includes(query)
+      );
+    }
+
+    this.filteredDropPoints = filtered;
+  }
+
+  onSearchChange(event: any) {
+    this.searchQuery = event.target.value;
+    this.filterDropPoints();
+  }
+
+  getStatusText(status: string): string {
+    switch (status) {
+      case 'open':
+        return '营业中';
+      case 'busy':
+        return '繁忙';
+      case 'closed':
+        return '已关闭';
+      default:
+        return '未知';
+    }
+  }
+
+  getStatusColor(status: string): string {
+    switch (status) {
+      case 'open':
+        return '#4CAF50';
+      case 'busy':
+        return '#FF9800';
+      case 'closed':
+        return '#F44336';
+      default:
+        return '#9E9E9E';
+    }
+  }
+
+  callDropPoint(phone: string) {
+    window.open(`tel:${phone}`);
+  }
+
+  navigateToDropPoint(point: any) {
+    // 这里可以集成地图导航功能
+    const url = `https://maps.google.com/?q=${point.coordinates.lat},${point.coordinates.lng}`;
+    window.open(url, '_blank');
+  }
+
+  viewDropPointDetails(point: any) {
+    // 可以导航到详情页面或显示详情弹窗
+    console.log('查看详情:', point);
+  }
+
+  bookDropPoint(point: any) {
+    // 导航到预约页面
+    this.router.navigate(['/consumer/booking'], { 
+      queryParams: { dropPointId: point.id } 
+    });
+  }
+
+  getTypeIcon(type: string): string {
+    const iconMap: { [key: string]: string } = {
+      plastic: 'plastic',
+      paper: 'paper',
+      metal: 'metal',
+      glass: 'glass',
+      electronic: 'electronic',
+      battery: 'battery'
+    };
+    return iconMap[type] || 'recycle';
+  }
+
+  getTypeLabel(type: string): string {
+    const labelMap: { [key: string]: string } = {
+      plastic: '塑料',
+      paper: '纸张',
+      metal: '金属',
+      glass: '玻璃',
+      electronic: '电子',
+      battery: '电池'
+    };
+    return labelMap[type] || type;
+  }
+}

+ 155 - 18
src/app/consumer/home/home.scss

@@ -424,33 +424,66 @@
   }
 }
 
-// 响应式设计
+// 移动端响应式设计
 @media (max-width: 480px) {
-  .status-bar {
-    padding: 12px 15px;
+  .header {
+    padding: 10px 15px;
     
-    .user-level .level-progress {
-      width: 100px;
+    .user-info {
+      .user-level {
+        font-size: 12px;
+      }
+      
+      .level-progress {
+        height: 3px;
+      }
     }
     
     .points-cash {
-      font-size: 11px;
+      gap: 15px;
+      
+      .points, .cash {
+        font-size: 12px;
+        
+        .value {
+          font-size: 16px;
+        }
+      }
     }
   }
   
   .core-functions {
-    margin: 10px 15px;
     padding: 15px;
     
-    .function-buttons .func-btn {
-      padding: 12px 8px;
+    .quick-booking {
+      padding: 15px;
       
-      .func-icon {
-        width: 35px;
-        height: 35px;
+      .booking-title {
+        font-size: 16px;
+      }
+      
+      .booking-subtitle {
+        font-size: 12px;
+      }
+    }
+    
+    .function-grid {
+      gap: 10px;
+      
+      .function-item {
+        padding: 12px 8px;
         
-        i {
-          font-size: 16px;
+        .function-icon {
+          width: 35px;
+          height: 35px;
+          
+          i {
+            font-size: 16px;
+          }
+        }
+        
+        .function-text {
+          font-size: 11px;
         }
       }
     }
@@ -461,17 +494,121 @@
     
     .info-section {
       padding: 15px;
+      
+      .section-header {
+        .section-title {
+          font-size: 16px;
+        }
+        
+        .view-more {
+          font-size: 12px;
+        }
+      }
+      
+      .collector-list {
+        gap: 8px;
+        
+        .collector-card {
+          min-width: 70px;
+          
+          .collector-avatar {
+            width: 45px;
+            height: 45px;
+            
+            i {
+              font-size: 18px;
+            }
+          }
+          
+          .collector-name {
+            font-size: 11px;
+          }
+          
+          .collector-status {
+            font-size: 9px;
+          }
+        }
+      }
+      
+      .drop-points {
+        .point-card {
+          padding: 8px 0;
+          
+          .point-name {
+            font-size: 13px;
+          }
+          
+          .point-distance {
+            font-size: 11px;
+          }
+          
+          .capacity-bar {
+            width: 50px;
+            height: 5px;
+          }
+          
+          .capacity-text {
+            font-size: 9px;
+            min-width: 35px;
+          }
+        }
+      }
+      
+      .activity-carousel {
+        .activity-card {
+          padding: 12px;
+          
+          .activity-title {
+            font-size: 13px;
+          }
+          
+          .activity-subtitle {
+            font-size: 11px;
+          }
+          
+          .activity-meta {
+            font-size: 10px;
+          }
+        }
+      }
     }
   }
   
   .ai-assistant {
-    width: 50px;
-    height: 50px;
+    width: 45px;
+    height: 45px;
     right: 15px;
-    bottom: 90px;
+    bottom: 85px;
     
     i {
-      font-size: 20px;
+      font-size: 18px;
+    }
+  }
+}
+
+// 平板端响应式设计
+@media (max-width: 768px) and (min-width: 481px) {
+  .header {
+    padding: 12px 20px;
+  }
+  
+  .core-functions {
+    padding: 20px;
+    
+    .function-grid {
+      gap: 12px;
+      
+      .function-item {
+        padding: 15px 10px;
+      }
+    }
+  }
+  
+  .dynamic-info {
+    padding: 0 20px 100px;
+    
+    .info-section {
+      padding: 18px;
     }
   }
 }

+ 6 - 13
src/app/consumer/home/home.ts

@@ -138,50 +138,43 @@ export class HomeComponent implements OnInit {
   // 打开通知页面
   openNotifications(): void {
     console.log('Opening notifications...');
-    // 实际项目中会导航到通知页面
-    // this.router.navigate(['/consumer/notifications']);
+    this.router.navigate(['/consumer/notifications']);
   }
 
   // 一键预约回收
   quickBooking(): void {
     console.log('Quick booking initiated...');
-    // 实际项目中会导航到预约页面或打开预约弹窗
     this.router.navigate(['/consumer/booking-recycle']);
   }
 
   // 打开AR识废品功能
   openARRecognition(): void {
     console.log('Opening AR recognition...');
-    // 实际项目中会导航到AR识别页面
-    // this.router.navigate(['/consumer/ar-recognition']);
+    this.router.navigate(['/consumer/ar-recognition']);
   }
 
   // 查找自助投递点
   findDropPoints(): void {
     console.log('Finding drop points...');
-    // 实际项目中会导航到地图页面
-    // this.router.navigate(['/consumer/drop-points']);
+    this.router.navigate(['/consumer/drop-points']);
   }
 
   // 查看更多回收员
   viewMoreCollectors(): void {
     console.log('Viewing more collectors...');
-    // 实际项目中会导航到回收员列表页面
-    // this.router.navigate(['/consumer/collectors']);
+    this.router.navigate(['/consumer/collectors']);
   }
 
   // 查看更多投递点
   viewMoreDropPoints(): void {
     console.log('Viewing more drop points...');
-    // 实际项目中会导航到投递点列表页面
-    // this.router.navigate(['/consumer/drop-points']);
+    this.router.navigate(['/consumer/drop-points']);
   }
 
   // 查看更多活动
   viewMoreActivities(): void {
     console.log('Viewing more activities...');
-    // 实际项目中会导航到活动列表页面
-    // this.router.navigate(['/consumer/activities']);
+    this.router.navigate(['/consumer/activities']);
   }
 
   // 打开AI助手

+ 47 - 0
src/app/consumer/home/notifications/notifications.html

@@ -0,0 +1,47 @@
+<div class="notifications-container">
+  <!-- Header -->
+  <div class="header">
+    <div class="header-left" (click)="goBack()">
+      <app-svg-icon name="arrow-left" class="back-icon"></app-svg-icon>
+    </div>
+    <div class="header-title">消息通知</div>
+    <div class="header-right" (click)="markAllAsRead()">
+      <span class="mark-all-read">全部已读</span>
+    </div>
+  </div>
+
+  <!-- Notifications List -->
+  <div class="notifications-list">
+    <div 
+      class="notification-item" 
+      *ngFor="let notification of notifications"
+      [class.unread]="!notification.read"
+      (click)="markAsRead(notification)"
+    >
+      <div class="notification-icon">
+        <app-svg-icon [name]="getNotificationIcon(notification.type)" class="icon"></app-svg-icon>
+        <div class="unread-dot" *ngIf="!notification.read"></div>
+      </div>
+      
+      <div class="notification-content">
+        <div class="notification-header">
+          <span class="notification-title">{{ notification.title }}</span>
+          <span class="notification-time">{{ notification.time }}</span>
+        </div>
+        <div class="notification-message">{{ notification.message }}</div>
+      </div>
+      
+      <div class="notification-actions">
+        <div class="delete-btn" (click)="deleteNotification(notification.id); $event.stopPropagation()">
+          <app-svg-icon name="delete" class="delete-icon"></app-svg-icon>
+        </div>
+      </div>
+    </div>
+  </div>
+
+  <!-- Empty State -->
+  <div class="empty-state" *ngIf="notifications.length === 0">
+    <app-svg-icon name="empty-notification" class="empty-icon"></app-svg-icon>
+    <div class="empty-text">暂无消息通知</div>
+  </div>
+</div>

+ 425 - 0
src/app/consumer/home/notifications/notifications.scss

@@ -0,0 +1,425 @@
+@use '../../../../styles/variables' as *;
+@use '../../../../styles/mixins' as *;
+
+.notifications-container {
+  min-height: 100vh;
+  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+  padding-bottom: 80px;
+
+  .header {
+    @include header-style();
+    background: rgba(255, 255, 255, 0.95);
+    backdrop-filter: blur(15px);
+    border-bottom: 1px solid rgba(0, 0, 0, 0.05);
+    box-shadow: 0 4px 25px rgba(0, 0, 0, 0.1);
+    position: sticky;
+    top: 0;
+    z-index: 100;
+
+    .header-left {
+      .back-icon {
+        width: 24px;
+        height: 24px;
+        color: var(--primary-color);
+        transition: all 0.3s ease;
+
+        &:hover {
+          transform: scale(1.1);
+          color: var(--primary-dark);
+        }
+      }
+    }
+
+    .header-title {
+      font-size: 18px;
+      font-weight: 600;
+      color: var(--text-primary);
+      background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
+      -webkit-background-clip: text;
+      -webkit-text-fill-color: transparent;
+      background-clip: text;
+    }
+
+    .header-right {
+      .mark-all-read {
+        padding: 8px 16px;
+        background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
+        color: white;
+        border-radius: 20px;
+        font-size: 12px;
+        font-weight: 600;
+        cursor: pointer;
+        transition: all 0.3s ease;
+        box-shadow: 0 4px 15px rgba(var(--primary-rgb), 0.3);
+
+        &:hover {
+          transform: translateY(-2px);
+          box-shadow: 0 6px 25px rgba(var(--primary-rgb), 0.4);
+        }
+      }
+    }
+  }
+
+  .notifications-list {
+    padding: 20px;
+    display: flex;
+    flex-direction: column;
+    gap: 16px;
+
+    .notification-item {
+      display: flex;
+      align-items: flex-start;
+      gap: 16px;
+      padding: 20px;
+      background: rgba(255, 255, 255, 0.95);
+      border-radius: 16px;
+      box-shadow: 0 6px 25px rgba(0, 0, 0, 0.1);
+      transition: all 0.3s ease;
+      cursor: pointer;
+      border: 1px solid rgba(255, 255, 255, 0.2);
+      backdrop-filter: blur(10px);
+      position: relative;
+      overflow: hidden;
+
+      &::before {
+        content: '';
+        position: absolute;
+        top: 0;
+        left: 0;
+        width: 4px;
+        height: 100%;
+        background: transparent;
+        transition: all 0.3s ease;
+      }
+
+      &:hover {
+        transform: translateY(-3px);
+        box-shadow: 0 12px 35px rgba(0, 0, 0, 0.15);
+
+        &::before {
+          background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
+        }
+      }
+
+      &.unread {
+        background: rgba(255, 255, 255, 0.98);
+        border-color: rgba(var(--primary-rgb), 0.2);
+        box-shadow: 0 8px 30px rgba(var(--primary-rgb), 0.15);
+
+        &::before {
+          background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
+        }
+
+        .notification-content {
+          .notification-title {
+            font-weight: 700;
+            color: var(--text-primary);
+          }
+        }
+      }
+
+      .notification-icon {
+        position: relative;
+        flex-shrink: 0;
+        width: 48px;
+        height: 48px;
+        border-radius: 50%;
+        background: linear-gradient(135deg, var(--primary-light), var(--secondary-light));
+        @include center-flex;
+        box-shadow: 0 4px 15px rgba(var(--primary-rgb), 0.2);
+
+        .icon {
+          width: 24px;
+          height: 24px;
+          color: var(--primary-color);
+        }
+
+        .unread-dot {
+          position: absolute;
+          top: 2px;
+          right: 2px;
+          width: 12px;
+          height: 12px;
+          background: linear-gradient(135deg, #ff4757, #ff3742);
+          border-radius: 50%;
+          border: 2px solid white;
+          box-shadow: 0 2px 8px rgba(255, 71, 87, 0.4);
+          animation: pulse 2s infinite;
+        }
+      }
+
+      .notification-content {
+        flex: 1;
+        min-width: 0;
+
+        .notification-header {
+          display: flex;
+          justify-content: space-between;
+          align-items: flex-start;
+          margin-bottom: 8px;
+          gap: 12px;
+
+          .notification-title {
+            font-size: 16px;
+            font-weight: 600;
+            color: var(--text-primary);
+            line-height: 1.4;
+            flex: 1;
+            min-width: 0;
+            word-break: break-word;
+          }
+
+          .notification-time {
+            font-size: 12px;
+            color: var(--text-secondary);
+            white-space: nowrap;
+            flex-shrink: 0;
+            background: rgba(var(--text-secondary-rgb), 0.1);
+            padding: 4px 8px;
+            border-radius: 8px;
+          }
+        }
+
+        .notification-message {
+          font-size: 14px;
+          color: var(--text-secondary);
+          line-height: 1.5;
+          word-break: break-word;
+          display: -webkit-box;
+           -webkit-line-clamp: 2;
+           line-clamp: 2;
+           -webkit-box-orient: vertical;
+           overflow: hidden;
+        }
+      }
+
+      .notification-actions {
+        flex-shrink: 0;
+
+        .delete-btn {
+          width: 36px;
+          height: 36px;
+          border-radius: 50%;
+          background: rgba(255, 255, 255, 0.8);
+          @include center-flex;
+          cursor: pointer;
+          transition: all 0.3s ease;
+          border: 1px solid rgba(0, 0, 0, 0.1);
+
+          .delete-icon {
+            width: 18px;
+            height: 18px;
+            color: var(--text-secondary);
+            transition: all 0.3s ease;
+          }
+
+          &:hover {
+            background: #ff4757;
+            border-color: #ff4757;
+            transform: scale(1.1);
+            box-shadow: 0 4px 15px rgba(255, 71, 87, 0.3);
+
+            .delete-icon {
+              color: white;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  .empty-state {
+    @include center-flex;
+    flex-direction: column;
+    padding: 80px 20px;
+    text-align: center;
+
+    .empty-icon {
+      width: 100px;
+      height: 100px;
+      color: rgba(255, 255, 255, 0.6);
+      margin-bottom: 24px;
+      opacity: 0.8;
+    }
+
+    .empty-text {
+      font-size: 20px;
+      font-weight: 600;
+      color: rgba(255, 255, 255, 0.9);
+      text-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
+    }
+  }
+}
+
+@keyframes pulse {
+  0% {
+    transform: scale(1);
+    opacity: 1;
+  }
+  50% {
+    transform: scale(1.2);
+    opacity: 0.7;
+  }
+  100% {
+    transform: scale(1);
+    opacity: 1;
+  }
+}
+
+// 平板响应式设计
+@include tablet {
+  .notifications-container {
+    .notifications-list {
+      padding: 24px;
+      gap: 20px;
+
+      .notification-item {
+        padding: 24px;
+
+        .notification-icon {
+          width: 52px;
+          height: 52px;
+
+          .icon {
+            width: 26px;
+            height: 26px;
+          }
+        }
+
+        .notification-content {
+          .notification-header {
+            .notification-title {
+              font-size: 17px;
+            }
+
+            .notification-time {
+              font-size: 13px;
+            }
+          }
+
+          .notification-message {
+            font-size: 15px;
+          }
+        }
+      }
+    }
+
+    .empty-state {
+      padding: 100px 24px;
+
+      .empty-icon {
+        width: 120px;
+        height: 120px;
+      }
+
+      .empty-text {
+        font-size: 22px;
+      }
+    }
+  }
+}
+
+// 移动端响应式设计
+@include mobile {
+  .notifications-container {
+    .header {
+      padding: 12px 16px;
+
+      .header-title {
+        font-size: 16px;
+      }
+
+      .header-left {
+        .back-icon {
+          width: 20px;
+          height: 20px;
+        }
+      }
+
+      .header-right {
+        .mark-all-read {
+          padding: 6px 12px;
+          font-size: 11px;
+        }
+      }
+    }
+
+    .notifications-list {
+      padding: 16px;
+      gap: 12px;
+
+      .notification-item {
+        padding: 16px;
+        gap: 12px;
+
+        .notification-icon {
+          width: 40px;
+          height: 40px;
+
+          .icon {
+            width: 20px;
+            height: 20px;
+          }
+
+          .unread-dot {
+            width: 10px;
+            height: 10px;
+            top: 1px;
+            right: 1px;
+          }
+        }
+
+        .notification-content {
+          .notification-header {
+            margin-bottom: 6px;
+            gap: 8px;
+
+            .notification-title {
+              font-size: 14px;
+            }
+
+            .notification-time {
+              font-size: 11px;
+              padding: 3px 6px;
+            }
+          }
+
+          .notification-message {
+            font-size: 13px;
+            display: -webkit-box;
+            -webkit-line-clamp: 3;
+            line-clamp: 3;
+            -webkit-box-orient: vertical;
+            overflow: hidden;
+          }
+        }
+
+        .notification-actions {
+          .delete-btn {
+            width: 32px;
+            height: 32px;
+
+            .delete-icon {
+              width: 16px;
+              height: 16px;
+            }
+          }
+        }
+      }
+    }
+
+    .empty-state {
+      padding: 60px 16px;
+
+      .empty-icon {
+        width: 80px;
+        height: 80px;
+        margin-bottom: 20px;
+      }
+
+      .empty-text {
+        font-size: 18px;
+      }
+    }
+  }
+}

+ 87 - 0
src/app/consumer/home/notifications/notifications.ts

@@ -0,0 +1,87 @@
+import { Component, OnInit } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { Router } from '@angular/router';
+import { SvgIconComponent } from '../../../shared/components/svg-icons/svg-icons.component';
+
+@Component({
+  selector: 'app-notifications',
+  standalone: true,
+  imports: [CommonModule, SvgIconComponent],
+  templateUrl: './notifications.html',
+  styleUrls: ['./notifications.scss']
+})
+export class NotificationsComponent implements OnInit {
+  notifications = [
+    {
+      id: 1,
+      type: 'system',
+      title: '系统通知',
+      message: '您的回收订单已完成,获得积分 50 分',
+      time: '2024-01-15 10:30',
+      read: false,
+      icon: 'notification'
+    },
+    {
+      id: 2,
+      type: 'activity',
+      title: '活动通知',
+      message: '环保知识竞赛开始啦!参与即可获得额外积分',
+      time: '2024-01-14 16:20',
+      read: false,
+      icon: 'activity'
+    },
+    {
+      id: 3,
+      type: 'reward',
+      title: '奖励通知',
+      message: '恭喜您完成每日回收任务,获得现金奖励 ¥5.00',
+      time: '2024-01-14 09:15',
+      read: true,
+      icon: 'reward'
+    },
+    {
+      id: 4,
+      type: 'system',
+      title: '系统通知',
+      message: '您的账户余额已更新,当前余额 ¥125.50',
+      time: '2024-01-13 14:45',
+      read: true,
+      icon: 'notification'
+    }
+  ];
+
+  constructor(private router: Router) {}
+
+  ngOnInit() {}
+
+  goBack() {
+    this.router.navigate(['/consumer/home']);
+  }
+
+  markAsRead(notification: any) {
+    notification.read = true;
+  }
+
+  markAllAsRead() {
+    this.notifications.forEach(notification => {
+      notification.read = true;
+    });
+  }
+
+  deleteNotification(id: number) {
+    this.notifications = this.notifications.filter(notification => notification.id !== id);
+  }
+
+  getNotificationIcon(type: string): string {
+    switch (type) {
+      case 'system':
+        return 'notification';
+      case 'activity':
+        return 'activity';
+      case 'reward':
+        return 'reward';
+      default:
+        return 'notification';
+    }
+  }
+}

+ 15 - 0
src/app/consumer/points-mall/points-mall.html

@@ -40,6 +40,21 @@
       </div>
     </div>
     
+    <!-- 环保知识学习板块 -->
+    <div class="eco-knowledge-card" (click)="navigateToEcoKnowledge()">
+      <div class="eco-knowledge-icon">
+        <app-svg-icon name="leaf" size="lg" color="#28a745"></app-svg-icon>
+      </div>
+      <div class="eco-knowledge-content">
+        <div class="eco-knowledge-title">环保知识学习</div>
+        <div class="eco-knowledge-desc">学习环保知识,赚取额外积分</div>
+        <div class="eco-knowledge-reward">+50积分/篇</div>
+      </div>
+      <div class="eco-knowledge-arrow">
+        <app-svg-icon name="arrow-right" size="md" color="#6c757d"></app-svg-icon>
+      </div>
+    </div>
+    
     <!-- 商品分类导航 -->
     <div class="section-title">商品分类</div>
     <div class="category-nav">

+ 51 - 0
src/app/consumer/points-mall/points-mall.scss

@@ -200,6 +200,57 @@ body {
   }
 }
 
+/* 环保知识学习卡片 */
+.eco-knowledge-card {
+  background: linear-gradient(135deg, #e8f5e9, #c8e6c9);
+  border-radius: 12px;
+  padding: 15px;
+  margin-bottom: 15px;
+  display: flex;
+  align-items: center;
+  cursor: pointer;
+  transition: all 0.3s ease;
+  border: 1px solid #e0e0e0;
+
+  &:hover {
+    transform: translateY(-2px);
+    box-shadow: 0 4px 15px rgba(46, 125, 50, 0.2);
+  }
+
+  .eco-knowledge-icon {
+    margin-right: 12px;
+    flex-shrink: 0;
+  }
+
+  .eco-knowledge-content {
+    flex: 1;
+  }
+
+  .eco-knowledge-title {
+    font-size: 16px;
+    font-weight: 600;
+    color: #2e7d32;
+    margin-bottom: 4px;
+  }
+
+  .eco-knowledge-desc {
+    font-size: 12px;
+    color: #6c757d;
+    margin-bottom: 6px;
+  }
+
+  .eco-knowledge-reward {
+    font-size: 12px;
+    color: #ff9800;
+    font-weight: 600;
+  }
+
+  .eco-knowledge-arrow {
+    color: #6c757d;
+    flex-shrink: 0;
+  }
+}
+
 /* 商品分类导航 */
 .category-nav {
   display: grid;

+ 7 - 1
src/app/consumer/points-mall/points-mall.ts

@@ -2,6 +2,7 @@ import { Component } from '@angular/core';
 import { CommonModule } from '@angular/common';
 import { RouterModule, Router } from '@angular/router';
 import { BottomNavComponent } from '../../shared/bottom-nav/bottom-nav.component';
+import { SvgIconComponent } from '../../shared/components/svg-icons/svg-icons.component';
 
 interface Product {
   id: number;
@@ -46,7 +47,7 @@ interface Task {
 
 @Component({
   selector: 'app-points-mall',
-  imports: [CommonModule, RouterModule, BottomNavComponent],
+  imports: [CommonModule, RouterModule, BottomNavComponent, SvgIconComponent],
   templateUrl: './points-mall.html',
   styleUrl: './points-mall.scss'
 })
@@ -425,4 +426,9 @@ export class PointsMall {
         break;
     }
   }
+
+  // 导航到环保知识页面
+  navigateToEcoKnowledge(): void {
+    this.router.navigate(['/consumer/eco-knowledge']);
+  }
 }

+ 94 - 0
src/app/shared/components/svg-icons/svg-icons.component.ts

@@ -0,0 +1,94 @@
+import { Component, Input } from '@angular/core';
+import { CommonModule } from '@angular/common';
+
+@Component({
+  selector: 'app-svg-icon',
+  imports: [CommonModule],
+  standalone: true,
+  template: `
+    <svg 
+      class="recycle-icon" 
+      [class]="size + ' ' + color + ' ' + (hover ? 'icon-hover' : '')"
+      [attr.width]="sizePx" 
+      [attr.height]="sizePx"
+      viewBox="0 0 24 24"
+      fill="currentColor"
+      stroke="currentColor"
+      stroke-width="0"
+    >
+      <ng-container [ngSwitch]="name">
+        <!-- 导航图标 -->
+        <path *ngSwitchCase="'home'" d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/>
+        <path *ngSwitchCase="'mall'" d="M12 2l9 4v2h-1v10c0 1.1-.9 2-2 2H6c-1.1 0-2-.9-2-2V8H3V6l9-4zm0 2.21L5 6.5V8h14V6.5L12 4.21zM6 8v10h12V8H6z"/>
+        <path *ngSwitchCase="'booking'" d="M19 3h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-7 0c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm2 14H7v-2h7v2zm3-4H7v-2h10v2zm0-4H7V7h10v2z"/>
+        <path *ngSwitchCase="'profile'" d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/>
+        <path *ngSwitchCase="'earnings'" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/>
+        <path *ngSwitchCase="'arrow-left'" d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/>
+        <path *ngSwitchCase="'arrow-right'" d="M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8z"/>
+        
+        <!-- 操作图标 -->
+        <path *ngSwitchCase="'search'" d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>
+        <path *ngSwitchCase="'cart'" d="M7 18c-1.1 0-1.99.9-1.99 2S5.9 22 7 22s2-.9 2-2-.9-2-2-2zM1 2v2h2l3.6 7.59-1.35 2.45c-.16.28-.25.61-.25.96 0 1.1.9 2 2 2h12v-2H7.42c-.14 0-.25-.11-.25-.25l.03-.12.9-1.63h7.45c.75 0 1.41-.41 1.75-1.03l3.58-6.49c.08-.14.12-.31.12-.48 0-.55-.45-1-1-1H5.21l-.94-2H1zm16 16c-1.1 0-1.99.9-1.99 2s.89 2 1.99 2 2-.9 2-2-.9-2-2-2z"/>
+        <path *ngSwitchCase="'trash'" d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/>
+        <path *ngSwitchCase="'favorite'" d="M16.5 3c-1.74 0-3.41.81-4.5 2.09C10.91 3.81 9.24 3 7.5 3 4.42 3 2 5.42 2 8.5c0 3.78 3.4 6.86 8.55 11.54L12 21.35l1.45-1.32C18.6 15.36 22 12.28 22 8.5 22 5.42 19.58 3 16.5 3zm-4.4 15.55l-.1.1-.1-.1C7.14 14.24 4 11.39 4 8.5 4 6.5 5.5 5 7.5 5c1.54 0 3.04.99 3.57 2.36h1.87C13.46 5.99 14.96 5 16.5 5c2 0 3.5 1.5 3.5 3.5 0 2.89-3.14 5.74-7.9 10.05z"/>
+        <path *ngSwitchCase="'share'" d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92 1.61 0 2.92-1.31 2.92-2.92s-1.31-2.92-2.92-2.92z"/>
+        <path *ngSwitchCase="'filter'" d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>
+        <path *ngSwitchCase="'sort'" d="M3 18h6v-2H3v2zM3 6v2h18V6H3zm0 7h12v-2H3v2z"/>
+        <path *ngSwitchCase="'plus'" d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
+        <path *ngSwitchCase="'minus'" d="M19 13H5v-2h14v2z"/>
+        <path *ngSwitchCase="'delete'" d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/>
+        <path *ngSwitchCase="'edit'" d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34a.9959.9959 0 0 0-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/>
+        <path *ngSwitchCase="'camera'" d="M12 15.2C13.767 15.2 15.2 13.767 15.2 12 15.2 10.233 13.767 8.8 12 8.8 10.233 8.8 8.8 10.233 8.8 12 8.8 13.767 10.233 15.2 12 15.2zM9 2 7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2h-3.17L15 2H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"/>
+        
+        <!-- 状态图标 -->
+        <path *ngSwitchCase="'favorite-filled'" d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/>
+        <path *ngSwitchCase="'cart-full'" d="M7 18c-1.1 0-1.99.9-1.99 2S5.9 22 7 22s2-.9 2-2-.9-2-2-2zM17 18c-1.1 0-1.99.9-1.99 2s.89 2 1.99 2 2-.9 2-2-.9-2-2-2zM12.5 2h-1c-.28 0-.5.22-.5.5V3h2v-.5c0-.28-.22-.5-.5-.5zM19 5H5c-.55 0-1 .45-1 1v1h16V6c0-.55-.45-1-1-1zm-1 4H6L5.78 5.94l-.65-1.88C5.01 3.95 5.45 3 6.12 3h11.76c.67 0 1.11.95.65 1.88L18.22 5.94 18 9z"/>
+        <path *ngSwitchCase="'cart-empty'" d="M7 18c-1.1 0-1.99.9-1.99 2S5.9 22 7 22s2-.9 2-2-.9-2-2-2zM1 2v2h2l3.6 7.59-1.35 2.45c-.16.28-.25.61-.25.96 0 1.1.9 2 2 2h12v-2H7.42c-.14 0-.25-.11-.25-.25l.03-.12.9-1.63h7.45c.75 0 1.41-.41 1.75-1.03l3.58-6.49c.08-.14.12-.31.12-.48 0-.55-.45-1-1-1H5.21l-.94-2H1zm16 16c-1.1 0-1.99.9-1.99 2s.89 2 1.99 2 2-.9 2-2-.9-2-2-2z"/>
+        <path *ngSwitchCase="'star'" d="M22 9.24l-7.19-.62L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21 12 17.27 18.18 21l-1.63-7.03L22 9.24zM12 15.4l-3.76 2.27 1-4.28-3.32-2.88 4.38-.38L12 6.1l1.71 4.04 4.38.38-3.32 2.88 1 4.28L12 15.4z"/>
+        <path *ngSwitchCase="'star-filled'" d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.62L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"/>
+        <path *ngSwitchCase="'success'" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>
+        <path *ngSwitchCase="'error'" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/>
+        <path *ngSwitchCase="'warning'" d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"/>
+        <path *ngSwitchCase="'times'" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
+        
+        <!-- 信息图标 -->
+        <path *ngSwitchCase="'location'" d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"/>
+        <path *ngSwitchCase="'phone'" d="M6.62 10.79c1.44 2.83 3.76 5.14 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17 0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.11.35.03.74-.25 1.02l-2.2 2.2z"/>
+        <path *ngSwitchCase="'calendar'" d="M19 4h-1V2h-2v2H8V2H6v2H5c-1.11 0-1.99.9-1.99 2L3 20c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 16H5V9h14v11zM7 11h2v2H7zm4 0h2v2h-2zm4 0h2v2h-2z"/>
+        <path *ngSwitchCase="'time'" d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67z"/>
+        <path *ngSwitchCase="'user'" d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/>
+        <path *ngSwitchCase="'email'" d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 14H4V8l8 5 8-5v10zm-8-7L4 6h16l-8 5z"/>
+        
+        <!-- 环保相关图标 -->
+        <path *ngSwitchCase="'recycle'" d="M5.77 7.15 7.2 4.78l1.03-1.71c.39-.65 1.33-.65 1.72 0l1.48 2.46-1.07 1.78-1.48-2.46-1.39 2.31L5.77 7.15zm15.95 5.82-1.41-2.31-1.39 2.31 1.39 2.31 1.41-2.31zm-5.56-2.31-1.39-2.31-1.41 2.31 1.41 2.31 1.39-2.31zM1.39 16.92l2.77-4.62h2.78l-2.77 4.62-2.78-4.62zm17.33-4.62h2.78l-2.77 4.62-2.78-4.62 2.77-4.62zm-8.66-2.31-1.39 2.31 1.41 2.31 1.39-2.31-1.41-2.31zM13.6 5.84l-1.07 1.78 1.48 2.46 1.07-1.78-1.48-2.46zm-3.73 6.2h2.78L9.88 16.92l-2.77-4.62 2.77-4.62z"/>
+        <path *ngSwitchCase="'leaf'" d="M17 12c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zm1.65 7.35L16.5 17.2V14h1v2.79l1.85 1.85-.7.71zM12 3v10l-3.2 6h10.5l2.7-9H21V3h-9zm-2 2h5v4.1l-2.1 2.1-2.9-2.9V5z"/>
+        <path *ngSwitchCase="'eco'" d="M6.05 8.05c-2.2 2.2-2.2 5.7 0 7.9 1.1 1.1 2.5 1.7 4 1.7s2.9-.6 4-1.7c2.2-2.2 2.2-5.7 0-7.9-2.2-2.2-5.7-2.2-7.9 0zm7.9 7.9c-1.6 1.6-4.1 1.6-5.7 0-1.6-1.6-1.6-4.1 0-5.7 1.6-1.6 4.1-1.6 5.7 0 1.6 1.6 1.6 4.1 0 5.7z"/>
+        <path *ngSwitchCase="'tree'" d="M20 18c1.1 0 1.99-.9 1.99-2L22 6c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2H1c-.55 0-1 .45-1 1s.45 1 1 1h22c.55 0 1-.45 1-1s-.45-1-1-1h-3zM4 6h16v10H4V6z"/>
+        <path *ngSwitchCase="'water'" d="M12 3.1L7.05 8.05c-2.73 2.73-2.73 7.17 0 9.9 2.73 2.73 7.17 2.73 9.9 0 2.73-2.73 2.73-7.17 0-9.9L12 3.1zm0 2.83l2.83 2.83c1.56 1.56 1.56 4.09 0 5.66-1.56 1.56-4.09 1.56-5.66 0-1.56-1.56-1.56-4.09 0-5.66L12 5.93z"/>
+        <path *ngSwitchCase="'leaf'" d="M17 12c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zm1.65 7.35L16.5 17.2V14h1v2.79l1.85 1.85-.7.71zM12 3v10l-3.2 6h10.5l2.7-9H21V3h-9zm-2 2h5v4.1l-2.1 2.1-2.9-2.9V5z"/>
+        <path *ngSwitchCase="'energy'" d="M11.21 20H5V10h14v4.38l2-2V6c0-1.1-.9-2-2-2h-1V2h-2v2H8V2H6v2H5c-1.11 0-1.99.9-1.99 2L3 20c0 1.1.89 2 2 2h8.21l-2-2zm5.58 2.41L13 18.83V21h-2v-2.17l-3.79-3.79 1.42-1.42L12 16.83l3.79-3.79 1.42 1.42zM19 10V6h-2v4h2z"/>
+        
+        <!-- 默认图标 -->
+        <path *ngSwitchDefault d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>
+      </ng-container>
+    </svg>
+  `,
+  styles: [`
+    :host {
+      display: inline-flex;
+      align-items: center;
+      justify-content: center;
+    }
+  `]
+})
+export class SvgIconComponent {
+  @Input() name: string = 'success';
+  @Input() size: 'sm' | 'md' | 'lg' | 'xl' = 'md';
+  @Input() color: string = 'currentColor';
+  @Input() hover: boolean = false;
+  
+  get sizePx(): string {
+    const sizes = { sm: '14', md: '18', lg: '24', xl: '32' };
+    return sizes[this.size];
+  }
+}

+ 7 - 0
src/assets/fonts/iconfont.css

@@ -0,0 +1,7 @@
+@font-face {
+  font-family: 'RecycleIcons';
+  src: url('./iconfont.eot');
+  src: url('./iconfont.eot?#iefix') format('embedded-opentype');
+  font-weight: normal;
+  font-style: normal;
+}

+ 10 - 0
src/assets/fonts/iconfont.eot

@@ -0,0 +1,10 @@
+<!-- Generator: Adobe Illustrator 25.2.3, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 1000 1000" style="enable-background:new 0 0 1000 1000;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:none;}
+</style>
+<g>
+	<path class="st0" d="M0,0h1000v1000H0V0z"/>
+</g>
+</svg>

+ 158 - 2
src/styles.scss

@@ -1,2 +1,158 @@
-/* 全局样式与第三方库导入 */
-@import '@fortawesome/fontawesome-free/css/all.min.css';
+// Import global variables and mixins using @use (must be first)
+@use './styles/variables' as *;
+@use './styles/mixins' as *;
+
+// Import Font Awesome (local package)
+@import '@fortawesome/fontawesome-free/css/all.min.css';
+@import url('./assets/fonts/iconfont.css');
+
+/* 全局基础样式 */
+* {
+  margin: 0;
+  padding: 0;
+  box-sizing: border-box;
+}
+
+html, body {
+  height: 100%;
+  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
+  font-size: 14px;
+  line-height: 1.5;
+  color: var(--text-primary);
+  background: var(--background-primary);
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+/* 滚动条样式 */
+::-webkit-scrollbar {
+  width: 6px;
+  height: 6px;
+}
+
+::-webkit-scrollbar-track {
+  background: var(--divider-color);
+  border-radius: 3px;
+}
+
+::-webkit-scrollbar-thumb {
+  background: var(--text-muted);
+  border-radius: 3px;
+  
+  &:hover {
+    background: var(--text-secondary);
+  }
+}
+
+/* 通用工具类 */
+.text-center { text-align: center; }
+.text-left { text-align: left; }
+.text-right { text-align: right; }
+
+.d-flex { display: flex; }
+.d-block { display: block; }
+.d-inline { display: inline; }
+.d-inline-block { display: inline-block; }
+.d-none { display: none; }
+
+.justify-center { justify-content: center; }
+.justify-between { justify-content: space-between; }
+.justify-around { justify-content: space-around; }
+.justify-start { justify-content: flex-start; }
+.justify-end { justify-content: flex-end; }
+
+.align-center { align-items: center; }
+.align-start { align-items: flex-start; }
+.align-end { align-items: flex-end; }
+.align-stretch { align-items: stretch; }
+
+.flex-1 { flex: 1; }
+.flex-wrap { flex-wrap: wrap; }
+.flex-nowrap { flex-wrap: nowrap; }
+
+.w-100 { width: 100%; }
+.h-100 { height: 100%; }
+
+.m-0 { margin: 0; }
+.p-0 { padding: 0; }
+
+.mt-1 { margin-top: var(--spacing-xs); }
+.mt-2 { margin-top: var(--spacing-sm); }
+.mt-3 { margin-top: var(--spacing-md); }
+.mt-4 { margin-top: var(--spacing-lg); }
+.mt-5 { margin-top: var(--spacing-xl); }
+
+.mb-1 { margin-bottom: var(--spacing-xs); }
+.mb-2 { margin-bottom: var(--spacing-sm); }
+.mb-3 { margin-bottom: var(--spacing-md); }
+.mb-4 { margin-bottom: var(--spacing-lg); }
+.mb-5 { margin-bottom: var(--spacing-xl); }
+
+.ml-1 { margin-left: var(--spacing-xs); }
+.ml-2 { margin-left: var(--spacing-sm); }
+.ml-3 { margin-left: var(--spacing-md); }
+.ml-4 { margin-left: var(--spacing-lg); }
+.ml-5 { margin-left: var(--spacing-xl); }
+
+.mr-1 { margin-right: var(--spacing-xs); }
+.mr-2 { margin-right: var(--spacing-sm); }
+.mr-3 { margin-right: var(--spacing-md); }
+.mr-4 { margin-right: var(--spacing-lg); }
+.mr-5 { margin-right: var(--spacing-xl); }
+
+.pt-1 { padding-top: var(--spacing-xs); }
+.pt-2 { padding-top: var(--spacing-sm); }
+.pt-3 { padding-top: var(--spacing-md); }
+.pt-4 { padding-top: var(--spacing-lg); }
+.pt-5 { padding-top: var(--spacing-xl); }
+
+.pb-1 { padding-bottom: var(--spacing-xs); }
+.pb-2 { padding-bottom: var(--spacing-sm); }
+.pb-3 { padding-bottom: var(--spacing-md); }
+.pb-4 { padding-bottom: var(--spacing-lg); }
+.pb-5 { padding-bottom: var(--spacing-xl); }
+
+.pl-1 { padding-left: var(--spacing-xs); }
+.pl-2 { padding-left: var(--spacing-sm); }
+.pl-3 { padding-left: var(--spacing-md); }
+.pl-4 { padding-left: var(--spacing-lg); }
+.pl-5 { padding-left: var(--spacing-xl); }
+
+.pr-1 { padding-right: var(--spacing-xs); }
+.pr-2 { padding-right: var(--spacing-sm); }
+.pr-3 { padding-right: var(--spacing-md); }
+.pr-4 { padding-right: var(--spacing-lg); }
+.pr-5 { padding-right: var(--spacing-xl); }
+
+/* 文字颜色 */
+.text-primary { color: var(--primary-color); }
+.text-secondary { color: var(--secondary-color); }
+.text-success { color: var(--success-color); }
+.text-warning { color: var(--warning-color); }
+.text-error { color: var(--error-color); }
+.text-muted { color: var(--text-muted); }
+.text-white { color: var(--text-white); }
+
+/* 背景颜色 */
+.bg-primary { background: var(--primary-color); }
+.bg-secondary { background: var(--secondary-color); }
+.bg-success { background: var(--success-color); }
+.bg-warning { background: var(--warning-color); }
+.bg-error { background: var(--error-color); }
+.bg-white { background: var(--background-secondary); }
+
+/* 圆角 */
+.rounded-sm { border-radius: var(--border-radius-small); }
+.rounded { border-radius: var(--border-radius-medium); }
+.rounded-lg { border-radius: var(--border-radius-large); }
+.rounded-full { border-radius: var(--border-radius-round); }
+
+/* 阴影 */
+.shadow-sm { box-shadow: var(--shadow-light); }
+.shadow { box-shadow: var(--shadow-medium); }
+.shadow-lg { box-shadow: var(--shadow-heavy); }
+
+/* 动画 */
+.fade-in { animation: fadeIn 0.3s ease-in-out; }
+.slide-in { animation: slideIn 0.3s ease-in-out; }
+.pulse { animation: pulse 2s infinite; }

+ 301 - 0
src/styles/_mixins.scss

@@ -0,0 +1,301 @@
+// 通用混合样式
+
+// 卡片样式
+@mixin card-style($padding: var(--spacing-lg), $radius: var(--border-radius-medium)) {
+  background: var(--background-card);
+  border-radius: $radius;
+  padding: $padding;
+  box-shadow: var(--shadow-medium);
+  backdrop-filter: blur(10px);
+  border: 1px solid var(--border-light);
+  transition: var(--transition-normal);
+  
+  &:hover {
+    transform: translateY(-2px);
+    box-shadow: var(--shadow-heavy);
+  }
+}
+
+// 按钮样式
+@mixin button-primary($size: medium) {
+  background: var(--primary-gradient);
+  color: var(--text-white);
+  border: none;
+  border-radius: var(--border-radius-medium);
+  font-weight: var(--font-weight-semibold);
+  cursor: pointer;
+  transition: var(--transition-normal);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  gap: var(--spacing-sm);
+  
+  @if $size == small {
+    padding: var(--spacing-sm) var(--spacing-md);
+    font-size: var(--font-size-sm);
+  } @else if $size == medium {
+    padding: var(--spacing-md) var(--spacing-lg);
+    font-size: var(--font-size-md);
+  } @else if $size == large {
+    padding: var(--spacing-lg) var(--spacing-xl);
+    font-size: var(--font-size-lg);
+  }
+  
+  &:hover:not(:disabled) {
+    transform: translateY(-1px);
+    box-shadow: var(--shadow-primary);
+  }
+  
+  &:disabled {
+    background: var(--text-muted);
+    cursor: not-allowed;
+    transform: none;
+    box-shadow: none;
+  }
+}
+
+@mixin button-secondary($size: medium) {
+  background: var(--background-secondary);
+  color: var(--primary-color);
+  border: 2px solid var(--primary-color);
+  border-radius: var(--border-radius-medium);
+  font-weight: var(--font-weight-semibold);
+  cursor: pointer;
+  transition: var(--transition-normal);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  gap: var(--spacing-sm);
+  
+  @if $size == small {
+    padding: var(--spacing-sm) var(--spacing-md);
+    font-size: var(--font-size-sm);
+  } @else if $size == medium {
+    padding: var(--spacing-md) var(--spacing-lg);
+    font-size: var(--font-size-md);
+  } @else if $size == large {
+    padding: var(--spacing-lg) var(--spacing-xl);
+    font-size: var(--font-size-lg);
+  }
+  
+  &:hover:not(:disabled) {
+    background: var(--primary-color);
+    color: var(--text-white);
+    transform: translateY(-1px);
+  }
+  
+  &:disabled {
+    border-color: var(--text-muted);
+    color: var(--text-muted);
+    cursor: not-allowed;
+    transform: none;
+  }
+}
+
+// 头部样式
+@mixin header-style($background: var(--primary-gradient)) {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: var(--spacing-lg) var(--spacing-md);
+  background: $background;
+  color: var(--text-white);
+  box-shadow: var(--shadow-medium);
+  
+  .header-left, .header-right {
+    width: 40px;
+    height: 40px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    border-radius: var(--border-radius-medium);
+    background: var(--background-overlay);
+    cursor: pointer;
+    transition: var(--transition-normal);
+    
+    &:hover {
+      background: rgba(255, 255, 255, 0.2);
+      transform: scale(1.05);
+    }
+    
+    svg, i {
+      width: 20px;
+      height: 20px;
+      color: var(--text-white);
+    }
+  }
+  
+  .header-title {
+    font-size: var(--font-size-xl);
+    font-weight: var(--font-weight-semibold);
+    color: var(--text-white);
+    text-align: center;
+  }
+}
+
+// 列表项样式
+@mixin list-item-style {
+  background: var(--background-card);
+  border-radius: var(--border-radius-medium);
+  padding: var(--spacing-md);
+  margin-bottom: var(--spacing-md);
+  box-shadow: var(--shadow-light);
+  border: 1px solid var(--border-light);
+  transition: var(--transition-normal);
+  
+  &:hover {
+    transform: translateY(-1px);
+    box-shadow: var(--shadow-medium);
+  }
+  
+  &:last-child {
+    margin-bottom: 0;
+  }
+}
+
+// 标签样式
+@mixin tag-style($color: var(--primary-color)) {
+  padding: var(--spacing-xs) var(--spacing-sm);
+  background: rgba($color, 0.1);
+  color: $color;
+  border-radius: var(--border-radius-small);
+  font-size: var(--font-size-xs);
+  font-weight: var(--font-weight-medium);
+  display: inline-block;
+}
+
+// Progress bar mixin
+@mixin progress-bar-style($color: #4CAF50) {
+  width: 100%;
+  height: 8px;
+  background: #f0f0f0;
+  border-radius: 4px;
+  overflow: hidden;
+  
+  .progress-fill {
+    height: 100%;
+    background: linear-gradient(90deg, $color 0%, lighten($color, 20%) 100%);
+    border-radius: 4px;
+    transition: width 0.3s ease;
+  }
+}
+
+// 空状态样式
+@mixin empty-state-style {
+  text-align: center;
+  padding: var(--spacing-xxl) var(--spacing-lg);
+  color: rgba(255, 255, 255, 0.8);
+  
+  .empty-icon {
+    width: 80px;
+    height: 80px;
+    margin: 0 auto var(--spacing-lg);
+    opacity: 0.6;
+  }
+  
+  .empty-text {
+    font-size: var(--font-size-lg);
+    font-weight: var(--font-weight-semibold);
+    margin-bottom: var(--spacing-sm);
+  }
+  
+  .empty-sub-text {
+    font-size: var(--font-size-md);
+    opacity: 0.8;
+  }
+}
+
+// 响应式断点
+@mixin mobile {
+  @media (max-width: 480px) {
+    @content;
+  }
+}
+
+@mixin tablet {
+  @media (max-width: 768px) {
+    @content;
+  }
+}
+
+@mixin desktop {
+  @media (min-width: 769px) {
+    @content;
+  }
+}
+
+// 文本截断
+@mixin text-ellipsis($lines: 1) {
+  @if $lines == 1 {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+  } @else {
+    display: -webkit-box;
+    -webkit-line-clamp: $lines;
+    -webkit-box-orient: vertical;
+    overflow: hidden;
+  }
+}
+
+// 居中对齐
+@mixin center-flex($direction: row) {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  flex-direction: $direction;
+}
+
+// 绝对居中
+@mixin absolute-center {
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+}
+
+// 毛玻璃效果
+@mixin glass-effect($opacity: 0.1) {
+  background: rgba(255, 255, 255, $opacity);
+  backdrop-filter: blur(10px);
+  border: 1px solid rgba(255, 255, 255, 0.2);
+}
+
+// 动画关键帧
+@keyframes fadeIn {
+  from {
+    opacity: 0;
+    transform: translateY(20px);
+  }
+  to {
+    opacity: 1;
+    transform: translateY(0);
+  }
+}
+
+@keyframes slideIn {
+  from {
+    transform: translateX(-100%);
+  }
+  to {
+    transform: translateX(0);
+  }
+}
+
+@keyframes pulse {
+  0%, 100% {
+    transform: scale(1);
+  }
+  50% {
+    transform: scale(1.05);
+  }
+}
+
+@keyframes spin {
+  from {
+    transform: rotate(0deg);
+  }
+  to {
+    transform: rotate(360deg);
+  }
+}

+ 150 - 0
src/styles/_variables.scss

@@ -0,0 +1,150 @@
+// 全局色彩变量
+:root {
+  // 主色调 - 环保绿色系
+  --primary-color: #4caf50;
+  --primary-light: #81c784;
+  --primary-dark: #2e7d32;
+  --primary-gradient: linear-gradient(135deg, #4caf50 0%, #66bb6a 100%);
+  
+  // 辅助色调
+  --secondary-color: #2196f3;
+  --secondary-light: #64b5f6;
+  --secondary-dark: #1976d2;
+  --secondary-gradient: linear-gradient(135deg, #2196f3 0%, #64b5f6 100%);
+  
+  // 背景色
+  --background-primary: linear-gradient(135deg, #e8f5e8 0%, #f0f8f0 100%);
+  --background-secondary: #ffffff;
+  --background-card: rgba(255, 255, 255, 0.95);
+  --background-overlay: rgba(255, 255, 255, 0.1);
+  
+  // 文字色彩
+  --text-primary: #333333;
+  --text-secondary: #666666;
+  --text-muted: #999999;
+  --text-white: #ffffff;
+  
+  // 状态色彩
+  --success-color: #4caf50;
+  --warning-color: #ff9800;
+  --error-color: #f44336;
+  --info-color: #2196f3;
+  
+  // 边框和分割线
+  --border-color: #e0e0e0;
+  --border-light: rgba(255, 255, 255, 0.2);
+  --divider-color: #f5f5f5;
+  
+  // 阴影
+  --shadow-light: 0 2px 8px rgba(0, 0, 0, 0.1);
+  --shadow-medium: 0 4px 16px rgba(0, 0, 0, 0.1);
+  --shadow-heavy: 0 8px 32px rgba(0, 0, 0, 0.15);
+  --shadow-primary: 0 4px 15px rgba(76, 175, 80, 0.3);
+  
+  // 圆角
+  --border-radius-small: 8px;
+  --border-radius-medium: 12px;
+  --border-radius-large: 16px;
+  --border-radius-round: 50%;
+  
+  // 间距
+  --spacing-xs: 4px;
+  --spacing-sm: 8px;
+  --spacing-md: 16px;
+  --spacing-lg: 20px;
+  --spacing-xl: 24px;
+  --spacing-xxl: 32px;
+  
+  // 字体大小
+  --font-size-xs: 10px;
+  --font-size-sm: 12px;
+  --font-size-md: 14px;
+  --font-size-lg: 16px;
+  --font-size-xl: 18px;
+  --font-size-xxl: 20px;
+  --font-size-title: 24px;
+  
+  // 字体权重
+  --font-weight-normal: 400;
+  --font-weight-medium: 500;
+  --font-weight-semibold: 600;
+  --font-weight-bold: 700;
+  
+  // 过渡动画
+  --transition-fast: 0.2s ease;
+  --transition-normal: 0.3s ease;
+  --transition-slow: 0.5s ease;
+  
+  // Z-index层级
+  --z-index-dropdown: 1000;
+  --z-index-sticky: 1020;
+  --z-index-fixed: 1030;
+  --z-index-modal-backdrop: 1040;
+  --z-index-modal: 1050;
+  --z-index-popover: 1060;
+  --z-index-tooltip: 1070;
+}
+
+// SCSS变量(兼容性)
+$primary-color: #4caf50;
+$primary-light: #81c784;
+$primary-dark: #2e7d32;
+$primary-gradient: linear-gradient(135deg, #4caf50 0%, #66bb6a 100%);
+
+$secondary-color: #2196f3;
+$secondary-light: #64b5f6;
+$secondary-dark: #1976d2;
+$secondary-gradient: linear-gradient(135deg, #2196f3 0%, #64b5f6 100%);
+
+$background-primary: linear-gradient(135deg, #e8f5e8 0%, #f0f8f0 100%);
+$background-secondary: #ffffff;
+$background-card: rgba(255, 255, 255, 0.95);
+$background-overlay: rgba(255, 255, 255, 0.1);
+
+$text-primary: #333333;
+$text-secondary: #666666;
+$text-muted: #999999;
+$text-white: #ffffff;
+
+$success-color: #4caf50;
+$warning-color: #ff9800;
+$error-color: #f44336;
+$info-color: #2196f3;
+
+$border-color: #e0e0e0;
+$border-light: rgba(255, 255, 255, 0.2);
+$divider-color: #f5f5f5;
+
+$shadow-light: 0 2px 8px rgba(0, 0, 0, 0.1);
+$shadow-medium: 0 4px 16px rgba(0, 0, 0, 0.1);
+$shadow-heavy: 0 8px 32px rgba(0, 0, 0, 0.15);
+$shadow-primary: 0 4px 15px rgba(76, 175, 80, 0.3);
+
+$border-radius-small: 8px;
+$border-radius-medium: 12px;
+$border-radius-large: 16px;
+$border-radius-round: 50%;
+
+$spacing-xs: 4px;
+$spacing-sm: 8px;
+$spacing-md: 16px;
+$spacing-lg: 20px;
+$spacing-xl: 24px;
+$spacing-xxl: 32px;
+
+$font-size-xs: 10px;
+$font-size-sm: 12px;
+$font-size-md: 14px;
+$font-size-lg: 16px;
+$font-size-xl: 18px;
+$font-size-xxl: 20px;
+$font-size-title: 24px;
+
+$font-weight-normal: 400;
+$font-weight-medium: 500;
+$font-weight-semibold: 600;
+$font-weight-bold: 700;
+
+$transition-fast: 0.2s ease;
+$transition-normal: 0.3s ease;
+$transition-slow: 0.5s ease;

+ 42 - 0
src/styles/iconfont.scss

@@ -0,0 +1,42 @@
+/* SVG 图标系统 - 再生视界回收APP专用 */
+
+.recycle-icon {
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  width: 1em;
+  height: 1em;
+  fill: currentColor;
+  stroke: currentColor;
+  stroke-width: 0;
+}
+
+/* 尺寸变体 */
+.icon-sm { width: 14px; height: 14px; }
+.icon-md { width: 18px; height: 18px; }
+.icon-lg { width: 24px; height: 24px; }
+.icon-xl { width: 32px; height: 32px; }
+
+/* 颜色变体 */
+.icon-primary { color: #2e7d32; }
+.icon-secondary { color: #6c757d; }
+.icon-success { color: #28a745; }
+.icon-warning { color: #ffc107; }
+.icon-danger { color: #dc3545; }
+.icon-info { color: #17a2b8; }
+
+/* 动画效果 */
+.icon-spin {
+  animation: icon-spin 2s infinite linear;
+}
+
+@keyframes icon-spin {
+  0% { transform: rotate(0deg); }
+  100% { transform: rotate(360deg); }
+}
+
+/* 悬停效果 */
+.icon-hover:hover {
+  opacity: 0.8;
+  transition: opacity 0.2s ease;
+}