case-detail.component.ts 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. import { Component, OnInit } from '@angular/core';
  2. import { CommonModule } from '@angular/common';
  3. import { ActivatedRoute, Router } from '@angular/router';
  4. interface Case {
  5. id: string;
  6. name: string;
  7. coverImage: string;
  8. projectType: '工装' | '家装';
  9. spaceType: '平层' | '复式' | '别墅' | '自建房';
  10. renderingLevel: '高端' | '中端' | '低端';
  11. designer: string;
  12. team: string;
  13. area: number;
  14. styleTags: string[];
  15. customerReview?: string;
  16. viewCount: number;
  17. shareCount: number;
  18. favoriteCount: number;
  19. isFavorite: boolean;
  20. isExcellent: boolean;
  21. createdAt: Date;
  22. }
  23. @Component({
  24. selector: 'app-case-detail',
  25. standalone: true,
  26. imports: [CommonModule],
  27. templateUrl: './case-detail.component.html',
  28. styleUrls: ['./case-detail.component.scss']
  29. })
  30. export class CaseDetailComponent implements OnInit {
  31. case: Case | null = null;
  32. isInternalUser: boolean = true; // 可根据实际用户权限设置
  33. isLoading: boolean = true;
  34. caseId: string = '';
  35. constructor(
  36. private route: ActivatedRoute,
  37. private router: Router
  38. ) {}
  39. ngOnInit() {
  40. // 获取路由参数中的案例ID
  41. this.route.paramMap.subscribe(params => {
  42. this.caseId = params.get('id') || '';
  43. if (this.caseId) {
  44. this.loadCaseDetail(this.caseId);
  45. }
  46. });
  47. }
  48. private loadCaseDetail(caseId: string) {
  49. this.isLoading = true;
  50. // 模拟API调用,实际项目中应该调用真实的API
  51. setTimeout(() => {
  52. this.case = this.generateMockCase(caseId);
  53. this.isLoading = false;
  54. // 增加浏览次数
  55. if (this.case) {
  56. this.case.viewCount++;
  57. this.recordBehavior('case_view', this.case.id, {
  58. caseName: this.case.name,
  59. designer: this.case.designer,
  60. projectType: this.case.projectType,
  61. spaceType: this.case.spaceType
  62. });
  63. }
  64. }, 500);
  65. }
  66. private generateMockCase(caseId: string): Case {
  67. const projectTypes: ('工装' | '家装')[] = ['工装', '家装'];
  68. const spaceTypes: ('平层' | '复式' | '别墅' | '自建房')[] = ['平层', '复式', '别墅', '自建房'];
  69. const renderingLevels: ('高端' | '中端' | '低端')[] = ['高端', '中端', '低端'];
  70. const styles = ['现代', '中式', '欧式', '美式', '日式', '工业风', '极简风', '轻奢风'];
  71. const designers = ['张三', '李四', '王五', '赵六', '钱七'];
  72. const teams = ['设计一组', '设计二组', '设计三组', '设计四组'];
  73. const projectType = projectTypes[Math.floor(Math.random() * projectTypes.length)];
  74. const spaceType = spaceTypes[Math.floor(Math.random() * spaceTypes.length)];
  75. const renderingLevel = renderingLevels[Math.floor(Math.random() * renderingLevels.length)];
  76. const styleCount = Math.floor(Math.random() * 3) + 1;
  77. const styleTags = Array.from({ length: styleCount }, () =>
  78. styles[Math.floor(Math.random() * styles.length)]
  79. );
  80. return {
  81. id: caseId,
  82. name: `${projectType}${spaceType}设计案例 - ${caseId}`,
  83. coverImage: this.generatePlaceholderImage(800, 600, caseId),
  84. projectType,
  85. spaceType,
  86. renderingLevel,
  87. designer: designers[Math.floor(Math.random() * designers.length)],
  88. team: teams[Math.floor(Math.random() * teams.length)],
  89. area: Math.floor(Math.random() * 200) + 50,
  90. styleTags: [...new Set(styleTags)], // 去重
  91. customerReview: Math.random() > 0.3 ? `客户非常满意这次${projectType}设计,${spaceType}空间利用得很合理,设计师的专业水平很高,整体效果超出预期。` : undefined,
  92. viewCount: Math.floor(Math.random() * 1000),
  93. shareCount: Math.floor(Math.random() * 100),
  94. favoriteCount: Math.floor(Math.random() * 50),
  95. isFavorite: Math.random() > 0.7,
  96. isExcellent: Math.random() > 0.5,
  97. createdAt: new Date(Date.now() - Math.floor(Math.random() * 365 * 24 * 60 * 60 * 1000))
  98. };
  99. }
  100. private generatePlaceholderImage(width: number, height: number, seed: string): string {
  101. return `https://picsum.photos/seed/${seed}/${width}/${height}`;
  102. }
  103. goBack() {
  104. this.router.navigate(['/customer-service/case-library']);
  105. }
  106. toggleFavorite() {
  107. if (!this.case) return;
  108. const wasLiked = this.case.isFavorite;
  109. this.case.isFavorite = !this.case.isFavorite;
  110. if (this.case.isFavorite) {
  111. this.case.favoriteCount++;
  112. this.showToast('已收藏该案例', 'success');
  113. this.recordBehavior('case_favorite', this.case.id, {
  114. action: 'add',
  115. caseName: this.case.name,
  116. designer: this.case.designer
  117. });
  118. } else {
  119. this.case.favoriteCount = Math.max(0, this.case.favoriteCount - 1);
  120. this.showToast('已取消收藏', 'info');
  121. this.recordBehavior('case_favorite', this.case.id, {
  122. action: 'remove',
  123. caseName: this.case.name,
  124. designer: this.case.designer
  125. });
  126. }
  127. }
  128. shareCase() {
  129. if (!this.case) return;
  130. this.case.shareCount++;
  131. // 生成分享链接
  132. const shareLink = this.generateShareLink();
  133. // 复制到剪贴板
  134. navigator.clipboard.writeText(shareLink).then(() => {
  135. this.showToast('分享链接已复制到剪贴板', 'success');
  136. }).catch(() => {
  137. this.showToast('复制失败,请手动复制链接', 'error');
  138. });
  139. this.recordBehavior('share', this.case.id, {
  140. caseName: this.case.name,
  141. designer: this.case.designer,
  142. projectType: this.case.projectType
  143. });
  144. }
  145. generateShareLink(): string {
  146. if (!this.case) return '';
  147. return `${window.location.origin}/customer-service/case-detail/${this.case.id}?from=share&designer=${encodeURIComponent(this.case.designer)}`;
  148. }
  149. getFormattedDate(date: Date): string {
  150. return new Intl.DateTimeFormat('zh-CN', {
  151. year: 'numeric',
  152. month: 'long',
  153. day: 'numeric'
  154. }).format(new Date(date));
  155. }
  156. private recordBehavior(action: string, caseId: string, data?: any) {
  157. // 模拟行为记录,实际项目中应该调用真实的API
  158. console.log('行为记录:', {
  159. action,
  160. caseId,
  161. data,
  162. timestamp: new Date().toISOString(),
  163. userAgent: navigator.userAgent
  164. });
  165. }
  166. private showToast(message: string, type: 'success' | 'error' | 'info' = 'info') {
  167. // 简单的toast实现,实际项目中可以使用更完善的toast库
  168. const toast = document.createElement('div');
  169. toast.className = `toast toast-${type}`;
  170. toast.textContent = message;
  171. toast.style.cssText = `
  172. position: fixed;
  173. top: 20px;
  174. right: 20px;
  175. padding: 12px 20px;
  176. border-radius: 8px;
  177. color: white;
  178. font-size: 14px;
  179. z-index: 10000;
  180. animation: slideInRight 0.3s ease-out;
  181. background: ${type === 'success' ? '#34c759' : type === 'error' ? '#ff3b30' : '#007aff'};
  182. `;
  183. document.body.appendChild(toast);
  184. setTimeout(() => {
  185. toast.style.animation = 'slideOutRight 0.3s ease-in';
  186. setTimeout(() => {
  187. document.body.removeChild(toast);
  188. }, 300);
  189. }, 3000);
  190. }
  191. }