徐福静0235668 před 13 hodinami
rodič
revize
67a790699b

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

@@ -10,6 +10,7 @@ import { ConsultationOrder } from './pages/customer-service/consultation-order/c
 import { ProjectList } from './pages/customer-service/project-list/project-list';
 import { ProjectDetail } from './pages/customer-service/project-detail/project-detail';
 import { CaseLibrary } from './pages/customer-service/case-library/case-library';
+
 // 客服工作台子页面
 import { ConsultationListComponent } from './pages/customer-service/dashboard/pages/consultation-list/consultation-list.component';
 import { AssignmentListComponent } from './pages/customer-service/dashboard/pages/assignment-list/assignment-list.component';
@@ -66,6 +67,7 @@ export const routes: Routes = [
       { path: 'project-list', component: ProjectList, title: '项目列表' },
       { path: 'project-detail/:id', component: DesignerProjectDetail, title: '项目详情' },
       { path: 'case-library', component: CaseLibrary, title: '案例库' },
+
       // 工作台子页面路由
       { path: 'consultation-list', component: ConsultationListComponent, title: '咨询列表' },
       { path: 'assignment-list', component: AssignmentListComponent, title: '待派单列表' },

+ 289 - 365
src/app/pages/customer-service/case-library/case-library.html

@@ -1,400 +1,324 @@
 <div class="case-library-container">
-  <!-- 中间内容区 -->
-  <div class="content-wrapper">
-      <!-- 欢迎区域 -->
-      <section class="welcome-section">
-        <h2>案例库</h2>
-        <p>今天是 {{ currentDate.toLocaleDateString('zh-CN', { year: 'numeric', month: 'long', day: 'numeric', weekday: 'long' }) }},为客户提供最佳的设计参考</p>
-      </section>
-
-      <!-- 筛选区域(固定在顶部) -->
-      <section class="filter-section">
-        <div class="filter-header">
-          <button class="filter-toggle-btn" (click)="toggleFilterPanel()">
-            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-              <line x1="4" y1="10" x2="20" y2="10"></line>
-              <line x1="4" y1="14" x2="20" y2="14"></line>
-              <line x1="4" y1="18" x2="13" y2="18"></line>
-            </svg>
-            <span>高级筛选</span>
-          </button>
-          <button class="reset-filter-btn" (click)="resetFilters()">
-            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-              <polyline points="23 4 23 10 17 10"></polyline>
-              <polyline points="1 20 1 14 7 14"></polyline>
-              <path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path>
-            </svg>
-            <span>重置筛选</span>
-          </button>
-        </div>
+  <!-- 页面头部 -->
+  <div class="page-header">
+    <h1>案例库</h1>
+    <div class="header-actions">
+      <button class="btn btn-primary" (click)="showStatistics()">
+        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+          <path d="M3 3v18h18"></path>
+          <path d="m19 9-5 5-4-4-3 3"></path>
+        </svg>
+        数据统计
+      </button>
+    </div>
+  </div>
 
-        <!-- 高级筛选面板 -->
-        <div class="filter-panel" [class.show]="showFilterPanel()">
-          <form [formGroup]="filterForm" class="filter-form">
-            <!-- 第一行:主要筛选项 -->
-            <div class="filter-row primary-filters">
-              <div class="filter-group">
-                <label>项目类型</label>
-                <select formControlName="projectType">
-                  <option value="">全部类型</option>
-                  @for (type of projectTypeOptions; track type) {
-                    <option [value]="type">{{ type }}</option>
-                  }
-                </select>
-              </div>
-              
-              <div class="filter-group">
-                <label>户型</label>
-                <select formControlName="houseType">
-                  <option value="">全部户型</option>
-                  @for (type of houseTypeOptions; track type) {
-                    <option [value]="type">{{ type }}</option>
-                  }
-                </select>
-              </div>
-              
-              <div class="filter-group">
-                <label>价格范围</label>
-                <select formControlName="price">
-                  <option value="">全部价格</option>
-                  @for (price of priceOptions; track price) {
-                    <option [value]="price">{{ price }}</option>
-                  }
-                </select>
-              </div>
-              
-              <div class="filter-group">
-                <label>排序方式</label>
-                <select formControlName="sortBy">
-                  @for (option of sortOptions; track option.value) {
-                    <option [value]="option.value">{{ option.label }}</option>
-                  }
-                </select>
-              </div>
-            </div>
-            
-            <!-- 第二行:风格选择 -->
-            <div class="filter-row style-filters">
-              <div class="filter-group style-group">
-                <label>设计风格</label>
-                <div class="style-checkboxes">
-                  @for (style of styleOptions; track style) {
-                    <label class="style-checkbox">
-                      <input 
-                        type="checkbox" 
-                        [value]="style"
-                        (change)="onStyleChange(style, $event.target.checked)"
-                      />
-                      <span class="style-tag">{{ style }}</span>
-                    </label>
-                  }
-                </div>
-              </div>
-            </div>
-            
-            <!-- 第三行:高级筛选 -->
-            <div class="filter-row advanced-filters">
-              <div class="filter-group">
-                <label>子类型</label>
-                <select formControlName="subType">
-                  <option value="">全部子类型</option>
-                  @for (subType of subTypeOptions; track subType) {
-                    <option [value]="subType">{{ subType }}</option>
-                  }
-                </select>
-              </div>
-              
-              <div class="filter-group">
-                <label>渲染级别</label>
-                <select formControlName="renderingLevel">
-                  <option value="">全部级别</option>
-                  @for (level of renderingLevelOptions; track level) {
-                    <option [value]="level">{{ level }}</option>
-                  }
-                </select>
-              </div>
-              
-              <div class="filter-group checkbox-group">
-                <label class="favorite-checkbox">
-                  <input type="checkbox" formControlName="favorite" />
-                  <span class="checkbox-text">只看收藏</span>
-                </label>
+  <!-- 统计模块 -->
+  @if (showStatsPanel) {
+    <div class="stats-panel">
+      <div class="stats-grid">
+        <div class="stat-card">
+          <h3>Top5 分享案例</h3>
+          <div class="stat-list">
+            @for (item of topSharedCases; track item.id) {
+              <div class="stat-item">
+                <span class="case-name">{{ item.name }}</span>
+                <span class="stat-value">{{ item.shareCount }} 次分享</span>
               </div>
-            </div>
-          </form>
-        </div>
-      </section>
-
-      <!-- 案例展示区域 -->
-      <section class="cases-section">
-        <div class="section-header">
-          <h3>精选案例 <span class="cases-count">({{ filteredCases().length }})</span></h3>
+            }
+          </div>
         </div>
         
-        <!-- 案例网格(瀑布流布局) -->
-        <div class="cases-grid">
-          <div *ngIf="filteredCases().length === 0" class="empty-state">
-            <svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-              <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
-              <circle cx="8.5" cy="8.5" r="1.5"></circle>
-              <polyline points="21 15 16 10 5 21"></polyline>
-            </svg>
-            <p>未找到符合条件的案例</p>
-            <button class="btn-reset" (click)="resetFilters()">重置筛选条件</button>
-          </div>
-          
-          <div *ngFor="let caseItem of paginatedCases()" class="case-card" (click)="viewCaseDetails(caseItem)">
-            <div class="case-image-container">
-              <img [src]="caseItem.coverImage" [alt]="caseItem.name" class="case-image">
-              <div class="case-overlay">
-                <button class="favorite-btn" (click)="$event.stopPropagation(); toggleFavorite(caseItem.id)">
-                  <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-                    <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path>
-                  </svg>
-                  <span *ngIf="caseItem.isFavorite">已收藏</span>
-                  <span *ngIf="!caseItem.isFavorite">收藏</span>
-                </button>
-                <button class="share-btn" (click)="$event.stopPropagation(); shareCase(caseItem.id)">
-                  <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-                    <path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"></path>
-                    <polyline points="16 6 12 2 8 6"></polyline>
-                    <line x1="12" y1="2" x2="12" y2="15"></line>
-                  </svg>
-                  <span>分享</span>
-                </button>
+        <div class="stat-card">
+          <h3>客户最喜欢案例风格</h3>
+          <div class="stat-list">
+            @for (item of favoriteStyles; track item.style) {
+              <div class="stat-item">
+                <span class="style-name">{{ item.style }}</span>
+                <span class="stat-value">{{ item.count }} 次收藏</span>
               </div>
-            </div>
-            
-            <div class="case-info">
-              <h4 class="case-name">{{ caseItem.name }}</h4>
-              
-              <div class="case-meta">
-                <div class="meta-item">
-                  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-                    <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
-                    <circle cx="12" cy="7" r="4"></circle>
-                  </svg>
-                  <span>{{ caseItem.designer }}</span>
-                </div>
-                <div class="meta-item">
-                  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-                    <polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline>
-                  </svg>
-                  <span>{{ caseItem.area }}㎡</span>
-                </div>
-                <div class="meta-item">
-                  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-                    <path d="M14 10h4.764a2 2 0 0 1 1.789 2.894l-3.5 7A2 2 0 0 1 15.263 21h-4.017c-.163 0-.326-.02-.485-.06L7 20m7-10V5a2 2 0 0 0-2-2h-.095c-.5 0-.905.405-.905.905 0 .714-.211 1.412-.608 2.006L7 11v9m7-10h-2M7 20H5a2 2 0 0 1-2-2v-6a2 2 0 0 1 2-2h2.5"></path>
-                  </svg>
-                  <span>{{ caseItem.views }}浏览</span>
-                </div>
-              </div>
-              
-              <div class="case-tags">
-                <span *ngFor="let tag of caseItem.tags" class="tag">{{ tag }}</span>
-              </div>
-            </div>
+            }
           </div>
         </div>
         
-        <!-- 分页控件 -->
-        <div class="pagination" *ngIf="totalPages() > 1">
-          <button class="page-btn" (click)="prevPage()" [disabled]="currentPage() === 1">
-            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-              <polyline points="15 18 9 12 15 6"></polyline>
-            </svg>
-          </button>
-          
-          <ng-container *ngFor="let page of pageNumbers()">
-            <button 
-              *ngIf="page !== -1; else ellipsis"
-              class="page-btn"
-              [class.active]="page === currentPage()"
-              (click)="goToPage(page)"
-            >
-              {{ page }}
-            </button>
-            <ng-template #ellipsis>
-              <div class="pagination-ellipsis">...</div>
-            </ng-template>
-          </ng-container>
-          
-          <button class="page-btn" (click)="nextPage()" [disabled]="currentPage() === totalPages()">
-            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-              <polyline points="9 18 15 12 9 6"></polyline>
-            </svg>
-          </button>
+        <div class="stat-card">
+          <h3>设计师作品推荐率</h3>
+          <div class="stat-list">
+            @for (item of designerRecommendations; track item.designer) {
+              <div class="stat-item">
+                <span class="designer-name">{{ item.designer }}</span>
+                <span class="stat-value">{{ item.rate }}% 推荐率</span>
+              </div>
+            }
+          </div>
         </div>
-      </section>
+      </div>
     </div>
+  }
 
-  <!-- 案例详情模态框 -->
-  <div class="case-modal" *ngIf="selectedCase()" (click)="closeCaseDetails()">
-    <div class="modal-content" (click)="$event.stopPropagation()">
-      <button class="close-btn" (click)="closeCaseDetails()">
-        <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-          <line x1="18" y1="6" x2="6" y2="18"></line>
-          <line x1="6" y1="6" x2="18" y2="18"></line>
+  <!-- 筛选栏 -->
+  <div class="filter-bar">
+    <div class="filter-group">
+      <div class="search-box">
+        <input 
+          type="text" 
+          placeholder="搜索案例名称、设计师或关键词..."
+          [formControl]="searchControl"
+          class="search-input"
+        >
+        <svg class="search-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+          <circle cx="11" cy="11" r="8"></circle>
+          <path d="m21 21-4.3-4.3"></path>
         </svg>
-      </button>
-      
-      <div class="case-detail-header">
-        <h2>{{ selectedCase()?.name }}</h2>
-        <div class="case-detail-meta">
-          <span>{{ selectedCase()?.designer }}</span>
-          <span>{{ selectedCase()?.area }}㎡</span>
-          <span>{{ formatDate(selectedCase()?.createdAt!) }}</span>
-        </div>
       </div>
-      
-      <!-- 案例图片轮播 -->
-      <div class="case-image-gallery">
-        <div class="main-image">
-          <img [src]="selectedCase()?.coverImage" [alt]="selectedCase()?.name">
-        </div>
-        <div class="thumbnails">
+    </div>
+
+    <div class="filter-group">
+      <select [formControl]="projectTypeControl" class="filter-select">
+        <option value="">项目类型</option>
+        <option value="工装">工装</option>
+        <option value="家装">家装</option>
+      </select>
+
+      <select [formControl]="spaceTypeControl" class="filter-select">
+        <option value="">空间类型</option>
+        <option value="平层">平层</option>
+        <option value="复式">复式</option>
+        <option value="别墅">别墅</option>
+        <option value="自建房">自建房</option>
+      </select>
+
+      <select [formControl]="renderingLevelControl" class="filter-select">
+        <option value="">渲染水平</option>
+        <option value="高端">高端</option>
+        <option value="中端">中端</option>
+        <option value="低端">低端</option>
+      </select>
+
+      <select [formControl]="styleControl" class="filter-select">
+        <option value="">设计风格</option>
+        <option value="现代">现代</option>
+        <option value="中式">中式</option>
+        <option value="欧式">欧式</option>
+        <option value="美式">美式</option>
+        <option value="日式">日式</option>
+        <option value="工业风">工业风</option>
+        <option value="极简风">极简风</option>
+        <option value="轻奢风">轻奢风</option>
+      </select>
+
+      <select [formControl]="areaRangeControl" class="filter-select">
+        <option value="">面积范围</option>
+        <option value="0-50">50㎡以下</option>
+        <option value="50-100">50-100㎡</option>
+        <option value="100-150">100-150㎡</option>
+        <option value="150-200">150-200㎡</option>
+        <option value="200+">200㎡以上</option>
+      </select>
+    </div>
+
+    <div class="filter-actions">
+      <button class="btn btn-secondary" (click)="resetFilters()">
+        重置筛选
+      </button>
+      <button class="btn btn-primary" (click)="applyFilters()">
+        应用筛选
+      </button>
+    </div>
+  </div>
+
+  <!-- 案例网格 -->
+  <div class="cases-grid">
+    @for (caseItem of filteredCases; track caseItem.id) {
+      <div class="case-card">
+        <!-- 图片容器 -->
+        <div class="case-image-container">
           <img 
-            *ngFor="let image of selectedCase()?.detailImages"
-            [src]="image"
-            [alt]="selectedCase()?.name"
-            class="thumbnail"
+            [src]="caseItem.coverImage" 
+            [alt]="caseItem.name"
+            class="case-image"
+            (click)="viewCaseDetail(caseItem)"
           >
-        </div>
-      </div>
-      
-      <!-- 案例详情信息 -->
-      <div class="case-detail-info">
-        <div class="info-section">
-          <h3>案例详情</h3>
-          <p>{{ selectedCase()?.description }}</p>
-          <p>本案例采用了{{ selectedCase()?.style }}风格设计,为{{ selectedCase()?.houseType }}户型,面积{{ selectedCase()?.area }}平方米。设计师{{ selectedCase()?.designer }}根据客户需求,融合了现代美学与实用功能,打造了舒适且富有个性的居住空间。</p>
-        </div>
-        
-        <div class="info-section">
-          <h3>基本信息</h3>
-          <div class="info-grid">
-            <div class="info-item">
-              <label>风格</label>
-              <span>{{ getSelectedCaseStyle() }}</span>
-            </div>
-            <div class="info-item">
-              <label>户型</label>
-              <span>{{ selectedCase()?.houseType }}</span>
-            </div>
-            <div class="info-item">
-              <label>楼盘</label>
-              <span>{{ selectedCase()?.property }}</span>
-            </div>
-            <div class="info-item">
-              <label>面积</label>
-              <span>{{ selectedCase()?.area }}㎡</span>
-            </div>
-            <div class="info-item">
-              <label>分类</label>
-              <span>{{ selectedCase()?.category }}</span>
-            </div>
-            <div class="info-item">
-              <label>项目类型</label>
-              <span>{{ selectedCase()?.projectType }}</span>
-            </div>
-            <div class="info-item">
-              <label>细分类型</label>
-              <span>{{ selectedCase()?.subType }}</span>
-            </div>
-            <div class="info-item">
-              <label>渲染水平</label>
-              <span>{{ selectedCase()?.renderingLevel }}</span>
-            </div>
-            <div class="info-item">
-              <label>浏览次数</label>
-              <span>{{ selectedCase()?.views }}</span>
+          
+          <!-- 图片覆盖层 -->
+          <div class="image-overlay">
+            <div class="case-badges">
+              <span class="badge project-type">{{ caseItem.projectType }}</span>
+              <span class="badge space-type">{{ caseItem.spaceType }}</span>
+              <span class="badge rendering-level">{{ caseItem.renderingLevel }}</span>
             </div>
-            <div class="info-item">
-              <label>收藏次数</label>
-              <span>{{ selectedCase()?.favoriteCount }}</span>
+            
+            <div class="action-buttons">
+              <button 
+                class="btn-icon favorite-btn" 
+                [class.active]="caseItem.isFavorite"
+                (click)="toggleFavorite(caseItem)"
+              >
+                <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
+                  <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path>
+                </svg>
+              </button>
+              
+              <button class="btn-icon share-btn" (click)="shareCase(caseItem)">
+                <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+                  <circle cx="18" cy="5" r="3"></circle>
+                  <circle cx="6" cy="12" r="3"></circle>
+                  <circle cx="18" cy="19" r="3"></circle>
+                  <line x1="8.59" y1="13.51" x2="15.42" y2="17.49"></line>
+                  <line x1="15.41" y1="6.51" x2="8.59" y2="10.49"></line>
+                </svg>
+              </button>
             </div>
-            <div class="info-item">
-              <label>喜欢次数</label>
-              <span>{{ selectedCase()?.likeCount }}</span>
+          </div>
+        </div>
+
+        <!-- 案例信息 -->
+        <div class="case-info">
+          <div class="case-header">
+            <h3 class="case-name" (click)="viewCaseDetail(caseItem)">{{ caseItem.name }}</h3>
+            <button class="info-share-btn" (click)="shareCase(caseItem)" title="分享案例">
+              <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+                <circle cx="18" cy="5" r="3"></circle>
+                <circle cx="6" cy="12" r="3"></circle>
+                <circle cx="18" cy="19" r="3"></circle>
+                <line x1="8.59" y1="13.51" x2="15.42" y2="17.49"></line>
+                <line x1="15.41" y1="6.51" x2="8.59" y2="10.49"></line>
+              </svg>
+            </button>
+          </div>
+          
+          <div class="case-meta">
+            <div class="meta-item">
+              <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+                <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
+                <circle cx="12" cy="7" r="4"></circle>
+              </svg>
+              <span>{{ caseItem.designer }}</span>
+              @if (isInternalUser) {
+                <span class="team-badge">{{ caseItem.team }}</span>
+              }
             </div>
-            <div class="info-item">
-              <label>分享次数</label>
-              <span>{{ selectedCase()?.shareCount }}</span>
+            
+            <div class="meta-item">
+              <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+                <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
+                <line x1="3" y1="9" x2="21" y2="9"></line>
+              </svg>
+              <span>{{ caseItem.area }}㎡</span>
             </div>
-            <div class="info-item">
-              <label>转化率</label>
-              <span>{{ selectedCase()?.conversionRate }}%</span>
+            
+            <div class="meta-item">
+              <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+                <path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"></path>
+                <path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"></path>
+              </svg>
+              <span>{{ caseItem.viewCount }} 浏览</span>
             </div>
           </div>
-        </div>
-        
-        <div class="info-section">
-          <h3>标签</h3>
-          <div class="tags-container">
-            <span *ngFor="let tag of selectedCase()?.tags" class="tag">{{ tag }}</span>
+
+          <!-- 风格标签 -->
+          <div class="case-tags">
+            @for (tag of caseItem.styleTags; track $index) {
+              <span class="tag">{{ tag }}</span>
+            }
           </div>
+
+          <!-- 客户评价 -->
+          @if (caseItem.customerReview) {
+            <div class="customer-review">
+              <p class="review-text">"{{ caseItem.customerReview }}"</p>
+            </div>
+          }
+
+          <!-- 设计师内部信息 -->
+          @if (isInternalUser) {
+            <div class="internal-info">
+              <div class="internal-badge" [class.excellent]="caseItem.isExcellent">
+                {{ caseItem.isExcellent ? '优秀案例库' : '普通案例' }}
+              </div>
+              <div class="internal-stats">
+                <span>分享: {{ caseItem.shareCount }}</span>
+                <span>收藏: {{ caseItem.favoriteCount }}</span>
+              </div>
+            </div>
+          }
         </div>
       </div>
-      
-      <!-- 操作按钮 -->
-      <div class="case-actions">
-        <button 
-          class="primary-btn"
-          (click)="toggleFavorite(selectedCase()?.id!)"
-        >
-          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-            <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path>
-          </svg>
-          <span *ngIf="selectedCase()?.isFavorite">已收藏</span>
-          <span *ngIf="!selectedCase()?.isFavorite">收藏案例</span>
-        </button>
-        <button class="secondary-btn" (click)="shareCase(selectedCase()?.id!)">
-          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-            <path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"></path>
-            <polyline points="16 6 12 2 8 6"></polyline>
-            <line x1="12" y1="2" x2="12" y2="15"></line>
-          </svg>
-          <span>分享案例</span>
-        </button>
-        <button class="secondary-btn">
-          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
-            <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"></path>
-          </svg>
-          <span>查看原图</span>
-        </button>
+    } @empty {
+      <div class="empty-state">
+        <svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+          <path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"></path>
+          <path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"></path>
+        </svg>
+        <h3>暂无案例</h3>
+        <p>尝试调整筛选条件或刷新页面</p>
+        <button class="btn btn-primary" (click)="resetFilters()">重置筛选</button>
       </div>
-    </div>
+    }
   </div>
 
-  <!-- 分享弹窗 -->
-  <div class="share-modal" *ngIf="showShareModal()" (click)="closeShareModal()">
-    <div class="share-modal-content" (click)="$event.stopPropagation()">
-      <button class="close-btn" (click)="closeShareModal()">
-        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
-          <path d="M6 6L18 18M18 6L6 18" stroke="#666" stroke-width="2" stroke-linecap="round"/>
-        </svg>
+  <!-- 分页控件 -->
+  @if (filteredCases.length > 0) {
+    <div class="pagination">
+      <button 
+        class="pagination-btn" 
+        [disabled]="currentPage === 1"
+        (click)="previousPage()"
+      >
+        上一页
+      </button>
+      
+      <span class="page-info">第 {{ currentPage }} 页 / 共 {{ totalPages }} 页</span>
+      
+      <button 
+        class="pagination-btn" 
+        [disabled]="currentPage === totalPages"
+        (click)="nextPage()"
+      >
+        下一页
       </button>
-  
-      <h3>分享案例</h3>
-      <p class="share-tip">复制链接发送给客户,或扫码打开案例页</p>
-  
-      <div class="share-body">
-        <div class="share-link-box">
-          <input class="share-link-input" [value]="shareLink()" readonly />
-          <button class="btn-primary" (click)="copyShareLink()">复制链接</button>
-          <button class="btn-secondary" (click)="openShareLink()">打开</button>
+    </div>
+  }
+
+  <!-- 分享弹窗 -->
+  @if (selectedCase) {
+    <div class="share-modal" (click)="closeShareModal()">
+      <div class="share-content" (click)="$event.stopPropagation()">
+        <div class="share-header">
+          <h3>分享案例</h3>
+          <button class="close-btn" (click)="closeShareModal()">×</button>
         </div>
-  
-        <div class="qr-box">
-          <img *ngIf="qrDataUrl(); else qrPlaceholder" [src]="qrDataUrl()" alt="分享二维码" width="160" height="160" />
-          <ng-template #qrPlaceholder>
-            <div class="qr-placeholder">二维码生成中或不可用</div>
-          </ng-template>
-          <div class="qr-actions" *ngIf="qrDataUrl()">
-            <button class="btn-secondary" (click)="downloadQrCode()">下载二维码</button>
+        
+        <div class="share-options">
+          <div class="qr-code">
+            <img [src]="generateQRCode(selectedCase)" alt="分享二维码">
+            <p>扫描二维码分享</p>
+          </div>
+          
+          <div class="share-links">
+            <div class="share-link">
+              <input 
+                type="text" 
+                [value]="generateShareLink(selectedCase)" 
+                readonly
+                class="link-input"
+              >
+              <button class="btn btn-secondary" (click)="copyShareLink()">复制链接</button>
+            </div>
+            
+            <div class="social-share">
+              <button class="btn btn-primary" (click)="shareToWeCom()">
+                <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
+                  <path d="M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-2-2 2 2 0 0 0-2 2v7h-4v-7a6 6 0 0 1 6-6z"></path>
+                  <rect x="2" y="9" width="4" height="12"></rect>
+                  <circle cx="4" cy="4" r="2"></circle>
+                </svg>
+                分享到企业微信
+              </button>
+            </div>
           </div>
         </div>
       </div>
     </div>
-  </div>
+  }
 </div>

+ 689 - 1211
src/app/pages/customer-service/case-library/case-library.scss

@@ -1,1321 +1,799 @@
-// 全局变量定义
-$primary-color: #165DFF;
-$primary-dark: #0E42CB;
-$secondary-color: #4E5BA6;
-$success-color: #00B42A;
-$warning-color: #FF7D00;
-$danger-color: #F53F3F;
-$text-primary: #1D2129;
-$text-secondary: #4E5969;
-$text-tertiary: #86909C;
-$text-light: #C9CDD4; // 新增浅色文本变量
-$border-color: #E5E6EB;
-$background-primary: #FFFFFF;
-$background-secondary: #F2F3F5;
-$background-tertiary: #F7F8FA;
-$shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
-$shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08);
-$shadow-lg: 0 10px 30px rgba(0, 0, 0, 0.1);
-$border-radius: 8px;
-$transition: all 0.3s ease;
+/* 颜色变量 */
+$primary-color: #007aff;
+$primary-dark: #0051a8;
+$success-color: #34c759;
+$warning-color: #ff9500;
+$error-color: #ff3b30;
+$text-primary: #1d1d1f;
+$text-secondary: #86868b;
+$text-muted: #a1a1aa;
+$background-light: #f5f5f7;
+$border-color: #d2d2d7;
+$white: #ffffff;
+
+// Gray colors
+$gray-50: #f9fafb;
+$gray-100: #f3f4f6;
+$gray-200: #e5e7eb;
+$gray-300: #d1d5db;
+$gray-400: #9ca3af;
+$gray-500: #6b7280;
+$gray-600: #4b5563;
+
+// Green colors
+$green-200: #bbf7d0;
+$green-600: #16a34a;
+
+// Red colors
+$red-500: #ef4444;
 
