|
|
@@ -78,6 +78,7 @@ export class EmployeeDetailPanelComponent implements OnInit, OnChanges {
|
|
|
|
|
|
// 组件内部状态
|
|
|
internalEmployeeDetail: EmployeeDetail | null = null; // 内部生成的详情
|
|
|
+ private cachedAllProjects: any[] = []; // 🔥 缓存加载的所有项目(含历史)
|
|
|
showFullSurvey: boolean = false;
|
|
|
refreshingSurvey: boolean = false;
|
|
|
|
|
|
@@ -199,8 +200,12 @@ export class EmployeeDetailPanelComponent implements OnInit, OnChanges {
|
|
|
// 生成红色标记说明
|
|
|
const redMarkExplanation = this.generateRedMarkExplanation(employeeName, employeeLeaveRecords, currentProjects);
|
|
|
|
|
|
+ // 🔥 加载该设计师的所有项目(包括历史项目)用于日历显示
|
|
|
+ const allDesignerProjects = await this.loadAllDesignerProjects(employeeName);
|
|
|
+ this.cachedAllProjects = allDesignerProjects; // 缓存用于月份切换
|
|
|
+
|
|
|
// 生成日历数据 (传入所有项目以显示历史负载)
|
|
|
- const calendarData = this.generateEmployeeCalendar(employeeName, this.projects);
|
|
|
+ const calendarData = this.generateEmployeeCalendar(employeeName, allDesignerProjects);
|
|
|
|
|
|
// 构建基础对象
|
|
|
this.internalEmployeeDetail = {
|
|
|
@@ -220,6 +225,104 @@ export class EmployeeDetailPanelComponent implements OnInit, OnChanges {
|
|
|
this.cdr.markForCheck();
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 加载设计师的所有项目(包括历史已完成项目)
|
|
|
+ * 用于日历显示历史负载
|
|
|
+ */
|
|
|
+ private async loadAllDesignerProjects(employeeName: string): Promise<any[]> {
|
|
|
+ try {
|
|
|
+ const Parse = await import('fmode-ng/parse').then(m => m.FmodeParse.with('nova'));
|
|
|
+ const cid = localStorage.getItem('company');
|
|
|
+
|
|
|
+ if (!cid) {
|
|
|
+ console.warn('⚠️ 未找到公司ID,使用传入的项目数据');
|
|
|
+ return this.projects;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 1. 先通过员工名字查找 Profile
|
|
|
+ const realnameQuery = new Parse.Query('Profile');
|
|
|
+ realnameQuery.equalTo('realname', employeeName);
|
|
|
+
|
|
|
+ const nameQuery = new Parse.Query('Profile');
|
|
|
+ nameQuery.equalTo('name', employeeName);
|
|
|
+
|
|
|
+ const profileQuery = Parse.Query.or(realnameQuery, nameQuery);
|
|
|
+ profileQuery.equalTo('company', cid);
|
|
|
+ profileQuery.limit(1);
|
|
|
+
|
|
|
+ const profileResults = await profileQuery.find();
|
|
|
+
|
|
|
+ if (profileResults.length === 0) {
|
|
|
+ console.warn(`⚠️ 未找到设计师 ${employeeName} 的 Profile,使用传入的项目数据`);
|
|
|
+ return this.projects;
|
|
|
+ }
|
|
|
+
|
|
|
+ const profile = profileResults[0];
|
|
|
+
|
|
|
+ // 2. 查询该设计师参与的所有项目(通过 ProjectTeam 表)
|
|
|
+ // 不过滤已完成状态,以便显示历史项目
|
|
|
+ const projectQuery = new Parse.Query('Project');
|
|
|
+ projectQuery.equalTo('company', cid);
|
|
|
+ projectQuery.notEqualTo('isDeleted', true);
|
|
|
+
|
|
|
+ const teamQuery = new Parse.Query('ProjectTeam');
|
|
|
+ teamQuery.equalTo('profile', profile.toPointer());
|
|
|
+ teamQuery.notEqualTo('isDeleted', true);
|
|
|
+ teamQuery.matchesQuery('project', projectQuery);
|
|
|
+ teamQuery.include('project');
|
|
|
+ // 🔥 关键:限制查询范围为最近6个月的项目,避免数据量过大
|
|
|
+ const sixMonthsAgo = new Date();
|
|
|
+ sixMonthsAgo.setMonth(sixMonthsAgo.getMonth() - 6);
|
|
|
+ teamQuery.greaterThan('createdAt', sixMonthsAgo);
|
|
|
+ teamQuery.limit(200);
|
|
|
+
|
|
|
+ const teamRecords = await teamQuery.find();
|
|
|
+
|
|
|
+ console.log(`📊 加载设计师 ${employeeName} 的历史项目: ${teamRecords.length} 条记录`);
|
|
|
+
|
|
|
+ // 3. 提取项目数据
|
|
|
+ const allProjects: any[] = [];
|
|
|
+ const projectIds = new Set<string>(); // 用于去重
|
|
|
+
|
|
|
+ teamRecords.forEach((record: any) => {
|
|
|
+ const project = record.get('project');
|
|
|
+ if (!project || projectIds.has(project.id)) return;
|
|
|
+
|
|
|
+ projectIds.add(project.id);
|
|
|
+
|
|
|
+ const projectData = project.get('data') || {};
|
|
|
+
|
|
|
+ allProjects.push({
|
|
|
+ id: project.id,
|
|
|
+ name: project.get('title') || '未命名项目',
|
|
|
+ status: project.get('status') || '进行中',
|
|
|
+ currentStage: project.get('currentStage') || '未知阶段',
|
|
|
+ deadline: project.get('deadline') || project.get('deliveryDate') || project.get('expectedDeliveryDate'),
|
|
|
+ createdAt: project.get('createdAt') || project.createdAt,
|
|
|
+ updatedAt: project.get('updatedAt') || project.updatedAt,
|
|
|
+ data: projectData,
|
|
|
+ designerName: employeeName
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ // 4. 合并传入的项目数据(可能包含一些未通过 ProjectTeam 关联的项目)
|
|
|
+ this.projects.forEach(p => {
|
|
|
+ if (!projectIds.has(p.id)) {
|
|
|
+ allProjects.push(p);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ console.log(`✅ 设计师 ${employeeName} 共有 ${allProjects.length} 个项目(含历史)`);
|
|
|
+
|
|
|
+ return allProjects;
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ console.error(`❌ 加载设计师 ${employeeName} 历史项目失败:`, error);
|
|
|
+ // 降级:使用传入的项目数据
|
|
|
+ return this.projects;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 加载问卷数据
|
|
|
*/
|
|
|
@@ -432,6 +535,7 @@ export class EmployeeDetailPanelComponent implements OnInit, OnChanges {
|
|
|
this.close.emit();
|
|
|
this.showFullSurvey = false;
|
|
|
this.closeCalendarProjectList();
|
|
|
+ this.cachedAllProjects = []; // 清理缓存
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -453,10 +557,13 @@ export class EmployeeDetailPanelComponent implements OnInit, OnChanges {
|
|
|
const newMonth = new Date(currentMonth);
|
|
|
newMonth.setMonth(newMonth.getMonth() + direction);
|
|
|
|
|
|
+ // 🔥 使用缓存的完整项目数据(含历史项目)重新生成日历
|
|
|
+ const projectsForCalendar = this.cachedAllProjects.length > 0 ? this.cachedAllProjects : this.projects;
|
|
|
+
|
|
|
// 重新生成日历数据
|
|
|
const newCalendarData = this.generateEmployeeCalendar(
|
|
|
this.employeeName,
|
|
|
- this.projects,
|
|
|
+ projectsForCalendar,
|
|
|
newMonth
|
|
|
);
|
|
|
|