123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452 |
- import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';
- import { CommonModule } from '@angular/common';
- import { FormsModule } from '@angular/forms';
- import { DesignerTeamAssignmentModalComponent } from '../designer-team-assignment-modal/designer-team-assignment-modal.component';
- export interface Designer {
- id: string;
- name: string;
- avatar?: string;
- teamId: string;
- teamName: string;
- isTeamLeader: boolean;
- status: 'idle' | 'busy' | 'reviewing';
- idleDays: number;
- recentOrders: number;
- lastOrderDate?: string;
- reviewDates: string[]; // 对图日期,这些日期不能安排其他工作
- workload: number; // 当前工作量 (0-100)
- skills: string[]; // 技能标签
- isOnStagnantProject?: boolean; // 是否在停滞期项目中
- isInStagnantProject: boolean; // 是否处于停滞期项目
- availableDates: string[]; // 空闲日期
- // 为了兼容团队分配弹窗与客户服务的日历组件,补充以下字段
- groupId: string; // 对应 teamId
- groupName: string; // 对应 teamName
- isLeader: boolean; // 对应 isTeamLeader
- currentProjects: number; // 当前项目数量
- }
- export interface ProjectTeam {
- id: string;
- name: string;
- leaderId: string;
- leaderName: string;
- members: Designer[];
- }
- export interface QuotationAssignment {
- quotationItemId: string;
- quotationItemName: string;
- assignedDesigners: string[];
- estimatedHours?: number;
- }
- export interface DesignerAssignmentData {
- primaryTeamId: string;
- quotationAssignments: QuotationAssignment[];
- crossTeamCollaborators: string[]; // 跨组合作的设计师ID
- notes?: string;
- }
- @Component({
- selector: 'app-designer-assignment',
- standalone: true,
- imports: [CommonModule, FormsModule, DesignerTeamAssignmentModalComponent],
- templateUrl: './designer-assignment.component.html',
- styleUrls: ['./designer-assignment.component.scss']
- })
- export class DesignerAssignmentComponent implements OnInit {
- @Input() quotationItems: any[] = [];
- @Input() initialAssignment?: DesignerAssignmentData;
- @Output() assignmentChange = new EventEmitter<DesignerAssignmentData>();
- @Output() designerClick = new EventEmitter<Designer>();
- showTeamAssignmentModal = false; // 控制弹窗显示
- showDesignerCalendar = false; // 控制设计师日历显示
- selectedDesignerForCalendar: Designer | null = null; // 当前查看日历的设计师
- // 模拟数据 - 实际项目中应该从服务获取
- projectTeams: ProjectTeam[] = [
- {
- id: 'team-1',
- name: '家装设计组',
- leaderId: 'designer-1',
- leaderName: '张组长',
- members: [
- {
- id: 'designer-1',
- name: '张组长',
- teamId: 'team-1',
- teamName: '家装设计组',
- isTeamLeader: true,
- // 为了兼容弹窗与日历组件,补充映射字段
- groupId: 'team-1',
- groupName: '家装设计组',
- isLeader: true,
- currentProjects: 3,
- status: 'busy',
- idleDays: 0,
- recentOrders: 3,
- lastOrderDate: '2024-01-15',
- reviewDates: ['2024-01-20', '2024-01-25'],
- workload: 85,
- skills: ['家装设计', '软装搭配', '项目管理'],
- isOnStagnantProject: false,
- isInStagnantProject: false,
- availableDates: ['2024-01-22', '2024-01-23', '2024-01-24']
- },
- {
- id: 'designer-2',
- name: '李设计师',
- teamId: 'team-1',
- teamName: '家装设计组',
- isTeamLeader: false,
- // 为了兼容弹窗与日历组件,补充映射字段
- groupId: 'team-1',
- groupName: '家装设计组',
- isLeader: false,
- currentProjects: 2,
- status: 'busy',
- idleDays: 0,
- recentOrders: 1,
- lastOrderDate: '2024-01-10',
- reviewDates: [],
- workload: 30,
- skills: ['家装设计', '3D建模'],
- isOnStagnantProject: false,
- isInStagnantProject: false,
- availableDates: ['2024-01-21', '2024-01-22', '2024-01-23', '2024-01-24', '2024-01-25']
- },
- {
- id: 'designer-5',
- name: '赵停滞',
- teamId: 'team-1',
- teamName: '家装设计组',
- isTeamLeader: false,
- // 为了兼容弹窗与日历组件,补充映射字段
- groupId: 'team-1',
- groupName: '家装设计组',
- isLeader: false,
- currentProjects: 4,
- status: 'busy',
- idleDays: 0,
- recentOrders: 1,
- lastOrderDate: '2023-12-20',
- reviewDates: [],
- workload: 90,
- skills: ['家装设计'],
- isOnStagnantProject: true,
- isInStagnantProject: true,
- availableDates: []
- }
- ]
- },
- {
- id: 'team-2',
- name: '工装设计组',
- leaderId: 'designer-3',
- leaderName: '王组长',
- members: [
- {
- id: 'designer-3',
- name: '王组长',
- teamId: 'team-2',
- teamName: '工装设计组',
- isTeamLeader: true,
- // 为了兼容弹窗与日历组件,补充映射字段
- groupId: 'team-2',
- groupName: '工装设计组',
- isLeader: true,
- currentProjects: 2,
- status: 'reviewing',
- idleDays: 0,
- recentOrders: 2,
- lastOrderDate: '2024-01-14',
- reviewDates: ['2024-01-18', '2024-01-22'],
- workload: 70,
- skills: ['工装设计', '商业空间', '项目管理'],
- isOnStagnantProject: false,
- isInStagnantProject: false,
- availableDates: ['2024-01-19', '2024-01-20', '2024-01-21']
- },
- {
- id: 'designer-4',
- name: '赵设计师',
- teamId: 'team-2',
- teamName: '工装设计组',
- isTeamLeader: false,
- // 为了兼容弹窗与日历组件,补充映射字段
- groupId: 'team-2',
- groupName: '工装设计组',
- isLeader: true,
- currentProjects: 2,
- status: 'reviewing',
- idleDays: 0,
- recentOrders: 0,
- lastOrderDate: '2024-01-03',
- reviewDates: [],
- workload: 10,
- skills: ['工装设计', '效果图制作'],
- isOnStagnantProject: false,
- isInStagnantProject: false,
- availableDates: ['2024-01-21', '2024-01-22', '2024-01-23', '2024-01-24', '2024-01-25', '2024-01-26']
- }
- ]
- }
- ];
- assignmentData: DesignerAssignmentData = {
- primaryTeamId: '',
- quotationAssignments: [],
- crossTeamCollaborators: [],
- notes: ''
- };
- selectedTeamId = '';
- showCrossTeamSelection = false;
- availableCrossTeamDesigners: Designer[] = [];
- constructor() {}
- ngOnInit() {
- if (this.initialAssignment) {
- this.assignmentData = { ...this.initialAssignment };
- this.selectedTeamId = this.assignmentData.primaryTeamId;
- }
- // 初始化报价分配
- this.initializeQuotationAssignments();
- }
- // 初始化报价分配
- initializeQuotationAssignments() {
- if (this.quotationItems.length > 0 && this.assignmentData.quotationAssignments.length === 0) {
- this.assignmentData.quotationAssignments = this.quotationItems.map(item => ({
- quotationItemId: item.id,
- quotationItemName: item.name,
- assignedDesigners: [],
- estimatedHours: 0
- }));
- }
- }
- // 选择主要项目组
- selectPrimaryTeam(teamId: string) {
- this.selectedTeamId = teamId;
- this.assignmentData.primaryTeamId = teamId;
-
- // 清空之前的分配
- this.assignmentData.quotationAssignments.forEach(assignment => {
- assignment.assignedDesigners = [];
- });
-
- this.updateAvailableCrossTeamDesigners();
- this.emitAssignmentChange();
- }
- // 获取选中的项目组
- getSelectedTeam(): ProjectTeam | undefined {
- return this.projectTeams.find(team => team.id === this.selectedTeamId);
- }
- // 分配设计师到报价项目
- assignDesignerToQuotation(quotationId: string, designerId: string) {
- const assignment = this.assignmentData.quotationAssignments.find(a => a.quotationItemId === quotationId);
- if (assignment) {
- if (!assignment.assignedDesigners.includes(designerId)) {
- assignment.assignedDesigners.push(designerId);
- this.emitAssignmentChange();
- }
- }
- }
- // 移除设计师分配
- removeDesignerFromQuotation(quotationId: string, designerId: string) {
- const assignment = this.assignmentData.quotationAssignments.find(a => a.quotationItemId === quotationId);
- if (assignment) {
- assignment.assignedDesigners = assignment.assignedDesigners.filter(id => id !== designerId);
- this.emitAssignmentChange();
- }
- }
- // 更新可用的跨组合作设计师
- updateAvailableCrossTeamDesigners() {
- this.availableCrossTeamDesigners = [];
- this.projectTeams.forEach(team => {
- if (team.id !== this.selectedTeamId) {
- this.availableCrossTeamDesigners.push(...team.members);
- }
- });
- }
- // 添加跨组合作设计师
- addCrossTeamCollaborator(designerId: string) {
- if (!this.assignmentData.crossTeamCollaborators.includes(designerId)) {
- this.assignmentData.crossTeamCollaborators.push(designerId);
- this.emitAssignmentChange();
- }
- }
- // 移除跨组合作设计师
- removeCrossTeamCollaborator(designerId: string) {
- this.assignmentData.crossTeamCollaborators = this.assignmentData.crossTeamCollaborators.filter(id => id !== designerId);
- this.emitAssignmentChange();
- }
- // 获取设计师信息
- getDesignerById(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;
- }
- // 获取设计师状态颜色
- getDesignerStatusColor(status: string): string {
- switch (status) {
- case 'idle': return '#52c41a';
- case 'busy': return '#faad14';
- case 'reviewing': return '#1890ff';
- default: return '#d9d9d9';
- }
- }
- // 获取设计师状态文本
- getDesignerStatusText(status: string): string {
- switch (status) {
- case 'idle': return '空闲';
- case 'busy': return '忙碌';
- case 'reviewing': return '对图中';
- default: return '未知';
- }
- }
- // 获取工作量状态
- getWorkloadStatus(workload: number): 'low' | 'medium' | 'high' {
- if (workload < 30) return 'low';
- if (workload < 70) return 'medium';
- return 'high';
- }
- // 点击设计师
- onDesignerClick(designer: Designer) {
- this.designerClick.emit(designer);
- }
- // 发送分配变化事件
- emitAssignmentChange() {
- this.assignmentChange.emit({ ...this.assignmentData });
- }
- // 自动分配建议
- getAutoAssignmentSuggestion(): string {
- const selectedTeam = this.getSelectedTeam();
- if (!selectedTeam) return '';
- const idleDesigners = selectedTeam.members.filter(d => d.status === 'idle' && d.workload < 50);
- const busyDesigners = selectedTeam.members.filter(d => d.workload >= 70);
- let suggestion = '';
- if (idleDesigners.length > 0) {
- suggestion += `建议优先分配给空闲设计师:${idleDesigners.map(d => d.name).join('、')}。`;
- }
- if (busyDesigners.length > 0) {
- suggestion += `注意:${busyDesigners.map(d => d.name).join('、')} 工作量较重。`;
- }
- return suggestion;
- }
- // 检查是否有冲突的对图日期
- hasReviewDateConflict(designerId: string, projectStartDate?: string): boolean {
- const designer = this.getDesignerById(designerId);
- if (!designer || !projectStartDate) return false;
- // 简单的日期冲突检查逻辑
- const startDate = new Date(projectStartDate);
- const endDate = new Date(startDate.getTime() + 30 * 24 * 60 * 60 * 1000); // 假设项目周期30天
- return designer.reviewDates.some(reviewDate => {
- const review = new Date(reviewDate);
- return review >= startDate && review <= endDate;
- });
- }
- // 获取团队空闲人数的安全方法
- getIdleCount(team: ProjectTeam): number {
- if (!team || !team.members) return 0;
- return team.members.filter(m => m && m.status === 'idle').length;
- }
- // 获取团队平均工作量
- getAverageWorkload(team: ProjectTeam): number {
- if (team.members.length === 0) return 0;
- const totalWorkload = team.members.reduce((sum, member) => sum + member.workload, 0);
- return Math.round(totalWorkload / team.members.length);
- }
- // 获取工作量样式类
- getWorkloadClass(workload: number): string {
- if (workload < 50) return 'low';
- if (workload < 80) return 'medium';
- return 'high';
- }
- // 打开设计师组分配弹窗
- openTeamAssignmentModal(): void {
- this.showTeamAssignmentModal = true;
- }
- // 关闭设计师组分配弹窗
- closeTeamAssignmentModal(): void {
- this.showTeamAssignmentModal = false;
- }
- // 显示设计师详细日历
- showDesignerDetailCalendar(designer: Designer): void {
- this.selectedDesignerForCalendar = designer;
- this.showDesignerCalendar = true;
- }
- // 关闭设计师详细日历
- closeDesignerCalendar(): void {
- this.showDesignerCalendar = false;
- this.selectedDesignerForCalendar = null;
- }
- // 过滤停滞期项目的设计师
- filterStagnantDesigners(designers: Designer[]): Designer[] {
- return designers.filter(designer => !designer.isOnStagnantProject);
- }
- // 获取可用的设计师(过滤停滞期项目)
- getAvailableDesigners(team: ProjectTeam): Designer[] {
- return this.filterStagnantDesigners(team.members);
- }
- // 处理弹窗中的设计师分配确认
- onModalAssignmentConfirm(assignmentResult: any): void {
- // 更新分配数据
- this.selectedTeamId = assignmentResult.primaryTeamId;
- this.assignmentData.primaryTeamId = assignmentResult.primaryTeamId;
- this.assignmentData.crossTeamCollaborators = assignmentResult.crossTeamCollaborators;
-
- // 更新报价项目分配
- if (assignmentResult.quotationAssignments) {
- this.assignmentData.quotationAssignments = assignmentResult.quotationAssignments;
- }
- // 关闭弹窗
- this.closeTeamAssignmentModal();
-
- // 触发变更事件
- this.emitAssignmentChange();
- }
- // 获取总分配设计师数量
- getTotalAssignedDesigners(): number {
- return this.assignmentData.quotationAssignments.reduce((total, assignment) => total + assignment.assignedDesigners.length, 0);
- }
- }
|