|
|
@@ -0,0 +1,217 @@
|
|
|
+# 停滞期和改图期功能实现说明
|
|
|
+
|
|
|
+## 功能概述
|
|
|
+在组长端的紧急事件中标记项目为"停滞"或"改图"后,该项目会在项目监控看板中的"停滞期"或"改图期"列以卡片形式显示,并注明停滞或改图的原因。
|
|
|
+
|
|
|
+## 实现的关键修改
|
|
|
+
|
|
|
+### 1. Dashboard主组件 (`dashboard.ts`)
|
|
|
+
|
|
|
+#### 新增变量
|
|
|
+```typescript
|
|
|
+// 停滞/改图原因弹窗控制
|
|
|
+showStagnationModal: boolean = false;
|
|
|
+stagnationModalType: 'stagnation' | 'modification' = 'stagnation';
|
|
|
+stagnationModalProject: Project | null = null;
|
|
|
+```
|
|
|
+
|
|
|
+#### 核心方法
|
|
|
+
|
|
|
+**标记紧急事件时同步更新项目对象**
|
|
|
+- `markEventAsStagnant()`: 标记紧急事件为停滞时,调用 `updateProjectMarkStatus()` 同步更新对应的项目对象
|
|
|
+- `markEventAsModification()`: 标记紧急事件为改图时,调用 `updateProjectMarkStatus()` 同步更新对应的项目对象
|
|
|
+
|
|
|
+**从看板直接标记时弹出原因输入弹窗**
|
|
|
+- `markProjectAsStalled()`: 弹出停滞原因弹窗
|
|
|
+- `markProjectAsModification()`: 弹出改图原因弹窗
|
|
|
+
|
|
|
+**处理原因输入**
|
|
|
+- `onStagnationReasonConfirm()`: 确认原因后调用 `updateProjectMarkStatus()` 更新项目
|
|
|
+- `closeStagnationModal()`: 关闭原因输入弹窗
|
|
|
+
|
|
|
+**项目状态更新核心方法**
|
|
|
+```typescript
|
|
|
+private updateProjectMarkStatus(projectId: string, type: 'stagnation' | 'modification', reason: any): void {
|
|
|
+ // 更新项目的 isStalled/isModification 标志
|
|
|
+ // 保存原因类型、自定义原因、预计恢复时间、备注等信息
|
|
|
+ // 重新应用筛选
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 2. Dashboard HTML (`dashboard.html`)
|
|
|
+
|
|
|
+新增停滞/改图原因弹窗组件:
|
|
|
+```html
|
|
|
+<app-stagnation-reason-modal
|
|
|
+ [isOpen]="showStagnationModal"
|
|
|
+ [eventType]="stagnationModalType"
|
|
|
+ [projectName]="stagnationModalProject?.name || ''"
|
|
|
+ (confirm)="onStagnationReasonConfirm($event)"
|
|
|
+ (cancel)="closeStagnationModal()">
|
|
|
+</app-stagnation-reason-modal>
|
|
|
+```
|
|
|
+
|
|
|
+### 3. 项目看板组件 (`project-kanban.component.ts`)
|
|
|
+
|
|
|
+修改了 `getProjectsByCorePhase()` 方法,实现了以下逻辑:
|
|
|
+
|
|
|
+```typescript
|
|
|
+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;
|
|
|
+ });
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 4. 看板卡片显示 (`project-kanban.component.html`)
|
|
|
+
|
|
|
+卡片中已有完善的原因显示逻辑(第42-77行):
|
|
|
+
|
|
|
+**停滞原因显示**
|
|
|
+```html
|
|
|
+@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>
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**改图原因显示**
|
|
|
+```html
|
|
|
+@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>
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**备注显示**
|
|
|
+```html
|
|
|
+@if ((project.isStalled || project.isModification) && project.reasonNotes) {
|
|
|
+ <div class="reason-notes">
|
|
|
+ {{ project.reasonNotes }}
|
|
|
+ </div>
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 5. 样式 (`project-kanban.component.scss`)
|
|
|
+
|
|
|
+已有完善的样式(第214-269行):
|
|
|
+- `.reason-label.stagnant`: 红色背景,用于停滞原因
|
|
|
+- `.reason-label.modification`: 黄色背景,用于改图原因
|
|
|
+- `.reason-notes`: 灰色背景,用于备注
|
|
|
+
|
|
|
+### 6. 常量定义 (`dashboard.constants.ts`)
|
|
|
+
|
|
|
+核心阶段已包含停滞期和改图期:
|
|
|
+```typescript
|
|
|
+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 }
|
|
|
+];
|
|
|
+```
|
|
|
+
|
|
|
+## 数据流
|
|
|
+
|
|
|
+### 从紧急事件标记
|
|
|
+1. 用户在"紧急事件"中点击"标记为停滞/改图"
|
|
|
+2. 弹出 `StagnationReasonModalComponent` 原因输入弹窗
|
|
|
+3. 用户填写原因信息并确认
|
|
|
+4. 触发 `markEventAsStagnant()` 或 `markEventAsModification()`
|
|
|
+5. 调用 `updateProjectMarkStatus()` 更新项目对象的以下字段:
|
|
|
+ - `isStalled` / `isModification`
|
|
|
+ - `stagnationReasonType` / `modificationReasonType`
|
|
|
+ - `stagnationCustomReason` / `modificationCustomReason`
|
|
|
+ - `estimatedResumeDate`(仅停滞)
|
|
|
+ - `reasonNotes`
|
|
|
+ - `markedAt`, `markedBy`
|
|
|
+6. 重新应用筛选,项目出现在对应的看板列中
|
|
|
+
|
|
|
+### 从看板直接标记
|
|
|
+1. 用户在项目卡片上点击"⏸️"(停滞)或"✏️"(改图)按钮
|
|
|
+2. 触发 `markProjectAsStalled()` 或 `markProjectAsModification()`
|
|
|
+3. 弹出 `StagnationReasonModalComponent` 原因输入弹窗
|
|
|
+4. 用户填写原因信息并确认
|
|
|
+5. 触发 `onStagnationReasonConfirm()`
|
|
|
+6. 直接调用 `updateProjectMarkStatus()` 更新项目对象
|
|
|
+7. 重新应用筛选,项目出现在对应的看板列中
|
|
|
+
|
|
|
+## 项目数据模型
|
|
|
+
|
|
|
+`Project` 接口中包含以下相关字段(在 `interfaces.ts` 中定义):
|
|
|
+
|
|
|
+```typescript
|
|
|
+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; // 标记人
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+## 用户体验流程
|
|
|
+
|
|
|
+1. **标记操作**
|
|
|
+ - 从紧急事件标记:点击"标记为停滞/改图" → 弹窗填写原因 → 确认
|
|
|
+ - 从看板标记:点击卡片上的按钮 → 弹窗填写原因 → 确认
|
|
|
+
|
|
|
+2. **原因选择**
|
|
|
+ - 停滞原因:设计师原因 / 客户原因 / 自定义
|
|
|
+ - 改图原因:客户要求 / 设计师优化 / 自定义
|
|
|
+ - 可选填写:预计恢复时间(停滞)、备注说明
|
|
|
+
|
|
|
+3. **看板显示**
|
|
|
+ - 项目自动移动到"停滞期"或"改图期"列
|
|
|
+ - 卡片显示原因标签(红色/黄色)
|
|
|
+ - 显示预计恢复时间(如有)
|
|
|
+ - 显示备注说明(如有)
|
|
|
+
|
|
|
+## 注意事项
|
|
|
+
|
|
|
+1. **互斥关系**:项目不能同时处于停滞期和改图期
|
|
|
+2. **列显示控制**:默认情况下,"停滞期"和"改图期"列始终可见(订单和需求阶段可通过切换隐藏)
|
|
|
+3. **数据持久化**:目前通过 `saveEventMarkToDatabase()` 方法预留了持久化接口,需要后续实现真正的数据库保存逻辑
|
|
|
+
|
|
|
+## 后续优化建议
|
|
|
+
|
|
|
+1. 实现 `saveEventMarkToDatabase()` 方法,将标记信息保存到 Parse 数据库
|
|
|
+2. 在项目加载时从数据库读取停滞/改图标记信息
|
|
|
+3. 添加"取消标记"功能,允许将项目移出停滞期/改图期
|
|
|
+4. 添加停滞/改图历史记录追踪
|
|
|
+5. 在项目详情页显示完整的标记历史
|