case-library.html 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. <div class="case-library-container">
  2. <!-- 页面头部 -->
  3. <div class="page-header">
  4. <h1>案例库</h1>
  5. <div class="header-actions">
  6. <button class="btn btn-primary" (click)="showStatistics()">
  7. <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
  8. <path d="M3 3v18h18"></path>
  9. <path d="m19 9-5 5-4-4-3 3"></path>
  10. </svg>
  11. 数据统计
  12. </button>
  13. </div>
  14. </div>
  15. <!-- 统计模块 -->
  16. @if (showStatsPanel) {
  17. <div class="stats-panel">
  18. <div class="stats-grid">
  19. <div class="stat-card">
  20. <h3>Top5 分享案例</h3>
  21. <div class="stat-list">
  22. @for (item of topSharedCases; track item.id) {
  23. <div class="stat-item">
  24. <span class="case-name">{{ item.name }}</span>
  25. <span class="stat-value">{{ item.shareCount }} 次分享</span>
  26. </div>
  27. }
  28. </div>
  29. </div>
  30. <div class="stat-card">
  31. <h3>客户最喜欢案例风格</h3>
  32. <div class="stat-list">
  33. @for (item of favoriteStyles; track item.style) {
  34. <div class="stat-item">
  35. <span class="style-name">{{ item.style }}</span>
  36. <span class="stat-value">{{ item.count }} 次收藏</span>
  37. </div>
  38. }
  39. </div>
  40. </div>
  41. <div class="stat-card">
  42. <h3>设计师作品推荐率</h3>
  43. <div class="stat-list">
  44. @for (item of designerRecommendations; track item.designer) {
  45. <div class="stat-item">
  46. <span class="designer-name">{{ item.designer }}</span>
  47. <span class="stat-value">{{ item.rate }}% 推荐率</span>
  48. </div>
  49. }
  50. </div>
  51. </div>
  52. </div>
  53. </div>
  54. }
  55. <!-- 筛选栏 -->
  56. <div class="filter-bar">
  57. <div class="filter-group">
  58. <div class="search-box">
  59. <input
  60. type="text"
  61. placeholder="搜索案例名称、设计师或关键词..."
  62. [formControl]="searchControl"
  63. class="search-input"
  64. >
  65. <svg class="search-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
  66. <circle cx="11" cy="11" r="8"></circle>
  67. <path d="m21 21-4.3-4.3"></path>
  68. </svg>
  69. </div>
  70. </div>
  71. <div class="filter-group">
  72. <select [formControl]="projectTypeControl" class="filter-select">
  73. <option value="">项目类型</option>
  74. <option value="工装">工装</option>
  75. <option value="家装">家装</option>
  76. </select>
  77. <select [formControl]="spaceTypeControl" class="filter-select">
  78. <option value="">空间类型</option>
  79. <option value="平层">平层</option>
  80. <option value="复式">复式</option>
  81. <option value="别墅">别墅</option>
  82. <option value="自建房">自建房</option>
  83. </select>
  84. <select [formControl]="renderingLevelControl" class="filter-select">
  85. <option value="">渲染水平</option>
  86. <option value="高端">高端</option>
  87. <option value="中端">中端</option>
  88. <option value="低端">低端</option>
  89. </select>
  90. <select [formControl]="styleControl" class="filter-select">
  91. <option value="">设计风格</option>
  92. <option value="现代">现代</option>
  93. <option value="中式">中式</option>
  94. <option value="欧式">欧式</option>
  95. <option value="美式">美式</option>
  96. <option value="日式">日式</option>
  97. <option value="工业风">工业风</option>
  98. <option value="极简风">极简风</option>
  99. <option value="轻奢风">轻奢风</option>
  100. </select>
  101. <select [formControl]="areaRangeControl" class="filter-select">
  102. <option value="">面积范围</option>
  103. <option value="0-50">50㎡以下</option>
  104. <option value="50-100">50-100㎡</option>
  105. <option value="100-150">100-150㎡</option>
  106. <option value="150-200">150-200㎡</option>
  107. <option value="200+">200㎡以上</option>
  108. </select>
  109. </div>
  110. <div class="filter-actions">
  111. <button class="btn btn-secondary" (click)="resetFilters()">
  112. 重置筛选
  113. </button>
  114. <button class="btn btn-primary" (click)="applyFilters()">
  115. 应用筛选
  116. </button>
  117. </div>
  118. </div>
  119. <!-- 案例网格 -->
  120. <div class="cases-grid">
  121. @for (caseItem of paginatedCases; track caseItem.id) {
  122. <div class="case-card">
  123. <!-- 图片容器 -->
  124. <div class="case-image-container">
  125. <img
  126. [src]="caseItem.coverImage"
  127. [alt]="caseItem.name"
  128. class="case-image"
  129. (click)="viewCaseDetail(caseItem)"
  130. >
  131. <!-- 图片覆盖层 -->
  132. <div class="image-overlay">
  133. <div class="case-badges">
  134. <span class="badge project-type">{{ caseItem.projectType }}</span>
  135. <span class="badge space-type">{{ caseItem.spaceType }}</span>
  136. <span class="badge rendering-level">{{ caseItem.renderingLevel }}</span>
  137. </div>
  138. <div class="action-buttons">
  139. <button
  140. class="btn-icon favorite-btn"
  141. [class.active]="caseItem.isFavorite"
  142. (click)="toggleFavorite(caseItem)"
  143. >
  144. <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
  145. <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>
  146. </svg>
  147. </button>
  148. <button class="btn-icon share-btn" (click)="shareCase(caseItem)">
  149. <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
  150. <circle cx="18" cy="5" r="3"></circle>
  151. <circle cx="6" cy="12" r="3"></circle>
  152. <circle cx="18" cy="19" r="3"></circle>
  153. <line x1="8.59" y1="13.51" x2="15.42" y2="17.49"></line>
  154. <line x1="15.41" y1="6.51" x2="8.59" y2="10.49"></line>
  155. </svg>
  156. </button>
  157. </div>
  158. </div>
  159. </div>
  160. <!-- 案例信息 -->
  161. <div class="case-info">
  162. <div class="case-header">
  163. <h3 class="case-name" (click)="viewCaseDetail(caseItem)">{{ caseItem.name }}</h3>
  164. <button class="info-share-btn" (click)="shareCase(caseItem)" title="分享案例">
  165. <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
  166. <circle cx="18" cy="5" r="3"></circle>
  167. <circle cx="6" cy="12" r="3"></circle>
  168. <circle cx="18" cy="19" r="3"></circle>
  169. <line x1="8.59" y1="13.51" x2="15.42" y2="17.49"></line>
  170. <line x1="15.41" y1="6.51" x2="8.59" y2="10.49"></line>
  171. </svg>
  172. </button>
  173. </div>
  174. <div class="case-meta">
  175. <div class="meta-item">
  176. <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
  177. <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
  178. <circle cx="12" cy="7" r="4"></circle>
  179. </svg>
  180. <span>{{ caseItem.designer }}</span>
  181. @if (isInternalUser) {
  182. <span class="team-badge">{{ caseItem.team }}</span>
  183. }
  184. </div>
  185. <div class="meta-item">
  186. <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
  187. <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
  188. <line x1="3" y1="9" x2="21" y2="9"></line>
  189. </svg>
  190. <span>{{ caseItem.area }}㎡</span>
  191. </div>
  192. <div class="meta-item">
  193. <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
  194. <path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"></path>
  195. <path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"></path>
  196. </svg>
  197. <span>{{ caseItem.viewCount }} 浏览</span>
  198. </div>
  199. </div>
  200. <!-- 风格标签 -->
  201. <div class="case-tags">
  202. @for (tag of caseItem.styleTags; track $index) {
  203. <span class="tag">{{ tag }}</span>
  204. }
  205. </div>
  206. <!-- 客户评价 -->
  207. @if (caseItem.customerReview) {
  208. <div class="customer-review">
  209. <p class="review-text">"{{ caseItem.customerReview }}"</p>
  210. </div>
  211. }
  212. <!-- 设计师内部信息 -->
  213. @if (isInternalUser) {
  214. <div class="internal-info">
  215. <div class="internal-badge" [class.excellent]="caseItem.isExcellent">
  216. {{ caseItem.isExcellent ? '优秀案例库' : '普通案例' }}
  217. </div>
  218. <div class="internal-stats">
  219. <span>分享: {{ caseItem.shareCount }}</span>
  220. <span>收藏: {{ caseItem.favoriteCount }}</span>
  221. </div>
  222. </div>
  223. }
  224. </div>
  225. </div>
  226. } @empty {
  227. <div class="empty-state">
  228. <svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor">
  229. <path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"></path>
  230. <path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"></path>
  231. </svg>
  232. <h3>暂无案例</h3>
  233. <p>尝试调整筛选条件或刷新页面</p>
  234. <button class="btn btn-primary" (click)="resetFilters()">重置筛选</button>
  235. </div>
  236. }
  237. </div>
  238. <!-- 分页控件 -->
  239. @if (filteredCases.length > 0) {
  240. <div class="pagination">
  241. <button
  242. class="pagination-btn"
  243. [disabled]="currentPage === 1"
  244. (click)="previousPage()"
  245. >
  246. 上一页
  247. </button>
  248. <span class="page-info">第 {{ currentPage }} 页 / 共 {{ totalPages }} 页 ({{ filteredCases.length }} 个案例)</span>
  249. <button
  250. class="pagination-btn"
  251. [disabled]="currentPage === totalPages"
  252. (click)="nextPage()"
  253. >
  254. 下一页
  255. </button>
  256. </div>
  257. }
  258. <!-- 案例详情面板 -->
  259. @if (selectedCase) {
  260. <app-case-detail-panel
  261. [case]="selectedCase"
  262. [isInternalUser]="isInternalUser"
  263. (close)="closeCaseDetail()"
  264. (toggleFavorite)="toggleFavorite($event)"
  265. (share)="shareCase($event)"
  266. ></app-case-detail-panel>
  267. }
  268. <!-- 分享弹窗 -->
  269. @if (selectedCaseForShare) {
  270. <div class="share-modal" (click)="closeShareModal()">
  271. <div class="share-content" (click)="$event.stopPropagation()">
  272. <div class="share-header">
  273. <h3>分享案例</h3>
  274. <button class="close-btn" (click)="closeShareModal()">×</button>
  275. </div>
  276. <div class="share-options">
  277. <div class="qr-code">
  278. <img [src]="generateQRCode(selectedCaseForShare)" alt="分享二维码">
  279. <p>扫描二维码分享</p>
  280. </div>
  281. <div class="share-links">
  282. <div class="share-link">
  283. <input
  284. type="text"
  285. [value]="generateShareLink(selectedCaseForShare)"
  286. readonly
  287. class="link-input"
  288. >
  289. <button class="btn btn-secondary" (click)="copyShareLink()">复制链接</button>
  290. </div>
  291. <div class="social-share">
  292. <button class="btn btn-primary" (click)="shareToWeCom()">
  293. <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
  294. <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>
  295. <rect x="2" y="9" width="4" height="12"></rect>
  296. <circle cx="4" cy="4" r="2"></circle>
  297. </svg>
  298. 分享到企业微信
  299. </button>
  300. </div>
  301. </div>
  302. </div>
  303. </div>
  304. </div>
  305. }
  306. </div>