import { ProjectSpaceDeliverableService } from '@modules/project/services/project-space-deliverable.service';
@Component({
// ...
})
export class YourComponent {
constructor(
private projectSpaceDeliverableService: ProjectSpaceDeliverableService
) {}
}
async loadProjectStats(projectId: string) {
const summary = await this.projectSpaceDeliverableService
.getProjectSpaceDeliverableSummary(projectId);
console.log('项目统计:', summary);
}
<div class="project-card">
<h3>{{ projectName }}</h3>
<!-- 显示空间统计 -->
<div class="stats">
<span>空间: {{ summary.spacesWithDeliverables }}/{{ summary.totalSpaces }}</span>
<span>文件: {{ summary.totalDeliverableFiles }}</span>
<span>完成率: {{ summary.overallCompletionRate }}%</span>
</div>
</div>
// TypeScript
getCompletionBadge(projectId: string) {
const summary = this.cache.get(projectId);
if (!summary) return null;
return {
text: `${summary.spacesWithDeliverables}/${summary.totalSpaces}`,
color: this.projectSpaceDeliverableService.getDeliveryStatusColor(
summary.overallCompletionRate
),
tooltip: `已完成 ${summary.spacesWithDeliverables} 个空间,共 ${summary.totalSpaces} 个`
};
}
<!-- HTML -->
@if (getCompletionBadge(project.id); as badge) {
<span class="badge"
[style.background-color]="badge.color"
[title]="badge.tooltip">
📦 {{ badge.text }}
</span>
}
async canDeliver(projectId: string): Promise<boolean> {
const isCompleted = await this.projectSpaceDeliverableService
.isAllSpacesDelivered(projectId);
if (!isCompleted) {
const incompleteSpaces = await this.projectSpaceDeliverableService
.getIncompleteSpaces(projectId);
alert(`以下空间还未完成:${incompleteSpaces.join(', ')}`);
return false;
}
return true;
}
// TypeScript
async loadProgress(projectId: string) {
this.progress = await this.projectSpaceDeliverableService
.getProjectDeliveryProgress(projectId);
this.progressColor = this.projectSpaceDeliverableService
.getDeliveryStatusColor(this.progress);
}
<!-- HTML -->
<div class="progress-bar">
<div class="progress-fill"
[style.width.%]="progress"
[style.background-color]="progressColor">
{{ progress }}%
</div>
</div>
// SCSS
.progress-bar {
width: 100%;
height: 24px;
background: #e5e7eb;
border-radius: 12px;
overflow: hidden;
.progress-fill {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 12px;
font-weight: 600;
transition: width 0.3s, background-color 0.3s;
}
}
export class YourComponent implements OnInit {
private statsCache = new Map<string, ProjectSpaceDeliverableSummary>();
async getStats(projectId: string) {
if (!this.statsCache.has(projectId)) {
const summary = await this.projectSpaceDeliverableService
.getProjectSpaceDeliverableSummary(projectId);
this.statsCache.set(projectId, summary);
}
return this.statsCache.get(projectId)!;
}
}
async loadAllStats(projectIds: string[]) {
const promises = projectIds.map(id =>
this.projectSpaceDeliverableService.getProjectSpaceDeliverableSummary(id)
);
const results = await Promise.allSettled(promises);
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
this.statsCache.set(projectIds[index], result.value);
}
});
}
async loadStats(projectId: string) {
try {
const summary = await this.projectSpaceDeliverableService
.getProjectSpaceDeliverableSummary(projectId);
this.statsCache.set(projectId, summary);
} catch (error) {
console.error('加载统计失败:', error);
// 使用默认值
this.statsCache.set(projectId, {
projectId,
projectName: '未知项目',
totalSpaces: 0,
spacesWithDeliverables: 0,
spaces: [],
totalDeliverableFiles: 0,
totalByType: {
whiteModel: 0,
softDecor: 0,
rendering: 0,
postProcess: 0
},
overallCompletionRate: 0
});
}
}
<div class="project-card">
<!-- 项目标题 -->
<div class="card-header">
<h3>{{ project.name }}</h3>
@if (getStats(project.id); as stats) {
<span class="completion-badge"
[style.background-color]="getStatusColor(stats.overallCompletionRate)">
{{ stats.overallCompletionRate }}%
</span>
}
</div>
<!-- 空间列表 -->
@if (getStats(project.id); as stats) {
<div class="spaces-list">
@for (space of stats.spaces; track space.spaceId) {
<div class="space-item">
<span class="space-name">{{ space.spaceName }}</span>
<span class="space-files">
📁 {{ space.totalFiles }}
</span>
@if (space.hasDeliverables) {
<span class="check-icon">✅</span>
} @else {
<span class="pending-icon">⏳</span>
}
</div>
}
</div>
}
<!-- 文件统计 -->
@if (getStats(project.id); as stats) {
<div class="file-stats">
<div class="stat-item">
<span class="icon">🏗️</span>
<span class="count">{{ stats.totalByType.whiteModel }}</span>
</div>
<div class="stat-item">
<span class="icon">🎨</span>
<span class="count">{{ stats.totalByType.softDecor }}</span>
</div>
<div class="stat-item">
<span class="icon">🖼️</span>
<span class="count">{{ stats.totalByType.rendering }}</span>
</div>
<div class="stat-item">
<span class="icon">✨</span>
<span class="count">{{ stats.totalByType.postProcess }}</span>
</div>
</div>
}
</div>
.project-card {
background: white;
border-radius: 12px;
padding: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
h3 {
margin: 0;
font-size: 16px;
font-weight: 600;
}
.completion-badge {
padding: 4px 12px;
border-radius: 12px;
color: white;
font-size: 12px;
font-weight: 600;
}
}
.spaces-list {
margin-bottom: 16px;
.space-item {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 0;
border-bottom: 1px solid #f3f4f6;
&:last-child {
border-bottom: none;
}
.space-name {
flex: 1;
font-size: 14px;
}
.space-files {
font-size: 12px;
color: #6b7280;
}
.check-icon {
font-size: 16px;
}
.pending-icon {
font-size: 16px;
opacity: 0.5;
}
}
}
.file-stats {
display: flex;
gap: 12px;
padding-top: 12px;
border-top: 1px solid #f3f4f6;
.stat-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
.icon {
font-size: 20px;
}
.count {
font-size: 14px;
font-weight: 600;
color: #374151;
}
}
}
}
import { Component, OnInit } from '@angular/core';
import { ProjectSpaceDeliverableService, ProjectSpaceDeliverableSummary }
from '@modules/project/services/project-space-deliverable.service';
@Component({
selector: 'app-project-dashboard',
templateUrl: './project-dashboard.component.html',
styleUrls: ['./project-dashboard.component.scss']
})
export class ProjectDashboardComponent implements OnInit {
projects: any[] = [];
statsCache = new Map<string, ProjectSpaceDeliverableSummary>();
loading = false;
constructor(
private projectSpaceDeliverableService: ProjectSpaceDeliverableService
) {}
async ngOnInit() {
await this.loadProjects();
await this.loadAllStats();
}
async loadProjects() {
// 加载项目列表...
this.projects = []; // 从API获取
}
async loadAllStats() {
this.loading = true;
try {
const projectIds = this.projects.map(p => p.id);
const promises = projectIds.map(id =>
this.projectSpaceDeliverableService.getProjectSpaceDeliverableSummary(id)
);
const results = await Promise.allSettled(promises);
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
this.statsCache.set(projectIds[index], result.value);
} else {
console.warn(`加载项目 ${projectIds[index]} 统计失败:`, result.reason);
}
});
} catch (error) {
console.error('加载统计失败:', error);
} finally {
this.loading = false;
}
}
getStats(projectId: string): ProjectSpaceDeliverableSummary | null {
return this.statsCache.get(projectId) || null;
}
getStatusColor(completionRate: number): string {
return this.projectSpaceDeliverableService.getDeliveryStatusColor(completionRate);
}
getStatusLabel(completionRate: number): string {
return this.projectSpaceDeliverableService.getDeliveryStatusLabel(completionRate);
}
formatTooltip(projectId: string): string {
const stats = this.getStats(projectId);
if (!stats) return '加载中...';
return `空间: ${stats.spacesWithDeliverables}/${stats.totalSpaces}\n` +
`文件: ${stats.totalDeliverableFiles}\n` +
`完成率: ${stats.overallCompletionRate}%`;
}
}
// 清除缓存并重新加载
this.statsCache.delete(projectId);
await this.loadStats(projectId);
async getStats(projectId: string) {
try {
if (!this.statsCache.has(projectId)) {
const summary = await this.projectSpaceDeliverableService
.getProjectSpaceDeliverableSummary(projectId);
this.statsCache.set(projectId, summary);
}
return this.statsCache.get(projectId)!;
} catch (error) {
console.error('加载失败:', error);
return null; // 返回null,让模板使用@if处理
}
}
// 使用分页加载
async loadStatsInPages(projectIds: string[], pageSize = 10) {
for (let i = 0; i < projectIds.length; i += pageSize) {
const page = projectIds.slice(i, i + pageSize);
await this.loadBatch(page);
// 触发UI更新
this.cdr.markForCheck();
}
}
private async loadBatch(projectIds: string[]) {
const promises = projectIds.map(id =>
this.projectSpaceDeliverableService.getProjectSpaceDeliverableSummary(id)
);
const results = await Promise.allSettled(promises);
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
this.statsCache.set(projectIds[index], result.value);
}
});
}
提示:更多高级用法和详细说明,请查看完整使用指南。