Browse Source

feet:anliku2

徐福静0235668 10 hours ago
parent
commit
e577932320

+ 153 - 0
src/app/pages/customer-service/case-library/case-detail-panel.component.html

@@ -0,0 +1,153 @@
+<div class="case-detail-panel">
+  <!-- 头部 -->
+  <div class="panel-header">
+    <h2 class="panel-title">案例详情</h2>
+    <button class="close-btn" (click)="closePanel()">
+      <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+        <line x1="18" y1="6" x2="6" y2="18"></line>
+        <line x1="6" y1="6" x2="18" y2="18"></line>
+      </svg>
+    </button>
+  </div>
+
+  <!-- 内容区域 -->
+  <div class="panel-content">
+    <!-- 封面图片 -->
+    <div class="cover-image">
+      <img [src]="case.coverImage" [alt]="case.name" class="cover-img">
+      <div class="image-overlay">
+        <div class="case-badges">
+          <span class="badge project-type">{{ case.projectType }}</span>
+          <span class="badge space-type">{{ case.spaceType }}</span>
+          <span class="badge rendering-level">{{ case.renderingLevel }}</span>
+        </div>
+      </div>
+    </div>
+
+    <!-- 基本信息 -->
+    <div class="basic-info">
+      <h1 class="case-name">{{ case.name }}</h1>
+      
+      <div class="meta-grid">
+        <div class="meta-item">
+          <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+            <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
+            <circle cx="12" cy="7" r="4"></circle>
+          </svg>
+          <div class="meta-content">
+            <span class="meta-label">设计师</span>
+            <span class="meta-value">{{ case.designer }}</span>
+            @if (isInternalUser) {
+              <span class="team-badge">{{ case.team }}</span>
+            }
+          </div>
+        </div>
+
+        <div class="meta-item">
+          <svg width="20" height="20" 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>
+          <div class="meta-content">
+            <span class="meta-label">面积</span>
+            <span class="meta-value">{{ case.area }}㎡</span>
+          </div>
+        </div>
+
+        <div class="meta-item">
+          <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>
+            <path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"></path>
+          </svg>
+          <div class="meta-content">
+            <span class="meta-label">浏览量</span>
+            <span class="meta-value">{{ case.viewCount }}</span>
+          </div>
+        </div>
+
+        <div class="meta-item">
+          <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
+            <circle cx="12" cy="12" r="10"></circle>
+            <polyline points="12 6 12 12 16 14"></polyline>
+          </svg>
+          <div class="meta-content">
+            <span class="meta-label">创建时间</span>
+            <span class="meta-value">{{ getFormattedDate(case.createdAt) }}</span>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <!-- 风格标签 -->
+    <div class="style-section">
+      <h3 class="section-title">设计风格</h3>
+      <div class="style-tags">
+        @for (tag of case.styleTags; track $index) {
+          <span class="style-tag">{{ tag }}</span>
+        }
+      </div>
+    </div>
+
+    <!-- 客户评价 -->
+    @if (case.customerReview) {
+      <div class="review-section">
+        <h3 class="section-title">客户评价</h3>
+        <div class="review-content">
+          <div class="quote-icon">"</div>
+          <p class="review-text">{{ case.customerReview }}</p>
+        </div>
+      </div>
+    }
+
+    <!-- 设计师内部信息 -->
+    @if (isInternalUser) {
+      <div class="internal-section">
+        <h3 class="section-title">内部信息</h3>
+        <div class="internal-grid">
+          <div class="internal-item">
+            <span class="internal-label">案例等级</span>
+            <span class="internal-badge" [class.excellent]="case.isExcellent">
+              {{ case.isExcellent ? '优秀案例' : '普通案例' }}
+            </span>
+          </div>
+          <div class="internal-item">
+            <span class="internal-label">分享次数</span>
+            <span class="internal-value">{{ case.shareCount }}</span>
+          </div>
+          <div class="internal-item">
+            <span class="internal-label">收藏次数</span>
+            <span class="internal-value">{{ case.favoriteCount }}</span>
+          </div>
+        </div>
+      </div>
+    }
+  </div>
+
+  <!-- 底部操作栏 -->
+  <div class="panel-footer">
+    <button 
+      class="btn btn-secondary" 
+      (click)="onShare()"
+    >
+      <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>
+    
+    <button 
+      class="btn btn-primary" 
+      [class.active]="case.isFavorite"
+      (click)="onToggleFavorite()"
+    >
+      <svg width="18" height="18" 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>
+      {{ case.isFavorite ? '已收藏' : '收藏案例' }}
+    </button>
+  </div>
+</div>

