ProjectSpaceDeliverableService 是一个封装的服务,用于计算项目中的空间数量和每个空间对应的交付物上传状态。这个服务可以在不同的地方使用,如组长端的项目时间轴、看板等。
✅ 空间统计:根据 Product 表计算项目中有多少个空间 ✅ 交付物统计:根据 ProjectFile 表统计每个空间的交付物上传情况 ✅ 多类型支持:支持白模、软装、渲染、后期四种交付类型 ✅ 自动去重:自动去除重复的空间(按名称) ✅ 完成率计算:自动计算每个空间和整体的完成率 ✅ 状态标签:提供状态文本和颜色标识
src/modules/project/services/project-space-deliverable.service.ts
interface SpaceDeliverableInfo {
spaceId: string; // 空间ID(Product ID)
spaceName: string; // 空间名称
spaceType: string; // 空间类型
deliverableTypes: {
whiteModel: number; // 白模文件数量
softDecor: number; // 软装文件数量
rendering: number; // 渲染文件数量
postProcess: number; // 后期文件数量
};
totalFiles: number; // 总文件数
hasDeliverables: boolean; // 是否已上传交付物
completionRate: number; // 完成度(0-100)
}
interface ProjectSpaceDeliverableSummary {
projectId: string; // 项目ID
projectName: string; // 项目名称
totalSpaces: number; // 空间总数
spacesWithDeliverables: number; // 已上传交付物的空间数
spaces: SpaceDeliverableInfo[]; // 空间详细列表
totalDeliverableFiles: number; // 总交付文件数
totalByType: { // 各类型总计
whiteModel: number;
softDecor: number;
rendering: number;
postProcess: number;
};
overallCompletionRate: number; // 整体完成率(0-100)
}
async getProjectSpaceDeliverableSummary(projectId: string): Promise<ProjectSpaceDeliverableSummary>
用途:获取项目的完整统计信息
示例:
import { ProjectSpaceDeliverableService } from '@modules/project/services/project-space-deliverable.service';
constructor(
private projectSpaceDeliverableService: ProjectSpaceDeliverableService
) {}
async loadProjectStats() {
const summary = await this.projectSpaceDeliverableService
.getProjectSpaceDeliverableSummary('project123');
console.log(`项目有 ${summary.totalSpaces} 个空间`);
console.log(`已上传交付物的空间:${summary.spacesWithDeliverables} 个`);
console.log(`总完成率:${summary.overallCompletionRate}%`);
// 遍历每个空间的详细信息
summary.spaces.forEach(space => {
console.log(`空间:${space.spaceName}`);
console.log(` 白模:${space.deliverableTypes.whiteModel} 个文件`);
console.log(` 软装:${space.deliverableTypes.softDecor} 个文件`);
console.log(` 渲染:${space.deliverableTypes.rendering} 个文件`);
console.log(` 后期:${space.deliverableTypes.postProcess} 个文件`);
console.log(` 完成率:${space.completionRate}%`);
});
}
async isAllSpacesDelivered(projectId: string): Promise<boolean>
用途:快速检查项目是否全部完成
示例:
const isCompleted = await this.projectSpaceDeliverableService
.isAllSpacesDelivered('project123');
if (isCompleted) {
console.log('✅ 项目所有空间都已完成交付');
} else {
console.log('⚠️ 还有空间未完成交付');
}
async getIncompleteSpaces(projectId: string): Promise<string[]>
用途:获取还未上传交付物的空间名称列表
示例:
const incompleteSpaces = await this.projectSpaceDeliverableService
.getIncompleteSpaces('project123');
if (incompleteSpaces.length > 0) {
console.log('未完成的空间:', incompleteSpaces.join(', '));
// 例如:未完成的空间:客厅, 主卧, 厨房
}
async getProjectDeliveryProgress(projectId: string): Promise<number>
用途:获取项目的整体完成进度(0-100)
示例:
const progress = await this.projectSpaceDeliverableService
.getProjectDeliveryProgress('project123');
console.log(`项目完成进度:${progress}%`);
// 在进度条中使用
this.progressWidth = `${progress}%`;
getDeliveryStatusLabel(completionRate: number): string
用途:根据完成率获取状态文本
返回值:
0%: "未开始"1-24%: "刚开始"25-49%: "进行中"50-74%: "接近完成"75-99%: "即将完成"100%: "已完成"示例:
const rate = 65;
const status = this.projectSpaceDeliverableService
.getDeliveryStatusLabel(rate);
console.log(status); // "接近完成"
getDeliveryStatusColor(completionRate: number): string
用途:根据完成率获取对应的颜色值
返回值:
0%: #94a3b8 (灰色)1-24%: #fbbf24 (黄色)25-49%: #fb923c (橙色)50-74%: #60a5fa (蓝色)75-99%: #818cf8 (紫色)100%: #34d399 (绿色)示例:
const rate = 75;
const color = this.projectSpaceDeliverableService
.getDeliveryStatusColor(rate);
// 在HTML中使用
<div [style.background-color]="color">
{{ rate }}%
</div>
formatSummaryText(summary: ProjectSpaceDeliverableSummary): string
用途:将统计摘要格式化为易读的文本
示例:
const summary = await this.projectSpaceDeliverableService
.getProjectSpaceDeliverableSummary('project123');
const text = this.projectSpaceDeliverableService
.formatSummaryText(summary);
console.log(text);
// 输出:
// 项目:某某项目
// 空间总数:3
// 已完成空间:2/3
// 总文件数:15
// - 白模:4
// - 软装:5
// - 渲染:4
// - 后期:2
// 完成率:67%
// project-timeline.ts
import { ProjectSpaceDeliverableService, ProjectSpaceDeliverableSummary }
from '@modules/project/services/project-space-deliverable.service';
@Component({
selector: 'app-project-timeline',
// ...
})
export class ProjectTimelineComponent implements OnInit {
spaceDeliverableCache: Map<string, ProjectSpaceDeliverableSummary> = new Map();
constructor(
private projectSpaceDeliverableService: ProjectSpaceDeliverableService
) {}
async ngOnInit() {
await this.loadProjectStats();
}
private async loadProjectStats() {
for (const project of this.projects) {
try {
const summary = await this.projectSpaceDeliverableService
.getProjectSpaceDeliverableSummary(project.projectId);
// 缓存统计数据
this.spaceDeliverableCache.set(project.projectId, summary);
console.log(`✅ 项目 ${project.projectName} 统计完成:`, {
空间数: summary.totalSpaces,
已完成空间: summary.spacesWithDeliverables,
完成率: `${summary.overallCompletionRate}%`
});
} catch (error) {
console.warn(`⚠️ 加载项目统计失败:`, error);
}
}
}
// 获取项目空间数量
getProjectSpaceCount(projectId: string): number {
const summary = this.spaceDeliverableCache.get(projectId);
return summary?.totalSpaces || 0;
}
// 获取项目交付完成率
getProjectDeliveryCompletionRate(projectId: string): number {
const summary = this.spaceDeliverableCache.get(projectId);
return summary?.overallCompletionRate || 0;
}
// 格式化工具提示
formatTooltip(projectId: string): string {
const summary = this.spaceDeliverableCache.get(projectId);
if (!summary) return '加载中...';
return `📦 空间与交付物统计\n\n` +
`空间总数: ${summary.totalSpaces}\n` +
`已完成空间: ${summary.spacesWithDeliverables}/${summary.totalSpaces}\n` +
`完成率: ${summary.overallCompletionRate}%`;
}
}
<!-- 显示空间统计徽章 -->
<div class="project-card">
<h3>{{ project.name }}</h3>
@if (getSpaceDeliverableSummary(project.id); as summary) {
<span class="badge"
[title]="formatTooltip(project.id)"
[style.background-color]="getProjectDeliveryStatusColor(project.id)">
📦 {{ summary.spacesWithDeliverables }}/{{ summary.totalSpaces }}
</span>
}
</div>
<!-- 显示进度条 -->
<div class="progress-bar">
<div class="progress-fill"
[style.width.%]="getProjectDeliveryCompletionRate(project.id)"
[style.background-color]="getProjectDeliveryStatusColor(project.id)">
{{ getProjectDeliveryCompletionRate(project.id) }}%
</div>
</div>
<!-- 显示详细统计 -->
<div class="stats-panel">
@if (getSpaceDeliverableSummary(project.id); as summary) {
<div class="stat-item">
<label>空间总数</label>
<span>{{ summary.totalSpaces }}</span>
</div>
<div class="stat-item">
<label>已完成空间</label>
<span>{{ summary.spacesWithDeliverables }}</span>
</div>
<div class="stat-item">
<label>总文件数</label>
<span>{{ summary.totalDeliverableFiles }}</span>
</div>
<div class="stat-item">
<label>白模</label>
<span>{{ summary.totalByType.whiteModel }}</span>
</div>
<div class="stat-item">
<label>软装</label>
<span>{{ summary.totalByType.softDecor }}</span>
</div>
<div class="stat-item">
<label>渲染</label>
<span>{{ summary.totalByType.rendering }}</span>
</div>
<div class="stat-item">
<label>后期</label>
<span>{{ summary.totalByType.postProcess }}</span>
</div>
}
</div>
// 缓存统计数据,避免重复查询
private cache: Map<string, ProjectSpaceDeliverableSummary> = new Map();
async getStats(projectId: string) {
// 检查缓存
if (this.cache.has(projectId)) {
return this.cache.get(projectId)!;
}
// 加载并缓存
const summary = await this.projectSpaceDeliverableService
.getProjectSpaceDeliverableSummary(projectId);
this.cache.set(projectId, summary);
return summary;
}
// 异步加载,不阻塞主流程
private async loadStatsInBackground() {
for (const project of this.projects) {
// 使用 setTimeout 避免阻塞UI
setTimeout(async () => {
const summary = await this.projectSpaceDeliverableService
.getProjectSpaceDeliverableSummary(project.id);
this.updateProjectStats(project.id, summary);
}, 0);
}
}
// 并行加载多个项目的统计数据
private async loadStatsInBatch(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.cache.set(projectIds[index], result.value);
} else {
console.warn(`加载项目 ${projectIds[index]} 统计失败:`, result.reason);
}
});
}
delivery_* 类型的文件(白模、软装、渲染、后期)await 或 .then()请参考以下文件中的实际应用:
src/app/pages/team-leader/project-timeline/project-timeline.tssrc/modules/project/pages/project-detail/stages/stage-delivery.component.ts如有问题,请联系开发团队或查看源码注释。