project-kanban.component.ts 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
  2. import { CommonModule } from '@angular/common';
  3. import { FormsModule } from '@angular/forms';
  4. import { Project } from '../../interfaces';
  5. @Component({
  6. selector: 'app-project-kanban',
  7. standalone: true,
  8. imports: [CommonModule, FormsModule],
  9. templateUrl: './project-kanban.component.html',
  10. styleUrls: ['./project-kanban.component.scss'],
  11. changeDetection: ChangeDetectionStrategy.OnPush
  12. })
  13. export class ProjectKanbanComponent {
  14. @Input() corePhases: any[] = [];
  15. @Input() projects: Project[] = [];
  16. // Outputs for actions that need to be handled by the parent
  17. @Output() viewProject = new EventEmitter<{projectId: string, phaseId: string}>();
  18. @Output() openSmartMatch = new EventEmitter<Project>();
  19. @Output() assignProject = new EventEmitter<string>(); // projectId
  20. @Output() reviewProject = new EventEmitter<{projectId: string, rating: 'excellent' | 'qualified' | 'unqualified'}>();
  21. @Output() markStalled = new EventEmitter<Project>(); // 🆕 标记停滞
  22. @Output() markModification = new EventEmitter<Project>(); // 🆕 标记改图
  23. @Output() cancelStalled = new EventEmitter<Project>(); // 🆕 取消停滞
  24. @Output() cancelModification = new EventEmitter<Project>(); // 🆕 取消改图
  25. getProjectCountByCorePhase(coreId: string): number {
  26. return this.getProjectsByCorePhase(coreId).length;
  27. }
  28. getProjectsByCorePhase(coreId: string): Project[] {
  29. if (!this.projects) {
  30. console.log(`📋 [看板-${coreId}] projects 输入为空`);
  31. return [];
  32. }
  33. console.log(`📋 [看板-${coreId}] 开始筛选,输入项目数: ${this.projects.length}`);
  34. const result = this.projects.filter(p => {
  35. // 🆕 优先判断是否被标记为停滞或改图
  36. if (coreId === 'stalled') {
  37. const match = p.isStalled === true;
  38. if (match) {
  39. console.log(`📋 [看板-停滞期] 匹配项目: ${p.name}, isStalled=${p.isStalled}`);
  40. }
  41. return match;
  42. }
  43. if (coreId === 'modification') {
  44. const match = p.isModification === true;
  45. if (match) {
  46. console.log(`📋 [看板-改图期] 匹配项目: ${p.name}, isModification=${p.isModification}`);
  47. }
  48. return match;
  49. }
  50. // 如果被标记为停滞或改图,不应该出现在其他常规列中
  51. if (p.isStalled || p.isModification) {
  52. return false;
  53. }
  54. // 否则,根据 currentStage 映射到常规核心阶段
  55. return this.mapStageToCorePhase(p.currentStage) === coreId;
  56. });
  57. console.log(`📋 [看板-${coreId}] 筛选结果: ${result.length} 个项目`);
  58. if (coreId === 'stalled' || coreId === 'modification') {
  59. result.forEach((p, i) => {
  60. console.log(` ${i + 1}. ${p.name} (isStalled=${p.isStalled}, isModification=${p.isModification})`);
  61. });
  62. }
  63. return result;
  64. }
  65. private mapStageToCorePhase(stageId: string): 'order' | 'requirements' | 'delivery' | 'aftercare' {
  66. if (!stageId) return 'order';
  67. const normalizedStage = stageId.trim().toLowerCase();
  68. // 1. 订单分配阶段
  69. if (normalizedStage === 'order' ||
  70. normalizedStage === 'pendingapproval' ||
  71. normalizedStage === 'pendingassignment' ||
  72. normalizedStage === '订单分配' ||
  73. normalizedStage === '待审批' ||
  74. normalizedStage === '待分配') {
  75. return 'order';
  76. }
  77. // 2. 确认需求阶段
  78. if (normalizedStage === 'requirements' ||
  79. normalizedStage === 'requirement' ||
  80. normalizedStage === 'planning' ||
  81. normalizedStage === '确认需求' ||
  82. normalizedStage === '需求沟通' ||
  83. normalizedStage === '方案规划') {
  84. return 'requirements';
  85. }
  86. // 3. 交付执行阶段
  87. if (normalizedStage === 'delivery' ||
  88. normalizedStage === 'modeling' ||
  89. normalizedStage === 'rendering' ||
  90. normalizedStage === 'postproduction' ||
  91. normalizedStage === 'review' ||
  92. normalizedStage === 'revision' ||
  93. normalizedStage === '交付执行' ||
  94. normalizedStage === '建模' ||
  95. normalizedStage === '建模阶段' ||
  96. normalizedStage === '渲染' ||
  97. normalizedStage === '渲染阶段' ||
  98. normalizedStage === '后期制作' ||
  99. normalizedStage === '评审' ||
  100. normalizedStage === '修改' ||
  101. normalizedStage === '修订' ||
  102. normalizedStage === '白模' ||
  103. normalizedStage === '软装' ||
  104. normalizedStage === '后期') {
  105. return 'delivery';
  106. }
  107. // 4. 售后归档阶段
  108. if (normalizedStage === 'aftercare' ||
  109. normalizedStage === 'completed' ||
  110. normalizedStage === 'archived' ||
  111. normalizedStage === '售后归档' ||
  112. normalizedStage === '售后' ||
  113. normalizedStage === '归档' ||
  114. normalizedStage === '已完成' ||
  115. normalizedStage === '已交付') {
  116. return 'aftercare';
  117. }
  118. return 'delivery';
  119. }
  120. isPendingApproval(project: Project): boolean {
  121. return project.currentStage === 'pendingApproval' ||
  122. (project.currentStage === 'review' && project.status === 'warning');
  123. }
  124. getUrgencyLabel(urgency: string): string {
  125. const labels: Record<string, string> = {
  126. 'high': '高',
  127. 'medium': '中',
  128. 'low': '低'
  129. };
  130. return labels[urgency] || '无';
  131. }
  132. onViewProject(projectId: string, phaseId: string, event?: Event): void {
  133. if (event) {
  134. event.stopPropagation();
  135. }
  136. this.viewProject.emit({ projectId, phaseId });
  137. }
  138. onOpenSmartMatch(project: Project, event?: Event): void {
  139. if (event) {
  140. event.stopPropagation();
  141. }
  142. this.openSmartMatch.emit(project);
  143. }
  144. onAssignProject(projectId: string, event?: Event): void {
  145. if (event) {
  146. event.stopPropagation();
  147. }
  148. this.assignProject.emit(projectId);
  149. }
  150. onReviewProject(projectId: string, rating: 'excellent' | 'qualified' | 'unqualified', event?: Event): void {
  151. if (event) {
  152. event.stopPropagation();
  153. }
  154. this.reviewProject.emit({ projectId, rating });
  155. }
  156. onMarkStalled(project: Project, event?: Event): void {
  157. if (event) {
  158. event.stopPropagation();
  159. }
  160. this.markStalled.emit(project);
  161. }
  162. onMarkModification(project: Project, event?: Event): void {
  163. if (event) {
  164. event.stopPropagation();
  165. }
  166. this.markModification.emit(project);
  167. }
  168. onCancelStalled(project: Project, event?: Event): void {
  169. if (event) {
  170. event.stopPropagation();
  171. }
  172. if (confirm(`确定要取消项目【${project.name}】的停滞期状态吗?`)) {
  173. this.cancelStalled.emit(project);
  174. }
  175. }
  176. onCancelModification(project: Project, event?: Event): void {
  177. if (event) {
  178. event.stopPropagation();
  179. }
  180. if (confirm(`确定要取消项目【${project.name}】的改图期状态吗?`)) {
  181. this.cancelModification.emit(project);
  182. }
  183. }
  184. }