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(); 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(); 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(); 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 = ` 案例二维码 ${caseItem.name} `; // 使用 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); } }