|
@@ -1,4 +1,4 @@
|
|
|
-import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';
|
|
|
+import { Component, Input, Output, EventEmitter, OnInit, OnDestroy, ChangeDetectorRef, ChangeDetectionStrategy } from '@angular/core';
|
|
|
import { CommonModule } from '@angular/common';
|
|
|
import { FormsModule, ReactiveFormsModule, FormBuilder, FormGroup, Validators } from '@angular/forms';
|
|
|
|
|
@@ -75,9 +75,10 @@ interface RequirementItem {
|
|
|
standalone: true,
|
|
|
imports: [CommonModule, FormsModule, ReactiveFormsModule],
|
|
|
templateUrl: './requirements-confirm-card.html',
|
|
|
- styleUrls: ['./requirements-confirm-card.scss']
|
|
|
+ styleUrls: ['./requirements-confirm-card.scss'],
|
|
|
+ changeDetection: ChangeDetectionStrategy.Default // 确保使用默认变更检测策略
|
|
|
})
|
|
|
-export class RequirementsConfirmCardComponent implements OnInit {
|
|
|
+export class RequirementsConfirmCardComponent implements OnInit, OnDestroy {
|
|
|
@Input() projectId?: string;
|
|
|
@Output() requirementConfirmed = new EventEmitter<RequirementItem>();
|
|
|
@Output() progressUpdated = new EventEmitter<number>();
|
|
@@ -102,6 +103,24 @@ export class RequirementsConfirmCardComponent implements OnInit {
|
|
|
showConsistencyWarning = false;
|
|
|
warningMessage = '';
|
|
|
|
|
|
+ // 自动保存相关状态
|
|
|
+ autoSaveEnabled = true;
|
|
|
+ lastSaveTime?: Date;
|
|
|
+ hasUnsavedChanges = false;
|
|
|
+ isSaving = false;
|
|
|
+ saveStatus: 'saved' | 'saving' | 'error' | 'unsaved' = 'saved';
|
|
|
+
|
|
|
+ // 流程状态
|
|
|
+ stageCompletionStatus = {
|
|
|
+ materialAnalysis: false,
|
|
|
+ requirementMapping: false,
|
|
|
+ collaboration: false,
|
|
|
+ progressReview: false
|
|
|
+ };
|
|
|
+
|
|
|
+ // 自动保存定时器
|
|
|
+ private autoSaveTimer?: any;
|
|
|
+
|
|
|
// 需求指标
|
|
|
colorIndicators = {
|
|
|
mainColor: { r: 255, g: 230, b: 180 },
|
|
@@ -170,12 +189,27 @@ export class RequirementsConfirmCardComponent implements OnInit {
|
|
|
consistencyWarnings: string[] = [];
|
|
|
historyStates: any[] = [];
|
|
|
|
|
|
- constructor(private fb: FormBuilder) {}
|
|
|
+ constructor(private fb: FormBuilder, private cdr: ChangeDetectorRef) {}
|
|
|
|
|
|
ngOnInit() {
|
|
|
this.initializeForms();
|
|
|
this.initializeRequirements();
|
|
|
this.loadPresetMetrics();
|
|
|
+
|
|
|
+ // 启用自动保存
|
|
|
+ this.autoSaveEnabled = true;
|
|
|
+
|
|
|
+ // 定期检查阶段完成状态
|
|
|
+ setInterval(() => {
|
|
|
+ this.checkStageCompletion();
|
|
|
+ }, 5000);
|
|
|
+ }
|
|
|
+
|
|
|
+ ngOnDestroy(): void {
|
|
|
+ // 清理自动保存定时器
|
|
|
+ if (this.autoSaveTimer) {
|
|
|
+ clearTimeout(this.autoSaveTimer);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
private initializeForms() {
|
|
@@ -436,16 +470,32 @@ export class RequirementsConfirmCardComponent implements OnInit {
|
|
|
|
|
|
// 需求确认功能
|
|
|
confirmRequirement(requirementId: string) {
|
|
|
+ // 检查是否可以进行需求确认操作
|
|
|
+ if (!this.canProceedToNextStage('materialAnalysis')) {
|
|
|
+ alert('请先完成素材分析阶段的所有必要操作');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
const requirement = this.requirementItems.find(r => r.id === requirementId);
|
|
|
if (requirement) {
|
|
|
requirement.status = 'confirmed';
|
|
|
requirement.lastUpdated = new Date();
|
|
|
this.requirementConfirmed.emit(requirement);
|
|
|
this.updateProgress();
|
|
|
+ this.triggerAutoSave();
|
|
|
+
|
|
|
+ // 检查阶段完成状态
|
|
|
+ this.checkStageCompletion();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
rejectRequirement(requirementId: string, reason?: string) {
|
|
|
+ // 检查是否可以进行需求拒绝操作
|
|
|
+ if (!this.canProceedToNextStage('materialAnalysis')) {
|
|
|
+ alert('请先完成素材分析阶段的所有必要操作');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
const requirement = this.requirementItems.find(r => r.id === requirementId);
|
|
|
if (requirement) {
|
|
|
requirement.status = 'rejected';
|
|
@@ -456,6 +506,10 @@ export class RequirementsConfirmCardComponent implements OnInit {
|
|
|
}
|
|
|
|
|
|
this.updateProgress();
|
|
|
+ this.triggerAutoSave();
|
|
|
+
|
|
|
+ // 检查阶段完成状态
|
|
|
+ this.checkStageCompletion();
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -474,6 +528,10 @@ export class RequirementsConfirmCardComponent implements OnInit {
|
|
|
|
|
|
this.collaborationComments.push(comment);
|
|
|
this.commentForm.reset();
|
|
|
+ this.triggerAutoSave();
|
|
|
+
|
|
|
+ // 检查阶段完成状态
|
|
|
+ this.checkStageCompletion();
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -481,6 +539,10 @@ export class RequirementsConfirmCardComponent implements OnInit {
|
|
|
const comment = this.collaborationComments.find(c => c.id === commentId);
|
|
|
if (comment) {
|
|
|
comment.status = 'resolved';
|
|
|
+ this.triggerAutoSave();
|
|
|
+
|
|
|
+ // 检查阶段完成状态
|
|
|
+ this.checkStageCompletion();
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -489,7 +551,149 @@ export class RequirementsConfirmCardComponent implements OnInit {
|
|
|
const totalRequirements = this.requirementItems.length;
|
|
|
const confirmedRequirements = this.requirementItems.filter(r => r.status === 'confirmed').length;
|
|
|
const progress = totalRequirements > 0 ? (confirmedRequirements / totalRequirements) * 100 : 0;
|
|
|
+
|
|
|
+ // 实时更新进度条
|
|
|
+ this.updateProgressBar(progress);
|
|
|
this.progressUpdated.emit(progress);
|
|
|
+
|
|
|
+ // 检查是否完成所有需求
|
|
|
+ if (progress === 100) {
|
|
|
+ this.onRequirementCommunicationComplete();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private updateProgressBar(progress: number): void {
|
|
|
+ // 更新进度条显示
|
|
|
+ const progressBar = document.querySelector('.progress-bar-fill') as HTMLElement;
|
|
|
+ if (progressBar) {
|
|
|
+ progressBar.style.width = `${progress}%`;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新进度文本
|
|
|
+ const progressText = document.querySelector('.progress-text') as HTMLElement;
|
|
|
+ if (progressText) {
|
|
|
+ progressText.textContent = `${Math.round(progress)}%`;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 添加进度动画效果
|
|
|
+ this.animateProgressUpdate(progress);
|
|
|
+ }
|
|
|
+
|
|
|
+ private animateProgressUpdate(progress: number): void {
|
|
|
+ // 添加进度更新的视觉反馈
|
|
|
+ const progressContainer = document.querySelector('.progress-container') as HTMLElement;
|
|
|
+ if (progressContainer) {
|
|
|
+ progressContainer.classList.add('progress-updated');
|
|
|
+ setTimeout(() => {
|
|
|
+ progressContainer.classList.remove('progress-updated');
|
|
|
+ }, 500);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 需求沟通阶段完成处理
|
|
|
+ private onRequirementCommunicationComplete(): void {
|
|
|
+ console.log('需求沟通阶段完成,开始同步关键信息');
|
|
|
+ this.syncKeyInfoToSolutionConfirmation();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 同步关键信息到方案确认阶段
|
|
|
+ private syncKeyInfoToSolutionConfirmation(): void {
|
|
|
+ const keyInfo = {
|
|
|
+ colorAtmosphere: {
|
|
|
+ mainColor: this.colorIndicators.mainColor,
|
|
|
+ colorTemperature: this.colorIndicators.colorTemperature,
|
|
|
+ saturation: this.colorIndicators.saturation,
|
|
|
+ brightness: this.colorIndicators.brightness
|
|
|
+ },
|
|
|
+ spaceStructure: {
|
|
|
+ lineRatio: this.spaceIndicators.lineRatio,
|
|
|
+ blankRatio: this.spaceIndicators.blankRatio,
|
|
|
+ flowWidth: this.spaceIndicators.flowWidth
|
|
|
+ },
|
|
|
+ materialWeights: {
|
|
|
+ fabricRatio: this.materialIndicators.fabricRatio,
|
|
|
+ woodRatio: this.materialIndicators.woodRatio,
|
|
|
+ metalRatio: this.materialIndicators.metalRatio,
|
|
|
+ smoothness: this.materialIndicators.smoothness
|
|
|
+ },
|
|
|
+ presetAtmosphere: this.getSelectedPresetAtmosphere()
|
|
|
+ };
|
|
|
+
|
|
|
+ // 触发同步事件
|
|
|
+ this.requirementConfirmed.emit({
|
|
|
+ id: 'requirement-sync',
|
|
|
+ title: '需求沟通完成',
|
|
|
+ description: '关键信息已同步至方案确认阶段',
|
|
|
+ priority: 'high',
|
|
|
+ status: 'confirmed',
|
|
|
+ lastUpdated: new Date(),
|
|
|
+ metrics: this.convertToMetrics(keyInfo)
|
|
|
+ });
|
|
|
+
|
|
|
+ // 启动交付执行流程
|
|
|
+ setTimeout(() => {
|
|
|
+ this.startDeliveryExecution();
|
|
|
+ }, 1000);
|
|
|
+ }
|
|
|
+
|
|
|
+ private getSelectedPresetAtmosphere(): any {
|
|
|
+ // 根据当前颜色指标找到最匹配的预设氛围
|
|
|
+ const currentRgb = `${this.colorIndicators.mainColor.r},${this.colorIndicators.mainColor.g},${this.colorIndicators.mainColor.b}`;
|
|
|
+ return this.presetAtmospheres.find(preset => preset.rgb === currentRgb) || this.presetAtmospheres[0];
|
|
|
+ }
|
|
|
+
|
|
|
+ private convertToMetrics(keyInfo: any): RequirementMetric[] {
|
|
|
+ return [
|
|
|
+ {
|
|
|
+ id: 'color-sync',
|
|
|
+ category: 'color',
|
|
|
+ name: '色彩氛围',
|
|
|
+ value: keyInfo.colorAtmosphere
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'space-sync',
|
|
|
+ category: 'space',
|
|
|
+ name: '空间结构',
|
|
|
+ value: keyInfo.spaceStructure
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'material-sync',
|
|
|
+ category: 'material',
|
|
|
+ name: '材质权重',
|
|
|
+ value: keyInfo.materialWeights
|
|
|
+ }
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 启动交付执行流程
|
|
|
+ private startDeliveryExecution(): void {
|
|
|
+ console.log('启动交付执行流程');
|
|
|
+
|
|
|
+ // 显示执行启动提示
|
|
|
+ this.showExecutionStartNotification();
|
|
|
+
|
|
|
+ // 这里可以触发路由跳转或其他业务逻辑
|
|
|
+ // 例如:this.router.navigate(['/project', this.projectId, 'execution']);
|
|
|
+ }
|
|
|
+
|
|
|
+ private showExecutionStartNotification(): void {
|
|
|
+ // 显示执行流程启动的通知
|
|
|
+ const notification = document.createElement('div');
|
|
|
+ notification.className = 'execution-notification';
|
|
|
+ notification.innerHTML = `
|
|
|
+ <div class="notification-content">
|
|
|
+ <svg viewBox="0 0 24 24" fill="currentColor">
|
|
|
+ <path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/>
|
|
|
+ </svg>
|
|
|
+ <span>交付执行流程已启动</span>
|
|
|
+ </div>
|
|
|
+ `;
|
|
|
+
|
|
|
+ document.body.appendChild(notification);
|
|
|
+
|
|
|
+ setTimeout(() => {
|
|
|
+ notification.remove();
|
|
|
+ }, 3000);
|
|
|
}
|
|
|
|
|
|
getProgressPercentage(): number {
|
|
@@ -541,12 +745,154 @@ export class RequirementsConfirmCardComponent implements OnInit {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // 滑动条数值同步方法
|
|
|
+ onSliderChange(key: string, value: number): void {
|
|
|
+ console.log(`滑动条变化: ${key} = ${value}`);
|
|
|
+ console.log('变化前的mainColor:', JSON.stringify(this.colorIndicators.mainColor));
|
|
|
+
|
|
|
+ // 直接更新数据,不需要额外的updateIndicatorValue调用
|
|
|
+ // 因为使用了双向绑定,数据已经自动更新
|
|
|
+ if (key === 'r' || key === 'g' || key === 'b') {
|
|
|
+ // 确保RGB值在有效范围内
|
|
|
+ const range = this.getIndicatorRange(key);
|
|
|
+ if (range) {
|
|
|
+ const clampedValue = Math.max(range.min, Math.min(range.max, value));
|
|
|
+ this.colorIndicators.mainColor[key as keyof typeof this.colorIndicators.mainColor] = clampedValue;
|
|
|
+ console.log(`RGB值已更新: ${key} = ${clampedValue}`);
|
|
|
+ console.log('变化后的mainColor:', JSON.stringify(this.colorIndicators.mainColor));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 触发颜色指标更新
|
|
|
+ this.updateColorIndicator('mainColor', this.colorIndicators.mainColor);
|
|
|
+ } else {
|
|
|
+ // 处理其他颜色指标
|
|
|
+ this.updateIndicatorValue(key, value);
|
|
|
+ }
|
|
|
+
|
|
|
+ this.triggerAutoSave(); // 触发自动保存
|
|
|
+ this.checkStageCompletion(); // 检查阶段完成状态
|
|
|
+
|
|
|
+ // 强制触发变更检测
|
|
|
+ this.cdr.detectChanges();
|
|
|
+ console.log('滑动条变更检测已触发');
|
|
|
+ }
|
|
|
+
|
|
|
+ onInputChange(key: string, value: number): void {
|
|
|
+ console.log(`输入框变化: ${key} = ${value}`);
|
|
|
+ console.log('变化前的mainColor:', JSON.stringify(this.colorIndicators.mainColor));
|
|
|
+
|
|
|
+ // 直接更新数据,不需要额外的updateIndicatorValue调用
|
|
|
+ // 因为使用了双向绑定,数据已经自动更新
|
|
|
+ if (key === 'r' || key === 'g' || key === 'b') {
|
|
|
+ // 确保RGB值在有效范围内
|
|
|
+ const range = this.getIndicatorRange(key);
|
|
|
+ if (range) {
|
|
|
+ const clampedValue = Math.max(range.min, Math.min(range.max, value));
|
|
|
+ this.colorIndicators.mainColor[key as keyof typeof this.colorIndicators.mainColor] = clampedValue;
|
|
|
+ console.log(`RGB值已更新: ${key} = ${clampedValue}`);
|
|
|
+ console.log('变化后的mainColor:', JSON.stringify(this.colorIndicators.mainColor));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 触发颜色指标更新
|
|
|
+ this.updateColorIndicator('mainColor', this.colorIndicators.mainColor);
|
|
|
+ } else {
|
|
|
+ // 处理其他颜色指标
|
|
|
+ this.updateIndicatorValue(key, value);
|
|
|
+ }
|
|
|
+
|
|
|
+ this.triggerAutoSave();
|
|
|
+
|
|
|
+ // 强制触发变更检测
|
|
|
+ this.cdr.detectChanges();
|
|
|
+ console.log('输入框变更检测已触发');
|
|
|
+ }
|
|
|
+
|
|
|
+ validateInput(key: string, value: string): void {
|
|
|
+ const numValue = parseFloat(value);
|
|
|
+ if (isNaN(numValue)) {
|
|
|
+ // 如果输入无效,恢复原值
|
|
|
+ this.restoreIndicatorValue(key);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const range = this.getIndicatorRange(key);
|
|
|
+ if (range) {
|
|
|
+ const clampedValue = Math.max(range.min, Math.min(range.max, numValue));
|
|
|
+ this.updateIndicatorValue(key, clampedValue);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private updateIndicatorValue(key: string, value: number): void {
|
|
|
+ console.log(`updateIndicatorValue调用: ${key} = ${value}`);
|
|
|
+
|
|
|
+ // 更新颜色指标
|
|
|
+ if (key in this.colorIndicators) {
|
|
|
+ if (key === 'r' || key === 'g' || key === 'b') {
|
|
|
+ // 创建新的mainColor对象以确保变更检测
|
|
|
+ const oldColor = { ...this.colorIndicators.mainColor };
|
|
|
+ this.colorIndicators.mainColor = {
|
|
|
+ ...this.colorIndicators.mainColor,
|
|
|
+ [key]: value
|
|
|
+ };
|
|
|
+ console.log(`RGB更新: ${key}从${oldColor[key as keyof typeof oldColor]}变为${value}`,
|
|
|
+ '新mainColor:', this.colorIndicators.mainColor);
|
|
|
+ this.updateColorIndicator('mainColor', this.colorIndicators.mainColor);
|
|
|
+ } else {
|
|
|
+ (this.colorIndicators as any)[key] = value;
|
|
|
+ this.updateColorIndicator(key, value);
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新空间指标
|
|
|
+ if (key in this.spaceIndicators) {
|
|
|
+ (this.spaceIndicators as any)[key] = value;
|
|
|
+ this.updateSpaceIndicator(key, value);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新材质指标
|
|
|
+ if (key in this.materialIndicators) {
|
|
|
+ (this.materialIndicators as any)[key] = value;
|
|
|
+ this.updateMaterialIndicator(key, value);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private restoreIndicatorValue(key: string): void {
|
|
|
+ // 这里可以从历史状态或默认值恢复
|
|
|
+ console.log(`恢复指标值: ${key}`);
|
|
|
+ }
|
|
|
+
|
|
|
+ private getIndicatorRange(key: string): { min: number; max: number } | null {
|
|
|
+ // 检查颜色范围
|
|
|
+ if (key === 'r' || key === 'g' || key === 'b') {
|
|
|
+ return this.indicatorRanges.color[key as keyof typeof this.indicatorRanges.color];
|
|
|
+ }
|
|
|
+ if (key in this.indicatorRanges.color) {
|
|
|
+ return (this.indicatorRanges.color as any)[key];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查空间范围
|
|
|
+ if (key in this.indicatorRanges.space) {
|
|
|
+ return (this.indicatorRanges.space as any)[key];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查材质范围
|
|
|
+ if (key in this.indicatorRanges.material) {
|
|
|
+ return (this.indicatorRanges.material as any)[key];
|
|
|
+ }
|
|
|
+
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
// 指标更新方法
|
|
|
updateColorIndicator(type: string, value?: any): void {
|
|
|
switch (type) {
|
|
|
case 'mainColor':
|
|
|
if (value && typeof value === 'object' && 'r' in value) {
|
|
|
- this.colorIndicators.mainColor = value;
|
|
|
+ this.colorIndicators.mainColor = { ...value }; // 创建新对象以触发变更检测
|
|
|
+ console.log('mainColor更新后:', this.colorIndicators.mainColor);
|
|
|
}
|
|
|
break;
|
|
|
case 'colorTemperature':
|
|
@@ -557,9 +903,12 @@ export class RequirementsConfirmCardComponent implements OnInit {
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
- console.log(`更新颜色指示器 ${type}:`, value);
|
|
|
+ console.log(`更新颜色指示器 ${type}:`, value, '当前RGB:', this.getRgbString());
|
|
|
this.checkConsistency();
|
|
|
this.updatePreview();
|
|
|
+ // 强制触发变更检测
|
|
|
+ this.cdr.detectChanges();
|
|
|
+ console.log('变更检测已触发');
|
|
|
}
|
|
|
|
|
|
updateSpaceIndicator(key: string, value: number) {
|
|
@@ -593,17 +942,17 @@ export class RequirementsConfirmCardComponent implements OnInit {
|
|
|
|
|
|
// 预览更新
|
|
|
private updatePreview() {
|
|
|
- const { r, g, b } = this.colorIndicators.mainColor;
|
|
|
- const previewElement = document.querySelector('.color-preview') as HTMLElement;
|
|
|
- if (previewElement) {
|
|
|
- previewElement.style.backgroundColor = `rgb(${r}, ${g}, ${b})`;
|
|
|
- }
|
|
|
+ // 移除直接DOM操作,依赖Angular的属性绑定
|
|
|
+ console.log('updatePreview调用,当前RGB:', this.getRgbString());
|
|
|
+ // 不再直接操作DOM,让Angular的变更检测处理
|
|
|
}
|
|
|
|
|
|
// 获取RGB字符串
|
|
|
getRgbString(): string {
|
|
|
const { r, g, b } = this.colorIndicators.mainColor;
|
|
|
- return `rgb(${r}, ${g}, ${b})`;
|
|
|
+ const rgbString = `rgb(${r}, ${g}, ${b})`;
|
|
|
+ console.log('getRgbString调用:', rgbString, '完整mainColor对象:', this.colorIndicators.mainColor);
|
|
|
+ return rgbString;
|
|
|
}
|
|
|
|
|
|
// 获取色温描述
|
|
@@ -796,4 +1145,253 @@ export class RequirementsConfirmCardComponent implements OnInit {
|
|
|
};
|
|
|
return priorityMap[priority] || priority;
|
|
|
}
|
|
|
+
|
|
|
+ // 刷新进度(保留用于手动刷新,但不再强制要求)
|
|
|
+ refreshProgress(): void {
|
|
|
+ // 重新计算进度
|
|
|
+ this.updateProgress();
|
|
|
+
|
|
|
+ // 更新进度条显示
|
|
|
+ const progress = this.getProgressPercentage();
|
|
|
+ this.updateProgressBar(progress);
|
|
|
+
|
|
|
+ // 检查阶段完成状态
|
|
|
+ this.checkStageCompletion();
|
|
|
+
|
|
|
+ // 触发进度更新事件
|
|
|
+ this.progressUpdated.emit(progress);
|
|
|
+
|
|
|
+ console.log('进度已手动刷新:', progress + '%');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 自动保存功能
|
|
|
+ private triggerAutoSave(): void {
|
|
|
+ if (!this.autoSaveEnabled) return;
|
|
|
+
|
|
|
+ this.hasUnsavedChanges = true;
|
|
|
+ this.saveStatus = 'unsaved';
|
|
|
+
|
|
|
+ // 清除之前的定时器
|
|
|
+ if (this.autoSaveTimer) {
|
|
|
+ clearTimeout(this.autoSaveTimer);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置新的自动保存定时器(延迟1秒)
|
|
|
+ this.autoSaveTimer = setTimeout(() => {
|
|
|
+ this.performAutoSave();
|
|
|
+ }, 1000);
|
|
|
+ }
|
|
|
+
|
|
|
+ private async performAutoSave(): Promise<void> {
|
|
|
+ if (this.isSaving) return;
|
|
|
+
|
|
|
+ this.isSaving = true;
|
|
|
+ this.saveStatus = 'saving';
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 收集所有需要保存的数据
|
|
|
+ const saveData = this.collectSaveData();
|
|
|
+
|
|
|
+ // 模拟保存操作(实际项目中这里会调用API)
|
|
|
+ await this.saveToServer(saveData);
|
|
|
+
|
|
|
+ this.hasUnsavedChanges = false;
|
|
|
+ this.saveStatus = 'saved';
|
|
|
+ this.lastSaveTime = new Date();
|
|
|
+
|
|
|
+ console.log('自动保存成功:', new Date().toLocaleTimeString());
|
|
|
+ } catch (error) {
|
|
|
+ this.saveStatus = 'error';
|
|
|
+ console.error('自动保存失败:', error);
|
|
|
+ } finally {
|
|
|
+ this.isSaving = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 手动保存
|
|
|
+ async manualSave(): Promise<void> {
|
|
|
+ if (this.isSaving) return;
|
|
|
+
|
|
|
+ // 清除自动保存定时器
|
|
|
+ if (this.autoSaveTimer) {
|
|
|
+ clearTimeout(this.autoSaveTimer);
|
|
|
+ }
|
|
|
+
|
|
|
+ await this.performAutoSave();
|
|
|
+ }
|
|
|
+
|
|
|
+ private collectSaveData(): any {
|
|
|
+ return {
|
|
|
+ projectId: this.projectId,
|
|
|
+ colorIndicators: this.colorIndicators,
|
|
|
+ spaceIndicators: this.spaceIndicators,
|
|
|
+ materialIndicators: this.materialIndicators,
|
|
|
+ requirementItems: this.requirementItems,
|
|
|
+ requirementMetrics: this.requirementMetrics,
|
|
|
+ materialFiles: this.materialFiles,
|
|
|
+ collaborationComments: this.collaborationComments,
|
|
|
+ stageCompletionStatus: this.stageCompletionStatus,
|
|
|
+ lastUpdated: new Date()
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ private async saveToServer(data: any): Promise<void> {
|
|
|
+ // 模拟API调用延迟
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ setTimeout(() => {
|
|
|
+ // 模拟90%成功率
|
|
|
+ if (Math.random() > 0.1) {
|
|
|
+ resolve();
|
|
|
+ } else {
|
|
|
+ reject(new Error('网络错误'));
|
|
|
+ }
|
|
|
+ }, 500);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查阶段完成状态
|
|
|
+ private checkStageCompletion(): void {
|
|
|
+ // 检查素材分析阶段 - 需要有素材文件且都已分析
|
|
|
+ this.stageCompletionStatus.materialAnalysis = this.materialFiles.length > 0 &&
|
|
|
+ this.materialFiles.every(m => m.analysis);
|
|
|
+
|
|
|
+ // 检查需求映射阶段 - 需要有确认的需求项,或者滑动条数据已调整且保存
|
|
|
+ const hasConfirmedRequirements = this.requirementItems.length > 0 &&
|
|
|
+ this.requirementItems.some(r => r.status === 'confirmed');
|
|
|
+ const hasAdjustedIndicators = this.hasIndicatorChanges();
|
|
|
+ this.stageCompletionStatus.requirementMapping = hasConfirmedRequirements ||
|
|
|
+ (hasAdjustedIndicators && this.saveStatus === 'saved');
|
|
|
+
|
|
|
+ // 检查协作验证阶段 - 需要有协作评论或需求评论
|
|
|
+ this.stageCompletionStatus.collaboration = this.collaborationComments.length > 0 ||
|
|
|
+ this.requirementItems.some(r => r.comments && r.comments.length > 0);
|
|
|
+
|
|
|
+ // 检查进度审查阶段 - 需要进度达到80%以上
|
|
|
+ this.stageCompletionStatus.progressReview = this.getProgressPercentage() >= 80;
|
|
|
+
|
|
|
+ console.log('阶段完成状态更新:', this.stageCompletionStatus);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查指示器是否有变化
|
|
|
+ private hasIndicatorChanges(): boolean {
|
|
|
+ // 检查颜色指示器是否有变化(与默认值比较)
|
|
|
+ const defaultColorIndicators = {
|
|
|
+ mainColor: { r: 255, g: 230, b: 180 },
|
|
|
+ colorTemperature: 2700,
|
|
|
+ colorRange: '暖色调',
|
|
|
+ saturation: 70,
|
|
|
+ brightness: 80,
|
|
|
+ contrast: 60
|
|
|
+ };
|
|
|
+
|
|
|
+ const defaultSpaceIndicators = {
|
|
|
+ lineRatio: 60,
|
|
|
+ blankRatio: 30,
|
|
|
+ flowWidth: 0.9,
|
|
|
+ aspectRatio: 1.6,
|
|
|
+ ceilingHeight: 2.8,
|
|
|
+ lightingLevel: 300
|
|
|
+ };
|
|
|
+
|
|
|
+ const defaultMaterialIndicators = {
|
|
|
+ fabricRatio: 50,
|
|
|
+ woodRatio: 30,
|
|
|
+ metalRatio: 20,
|
|
|
+ smoothness: 7,
|
|
|
+ glossiness: 4,
|
|
|
+ texture: 6
|
|
|
+ };
|
|
|
+
|
|
|
+ // 检查颜色指示器变化
|
|
|
+ const colorChanged = JSON.stringify(this.colorIndicators) !== JSON.stringify(defaultColorIndicators);
|
|
|
+
|
|
|
+ // 检查空间指示器变化
|
|
|
+ const spaceChanged = JSON.stringify(this.spaceIndicators) !== JSON.stringify(defaultSpaceIndicators);
|
|
|
+
|
|
|
+ // 检查材质指示器变化
|
|
|
+ const materialChanged = JSON.stringify(this.materialIndicators) !== JSON.stringify(defaultMaterialIndicators);
|
|
|
+
|
|
|
+ return colorChanged || spaceChanged || materialChanged;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取阶段状态文本
|
|
|
+ getStageStatusText(stage: keyof typeof this.stageCompletionStatus): string {
|
|
|
+ if (this.stageCompletionStatus[stage]) {
|
|
|
+ return '已完成';
|
|
|
+ } else {
|
|
|
+ // 检查是否为未开始状态
|
|
|
+ const stages = ['materialAnalysis', 'requirementMapping', 'collaboration', 'progressReview'];
|
|
|
+ const currentIndex = stages.indexOf(stage);
|
|
|
+
|
|
|
+ // 检查前面的阶段是否都已完成
|
|
|
+ let allPreviousCompleted = true;
|
|
|
+ for (let i = 0; i < currentIndex; i++) {
|
|
|
+ if (!this.stageCompletionStatus[stages[i] as keyof typeof this.stageCompletionStatus]) {
|
|
|
+ allPreviousCompleted = false;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return allPreviousCompleted ? '进行中' : '未进行';
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取阶段状态类名
|
|
|
+ getStageStatusClass(stage: keyof typeof this.stageCompletionStatus): string {
|
|
|
+ if (this.stageCompletionStatus[stage]) {
|
|
|
+ return 'stage-completed';
|
|
|
+ } else {
|
|
|
+ // 检查是否为未开始状态
|
|
|
+ const stages = ['materialAnalysis', 'requirementMapping', 'collaboration', 'progressReview'];
|
|
|
+ const currentIndex = stages.indexOf(stage);
|
|
|
+
|
|
|
+ // 检查前面的阶段是否都已完成
|
|
|
+ let allPreviousCompleted = true;
|
|
|
+ for (let i = 0; i < currentIndex; i++) {
|
|
|
+ if (!this.stageCompletionStatus[stages[i] as keyof typeof this.stageCompletionStatus]) {
|
|
|
+ allPreviousCompleted = false;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return allPreviousCompleted ? 'stage-in-progress' : 'stage-pending';
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查是否可以进入下一阶段
|
|
|
+ canProceedToNextStage(currentStage: keyof typeof this.stageCompletionStatus): boolean {
|
|
|
+ const stages = Object.keys(this.stageCompletionStatus) as (keyof typeof this.stageCompletionStatus)[];
|
|
|
+ const currentIndex = stages.indexOf(currentStage);
|
|
|
+
|
|
|
+ // 检查当前阶段之前的所有阶段是否都已完成
|
|
|
+ for (let i = 0; i <= currentIndex; i++) {
|
|
|
+ if (!this.stageCompletionStatus[stages[i]]) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取保存状态文本
|
|
|
+ getSaveStatusText(): string {
|
|
|
+ switch (this.saveStatus) {
|
|
|
+ case 'saved': return this.lastSaveTime ? `已保存 ${this.lastSaveTime.toLocaleTimeString()}` : '已保存';
|
|
|
+ case 'saving': return '保存中...';
|
|
|
+ case 'error': return '保存失败';
|
|
|
+ case 'unsaved': return '有未保存的更改';
|
|
|
+ default: return '';
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取保存状态图标
|
|
|
+ getSaveStatusIcon(): string {
|
|
|
+ switch (this.saveStatus) {
|
|
|
+ case 'saved': return '✓';
|
|
|
+ case 'saving': return '⟳';
|
|
|
+ case 'error': return '⚠';
|
|
|
+ case 'unsaved': return '●';
|
|
|
+ default: return '';
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|