|
@@ -1,8 +1,12 @@
|
|
|
-import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';
|
|
|
|
|
|
|
+import { Component, Input, Output, EventEmitter, OnInit, ChangeDetectorRef } from '@angular/core';
|
|
|
import { CommonModule } from '@angular/common';
|
|
import { CommonModule } from '@angular/common';
|
|
|
import { FormsModule } from '@angular/forms';
|
|
import { FormsModule } from '@angular/forms';
|
|
|
import { DesignerCalendarComponent } from '../../../../customer-service/consultation-order/components/designer-calendar/designer-calendar.component';
|
|
import { DesignerCalendarComponent } from '../../../../customer-service/consultation-order/components/designer-calendar/designer-calendar.component';
|
|
|
import { Designer as CalendarDesigner } from '../../../../customer-service/consultation-order/components/designer-calendar/designer-calendar.component';
|
|
import { Designer as CalendarDesigner } from '../../../../customer-service/consultation-order/components/designer-calendar/designer-calendar.component';
|
|
|
|
|
+import { FmodeParse, FmodeObject } from 'fmode-ng/parse';
|
|
|
|
|
+import { ProductSpaceService, Project as ProductSpace } from '../../../../../../modules/project/services/product-space.service';
|
|
|
|
|
+
|
|
|
|
|
+const Parse = FmodeParse.with('nova');
|
|
|
|
|
|
|
|
export interface Designer {
|
|
export interface Designer {
|
|
|
id: string;
|
|
id: string;
|
|
@@ -36,11 +40,25 @@ export interface ProjectTeam {
|
|
|
description?: string;
|
|
description?: string;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+export interface SpaceScene {
|
|
|
|
|
+ id: string;
|
|
|
|
|
+ name: string;
|
|
|
|
|
+ area?: number;
|
|
|
|
|
+ description?: string;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+export interface DesignerSpaceAssignment {
|
|
|
|
|
+ designerId: string;
|
|
|
|
|
+ designerName: string;
|
|
|
|
|
+ spaceIds: string[];
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
export interface DesignerAssignmentResult {
|
|
export interface DesignerAssignmentResult {
|
|
|
selectedDesigners: Designer[];
|
|
selectedDesigners: Designer[];
|
|
|
primaryTeamId: string;
|
|
primaryTeamId: string;
|
|
|
crossTeamCollaborators: Designer[];
|
|
crossTeamCollaborators: Designer[];
|
|
|
quotationAssignments: any[];
|
|
quotationAssignments: any[];
|
|
|
|
|
+ spaceAssignments: DesignerSpaceAssignment[]; // 空间分配结果
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@Component({
|
|
@Component({
|
|
@@ -59,9 +77,25 @@ export class DesignerTeamAssignmentModalComponent implements OnInit {
|
|
|
@Input() selectedDesigners: any[] = []; // 添加selectedDesigners输入属性
|
|
@Input() selectedDesigners: any[] = []; // 添加selectedDesigners输入属性
|
|
|
@Input() crossTeamCollaborators: any[] = []; // 添加crossTeamCollaborators输入属性
|
|
@Input() crossTeamCollaborators: any[] = []; // 添加crossTeamCollaborators输入属性
|
|
|
@Input() calendarViewMode: 'week' | 'month' | 'quarter' = 'month'; // 日历视图模式,默认为月视图
|
|
@Input() calendarViewMode: 'week' | 'month' | 'quarter' = 'month'; // 日历视图模式,默认为月视图
|
|
|
|
|
+ @Input() spaceScenes: SpaceScene[] = []; // 空间场景列表
|
|
|
|
|
+ @Input() enableSpaceAssignment: boolean = false; // 是否启用空间分配功能
|
|
|
|
|
+ @Input() projectId: string = ''; // 项目ID,用于加载真实数据
|
|
|
|
|
+ @Input() loadRealData: boolean = true; // 是否加载真实数据,默认为true
|
|
|
|
|
+ @Input() loadRealSpaces: boolean = true; // 是否自动加载真实空间数据,默认为true
|
|
|
@Output() close = new EventEmitter<void>();
|
|
@Output() close = new EventEmitter<void>();
|
|
|
@Output() confirm = new EventEmitter<DesignerAssignmentResult>();
|
|
@Output() confirm = new EventEmitter<DesignerAssignmentResult>();
|
|
|
|
|
|
|
|
|
|
+ // Parse数据
|
|
|
|
|
+ private parseDepartments: FmodeObject[] = [];
|
|
|
|
|
+ private parseProfiles: Map<string, FmodeObject[]> = new Map(); // departmentId -> profiles[]
|
|
|
|
|
+ private parseProducts: ProductSpace[] = []; // 项目的产品空间列表
|
|
|
|
|
+
|
|
|
|
|
+ // 加载状态
|
|
|
|
|
+ loadingTeams = false;
|
|
|
|
|
+ loadingSpaces = false;
|
|
|
|
|
+ loadError = '';
|
|
|
|
|
+ spaceLoadError = '';
|
|
|
|
|
+
|
|
|
// 项目组数据(作为默认数据,如果没有通过@Input传入)
|
|
// 项目组数据(作为默认数据,如果没有通过@Input传入)
|
|
|
defaultProjectTeams: ProjectTeam[] = [
|
|
defaultProjectTeams: ProjectTeam[] = [
|
|
|
{
|
|
{
|
|
@@ -228,11 +262,27 @@ export class DesignerTeamAssignmentModalComponent implements OnInit {
|
|
|
// 为日历组件准备的设计师数据(转换成客户服务模块的 Designer 类型)
|
|
// 为日历组件准备的设计师数据(转换成客户服务模块的 Designer 类型)
|
|
|
selectedCalendarDesigners: CalendarDesigner[] = [];
|
|
selectedCalendarDesigners: CalendarDesigner[] = [];
|
|
|
allowCrossTeamSelection = true;
|
|
allowCrossTeamSelection = true;
|
|
|
-
|
|
|
|
|
- constructor() {}
|
|
|
|
|
-
|
|
|
|
|
- ngOnInit() {
|
|
|
|
|
- // 如果没有传入projectTeams,使用默认数据
|
|
|
|
|
|
|
+ // 空间分配相关
|
|
|
|
|
+ designerSpaceMap: Map<string, string[]> = new Map(); // designerId -> spaceIds[]
|
|
|
|
|
+ selectedDesignerForSpaceAssignment: Designer | null = null;
|
|
|
|
|
+
|
|
|
|
|
+ constructor(
|
|
|
|
|
+ private cdr: ChangeDetectorRef,
|
|
|
|
|
+ private productSpaceService: ProductSpaceService
|
|
|
|
|
+ ) {}
|
|
|
|
|
+
|
|
|
|
|
+ async ngOnInit() {
|
|
|
|
|
+ // 如果需要加载真实数据
|
|
|
|
|
+ if (this.loadRealData) {
|
|
|
|
|
+ await this.loadRealProjectTeams();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如果需要加载真实空间数据
|
|
|
|
|
+ if (this.loadRealSpaces && this.projectId) {
|
|
|
|
|
+ await this.loadRealProjectSpaces();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如果没有传入projectTeams且没有加载到真实数据,使用默认数据
|
|
|
if (this.projectTeams.length === 0) {
|
|
if (this.projectTeams.length === 0) {
|
|
|
this.projectTeams = [...this.defaultProjectTeams];
|
|
this.projectTeams = [...this.defaultProjectTeams];
|
|
|
}
|
|
}
|
|
@@ -244,6 +294,262 @@ export class DesignerTeamAssignmentModalComponent implements OnInit {
|
|
|
this.filterStagnantDesigners();
|
|
this.filterStagnantDesigners();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 从Parse Server加载真实的项目组和成员数据
|
|
|
|
|
+ */
|
|
|
|
|
+ async loadRealProjectTeams() {
|
|
|
|
|
+ try {
|
|
|
|
|
+ this.loadingTeams = true;
|
|
|
|
|
+ this.loadError = '';
|
|
|
|
|
+
|
|
|
|
|
+ // 1. 加载所有项目组(Department)
|
|
|
|
|
+ const deptQuery = new Parse.Query('Department');
|
|
|
|
|
+ deptQuery.include('leader');
|
|
|
|
|
+ deptQuery.equalTo('type', 'project');
|
|
|
|
|
+ const companyId = localStorage.getItem('company');
|
|
|
|
|
+ if (companyId) {
|
|
|
|
|
+ deptQuery.equalTo('company', companyId);
|
|
|
|
|
+ }
|
|
|
|
|
+ deptQuery.notEqualTo('isDeleted', true);
|
|
|
|
|
+ deptQuery.ascending('name');
|
|
|
|
|
+
|
|
|
|
|
+ this.parseDepartments = await deptQuery.find();
|
|
|
|
|
+
|
|
|
|
|
+ if (this.parseDepartments.length === 0) {
|
|
|
|
|
+ console.warn('未找到项目组数据');
|
|
|
|
|
+ this.loadError = '未找到项目组数据';
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 为每个项目组加载成员
|
|
|
|
|
+ const projectTeams: ProjectTeam[] = [];
|
|
|
|
|
+
|
|
|
|
|
+ for (const dept of this.parseDepartments) {
|
|
|
|
|
+ const members = await this.loadDepartmentMembers(dept);
|
|
|
|
|
+
|
|
|
|
|
+ const leader = dept.get('leader');
|
|
|
|
|
+ const projectTeam: ProjectTeam = {
|
|
|
|
|
+ id: dept.id,
|
|
|
|
|
+ name: dept.get('name') || '未命名项目组',
|
|
|
|
|
+ leaderId: leader?.id || '',
|
|
|
|
|
+ leaderName: leader?.get('name') || '未设置',
|
|
|
|
|
+ description: dept.get('data')?.description || `${dept.get('name')}项目组`,
|
|
|
|
|
+ members: members
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ projectTeams.push(projectTeam);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 3. 更新projectTeams
|
|
|
|
|
+ if (projectTeams.length > 0) {
|
|
|
|
|
+ this.projectTeams = projectTeams;
|
|
|
|
|
+ console.log('成功加载项目组数据:', this.projectTeams);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ } catch (err) {
|
|
|
|
|
+ console.error('加载项目组数据失败:', err);
|
|
|
|
|
+ this.loadError = '加载项目组数据失败';
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ this.loadingTeams = false;
|
|
|
|
|
+ this.cdr.markForCheck();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 加载项目组成员
|
|
|
|
|
+ */
|
|
|
|
|
+ async loadDepartmentMembers(department: FmodeObject): Promise<Designer[]> {
|
|
|
|
|
+ const departmentId = department.id;
|
|
|
|
|
+ if (!departmentId) return [];
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 查询该项目组的所有成员
|
|
|
|
|
+ const query = new Parse.Query('Profile');
|
|
|
|
|
+ query.equalTo('department', departmentId);
|
|
|
|
|
+ query.notEqualTo('isDeleted', true);
|
|
|
|
|
+ query.ascending('name');
|
|
|
|
|
+
|
|
|
|
|
+ const profiles = await query.find();
|
|
|
|
|
+
|
|
|
|
|
+ // 缓存查询结果
|
|
|
|
|
+ this.parseProfiles.set(departmentId, profiles);
|
|
|
|
|
+
|
|
|
|
|
+ // 转换为Designer格式
|
|
|
|
|
+ const members: Designer[] = profiles.map(profile => this.convertProfileToDesigner(profile, department));
|
|
|
|
|
+
|
|
|
|
|
+ // 确保组长在列表中
|
|
|
|
|
+ const leader = department.get('leader');
|
|
|
|
|
+ if (leader) {
|
|
|
|
|
+ const leaderId = leader.id;
|
|
|
|
|
+ const leaderExists = members.some(m => m.id === leaderId);
|
|
|
|
|
+
|
|
|
|
|
+ if (!leaderExists) {
|
|
|
|
|
+ members.unshift(this.convertProfileToDesigner(leader, department, true));
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 将组长移到第一位
|
|
|
|
|
+ const leaderIndex = members.findIndex(m => m.id === leaderId);
|
|
|
|
|
+ if (leaderIndex > 0) {
|
|
|
|
|
+ const [leaderMember] = members.splice(leaderIndex, 1);
|
|
|
|
|
+ leaderMember.isTeamLeader = true;
|
|
|
|
|
+ members.unshift(leaderMember);
|
|
|
|
|
+ } else if (leaderIndex === 0) {
|
|
|
|
|
+ members[0].isTeamLeader = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return members;
|
|
|
|
|
+ } catch (err) {
|
|
|
|
|
+ console.error(`加载项目组 ${departmentId} 成员失败:`, err);
|
|
|
|
|
+ return [];
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 将Parse Profile对象转换为Designer接口
|
|
|
|
|
+ */
|
|
|
|
|
+ convertProfileToDesigner(profile: FmodeObject, department: FmodeObject, isLeader: boolean = false): Designer {
|
|
|
|
|
+ const data = profile.get('data') || {};
|
|
|
|
|
+ const leader = department.get('leader');
|
|
|
|
|
+ const isTeamLeader = isLeader || (leader && leader.id === profile.id);
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ id: profile.id,
|
|
|
|
|
+ name: profile.get('name') || '未命名',
|
|
|
|
|
+ avatar: data.avatar || '',
|
|
|
|
|
+ teamId: department.id,
|
|
|
|
|
+ teamName: department.get('name') || '未命名项目组',
|
|
|
|
|
+ isTeamLeader: isTeamLeader,
|
|
|
|
|
+ status: this.getDesignerStatus(profile),
|
|
|
|
|
+ idleDays: data.idleDays || 0,
|
|
|
|
|
+ recentOrders: data.recentOrders || 0,
|
|
|
|
|
+ lastOrderDate: data.lastOrderDate,
|
|
|
|
|
+ reviewDates: data.reviewDates || [],
|
|
|
|
|
+ workload: data.workload || 0,
|
|
|
|
|
+ skills: data.skills || ['建模', '渲染'],
|
|
|
|
|
+ isInStagnantProject: data.isInStagnantProject || false,
|
|
|
|
|
+ availableDates: data.availableDates || [],
|
|
|
|
|
+ // 兼容designer-calendar组件
|
|
|
|
|
+ groupId: department.id,
|
|
|
|
|
+ groupName: department.get('name') || '未命名项目组',
|
|
|
|
|
+ isLeader: isTeamLeader,
|
|
|
|
|
+ currentProjects: data.currentProjects || 0
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取设计师状态
|
|
|
|
|
+ */
|
|
|
|
|
+ getDesignerStatus(profile: FmodeObject): 'idle' | 'busy' | 'reviewing' | 'stagnant' {
|
|
|
|
|
+ const data = profile.get('data') || {};
|
|
|
|
|
+
|
|
|
|
|
+ // 如果有明确的status字段
|
|
|
|
|
+ if (data.status) {
|
|
|
|
|
+ return data.status;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 根据工作量推断状态
|
|
|
|
|
+ const workload = data.workload || 0;
|
|
|
|
|
+ const currentProjects = data.currentProjects || 0;
|
|
|
|
|
+
|
|
|
|
|
+ if (workload === 0 || currentProjects === 0) {
|
|
|
|
|
+ return 'idle';
|
|
|
|
|
+ } else if (workload >= 80) {
|
|
|
|
|
+ return 'busy';
|
|
|
|
|
+ } else if (data.isInStagnantProject) {
|
|
|
|
|
+ return 'stagnant';
|
|
|
|
|
+ } else {
|
|
|
|
|
+ return 'reviewing';
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 从Parse Server加载真实的项目空间数据(从Product表)
|
|
|
|
|
+ */
|
|
|
|
|
+ async loadRealProjectSpaces() {
|
|
|
|
|
+ if (!this.projectId) {
|
|
|
|
|
+ console.warn('未提供projectId,无法加载空间数据');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ this.loadingSpaces = true;
|
|
|
|
|
+ this.spaceLoadError = '';
|
|
|
|
|
+
|
|
|
|
|
+ // 使用ProductSpaceService查询项目的所有空间产品
|
|
|
|
|
+ this.parseProducts = await this.productSpaceService.getProjectProductSpaces(this.projectId);
|
|
|
|
|
+
|
|
|
|
|
+ if (this.parseProducts.length === 0) {
|
|
|
|
|
+ console.warn('未找到项目空间数据');
|
|
|
|
|
+ this.spaceLoadError = '未找到项目空间数据';
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 转换为SpaceScene格式
|
|
|
|
|
+ this.spaceScenes = this.parseProducts.map(product => ({
|
|
|
|
|
+ id: product.id,
|
|
|
|
|
+ name: product.name, // productName
|
|
|
|
|
+ area: product.area,
|
|
|
|
|
+ description: this.getProductDescription(product)
|
|
|
|
|
+ }));
|
|
|
|
|
+
|
|
|
|
|
+ console.log('成功加载项目空间数据:', this.spaceScenes);
|
|
|
|
|
+
|
|
|
|
|
+ } catch (err) {
|
|
|
|
|
+ console.error('加载项目空间数据失败:', err);
|
|
|
|
|
+ this.spaceLoadError = '加载项目空间数据失败';
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ this.loadingSpaces = false;
|
|
|
|
|
+ this.cdr.markForCheck();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取产品描述
|
|
|
|
|
+ */
|
|
|
|
|
+ private getProductDescription(product: ProductSpace): string {
|
|
|
|
|
+ const parts: string[] = [];
|
|
|
|
|
+
|
|
|
|
|
+ // 产品类型
|
|
|
|
|
+ if (product.type) {
|
|
|
|
|
+ const typeMap: Record<string, string> = {
|
|
|
|
|
+ 'living_room': '客厅',
|
|
|
|
|
+ 'bedroom': '卧室',
|
|
|
|
|
+ 'kitchen': '厨房',
|
|
|
|
|
+ 'bathroom': '卫生间',
|
|
|
|
|
+ 'study': '书房',
|
|
|
|
|
+ 'dining_room': '餐厅',
|
|
|
|
|
+ 'balcony': '阳台',
|
|
|
|
|
+ 'entrance': '玄关',
|
|
|
|
|
+ 'other': '其他'
|
|
|
|
|
+ };
|
|
|
|
|
+ parts.push(typeMap[product.type] || product.type);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 面积
|
|
|
|
|
+ if (product.area) {
|
|
|
|
|
+ parts.push(`${product.area}㎡`);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 状态
|
|
|
|
|
+ if (product.status) {
|
|
|
|
|
+ const statusMap: Record<string, string> = {
|
|
|
|
|
+ 'pending': '待开始',
|
|
|
|
|
+ 'in_progress': '进行中',
|
|
|
|
|
+ 'completed': '已完成',
|
|
|
|
|
+ 'on_hold': '暂停中'
|
|
|
|
|
+ };
|
|
|
|
|
+ parts.push(statusMap[product.status] || product.status);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如果有metadata中的描述,使用它
|
|
|
|
|
+ if (product.metadata?.description) {
|
|
|
|
|
+ parts.push(product.metadata.description);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return parts.join(' · ') || '暂无描述';
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// 过滤停滞期项目的设计师
|
|
// 过滤停滞期项目的设计师
|
|
|
filterStagnantDesigners() {
|
|
filterStagnantDesigners() {
|
|
|
this.projectTeams.forEach(team => {
|
|
this.projectTeams.forEach(team => {
|
|
@@ -384,11 +690,25 @@ export class DesignerTeamAssignmentModalComponent implements OnInit {
|
|
|
|
|
|
|
|
// 确认分配
|
|
// 确认分配
|
|
|
confirmAssignment() {
|
|
confirmAssignment() {
|
|
|
|
|
+ // 生成空间分配结果
|
|
|
|
|
+ const spaceAssignments: DesignerSpaceAssignment[] = [];
|
|
|
|
|
+ this.designerSpaceMap.forEach((spaceIds, designerId) => {
|
|
|
|
|
+ const designer = this.findDesignerById(designerId);
|
|
|
|
|
+ if (designer && spaceIds.length > 0) {
|
|
|
|
|
+ spaceAssignments.push({
|
|
|
|
|
+ designerId,
|
|
|
|
|
+ designerName: designer.name,
|
|
|
|
|
+ spaceIds
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
const result: DesignerAssignmentResult = {
|
|
const result: DesignerAssignmentResult = {
|
|
|
selectedDesigners: [...this.internalSelectedDesigners],
|
|
selectedDesigners: [...this.internalSelectedDesigners],
|
|
|
primaryTeamId: this.internalSelectedTeamId,
|
|
primaryTeamId: this.internalSelectedTeamId,
|
|
|
crossTeamCollaborators: [...this.internalCrossTeamCollaborators],
|
|
crossTeamCollaborators: [...this.internalCrossTeamCollaborators],
|
|
|
- quotationAssignments: [] // 这里可以根据需要生成报价分配
|
|
|
|
|
|
|
+ quotationAssignments: [], // 这里可以根据需要生成报价分配
|
|
|
|
|
+ spaceAssignments
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
this.confirm.emit(result);
|
|
this.confirm.emit(result);
|
|
@@ -436,4 +756,62 @@ export class DesignerTeamAssignmentModalComponent implements OnInit {
|
|
|
getCrossTeamCollaboratorsNames(): string {
|
|
getCrossTeamCollaboratorsNames(): string {
|
|
|
return this.internalCrossTeamCollaborators.map(d => d.name).join(', ');
|
|
return this.internalCrossTeamCollaborators.map(d => d.name).join(', ');
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // ===== 空间分配相关方法 =====
|
|
|
|
|
+
|
|
|
|
|
+ // 打开空间分配面板
|
|
|
|
|
+ openSpaceAssignment(designer: Designer) {
|
|
|
|
|
+ this.selectedDesignerForSpaceAssignment = designer;
|
|
|
|
|
+ // 初始化该设计师的空间选择
|
|
|
|
|
+ if (!this.designerSpaceMap.has(designer.id)) {
|
|
|
|
|
+ this.designerSpaceMap.set(designer.id, []);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 关闭空间分配面板
|
|
|
|
|
+ closeSpaceAssignment() {
|
|
|
|
|
+ this.selectedDesignerForSpaceAssignment = null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 切换空间选择
|
|
|
|
|
+ toggleSpaceSelection(designerId: string, spaceId: string) {
|
|
|
|
|
+ const spaces = this.designerSpaceMap.get(designerId) || [];
|
|
|
|
|
+ const index = spaces.indexOf(spaceId);
|
|
|
|
|
+
|
|
|
|
|
+ if (index > -1) {
|
|
|
|
|
+ spaces.splice(index, 1);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ spaces.push(spaceId);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ this.designerSpaceMap.set(designerId, spaces);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 检查空间是否被选中
|
|
|
|
|
+ isSpaceSelected(designerId: string, spaceId: string): boolean {
|
|
|
|
|
+ const spaces = this.designerSpaceMap.get(designerId) || [];
|
|
|
|
|
+ return spaces.includes(spaceId);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 获取设计师已分配的空间列表
|
|
|
|
|
+ getDesignerSpaces(designerId: string): SpaceScene[] {
|
|
|
|
|
+ const spaceIds = this.designerSpaceMap.get(designerId) || [];
|
|
|
|
|
+ return this.spaceScenes.filter(space => spaceIds.includes(space.id));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 获取设计师已分配空间的名称文本
|
|
|
|
|
+ getDesignerSpacesText(designerId: string): string {
|
|
|
|
|
+ const spaces = this.getDesignerSpaces(designerId);
|
|
|
|
|
+ if (spaces.length === 0) return '未分配空间';
|
|
|
|
|
+ return spaces.map(s => s.name).join(', ');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 查找设计师
|
|
|
|
|
+ private findDesignerById(designerId: string): Designer | undefined {
|
|
|
|
|
+ for (const team of this.projectTeams) {
|
|
|
|
|
+ const designer = team.members.find(d => d.id === designerId);
|
|
|
|
|
+ if (designer) return designer;
|
|
|
|
|
+ }
|
|
|
|
|
+ return undefined;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|