| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526 | 
							- import { Component, OnInit } from '@angular/core';
 
- import { CommonModule } from '@angular/common';
 
- import { FormsModule, ReactiveFormsModule } from '@angular/forms';
 
- import { FormControl } from '@angular/forms';
 
- import { Router } from '@angular/router';
 
- import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
 
- import { CaseDetailPanelComponent } from './case-detail-panel.component';
 
- 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;
 
- }
 
- 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, CaseDetailPanelComponent],
 
-   templateUrl: './case-library.html',
 
-   styleUrls: ['./case-library.scss']
 
- })
 
- export class CaseLibrary implements OnInit {
 
-   // 表单控件
 
-   searchControl = new FormControl('');
 
-   projectTypeControl = new FormControl('');
 
-   spaceTypeControl = new FormControl('');
 
-   renderingLevelControl = new FormControl('');
 
-   styleControl = new FormControl('');
 
-   areaRangeControl = new FormControl('');
 
-   // 数据
 
-   cases: Case[] = [];
 
-   filteredCases: Case[] = [];
 
-   
 
-   // 统计数据
 
-   topSharedCases: StatItem[] = [];
 
-   favoriteStyles: StyleStat[] = [];
 
-   designerRecommendations: DesignerStat[] = [];
 
-   
 
-   // 状态
 
-   showStatsPanel = false;
 
-   selectedCase: Case | null = null; // 用于详情面板
 
-   selectedCaseForShare: Case | null = null; // 用于分享模态框
 
-   currentPage = 1;
 
-   itemsPerPage = 10; // 每页显示10个案例
 
-   totalPages = 1;
 
-   
 
-   // 用户类型(模拟)
 
-   isInternalUser = true; // 可根据实际用户权限设置
 
-   
 
-   // 行为追踪
 
-   private pageStartTime = Date.now();
 
-   private caseViewStartTimes = new Map<string, number>();
 
-   constructor(private router: Router) {}
 
-   ngOnInit() {
 
-     this.initializeData();
 
-     this.setupFilterListeners();
 
-     this.setupBehaviorTracking();
 
-   }
 
-   
 
-   private setupBehaviorTracking() {
 
-     // 记录页面访问
 
-     this.recordBehavior('page_view', 'case-library', {
 
-       timestamp: new Date().toISOString()
 
-     });
 
-     
 
-     // 页面卸载时记录停留时长
 
-     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());
 
-     // 其他筛选条件变化
 
-     [
 
-       this.projectTypeControl,
 
-       this.spaceTypeControl,
 
-       this.renderingLevelControl,
 
-       this.styleControl,
 
-       this.areaRangeControl
 
-     ].forEach(control => {
 
-       control.valueChanges.subscribe(() => this.applyFilters());
 
-     });
 
-   }
 
-   private generateMockCases(): Case[] {
 
-     const mockCases: Case[] = [];
 
-     const projectTypes: ('工装' | '家装')[] = ['工装', '家装'];
 
-     const spaceTypes: ('平层' | '复式' | '别墅' | '自建房')[] = ['平层', '复式', '别墅', '自建房'];
 
-     const renderingLevels: ('高端' | '中端' | '低端')[] = ['高端', '中端', '低端'];
 
-     const styles = ['现代', '中式', '欧式', '美式', '日式', '工业风', '极简风', '轻奢风'];
 
-     const designers = ['张三', '李四', '王五', '赵六', '钱七'];
 
-     const teams = ['设计一组', '设计二组', '设计三组', '设计四组'];
 
-     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)]
 
-       );
 
-       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))
 
-       });
 
-     }
 
-     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);
 
-       });
 
-     });
 
-     
 
-     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);
 
-   }
 
-   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();
 
-   }
 
-   resetFilters() {
 
-     this.searchControl.setValue('');
 
-     this.projectTypeControl.setValue('');
 
-     this.spaceTypeControl.setValue('');
 
-     this.renderingLevelControl.setValue('');
 
-     this.styleControl.setValue('');
 
-     this.areaRangeControl.setValue('');
 
-     
 
-     this.filteredCases = [...this.cases];
 
-     this.currentPage = 1;
 
-     this.updatePagination();
 
-   }
 