+ 382 - 0
src/app/pages/customer-service/case-library/case-detail-panel.component.scss

@@ -0,0 +1,382 @@
+/* iOS风格案例详情面板样式 */
+
+.case-detail-panel {
+  position: fixed;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  width: 400px;
+  background: #ffffff;
+  box-shadow: -2px 0 20px rgba(0, 0, 0, 0.15);
+  z-index: 1000;
+  display: flex;
+  flex-direction: column;
+  border-left: 1px solid #e5e5e7;
+  
+  /* iOS风格滚动 */
+  overflow-y: auto;
+  -webkit-overflow-scrolling: touch;
+  scrollbar-width: none;
+  &::-webkit-scrollbar {
+    display: none;
+  }
+}
+
+/* 面板头部 */
+.panel-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 20px 24px;
+  background: #f5f5f7;
+  border-bottom: 1px solid #e5e5e7;
+  position: sticky;
+  top: 0;
+  z-index: 10;
+  
+  .panel-title {
+    font-size: 20px;
+    font-weight: 600;
+    color: #1d1d1f;
+    margin: 0;
+  }
+  
+  .close-btn {
+    background: none;
+    border: none;
+    padding: 8px;
+    border-radius: 50%;
+    cursor: pointer;
+    color: #86868b;
+    transition: all 0.2s ease;
+    
+    &:hover {
+      background: rgba(0, 0, 0, 0.1);
+      color: #1d1d1f;
+    }
+    
+    &:active {
+      transform: scale(0.95);
+    }
+  }
+}
+
+/* 面板内容 */
+.panel-content {
+  flex: 1;
+  padding: 0;
+  overflow-y: auto;
+}
+
+/* 封面图片 */
+.cover-image {
+  position: relative;
+  height: 250px;
+  overflow: hidden;
+  
+  .cover-img {
+    width: 100%;
+    height: 100%;
+    object-fit: cover;
+  }
+  
+  .image-overlay {
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    padding: 16px;
+    background: linear-gradient(transparent, rgba(0, 0, 0, 0.7));
+    
+    .case-badges {
+      display: flex;
+      gap: 8px;
+      flex-wrap: wrap;
+      
+      .badge {
+        padding: 6px 12px;
+        border-radius: 16px;
+        font-size: 12px;
+        font-weight: 500;
+        color: white;
+        background: rgba(255, 255, 255, 0.2);
+        backdrop-filter: blur(10px);
+        border: 1px solid rgba(255, 255, 255, 0.3);
+      }
+    }
+  }
+}
+
+/* 基本信息 */
+.basic-info {
+  padding: 24px;
+  border-bottom: 1px solid #f2f2f7;
+  
+  .case-name {
+    font-size: 24px;
+    font-weight: 600;
+    color: #1d1d1f;
+    margin: 0 0 20px 0;
+    line-height: 1.3;
+  }
+  
+  .meta-grid {
+    display: grid;
+    grid-template-columns: 1fr 1fr;
+    gap: 16px;
+    
+    .meta-item {
+      display: flex;
+      align-items: center;
+      gap: 12px;
+      
+      svg {
+        color: #86868b;
+        flex-shrink: 0;
+      }
+      
+      .meta-content {
+        display: flex;
+        flex-direction: column;
+        gap: 2px;
+        
+        .meta-label {
+          font-size: 12px;
+          color: #86868b;
+          font-weight: 400;
+        }
+        
+        .meta-value {
+          font-size: 14px;
+          color: #1d1d1f;
+          font-weight: 500;
+        }
+        
+        .team-badge {
+          font-size: 11px;
+          color: #007aff;
+          background: rgba(0, 122, 255, 0.1);
+          padding: 2px 6px;
+          border-radius: 4px;
+          margin-top: 2px;
+        }
+      }
+    }
+  }
+}
+
+/* 分区标题 */
+.section-title {
+  font-size: 18px;
+  font-weight: 600;
+  color: #1d1d1f;
+  margin: 0 0 16px 0;
+  padding: 0 24px;
+}
+
+/* 风格标签 */
+.style-section {
+  padding: 24px 24px 0 24px;
+  
+  .style-tags {
+    display: flex;
+    flex-wrap: wrap;
+    gap: 8px;
+    
+    .style-tag {
+      padding: 8px 16px;
+      background: #f2f2f7;
+      border-radius: 20px;
+      font-size: 14px;
+      color: #1d1d1f;
+      font-weight: 500;
+      border: 1px solid #e5e5e7;
+    }
+  }
+}
+
+/* 客户评价 */
+.review-section {
+  padding: 24px;
+  border-top: 1px solid #f2f2f7;
+  
+  .review-content {
+    position: relative;
+    
+    .quote-icon {
+      position: absolute;
+      top: -8px;
+      left: -12px;
+      font-size: 48px;
+      color: #f2f2f7;
+      font-weight: 600;
+      line-height: 1;
+    }
+    
+    .review-text {
+      font-size: 16px;
+      line-height: 1.6;
+      color: #1d1d1f;
+      margin: 0;
+      font-style: italic;
+      position: relative;
+      z-index: 1;
+    }
+  }
+}
+
+/* 内部信息 */
+.internal-section {
+  padding: 24px;
+  background: #f5f5f7;
+  border-radius: 12px;
+  margin: 24px;
+  
+  .internal-grid {
+    display: grid;
+    grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
+    gap: 16px;
+    
+    .internal-item {
+      display: flex;
+      flex-direction: column;
+      gap: 4px;
+      
+      .internal-label {
+        font-size: 12px;
+        color: #86868b;
+        font-weight: 400;
+      }
+      
+      .internal-value {
+        font-size: 16px;
+        color: #1d1d1f;
+        font-weight: 600;
+      }
+      
+      .internal-badge {
+        padding: 4px 8px;
+        border-radius: 6px;
+        font-size: 12px;
+        font-weight: 500;
+        color: white;
+        background: #34c759;
+        
+        &.excellent {
+          background: #ff9500;
+        }
+      }
+    }
+  }
+}
+
+/* 面板底部 */
+.panel-footer {
+  padding: 20px 24px;
+  background: #ffffff;
+  border-top: 1px solid #e5e5e7;
+  display: flex;
+  gap: 12px;
+  position: sticky;
+  bottom: 0;
+  z-index: 10;
+  
+  .btn {
+    flex: 1;
+    padding: 14px 20px;
+    border-radius: 12px;
+    font-size: 16px;
+    font-weight: 500;
+    border: none;
+    cursor: pointer;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    gap: 8px;
+    transition: all 0.2s ease;
+    
+    &:active {
+      transform: scale(0.98);
+    }
+    
+    &.btn-secondary {
+      background: #f2f2f7;
+      color: #1d1d1f;
+      
+      &:hover {
+        background: #e5e5e7;
+      }
+    }
+    
+    &.btn-primary {
+      background: #007aff;
+      color: white;
+      
+      &:hover {
+        background: #0051a8;
+      }
+      
+      &.active {
+        background: #34c759;
+        
+        &:hover {
+          background: #248a3d;
+        }
+      }
+    }
+  }
+}
+
+/* 响应式设计 */
+@media (max-width: 480px) {
+  .case-detail-panel {
+    width: 100%;
+    right: 0;
+    left: 0;
+    border-left: none;
+    border-right: none;
+  }
+  
+  .basic-info {
+    .meta-grid {
+      grid-template-columns: 1fr;
+    }
+  }
+  
+  .internal-section {
+    .internal-grid {
+      grid-template-columns: 1fr;
+    }
+  }
+}
+
+/* iOS风格动画 */
+@keyframes slideInRight {
+  from {
+    transform: translateX(100%);
+    opacity: 0;
+  }
+  to {
+    transform: translateX(0);
+    opacity: 1;
+  }
+}
+
+@keyframes slideOutRight {
+  from {
+    transform: translateX(0);
+    opacity: 1;
+  }
+  to {
+    transform: translateX(100%);
+    opacity: 0;
+  }
+}
+
+.case-detail-panel {
+  animation: slideInRight 0.3s ease-out;
+  
+  &.closing {
+    animation: slideOutRight 0.3s ease-in;
+  }
+}

