|
|
@@ -1,60 +1,181 @@
|
|
|
<div class="case-library-container">
|
|
|
- <!-- 页面头部 -->
|
|
|
- <div class="page-header">
|
|
|
- <h1>案例库</h1>
|
|
|
- <div class="header-actions">
|
|
|
- <button class="btn btn-success" (click)="openCreateModal()">
|
|
|
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
|
- <line x1="12" y1="5" x2="12" y2="19"></line>
|
|
|
- <line x1="5" y1="12" x2="19" y2="12"></line>
|
|
|
- </svg>
|
|
|
- 创建案例
|
|
|
- </button>
|
|
|
- <button class="btn btn-primary" (click)="showStatistics()">
|
|
|
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
|
- <path d="M3 3v18h18"></path>
|
|
|
- <path d="m19 9-5 5-4-4-3 3"></path>
|
|
|
- </svg>
|
|
|
- 数据统计
|
|
|
- </button>
|
|
|
+ <!-- 精美页面头部 -->
|
|
|
+ <div class="page-header-enhanced">
|
|
|
+ <div class="header-background"></div>
|
|
|
+ <div class="header-content">
|
|
|
+ <div class="header-left">
|
|
|
+ <h1 class="page-title">
|
|
|
+ <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
|
+ <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>
|
|
|
+ 已完成项目案例库
|
|
|
+ </h1>
|
|
|
+ <p class="page-subtitle">查看所有已归档的精品设计案例</p>
|
|
|
+ </div>
|
|
|
+ <div class="header-stats">
|
|
|
+ <div class="stat-box">
|
|
|
+ <div class="stat-value">{{ totalCount }}</div>
|
|
|
+ <div class="stat-label">案例总数</div>
|
|
|
+ </div>
|
|
|
+ <div class="stat-box">
|
|
|
+ <div class="stat-value">{{ monthlyNewCases }}</div>
|
|
|
+ <div class="stat-label">本月新增</div>
|
|
|
+ </div>
|
|
|
+ <button class="btn-statistics" (click)="showStatistics()" [class.active]="showStatsPanel">
|
|
|
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
|
+ <path d="M3 3v18h18"></path>
|
|
|
+ <path d="m19 9-5 5-4-4-3 3"></path>
|
|
|
+ </svg>
|
|
|
+ 数据统计
|
|
|
+ <svg *ngIf="!showStatsPanel" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
|
+ <polyline points="6 9 12 15 18 9"></polyline>
|
|
|
+ </svg>
|
|
|
+ <svg *ngIf="showStatsPanel" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
|
+ <polyline points="18 15 12 9 6 15"></polyline>
|
|
|
+ </svg>
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- <!-- 统计模块 -->
|
|
|
+ <!-- 统计面板 -->
|
|
|
@if (showStatsPanel) {
|
|
|
- <div class="stats-panel">
|
|
|
- <div class="stats-grid">
|
|
|
- <div class="stat-card">
|
|
|
- <h3>Top5 分享案例</h3>
|
|
|
- <div class="stat-list">
|
|
|
- @for (item of topSharedCases; track item.id) {
|
|
|
- <div class="stat-item">
|
|
|
- <span class="case-name">{{ item.name }}</span>
|
|
|
- <span class="stat-value">{{ item.shareCount }} 次分享</span>
|
|
|
+ <div class="stats-panel-enhanced">
|
|
|
+ <div class="stats-panel-header">
|
|
|
+ <h2>
|
|
|
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
|
+ <line x1="12" y1="20" x2="12" y2="10"></line>
|
|
|
+ <line x1="18" y1="20" x2="18" y2="4"></line>
|
|
|
+ <line x1="6" y1="20" x2="6" y2="16"></line>
|
|
|
+ </svg>
|
|
|
+ 数据洞察
|
|
|
+ </h2>
|
|
|
+ <p>案例库核心指标分析</p>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="stats-grid-enhanced">
|
|
|
+ <!-- Top 5 分享案例 -->
|
|
|
+ <div class="stat-card-enhanced">
|
|
|
+ <div class="stat-card-header">
|
|
|
+ <div class="stat-card-icon share-icon">
|
|
|
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
|
+ <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>
|
|
|
+ </div>
|
|
|
+ <h3>Top 5 分享案例</h3>
|
|
|
+ </div>
|
|
|
+ <div class="stat-list-enhanced">
|
|
|
+ @if (topSharedCases.length > 0) {
|
|
|
+ @for (item of topSharedCases; track item.id; let idx = $index) {
|
|
|
+ <div class="stat-item-enhanced" [class.rank-1]="idx === 0" [class.rank-2]="idx === 1" [class.rank-3]="idx === 2">
|
|
|
+ <div class="rank-badge">{{ idx + 1 }}</div>
|
|
|
+ <div class="stat-item-content">
|
|
|
+ <span class="item-name">{{ item.name }}</span>
|
|
|
+ <span class="item-value">
|
|
|
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
|
+ <path d="M22 2L11 13"></path>
|
|
|
+ <path d="M22 2L15 22 11 13 2 9z"></path>
|
|
|
+ </svg>
|
|
|
+ {{ item.shareCount }} 次
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ }
|
|
|
+ } @else {
|
|
|
+ <div class="empty-state-small">
|
|
|
+ <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
|
|
+ <circle cx="12" cy="12" r="10"></circle>
|
|
|
+ <line x1="12" y1="8" x2="12" y2="12"></line>
|
|
|
+ <line x1="12" y1="16" x2="12.01" y2="16"></line>
|
|
|
+ </svg>
|
|
|
+ <p>暂无分享数据</p>
|
|
|
</div>
|
|
|
}
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- <div class="stat-card">
|
|
|
- <h3>客户最喜欢案例风格</h3>
|
|
|
- <div class="stat-list">
|
|
|
- @for (item of favoriteStyles; track item.style) {
|
|
|
- <div class="stat-item">
|
|
|
- <span class="style-name">{{ item.style }}</span>
|
|
|
- <span class="stat-value">{{ item.count }} 次收藏</span>
|
|
|
+ <!-- 客户最喜欢案例风格 -->
|
|
|
+ <div class="stat-card-enhanced">
|
|
|
+ <div class="stat-card-header">
|
|
|
+ <div class="stat-card-icon style-icon">
|
|
|
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
|
+ <path d="M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z"></path>
|
|
|
+ <line x1="7" y1="7" x2="7.01" y2="7"></line>
|
|
|
+ </svg>
|
|
|
+ </div>
|
|
|
+ <h3>客户最喜欢风格</h3>
|
|
|
+ </div>
|
|
|
+ <div class="stat-list-enhanced">
|
|
|
+ @if (favoriteStyles.length > 0) {
|
|
|
+ @for (item of favoriteStyles; track item.style; let idx = $index) {
|
|
|
+ <div class="stat-item-enhanced" [class.rank-1]="idx === 0" [class.rank-2]="idx === 1" [class.rank-3]="idx === 2">
|
|
|
+ <div class="rank-badge">{{ idx + 1 }}</div>
|
|
|
+ <div class="stat-item-content">
|
|
|
+ <span class="item-name">{{ item.style }}</span>
|
|
|
+ <span class="item-value">
|
|
|
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
|
+ <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>
|
|
|
+ {{ item.count }} 次收藏
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ }
|
|
|
+ } @else {
|
|
|
+ <div class="empty-state-small">
|
|
|
+ <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
|
|
+ <circle cx="12" cy="12" r="10"></circle>
|
|
|
+ <line x1="12" y1="8" x2="12" y2="12"></line>
|
|
|
+ <line x1="12" y1="16" x2="12.01" y2="16"></line>
|
|
|
+ </svg>
|
|
|
+ <p>暂无收藏数据</p>
|
|
|
</div>
|
|
|
}
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- <div class="stat-card">
|
|
|
- <h3>设计师作品推荐率</h3>
|
|
|
- <div class="stat-list">
|
|
|
- @for (item of designerRecommendations; track item.designer) {
|
|
|
- <div class="stat-item">
|
|
|
- <span class="designer-name">{{ item.designer }}</span>
|
|
|
- <span class="stat-value">{{ item.rate }}% 推荐率</span>
|
|
|
+ <!-- 设计师作品推荐率 -->
|
|
|
+ <div class="stat-card-enhanced">
|
|
|
+ <div class="stat-card-header">
|
|
|
+ <div class="stat-card-icon designer-icon">
|
|
|
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
|
+ <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
|
|
|
+ <circle cx="9" cy="7" r="4"></circle>
|
|
|
+ <path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
|
|
|
+ <path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
|
|
|
+ </svg>
|
|
|
+ </div>
|
|
|
+ <h3>设计师推荐率</h3>
|
|
|
+ </div>
|
|
|
+ <div class="stat-list-enhanced">
|
|
|
+ @if (designerRecommendations.length > 0) {
|
|
|
+ @for (item of designerRecommendations; track item.designer; let idx = $index) {
|
|
|
+ <div class="stat-item-enhanced" [class.rank-1]="idx === 0" [class.rank-2]="idx === 1" [class.rank-3]="idx === 2">
|
|
|
+ <div class="rank-badge">{{ idx + 1 }}</div>
|
|
|
+ <div class="stat-item-content">
|
|
|
+ <span class="item-name">{{ item.designer }}</span>
|
|
|
+ <span class="item-value">
|
|
|
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
|
+ <path d="m12 2 3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"></path>
|
|
|
+ </svg>
|
|
|
+ {{ item.rate }}%
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ }
|
|
|
+ } @else {
|
|
|
+ <div class="empty-state-small">
|
|
|
+ <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
|
|
+ <circle cx="12" cy="12" r="10"></circle>
|
|
|
+ <line x1="12" y1="8" x2="12" y2="12"></line>
|
|
|
+ <line x1="12" y1="16" x2="12.01" y2="16"></line>
|
|
|
+ </svg>
|
|
|
+ <p>暂无推荐数据</p>
|
|
|
</div>
|
|
|
}
|
|
|
</div>
|
|
|
@@ -64,19 +185,19 @@
|
|
|
}
|
|
|
|
|
|
<!-- 筛选栏 -->
|
|
|
- <div class="filter-bar">
|
|
|
+ <div class="filter-bar-enhanced">
|
|
|
<div class="filter-group">
|
|
|
<div class="search-box">
|
|
|
+ <svg class="search-icon" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
|
+ <circle cx="11" cy="11" r="8"></circle>
|
|
|
+ <path d="m21 21-4.3-4.3"></path>
|
|
|
+ </svg>
|
|
|
<input
|
|
|
type="text"
|
|
|
placeholder="搜索案例名称、设计师或关键词..."
|
|
|
[formControl]="searchControl"
|
|
|
class="search-input"
|
|
|
>
|
|
|
- <svg class="search-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
|
- <circle cx="11" cy="11" r="8"></circle>
|
|
|
- <path d="m21 21-4.3-4.3"></path>
|
|
|
- </svg>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
@@ -104,13 +225,13 @@
|
|
|
|
|
|
<select [formControl]="styleControl" class="filter-select">
|
|
|
<option value="">设计风格</option>
|
|
|
- <option value="现代">现代</option>
|
|
|
+ <option value="现代简约">现代简约</option>
|
|
|
+ <option value="北欧风">北欧风</option>
|
|
|
<option value="中式">中式</option>
|
|
|
<option value="欧式">欧式</option>
|
|
|
<option value="美式">美式</option>
|
|
|
<option value="日式">日式</option>
|
|
|
<option value="工业风">工业风</option>
|
|
|
- <option value="极简风">极简风</option>
|
|
|
<option value="轻奢风">轻奢风</option>
|
|
|
</select>
|
|
|
|
|
|
@@ -120,198 +241,189 @@
|
|
|
<option value="50-100">50-100㎡</option>
|
|
|
<option value="100-150">100-150㎡</option>
|
|
|
<option value="150-200">150-200㎡</option>
|
|
|
- <option value="200+">200㎡以上</option>
|
|
|
+ <option value="200-">200㎡以上</option>
|
|
|
</select>
|
|
|
</div>
|
|
|
|
|
|
<div class="filter-actions">
|
|
|
<button class="btn btn-secondary" (click)="resetFilters()">
|
|
|
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
|
+ <polyline points="1 4 1 10 7 10"></polyline>
|
|
|
+ <path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"></path>
|
|
|
+ </svg>
|
|
|
重置筛选
|
|
|
</button>
|
|
|
- <button class="btn btn-primary" (click)="applyFilters()">
|
|
|
- 应用筛选
|
|
|
- </button>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 加载状态 -->
|
|
|
@if (loading) {
|
|
|
<div class="loading-state">
|
|
|
- <div class="spinner"></div>
|
|
|
- <p>加载中...</p>
|
|
|
+ <div class="spinner-enhanced"></div>
|
|
|
+ <p>正在加载案例...</p>
|
|
|
</div>
|
|
|
}
|
|
|
|
|
|
<!-- 案例网格 -->
|
|
|
- <div class="cases-grid" [class.hidden]="loading">
|
|
|
+ <div class="cases-grid-enhanced" [class.hidden]="loading">
|
|
|
@for (caseItem of paginatedCases; track caseItem.id) {
|
|
|
- <div class="case-card">
|
|
|
+ <div class="case-card-enhanced" (click)="viewCaseDetail(caseItem)">
|
|
|
<!-- 图片容器 -->
|
|
|
- <div class="case-image-container">
|
|
|
+ <div class="case-image-wrapper">
|
|
|
<img
|
|
|
[src]="caseItem.coverImage"
|
|
|
[alt]="caseItem.name"
|
|
|
class="case-image"
|
|
|
- (click)="viewCaseDetail(caseItem)"
|
|
|
+ onerror="this.src='https://via.placeholder.com/400x300/667eea/ffffff?text=暂无封面'"
|
|
|
>
|
|
|
|
|
|
- <!-- 图片覆盖层 -->
|
|
|
- <div class="image-overlay">
|
|
|
- <div class="case-badges">
|
|
|
- <span class="badge project-type">{{ caseItem.projectType }}</span>
|
|
|
- <span class="badge space-type">{{ caseItem.spaceType }}</span>
|
|
|
- <span class="badge rendering-level">{{ caseItem.renderingLevel }}</span>
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="action-buttons">
|
|
|
- <button
|
|
|
- class="btn-icon favorite-btn"
|
|
|
- [class.active]="caseItem.isFavorite"
|
|
|
- (click)="toggleFavorite(caseItem)"
|
|
|
- >
|
|
|
- <svg width="20" height="20" 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>
|
|
|
- </button>
|
|
|
-
|
|
|
- <button class="btn-icon share-btn" (click)="shareCase(caseItem)">
|
|
|
- <svg width="20" height="20" 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>
|
|
|
- </div>
|
|
|
+ <!-- 完成徽章 -->
|
|
|
+ <div class="completion-badge">
|
|
|
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
|
+ <polyline points="20 6 9 17 4 12"></polyline>
|
|
|
+ </svg>
|
|
|
+ 售后归档完成
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 标签 -->
|
|
|
+ <div class="case-badges">
|
|
|
+ <span class="badge badge-primary">{{ caseItem.projectType }}</span>
|
|
|
+ <span class="badge badge-secondary">{{ caseItem.spaceType }}</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 案例信息 -->
|
|
|
- <div class="case-info">
|
|
|
- <div class="case-header">
|
|
|
- <h3 class="case-name" (click)="viewCaseDetail(caseItem)">{{ caseItem.name }}</h3>
|
|
|
- <button class="info-share-btn" (click)="shareCase(caseItem)" title="分享案例">
|
|
|
- <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>
|
|
|
- </div>
|
|
|
+ <div class="case-content">
|
|
|
+ <h3 class="case-title">{{ caseItem.name }}</h3>
|
|
|
|
|
|
- <div class="case-meta">
|
|
|
- <div class="meta-item">
|
|
|
- <svg width="16" height="16" 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>
|
|
|
- <span>{{ caseItem.designer }}</span>
|
|
|
- @if (isInternalUser) {
|
|
|
- <span class="team-badge">{{ caseItem.team }}</span>
|
|
|
- }
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="meta-item">
|
|
|
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
|
+ <!-- 设计师信息 -->
|
|
|
+ <div class="designer-info">
|
|
|
+ <svg width="16" height="16" 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>
|
|
|
+ <span class="designer-name">{{ caseItem.designer }}</span>
|
|
|
+ @if (caseItem.team) {
|
|
|
+ <span class="team-badge">{{ caseItem.team }}</span>
|
|
|
+ }
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 案例详情 -->
|
|
|
+ <div class="case-details">
|
|
|
+ <div class="detail-item">
|
|
|
+ <svg width="14" height="14" 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>
|
|
|
<span>{{ caseItem.area }}㎡</span>
|
|
|
</div>
|
|
|
-
|
|
|
- <div class="meta-item">
|
|
|
- <svg width="16" height="16" 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>
|
|
|
+ <div class="detail-item">
|
|
|
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
|
+ <polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline>
|
|
|
</svg>
|
|
|
- <span>{{ caseItem.viewCount }} 浏览</span>
|
|
|
+ <span>{{ caseItem.renderingLevel }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="detail-item">
|
|
|
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
|
+ <rect x="2" y="7" width="20" height="14" rx="2" ry="2"></rect>
|
|
|
+ <path d="M16 21V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16"></path>
|
|
|
+ </svg>
|
|
|
+ <span>{{ caseItem.roomType || caseItem.spaceType }}</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 风格标签 -->
|
|
|
- <div class="case-tags">
|
|
|
- @for (tag of caseItem.styleTags; track $index) {
|
|
|
- <span class="tag">{{ tag }}</span>
|
|
|
- }
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 客户评价 -->
|
|
|
- @if (caseItem.customerReview) {
|
|
|
- <div class="customer-review">
|
|
|
- <p class="review-text">"{{ caseItem.customerReview }}"</p>
|
|
|
+ @if (caseItem.tag && caseItem.tag.length > 0) {
|
|
|
+ <div class="style-tags">
|
|
|
+ @for (tag of caseItem.tag.slice(0, 3); track $index) {
|
|
|
+ <span class="tag">{{ tag }}</span>
|
|
|
+ }
|
|
|
+ @if (caseItem.tag.length > 3) {
|
|
|
+ <span class="tag-more">+{{ caseItem.tag.length - 3 }}</span>
|
|
|
+ }
|
|
|
</div>
|
|
|
}
|
|
|
|
|
|
- <!-- 设计师内部信息 -->
|
|
|
- @if (isInternalUser) {
|
|
|
- <div class="internal-info">
|
|
|
- <div class="internal-badge" [class.excellent]="caseItem.isExcellent">
|
|
|
- {{ caseItem.isExcellent ? '优秀案例库' : '普通案例' }}
|
|
|
- </div>
|
|
|
- <div class="internal-stats">
|
|
|
- <span>分享: {{ caseItem.shareCount }}</span>
|
|
|
- <span>收藏: {{ caseItem.favoriteCount }}</span>
|
|
|
- </div>
|
|
|
+ <!-- 项目总额 -->
|
|
|
+ @if (caseItem.totalPrice) {
|
|
|
+ <div class="price-card">
|
|
|
+ <div class="price-label">项目总额</div>
|
|
|
+ <div class="price-value">¥{{ caseItem.totalPrice.toLocaleString() }}</div>
|
|
|
</div>
|
|
|
-
|
|
|
- <!-- 管理操作按钮 -->
|
|
|
- <div class="case-actions">
|
|
|
- <button class="btn-action btn-edit" (click)="openEditModal(caseItem)" title="编辑案例">
|
|
|
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
|
- <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path>
|
|
|
- <path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path>
|
|
|
- </svg>
|
|
|
- 编辑
|
|
|
- </button>
|
|
|
- <button class="btn-action btn-delete" (click)="deleteCase(caseItem)" title="删除案例">
|
|
|
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
|
- <polyline points="3 6 5 6 21 6"></polyline>
|
|
|
- <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
|
|
|
- </svg>
|
|
|
- 删除
|
|
|
- </button>
|
|
|
+ }
|
|
|
+
|
|
|
+ <!-- 完成时间 -->
|
|
|
+ @if (caseItem.completionDate) {
|
|
|
+ <div class="completion-info">
|
|
|
+ <svg width="14" height="14" 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>
|
|
|
+ <span>完成于 {{ caseItem.completionDate | date: 'yyyy-MM-dd' }}</span>
|
|
|
</div>
|
|
|
}
|
|
|
|
|
|
<!-- 操作按钮 -->
|
|
|
- <div class="case-actions">
|
|
|
- <button class="btn btn-outline" (click)="viewCaseDetail(caseItem)">
|
|
|
- 快速预览
|
|
|
- </button>
|
|
|
- <button class="btn btn-primary" (click)="navigateToCaseDetail(caseItem)">
|
|
|
+ <div class="case-actions-row">
|
|
|
+ <button class="btn-view-detail" (click)="viewCaseDetail(caseItem); $event.stopPropagation()">
|
|
|
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
|
+ <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
|
|
|
+ <circle cx="12" cy="12" r="3"></circle>
|
|
|
+ </svg>
|
|
|
查看详情
|
|
|
</button>
|
|
|
+ <button class="btn-share" (click)="shareCase(caseItem); $event.stopPropagation()" title="分享案例">
|
|
|
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
|
+ <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>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
} @empty {
|
|
|
- <div class="empty-state">
|
|
|
- <svg width="64" height="64" 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>
|
|
|
- <h3>暂无案例</h3>
|
|
|
- <p>尝试调整筛选条件或刷新页面</p>
|
|
|
- <button class="btn btn-primary" (click)="resetFilters()">重置筛选</button>
|
|
|
+ <div class="empty-state-enhanced">
|
|
|
+ <div class="empty-icon">
|
|
|
+ <svg width="80" height="80" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
|
|
+ <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>
|
|
|
+ <h3>暂无已完成项目案例</h3>
|
|
|
+ <p>当项目进入"售后归档"阶段时,案例会自动出现在这里</p>
|
|
|
+ <button class="btn btn-primary" (click)="resetFilters()">
|
|
|
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
|
+ <polyline points="1 4 1 10 7 10"></polyline>
|
|
|
+ <path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"></path>
|
|
|
+ </svg>
|
|
|
+ 重置筛选
|
|
|
+ </button>
|
|
|
</div>
|
|
|
}
|
|
|
</div>
|
|
|
|
|
|
<!-- 分页控件 -->
|
|
|
@if (totalCount > 0 && !loading) {
|
|
|
- <div class="pagination">
|
|
|
+ <div class="pagination-enhanced">
|
|
|
<button
|
|
|
class="pagination-btn"
|
|
|
[disabled]="currentPage === 1"
|
|
|
(click)="previousPage()"
|
|
|
>
|
|
|
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
|
+ <polyline points="15 18 9 12 15 6"></polyline>
|
|
|
+ </svg>
|
|
|
上一页
|
|
|
</button>
|
|
|
|
|
|
- <span class="page-info">第 {{ currentPage }} 页 / 共 {{ totalPages }} 页 (共 {{ totalCount }} 个案例)</span>
|
|
|
+ <span class="page-info">
|
|
|
+ 第 <strong>{{ currentPage }}</strong> 页 / 共 <strong>{{ totalPages }}</strong> 页
|
|
|
+ <span class="divider">|</span>
|
|
|
+ 共 <strong>{{ totalCount }}</strong> 个案例
|
|
|
+ </span>
|
|
|
|
|
|
<button
|
|
|
class="pagination-btn"
|
|
|
@@ -319,6 +431,9 @@
|
|
|
(click)="nextPage()"
|
|
|
>
|
|
|
下一页
|
|
|
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
|
+ <polyline points="9 18 15 12 9 6"></polyline>
|
|
|
+ </svg>
|
|
|
</button>
|
|
|
</div>
|
|
|
}
|
|
|
@@ -336,51 +451,61 @@
|
|
|
|
|
|
<!-- 分享弹窗 -->
|
|
|
@if (selectedCaseForShare) {
|
|
|
- <div class="share-modal" (click)="closeShareModal()">
|
|
|
- <div class="share-content" (click)="$event.stopPropagation()">
|
|
|
+ <div class="share-modal-overlay" (click)="closeShareModal()">
|
|
|
+ <div class="share-modal-content" (click)="$event.stopPropagation()">
|
|
|
<div class="share-header">
|
|
|
<h3>分享案例</h3>
|
|
|
- <button class="close-btn" (click)="closeShareModal()">×</button>
|
|
|
+ <button class="close-btn" (click)="closeShareModal()">
|
|
|
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
|
+ <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="share-options">
|
|
|
- <div class="qr-code">
|
|
|
- <img [src]="generateQRCode(selectedCaseForShare)" alt="分享二维码">
|
|
|
- <p>扫描二维码分享</p>
|
|
|
+ <div class="share-body">
|
|
|
+ <div class="share-preview">
|
|
|
+ <img [src]="selectedCaseForShare.coverImage" [alt]="selectedCaseForShare.name">
|
|
|
+ <h4>{{ selectedCaseForShare.name }}</h4>
|
|
|
+ <p>{{ selectedCaseForShare.designer }} | {{ selectedCaseForShare.area }}㎡</p>
|
|
|
</div>
|
|
|
|
|
|
- <div class="share-links">
|
|
|
- <div class="share-link">
|
|
|
- <input
|
|
|
- type="text"
|
|
|
- [value]="generateShareLink(selectedCaseForShare)"
|
|
|
- readonly
|
|
|
- class="link-input"
|
|
|
- >
|
|
|
- <button class="btn btn-secondary" (click)="copyShareLink()">复制链接</button>
|
|
|
+ <div class="share-options">
|
|
|
+ <div class="qr-code">
|
|
|
+ <img [src]="generateQRCode(selectedCaseForShare)" alt="分享二维码">
|
|
|
+ <p>扫描二维码分享</p>
|
|
|
</div>
|
|
|
|
|
|
- <div class="social-share">
|
|
|
- <button class="btn btn-primary" (click)="shareToWeCom()">
|
|
|
- <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
|
|
- <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>
|
|
|
- <rect x="2" y="9" width="4" height="12"></rect>
|
|
|
- <circle cx="4" cy="4" r="2"></circle>
|
|
|
- </svg>
|
|
|
- 分享到企业微信
|
|
|
- </button>
|
|
|
+ <div class="share-link-box">
|
|
|
+ <label>分享链接</label>
|
|
|
+ <div class="link-input-group">
|
|
|
+ <input
|
|
|
+ type="text"
|
|
|
+ [value]="generateShareLink(selectedCaseForShare)"
|
|
|
+ readonly
|
|
|
+ class="link-input"
|
|
|
+ >
|
|
|
+ <button class="btn btn-primary" (click)="copyShareLink()">
|
|
|
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
|
+ <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
|
|
|
+ <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
|
|
|
+ </svg>
|
|
|
+ 复制链接
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
+
|
|
|
+ <button class="btn btn-wecom" (click)="shareToWeCom()">
|
|
|
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
|
|
|
+ <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>
|
|
|
+ <rect x="2" y="9" width="4" height="12"></rect>
|
|
|
+ <circle cx="4" cy="4" r="2"></circle>
|
|
|
+ </svg>
|
|
|
+ 分享到企业微信
|
|
|
+ </button>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
}
|
|
|
-
|
|
|
- <!-- 案例管理模态框 -->
|
|
|
- <app-case-management-modal
|
|
|
- [isVisible]="showManagementModal"
|
|
|
- [editingCase]="editingCase"
|
|
|
- (close)="closeManagementModal()"
|
|
|
- (success)="handleManagementSuccess($event)"
|
|
|
- ></app-case-management-modal>
|
|
|
-</div>
|
|
|
+</div>
|