|  | @@ -1,12 +1,15 @@
 | 
											
												
													
														|  |  import { Component, OnInit, OnDestroy } from '@angular/core';
 |  |  import { Component, OnInit, OnDestroy } from '@angular/core';
 | 
											
												
													
														|  |  import { CommonModule } from '@angular/common';
 |  |  import { CommonModule } from '@angular/common';
 | 
											
												
													
														|  | -import { RouterModule } from '@angular/router';
 |  | 
 | 
											
												
													
														|  | 
 |  | +import { RouterModule, ActivatedRoute, Router } from '@angular/router';
 | 
											
												
													
														|  |  import { ProjectService } from '../../../services/project.service';
 |  |  import { ProjectService } from '../../../services/project.service';
 | 
											
												
													
														|  |  import { Task } from '../../../models/project.model';
 |  |  import { Task } from '../../../models/project.model';
 | 
											
												
													
														|  |  import { SkillRadarComponent } from './skill-radar/skill-radar.component';
 |  |  import { SkillRadarComponent } from './skill-radar/skill-radar.component';
 | 
											
												
													
														|  |  import { PersonalBoard } from '../personal-board/personal-board';
 |  |  import { PersonalBoard } from '../personal-board/personal-board';
 | 
											
												
													
														|  |  import { FmodeQuery, FmodeObject, FmodeUser } from 'fmode-ng/core';
 |  |  import { FmodeQuery, FmodeObject, FmodeUser } from 'fmode-ng/core';
 | 
											
												
													
														|  |  import { WxworkAuth } from 'fmode-ng/core';
 |  |  import { WxworkAuth } from 'fmode-ng/core';
 | 
											
												
													
														|  | 
 |  | +import { DesignerTaskService } from '../../../services/designer-task.service';
 | 
											
												
													
														|  | 
 |  | +import { LeaveService, LeaveApplication } from '../../../services/leave.service';
 | 
											
												
													
														|  | 
 |  | +import { FormsModule } from '@angular/forms';
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  interface ShiftTask {
 |  |  interface ShiftTask {
 | 
											
												
													
														|  |    id: string;
 |  |    id: string;
 | 
											
										
											
												
													
														|  | @@ -28,7 +31,7 @@ interface ProjectTimelineItem {
 | 
											
												
													
														|  |  @Component({
 |  |  @Component({
 | 
											
												
													
														|  |    selector: 'app-dashboard',
 |  |    selector: 'app-dashboard',
 | 
											
												
													
														|  |    standalone: true,
 |  |    standalone: true,
 | 
											
												
													
														|  | -  imports: [CommonModule, RouterModule, SkillRadarComponent, PersonalBoard],
 |  | 
 | 
											
												
													
														|  | 
 |  | +  imports: [CommonModule, RouterModule, FormsModule, SkillRadarComponent, PersonalBoard],
 | 
											
												
													
														|  |    templateUrl: './dashboard.html',
 |  |    templateUrl: './dashboard.html',
 | 
											
												
													
														|  |    styleUrl: './dashboard.scss'
 |  |    styleUrl: './dashboard.scss'
 | 
											
												
													
														|  |  })
 |  |  })
 | 
											
										
											
												
													
														|  | @@ -54,52 +57,122 @@ export class Dashboard implements OnInit {
 | 
											
												
													
														|  |    projectTimeline: ProjectTimelineItem[] = [];
 |  |    projectTimeline: ProjectTimelineItem[] = [];
 | 
											
												
													
														|  |    private wxAuth: WxworkAuth | null = null;
 |  |    private wxAuth: WxworkAuth | null = null;
 | 
											
												
													
														|  |    private currentUser: FmodeUser | null = null;
 |  |    private currentUser: FmodeUser | null = null;
 | 
											
												
													
														|  | 
 |  | +  private currentProfile: FmodeObject | null = null; // 当前Profile
 | 
											
												
													
														|  | 
 |  | +  private cid: string = '';
 | 
											
												
													
														|  | 
 |  | +  
 | 
											
												
													
														|  | 
 |  | +  // 请假相关
 | 
											
												
													
														|  | 
 |  | +  showLeaveModal: boolean = false;
 | 
											
												
													
														|  | 
 |  | +  leaveApplications: LeaveApplication[] = [];
 | 
											
												
													
														|  | 
 |  | +  today: string = new Date().toISOString().split('T')[0];
 | 
											
												
													
														|  | 
 |  | +  
 | 
											
												
													
														|  | 
 |  | +  // 请假表单
 | 
											
												
													
														|  | 
 |  | +  leaveForm = {
 | 
											
												
													
														|  | 
 |  | +    startDate: '',
 | 
											
												
													
														|  | 
 |  | +    endDate: '',
 | 
											
												
													
														|  | 
 |  | +    type: 'personal' as 'annual' | 'sick' | 'personal' | 'other',
 | 
											
												
													
														|  | 
 |  | +    reason: ''
 | 
											
												
													
														|  | 
 |  | +  };
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -  constructor(private projectService: ProjectService) {
 |  | 
 | 
											
												
													
														|  | -    this.initAuth();
 |  | 
 | 
											
												
													
														|  | 
 |  | +  constructor(
 | 
											
												
													
														|  | 
 |  | +    private projectService: ProjectService,
 | 
											
												
													
														|  | 
 |  | +    private route: ActivatedRoute,
 | 
											
												
													
														|  | 
 |  | +    private router: Router,
 | 
											
												
													
														|  | 
 |  | +    private taskService: DesignerTaskService,
 | 
											
												
													
														|  | 
 |  | +    private leaveService: LeaveService
 | 
											
												
													
														|  | 
 |  | +  ) {}
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +  async ngOnInit(): Promise<void> {
 | 
											
												
													
														|  | 
 |  | +    // 1. 从URL获取cid
 | 
											
												
													
														|  | 
 |  | +    this.route.paramMap.subscribe(async params => {
 | 
											
												
													
														|  | 
 |  | +      this.cid = params.get('cid') || localStorage.getItem('company') || '';
 | 
											
												
													
														|  | 
 |  | +      
 | 
											
												
													
														|  | 
 |  | +      if (!this.cid) {
 | 
											
												
													
														|  | 
 |  | +        console.warn('⚠️ 未找到公司ID,尝试使用默认值');
 | 
											
												
													
														|  | 
 |  | +        this.cid = 'cDL6R1hgSi'; // 默认公司ID
 | 
											
												
													
														|  | 
 |  | +      }
 | 
											
												
													
														|  | 
 |  | +      
 | 
											
												
													
														|  | 
 |  | +      // 2. 初始化企微认证
 | 
											
												
													
														|  | 
 |  | +      this.initAuth();
 | 
											
												
													
														|  | 
 |  | +      
 | 
											
												
													
														|  | 
 |  | +      // 3. 执行认证并加载数据
 | 
											
												
													
														|  | 
 |  | +      await this.authenticateAndLoadData();
 | 
											
												
													
														|  | 
 |  | +    });
 | 
											
												
													
														|  |    }
 |  |    }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -  // 初始化企业微信认证
 |  | 
 | 
											
												
													
														|  | 
 |  | +  // 初始化企业微信认证(优化版)
 | 
											
												
													
														|  |    private initAuth(): void {
 |  |    private initAuth(): void {
 | 
											
												
													
														|  |      try {
 |  |      try {
 | 
											
												
													
														|  |        this.wxAuth = new WxworkAuth({
 |  |        this.wxAuth = new WxworkAuth({
 | 
											
												
													
														|  | -        cid: 'cDL6R1hgSi'  // 公司帐套ID
 |  | 
 | 
											
												
													
														|  | 
 |  | +        cid: this.cid,  // 使用动态获取的cid
 | 
											
												
													
														|  | 
 |  | +        appId: 'crm'
 | 
											
												
													
														|  |        });
 |  |        });
 | 
											
												
													
														|  | -      console.log('✅ 设计师仪表板企业微信认证初始化成功');
 |  | 
 | 
											
												
													
														|  | 
 |  | +      console.log('✅ 设计师端企微认证初始化成功,CID:', this.cid);
 | 
											
												
													
														|  |      } catch (error) {
 |  |      } catch (error) {
 | 
											
												
													
														|  | -      console.error('❌ 设计师仪表板企业微信认证初始化失败:', error);
 |  | 
 | 
											
												
													
														|  | 
 |  | +      console.error('❌ 设计师端企微认证初始化失败:', error);
 | 
											
												
													
														|  |      }
 |  |      }
 | 
											
												
													
														|  |    }
 |  |    }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -  async ngOnInit(): Promise<void> {
 |  | 
 | 
											
												
													
														|  | -    await this.authenticateAndLoadData();
 |  | 
 | 
											
												
													
														|  | -  }
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  | -  // 认证并加载数据
 |  | 
 | 
											
												
													
														|  | 
 |  | +  // 认证并加载数据(优化版)
 | 
											
												
													
														|  |    private async authenticateAndLoadData(): Promise<void> {
 |  |    private async authenticateAndLoadData(): Promise<void> {
 | 
											
												
													
														|  |      try {
 |  |      try {
 | 
											
												
													
														|  |        // 执行企业微信认证和登录
 |  |        // 执行企业微信认证和登录
 | 
											
												
													
														|  | -      const { user } = await this.wxAuth!.authenticateAndLogin();
 |  | 
 | 
											
												
													
														|  | 
 |  | +      const { user, profile } = await this.wxAuth!.authenticateAndLogin();
 | 
											
												
													
														|  |        this.currentUser = user;
 |  |        this.currentUser = user;
 | 
											
												
													
														|  | 
 |  | +      this.currentProfile = profile;
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -      if (user) {
 |  | 
 | 
											
												
													
														|  | -        console.log('✅ 设计师登录成功:', user.get('username'));
 |  | 
 | 
											
												
													
														|  | -        await this.loadDashboardData();
 |  | 
 | 
											
												
													
														|  | -      } else {
 |  | 
 | 
											
												
													
														|  | 
 |  | +      if (!user || !profile) {
 | 
											
												
													
														|  |          console.error('❌ 设计师登录失败');
 |  |          console.error('❌ 设计师登录失败');
 | 
											
												
													
														|  | 
 |  | +        this.loadMockData();
 | 
											
												
													
														|  | 
 |  | +        return;
 | 
											
												
													
														|  | 
 |  | +      }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +      console.log('✅ 设计师登录成功:', user.get('username'));
 | 
											
												
													
														|  | 
 |  | +      console.log('✅ Profile ID:', profile.id);
 | 
											
												
													
														|  | 
 |  | +      
 | 
											
												
													
														|  | 
 |  | +      // 验证角色是否为"组员"
 | 
											
												
													
														|  | 
 |  | +      if (!await this.validateDesignerRole()) {
 | 
											
												
													
														|  | 
 |  | +        alert('您不是设计师(组员),无权访问此页面');
 | 
											
												
													
														|  | 
 |  | +        this.router.navigate(['/']);
 | 
											
												
													
														|  | 
 |  | +        return;
 | 
											
												
													
														|  |        }
 |  |        }
 | 
											
												
													
														|  | 
 |  | +      
 | 
											
												
													
														|  | 
 |  | +      // 缓存Profile ID
 | 
											
												
													
														|  | 
 |  | +      localStorage.setItem('Parse/ProfileId', profile.id);
 | 
											
												
													
														|  | 
 |  | +      
 | 
											
												
													
														|  | 
 |  | +      // 加载真实数据
 | 
											
												
													
														|  | 
 |  | +      await this.loadDashboardData();
 | 
											
												
													
														|  | 
 |  | +      
 | 
											
												
													
														|  |      } catch (error) {
 |  |      } catch (error) {
 | 
											
												
													
														|  |        console.error('❌ 设计师认证过程出错:', error);
 |  |        console.error('❌ 设计师认证过程出错:', error);
 | 
											
												
													
														|  |        // 降级到模拟数据
 |  |        // 降级到模拟数据
 | 
											
												
													
														|  |        this.loadMockData();
 |  |        this.loadMockData();
 | 
											
												
													
														|  |      }
 |  |      }
 | 
											
												
													
														|  |    }
 |  |    }
 | 
											
												
													
														|  | 
 |  | +  
 | 
											
												
													
														|  | 
 |  | +  /**
 | 
											
												
													
														|  | 
 |  | +   * 验证设计师(组员)角色
 | 
											
												
													
														|  | 
 |  | +   */
 | 
											
												
													
														|  | 
 |  | +  private async validateDesignerRole(): Promise<boolean> {
 | 
											
												
													
														|  | 
 |  | +    if (!this.currentProfile) {
 | 
											
												
													
														|  | 
 |  | +      return false;
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +    
 | 
											
												
													
														|  | 
 |  | +    const roleName = this.currentProfile.get('roleName');
 | 
											
												
													
														|  | 
 |  | +    
 | 
											
												
													
														|  | 
 |  | +    if (roleName !== '组员') {
 | 
											
												
													
														|  | 
 |  | +      console.warn(`⚠️ 用户角色为"${roleName}",不是"组员"`);
 | 
											
												
													
														|  | 
 |  | +      return false;
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +    
 | 
											
												
													
														|  | 
 |  | +    console.log('✅ 角色验证通过:组员(设计师)');
 | 
											
												
													
														|  | 
 |  | +    return true;
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |    // 加载仪表板数据
 |  |    // 加载仪表板数据
 | 
											
												
													
														|  |    private async loadDashboardData(): Promise<void> {
 |  |    private async loadDashboardData(): Promise<void> {
 | 
											
												
													
														|  |      try {
 |  |      try {
 | 
											
												
													
														|  |        await Promise.all([
 |  |        await Promise.all([
 | 
											
												
													
														|  | -        this.loadTasks(),
 |  | 
 | 
											
												
													
														|  | 
 |  | +        this.loadRealTasks(),      // 使用真实数据
 | 
											
												
													
														|  |          this.loadShiftTasks(),
 |  |          this.loadShiftTasks(),
 | 
											
												
													
														|  |          this.calculateWorkloadPercentage(),
 |  |          this.calculateWorkloadPercentage(),
 | 
											
												
													
														|  |          this.loadProjectTimeline()
 |  |          this.loadProjectTimeline()
 | 
											
										
											
												
													
														|  | @@ -111,6 +184,127 @@ export class Dashboard implements OnInit {
 | 
											
												
													
														|  |      }
 |  |      }
 | 
											
												
													
														|  |    }
 |  |    }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | 
 |  | +  /**
 | 
											
												
													
														|  | 
 |  | +   * 加载真实任务数据
 | 
											
												
													
														|  | 
 |  | +   */
 | 
											
												
													
														|  | 
 |  | +  private async loadRealTasks(): Promise<void> {
 | 
											
												
													
														|  | 
 |  | +    try {
 | 
											
												
													
														|  | 
 |  | +      if (!this.currentProfile) {
 | 
											
												
													
														|  | 
 |  | +        throw new Error('未找到当前Profile');
 | 
											
												
													
														|  | 
 |  | +      }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +      const designerTasks = await this.taskService.getMyTasks(this.currentProfile.id);
 | 
											
												
													
														|  | 
 |  | +      
 | 
											
												
													
														|  | 
 |  | +      // 转换为组件所需格式
 | 
											
												
													
														|  | 
 |  | +      this.tasks = designerTasks.map(task => ({
 | 
											
												
													
														|  | 
 |  | +        id: task.id,
 | 
											
												
													
														|  | 
 |  | +        projectId: task.projectId,
 | 
											
												
													
														|  | 
 |  | +        projectName: task.projectName,
 | 
											
												
													
														|  | 
 |  | +        title: task.projectName,
 | 
											
												
													
														|  | 
 |  | +        stage: task.stage,
 | 
											
												
													
														|  | 
 |  | +        deadline: task.deadline,
 | 
											
												
													
														|  | 
 |  | +        isOverdue: task.isOverdue,
 | 
											
												
													
														|  | 
 |  | +        priority: task.priority,
 | 
											
												
													
														|  | 
 |  | +        isCompleted: false,
 | 
											
												
													
														|  | 
 |  | +        assignee: null,
 | 
											
												
													
														|  | 
 |  | +        description: `${task.stage} - ${task.customerName}`
 | 
											
												
													
														|  | 
 |  | +      })) as Task[];
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +      // 筛选超期任务
 | 
											
												
													
														|  | 
 |  | +      this.overdueTasks = this.tasks.filter(task => task.isOverdue);
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +      // 筛选紧急任务
 | 
											
												
													
														|  | 
 |  | +      this.urgentTasks = this.tasks.filter(task => {
 | 
											
												
													
														|  | 
 |  | +        const now = new Date();
 | 
											
												
													
														|  | 
 |  | +        const diffHours = (task.deadline.getTime() - now.getTime()) / (1000 * 60 * 60);
 | 
											
												
													
														|  | 
 |  | +        return diffHours <= 3 && diffHours > 0 && task.stage === '渲染';
 | 
											
												
													
														|  | 
 |  | +      });
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +      // 设置反馈项目ID
 | 
											
												
													
														|  | 
 |  | +      if (this.overdueTasks.length > 0) {
 | 
											
												
													
														|  | 
 |  | +        this.feedbackProjectId = this.overdueTasks[0].projectId;
 | 
											
												
													
														|  | 
 |  | +      }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +      // 加载待处理反馈
 | 
											
												
													
														|  | 
 |  | +      await this.loadRealPendingFeedbacks();
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +      // 启动倒计时
 | 
											
												
													
														|  | 
 |  | +      this.startCountdowns();
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +      console.log(`✅ 成功加载 ${this.tasks.length} 个真实任务`);
 | 
											
												
													
														|  | 
 |  | +    } catch (error) {
 | 
											
												
													
														|  | 
 |  | +      console.error('❌ 加载真实任务失败:', error);
 | 
											
												
													
														|  | 
 |  | +      throw error;
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +  
 | 
											
												
													
														|  | 
 |  | +  /**
 | 
											
												
													
														|  | 
 |  | +   * 加载真实待处理反馈
 | 
											
												
													
														|  | 
 |  | +   */
 | 
											
												
													
														|  | 
 |  | +  private async loadRealPendingFeedbacks(): Promise<void> {
 | 
											
												
													
														|  | 
 |  | +    try {
 | 
											
												
													
														|  | 
 |  | +      const Parse = await import('fmode-ng/parse').then(m => m.FmodeParse.with('nova'));
 | 
											
												
													
														|  | 
 |  | +      
 | 
											
												
													
														|  | 
 |  | +      // 查询该设计师相关项目的待处理反馈
 | 
											
												
													
														|  | 
 |  | +      const projectIds = this.tasks.map(t => t.projectId);
 | 
											
												
													
														|  | 
 |  | +      
 | 
											
												
													
														|  | 
 |  | +      if (projectIds.length === 0) {
 | 
											
												
													
														|  | 
 |  | +        this.pendingFeedbacks = [];
 | 
											
												
													
														|  | 
 |  | +        return;
 | 
											
												
													
														|  | 
 |  | +      }
 | 
											
												
													
														|  | 
 |  | +      
 | 
											
												
													
														|  | 
 |  | +      const query = new Parse.Query('ProjectFeedback');
 | 
											
												
													
														|  | 
 |  | +      const ProjectClass = Parse.Object.extend('Project');
 | 
											
												
													
														|  | 
 |  | +      query.containedIn('project', projectIds.map(id => {
 | 
											
												
													
														|  | 
 |  | +        const obj = new ProjectClass();
 | 
											
												
													
														|  | 
 |  | +        obj.id = id;
 | 
											
												
													
														|  | 
 |  | +        return obj;
 | 
											
												
													
														|  | 
 |  | +      }));
 | 
											
												
													
														|  | 
 |  | +      query.equalTo('status', '待处理');
 | 
											
												
													
														|  | 
 |  | +      query.notEqualTo('isDeleted', true);
 | 
											
												
													
														|  | 
 |  | +      query.include('project');
 | 
											
												
													
														|  | 
 |  | +      query.include('contact');
 | 
											
												
													
														|  | 
 |  | +      query.descending('createdAt');
 | 
											
												
													
														|  | 
 |  | +      query.limit(100);
 | 
											
												
													
														|  | 
 |  | +      
 | 
											
												
													
														|  | 
 |  | +      const feedbacks = await query.find();
 | 
											
												
													
														|  | 
 |  | +      
 | 
											
												
													
														|  | 
 |  | +      this.pendingFeedbacks = feedbacks.map((feedback: any) => {
 | 
											
												
													
														|  | 
 |  | +        const project = feedback.get('project');
 | 
											
												
													
														|  | 
 |  | +        const task = this.tasks.find(t => t.projectId === project?.id);
 | 
											
												
													
														|  | 
 |  | +        
 | 
											
												
													
														|  | 
 |  | +        const fallbackTask: Task = {
 | 
											
												
													
														|  | 
 |  | +          id: project?.id || '',
 | 
											
												
													
														|  | 
 |  | +          projectId: project?.id || '',
 | 
											
												
													
														|  | 
 |  | +          projectName: project?.get('title') || '未知项目',
 | 
											
												
													
														|  | 
 |  | +          title: project?.get('title') || '未知项目',
 | 
											
												
													
														|  | 
 |  | +          stage: '投诉处理',
 | 
											
												
													
														|  | 
 |  | +          deadline: new Date(),
 | 
											
												
													
														|  | 
 |  | +          isOverdue: false,
 | 
											
												
													
														|  | 
 |  | +          isCompleted: false,
 | 
											
												
													
														|  | 
 |  | +          priority: 'high',
 | 
											
												
													
														|  | 
 |  | +          assignee: null,
 | 
											
												
													
														|  | 
 |  | +          description: '待处理反馈'
 | 
											
												
													
														|  | 
 |  | +        };
 | 
											
												
													
														|  | 
 |  | +        
 | 
											
												
													
														|  | 
 |  | +        return {
 | 
											
												
													
														|  | 
 |  | +          task: task || fallbackTask,
 | 
											
												
													
														|  | 
 |  | +          feedback: {
 | 
											
												
													
														|  | 
 |  | +            id: feedback.id,
 | 
											
												
													
														|  | 
 |  | +            content: feedback.get('content') || '',
 | 
											
												
													
														|  | 
 |  | +            rating: feedback.get('rating'),
 | 
											
												
													
														|  | 
 |  | +            createdAt: feedback.get('createdAt')
 | 
											
												
													
														|  | 
 |  | +          }
 | 
											
												
													
														|  | 
 |  | +        };
 | 
											
												
													
														|  | 
 |  | +      });
 | 
											
												
													
														|  | 
 |  | +      
 | 
											
												
													
														|  | 
 |  | +      console.log(`✅ 成功加载 ${this.pendingFeedbacks.length} 个待处理反馈`);
 | 
											
												
													
														|  | 
 |  | +    } catch (error) {
 | 
											
												
													
														|  | 
 |  | +      console.error('❌ 加载待处理反馈失败:', error);
 | 
											
												
													
														|  | 
 |  | +      this.pendingFeedbacks = [];
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  |    // 降级到模拟数据
 |  |    // 降级到模拟数据
 | 
											
												
													
														|  |    private loadMockData(): void {
 |  |    private loadMockData(): void {
 | 
											
												
													
														|  |      console.warn('⚠️ 使用模拟数据');
 |  |      console.warn('⚠️ 使用模拟数据');
 | 
											
										
											
												
													
														|  | @@ -511,5 +705,135 @@ export class Dashboard implements OnInit {
 | 
											
												
													
														|  |        }
 |  |        }
 | 
											
												
													
														|  |      ].sort((a, b) => new Date(a.deadline).getTime() - new Date(b.deadline).getTime());
 |  |      ].sort((a, b) => new Date(a.deadline).getTime() - new Date(b.deadline).getTime());
 | 
											
												
													
														|  |    }
 |  |    }
 | 
											
												
													
														|  | 
 |  | +  
 | 
											
												
													
														|  | 
 |  | +  /**
 | 
											
												
													
														|  | 
 |  | +   * 打开请假申请弹窗
 | 
											
												
													
														|  | 
 |  | +   */
 | 
											
												
													
														|  | 
 |  | +  openLeaveModal(): void {
 | 
											
												
													
														|  | 
 |  | +    this.showLeaveModal = true;
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +  /**
 | 
											
												
													
														|  | 
 |  | +   * 关闭请假申请弹窗
 | 
											
												
													
														|  | 
 |  | +   */
 | 
											
												
													
														|  | 
 |  | +  closeLeaveModal(): void {
 | 
											
												
													
														|  | 
 |  | +    this.showLeaveModal = false;
 | 
											
												
													
														|  | 
 |  | +    this.resetLeaveForm();
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +  /**
 | 
											
												
													
														|  | 
 |  | +   * 提交请假申请
 | 
											
												
													
														|  | 
 |  | +   */
 | 
											
												
													
														|  | 
 |  | +  async submitLeaveApplication(): Promise<void> {
 | 
											
												
													
														|  | 
 |  | +    if (!this.currentProfile) {
 | 
											
												
													
														|  | 
 |  | +      alert('未找到当前用户信息');
 | 
											
												
													
														|  | 
 |  | +      return;
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    // 验证表单
 | 
											
												
													
														|  | 
 |  | +    if (!this.leaveForm.startDate || !this.leaveForm.endDate) {
 | 
											
												
													
														|  | 
 |  | +      alert('请选择请假日期');
 | 
											
												
													
														|  | 
 |  | +      return;
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    if (!this.leaveForm.reason.trim()) {
 | 
											
												
													
														|  | 
 |  | +      alert('请输入请假原因');
 | 
											
												
													
														|  | 
 |  | +      return;
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    const startDate = new Date(this.leaveForm.startDate);
 | 
											
												
													
														|  | 
 |  | +    const endDate = new Date(this.leaveForm.endDate);
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    if (startDate > endDate) {
 | 
											
												
													
														|  | 
 |  | +      alert('结束日期不能早于开始日期');
 | 
											
												
													
														|  | 
 |  | +      return;
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    // 计算请假天数
 | 
											
												
													
														|  | 
 |  | +    const days = this.leaveService.calculateLeaveDays(startDate, endDate);
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    if (days === 0) {
 | 
											
												
													
														|  | 
 |  | +      alert('请假天数必须大于0(周末不计入)');
 | 
											
												
													
														|  | 
 |  | +      return;
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    try {
 | 
											
												
													
														|  | 
 |  | +      const success = await this.leaveService.submitLeaveApplication(
 | 
											
												
													
														|  | 
 |  | +        this.currentProfile.id,
 | 
											
												
													
														|  | 
 |  | +        {
 | 
											
												
													
														|  | 
 |  | +          startDate,
 | 
											
												
													
														|  | 
 |  | +          endDate,
 | 
											
												
													
														|  | 
 |  | +          type: this.leaveForm.type,
 | 
											
												
													
														|  | 
 |  | +          reason: this.leaveForm.reason,
 | 
											
												
													
														|  | 
 |  | +          days
 | 
											
												
													
														|  | 
 |  | +        }
 | 
											
												
													
														|  | 
 |  | +      );
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +      if (success) {
 | 
											
												
													
														|  | 
 |  | +        alert(`请假申请已提交!共${days}天(已排除周末)`);
 | 
											
												
													
														|  | 
 |  | +        this.closeLeaveModal();
 | 
											
												
													
														|  | 
 |  | +        await this.loadMyLeaveRecords();
 | 
											
												
													
														|  | 
 |  | +      } else {
 | 
											
												
													
														|  | 
 |  | +        alert('请假申请提交失败,请重试');
 | 
											
												
													
														|  | 
 |  | +      }
 | 
											
												
													
														|  | 
 |  | +    } catch (error) {
 | 
											
												
													
														|  | 
 |  | +      console.error('提交请假申请失败:', error);
 | 
											
												
													
														|  | 
 |  | +      alert('请假申请提交失败,请重试');
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +  /**
 | 
											
												
													
														|  | 
 |  | +   * 加载我的请假记录
 | 
											
												
													
														|  | 
 |  | +   */
 | 
											
												
													
														|  | 
 |  | +  private async loadMyLeaveRecords(): Promise<void> {
 | 
											
												
													
														|  | 
 |  | +    if (!this.currentProfile) return;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    try {
 | 
											
												
													
														|  | 
 |  | +      this.leaveApplications = await this.leaveService.getMyLeaveRecords(
 | 
											
												
													
														|  | 
 |  | +        this.currentProfile.id
 | 
											
												
													
														|  | 
 |  | +      );
 | 
											
												
													
														|  | 
 |  | +      console.log(`✅ 成功加载 ${this.leaveApplications.length} 条请假记录`);
 | 
											
												
													
														|  | 
 |  | +    } catch (error) {
 | 
											
												
													
														|  | 
 |  | +      console.error('❌ 加载请假记录失败:', error);
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +  /**
 | 
											
												
													
														|  | 
 |  | +   * 重置请假表单
 | 
											
												
													
														|  | 
 |  | +   */
 | 
											
												
													
														|  | 
 |  | +  private resetLeaveForm(): void {
 | 
											
												
													
														|  | 
 |  | +    this.leaveForm = {
 | 
											
												
													
														|  | 
 |  | +      startDate: '',
 | 
											
												
													
														|  | 
 |  | +      endDate: '',
 | 
											
												
													
														|  | 
 |  | +      type: 'personal',
 | 
											
												
													
														|  | 
 |  | +      reason: ''
 | 
											
												
													
														|  | 
 |  | +    };
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +  
 | 
											
												
													
														|  | 
 |  | +  /**
 | 
											
												
													
														|  | 
 |  | +   * 获取请假类型文本
 | 
											
												
													
														|  | 
 |  | +   */
 | 
											
												
													
														|  | 
 |  | +  getLeaveTypeText(type: string): string {
 | 
											
												
													
														|  | 
 |  | +    const typeMap: Record<string, string> = {
 | 
											
												
													
														|  | 
 |  | +      'annual': '年假',
 | 
											
												
													
														|  | 
 |  | +      'sick': '病假',
 | 
											
												
													
														|  | 
 |  | +      'personal': '事假',
 | 
											
												
													
														|  | 
 |  | +      'other': '其他'
 | 
											
												
													
														|  | 
 |  | +    };
 | 
											
												
													
														|  | 
 |  | +    return typeMap[type] || type;
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +  /**
 | 
											
												
													
														|  | 
 |  | +   * 获取请假状态文本
 | 
											
												
													
														|  | 
 |  | +   */
 | 
											
												
													
														|  | 
 |  | +  getLeaveStatusText(status: string): string {
 | 
											
												
													
														|  | 
 |  | +    const statusMap: Record<string, string> = {
 | 
											
												
													
														|  | 
 |  | +      'pending': '待审批',
 | 
											
												
													
														|  | 
 |  | +      'approved': '已批准',
 | 
											
												
													
														|  | 
 |  | +      'rejected': '已拒绝'
 | 
											
												
													
														|  | 
 |  | +    };
 | 
											
												
													
														|  | 
 |  | +    return statusMap[status] || status;
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  }
 |  |  }
 | 
											
												
													
														|  | 
 |  | +
 |