+ 57 - 0
src/app/pages/customer-service/case-library/case-detail-panel.component.ts

@@ -0,0 +1,57 @@
+import { Component, Input, Output, EventEmitter } from '@angular/core';
+import { CommonModule } from '@angular/common';
+
+interface Case {
+  id: string;
+  name: string;
+  coverImage: string;
+  projectType: '工装' | '家装';
+  spaceType: '平层' | '复式' | '别墅' | '自建房';
+  renderingLevel: '高端' | '中端' | '低端';
+  designer: string;
+  team: string;
+  area: number;
+  styleTags: string[];
+  customerReview?: string;
+  viewCount: number;
+  shareCount: number;
+  favoriteCount: number;
+  isFavorite: boolean;
+  isExcellent: boolean;
+  createdAt: Date;
+}
+
+@Component({
+  selector: 'app-case-detail-panel',
+  standalone: true,
+  imports: [CommonModule],
+  templateUrl: './case-detail-panel.component.html',
+  styleUrls: ['./case-detail-panel.component.scss']
+})
+export class CaseDetailPanelComponent {
+  @Input() case!: Case;
+  @Input() isInternalUser: boolean = false;
+  @Output() close = new EventEmitter<void>();
+  @Output() toggleFavorite = new EventEmitter<Case>();
+  @Output() share = new EventEmitter<Case>();
+
+  closePanel() {
+    this.close.emit();
+  }
+
+  onToggleFavorite() {
+    this.toggleFavorite.emit(this.case);
+  }
+
+  onShare() {
+    this.share.emit(this.case);
+  }
+
+  getFormattedDate(date: Date): string {
+    return new Intl.DateTimeFormat('zh-CN', {
+      year: 'numeric',
+      month: 'long',
+      day: 'numeric'
+    }).format(new Date(date));
+  }
+}