-   updatePagination() {
 
-     this.totalPages = Math.ceil(this.filteredCases.length / this.itemsPerPage);
 
-     if (this.currentPage > this.totalPages) {
 
-       this.currentPage = this.totalPages || 1;
 
-     }
 
-   }
 
-   get paginatedCases(): Case[] {
 
-     const startIndex = (this.currentPage - 1) * this.itemsPerPage;
 
-     return this.filteredCases.slice(startIndex, startIndex + this.itemsPerPage);
 
-   }
 
-   nextPage() {
 
-     if (this.currentPage < this.totalPages) {
 
-       this.currentPage++;
 
-     }
 
-   }
 
-   previousPage() {
 
-     if (this.currentPage > 1) {
 
-       this.currentPage--;
 
-     }
 
-   }
 
-   showStatistics() {
 
-     this.showStatsPanel = !this.showStatsPanel;
 
-   }
 
-   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
 
-     });
 
-     
 
-     // 设置当前选中的案例以显示详情面板
 
-     this.selectedCase = caseItem;
 
-   }
 
-   // 跳转到独立的案例详情页面
 
-   navigateToCaseDetail(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
 
-     });
 
-     
 
-     // 跳转到独立的案例详情页面
 
-     this.router.navigate(['/customer-service/case-detail', caseItem.id]);
 
-   }
 
-   closeCaseDetail() {
 
-     // 记录案例查看时长
 
-     if (this.selectedCase) {
 
-       const viewStartTime = this.caseViewStartTimes.get(this.selectedCase.id);
 
-       if (viewStartTime) {
 
-         const viewDuration = Date.now() - viewStartTime;
 
-         this.recordBehavior('case_view_duration', this.selectedCase.id, {
 
-           duration: viewDuration,
 
-           durationSeconds: Math.round(viewDuration / 1000)
 
-         });
 
-         this.caseViewStartTimes.delete(this.selectedCase.id);
 
-       }
 
-     }
 
-     
 
-     this.selectedCase = 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
 
-       });
 
-     }
 
-   }
 
-   shareCase(caseItem: Case) {
 
-     this.selectedCaseForShare = caseItem;
 
-     caseItem.shareCount++;
 
-     
 
-     // 记录分享行为数据
 
-     this.recordBehavior('share', caseItem.id, {
 
-       caseName: caseItem.name,
 
-       designer: caseItem.designer,
 
-       projectType: caseItem.projectType
 
-     });
 
-   }
 
-   closeShareModal() {
 
-     this.selectedCaseForShare = null;
 
-   }
 
-   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>
 
-     `;
 
-     // 使用 encodeURIComponent 来正确处理SVG内容
 
-     const encodedSVG = encodeURIComponent(svgContent);
 
-     return `data:image/svg+xml;charset=utf-8,${encodedSVG}`;
 
-   }
 
-   generateShareLink(caseItem: Case): string {
 
-     return `${window.location.origin}/customer-service/case-detail/${caseItem.id}?from=share&designer=${encodeURIComponent(caseItem.designer)}`;
 
-   }
 
-   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);
 
-       });
 
-     }
 
-   }
 
-   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 || {}
 
-     };
 
-     
 
-     // 实际项目中应发送到后端API
 
-     console.log('记录用户行为:', behaviorData);
 
-     
 
-     // 模拟存储到本地(实际应发送到服务器)
 
-     const behaviors = JSON.parse(localStorage.getItem('caseBehaviors') || '[]');
 
-     behaviors.push(behaviorData);
 
-     localStorage.setItem('caseBehaviors', JSON.stringify(behaviors));
 
-   }
 
-   // 新增:显示提示消息
 
-   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'};
 
-     `;
 
-     
 
-     document.body.appendChild(toast);
 
-     
 
-     setTimeout(() => {
 
-       toast.style.animation = 'slideOut 0.3s ease';
 
-       setTimeout(() => document.body.removeChild(toast), 300);
 
-     }, 3000);
 
-   }
 
- }
 
 
  |