|
@@ -3,9 +3,10 @@ import { CommonModule } from '@angular/common';
|
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
|
import { ActivatedRoute } from '@angular/router';
|
|
import { ActivatedRoute } from '@angular/router';
|
|
import { IonIcon } from '@ionic/angular/standalone';
|
|
import { IonIcon } from '@ionic/angular/standalone';
|
|
|
|
+import { ProductSpaceService, Project } from '../../../services/product-space.service';
|
|
|
|
|
|
/**
|
|
/**
|
|
- * 确认需求阶段组件 - 多空间支持
|
|
|
|
|
|
+ * 确认需求阶段组件 - Product表统一空间管理
|
|
*/
|
|
*/
|
|
@Component({
|
|
@Component({
|
|
selector: 'app-stage-requirements',
|
|
selector: 'app-stage-requirements',
|
|
@@ -25,11 +26,11 @@ export class StageRequirementsComponent implements OnInit {
|
|
cid: string = '';
|
|
cid: string = '';
|
|
projectId: string = '';
|
|
projectId: string = '';
|
|
|
|
|
|
- // 多空间管理
|
|
|
|
- projectSpaces: any[] = [];
|
|
|
|
- isMultiSpaceProject: boolean = false;
|
|
|
|
- activeSpaceId: string = '';
|
|
|
|
- selectedSpaceIds: string[] = [];
|
|
|
|
|
|
+ // Product-based 空间管理
|
|
|
|
+ projectProducts: Project[] = [];
|
|
|
|
+ isMultiProductProject: boolean = false;
|
|
|
|
+ activeProductId: string = '';
|
|
|
|
+ selectedProductIds: string[] = [];
|
|
|
|
|
|
// 需求分段管理
|
|
// 需求分段管理
|
|
requirementsSegment: string = 'global'; // global | spaces | cross-space
|
|
requirementsSegment: string = 'global'; // global | spaces | cross-space
|
|
@@ -127,7 +128,8 @@ export class StageRequirementsComponent implements OnInit {
|
|
|
|
|
|
constructor(
|
|
constructor(
|
|
private route: ActivatedRoute,
|
|
private route: ActivatedRoute,
|
|
- private cdr: ChangeDetectorRef
|
|
|
|
|
|
+ private cdr: ChangeDetectorRef,
|
|
|
|
+ private productSpaceService: ProductSpaceService
|
|
) {}
|
|
) {}
|
|
|
|
|
|
async ngOnInit() {
|
|
async ngOnInit() {
|
|
@@ -148,17 +150,19 @@ export class StageRequirementsComponent implements OnInit {
|
|
try {
|
|
try {
|
|
this.loading = true;
|
|
this.loading = true;
|
|
|
|
|
|
- // 模拟加载项目空间数据
|
|
|
|
- this.projectSpaces = [
|
|
|
|
- { id: '1', name: '客厅', type: 'living_room', area: 25, priority: 5, status: 'not_started', complexity: 'medium', order: 0 },
|
|
|
|
- { id: '2', name: '主卧', type: 'bedroom', area: 18, priority: 4, status: 'not_started', complexity: 'medium', order: 1 },
|
|
|
|
- { id: '3', name: '厨房', type: 'kitchen', area: 12, priority: 5, status: 'not_started', complexity: 'high', order: 2 }
|
|
|
|
- ];
|
|
|
|
- this.isMultiSpaceProject = this.projectSpaces.length > 1;
|
|
|
|
|
|
+ // 从项目ID加载Product数据
|
|
|
|
+ if (this.projectId) {
|
|
|
|
+ this.projectProducts = await this.productSpaceService.getProjectProductSpaces(this.projectId);
|
|
|
|
+ } else if (this.project) {
|
|
|
|
+ // 如果有project对象,使用其ID
|
|
|
|
+ this.projectProducts = await this.productSpaceService.getProjectProductSpaces(this.project.id);
|
|
|
|
+ }
|
|
|
|
|
|
- // 如果有空间,默认选中第一个
|
|
|
|
- if (this.projectSpaces.length > 0 && !this.activeSpaceId) {
|
|
|
|
- this.activeSpaceId = this.projectSpaces[0].id;
|
|
|
|
|
|
+ this.isMultiProductProject = this.projectProducts.length > 1;
|
|
|
|
+
|
|
|
|
+ // 如果有产品,默认选中第一个
|
|
|
|
+ if (this.projectProducts.length > 0 && !this.activeProductId) {
|
|
|
|
+ this.activeProductId = this.projectProducts[0].id;
|
|
}
|
|
}
|
|
|
|
|
|
// 模拟加载需求数据
|
|
// 模拟加载需求数据
|
|
@@ -216,22 +220,22 @@ export class StageRequirementsComponent implements OnInit {
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * 选择空间
|
|
|
|
|
|
+ * 选择产品空间
|
|
*/
|
|
*/
|
|
- selectSpace(spaceId: string): void {
|
|
|
|
- this.activeSpaceId = spaceId;
|
|
|
|
|
|
+ selectProduct(productId: string): void {
|
|
|
|
+ this.activeProductId = productId;
|
|
this.cdr.markForCheck();
|
|
this.cdr.markForCheck();
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * 切换空间选择状态
|
|
|
|
|
|
+ * 切换产品选择状态
|
|
*/
|
|
*/
|
|
- toggleSpaceSelection(spaceId: string): void {
|
|
|
|
- const index = this.selectedSpaceIds.indexOf(spaceId);
|
|
|
|
|
|
+ toggleProductSelection(productId: string): void {
|
|
|
|
+ const index = this.selectedProductIds.indexOf(productId);
|
|
if (index > -1) {
|
|
if (index > -1) {
|
|
- this.selectedSpaceIds.splice(index, 1);
|
|
|
|
|
|
+ this.selectedProductIds.splice(index, 1);
|
|
} else {
|
|
} else {
|
|
- this.selectedSpaceIds.push(spaceId);
|
|
|
|
|
|
+ this.selectedProductIds.push(productId);
|
|
}
|
|
}
|
|
this.cdr.markForCheck();
|
|
this.cdr.markForCheck();
|
|
}
|
|
}
|
|
@@ -239,7 +243,7 @@ export class StageRequirementsComponent implements OnInit {
|
|
/**
|
|
/**
|
|
* 上传参考图片
|
|
* 上传参考图片
|
|
*/
|
|
*/
|
|
- async uploadReferenceImage(event: any, spaceId?: string): Promise<void> {
|
|
|
|
|
|
+ async uploadReferenceImage(event: any, productId?: string): Promise<void> {
|
|
const files = event.target.files;
|
|
const files = event.target.files;
|
|
if (!files || files.length === 0) return;
|
|
if (!files || files.length === 0) return;
|
|
|
|
|
|
@@ -268,7 +272,7 @@ export class StageRequirementsComponent implements OnInit {
|
|
name: file.name,
|
|
name: file.name,
|
|
type: 'style',
|
|
type: 'style',
|
|
uploadTime: new Date(),
|
|
uploadTime: new Date(),
|
|
- spaceId: spaceId || this.activeSpaceId,
|
|
|
|
|
|
+ spaceId: productId || this.activeProductId, // 保持兼容性,后续可改为productId
|
|
tags: []
|
|
tags: []
|
|
};
|
|
};
|
|
|
|
|
|
@@ -302,7 +306,7 @@ export class StageRequirementsComponent implements OnInit {
|
|
/**
|
|
/**
|
|
* 上传CAD文件
|
|
* 上传CAD文件
|
|
*/
|
|
*/
|
|
- async uploadCAD(event: any, spaceId?: string): Promise<void> {
|
|
|
|
|
|
+ async uploadCAD(event: any, productId?: string): Promise<void> {
|
|
const files = event.target.files;
|
|
const files = event.target.files;
|
|
if (!files || files.length === 0) return;
|
|
if (!files || files.length === 0) return;
|
|
|
|
|
|
@@ -333,7 +337,7 @@ export class StageRequirementsComponent implements OnInit {
|
|
name: file.name,
|
|
name: file.name,
|
|
uploadTime: new Date(),
|
|
uploadTime: new Date(),
|
|
size: file.size,
|
|
size: file.size,
|
|
- spaceId: spaceId || this.activeSpaceId
|
|
|
|
|
|
+ spaceId: productId || this.activeProductId // 保持兼容性,后续可改为productId
|
|
};
|
|
};
|
|
|
|
|
|
// 添加到CAD文件列表
|
|
// 添加到CAD文件列表
|
|
@@ -376,16 +380,16 @@ export class StageRequirementsComponent implements OnInit {
|
|
this.aiSolution = {
|
|
this.aiSolution = {
|
|
generated: true,
|
|
generated: true,
|
|
content: `基于您的${this.globalRequirements.stylePreference}风格需求,我们为您设计了以下方案...`,
|
|
content: `基于您的${this.globalRequirements.stylePreference}风格需求,我们为您设计了以下方案...`,
|
|
- spaces: this.projectSpaces.map(space => ({
|
|
|
|
- id: space.id,
|
|
|
|
- name: space.name,
|
|
|
|
- type: space.type,
|
|
|
|
- styleDescription: `${this.globalRequirements.stylePreference}风格${space.name}设计`,
|
|
|
|
|
|
+ spaces: this.projectProducts.map(product => ({
|
|
|
|
+ id: product.id,
|
|
|
|
+ name: product.name,
|
|
|
|
+ type: product.type,
|
|
|
|
+ styleDescription: `${this.globalRequirements.stylePreference}风格${product.name}设计`,
|
|
colorPalette: [this.globalRequirements.colorScheme.primary, this.globalRequirements.colorScheme.secondary, this.globalRequirements.colorScheme.accent],
|
|
colorPalette: [this.globalRequirements.colorScheme.primary, this.globalRequirements.colorScheme.secondary, this.globalRequirements.colorScheme.accent],
|
|
materials: ['实木', '大理石', '布艺'],
|
|
materials: ['实木', '大理石', '布艺'],
|
|
- furnitureRecommendations: this.getFurnitureRecommendations(space.type),
|
|
|
|
- estimatedCost: this.calculateSpaceEstimatedCost(space),
|
|
|
|
- timeline: this.calculateSpaceTimeline(space)
|
|
|
|
|
|
+ furnitureRecommendations: this.getFurnitureRecommendations(product.type),
|
|
|
|
+ estimatedCost: this.calculateProductEstimatedCost(product),
|
|
|
|
+ timeline: this.calculateProductTimeline(product)
|
|
})),
|
|
})),
|
|
estimatedCost: this.globalRequirements.overallBudget.max || this.calculateTotalEstimatedCost(),
|
|
estimatedCost: this.globalRequirements.overallBudget.max || this.calculateTotalEstimatedCost(),
|
|
timeline: this.calculateTotalTimeline(),
|
|
timeline: this.calculateTotalTimeline(),
|
|
@@ -417,9 +421,9 @@ export class StageRequirementsComponent implements OnInit {
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * 计算空间估算成本
|
|
|
|
|
|
+ * 计算产品估算成本
|
|
*/
|
|
*/
|
|
- private calculateSpaceEstimatedCost(space: any): number {
|
|
|
|
|
|
+ private calculateProductEstimatedCost(product: Project): number {
|
|
const baseCostPerSqm: Record<string, number> = {
|
|
const baseCostPerSqm: Record<string, number> = {
|
|
'living_room': 1500,
|
|
'living_room': 1500,
|
|
'bedroom': 1200,
|
|
'bedroom': 1200,
|
|
@@ -428,7 +432,7 @@ export class StageRequirementsComponent implements OnInit {
|
|
'dining_room': 1300,
|
|
'dining_room': 1300,
|
|
'study': 1100
|
|
'study': 1100
|
|
};
|
|
};
|
|
- const baseCost = (baseCostPerSqm[space.type] || 1000) * (space.area || 10);
|
|
|
|
|
|
+ const baseCost = (baseCostPerSqm[product.type] || 1000) * (product.area || 10);
|
|
const qualityMultiplier: Record<string, number> = {
|
|
const qualityMultiplier: Record<string, number> = {
|
|
'standard': 1.0,
|
|
'standard': 1.0,
|
|
'premium': 1.5,
|
|
'premium': 1.5,
|
|
@@ -441,15 +445,15 @@ export class StageRequirementsComponent implements OnInit {
|
|
* 计算总估算成本
|
|
* 计算总估算成本
|
|
*/
|
|
*/
|
|
private calculateTotalEstimatedCost(): number {
|
|
private calculateTotalEstimatedCost(): number {
|
|
- return this.projectSpaces.reduce((total, space) => {
|
|
|
|
- return total + this.calculateSpaceEstimatedCost(space);
|
|
|
|
|
|
+ return this.projectProducts.reduce((total, product) => {
|
|
|
|
+ return total + this.calculateProductEstimatedCost(product);
|
|
}, 0);
|
|
}, 0);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * 计算空间工期
|
|
|
|
|
|
+ * 计算产品工期
|
|
*/
|
|
*/
|
|
- private calculateSpaceTimeline(space: any): string {
|
|
|
|
|
|
+ private calculateProductTimeline(product: Project): string {
|
|
const baseDays: Record<string, number> = {
|
|
const baseDays: Record<string, number> = {
|
|
'living_room': 15,
|
|
'living_room': 15,
|
|
'bedroom': 12,
|
|
'bedroom': 12,
|
|
@@ -458,14 +462,14 @@ export class StageRequirementsComponent implements OnInit {
|
|
'dining_room': 10,
|
|
'dining_room': 10,
|
|
'study': 8
|
|
'study': 8
|
|
};
|
|
};
|
|
- return `${baseDays[space.type] || 10}-15个工作日`;
|
|
|
|
|
|
+ return `${baseDays[product.type] || 10}-15个工作日`;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
* 计算总工期
|
|
* 计算总工期
|
|
*/
|
|
*/
|
|
private calculateTotalTimeline(): string {
|
|
private calculateTotalTimeline(): string {
|
|
- const totalDays = this.projectSpaces.reduce((total, space) => {
|
|
|
|
|
|
+ const totalDays = this.projectProducts.reduce((total, product) => {
|
|
const baseDays: Record<string, number> = {
|
|
const baseDays: Record<string, number> = {
|
|
'living_room': 15,
|
|
'living_room': 15,
|
|
'bedroom': 12,
|
|
'bedroom': 12,
|
|
@@ -474,7 +478,7 @@ export class StageRequirementsComponent implements OnInit {
|
|
'dining_room': 10,
|
|
'dining_room': 10,
|
|
'study': 8
|
|
'study': 8
|
|
};
|
|
};
|
|
- return total + (baseDays[space.type] || 10);
|
|
|
|
|
|
+ return total + (baseDays[product.type] || 10);
|
|
}, 0);
|
|
}, 0);
|
|
return `预计${Math.ceil(totalDays * 0.7)}-${totalDays}个工作日(考虑并行施工)`;
|
|
return `预计${Math.ceil(totalDays * 0.7)}-${totalDays}个工作日(考虑并行施工)`;
|
|
}
|
|
}
|
|
@@ -589,12 +593,12 @@ export class StageRequirementsComponent implements OnInit {
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * 获取当前空间的需求
|
|
|
|
|
|
+ * 获取当前产品的需求
|
|
*/
|
|
*/
|
|
- getCurrentSpaceRequirement(): any {
|
|
|
|
- if (!this.activeSpaceId) return null;
|
|
|
|
- return this.spaceRequirements.find(req => req.spaceId === this.activeSpaceId) || {
|
|
|
|
- spaceId: this.activeSpaceId,
|
|
|
|
|
|
+ getCurrentProductRequirement(): any {
|
|
|
|
+ if (!this.activeProductId) return null;
|
|
|
|
+ return this.spaceRequirements.find(req => req.productId === this.activeProductId) || {
|
|
|
|
+ productId: this.activeProductId,
|
|
colorRequirement: {},
|
|
colorRequirement: {},
|
|
spaceStructureRequirement: {},
|
|
spaceStructureRequirement: {},
|
|
materialRequirement: {},
|
|
materialRequirement: {},
|
|
@@ -604,24 +608,24 @@ export class StageRequirementsComponent implements OnInit {
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * 获取当前空间特殊需求的 getter/setter
|
|
|
|
|
|
+ * 获取当前产品特殊需求的 getter/setter
|
|
*/
|
|
*/
|
|
- get currentSpaceSpecificRequirements(): string {
|
|
|
|
- const requirement = this.getCurrentSpaceRequirement();
|
|
|
|
|
|
+ get currentProductSpecificRequirements(): string {
|
|
|
|
+ const requirement = this.getCurrentProductRequirement();
|
|
return requirement?.specificRequirements || '';
|
|
return requirement?.specificRequirements || '';
|
|
}
|
|
}
|
|
|
|
|
|
- set currentSpaceSpecificRequirements(value: string) {
|
|
|
|
- const requirement = this.getCurrentSpaceRequirement();
|
|
|
|
|
|
+ set currentProductSpecificRequirements(value: string) {
|
|
|
|
+ const requirement = this.getCurrentProductRequirement();
|
|
if (requirement) {
|
|
if (requirement) {
|
|
requirement.specificRequirements = value;
|
|
requirement.specificRequirements = value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * 获取当前空间的评价
|
|
|
|
|
|
+ * 获取当前产品的评价
|
|
*/
|
|
*/
|
|
- getCurrentSpaceFeedback(): any {
|
|
|
|
|
|
+ getCurrentProductFeedback(): any {
|
|
return null;
|
|
return null;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -629,20 +633,20 @@ export class StageRequirementsComponent implements OnInit {
|
|
* 过滤参考图片
|
|
* 过滤参考图片
|
|
*/
|
|
*/
|
|
getFilteredReferenceImages(): typeof this.referenceImages {
|
|
getFilteredReferenceImages(): typeof this.referenceImages {
|
|
- if (!this.isMultiSpaceProject || !this.activeSpaceId) {
|
|
|
|
|
|
+ if (!this.isMultiProductProject || !this.activeProductId) {
|
|
return this.referenceImages;
|
|
return this.referenceImages;
|
|
}
|
|
}
|
|
- return this.referenceImages.filter(img => !img.spaceId || img.spaceId === this.activeSpaceId);
|
|
|
|
|
|
+ return this.referenceImages.filter(img => !img.spaceId || img.spaceId === this.activeProductId);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
* 过滤CAD文件
|
|
* 过滤CAD文件
|
|
*/
|
|
*/
|
|
getFilteredCADFiles(): typeof this.cadFiles {
|
|
getFilteredCADFiles(): typeof this.cadFiles {
|
|
- if (!this.isMultiSpaceProject || !this.activeSpaceId) {
|
|
|
|
|
|
+ if (!this.isMultiProductProject || !this.activeProductId) {
|
|
return this.cadFiles;
|
|
return this.cadFiles;
|
|
}
|
|
}
|
|
- return this.cadFiles.filter(file => !file.spaceId || file.spaceId === this.activeSpaceId);
|
|
|
|
|
|
+ return this.cadFiles.filter(file => !file.spaceId || file.spaceId === this.activeProductId);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -752,10 +756,10 @@ export class StageRequirementsComponent implements OnInit {
|
|
if (this.globalRequirements.timeline) completedItems++;
|
|
if (this.globalRequirements.timeline) completedItems++;
|
|
totalItems++;
|
|
totalItems++;
|
|
|
|
|
|
- // 空间需求检查
|
|
|
|
- for (const space of this.projectSpaces) {
|
|
|
|
- const spaceFeedback = this.spaceRequirements.find(req => req.spaceId === space.id);
|
|
|
|
- if (spaceFeedback) {
|
|
|
|
|
|
+ // 产品需求检查
|
|
|
|
+ for (const product of this.projectProducts) {
|
|
|
|
+ const productFeedback = this.spaceRequirements.find(req => req.productId === product.id);
|
|
|
|
+ if (productFeedback) {
|
|
completedItems++;
|
|
completedItems++;
|
|
}
|
|
}
|
|
totalItems++;
|
|
totalItems++;
|
|
@@ -765,25 +769,25 @@ export class StageRequirementsComponent implements OnInit {
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * 计算空间完成度
|
|
|
|
|
|
+ * 计算产品完成度
|
|
*/
|
|
*/
|
|
- calculateSpaceCompletion(_spaceId: string): number {
|
|
|
|
- // 简化计算,实际应该基于空间的具体需求完成情况
|
|
|
|
|
|
+ calculateProductCompletion(_productId: string): number {
|
|
|
|
+ // 简化计算,实际应该基于产品的具体需求完成情况
|
|
return Math.floor(Math.random() * 100);
|
|
return Math.floor(Math.random() * 100);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * 创建跨空间需求
|
|
|
|
|
|
+ * 创建跨产品需求
|
|
*/
|
|
*/
|
|
- async createCrossSpaceRequirement(requirement?: any): Promise<void> {
|
|
|
|
- console.log('创建跨空间需求:', requirement || {});
|
|
|
|
|
|
+ async createCrossProductRequirement(requirement?: any): Promise<void> {
|
|
|
|
+ console.log('创建跨产品需求:', requirement || {});
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * 删除跨空间需求
|
|
|
|
|
|
+ * 删除跨产品需求
|
|
*/
|
|
*/
|
|
- async deleteCrossSpaceRequirement(requirementId: string): Promise<void> {
|
|
|
|
- console.log('删除跨空间需求:', requirementId);
|
|
|
|
|
|
+ async deleteCrossProductRequirement(requirementId: string): Promise<void> {
|
|
|
|
+ console.log('删除跨产品需求:', requirementId);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -830,11 +834,18 @@ export class StageRequirementsComponent implements OnInit {
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * 根据空间ID获取空间显示名称(用于模板)
|
|
|
|
|
|
+ * 根据产品ID获取产品显示名称(用于模板)
|
|
|
|
+ */
|
|
|
|
+ getProductDisplayNameById(productId: string): string {
|
|
|
|
+ const product = this.projectProducts.find(p => p.id === productId);
|
|
|
|
+ return this.getProductDisplayName(product);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 获取产品显示名称
|
|
*/
|
|
*/
|
|
- getSpaceDisplayNameById(spaceId: string): string {
|
|
|
|
- const space = this.projectSpaces.find(s => s.id === spaceId);
|
|
|
|
- return this.getSpaceDisplayName(space);
|
|
|
|
|
|
+ getProductDisplayName(product: Project | undefined): string {
|
|
|
|
+ return product?.name || '未知产品';
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|