+ 16 - 5
src/app/pages/customer-service/case-library/case-library.html

@@ -129,7 +129,7 @@
 
   <!-- 案例网格 -->
   <div class="cases-grid">
-    @for (caseItem of filteredCases; track caseItem.id) {
+    @for (caseItem of paginatedCases; track caseItem.id) {
       <div class="case-card">
         <!-- 图片容器 -->
         <div class="case-image-container">
@@ -268,7 +268,7 @@
         上一页
       </button>
       
-      <span class="page-info">第 {{ currentPage }} 页 / 共 {{ totalPages }} 页</span>
+      <span class="page-info">第 {{ currentPage }} 页 / 共 {{ totalPages }} 页 ({{ filteredCases.length }} 个案例)</span>
       
       <button 
         class="pagination-btn" 
@@ -280,8 +280,19 @@
     </div>
   }
 
-  <!-- 分享弹窗 -->
+  <!-- 案例详情面板 -->
   @if (selectedCase) {
+    <app-case-detail-panel
+      [case]="selectedCase"
+      [isInternalUser]="isInternalUser"
+      (close)="closeCaseDetail()"
+      (toggleFavorite)="toggleFavorite($event)"
+      (share)="shareCase($event)"
+    ></app-case-detail-panel>
+  }
+
+  <!-- 分享弹窗 -->
+  @if (selectedCaseForShare) {
     <div class="share-modal" (click)="closeShareModal()">
       <div class="share-content" (click)="$event.stopPropagation()">
         <div class="share-header">
@@ -291,7 +302,7 @@
         
         <div class="share-options">
           <div class="qr-code">
-            <img [src]="generateQRCode(selectedCase)" alt="分享二维码">
+            <img [src]="generateQRCode(selectedCaseForShare)" alt="分享二维码">
             <p>扫描二维码分享</p>
           </div>
           
@@ -299,7 +310,7 @@
             <div class="share-link">
               <input 
                 type="text" 
-                [value]="generateShareLink(selectedCase)" 
+                [value]="generateShareLink(selectedCaseForShare)" 
                 readonly
                 class="link-input"
               >

+ 101 - 32
src/app/pages/customer-service/case-library/case-library.scss

@@ -121,6 +121,10 @@ $red-500: #ef4444;
     margin-bottom: 32px;
     box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
     border: 1px solid $border-color;
+    position: sticky;
+    top: 20px;
+    z-index: 100;
+    transition: all 0.3s ease;
     
     .filter-group {
       display: flex;
@@ -587,88 +591,124 @@ $red-500: #ef4444;
     left: 0;
     right: 0;
     bottom: 0;
-    background: rgba(0, 0, 0, 0.5);
+    background: rgba(0, 0, 0, 0.4);
     display: flex;
     align-items: center;
     justify-content: center;
     z-index: 1000;
-    padding: 20px;
+    backdrop-filter: blur(8px);
+    animation: modalFadeIn 0.3s ease;
     
     .share-content {
-      background: white;
-      border-radius: 12px;
-      padding: 24px;
-      max-width: 500px;
-      width: 100%;
-      box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
+      background: #f8f9fa;
+      border-radius: 16px;
+      padding: 0;
+      max-width: 380px;
+      width: 90%;
+      box-shadow: 0 25px 50px rgba(0, 0, 0, 0.25);
+      overflow: hidden;
       
       .share-header {
         display: flex;
         justify-content: space-between;
         align-items: center;
-        margin-bottom: 24px;
+        padding: 20px 24px 16px;
+        background: white;
+        border-bottom: 1px solid #e5e5ea;
         
         h3 {
           margin: 0;
-          font-size: 20px;
+          font-size: 18px;
           font-weight: 600;
+          color: #000;
         }
         
         .close-btn {
           background: none;
           border: none;
-          font-size: 24px;
+          font-size: 28px;
           cursor: pointer;
-          color: $text-secondary;
+          color: #8e8e93;
           padding: 0;
-          width: 32px;
-          height: 32px;
+          width: 44px;
+          height: 44px;
           border-radius: 50%;
           display: flex;
           align-items: center;
           justify-content: center;
+          transition: background-color 0.2s ease;
           
           &:hover {
-            background: $gray-100;
+            background: #f1f1f1;
           }
         }
       }
       
       .share-options {
-        display: grid;
-        grid-template-columns: 1fr 2fr;
-        gap: 24px;
+        padding: 24px;
+        background: white;
         
         .qr-code {
           text-align: center;
+          margin-bottom: 24px;
+          padding: 20px;
+          background: #f8f9fa;
+          border-radius: 12px;
           
           img {
-            width: 120px;
-            height: 120px;
-            margin-bottom: 12px;
-            border-radius: 8px;
+            width: 140px;
+            height: 140px;
+            margin-bottom: 16px;
+            border-radius: 12px;
+            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
           }
           
           p {
             margin: 0;
-            font-size: 14px;
-            color: $text-secondary;
+            font-size: 15px;
+            color: #8e8e93;
+            font-weight: 500;
           }
         }
         
         .share-links {
           .share-link {
             display: flex;
-            gap: 8px;
-            margin-bottom: 16px;
+            gap: 12px;
+            margin-bottom: 20px;
             
             .link-input {
               flex: 1;
-              padding: 10px 12px;
-              border: 1px solid $border-color;
-              border-radius: 6px;
-              font-size: 14px;
-              background: $gray-50;
+              padding: 14px 16px;
+              border: 1px solid #c7c7cc;
+              border-radius: 10px;
+              font-size: 15px;
+              background: white;
+              color: #000;
+              font-family: -apple-system, BlinkMacSystemFont, sans-serif;
+              
+              &:focus {
+                outline: none;
+                border-color: #007aff;
+              }
+            }
+            
+            .btn {
+              padding: 14px 20px;
+              border-radius: 10px;
+              font-weight: 500;
+              font-size: 15px;
+              font-family: -apple-system, BlinkMacSystemFont, sans-serif;
+              
+              &.btn-secondary {
+                background: #007aff;
+                border: none;
+                color: white;
+                
+                &:hover {
+                  background: #0056d6;
+                }
+              }
             }
           }
           
@@ -676,15 +716,44 @@ $red-500: #ef4444;
             .btn {
               display: flex;
               align-items: center;
-              gap: 8px;
+              gap: 12px;
               width: 100%;
               justify-content: center;
+              padding: 16px;
+              background: #007aff;
+              border: none;
+              border-radius: 12px;
+              color: white;
+              font-weight: 500;
+              font-size: 16px;
+              font-family: -apple-system, BlinkMacSystemFont, sans-serif;
+              transition: background-color 0.2s ease;
+              
+              &:hover {
+                background: #0056d6;
+              }
+              
+              svg {
+                width: 20px;
+                height: 20px;
+              }
             }
           }
         }
       }
     }
   }
+  
+  @keyframes modalFadeIn {
+    from {
+      opacity: 0;
+      backdrop-filter: blur(0px);
+    }
+    to {
+      opacity: 1;
+      backdrop-filter: blur(8px);
+    }
+  }
 }
 
 // 响应式设计

