import { Component, Input, OnInit, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { FmodeObject, FmodeParse } from 'fmode-ng/parse'; import { ProductSpaceService, Project } from '../../services/product-space.service'; import { DesignerTeamAssignmentModalComponent } from '../../../../app/pages/designer/project-detail/components/designer-team-assignment-modal/designer-team-assignment-modal.component'; import type { ProjectTeam, Designer, SpaceScene, DesignerAssignmentResult } from '../../../../app/pages/designer/project-detail/components/designer-team-assignment-modal/designer-team-assignment-modal.component'; const Parse = FmodeParse.with('nova'); @Component({ selector: 'app-team-assign', standalone: true, imports: [CommonModule, FormsModule, DesignerTeamAssignmentModalComponent], templateUrl: './team-assign.component.html', styleUrls: ['./team-assign.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush }) export class TeamAssignComponent implements OnInit { @Input() project: FmodeObject | null = null; @Input() canEdit: boolean = true; // 可选:未传入时默认允许编辑 @Input() currentUser: FmodeObject | null = null; // 可选:未传入时为 null // 项目组(Department)列表 departments: FmodeObject[] = []; selectedDepartment: FmodeObject | null = null; // 项目组成员(Profile)列表 departmentMembers: FmodeObject[] = []; selectedDesigner: FmodeObject | null = null; // 已分配的项目团队成员 projectTeams: FmodeObject[] = []; // 设计师分配对话框(旧的,保留以兼容) showAssignDialog: boolean = false; assigningDesigner: FmodeObject | null = null; selectedSpaces: string[] = []; editingTeam: FmodeObject | null = null; // 当前正在编辑的团队对象 // 统一的设计师分配弹窗 showDesignerModal: boolean = false; modalProjectTeams: ProjectTeam[] = []; modalSpaceScenes: SpaceScene[] = []; modalSelectedTeamId: string = ''; // 加载状态 loadingMembers: boolean = false; loadingTeams: boolean = false; loadingSpaces: boolean = false; saving: boolean = false; // 空间数据 projectSpaces: Project[] = []; constructor( private productSpaceService: ProductSpaceService, private cdr: ChangeDetectorRef ) {} async ngOnInit() { await this.loadData(); } async loadData() { if (!this.project) return; try { // 初始化已选择的项目组与设计师(若项目已有) const department = this.project.get('department'); if (department) { this.selectedDepartment = department; await this.loadDepartmentMembers(department); } const assignee = this.project.get('assignee'); if (assignee) { this.selectedDesigner = assignee; } // 加载项目组列表 const deptQuery = new Parse.Query('Department'); deptQuery.include('leader'); deptQuery.equalTo('type', 'project'); deptQuery.equalTo('company', localStorage.getItem('company')); deptQuery.notEqualTo('isDeleted', true); deptQuery.ascending('name'); this.departments = await deptQuery.find(); console.log("this.departments",this.departments) // 加载项目团队 await this.loadProjectTeams(); // 加载项目空间 await this.loadProjectSpaces(); } catch (err) { console.error('加载团队分配数据失败:', err); } finally { this.cdr.markForCheck(); } } async loadProjectSpaces(): Promise { if (!this.project) return; try { this.loadingSpaces = true; const projectId = this.project.id || ''; this.projectSpaces = await this.productSpaceService.getProjectProductSpaces(projectId); } catch (err) { console.error('加载项目空间失败:', err); } finally { this.loadingSpaces = false; this.cdr.markForCheck(); } } async loadProjectTeams() { if (!this.project) return; try { this.loadingTeams = true; const query = new Parse.Query('ProjectTeam'); query.equalTo('project', this.project.toPointer()); query.include('profile'); query.notEqualTo('isDeleted', true); this.projectTeams = await query.find(); } catch (err) { console.error('加载项目团队失败:', err); } finally { this.loadingTeams = false; } } async selectDepartment(department: FmodeObject) { this.selectedDepartment = department; this.selectedDesigner = null; this.departmentMembers = []; // ✅ 自动设置组长为项目负责人 const leader = department.get('leader'); if (leader && this.project) { try { // 更新项目的assignee字段为组长 this.project.set('assignee', leader); this.project.set('department', department); await this.project.save(); console.log('✅ 项目负责人已设置为组长:', leader.get('name')); // 触发界面更新 this.cdr.markForCheck(); } catch (error) { console.error('❌ 设置项目负责人失败:', error); } } await this.loadDepartmentMembers(department); } async loadDepartmentMembers(department: FmodeObject) { const departmentId = department?.id; if (!departmentId) return []; try { this.loadingMembers = true; const query = new Parse.Query('Profile'); query.equalTo('department', departmentId); query.equalTo('roleName', '组员'); query.notEqualTo('isDeleted', true); query.ascending('name'); this.departmentMembers = await query.find(); // 将组长置顶展示 const leader = department?.get('leader'); if (leader) { this.departmentMembers.unshift(leader); } this.loadingMembers = false; } catch (err) { console.error('加载项目组成员失败:', err); } finally { this.loadingMembers = false; } this.cdr.detectChanges() return this.departmentMembers; } selectDesigner(designer: FmodeObject) { // 检查是否已分配 const isAssigned = this.projectTeams.some(team => team.get('profile')?.id === designer.id); if (isAssigned) { alert('该设计师已分配到此项目'); return; } this.assigningDesigner = designer; this.selectedSpaces = []; // 如果只有一个空间,默认选中 if (this.projectSpaces.length === 1) { const only = this.projectSpaces[0]; this.selectedSpaces = [only.name]; } this.showAssignDialog = true; } editAssignedDesigner(team: FmodeObject) { const designer = team.get('profile'); if (!designer) return; this.assigningDesigner = designer; this.editingTeam = team; const currentSpaces = team.get('data')?.spaces || []; this.selectedSpaces = [...currentSpaces]; this.showAssignDialog = true; } toggleSpaceSelection(spaceName: string) { const index = this.selectedSpaces.indexOf(spaceName); if (index > -1) { this.selectedSpaces.splice(index, 1); } else { this.selectedSpaces.push(spaceName); } } async confirmAssignDesigner() { if (!this.assigningDesigner || !this.project) return; if (this.selectedSpaces.length === 0) { alert('请至少选择一个空间场景'); return; } try { this.saving = true; if (this.editingTeam) { // 更新现有团队成员的空间分配 const data = this.editingTeam.get('data') || {}; data.spaces = this.selectedSpaces; data.updatedAt = new Date(); data.updatedBy = this.currentUser?.id; this.editingTeam.set('data', data); await this.editingTeam.save(); alert('更新成功'); } else { // 创建新的 ProjectTeam const ProjectTeam = Parse.Object.extend('ProjectTeam'); const team = new ProjectTeam(); team.set('project', this.project.toPointer()); team.set('profile', this.assigningDesigner.toPointer()); team.set('department', this.assigningDesigner.get("department")); team.set('role', '组员'); team.set('data', { spaces: this.selectedSpaces, assignedAt: new Date(), assignedBy: this.currentUser?.id }); await team.save(); // 加入群聊(静默执行) await this.addMemberToGroupChat(this.assigningDesigner.get('userId')); alert('分配成功'); } await this.loadProjectTeams(); this.showAssignDialog = false; this.assigningDesigner = null; this.selectedSpaces = []; this.editingTeam = null; } catch (err) { console.error(this.editingTeam ? '更新失败:' : '分配设计师失败:', err); alert(this.editingTeam ? '更新失败' : '分配失败'); } finally { this.saving = false; } } cancelAssignDialog() { this.showAssignDialog = false; this.assigningDesigner = null; this.selectedSpaces = []; this.editingTeam = null; } async addMemberToGroupChat(userId: string) { if (!userId) return; try { const groupChat = (this as any).groupChat; if (!groupChat) return; const chatId = groupChat.get('chat_id'); if (!chatId) return; if (typeof (window as any).ww !== 'undefined') { await (window as any).ww.updateEnterpriseChat({ chatId: chatId, userIdsToAdd: [userId] }); } } catch (err) { console.warn('添加群成员失败:', err); } } async confirmDeleteMember() { if (!this.editingTeam) return; const ok = window.confirm('确定要删除该成员的项目分配吗?'); if (!ok) return; try { this.saving = true; // 软删除 ProjectTeam this.editingTeam.set('isDeleted', true); const data = this.editingTeam.get('data') || {}; data.deletedAt = new Date(); data.deletedBy = this.currentUser?.id; this.editingTeam.set('data', data); await this.editingTeam.save(); // 从群聊移除(静默尝试) const profile = this.editingTeam.get('profile'); const userId = profile?.get?.('userId'); if (userId) { await this.removeMemberFromGroupChat(userId); } alert('成员已删除'); // 刷新列表并收起弹窗 await this.loadProjectTeams(); this.showAssignDialog = false; this.assigningDesigner = null; this.selectedSpaces = []; this.editingTeam = null; } catch (err) { console.error('删除成员失败:', err); alert('删除失败'); } finally { this.saving = false; this.cdr.markForCheck(); } } async removeMemberFromGroupChat(userId: string) { if (!userId) return; try { const groupChat = (this as any).groupChat; if (!groupChat) return; const chatId = groupChat.get('chat_id'); if (!chatId) return; if (typeof (window as any).ww !== 'undefined') { await (window as any).ww.updateEnterpriseChat({ chatId: chatId, userIdsToRemove: [userId] }); } } catch (err) { console.warn('移除群成员失败或不支持:', err); } } getMemberSpaces(team: FmodeObject): string { const spaces = team.get('data')?.spaces || []; return spaces.join('、') || '未分配'; } getDesignerWorkload(designer: FmodeObject): string { return '3个项目'; } /** * 移除团队成员 */ async removeMember(team: FmodeObject) { if (!confirm(`确定要移除 ${team.get('profile')?.get('name')} 吗?`)) { return; } try { this.saving = true; // 删除ProjectTeam记录 await team.destroy(); // 重新加载项目团队 await this.loadProjectTeams(); this.cdr.markForCheck(); } catch (err) { console.error('移除成员失败:', err); alert('移除失败'); } finally { this.saving = false; this.cdr.markForCheck(); } } // ===== 统一设计师分配弹窗相关方法 ===== /** * 打开统一的设计师分配弹窗 */ openDesignerAssignmentModal() { // 设置当前选中的项目组 // 项目组数据和空间数据都由弹窗自己加载真实数据 this.modalSelectedTeamId = this.selectedDepartment?.id || ''; this.showDesignerModal = true; this.cdr.markForCheck(); } /** * 将Parse的Department对象转换为ProjectTeam */ private convertToProjectTeam(dept: FmodeObject): ProjectTeam { const leader = dept.get('leader'); const members = this.departments.find(d => d.id === dept.id)?.get('members') || []; return { id: dept.id, name: dept.get('name') || '', leaderId: leader?.id || '', leaderName: leader?.get('name') || '未指定', description: dept.get('description') || '', members: this.convertDepartmentMembers(dept.id) }; } /** * 转换项目组成员为Designer格式 */ private convertDepartmentMembers(deptId: string): Designer[] { // 如果是当前选中的项目组,使用已加载的成员数据 if (this.selectedDepartment?.id === deptId && this.departmentMembers.length > 0) { return this.departmentMembers .filter(member => member?.get) .map(member => this.convertToDesigner(member, deptId)); } return []; } /** * 将Parse的Profile对象转换为Designer */ private convertToDesigner(profile: FmodeObject, teamId: string): Designer { const data = profile.get('data') || {}; const dept = this.departments.find(d => d.id === teamId); return { id: profile.id, name: profile.get('name') || '', avatar: data.avatar, teamId: teamId, teamName: dept?.get('name') || '', isTeamLeader: false, // 可以根据实际情况判断 status: 'idle', // 默认空闲,可以根据实际工作量判断 idleDays: 0, recentOrders: 0, lastOrderDate: undefined, reviewDates: [], workload: 0, skills: data.skills || [], isInStagnantProject: false, availableDates: [], groupId: teamId, groupName: dept?.get('name') || '', isLeader: false, currentProjects: 0 }; } /** * 关闭设计师分配弹窗 */ closeDesignerModal() { this.showDesignerModal = false; this.cdr.markForCheck(); } /** * 确认设计师分配 */ async handleDesignerAssignment(result: DesignerAssignmentResult) { console.log('设计师分配结果:', result); try { this.saving = true; // 保存选中的设计师到项目团队 for (const designer of result.selectedDesigners) { // 查找该设计师负责的空间 const spaceAssignment = result.spaceAssignments.find( sa => sa.designerId === designer.id ); await this.saveDesignerToTeam(designer, spaceAssignment?.spaceIds || []); } // 保存跨组合作者 for (const collaborator of result.crossTeamCollaborators) { const spaceAssignment = result.spaceAssignments.find( sa => sa.designerId === collaborator.id ); await this.saveDesignerToTeam(collaborator, spaceAssignment?.spaceIds || [], true); } // 重新加载项目团队数据 await this.loadProjectTeams(); // 关闭弹窗 this.closeDesignerModal(); alert('设计师分配成功!'); } catch (err) { console.error('保存设计师分配失败:', err); alert('保存失败,请重试'); } finally { this.saving = false; this.cdr.markForCheck(); } } /** * 保存设计师到项目团队 */ private async saveDesignerToTeam( designer: Designer, spaceIds: string[], isCrossTeam: boolean = false ): Promise { if (!this.project) return; // 查找对应的Profile对象 const profileQuery = new Parse.Query('Profile'); profileQuery.equalTo('objectId', designer.id); const profile = await profileQuery.first(); if (!profile) { console.error('未找到设计师Profile:', designer.id); return; } // 查找是否已存在团队记录 const existingTeamQuery = new Parse.Query('ProjectTeam'); existingTeamQuery.equalTo('project', this.project); existingTeamQuery.equalTo('profile', profile); let teamObj = await existingTeamQuery.first(); if (!teamObj) { // 创建新的团队记录 const ProjectTeam = Parse.Object.extend('ProjectTeam'); teamObj = new ProjectTeam(); teamObj.set('project', this.project); teamObj.set('profile', profile); teamObj.set('role', 'designer'); } // 转换空间ID为空间名称 const spaceNames = spaceIds.map(id => { const space = this.projectSpaces.find(s => s.id === id); return space?.name || ''; }).filter(name => name !== ''); // 保存空间分配信息 teamObj.set('data', { ...teamObj.get('data'), spaces: spaceNames, isCrossTeam: isCrossTeam, assignedAt: new Date().toISOString() }); await teamObj.save(); } }