-// 主容器
 .case-library-container {
-  display: flex;
-  flex-direction: column;
-  height: 100vh;
-  background-color: $background-secondary;
-  color: $text-primary;
-  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
-}
-
-// 顶部导航栏
-.top-navbar {
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  padding: 0 24px;
-  height: 64px;
-  background-color: $background-primary;
-  border-bottom: 1px solid $border-color;
-  box-shadow: $shadow-sm;
-  position: sticky;
-  top: 0;
-  z-index: 1000;
-
-  .navbar-left {
-    display: flex;
-    align-items: center;
-    gap: 16px;
-  }
-
-  .menu-toggle {
-    display: none;
-    background: none;
-    border: none;
-    cursor: pointer;
-    color: $text-primary;
-    padding: 8px;
-    transition: $transition;
-
-    &:hover {
-      color: $primary-color;
-    }
-  }
-
-  .app-title {
-    font-size: 20px;
-    font-weight: 600;
-    color: $primary-color;
-  }
-
-  .navbar-center {
-    flex: 1;
-    max-width: 400px;
-    margin: 0 32px;
-  }
-
-  .search-container {
-    display: flex;
-    align-items: center;
-    background-color: $background-tertiary;
-    border-radius: 20px;
-    padding: 8px 16px;
-    height: 36px;
-
-    .search-input {
-      flex: 1;
-      border: none;
-      background: none;
-      outline: none;
-      font-size: 14px;
-      color: $text-primary;
-
-      &::placeholder {
-        color: $text-tertiary;
-      }
-    }
-
-    .search-button {
-      background: none;
-      border: none;
-      cursor: pointer;
-      color: $text-tertiary;
-      padding: 4px;
-      transition: $transition;
-
-      &:hover {
-        color: $primary-color;
-      }
-    }
-  }
-
-  .navbar-right {
-    display: flex;
-    align-items: center;
-    gap: 16px;
-  }
-
-  .notification-btn {
-    position: relative;
-    background: none;
-    border: none;
-    cursor: pointer;
-    color: $text-secondary;
-    padding: 8px;
-    transition: $transition;
-
-    &:hover {
-      color: $primary-color;
-    }
-
-    .notification-badge {
-      position: absolute;
-      top: 0;
-      right: 0;
-      background-color: $danger-color;
-      color: white;
-      font-size: 10px;
-      padding: 2px 6px;
-      border-radius: 10px;
-      min-width: 16px;
-      text-align: center;
-    }
-  }
-// 在分页相关样式中添加省略号样式
-.pagination-ellipsis {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  width: 36px;
-  height: 36px;
-  font-size: 14px;
-  color: $text-light;
-  cursor: default;
-}
-  .user-profile {
-    display: flex;
-    align-items: center;
-    gap: 8px;
-
-    .user-avatar {
-      width: 36px;
-      height: 36px;
-      border-radius: 50%;
-      object-fit: cover;
-    }
-
-    .user-name {
-      font-size: 14px;
-      font-weight: 500;
-      color: $text-primary;
-    }
-  }
-}
-
-// 主要内容区
-.dashboard-content {
-  display: flex;
-  flex: 1;
-  overflow: hidden;
-}
-
-// 左侧侧边栏
-.sidebar {
-  width: 220px;
-  background-color: $background-primary;
-  border-right: 1px solid $border-color;
-  display: flex;
-  flex-direction: column;
-  transition: $transition;
-
-  .sidebar-nav {
-    flex: 1;
-    padding: 16px 0;
-
-    .nav-item {
-      display: flex;
-      align-items: center;
-      gap: 12px;
-      padding: 12px 24px;
-      color: $text-secondary;
-      text-decoration: none;
-      border-left: 3px solid transparent;
-      transition: $transition;
-
-      &:hover {
-        background-color: $background-tertiary;
-        color: $primary-color;
-      }
-
-      &.active {
-        color: $primary-color;
-        background-color: color-mix(in srgb, $primary-color 5%, transparent);
-        border-left-color: $primary-color;
-        font-weight: 500;
-      }
-    }
-  }
-
-  .sidebar-footer {
-    padding: 16px 24px;
-    border-top: 1px solid $border-color;
-
-    .storage-info {
-      margin-bottom: 16px;
-      font-size: 12px;
-      color: $text-tertiary;
-    }
-
-    .logout-btn {
-      display: flex;
-      align-items: center;
-      gap: 8px;
-
-      padding: 8px 12px;
-      background: none;
-      border: 1px solid $border-color;
-      border-radius: $border-radius;
-      color: $text-secondary;
-      cursor: pointer;
-      font-size: 14px;
-      transition: $transition;
-
-      &:hover {
-        background-color: $background-tertiary;
-        border-color: $danger-color;
-        color: $danger-color;
-      }
-    }
-  }
-}
-
-// 中间内容区
-.content-wrapper {
-  flex: 1;
-  overflow-y: auto;
   padding: 24px;
-}
-
-// 欢迎区域
-.welcome-section {
-  margin-bottom: 24px;
-
-  h2 {
-    font-size: 24px;
-    font-weight: 600;
-    margin-bottom: 8px;
-    color: $text-primary;
-  }
-
-  p {
-    font-size: 14px;
-    color: $text-secondary;
-  }
-}
-
-// 筛选区域
-.filter-section {
-  background-color: $background-primary;
-  border-radius: $border-radius;
-  padding: 16px 20px;
-  box-shadow: $shadow-sm;
-  margin-bottom: 24px;
-  position: sticky;
-  top: 0;
-  z-index: 100; // 降低z-index,避免覆盖模态框
-  backdrop-filter: blur(8px);
-  background-color: rgba(255, 255, 255, 0.95);
-  border-bottom: 1px solid $border-color;
-}
-
-.filter-header {
-  display: flex;
-  gap: 12px;
-  align-items: center;
-}
-
-.filter-toggle-btn,
-.reset-filter-btn {
-  display: flex;
-  align-items: center;
-  gap: 6px;
-  padding: 8px 16px;
-  background-color: $background-tertiary;
-  border: 1px solid $border-color;
-  border-radius: $border-radius;
-  color: $text-secondary;
-  cursor: pointer;
-  font-size: 14px;
-  transition: $transition;
-
-  &:hover {
-    background-color: $background-secondary;
-    border-color: $primary-color;
-    color: $primary-color;
-  }
-}
-
-.filter-panel {
-  background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
-  border: 1px solid #e2e8f0;
-  border-radius: 16px;
-  padding: 24px;
-  margin-bottom: 24px;
-  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
-  transition: all 0.3s ease;
-  
-  &.show {
-    opacity: 1;
-    transform: translateY(0);
-  }
-  
-  &:not(.show) {
-    opacity: 0;
-    transform: translateY(-10px);
-    pointer-events: none;
-  }
-}
-
-.filter-form {
-  .filter-row {
-    display: flex;
-    gap: 20px;
-    margin-bottom: 20px;
-    align-items: flex-end;
-    
-    &:last-child {
-      margin-bottom: 0;
-    }
-    
-    // 主要筛选项样式
-    &.primary-filters {
-      .filter-group {
-        flex: 1;
-        min-width: 180px;
-      }
-    }
-    
-    // 风格筛选样式
-    &.style-filters {
-      .style-group {
-        width: 100%;
-      }
-    }
-    
-    // 高级筛选样式
-    &.advanced-filters {
-      .filter-group {
-        flex: 1;
-        
-        &.checkbox-group {
-          display: flex;
-          align-items: center;
-          justify-content: center;
-        }
-      }
-    }
-  }
-  
-  .filter-group {
-    display: flex;
-    flex-direction: column;
-    gap: 8px;
-    
-    label {
-      font-size: 14px;
-      font-weight: 600;
-      color: #374151;
-      margin-bottom: 4px;
-    }
-    
-    select {
-      padding: 10px 12px;
-      border: 2px solid #e5e7eb;
-      border-radius: 8px;
-      font-size: 14px;
-      background: white;
-      color: #374151;
-      transition: all 0.2s ease;
-      position: relative;
-      z-index: 10;
-      
-      &:focus {
-        outline: none;
-        border-color: #3b82f6;
-        box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
-      }
-      
-      &:hover {
-        border-color: #d1d5db;
-      }
-    }
-  }
-  
-  // 风格选择器样式
-  .style-checkboxes {
-    display: flex;
-    flex-wrap: wrap;
-    gap: 12px;
-    margin-top: 8px;
-  }
-  
-  .style-checkbox {
-    display: flex;
-    align-items: center;
-    cursor: pointer;
-    
-    input[type="checkbox"] {
-      display: none;
-    }
-    
-    .style-tag {
-      padding: 8px 16px;
-      background: white;
-      border: 2px solid #e5e7eb;
-      border-radius: 20px;
-      font-size: 13px;
-      font-weight: 500;
-      color: #6b7280;
-      transition: all 0.2s ease;
-      white-space: nowrap;
-      
-      &:hover {
-        border-color: #3b82f6;
-        color: #3b82f6;
-        transform: translateY(-1px);
-      }
-    }
-    
-    input[type="checkbox"]:checked + .style-tag {
-      background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
-      border-color: #3b82f6;
-      color: white;
-      box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
-    }
-  }
+  max-width: 1440px;
+  margin: 0 auto;
   
-  // 收藏复选框样式
-  .favorite-checkbox {
+  .page-header {
     display: flex;
+    justify-content: space-between;
     align-items: center;
-    gap: 8px;
-    cursor: pointer;
-    padding: 10px 16px;
-    background: white;
-    border: 2px solid #e5e7eb;
-    border-radius: 8px;
-    transition: all 0.2s ease;
+    margin-bottom: 32px;
     
-    &:hover {
-      border-color: #f59e0b;
-      background: #fef3c7;
-    }
-    
-    input[type="checkbox"] {
-      width: 18px;
-      height: 18px;
-      accent-color: #f59e0b;
-    }
-    
-    .checkbox-text {
-      font-size: 14px;
-      font-weight: 500;
-      color: #374151;
-    }
-    
-    input[type="checkbox"]:checked ~ .checkbox-text {
-      color: #f59e0b;
+    h1 {
+      font-size: 28px;
       font-weight: 600;
-    }
-  }
-}
-
-// 案例展示区域优化
-.cases-grid {
-  display: grid;
-  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
-  gap: 24px;
-  margin-top: 24px;
-}
-
-.case-card {
-  background: white;
-  border-radius: 16px;
-  overflow: hidden;
-  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
-  transition: all 0.3s ease;
-  position: relative;
-  z-index: 1;
-  
-  &:hover {
-    transform: translateY(-8px);
-    box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
-  }
-  
-  .case-image {
-    width: 100%;
-    height: 200px;
-    overflow: hidden;
-    position: relative;
-    
-    img, svg {
-      width: 100%;
-      height: 100%;
-      object-fit: cover;
-      transition: transform 0.3s ease;
-    }
-    
-    &:hover img,
-    &:hover svg {
-      transform: scale(1.05);
-    }
-  }
-  
-  .case-info {
-    padding: 20px;
-    
-    .case-title {
-      font-size: 18px;
-      font-weight: 700;
-      color: #1f2937;
-      margin-bottom: 8px;
-      line-height: 1.4;
+      color: $text-primary;
+      margin: 0;
     }
     
-    .case-details {
-      display: flex;
-      flex-wrap: wrap;
-      gap: 12px;
-      margin-bottom: 16px;
-      
-      .detail-item {
+    .header-actions {
+      .btn {
         display: flex;
         align-items: center;
-        gap: 4px;
-        font-size: 13px;
-        color: #6b7280;
-        
-        .label {
-          font-weight: 500;
-        }
+        gap: 8px;
+        padding: 10px 16px;
+        border-radius: 8px;
+        font-weight: 500;
+        transition: all 0.2s ease;
         
-        .value {
-          color: #374151;
+        &:hover {
+          transform: translateY(-1px);
+          box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
         }
       }
     }
-    
-    .case-tags {
-      display: flex;
-      flex-wrap: wrap;
-      gap: 8px;
-      
-      .tag {
-        padding: 4px 8px;
-        background: #f3f4f6;
-        color: #6b7280;
-        border-radius: 6px;
-        font-size: 12px;
-        font-weight: 500;
-      }
-    }
-  }
-}
-
-// 响应式设计
-@media (max-width: 768px) {
-  .filter-form {
-    .filter-row {
-      flex-direction: column;
-      gap: 16px;
-      
-      &.primary-filters,
-      &.advanced-filters {
-        .filter-group {
-          width: 100%;
-        }
-      }
-    }
-    
-    .style-checkboxes {
-      justify-content: center;
-    }
   }
   
-  .cases-grid {
-    grid-template-columns: 1fr;
-    gap: 16px;
-  }
-}
-
-/* 优化的筛选表单布局 */
-.filter-form {
-  padding: 12px 0 8px;
-}
-.filter-row {
-  display: grid;
-  grid-template-columns: repeat(12, 1fr);
-  gap: 12px 16px;
-  margin-bottom: 10px;
-}
-.filter-group { grid-column: span 3; }
-@media (max-width: 1200px) { .filter-group { grid-column: span 4; } }
-@media (max-width: 900px) { .filter-group { grid-column: span 6; } }
-@media (max-width: 640px) { .filter-group { grid-column: span 12; } }
-
-.checkbox-group {
-  display: flex;
-  flex-wrap: wrap;
-  gap: 8px 10px;
-}
-.checkbox-item { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; }
-.checkbox-item input { accent-color: #1e5eff; }
-
-.range-inputs { display: inline-flex; align-items: center; gap: 6px; }
-.range-inputs input { width: 110px; }
-
-/* 瀑布流布局 */
-.cases-grid {
-  column-count: 1;
-  column-gap: 16px;
-}
-@media (min-width: 768px) { .cases-grid { column-count: 2; } }
-@media (min-width: 1200px) { .cases-grid { column-count: 3; } }
-@media (min-width: 1600px) { .cases-grid { column-count: 4; } }
-
-.case-card {
-  display: inline-block; // 关键:配合 columns
-  width: 100%;
-  break-inside: avoid;
-  margin: 0 0 16px;
-  border-radius: 12px;
-  background: #fff;
-  box-shadow: 0 6px 18px rgba(0,0,0,0.06);
-  overflow: hidden;
-  transition: transform .18s ease, box-shadow .18s ease;
-}
-.case-card:hover { transform: translateY(-2px); box-shadow: 0 10px 24px rgba(0,0,0,0.08); }
-
-.case-image-container { position: relative; }
-.case-image { width: 100%; height: auto; display: block; }
-
-.case-overlay {
-  position: absolute;
-  inset: 0;
-  display: flex;
-  align-items: flex-end;
-  justify-content: space-between;
-  padding: 10px;
-  opacity: 0;
-  background: linear-gradient(to top, rgba(0,0,0,0.35), rgba(0,0,0,0.0));
-  transition: opacity .2s ease;
-}
-.case-card:hover .case-overlay { opacity: 1; }
-
-.favorite-btn, .share-btn {
-  display: inline-flex;
-  align-items: center;
-  gap: 6px;
-  padding: 6px 10px;
-  color: #fff;
-  background: rgba(255,255,255,0.1);
-  border: 1px solid rgba(255,255,255,0.3);
-  border-radius: 8px;
-  backdrop-filter: blur(2px);
-  cursor: pointer;
-}
-.favorite-btn:hover, .share-btn:hover { background: rgba(255,255,255,0.2); }
-
-.case-info { padding: 12px; }
-.case-name { font-size: 15px; margin: 0 0 6px; }
-.case-meta { display: flex; gap: 12px; color: #667085; font-size: 12px; }
-.case-meta .meta-item { display: inline-flex; align-items: center; gap: 6px; }
-.case-tags { margin-top: 8px; display: flex; flex-wrap: wrap; gap: 6px; }
-.case-tags .tag { padding: 2px 8px; border-radius: 999px; background: #f3f7ff; color: #1e5eff; font-size: 12px; }
-
-/* 空状态 */
-.empty-state { text-align: center; color: #98a2b3; padding: 40px 0; }
-.btn-reset { margin-top: 10px; padding: 6px 12px; border-radius: 6px; border: 1px solid #d0d5dd; background: #fff; cursor: pointer; }
-
-/* 分页省略号 */
-.pagination-ellipsis { padding: 0 6px; color: #98a2b3; display: inline-flex; align-items: center; }
-
-/* 案例详情模态框优化 */
-.case-modal { position: fixed; inset: 0; background: rgba(17,24,39,0.45); backdrop-filter: blur(2px); z-index: 100; display: flex; align-items: center; justify-content: center; padding: 24px; }
-.case-modal .modal-content { width: min(980px, 92vw); max-height: 92vh; overflow: auto; border-radius: 12px; background: #fff; position: relative; box-shadow: 0 20px 48px rgba(0,0,0,0.18); }
-.case-modal .close-btn { position: absolute; right: 12px; top: 12px; background: #fff; border: 1px solid #e5e7eb; border-radius: 999px; width: 36px; height: 36px; display: grid; place-items: center; cursor: pointer; }
-
-.case-image-gallery { padding: 12px; }
-.case-image-gallery .main-image img { width: 100%; border-radius: 10px; }
-.case-image-gallery .thumbnails { display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; margin-top: 10px; }
-.case-image-gallery .thumbnail { width: 100%; border-radius: 8px; }
-
-.info-section { padding: 12px; }
-.info-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px 14px; }
-@media (max-width: 900px) { .info-grid { grid-template-columns: repeat(2, 1fr); } }
-.info-item label { color: #98a2b3; font-size: 12px; }
-.info-item span { color: #111827; font-size: 14px; }
-
-.case-actions { padding: 12px; display: flex; gap: 10px; border-top: 1px solid #f2f4f7; }
-.primary-btn, .secondary-btn { display: inline-flex; align-items: center; gap: 8px; border-radius: 8px; padding: 8px 12px; cursor: pointer; border: 1px solid transparent; }
-.primary-btn { background: linear-gradient(135deg, #1e5eff, #4f8cff); color: #fff; box-shadow: 0 6px 16px rgba(30,94,255,0.25); }
-.secondary-btn { background: #f8fafc; color: #1f2937; border-color: #e5e7eb; }
-
-/* 分享弹窗 */
-.share-modal { position: fixed; inset: 0; background: rgba(17,24,39,0.45); z-index: 110; display: flex; align-items: center; justify-content: center; padding: 24px; }
-.share-modal-content { width: min(520px, 92vw); background: #fff; border-radius: 12px; box-shadow: 0 18px 44px rgba(0,0,0,0.16); position: relative; padding: 18px; }
-.share-modal .close-btn { position: absolute; right: 12px; top: 12px; background: #fff; border: 1px solid #e5e7eb; border-radius: 999px; width: 36px; height: 36px; display: grid; place-items: center; cursor: pointer; }
-.share-modal h3 { margin: 0 0 6px; font-size: 18px; }
-.share-tip { color: #667085; font-size: 13px; margin-bottom: 12px; }
-.share-body { display: flex; gap: 12px; align-items: flex-start; }
-.share-link-box { flex: 1; display: flex; gap: 8px; }
-.share-link-input { flex: 1; height: 36px; padding: 0 10px; border: 1px solid #e5e7eb; border-radius: 8px; font-size: 13px; }
-.btn-primary { height: 36px; padding: 0 12px; border-radius: 8px; color: #fff; background: #1e5eff; border: 1px solid #1e5eff; cursor: pointer; }
-.qr-box { width: 160px; height: 160px; border: 1px dashed #e5e7eb; border-radius: 8px; display: grid; place-items: center; color: #98a2b3; font-size: 12px; }
-
-/* 响应式微调 */
-@media (max-width: 720px) {
-  .share-body { flex-direction: column; }
-  .qr-box { width: 100%; height: 140px; }
-}
-
-// 页面整体容器优化
-.case-library-container {
-  background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 50%, #e2e8f0 100%);
-  min-height: 100vh;
-  padding: 20px;
-}
-
-// 页面标题区域
-.page-header {
-  text-align: center;
-  margin-bottom: 32px;
-  
-  h1 {
-    font-size: 32px;
-    font-weight: 800;
-    background: linear-gradient(135deg, #1f2937 0%, #374151 100%);
-    -webkit-background-clip: text;
-    -webkit-text-fill-color: transparent;
-    background-clip: text;
-    margin-bottom: 8px;
-  }
-  
-  .subtitle {
-    font-size: 16px;
-    color: #6b7280;
-    font-weight: 500;
-  }
-}
-
-// 筛选切换按钮优化
-.filter-toggle {
-  background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
-  color: white;
-  border: none;
-  padding: 12px 24px;
-  border-radius: 12px;
-  font-size: 14px;
-  font-weight: 600;
-  cursor: pointer;
-  transition: all 0.3s ease;
-  box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
-  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 ease;
-  }
-  
-  &:hover {
-    transform: translateY(-2px);
-    box-shadow: 0 8px 20px rgba(59, 130, 246, 0.4);
-    
-    &::before {
-      left: 100%;
-    }
-  }
-  
-  &:active {
-    transform: translateY(0);
-  }
-}
-
-.filter-panel {
-  background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
-  border: 1px solid #e2e8f0;
-  border-radius: 20px;
-  padding: 32px;
-  margin-bottom: 32px;
-  box-shadow: 
-    0 10px 15px -3px rgba(0, 0, 0, 0.1), 
-    0 4px 6px -2px rgba(0, 0, 0, 0.05),
-    inset 0 1px 0 rgba(255, 255, 255, 0.1);
-  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, #3b82f6, #8b5cf6, #06b6d4, #10b981);
-    background-size: 300% 100%;
-    animation: gradientShift 6s ease infinite;
-  }
-  
-  &.show {
-    opacity: 1;
-    transform: translateY(0) scale(1);
-  }
-  
-  &:not(.show) {
-    opacity: 0;
-    transform: translateY(-20px) scale(0.95);
-    pointer-events: none;
-  }
-}
-
-@keyframes gradientShift {
-  0%, 100% { background-position: 0% 50%; }
-  50% { background-position: 100% 50%; }
-}
-
-.filter-form {
-  .filter-row {
-    display: flex;
-    gap: 24px;
-    margin-bottom: 24px;
-    align-items: flex-end;
-    
-    &:last-child {
-      margin-bottom: 0;
-    }
-    
-    // 主要筛选项样式
-    &.primary-filters {
-      .filter-group {
-        flex: 1;
-        min-width: 200px;
-      }
-    }
+  .stats-panel {
+    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+    border-radius: 12px;
+    padding: 24px;
+    margin-bottom: 24px;
+    color: white;
+    box-shadow: 0 8px 32px rgba(102, 126, 234, 0.3);
     
-    // 风格筛选样式
-    &.style-filters {
-      .style-group {
-        width: 100%;
-      }
+    .stats-grid {
+      display: grid;
+      grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
+      gap: 20px;
       
-      padding: 20px;
-      background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
-      border-radius: 16px;
-      border: 1px solid #e2e8f0;
-    }
-    
-    // 高级筛选样式
-    &.advanced-filters {
-      .filter-group {
-        flex: 1;
+      .stat-card {
+        background: rgba(255, 255, 255, 0.1);
+        border-radius: 8px;
+        padding: 16px;
+        backdrop-filter: blur(10px);
         
-        &.checkbox-group {
-          display: flex;
-          align-items: center;
-          justify-content: center;
+        h3 {
+          margin: 0 0 16px 0;
+          font-size: 16px;
+          font-weight: 600;
+        }
+        
+        .stat-list {
+          .stat-item {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            padding: 8px 0;
+            border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+            
+            &:last-child {
+              border-bottom: none;
+            }
+            
+            .case-name, .style-name, .designer-name {
+              font-weight: 500;
+            }
+            
+            .stat-value {
+              color: rgba(255, 255, 255, 0.8);
+              font-size: 14px;
+            }
+          }
         }
       }
     }
   }
   
-  .filter-group {
-    display: flex;
-    flex-direction: column;
-    gap: 10px;
+  .filter-bar {
+    background: white;
+    border-radius: 12px;
+    padding: 24px;
+    margin-bottom: 32px;
+    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
+    border: 1px solid $border-color;
     
-    label {
-      font-size: 15px;
-      font-weight: 700;
-      color: #374151;
-      margin-bottom: 6px;
-      position: relative;
+    .filter-group {
+      display: flex;
+      gap: 16px;
+      margin-bottom: 16px;
+      flex-wrap: wrap;
+      
+      &:last-child {
+        margin-bottom: 0;
+      }
+      
+      @media (max-width: 768px) {
+        flex-direction: column;
+        gap: 12px;
+        
+        .search-box {
+          min-width: unset;
+          max-width: unset;
+        }
+        
+        .filter-select {
+          min-width: unset;
+        }
+      }
+      
+      .search-box {
+        position: relative;
+        flex: 1;
+        min-width: 250px;
+        max-width: 400px;
+        
+        .search-input {
+          width: 100%;
+          padding: 12px 16px 12px 40px;
+          border: 1px solid $border-color;
+          border-radius: 8px;
+          font-size: 14px;
+          transition: border-color 0.2s ease;
+          
+          &:focus {
+            outline: none;
+            border-color: $primary-color;
+            box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1);
+          }
+        }
+        
+        .search-icon {
+          position: absolute;
+          left: 16px;
+          top: 50%;
+          transform: translateY(-50%);
+          color: $text-secondary;
+        }
+      }
       
-      &::after {
-        content: '';
-        position: absolute;
-        bottom: -2px;
-        left: 0;
-        width: 20px;
-        height: 2px;
-        background: linear-gradient(90deg, #3b82f6, #8b5cf6);
-        border-radius: 1px;
+      .filter-select {
+        padding: 12px 16px;
+        border: 1px solid $border-color;
+        border-radius: 8px;
+        font-size: 14px;
+        background: white;
+        min-width: 140px;
+        cursor: pointer;
+        transition: border-color 0.2s ease;
+        
+        &:focus {
+          outline: none;
+          border-color: $primary-color;
+        }
       }
     }
     
-    select {
-      padding: 12px 16px;
-      border: 2px solid #e5e7eb;
-      border-radius: 12px;
-      font-size: 14px;
-      background: white;
-      color: #374151;
-      transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
-      position: relative;
-      z-index: 10;
-      box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+    .filter-actions {
+      display: flex;
+      gap: 12px;
+      justify-content: flex-end;
       
-      &:focus {
-        outline: none;
-        border-color: #3b82f6;
-        box-shadow: 
-          0 0 0 4px rgba(59, 130, 246, 0.1),
-          0 4px 12px rgba(59, 130, 246, 0.15);
-        transform: translateY(-1px);
+      @media (max-width: 768px) {
+        justify-content: stretch;
+        
+        .btn {
+          flex: 1;
+        }
       }
       
-      &:hover {
-        border-color: #d1d5db;
-        box-shadow: 0 4px 8px rgba(0, 0, 0, 0.12);
+      .btn {
+        padding: 10px 20px;
+        border-radius: 8px;
+        font-weight: 500;
+        transition: all 0.2s ease;
+        
+        &:hover {
+          transform: translateY(-1px);
+        }
+        
+        &.btn-secondary {
+          background: white;
+          border: 1px solid $border-color;
+          color: $text-primary;
+          
+          &:hover {
+            background: $gray-50;
+          }
+        }
+        
+        &.btn-primary {
+          background: $primary-color;
+          border: 1px solid $primary-color;
+          color: white;
+          
+          &:hover {
+          background: #0051a8; // 手动计算的深色版本
+        }
+        }
       }
     }
   }
   
-  // 风格选择器样式
-  .style-checkboxes {
-    display: flex;
-    flex-wrap: wrap;
-    gap: 16px;
-    margin-top: 12px;
-    justify-content: center;
-  }
-  
-  .style-checkbox {
-    display: flex;
-    align-items: center;
-    cursor: pointer;
-    
-    input[type="checkbox"] {
-      display: none;
-    }
+  .cases-grid {
+    display: grid;
+    grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
+    gap: 24px;
+    margin-bottom: 32px;
     
-    .style-tag {
-      padding: 10px 20px;
+    .case-card {
       background: white;
-      border: 2px solid #e5e7eb;
-      border-radius: 25px;
-      font-size: 14px;
-      font-weight: 600;
-      color: #6b7280;
-      transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
-      white-space: nowrap;
-      position: relative;
+      border-radius: 12px;
       overflow: hidden;
+      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
+      transition: all 0.3s ease;
+      border: 1px solid $border-color;
+      
+      &:hover {
+        transform: translateY(-4px);
+        box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
+        
+        .case-image {
+          transform: scale(1.05);
+        }
+        
+        .image-overlay {
+          opacity: 1;
+        }
+      }
       
-      &::before {
-        content: '';
-        position: absolute;
-        top: 0;
-        left: -100%;
-        width: 100%;
-        height: 100%;
-        background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent);
-        transition: left 0.5s ease;
+      .case-image-container {
+        position: sticky;
+        top: 20px; /* 距离顶部20px */
+        z-index: 10;
+        overflow: hidden;
+        height: 240px;
+        
+        .case-image {
+          width: 100%;
+          height: 100%;
+          object-fit: cover;
+          transition: transform 0.3s ease;
+          cursor: pointer;
+        }
+        
+        .image-overlay {
+          position: absolute;
+          top: 0;
+          left: 0;
+          right: 0;
+          bottom: 0;
+          background: linear-gradient(
+            to bottom,
+            rgba(0, 0, 0, 0.3) 0%,
+            rgba(0, 0, 0, 0.1) 50%,
+            rgba(0, 0, 0, 0.8) 100%
+          );
+          opacity: 0;
+          transition: opacity 0.3s ease;
+          display: flex;
+          flex-direction: column;
+          justify-content: space-between;
+          padding: 16px;
+          
+          .case-badges {
+            display: flex;
+            gap: 8px;
+            flex-wrap: wrap;
+            
+            .badge {
+              padding: 4px 8px;
+              border-radius: 4px;
+              font-size: 12px;
+              font-weight: 500;
+              color: white;
+              
+              &.project-type {
+                background: rgba(79, 70, 229, 0.9);
+              }
+              
+              &.space-type {
+                background: rgba(245, 158, 11, 0.9);
+              }
+              
+              &.rendering-level {
+                background: rgba(16, 185, 129, 0.9);
+              }
+            }
+          }
+          
+          .action-buttons {
+            display: flex;
+            gap: 12px;
+            justify-content: flex-end;
+            
+            .btn-icon {
+              width: 40px;
+              height: 40px;
+              border-radius: 50%;
+              border: none;
+              background: rgba(255, 255, 255, 0.9);
+              color: $text-primary;
+              display: flex;
+              align-items: center;
+              justify-content: center;
+              cursor: pointer;
+              transition: all 0.2s ease;
+              backdrop-filter: blur(10px);
+              
+              &:hover {
+                background: white;
+                transform: scale(1.1);
+                box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+              }
+              
+              &.favorite-btn {
+                &.active {
+                  background: $red-500;
+                  color: white;
+                }
+                
+                &:hover:not(.active) {
+                  color: $red-500;
+                }
+              }
+              
+              &.share-btn {
+                &:hover {
+                  background: $primary-color;
+                  color: white;
+                }
+              }
+            }
+          }
+        }
       }
       
-      &:hover {
-        border-color: #3b82f6;
-        color: #3b82f6;
-        transform: translateY(-2px);
-        box-shadow: 0 8px 16px rgba(59, 130, 246, 0.2);
+      .case-info {
+        padding: 20px;
+        
+        .case-header {
+          display: flex;
+          justify-content: space-between;
+          align-items: flex-start;
+          margin-bottom: 12px;
+          
+          .case-name {
+            font-size: 18px;
+            font-weight: 600;
+            color: $text-primary;
+            margin: 0;
+            cursor: pointer;
+            transition: color 0.2s ease;
+            flex: 1;
+            margin-right: 12px;
+            
+            &:hover {
+              color: $primary-color;
+            }
+          }
+          
+          .info-share-btn {
+            width: 32px;
+            height: 32px;
+            border-radius: 6px;
+            border: 1px solid $border-color;
+            background: white;
+            color: $text-muted;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            cursor: pointer;
+            transition: all 0.2s ease;
+            flex-shrink: 0;
+            
+            &:hover {
+              background: $primary-color;
+              color: white;
+              border-color: $primary-color;
+              transform: scale(1.05);
+            }
+            
+            svg {
+              stroke-width: 2;
+            }
+          }
+        }
+        
+        .case-meta {
+          display: flex;
+          flex-direction: column;
+          gap: 8px;
+          margin-bottom: 16px;
+          
+          .meta-item {
+            display: flex;
+            align-items: center;
+            gap: 8px;
+            font-size: 14px;
+            color: $text-secondary;
+            
+            svg {
+              color: $text-muted;
+            }
+            
+            .team-badge {
+              background: $gray-100;
+              color: $text-secondary;
+              padding: 2px 6px;
+              border-radius: 4px;
+              font-size: 12px;
+              margin-left: 8px;
+            }
+          }
+        }
+        
+        .case-tags {
+          display: flex;
+          flex-wrap: wrap;
+          gap: 6px;
+          margin-bottom: 16px;
+          
+          .tag {
+            background: $gray-100;
+            color: $text-secondary;
+            padding: 4px 8px;
+            border-radius: 16px;
+            font-size: 12px;
+            font-weight: 500;
+          }
+        }
+        
+        .customer-review {
+          background: $gray-50;
+          padding: 12px;
+          border-radius: 8px;
+          border-left: 4px solid $primary-color;
+          margin-bottom: 16px;
+          
+          .review-text {
+            margin: 0;
+            font-size: 14px;
+            color: $text-secondary;
+            font-style: italic;
+            line-height: 1.5;
+          }
+        }
         
-        &::before {
-          left: 100%;
+        .internal-info {
+          border-top: 1px solid $border-color;
+          padding-top: 16px;
+          
+          .internal-badge {
+            display: inline-block;
+            padding: 4px 8px;
+            border-radius: 4px;
+            font-size: 12px;
+            font-weight: 500;
+            margin-bottom: 8px;
+            
+            &.excellent {
+              background: rgba(34, 197, 94, 0.1);
+              color: $green-600;
+              border: 1px solid $green-200;
+            }
+            
+            &:not(.excellent) {
+              background: $gray-100;
+              color: $text-secondary;
+            }
+          }
+          
+          .internal-stats {
+            display: flex;
+            gap: 16px;
+            font-size: 12px;
+            color: $text-muted;
+            
+            span {
+              display: flex;
+              align-items: center;
+              gap: 4px;
+            }
+          }
         }
       }
     }
+  }
+  
+  .empty-state {
+    text-align: center;
+    padding: 80px 20px;
+    color: $text-secondary;
+    
+    svg {
+      color: $gray-300;
+      margin-bottom: 16px;
+    }
+    
+    h3 {
+      font-size: 20px;
+      font-weight: 600;
+      margin: 0 0 8px 0;
+    }
     
-    input[type="checkbox"]:checked + .style-tag {
-      background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
-      border-color: #3b82f6;
-      color: white;
-      box-shadow: 0 8px 20px rgba(59, 130, 246, 0.4);
-      transform: translateY(-2px);
+    p {
+      margin: 0 0 24px 0;
+      font-size: 16px;
     }
   }
   
-  // 收藏复选框样式
-  .favorite-checkbox {
+  .pagination {
     display: flex;
+    justify-content: center;
     align-items: center;
-    gap: 10px;
-    cursor: pointer;
-    padding: 12px 20px;
-    background: white;
-    border: 2px solid #e5e7eb;
-    border-radius: 12px;
-    transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
-    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
-    
-    &:hover {
-      border-color: #f59e0b;
-      background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
-      transform: translateY(-1px);
-      box-shadow: 0 4px 12px rgba(245, 158, 11, 0.2);
-    }
+    gap: 16px;
+    margin-top: 32px;
     
-    input[type="checkbox"] {
-      width: 20px;
-      height: 20px;
-      accent-color: #f59e0b;
+    .pagination-btn {
+      padding: 10px 20px;
+      border: 1px solid $border-color;
+      border-radius: 8px;
+      background: white;
+      color: $text-primary;
+      font-weight: 500;
+      cursor: pointer;
+      transition: all 0.2s ease;
+      
+      &:hover:not(:disabled) {
+        background: $gray-50;
+        border-color: $gray-300;
+      }
+      
+      &:disabled {
+        opacity: 0.5;
+        cursor: not-allowed;
+      }
     }
     
-    .checkbox-text {
+    .page-info {
+      color: $text-secondary;
       font-size: 14px;
-      font-weight: 600;
-      color: #374151;
-    }
-    
-    input[type="checkbox"]:checked ~ .checkbox-text {
-      color: #f59e0b;
-      font-weight: 700;
     }
   }
-}
-
-// 案例展示区域优化
-.cases-grid {
-  display: grid;
-  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
-  gap: 28px;
-  margin-top: 32px;
-  padding: 0 4px; // 为阴影留出空间
-}
-
-.case-card {
-  background: white;
-  border-radius: 20px;
-  overflow: hidden;
-  box-shadow: 
-    0 4px 6px -1px rgba(0, 0, 0, 0.1), 
-    0 2px 4px -1px rgba(0, 0, 0, 0.06);
-  transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
-  position: relative;
-  z-index: 1;
-  border: 1px solid #f3f4f6;
   
-  &::before {
-    content: '';
-    position: absolute;
+  .share-modal {
+    position: fixed;
     top: 0;
     left: 0;
     right: 0;
     bottom: 0;
-    background: linear-gradient(135deg, rgba(59, 130, 246, 0.05) 0%, rgba(139, 92, 246, 0.05) 100%);
-    opacity: 0;
-    transition: opacity 0.3s ease;
-    z-index: -1;
-  }
-  
-  &:hover {
-    transform: translateY(-12px) scale(1.02);
-    box-shadow: 
-      0 25px 50px -12px rgba(0, 0, 0, 0.15),
-      0 0 0 1px rgba(59, 130, 246, 0.1);
-    
-    &::before {
-      opacity: 1;
-    }
-  }
-  
-  .case-image {
-    width: 100%;
-    height: 220px;
-    overflow: hidden;
-    position: relative;
+    background: rgba(0, 0, 0, 0.5);
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    z-index: 1000;
+    padding: 20px;
     
-    img, svg {
+    .share-content {
+      background: white;
+      border-radius: 12px;
+      padding: 24px;
+      max-width: 500px;
       width: 100%;
-      height: 100%;
-      object-fit: cover;
-      transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
-    }
-    
-    &:hover img,
-    &:hover svg {
-      transform: scale(1.08);
-    }
-    
-    // 添加渐变遮罩
-    &::after {
-      content: '';
-      position: absolute;
-      bottom: 0;
-      left: 0;
-      right: 0;
-      height: 60px;
-      background: linear-gradient(to top, rgba(0,0,0,0.3) 0%, transparent 100%);
-      opacity: 0;
-      transition: opacity 0.3s ease;
-    }
-    
-    &:hover::after {
-      opacity: 1;
-    }
-  }
-  
-  .case-info {
-    padding: 24px;
-    
-    .case-title {
-      font-size: 20px;
-      font-weight: 800;
-      color: #1f2937;
-      margin-bottom: 12px;
-      line-height: 1.3;
-      background: linear-gradient(135deg, #1f2937 0%, #374151 100%);
-      -webkit-background-clip: text;
-      -webkit-text-fill-color: transparent;
-      background-clip: text;
-    }
-    
-    .case-details {
-      display: flex;
-      flex-wrap: wrap;
-      gap: 16px;
-      margin-bottom: 20px;
+      box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
       
-      .detail-item {
+      .share-header {
         display: flex;
+        justify-content: space-between;
         align-items: center;
-        gap: 6px;
-        font-size: 13px;
-        color: #6b7280;
-        background: #f8fafc;
-        padding: 6px 12px;
-        border-radius: 8px;
+        margin-bottom: 24px;
         
-        .label {
+        h3 {
+          margin: 0;
+          font-size: 20px;
           font-weight: 600;
         }
         
-        .value {
-          color: #374151;
-          font-weight: 500;
+        .close-btn {
+          background: none;
+          border: none;
+          font-size: 24px;
+          cursor: pointer;
+          color: $text-secondary;
+          padding: 0;
+          width: 32px;
+          height: 32px;
+          border-radius: 50%;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          
+          &:hover {
+            background: $gray-100;
+          }
         }
       }
-    }
-    
-    .case-tags {
-      display: flex;
-      flex-wrap: wrap;
-      gap: 10px;
       
-      .tag {
-        padding: 6px 12px;
-        background: linear-gradient(135deg, #f3f4f6 0%, #e5e7eb 100%);
-        color: #6b7280;
-        border-radius: 8px;
-        font-size: 12px;
-        font-weight: 600;
-        border: 1px solid #e5e7eb;
-        transition: all 0.2s ease;
+      .share-options {
+        display: grid;
+        grid-template-columns: 1fr 2fr;
+        gap: 24px;
         
-        &:hover {
-          background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
-          color: white;
-          transform: translateY(-1px);
+        .qr-code {
+          text-align: center;
+          
+          img {
+            width: 120px;
+            height: 120px;
+            margin-bottom: 12px;
+            border-radius: 8px;
+          }
+          
+          p {
+            margin: 0;
+            font-size: 14px;
+            color: $text-secondary;
+          }
+        }
+        
+        .share-links {
+          .share-link {
+            display: flex;
+            gap: 8px;
+            margin-bottom: 16px;
+            
+            .link-input {
+              flex: 1;
+              padding: 10px 12px;
+              border: 1px solid $border-color;
+              border-radius: 6px;
+              font-size: 14px;
+              background: $gray-50;
+            }
+          }
+          
+          .social-share {
+            .btn {
+              display: flex;
+              align-items: center;
+              gap: 8px;
+              width: 100%;
+              justify-content: center;
+            }
+          }
         }
       }
     }
   }
 }
 
-// 加载状态
-.loading-state {
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  height: 200px;
-  
-  .spinner {
-    width: 40px;
-    height: 40px;
-    border: 4px solid #e5e7eb;
-    border-top: 4px solid #3b82f6;
-    border-radius: 50%;
-    animation: spin 1s linear infinite;
-  }
-}
-
-@keyframes spin {
-  0% { transform: rotate(0deg); }
-  100% { transform: rotate(360deg); }
-}
-
-// 空状态
-.empty-state {
-  text-align: center;
-  padding: 60px 20px;
-  
-  .empty-icon {
-    font-size: 64px;
-    color: #d1d5db;
-    margin-bottom: 16px;
-  }
-  
-  .empty-title {
-    font-size: 20px;
-    font-weight: 600;
-    color: #6b7280;
-    margin-bottom: 8px;
-  }
-  
-  .empty-description {
-    font-size: 14px;
-    color: #9ca3af;
-  }
-}
-
 // 响应式设计
 @media (max-width: 1024px) {
-  .cases-grid {
-    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
-    gap: 20px;
+  .case-library-container {
+    padding: 16px;
+    
+    .cases-grid {
+      grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
+      gap: 20px;
+    }
+    
+    .filter-bar {
+      .filter-group {
+        .search-box {
+          min-width: 250px;
+        }
+      }
+    }
   }
 }
 
 @media (max-width: 768px) {
   .case-library-container {
-    padding: 16px;
-  }
-  
-  .filter-panel {
-    padding: 20px;
-    border-radius: 16px;
-  }
-  
-  .filter-form {
-    .filter-row {
+    .page-header {
       flex-direction: column;
       gap: 16px;
+      align-items: stretch;
       
-      &.primary-filters,
-      &.advanced-filters {
-        .filter-group {
+      .header-actions {
+        .btn {
           width: 100%;
+          justify-content: center;
+        }
+      }
+    }
+    
+    .stats-panel {
+      .stats-grid {
+        grid-template-columns: 1fr;
+      }
+    }
+    
+    .filter-bar {
+      .filter-group {
+        .search-box {
+          min-width: 100%;
+        }
+        
+        .filter-select {
+          min-width: 120px;
         }
       }
       
-      &.style-filters {
-        padding: 16px;
+      .filter-actions {
+        justify-content: stretch;
+        
+        .btn {
+          flex: 1;
+        }
       }
     }
     
-    .style-checkboxes {
-      justify-content: center;
-      gap: 12px;
+    .cases-grid {
+      grid-template-columns: 1fr;
     }
-  }
-  
-  .cases-grid {
-    grid-template-columns: 1fr;
-    gap: 16px;
-  }
-  
-  .case-card {
-    .case-info {
-      padding: 20px;
+    
+    .share-modal {
+      .share-content {
+        .share-options {
+          grid-template-columns: 1fr;
+          gap: 20px;
+          
+          .qr-code {
+            order: 2;
+          }
+          
+          .share-links {
+            order: 1;
+          }
+        }
+      }
     }
   }
 }
 
 @media (max-width: 480px) {
-  .page-header {
-    h1 {
-      font-size: 24px;
+  .case-library-container {
+    padding: 12px;
+    
+    .filter-bar {
+      padding: 16px;
+      
+      .filter-group {
+        .filter-select {
+          min-width: 100px;
+          font-size: 13px;
+        }
+      }
     }
-  }
-  
-  .filter-toggle {
-    width: 100%;
-    margin-bottom: 16px;
-  }
-  
-  .style-checkboxes {
-    .style-tag {
-      padding: 8px 16px;
-      font-size: 13px;
+    
+    .case-card {
+      .case-image-container {
+        height: 200px;
+      }
+      
+      .case-info {
+        padding: 16px;
+      }
     }
   }
-}
-
-// ... existing code ...
+}

+ 430 - 399
src/app/pages/customer-service/case-library/case-library.ts

@@ -1,468 +1,499 @@
-import { Component, OnInit, signal, computed } from '@angular/core';
+import { Component, OnInit } from '@angular/core';
 import { CommonModule } from '@angular/common';
-import { FormsModule, ReactiveFormsModule, FormBuilder, FormGroup } from '@angular/forms';
-import { RouterModule, ActivatedRoute } from '@angular/router';
-import * as QRCode from 'qrcode';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { FormControl } from '@angular/forms';
+import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
 
-// 定义案例接口
-interface CaseItem {
+interface Case {
   id: string;
   name: string;
-  category: string;
-  style: string[];
-  houseType: string;
-  property: string;
-  designer: string;
-  area: number;
-  createdAt: Date;
   coverImage: string;
-  detailImages: string[];
-  isFavorite: boolean;
-  tags: string[];
-  views: number;
-  description: string;
-  // 新增字段
   projectType: '工装' | '家装';
-  subType: '平层' | '复式' | '别墅' | '自建房' | '其他';
-  renderingLevel: '高端' | '中端';
+  spaceType: '平层' | '复式' | '别墅' | '自建房';
+  renderingLevel: '高端' | '中端' | '低端';
+  designer: string;
+  team: string;
+  area: number;
+  styleTags: string[];
+  customerReview?: string;
+  viewCount: number;
   shareCount: number;
   favoriteCount: number;
-  likeCount: number;
-  conversionRate: number; // 0-100
+  isFavorite: boolean;
+  isExcellent: boolean;
+  createdAt: Date;
+}
+
+interface StatItem {
+  id: string;
+  name: string;
+  shareCount: number;
+}
+
+interface StyleStat {
+  style: string;
+  count: number;
+}
+
+interface DesignerStat {
+  designer: string;
+  rate: number;
 }
 
 @Component({
   selector: 'app-case-library',
   standalone: true,
-  imports: [CommonModule, FormsModule, ReactiveFormsModule, RouterModule],
+  imports: [CommonModule, FormsModule, ReactiveFormsModule],
   templateUrl: './case-library.html',
-  styleUrls: ['./case-library.scss', '../customer-service-styles.scss']
+  styleUrls: ['./case-library.scss']
 })
 export class CaseLibrary implements OnInit {
-  // 当前日期
-  currentDate = new Date();
+  // 表单控件
+  searchControl = new FormControl('');
+  projectTypeControl = new FormControl('');
+  spaceTypeControl = new FormControl('');
+  renderingLevelControl = new FormControl('');
+  styleControl = new FormControl('');
+  areaRangeControl = new FormControl('');
+
+  // 数据
+  cases: Case[] = [];
+  filteredCases: Case[] = [];
   
-  // 搜索关键词
-  searchTerm = signal('');
+  // 统计数据
+  topSharedCases: StatItem[] = [];
+  favoriteStyles: StyleStat[] = [];
+  designerRecommendations: DesignerStat[] = [];
   
-  // 分享弹窗
-  showShareModal = signal(false);
-  shareLink = signal('');
-  qrDataUrl = signal('');
-  sharedCaseId = signal<string | null>(null);
-
-  // 筛选表单
-  filterForm: FormGroup;
+  // 状态
+  showStatsPanel = false;
+  selectedCase: Case | null = null;
+  currentPage = 1;
+  itemsPerPage = 12;
+  totalPages = 1;
   
-  // 案例列表
-  cases = signal<CaseItem[]>([]);
+  // 用户类型(模拟)
+  isInternalUser = true; // 可根据实际用户权限设置
   
-  // 筛选后的案例
-  filteredCases = computed(() => {
-    let result = [...this.cases()];
-    
-    // 应用搜索筛选
-    if (this.searchTerm()) {
-      const searchLower = this.searchTerm().toLowerCase();
-      result = result.filter(caseItem => 
-        caseItem.name.toLowerCase().includes(searchLower) ||
-        caseItem.designer.toLowerCase().includes(searchLower) ||
-        caseItem.description.toLowerCase().includes(searchLower) ||
-        caseItem.tags.some(tag => tag.toLowerCase().includes(searchLower))
-      );
-    }
-    
-    // 应用表单筛选
-    const filters = this.filterForm.value as any;
-    
-    if (filters.style && filters.style.length > 0) {
-      result = result.filter(caseItem => 
-        caseItem.style.some((s: string) => filters.style.includes(s))
-      );
-    }
+  // 行为追踪
+  private pageStartTime = Date.now();
+  private caseViewStartTimes = new Map<string, number>();
+
+  ngOnInit() {
+    this.initializeData();
+    this.setupFilterListeners();
+    this.setupBehaviorTracking();
+  }
+  
+  private setupBehaviorTracking() {
+    // 记录页面访问
+    this.recordBehavior('page_view', 'case-library', {
+      timestamp: new Date().toISOString()
+    });
     
-    if (filters.houseType) {
-      result = result.filter(caseItem => caseItem.houseType === filters.houseType);
-    }
+    // 页面卸载时记录停留时长
+    window.addEventListener('beforeunload', () => {
+      const stayDuration = Date.now() - this.pageStartTime;
+      this.recordBehavior('page_stay', 'case-library', {
+        duration: stayDuration,
+        durationMinutes: Math.round(stayDuration / 60000 * 100) / 100
+      });
+    });
+  }
+
+  private initializeData() {
+    // 模拟案例数据
+    this.cases = this.generateMockCases();
+    this.filteredCases = [...this.cases];
+    this.updatePagination();
     
+    // 初始化统计数据
+    this.initializeStats();
+  }
 
+  private setupFilterListeners() {
+    // 搜索框防抖
+    this.searchControl.valueChanges.pipe(
+      debounceTime(300),
+      distinctUntilChanged()
+    ).subscribe(() => this.applyFilters());
 
-    if (filters.projectType) {
-      result = result.filter(caseItem => caseItem.projectType === filters.projectType);
-    }
+    // 其他筛选条件变化
+    [
+      this.projectTypeControl,
+      this.spaceTypeControl,
+      this.renderingLevelControl,
+      this.styleControl,
+      this.areaRangeControl
+    ].forEach(control => {
+      control.valueChanges.subscribe(() => this.applyFilters());
+    });
+  }
 
-    if (filters.subType) {
-      result = result.filter(caseItem => caseItem.subType === filters.subType);
-    }
+  private generateMockCases(): Case[] {
+    const mockCases: Case[] = [];
+    const projectTypes: ('工装' | '家装')[] = ['工装', '家装'];
+    const spaceTypes: ('平层' | '复式' | '别墅' | '自建房')[] = ['平层', '复式', '别墅', '自建房'];
+    const renderingLevels: ('高端' | '中端' | '低端')[] = ['高端', '中端', '低端'];
+    const styles = ['现代', '中式', '欧式', '美式', '日式', '工业风', '极简风', '轻奢风'];
+    const designers = ['张三', '李四', '王五', '赵六', '钱七'];
+    const teams = ['设计一组', '设计二组', '设计三组', '设计四组'];
 
-    if (filters.renderingLevel) {
-      result = result.filter(caseItem => caseItem.renderingLevel === filters.renderingLevel);
-    }
+    for (let i = 1; i <= 50; i++) {
+      const projectType = projectTypes[Math.floor(Math.random() * projectTypes.length)];
+      const spaceType = spaceTypes[Math.floor(Math.random() * spaceTypes.length)];
+      const renderingLevel = renderingLevels[Math.floor(Math.random() * renderingLevels.length)];
+      const styleCount = Math.floor(Math.random() * 3) + 1;
+      const styleTags = Array.from({ length: styleCount }, () => 
+        styles[Math.floor(Math.random() * styles.length)]
+      );
 
-    // 价格筛选(基于案例的预估价格范围)
-    if (filters.price) {
-      // 这里可以根据实际业务逻辑来筛选价格
-      // 暂时保留所有案例,实际项目中需要根据案例的价格字段进行筛选
-    }
-    
-    if (filters.favorite) {
-      result = result.filter(caseItem => caseItem.isFavorite);
+      mockCases.push({
+        id: `case-${i}`,
+        name: `${projectType}${spaceType}设计案例 ${i}`,
+        coverImage: this.generatePlaceholderImage(400, 300, i),
+        projectType,
+        spaceType,
+        renderingLevel,
+        designer: designers[Math.floor(Math.random() * designers.length)],
+        team: teams[Math.floor(Math.random() * teams.length)],
+        area: Math.floor(Math.random() * 200) + 50,
+        styleTags: [...new Set(styleTags)], // 去重
+        customerReview: Math.random() > 0.3 ? `客户非常满意这次${projectType}设计,${spaceType}空间利用得很合理` : undefined,
+        viewCount: Math.floor(Math.random() * 1000),
+        shareCount: Math.floor(Math.random() * 100),
+        favoriteCount: Math.floor(Math.random() * 50),
+        isFavorite: Math.random() > 0.7,
+        isExcellent: Math.random() > 0.5,
+        createdAt: new Date(Date.now() - Math.floor(Math.random() * 365 * 24 * 60 * 60 * 1000))
+      });
     }
 
-    // 排序
-    if (filters.sortBy) {
-      switch (filters.sortBy) {
-        case 'views':
-          result.sort((a, b) => b.views - a.views);
-          break;
-        case 'shares':
-          result.sort((a, b) => b.shareCount - a.shareCount);
-          break;
-        case 'conversion':
-          result.sort((a, b) => b.conversionRate - a.conversionRate);
-          break;
-        case 'createdAt':
-          result.sort((a, b) => +b.createdAt - +a.createdAt);
-          break;
-      }
-    } else {
-      // 默认按创建时间倒序
-      result.sort((a, b) => +b.createdAt - +a.createdAt);
-    }
+    return mockCases;
+  }
+
+  private generatePlaceholderImage(width: number, height: number, seed: number): string {
+    return `https://picsum.photos/seed/${seed}/${width}/${height}`;
+  }
+
+  private initializeStats() {
+    // Top5 分享案例
+    this.topSharedCases = this.cases
+      .sort((a, b) => b.shareCount - a.shareCount)
+      .slice(0, 5)
+      .map(caseItem => ({
+        id: caseItem.id,
+        name: caseItem.name,
+        shareCount: caseItem.shareCount
+      }));
+
+    // 客户最喜欢案例风格
+    const styleCounts = new Map<string, number>();
+    this.cases.forEach(caseItem => {
+      caseItem.styleTags.forEach(style => {
+        styleCounts.set(style, (styleCounts.get(style) || 0) + caseItem.favoriteCount);
+      });
+    });
     
-    return result;
-  });
-  
-  // 显示筛选面板
-  showFilterPanel = signal(false);
-  
-  // 当前查看的案例详情
-  selectedCase = signal<CaseItem | null>(null);
-  
-  // 分页信息
-  currentPage = signal(1);
-  itemsPerPage = signal(12);
-  
-  // 分页后的案例
-  paginatedCases = computed(() => {
-    const startIndex = (this.currentPage() - 1) * this.itemsPerPage();
-    return this.filteredCases().slice(startIndex, startIndex + this.itemsPerPage());
-  });
-  
-  // 总页数
-  totalPages = computed(() => {
-    return Math.ceil(this.filteredCases().length / this.itemsPerPage());
-  });
-  
-  // 筛选选项
-  styleOptions = ['现代简约', '北欧风', '工业风', '新中式', '法式轻奢', '日式', '美式', '混搭'];
-  houseTypeOptions = ['一室一厅', '两室一厅', '两室两厅', '三室一厅', '三室两厅', '四室两厅', '复式', '别墅', '其他'];
-  projectTypeOptions: Array<CaseItem['projectType']> = ['工装', '家装'];
-  subTypeOptions: Array<CaseItem['subType']> = ['平层', '复式', '别墅', '自建房', '其他'];
-  renderingLevelOptions: Array<CaseItem['renderingLevel']> = ['高端', '中端'];
-  priceOptions = ['5万以下', '5-10万', '10-20万', '20-30万', '30-50万', '50万以上'];
-  sortOptions = [
-    { label: '最新上传', value: 'createdAt' },
-    { label: '浏览最多', value: 'views' },
-    { label: '分享最多', value: 'shares' },
-    { label: '转化率最高', value: 'conversion' }
-  ];
-  
-  constructor(private fb: FormBuilder, private route: ActivatedRoute) {
-    // 初始化筛选表单
-    this.filterForm = this.fb.group({
-      style: [[]],
-      houseType: [''],
-      projectType: [''],
-      subType: [''],
-      renderingLevel: [''],
-      price: [''],
-      favorite: [false],
-      sortBy: ['createdAt']
+    this.favoriteStyles = Array.from(styleCounts.entries())
+      .sort(([, a], [, b]) => b - a)
+      .slice(0, 5)
+      .map(([style, count]) => ({ style, count }));
+
+    // 设计师作品推荐率
+    const designerStats = new Map<string, { total: number; excellent: number }>();
+    this.cases.forEach(caseItem => {
+      const stats = designerStats.get(caseItem.designer) || { total: 0, excellent: 0 };
+      stats.total++;
+      if (caseItem.isExcellent) stats.excellent++;
+      designerStats.set(caseItem.designer, stats);
     });
+
+    this.designerRecommendations = Array.from(designerStats.entries())
+      .map(([designer, stats]) => ({
+        designer,
+        rate: Math.round((stats.excellent / stats.total) * 100)
+      }))
+      .sort((a, b) => b.rate - a.rate)
+      .slice(0, 5);
   }
-  
-  ngOnInit(): void {
-    // 加载模拟案例数据
-    this.loadCases();
-    
-    // 读取分享链接参数并打开对应案例详情
-    this.route.queryParamMap.subscribe(params => {
-      const caseId = params.get('case');
-      if (caseId) {
-        const item = this.cases().find(c => c.id === caseId);
-        if (item) {
-          this.viewCaseDetails(item);
+
+  applyFilters() {
+    const searchTerm = this.searchControl.value?.toLowerCase() || '';
+    const projectType = this.projectTypeControl.value;
+    const spaceType = this.spaceTypeControl.value;
+    const renderingLevel = this.renderingLevelControl.value;
+    const style = this.styleControl.value;
+    const areaRange = this.areaRangeControl.value;
+
+    this.filteredCases = this.cases.filter(caseItem => {
+      // 搜索条件
+      if (searchTerm && !(
+        caseItem.name.toLowerCase().includes(searchTerm) ||
+        caseItem.designer.toLowerCase().includes(searchTerm) ||
+        caseItem.styleTags.some(tag => tag.toLowerCase().includes(searchTerm))
+      )) {
+        return false;
+      }
+
+      // 项目类型筛选
+      if (projectType && caseItem.projectType !== projectType) {
+        return false;
+      }
+
+      // 空间类型筛选
+      if (spaceType && caseItem.spaceType !== spaceType) {
+        return false;
+      }
+
+      // 渲染水平筛选
+      if (renderingLevel && caseItem.renderingLevel !== renderingLevel) {
+        return false;
+      }
+
+      // 风格筛选
+      if (style && !caseItem.styleTags.includes(style)) {
+        return false;
+      }
+
+      // 面积范围筛选
+      if (areaRange) {
+        const [min, max] = areaRange.split('-').map(Number);
+        if (max === undefined) {
+          if (caseItem.area < min) return false;
+        } else if (caseItem.area < min || caseItem.area > max) {
+          return false;
         }
       }
+
+      return true;
     });
+
+    this.currentPage = 1;
+    this.updatePagination();
   }
-  
-  // 加载案例数据
-  loadCases(): void {
-    // 本地占位图集合
-    const LOCAL_IMAGES = [
-      '/assets/images/portfolio-1.svg',
-      '/assets/images/portfolio-2.svg',
-      '/assets/images/portfolio-3.svg',
-      '/assets/images/portfolio-4.svg'
-    ];
-
-    const rand = (min: number, max: number) => Math.floor(Math.random() * (max - min + 1)) + min;
-    const pick = <T,>(arr: T[]): T => arr[Math.floor(Math.random() * arr.length)];
-
-    // 模拟API请求获取案例数据
-    const mockCases: CaseItem[] = Array.from({ length: 24 }, (_, i) => {
-      const cover = LOCAL_IMAGES[i % LOCAL_IMAGES.length];
-      const details = Array.from({ length: 4 }, (_, j) => LOCAL_IMAGES[(i + j) % LOCAL_IMAGES.length]);
-      const projectType = pick(this.projectTypeOptions);
-      const subType = pick(this.subTypeOptions);
-      const renderingLevel = pick(this.renderingLevelOptions);
-      const createdAt = new Date(Date.now() - rand(0, 365) * 24 * 60 * 60 * 1000);
-      const views = rand(100, 3000);
-      const shareCount = rand(10, 500);
-      const favoriteCount = rand(5, 400);
-      const likeCount = rand(10, 800);
-      const conversionRate = Number((Math.random() * 30 + 5).toFixed(1)); // 5% - 35%
-
-      return {
-        id: `case-${i + 1}`,
-        name: `${pick(this.styleOptions)}风格 ${pick(this.houseTypeOptions)}设计`,
-        category: pick(['客厅', '卧室', '厨房', '浴室', '书房', '餐厅']),
-        style: [pick(this.styleOptions)],
-        houseType: pick(this.houseTypeOptions),
-        property: pick(['万科', '绿城', '保利', '龙湖', '融创']),
-        designer: pick(['张设计', '李设计', '王设计', '赵设计', '陈设计']),
-        area: rand(50, 150),
-        createdAt,
-        coverImage: cover,
-        detailImages: details,
-        isFavorite: Math.random() > 0.7,
-        tags: ['热门', '精选', '新上传', '高性价比', '业主好评'].filter(() => Math.random() > 0.5),
-        views,
-        description: '这是一个精美的' + pick(['现代简约', '北欧风', '新中式']) + '风格设计案例,融合了功能性与美学,为客户打造了舒适宜人的居住环境。',
-        projectType,
-        subType,
-        renderingLevel,
-        shareCount,
-        favoriteCount,
-        likeCount,
-        conversionRate
-      };
-    });
+
+  resetFilters() {
+    this.searchControl.setValue('');
+    this.projectTypeControl.setValue('');
+    this.spaceTypeControl.setValue('');
+    this.renderingLevelControl.setValue('');
+    this.styleControl.setValue('');
+    this.areaRangeControl.setValue('');
     
-    this.cases.set(mockCases);
+    this.filteredCases = [...this.cases];
+    this.currentPage = 1;
+    this.updatePagination();
   }
-  
-  // 切换收藏状态(同时更新收藏计数)
-  toggleFavorite(caseId: string): void {
-    this.cases.set(
-      this.cases().map(caseItem => {
-        if (caseItem.id === caseId) {
-          const isFav = !caseItem.isFavorite;
-          const favoriteCount = Math.max(0, caseItem.favoriteCount + (isFav ? 1 : -1));
-          return { ...caseItem, isFavorite: isFav, favoriteCount };
-        }
-        return caseItem;
-      })
-    );
-  }
-  
-  // 查看案例详情(增加浏览量)
-  viewCaseDetails(caseItem: CaseItem): void {
-    this.selectedCase.set(caseItem);
-    // 增加浏览量
-    this.cases.set(
-      this.cases().map(item => 
-        item.id === caseItem.id 
-          ? { ...item, views: item.views + 1 }
-          : item
-      )
-    );
-  }
-  
-  // 关闭案例详情
-  closeCaseDetails(): void {
-    this.selectedCase.set(null);
-  }
-  
-  // 分享案例:生成链接、复制并展示弹窗,同时更新分享计数
-  async shareCase(caseId: string): Promise<void> {
-    const link = this.getShareLink(caseId);
-    this.shareLink.set(link);
-    this.showShareModal.set(true);
-    this.sharedCaseId.set(caseId);
-
-    // 生成二维码
-    await this.generateQrCode(link);
-
-    // 分享计数 +1
-    this.cases.set(
-      this.cases().map(item => item.id === caseId ? { ...item, shareCount: item.shareCount + 1 } : item)
-    );
-
-    // 尝试自动复制
-    try {
-      await navigator.clipboard.writeText(link);
-    } catch {
-      // 忽略复制失败(例如非安全上下文),用户可手动复制
+
+  updatePagination() {
+    this.totalPages = Math.ceil(this.filteredCases.length / this.itemsPerPage);
+    if (this.currentPage > this.totalPages) {
+      this.currentPage = this.totalPages || 1;
     }
   }
 
-  getShareLink(caseId: string): string {
-    const base = window.location.origin;
-    return `${base}/customer-service/case-library?case=${encodeURIComponent(caseId)}`;
+  get paginatedCases(): Case[] {
+    const startIndex = (this.currentPage - 1) * this.itemsPerPage;
+    return this.filteredCases.slice(startIndex, startIndex + this.itemsPerPage);
   }
 
-  async copyShareLink(): Promise<void> {
-    const link = this.shareLink();
-    try {
-      await navigator.clipboard.writeText(link);
-      alert('链接已复制到剪贴板');
-    } catch {
-      alert('复制失败,请手动选择链接复制');
+  nextPage() {
+    if (this.currentPage < this.totalPages) {
+      this.currentPage++;
     }
   }
 
-  // 生成二维码
-  private async generateQrCode(text: string): Promise<void> {
-    try {
-      const url = await QRCode.toDataURL(text, { width: 160, margin: 1 });
-      this.qrDataUrl.set(url);
-    } catch (e) {
-      console.error('生成二维码失败', e);
-      this.qrDataUrl.set('');
+  previousPage() {
+    if (this.currentPage > 1) {
+      this.currentPage--;
     }
   }
 
-  downloadQrCode(): void {
-    const dataUrl = this.qrDataUrl();
-    if (!dataUrl) { return; }
-    const a = document.createElement('a');
-    const name = this.sharedCaseId() ? `${this.sharedCaseId()}-qr.png` : 'case-qr.png';
-    a.href = dataUrl;
-    a.download = name;
-    document.body.appendChild(a);
-    a.click();
-    document.body.removeChild(a);
+  showStatistics() {
+    this.showStatsPanel = !this.showStatsPanel;
   }
 
-  openShareLink(): void {
-    const link = this.shareLink();
-    if (link) {
-      window.open(link, '_blank', 'noopener');
-    }
+  viewCaseDetail(caseItem: Case) {
+    // 记录案例查看开始时间
+    this.caseViewStartTimes.set(caseItem.id, Date.now());
+    
+    // 增加浏览次数
+    caseItem.viewCount++;
+    
+    // 记录浏览行为
+    this.recordBehavior('case_view', caseItem.id, {
+      caseName: caseItem.name,
+      designer: caseItem.designer,
+      projectType: caseItem.projectType,
+      spaceType: caseItem.spaceType
+    });
+    
+    // 实际项目中可以跳转到详情页或打开模态框
+    console.log('查看案例详情:', caseItem);
+    
+    // 模拟停留时长记录(实际应在详情页面实现)
+    setTimeout(() => {
+      const viewStartTime = this.caseViewStartTimes.get(caseItem.id);
+      if (viewStartTime) {
+        const viewDuration = Date.now() - viewStartTime;
+        this.recordBehavior('case_view_duration', caseItem.id, {
+          duration: viewDuration,
+          durationSeconds: Math.round(viewDuration / 1000)
+        });
+        this.caseViewStartTimes.delete(caseItem.id);
+      }
+    }, 5000); // 模拟5秒后记录停留时长
   }
 
-  closeShareModal(): void {
-    this.showShareModal.set(false);
-    this.qrDataUrl.set('');
-    this.sharedCaseId.set(null);
+  toggleFavorite(caseItem: Case) {
+    const wasLiked = caseItem.isFavorite;
+    caseItem.isFavorite = !caseItem.isFavorite;
+    
+    if (caseItem.isFavorite) {
+      caseItem.favoriteCount++;
+      this.showToast('已收藏该案例', 'success');
+      
+      // 记录收藏行为
+      this.recordBehavior('case_favorite', caseItem.id, {
+        action: 'add',
+        caseName: caseItem.name,
+        designer: caseItem.designer
+      });
+    } else {
+      caseItem.favoriteCount = Math.max(0, caseItem.favoriteCount - 1);
+      this.showToast('已取消收藏', 'info');
+      
+      // 记录取消收藏行为
+      this.recordBehavior('case_favorite', caseItem.id, {
+        action: 'remove',
+        caseName: caseItem.name,
+        designer: caseItem.designer
+      });
+    }
   }
-  
-  // 重置筛选条件
-  resetFilters(): void {
-    this.filterForm.reset({
-      style: [],
-      houseType: '',
-      projectType: '',
-      subType: '',
-      renderingLevel: '',
-      price: '',
-      favorite: false,
-      sortBy: 'createdAt'
+
+  shareCase(caseItem: Case) {
+    this.selectedCase = caseItem;
+    caseItem.shareCount++;
+    
+    // 记录分享行为数据
+    this.recordBehavior('share', caseItem.id, {
+      caseName: caseItem.name,
+      designer: caseItem.designer,
+      projectType: caseItem.projectType
     });
-    this.searchTerm.set('');
-    this.currentPage.set(1);
-  }
-  
-  // 切换筛选面板
-  toggleFilterPanel(): void {
-    this.showFilterPanel.set(!this.showFilterPanel());
-  }
-  
-  // 分页导航
-  goToPage(page: number): void {
-    if (page >= 1 && page <= this.totalPages()) {
-      this.currentPage.set(page);
-    }
   }
-  
-  // 上一页
-  prevPage(): void {
-    this.goToPage(this.currentPage() - 1);
+
+  closeShareModal() {
+    this.selectedCase = null;
   }
-  
-  // 下一页
-  nextPage(): void {
-    this.goToPage(this.currentPage() + 1);
+
+  generateQRCode(caseItem: Case): string {
+    // 实际项目中应使用二维码生成库,如 qrcode.js
+    const qrData = this.generateShareLink(caseItem);
+    // 这里返回一个模拟的二维码图片
+    const svgContent = `
+      <svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
+        <rect width="200" height="200" fill="white"/>
+        <rect x="20" y="20" width="160" height="160" fill="black"/>
+        <rect x="30" y="30" width="140" height="140" fill="white"/>
+        <text x="100" y="105" text-anchor="middle" font-size="12" fill="black">案例二维码</text>
+        <text x="100" y="125" text-anchor="middle" font-size="10" fill="gray">${caseItem.name}</text>
+      </svg>
+    `;
+    // 使用 TextEncoder 和 btoa 来正确处理包含中文的字符串
+    const encoder = new TextEncoder();
+    const data = encoder.encode(svgContent);
+    const base64 = btoa(String.fromCharCode(...data));
+    return `data:image/svg+xml;base64,${base64}`;
   }
-  
-  // 格式化日期
-  formatDate(date: Date): string {
-    return new Date(date).toLocaleDateString('zh-CN', {
-      month: '2-digit',
-      day: '2-digit',
-      year: 'numeric'
-    });
+
+  generateShareLink(caseItem: Case): string {
+    return `${window.location.origin}/case/${caseItem.id}?from=share&designer=${encodeURIComponent(caseItem.designer)}`;
   }
-  // 智能页码生成
-  pageNumbers = computed(() => {
-    const pages = [] as number[];
-    const total = this.totalPages();
-    const current = this.currentPage();
-    
-    // 显示当前页及前后2页,加上第一页和最后一页
-    const start = Math.max(1, current - 2);
-    const end = Math.min(total, current + 2);
-    
-    if (start > 1) {
-      pages.push(1);
-      if (start > 2) {
-        pages.push(-1); // 用-1表示省略号
-      }
+
+  copyShareLink() {
+    if (this.selectedCase) {
+      const link = this.generateShareLink(this.selectedCase);
+      navigator.clipboard.writeText(link).then(() => {
+        this.showToast('链接已复制到剪贴板,可直接分享给客户!', 'success');
+        
+        // 记录复制行为
+        this.recordBehavior('copy_link', this.selectedCase!.id, {
+          link: link
+        });
+      }).catch(err => {
+        this.showToast('复制失败,请手动复制链接', 'error');
+        console.error('复制链接失败:', err);
+      });
     }
-    
-    for (let i = start; i <= end; i++) {
-      pages.push(i);
+  }
+
+  shareToWeCom() {
+    if (this.selectedCase) {
+      // 实际项目中应集成企业微信分享SDK
+      const shareData = {
+        title: `${this.selectedCase.name} - ${this.selectedCase.designer}设计作品`,
+        description: `${this.selectedCase.projectType} | ${this.selectedCase.spaceType} | ${this.selectedCase.area}㎡`,
+        link: this.generateShareLink(this.selectedCase),
+        imgUrl: this.selectedCase.coverImage
+      };
+      
+      // 模拟企业微信分享
+      console.log('分享到企业微信:', shareData);
+      this.showToast('已调用企业微信分享', 'success');
+      
+      // 记录企业微信分享行为
+      this.recordBehavior('share_wecom', this.selectedCase.id, shareData);
+      
+      this.closeShareModal();
     }
+  }
+
+  // 新增:行为数据记录方法
+  private recordBehavior(action: string, caseId: string, data?: any) {
+    const behaviorData = {
+      action,
+      caseId,
+      timestamp: new Date().toISOString(),
+      userAgent: navigator.userAgent,
+      data: data || {}
+    };
     
-    if (end < total) {
-      if (end < total - 1) {
-        pages.push(-1); // 用-1表示省略号
-      }
-      pages.push(total);
-    }
+    // 实际项目中应发送到后端API
+    console.log('记录用户行为:', behaviorData);
     
-    return pages;
-  });
-  
-  // 格式化样式显示的辅助方法
-  getStyleDisplay(caseItem: CaseItem | null | undefined): string {
-    if (!caseItem || !caseItem.style) {
-      return '';
-    }
-    return Array.isArray(caseItem.style) ? caseItem.style.join('、') : String(caseItem.style);
+    // 模拟存储到本地(实际应发送到服务器)
+    const behaviors = JSON.parse(localStorage.getItem('caseBehaviors') || '[]');
+    behaviors.push(behaviorData);
+    localStorage.setItem('caseBehaviors', JSON.stringify(behaviors));
   }
 
-  // 获取当前选中案例的样式显示
-  getSelectedCaseStyle(): string {
-    return this.getStyleDisplay(this.selectedCase());
-  }
-  
-  // 修复 onStyleChange 方法中的类型安全问题
-  onStyleChange(style: string, isChecked: boolean): void {
-    const currentStyles = (this.filterForm.get('style')?.value || []) as string[];
-    let updatedStyles: string[];
+  // 新增:显示提示消息
+  private showToast(message: string, type: 'success' | 'error' | 'info' = 'info') {
+    // 实际项目中应使用专业的Toast组件
+    const toast = document.createElement('div');
+    toast.className = `toast toast-${type}`;
+    toast.textContent = message;
+    toast.style.cssText = `
+      position: fixed;
+      top: 20px;
+      right: 20px;
+      padding: 12px 20px;
+      border-radius: 8px;
+      color: white;
+      font-weight: 500;
+      z-index: 10000;
+      animation: slideIn 0.3s ease;
+      background: ${type === 'success' ? '#10b981' : type === 'error' ? '#ef4444' : '#3b82f6'};
+    `;
     
-    if (isChecked) {
-      // 如果勾选,则添加风格(避免重复)
-      updatedStyles = [...new Set([...currentStyles, style])];
-    } else {
-      // 如果取消勾选,则移除风格
-      updatedStyles = currentStyles.filter(s => s !== style);
-    }
+    document.body.appendChild(toast);
     
-    this.filterForm.patchValue({ style: updatedStyles });
+    setTimeout(() => {
+      toast.style.animation = 'slideOut 0.3s ease';
+      setTimeout(() => document.body.removeChild(toast), 300);
+    }, 3000);
   }
 }

+ 2 - 1
src/app/pages/customer-service/customer-service-layout/customer-service-layout.html

@@ -105,7 +105,6 @@
         </svg>
         <span>项目列表</span>
       </a>
-
       <a routerLink="/customer-service/case-library" class="nav-item" routerLinkActive="active">
         <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
           <path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"></path>
@@ -113,6 +112,8 @@
         </svg>
         <span>案例库</span>
       </a>
+
+
     </nav>
     
     <div class="sidebar-footer">

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

@@ -80,7 +80,7 @@
                       @for (tag of project.customerTags; track $index) {
                         <span class="tag">{{ tag.source }} · {{ tag.needType }} · {{ tag.preference }} · {{ tag.colorAtmosphere }}</span>
                       }
-                      @if (project.customerTags?.length === 0) { <span class="desc">暂无标签</span> }
+                      @if (project.customerTags.length === 0) { <span class="desc">暂无标签</span> }
                     </div>
                   </div>
                   <div class="tag-section">
@@ -89,7 +89,7 @@
                       @for (need of project.highPriorityNeeds; track $index) {
                         <li>{{ need }}</li>
                       }
-                      @if (project.highPriorityNeeds?.length === 0) { <li class="desc">无</li> }
+                      @if (project.highPriorityNeeds.length === 0) { <li class="desc">无</li> }
                     </ul>
                   </div>
                 </div>

+ 9 - 0
src/assets/images/case-1-cover.svg

@@ -0,0 +1,9 @@
+<svg width="400" height="300" viewBox="0 0 400 300" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <rect width="400" height="300" fill="#F8FAFC"/>
+  <rect x="50" y="50" width="300" height="200" fill="#E2E8F0" rx="8"/>
+  <rect x="80" y="80" width="240" height="120" fill="#CBD5E1" rx="4"/>
+  <circle cx="120" cy="120" r="20" fill="#94A3B8"/>
+  <rect x="160" y="110" width="120" height="8" fill="#94A3B8" rx="4"/>
+  <rect x="160" y="130" width="80" height="8" fill="#CBD5E1" rx="4"/>
+  <text x="200" y="260" text-anchor="middle" fill="#64748B" font-family="Arial" font-size="14" font-weight="500">现代简约客厅</text>
+</svg>

+ 9 - 0
src/assets/images/case-2-cover.svg

@@ -0,0 +1,9 @@
+<svg width="400" height="300" viewBox="0 0 400 300" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <rect width="400" height="300" fill="#FEF7F0"/>
+  <rect x="50" y="50" width="300" height="200" fill="#FED7AA" rx="8"/>
+  <rect x="80" y="80" width="240" height="120" fill="#FB923C" rx="4"/>
+  <circle cx="120" cy="120" r="20" fill="#EA580C"/>
+  <rect x="160" y="110" width="120" height="8" fill="#EA580C" rx="4"/>
+  <rect x="160" y="130" width="80" height="8" fill="#FB923C" rx="4"/>
+  <text x="200" y="260" text-anchor="middle" fill="#9A3412" font-family="Arial" font-size="14" font-weight="500">温馨北欧卧室</text>
+</svg>

+ 9 - 0
src/assets/images/case-3-cover.svg

@@ -0,0 +1,9 @@
+<svg width="400" height="300" viewBox="0 0 400 300" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <rect width="400" height="300" fill="#F0FDF4"/>
+  <rect x="50" y="50" width="300" height="200" fill="#BBF7D0" rx="8"/>
+  <rect x="80" y="80" width="240" height="120" fill="#4ADE80" rx="4"/>
+  <circle cx="120" cy="120" r="20" fill="#16A34A"/>
+  <rect x="160" y="110" width="120" height="8" fill="#16A34A" rx="4"/>
+  <rect x="160" y="130" width="80" height="8" fill="#4ADE80" rx="4"/>
+  <text x="200" y="260" text-anchor="middle" fill="#15803D" font-family="Arial" font-size="14" font-weight="500">工业风餐厅</text>
+</svg>

+ 9 - 0
src/assets/images/case-4-cover.svg

@@ -0,0 +1,9 @@
+<svg width="400" height="300" viewBox="0 0 400 300" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <rect width="400" height="300" fill="#FEF3F2"/>
+  <rect x="50" y="50" width="300" height="200" fill="#FECACA" rx="8"/>
+  <rect x="80" y="80" width="240" height="120" fill="#F87171" rx="4"/>
+  <circle cx="120" cy="120" r="20" fill="#DC2626"/>
+  <rect x="160" y="110" width="120" height="8" fill="#DC2626" rx="4"/>
+  <rect x="160" y="130" width="80" height="8" fill="#F87171" rx="4"/>
+  <text x="200" y="260" text-anchor="middle" fill="#991B1B" font-family="Arial" font-size="14" font-weight="500">中式古典书房</text>
+</svg>