+ 23 - 19
src/app/pages/customer-service/case-library/case-library.ts

@@ -3,6 +3,7 @@ import { CommonModule } from '@angular/common';
 import { FormsModule, ReactiveFormsModule } from '@angular/forms';
 import { FormControl } from '@angular/forms';
 import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
+import { CaseDetailPanelComponent } from './case-detail-panel.component';
 
 interface Case {
   id: string;
@@ -43,7 +44,7 @@ interface DesignerStat {
 @Component({
   selector: 'app-case-library',
   standalone: true,
-  imports: [CommonModule, FormsModule, ReactiveFormsModule],
+  imports: [CommonModule, FormsModule, ReactiveFormsModule, CaseDetailPanelComponent],
   templateUrl: './case-library.html',
   styleUrls: ['./case-library.scss']
 })
@@ -67,9 +68,10 @@ export class CaseLibrary implements OnInit {
   
   // 状态
   showStatsPanel = false;
-  selectedCase: Case | null = null;
+  selectedCase: Case | null = null; // 用于详情面板
+  selectedCaseForShare: Case | null = null; // 用于分享模态框
   currentPage = 1;
-  itemsPerPage = 12;
+  itemsPerPage = 10; // 每页显示10个案例
   totalPages = 1;
   
   // 用户类型(模拟)
@@ -329,21 +331,25 @@ export class CaseLibrary implements OnInit {
       spaceType: caseItem.spaceType
     });
     
-    // 实际项目中可以跳转到详情页或打开模态框
-    console.log('查看案例详情:', caseItem);
-    
-    // 模拟停留时长记录(实际应在详情页面实现)
-    setTimeout(() => {
-      const viewStartTime = this.caseViewStartTimes.get(caseItem.id);
+    // 设置当前选中的案例以显示详情面板
+    this.selectedCase = caseItem;
+  }
+
+  closeCaseDetail() {
+    // 记录案例查看时长
+    if (this.selectedCase) {
+      const viewStartTime = this.caseViewStartTimes.get(this.selectedCase.id);
       if (viewStartTime) {
         const viewDuration = Date.now() - viewStartTime;
-        this.recordBehavior('case_view_duration', caseItem.id, {
+        this.recordBehavior('case_view_duration', this.selectedCase.id, {
           duration: viewDuration,
           durationSeconds: Math.round(viewDuration / 1000)
         });
-        this.caseViewStartTimes.delete(caseItem.id);
+        this.caseViewStartTimes.delete(this.selectedCase.id);
       }
-    }, 5000); // 模拟5秒后记录停留时长
+    }
+    
+    this.selectedCase = null;
   }
 
   toggleFavorite(caseItem: Case) {
@@ -374,7 +380,7 @@ export class CaseLibrary implements OnInit {
   }
 
   shareCase(caseItem: Case) {
-    this.selectedCase = caseItem;
+    this.selectedCaseForShare = caseItem;
     caseItem.shareCount++;
     
     // 记录分享行为数据
@@ -386,7 +392,7 @@ export class CaseLibrary implements OnInit {
   }
 
   closeShareModal() {
-    this.selectedCase = null;
+    this.selectedCaseForShare = null;
   }
 
   generateQRCode(caseItem: Case): string {
@@ -402,11 +408,9 @@ export class CaseLibrary implements OnInit {
         <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}`;
+    // 使用 encodeURIComponent 来正确处理SVG内容
+    const encodedSVG = encodeURIComponent(svgContent);
+    return `data:image/svg+xml;charset=utf-8,${encodedSVG}`;
   }
 
   generateShareLink(caseItem: Case): string {