|
|
@@ -1,7 +1,7 @@
|
|
|
import { CommonModule } from '@angular/common';
|
|
|
import { FormsModule } from '@angular/forms';
|
|
|
import { Router, RouterModule } from '@angular/router';
|
|
|
-import { Component, OnInit, OnDestroy, ElementRef, ViewChild } from '@angular/core';
|
|
|
+import { Component, OnInit, OnDestroy, ElementRef, ViewChild, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
|
|
|
import { ProjectService } from '../../../services/project.service';
|
|
|
import { DesignerService } from '../services/designer.service';
|
|
|
import { WxworkAuth } from 'fmode-ng/core';
|
|
|
@@ -90,7 +90,7 @@ interface UrgentEvent {
|
|
|
id: string;
|
|
|
title: string;
|
|
|
description: string;
|
|
|
- eventType: 'review' | 'delivery' | 'phase_deadline'; // 事件类型
|
|
|
+ eventType: 'review' | 'delivery' | 'phase_deadline' | 'customer_alert'; // 事件类型
|
|
|
phaseName?: string; // 阶段名称(如果是阶段截止)
|
|
|
deadline: Date; // 截止时间
|
|
|
projectId: string;
|
|
|
@@ -99,6 +99,16 @@ interface UrgentEvent {
|
|
|
urgencyLevel: 'critical' | 'high' | 'medium'; // 紧急程度
|
|
|
overdueDays?: number; // 逾期天数(负数表示还有几天)
|
|
|
completionRate?: number; // 完成率(0-100)
|
|
|
+ category?: 'customer' | 'phase' | 'review' | 'delivery';
|
|
|
+ statusType?: 'dueSoon' | 'overdue' | 'stagnant';
|
|
|
+ followUpNeeded?: boolean;
|
|
|
+ allowConfirmOnTime?: boolean;
|
|
|
+ allowMarkHandled?: boolean;
|
|
|
+ allowCreateTodo?: boolean;
|
|
|
+ stagnationDays?: number;
|
|
|
+ customerIssueType?: 'feedback_pending' | 'complaint' | 'idle';
|
|
|
+ labels?: string[];
|
|
|
+ isMuted?: boolean;
|
|
|
}
|
|
|
|
|
|
// 员工请假记录接口
|
|
|
@@ -147,7 +157,8 @@ declare const echarts: any;
|
|
|
standalone: true,
|
|
|
imports: [CommonModule, FormsModule, RouterModule, ProjectTimelineComponent, EmployeeDetailPanelComponent],
|
|
|
templateUrl: './dashboard.html',
|
|
|
- styleUrl: './dashboard.scss'
|
|
|
+ styleUrl: './dashboard.scss',
|
|
|
+ changeDetection: ChangeDetectionStrategy.OnPush
|
|
|
})
|
|
|
|
|
|
export class Dashboard implements OnInit, OnDestroy {
|
|
|
@@ -170,8 +181,14 @@ export class Dashboard implements OnInit, OnDestroy {
|
|
|
// 🆕 紧急事件(从项目时间轴自动计算)
|
|
|
urgentEvents: UrgentEvent[] = [];
|
|
|
loadingUrgentEvents: boolean = false;
|
|
|
+ handledUrgentEventIds: Set<string> = new Set();
|
|
|
+ mutedUrgentEventIds: Set<string> = new Set();
|
|
|
+ filteredUrgentEventsList: UrgentEvent[] = [];
|
|
|
urgentEventTagFilter: 'all' | 'customer' | 'phase' | 'review' | 'delivery' = 'all';
|
|
|
|
|
|
+ // 🔥 性能优化:预计算各标签的事件列表(O(1) 切换)
|
|
|
+ private urgentEventsCache = new Map<string, UrgentEvent[]>();
|
|
|
+
|
|
|
// 新增:当前用户信息
|
|
|
currentUser = {
|
|
|
name: '组长',
|
|
|
@@ -305,11 +322,13 @@ export class Dashboard implements OnInit, OnDestroy {
|
|
|
{ id: '27', employeeName: '赵六', date: '2024-01-25', isLeave: false },
|
|
|
{ id: '28', employeeName: '赵六', date: '2024-01-26', isLeave: false }
|
|
|
];
|
|
|
+
|
|
|
constructor(
|
|
|
private projectService: ProjectService,
|
|
|
private router: Router,
|
|
|
private designerService: DesignerService,
|
|
|
- private issueService: ProjectIssueService
|
|
|
+ private issueService: ProjectIssueService,
|
|
|
+ private cdr: ChangeDetectorRef
|
|
|
) {}
|
|
|
|
|
|
async ngOnInit(): Promise<void> {
|
|
|
@@ -3928,6 +3947,60 @@ export class Dashboard implements OnInit, OnDestroy {
|
|
|
const events: UrgentEvent[] = [];
|
|
|
const now = new Date();
|
|
|
const oneDayMs = 24 * 60 * 60 * 1000;
|
|
|
+ const handledSet = this.handledUrgentEventIds;
|
|
|
+ const mutedSet = this.mutedUrgentEventIds;
|
|
|
+ const projectEventCount = new Map<string, number>(); // 追踪每个项目生成的事件数
|
|
|
+ const MAX_EVENTS_PER_PROJECT = 2; // 每个项目最多生成2个最紧急的事件
|
|
|
+
|
|
|
+ const resolveCategory = (
|
|
|
+ eventType: UrgentEvent['eventType'],
|
|
|
+ category?: 'customer' | 'phase' | 'review' | 'delivery'
|
|
|
+ ): 'customer' | 'phase' | 'review' | 'delivery' => {
|
|
|
+ if (category) return category;
|
|
|
+ switch (eventType) {
|
|
|
+ case 'phase_deadline':
|
|
|
+ return 'phase';
|
|
|
+ case 'delivery':
|
|
|
+ return 'delivery';
|
|
|
+ case 'customer_alert':
|
|
|
+ return 'customer';
|
|
|
+ default:
|
|
|
+ return 'review';
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const addEvent = (
|
|
|
+ partial: Omit<UrgentEvent, 'category' | 'statusType' | 'labels' | 'allowConfirmOnTime' | 'allowMarkHandled' | 'allowCreateTodo' | 'followUpNeeded'> &
|
|
|
+ Partial<UrgentEvent>
|
|
|
+ ) => {
|
|
|
+ // 检查该项目是否已达到事件数量上限
|
|
|
+ const currentCount = projectEventCount.get(partial.projectId) || 0;
|
|
|
+ if (currentCount >= MAX_EVENTS_PER_PROJECT) {
|
|
|
+ return; // 跳过,避免单个项目产生过多事件
|
|
|
+ }
|
|
|
+
|
|
|
+ const category = resolveCategory(partial.eventType, partial.category);
|
|
|
+ const statusType: UrgentEvent['statusType'] =
|
|
|
+ partial.statusType || (partial.overdueDays && partial.overdueDays > 0 ? 'overdue' : 'dueSoon');
|
|
|
+
|
|
|
+ // 简化描述,避免过长字符串
|
|
|
+ const description = partial.description?.substring(0, 100) || '';
|
|
|
+
|
|
|
+ const event: UrgentEvent = {
|
|
|
+ ...partial,
|
|
|
+ description,
|
|
|
+ category,
|
|
|
+ statusType,
|
|
|
+ labels: partial.labels ?? [],
|
|
|
+ followUpNeeded: partial.followUpNeeded ?? false,
|
|
|
+ allowConfirmOnTime:
|
|
|
+ partial.allowConfirmOnTime ?? (category !== 'customer' && statusType === 'dueSoon'),
|
|
|
+ allowMarkHandled: partial.allowMarkHandled ?? true,
|
|
|
+ allowCreateTodo: partial.allowCreateTodo ?? category === 'customer'
|
|
|
+ };
|
|
|
+ events.push(event);
|
|
|
+ projectEventCount.set(partial.projectId, currentCount + 1);
|
|
|
+ };
|
|
|
|
|
|
try {
|
|
|
// 从 projectTimelineData 中提取数据
|
|
|
@@ -3940,7 +4013,7 @@ export class Dashboard implements OnInit, OnDestroy {
|
|
|
|
|
|
// 如果小图对图已经到期或即将到期(1天内),且不在已完成状态
|
|
|
if (daysDiff <= 1 && project.currentStage !== 'delivery') {
|
|
|
- events.push({
|
|
|
+ addEvent({
|
|
|
id: `${project.projectId}-review`,
|
|
|
title: `小图对图截止`,
|
|
|
description: `项目「${project.projectName}」的小图对图时间已${daysDiff < 0 ? '逾期' : '临近'}`,
|
|
|
@@ -3950,7 +4023,9 @@ export class Dashboard implements OnInit, OnDestroy {
|
|
|
projectName: project.projectName,
|
|
|
designerName: project.designerName,
|
|
|
urgencyLevel: daysDiff < 0 ? 'critical' : daysDiff === 0 ? 'high' : 'medium',
|
|
|
- overdueDays: -daysDiff
|
|
|
+ overdueDays: -daysDiff,
|
|
|
+ labels: daysDiff < 0 ? ['逾期'] : ['临近'],
|
|
|
+ followUpNeeded: (project.stageName || '').includes('图') || project.status === 'warning'
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
@@ -3966,7 +4041,7 @@ export class Dashboard implements OnInit, OnDestroy {
|
|
|
const summary = project.spaceDeliverableSummary;
|
|
|
const completionRate = summary?.overallCompletionRate || 0;
|
|
|
|
|
|
- events.push({
|
|
|
+ addEvent({
|
|
|
id: `${project.projectId}-delivery`,
|
|
|
title: `项目交付截止`,
|
|
|
description: `项目「${project.projectName}」需要在 ${project.deliveryDate.toLocaleDateString()} 交付(当前完成率 ${completionRate}%)`,
|
|
|
@@ -3977,7 +4052,8 @@ export class Dashboard implements OnInit, OnDestroy {
|
|
|
designerName: project.designerName,
|
|
|
urgencyLevel: daysDiff < 0 ? 'critical' : daysDiff === 0 ? 'high' : 'medium',
|
|
|
overdueDays: -daysDiff,
|
|
|
- completionRate
|
|
|
+ completionRate,
|
|
|
+ labels: daysDiff < 0 ? ['逾期'] : ['临近']
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
@@ -4010,7 +4086,7 @@ export class Dashboard implements OnInit, OnDestroy {
|
|
|
completionRate = phaseProgress?.completionRate || 0;
|
|
|
}
|
|
|
|
|
|
- events.push({
|
|
|
+ addEvent({
|
|
|
id: `${project.projectId}-phase-${key}`,
|
|
|
title: `${phaseName}阶段截止`,
|
|
|
description: `项目「${project.projectName}」的${phaseName}阶段截止时间已${daysDiff < 0 ? '逾期' : '临近'}(完成率 ${completionRate}%)`,
|
|
|
@@ -4022,12 +4098,75 @@ export class Dashboard implements OnInit, OnDestroy {
|
|
|
designerName: project.designerName,
|
|
|
urgencyLevel: daysDiff < 0 ? 'critical' : daysDiff === 0 ? 'high' : 'medium',
|
|
|
overdueDays: -daysDiff,
|
|
|
- completionRate
|
|
|
+ completionRate,
|
|
|
+ labels: daysDiff < 0 ? ['逾期'] : ['临近']
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
+
|
|
|
+ if (project.stalledDays && project.stalledDays >= 7) {
|
|
|
+ addEvent({
|
|
|
+ id: `${project.projectId}-stagnant`,
|
|
|
+ title: project.stalledDays >= 14 ? '客户停滞预警' : '停滞期提醒',
|
|
|
+ description: `项目「${project.projectName}」已有 ${project.stalledDays} 天未收到客户反馈,请主动跟进。`,
|
|
|
+ eventType: 'customer_alert',
|
|
|
+ deadline: new Date(),
|
|
|
+ projectId: project.projectId,
|
|
|
+ projectName: project.projectName,
|
|
|
+ designerName: project.designerName,
|
|
|
+ urgencyLevel: project.stalledDays >= 14 ? 'critical' : 'high',
|
|
|
+ statusType: 'stagnant',
|
|
|
+ stagnationDays: project.stalledDays,
|
|
|
+ labels: ['停滞期'],
|
|
|
+ followUpNeeded: true,
|
|
|
+ allowCreateTodo: true,
|
|
|
+ allowConfirmOnTime: false,
|
|
|
+ category: 'customer'
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ const inReviewStage = (project.stageName || '').includes('图') || (project.currentStage || '').includes('图');
|
|
|
+ if (inReviewStage && project.status === 'warning') {
|
|
|
+ addEvent({
|
|
|
+ id: `${project.projectId}-review-followup`,
|
|
|
+ title: '对图反馈待跟进',
|
|
|
+ description: `项目「${project.projectName}」客户反馈尚未处理,请尽快跟进。`,
|
|
|
+ eventType: 'customer_alert',
|
|
|
+ deadline: project.reviewDate || new Date(),
|
|
|
+ projectId: project.projectId,
|
|
|
+ projectName: project.projectName,
|
|
|
+ designerName: project.designerName,
|
|
|
+ urgencyLevel: 'high',
|
|
|
+ statusType: project.reviewDate && project.reviewDate < now ? 'overdue' : 'dueSoon',
|
|
|
+ labels: ['对图期'],
|
|
|
+ followUpNeeded: true,
|
|
|
+ allowCreateTodo: true,
|
|
|
+ customerIssueType: 'feedback_pending',
|
|
|
+ category: 'customer'
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ if (project.priority === 'critical') {
|
|
|
+ addEvent({
|
|
|
+ id: `${project.projectId}-customer-alert`,
|
|
|
+ title: '客户服务预警',
|
|
|
+ description: `项目「${project.projectName}」存在客户不满或抱怨,需要立即处理并记录。`,
|
|
|
+ eventType: 'customer_alert',
|
|
|
+ deadline: new Date(),
|
|
|
+ projectId: project.projectId,
|
|
|
+ projectName: project.projectName,
|
|
|
+ designerName: project.designerName,
|
|
|
+ urgencyLevel: 'critical',
|
|
|
+ statusType: 'dueSoon',
|
|
|
+ labels: ['客户预警'],
|
|
|
+ followUpNeeded: true,
|
|
|
+ allowCreateTodo: true,
|
|
|
+ customerIssueType: 'complaint',
|
|
|
+ category: 'customer'
|
|
|
+ });
|
|
|
+ }
|
|
|
});
|
|
|
|
|
|
// 按紧急程度和时间排序
|
|
|
@@ -4041,61 +4180,202 @@ export class Dashboard implements OnInit, OnDestroy {
|
|
|
return a.deadline.getTime() - b.deadline.getTime();
|
|
|
});
|
|
|
|
|
|
- this.urgentEvents = events;
|
|
|
- console.log(`✅ 计算紧急事件完成,共 ${events.length} 个紧急事件`);
|
|
|
+ // 过滤已处理和静音的事件
|
|
|
+ const filteredEvents = events.filter(event => !handledSet.has(event.id) && !mutedSet.has(event.id));
|
|
|
+
|
|
|
+ // 🔥 限制最大显示数量,避免渲染过多 DOM 导致卡顿和 RangeError
|
|
|
+ const MAX_URGENT_EVENTS = 50;
|
|
|
+ this.urgentEvents = filteredEvents.slice(0, MAX_URGENT_EVENTS);
|
|
|
+
|
|
|
+ if (filteredEvents.length > MAX_URGENT_EVENTS) {
|
|
|
+ console.warn(`⚠️ 紧急事件过多(${filteredEvents.length}个),已限制为前 ${MAX_URGENT_EVENTS} 个最紧急的事件`);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 🔥 性能关键:预计算所有标签的列表,缓存起来
|
|
|
+ this.precalculateTagCaches();
|
|
|
+
|
|
|
+ // 初始化显示列表
|
|
|
+ this.updateFilteredUrgentEvents();
|
|
|
+ console.log(`✅ 计算紧急事件完成,共 ${this.urgentEvents.length} 个紧急事件(原始${events.length}个)`);
|
|
|
|
|
|
} catch (error) {
|
|
|
console.error('❌ 计算紧急事件失败:', error);
|
|
|
+ // 发生错误时清空列表,避免渲染异常数据
|
|
|
+ this.urgentEvents = [];
|
|
|
+ this.filteredUrgentEventsList = [];
|
|
|
} finally {
|
|
|
this.loadingUrgentEvents = false;
|
|
|
+ // 确保触发变更检测
|
|
|
+ this.cdr.markForCheck();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 🆕 待办事项标签筛选
|
|
|
+ * 🔥 预计算所有标签的事件列表(一次计算,多次使用)
|
|
|
+ */
|
|
|
+ private precalculateTagCaches(): void {
|
|
|
+ const MAX_PER_TAG = 30;
|
|
|
+
|
|
|
+ // 清空旧缓存
|
|
|
+ this.urgentEventsCache.clear();
|
|
|
+
|
|
|
+ // 缓存"全部"标签
|
|
|
+ this.urgentEventsCache.set('all', this.urgentEvents.slice(0, MAX_PER_TAG));
|
|
|
+
|
|
|
+ // 按类别分组并缓存
|
|
|
+ const categories: Array<'customer' | 'phase' | 'review' | 'delivery'> = ['customer', 'phase', 'review', 'delivery'];
|
|
|
+
|
|
|
+ for (const category of categories) {
|
|
|
+ const filtered = this.urgentEvents
|
|
|
+ .filter(event => this.getEventCategory(event) === category)
|
|
|
+ .slice(0, MAX_PER_TAG);
|
|
|
+ this.urgentEventsCache.set(category, filtered);
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log(`✅ 标签缓存已预计算: ${Array.from(this.urgentEventsCache.keys()).map(k => `${k}(${this.urgentEventsCache.get(k)?.length})`).join(', ')}`);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 🆕 待办事项标签筛选(O(1) 操作,从缓存直接取)
|
|
|
*/
|
|
|
filterUrgentEventsByTag(tag: 'all' | 'customer' | 'phase' | 'review' | 'delivery'): void {
|
|
|
this.urgentEventTagFilter = tag;
|
|
|
+
|
|
|
+ // 🔥 直接从缓存取出预计算的列表(O(1) 操作)
|
|
|
+ const cached = this.urgentEventsCache.get(tag);
|
|
|
+ if (cached) {
|
|
|
+ this.filteredUrgentEventsList = cached;
|
|
|
+ } else {
|
|
|
+ // 降级:如果缓存不存在,使用旧逻辑
|
|
|
+ this.updateFilteredUrgentEvents();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 🔥 使用 requestAnimationFrame 在浏览器下一帧更新,确保流畅
|
|
|
+ requestAnimationFrame(() => {
|
|
|
+ this.cdr.markForCheck();
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ confirmEventOnTime(event: UrgentEvent): void {
|
|
|
+ this.mutedUrgentEventIds.add(event.id);
|
|
|
+ // 立即从当前视图移除
|
|
|
+ this.urgentEvents = this.urgentEvents.filter(e => e.id !== event.id);
|
|
|
+ // 重新计算缓存
|
|
|
+ this.precalculateTagCaches();
|
|
|
+ this.updateFilteredUrgentEvents();
|
|
|
+ this.cdr.markForCheck();
|
|
|
+ }
|
|
|
+
|
|
|
+ resolveUrgentEvent(event: UrgentEvent): void {
|
|
|
+ this.handledUrgentEventIds.add(event.id);
|
|
|
+ // 立即从当前视图移除
|
|
|
+ this.urgentEvents = this.urgentEvents.filter(e => e.id !== event.id);
|
|
|
+ // 重新计算缓存
|
|
|
+ this.precalculateTagCaches();
|
|
|
+ this.updateFilteredUrgentEvents();
|
|
|
+ this.cdr.markForCheck();
|
|
|
+ }
|
|
|
+
|
|
|
+ markEventAsStagnant(event: UrgentEvent): void {
|
|
|
+ this.urgentEvents = this.urgentEvents.map(item => {
|
|
|
+ if (item.id !== event.id) {
|
|
|
+ return item;
|
|
|
+ }
|
|
|
+ const labels = new Set(item.labels || []);
|
|
|
+ labels.add('停滞期');
|
|
|
+ return {
|
|
|
+ ...item,
|
|
|
+ category: 'customer' as const,
|
|
|
+ statusType: 'stagnant' as const,
|
|
|
+ stagnationDays: item.stagnationDays || 7,
|
|
|
+ labels: Array.from(labels),
|
|
|
+ followUpNeeded: true
|
|
|
+ };
|
|
|
+ });
|
|
|
+ // 重新计算缓存
|
|
|
+ this.precalculateTagCaches();
|
|
|
+ this.updateFilteredUrgentEvents();
|
|
|
+ this.cdr.markForCheck();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 追踪函数:优化 ngFor 的渲染性能
|
|
|
+ */
|
|
|
+ trackUrgentEventById(index: number, event: UrgentEvent): string {
|
|
|
+ return event.id;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从紧急事件创建待办
|
|
|
+ */
|
|
|
+ createTodoFromEvent(event: UrgentEvent): void {
|
|
|
+ const now = new Date();
|
|
|
+ const newTask: TodoTaskFromIssue = {
|
|
|
+ id: `urgent-todo-${event.id}-${now.getTime()}`,
|
|
|
+ title: `【紧急】${event.title}`,
|
|
|
+ description: event.description,
|
|
|
+ priority: event.urgencyLevel === 'critical' ? 'urgent' : event.urgencyLevel === 'high' ? 'high' : 'medium',
|
|
|
+ type: 'feedback',
|
|
|
+ status: 'open',
|
|
|
+ projectId: event.projectId,
|
|
|
+ projectName: event.projectName,
|
|
|
+ relatedStage: event.phaseName,
|
|
|
+ assigneeName: event.designerName || '待分配',
|
|
|
+ creatorName: this.currentUser.name,
|
|
|
+ createdAt: now,
|
|
|
+ updatedAt: now,
|
|
|
+ dueDate: event.deadline,
|
|
|
+ tags: [...(event.labels || []), '来自紧急事件']
|
|
|
+ };
|
|
|
+ this.todoTasksFromIssues = [newTask, ...this.todoTasksFromIssues];
|
|
|
+ this.resolveUrgentEvent(event);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 获取指定标签的事件数量
|
|
|
*/
|
|
|
getTagCount(tagId: 'customer' | 'phase' | 'review' | 'delivery'): number {
|
|
|
- switch (tagId) {
|
|
|
- case 'customer':
|
|
|
- case 'review':
|
|
|
- return this.urgentEvents.filter(event => event.eventType === 'review').length;
|
|
|
- case 'phase':
|
|
|
- return this.urgentEvents.filter(event => event.eventType === 'phase_deadline').length;
|
|
|
+ return this.urgentEvents.filter(event => this.getEventCategory(event) === tagId).length;
|
|
|
+ }
|
|
|
+
|
|
|
+ getEventCategory(event: UrgentEvent): 'customer' | 'phase' | 'review' | 'delivery' {
|
|
|
+ if (event.category) return event.category;
|
|
|
+ switch (event.eventType) {
|
|
|
+ case 'phase_deadline':
|
|
|
+ return 'phase';
|
|
|
case 'delivery':
|
|
|
- return this.urgentEvents.filter(event => event.eventType === 'delivery').length;
|
|
|
+ return 'delivery';
|
|
|
+ case 'customer_alert':
|
|
|
+ return 'customer';
|
|
|
default:
|
|
|
- return 0;
|
|
|
+ return 'review';
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 计算筛选后的紧急事件列表
|
|
|
+ * 更新筛选后的紧急事件列表(优先从缓存读取)
|
|
|
*/
|
|
|
- get filteredUrgentEvents(): UrgentEvent[] {
|
|
|
- if (this.urgentEventTagFilter === 'all') {
|
|
|
- return this.urgentEvents;
|
|
|
- }
|
|
|
-
|
|
|
- if (this.urgentEventTagFilter === 'customer' || this.urgentEventTagFilter === 'review') {
|
|
|
- return this.urgentEvents.filter(event => event.eventType === 'review');
|
|
|
- }
|
|
|
-
|
|
|
- if (this.urgentEventTagFilter === 'phase') {
|
|
|
- return this.urgentEvents.filter(event => event.eventType === 'phase_deadline');
|
|
|
- }
|
|
|
-
|
|
|
- if (this.urgentEventTagFilter === 'delivery') {
|
|
|
- return this.urgentEvents.filter(event => event.eventType === 'delivery');
|
|
|
+ private updateFilteredUrgentEvents(): void {
|
|
|
+ try {
|
|
|
+ // 🔥 优先从缓存读取
|
|
|
+ const cached = this.urgentEventsCache.get(this.urgentEventTagFilter);
|
|
|
+ if (cached) {
|
|
|
+ this.filteredUrgentEventsList = cached;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 降级:缓存不存在时的旧逻辑
|
|
|
+ if (this.urgentEventTagFilter === 'all') {
|
|
|
+ this.filteredUrgentEventsList = this.urgentEvents.slice(0, 30);
|
|
|
+ } else {
|
|
|
+ this.filteredUrgentEventsList = this.urgentEvents
|
|
|
+ .filter(event => this.getEventCategory(event) === this.urgentEventTagFilter)
|
|
|
+ .slice(0, 30);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('❌ 更新筛选列表失败:', error);
|
|
|
+ this.filteredUrgentEventsList = [];
|
|
|
}
|
|
|
-
|
|
|
- return this.urgentEvents;
|
|
|
}
|
|
|
|
|
|
/**
|