在组长端的紧急事件中标记项目为"停滞"或"改图"后,该项目会在项目监控看板中的"停滞期"或"改图期"列以卡片形式显示,并注明停滞或改图的原因。
dashboard.ts)// 停滞/改图原因弹窗控制
showStagnationModal: boolean = false;
stagnationModalType: 'stagnation' | 'modification' = 'stagnation';
stagnationModalProject: Project | null = null;
标记紧急事件时同步更新项目对象
markEventAsStagnant(): 标记紧急事件为停滞时,调用 updateProjectMarkStatus() 同步更新对应的项目对象markEventAsModification(): 标记紧急事件为改图时,调用 updateProjectMarkStatus() 同步更新对应的项目对象从看板直接标记时弹出原因输入弹窗
markProjectAsStalled(): 弹出停滞原因弹窗markProjectAsModification(): 弹出改图原因弹窗处理原因输入
onStagnationReasonConfirm(): 确认原因后调用 updateProjectMarkStatus() 更新项目closeStagnationModal(): 关闭原因输入弹窗项目状态更新核心方法
private updateProjectMarkStatus(projectId: string, type: 'stagnation' | 'modification', reason: any): void {
// 更新项目的 isStalled/isModification 标志
// 保存原因类型、自定义原因、预计恢复时间、备注等信息
// 重新应用筛选
}
dashboard.html)新增停滞/改图原因弹窗组件:
<app-stagnation-reason-modal
[isOpen]="showStagnationModal"
[eventType]="stagnationModalType"
[projectName]="stagnationModalProject?.name || ''"
(confirm)="onStagnationReasonConfirm($event)"
(cancel)="closeStagnationModal()">
</app-stagnation-reason-modal>
project-kanban.component.ts)修改了 getProjectsByCorePhase() 方法,实现了以下逻辑:
getProjectsByCorePhase(coreId: string): Project[] {
return this.projects.filter(p => {
// 优先判断是否被标记为停滞或改图
if (p.isStalled && coreId === 'stalled') {
return true;
}
if (p.isModification && coreId === 'modification') {
return true;
}
// 如果被标记为停滞或改图,不应该出现在其他常规列中
if (p.isStalled || p.isModification) {
return false;
}
// 否则,根据 currentStage 映射到常规核心阶段
return this.mapStageToCorePhase(p.currentStage) === coreId;
});
}
project-kanban.component.html)卡片中已有完善的原因显示逻辑(第42-77行):
停滞原因显示
@if (project.isStalled && project.stagnationReasonType) {
<div class="reason-label stagnant">
<span class="reason-text">
@if (project.stagnationReasonType === 'designer') { 设计师原因停滞 }
@if (project.stagnationReasonType === 'customer') { 客户原因停滞 }
@if (project.stagnationReasonType === 'custom') { {{ project.stagnationCustomReason }} }
</span>
@if (project.estimatedResumeDate) {
<span class="resume-date">({{ project.estimatedResumeDate | date:'MM-dd' }}恢复)</span>
}
</div>
}
改图原因显示
@if (project.isModification && project.modificationReasonType) {
<div class="reason-label modification">
<span class="reason-text">
@if (project.modificationReasonType === 'customer') { 客户要求改图 }
@if (project.modificationReasonType === 'designer') { 设计师优化 }
@if (project.modificationReasonType === 'custom') { {{ project.modificationCustomReason }} }
</span>
</div>
}
备注显示
@if ((project.isStalled || project.isModification) && project.reasonNotes) {
<div class="reason-notes">
{{ project.reasonNotes }}
</div>
}
project-kanban.component.scss)已有完善的样式(第214-269行):
.reason-label.stagnant: 红色背景,用于停滞原因.reason-label.modification: 黄色背景,用于改图原因.reason-notes: 灰色背景,用于备注dashboard.constants.ts)核心阶段已包含停滞期和改图期:
export const CORE_PHASES: ProjectStage[] = [
{ id: 'order', name: '订单分配', order: 1 },
{ id: 'requirements', name: '确认需求', order: 2 },
{ id: 'delivery', name: '交付执行', order: 3 },
{ id: 'stalled', name: '停滞期', order: 3.5 }, // 停滞期
{ id: 'modification', name: '改图期', order: 3.8 }, // 改图期
{ id: 'aftercare', name: '售后', order: 4 }
];
StagnationReasonModalComponent 原因输入弹窗markEventAsStagnant() 或 markEventAsModification()updateProjectMarkStatus() 更新项目对象的以下字段:
isStalled / isModificationstagnationReasonType / modificationReasonTypestagnationCustomReason / modificationCustomReasonestimatedResumeDate(仅停滞)reasonNotesmarkedAt, markedBymarkProjectAsStalled() 或 markProjectAsModification()StagnationReasonModalComponent 原因输入弹窗onStagnationReasonConfirm()updateProjectMarkStatus() 更新项目对象Project 接口中包含以下相关字段(在 interfaces.ts 中定义):
export interface Project {
// ... 其他字段
isStalled?: boolean; // 是否停滞
isModification?: boolean; // 是否改图
stagnationReasonType?: 'designer' | 'customer' | 'custom';
stagnationCustomReason?: string;
modificationReasonType?: 'designer' | 'customer' | 'custom';
modificationCustomReason?: string;
estimatedResumeDate?: Date; // 预计恢复时间(仅停滞)
reasonNotes?: string; // 备注说明
markedAt?: Date; // 标记时间
markedBy?: string; // 标记人
}
标记操作
原因选择
看板显示
saveEventMarkToDatabase() 方法预留了持久化接口,需要后续实现真正的数据库保存逻辑saveEventMarkToDatabase() 方法,将标记信息保存到 Parse 数据库