徐福静0235668 il y a 14 heures
Parent
commit
933a077f56

+ 16 - 0
src/app/pages/hr/dashboard/dashboard.html

@@ -446,6 +446,22 @@
     </div>
   }
 
+  <!-- 离职原因详情面板 -->
+  @if (showDetailPanel && selectedReason && selectedDetailAnalysis && selectedImprovementPlan) {
+    <div class="detail-panel-overlay">
+      <div class="detail-panel-container">
+        <app-resignation-detail-panel
+          [reason]="selectedReason"
+          [detailAnalysis]="selectedDetailAnalysis"
+          [improvementPlan]="selectedImprovementPlan"
+          (close)="closeDetailPanel()"
+          (export)="exportDetailReport()">
+        </app-resignation-detail-panel>
+      </div>
+    </div>
+  }
+
+
   <!-- 招聘流程优化页面 -->
   @if (activeTab === 'recruitment') {
     <div class="recruitment-page">

+ 98 - 5
src/app/pages/hr/dashboard/dashboard.scss

@@ -3,6 +3,7 @@
   background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
   min-height: 100vh;
   font-family: 'Roboto', sans-serif;
+  font-size: 16px; // 增加全局基础字体大小
 
   .top-navigation {
     margin-bottom: 20px;
@@ -93,13 +94,13 @@
           mat-card-title {
             display: flex;
             align-items: center;
-            font-size: 18px;
+            font-size: 20px;
             font-weight: 600;
             margin: 0;
 
             mat-icon {
               margin-right: 10px;
-              font-size: 24px;
+              font-size: 26px;
             }
           }
 
@@ -538,10 +539,101 @@
               }
             }
           }
-        }
-      }
+       }
+     }
+   }
+
+/* 详情面板覆盖层样式 */
+.detail-panel-overlay {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background: rgba(0, 0, 0, 0.5);
+  z-index: 1000;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 20px;
+
+  .detail-panel-container {
+    width: 90%;
+    max-width: 1200px;
+    height: 90%;
+    max-height: 800px;
+    background: #ffffff;
+    border-radius: 12px;
+    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
+    overflow: hidden;
+    animation: slideIn 0.3s ease-out;
+  }
+}
+
+@keyframes slideIn {
+  from {
+    opacity: 0;
+    transform: scale(0.9) translateY(-20px);
+  }
+  to {
+    opacity: 1;
+    transform: scale(1) translateY(0);
+  }
+}
+}
+
+// 全局下拉面板样式修复
+::ng-deep .mat-mdc-select-panel {
+  background: rgba(255, 255, 255, 0.98) !important;
+  backdrop-filter: blur(10px) !important;
+  border-radius: 12px !important;
+  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15) !important;
+  border: 1px solid rgba(255, 255, 255, 0.2) !important;
+  max-height: 300px !important;
+  
+  .mat-mdc-option {
+    background: transparent !important;
+    color: #333 !important;
+    font-size: 16px !important;
+    padding: 12px 16px !important;
+    border-bottom: 1px solid rgba(0, 0, 0, 0.05) !important;
+    transition: all 0.2s ease !important;
+    
+    &:hover {
+      background: rgba(0, 122, 255, 0.1) !important;
+      color: #007AFF !important;
+    }
+    
+    &.mdc-list-item--selected {
+      background: rgba(0, 122, 255, 0.15) !important;
+      color: #007AFF !important;
+      font-weight: 600 !important;
+    }
+    
+    &:last-child {
+      border-bottom: none !important;
     }
   }
+}
+
+// 修复时间范围选择器在深色背景下的可见性
+::ng-deep .time-range-selector .mat-mdc-select-panel {
+  background: rgba(255, 255, 255, 0.95) !important;
+  
+  .mat-mdc-option {
+    color: #333 !important;
+    
+    &:hover {
+      background: rgba(255, 255, 255, 0.8) !important;
+      color: #007AFF !important;
+    }
+    
+    &.mdc-list-item--selected {
+      background: rgba(0, 122, 255, 0.2) !important;
+      color: #007AFF !important;
+    }
+  }
+}
 
 
 
@@ -1173,8 +1265,9 @@
         .resignation-reasons-analysis {
           display: grid;
           grid-template-columns: 1fr 1fr;
-          gap: 24px;
+          gap: 16px;
           margin-bottom: 32px;
+          padding: 0 8px;
           
           @media (max-width: 1200px) {
             grid-template-columns: 1fr;

+ 230 - 8
src/app/pages/hr/dashboard/dashboard.ts

@@ -20,7 +20,9 @@ import { DragDropModule, CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-
 import { Chart, ChartConfiguration, registerables } from 'chart.js';
 import { trigger, state, style, transition, animate } from '@angular/animations';
 import { DoubaoAiService, ResumeAnalysisRequest, ResumeAnalysisResponse } from '../../../services/doubao-ai.service';
+import { ResignationDetailPanelComponent, DetailAnalysis, ImprovementPlan } from './resignation-detail-panel.component';
 
+Chart.register(...registerables);
 // 数据模型定义
 export interface TodoItem {
   id: number;
@@ -157,7 +159,8 @@ export interface PerformanceMetric {
     MatTableModule,
     MatButtonToggleModule,
     MatProgressSpinnerModule,
-    MatSnackBarModule
+    MatSnackBarModule,
+    ResignationDetailPanelComponent
   ],
   templateUrl: './dashboard.html',
   styleUrls: ['./dashboard.scss'],
@@ -654,17 +657,23 @@ export class Dashboard implements OnInit, AfterViewInit {
     { id: 'senior', name: '高级', count: 7, selected: true }
   ];
   
+  // 详情面板相关
+  showDetailPanel = false;
+  selectedReason: any = null;
+  selectedDetailAnalysis: DetailAnalysis | null = null;
+  selectedImprovementPlan: ImprovementPlan | null = null;
+
   resignationReasons = [
     {
       id: 'salary',
       name: '薪资待遇',
       category: 'compensation',
       categoryName: '薪酬福利',
-      icon: 'attach_money',
+      icon: 'payments',
       percentage: 28.5,
-      count: 13,
+      count: 14,
       description: '薪资水平低于市场平均水平,缺乏有竞争力的薪酬体系',
-      trend: { direction: 'up', value: 5.2 }
+      trend: { direction: 'up', value: 3.2 }
     },
     {
       id: 'career',
@@ -2404,19 +2413,232 @@ export class Dashboard implements OnInit, AfterViewInit {
   viewReasonDetails(reasonId: string): void {
     const reason = this.resignationReasons.find(r => r.id === reasonId);
     if (reason) {
-      console.log('查看离职原因详情:', reason);
-      // 这里可以打开详情弹窗显示更多信息
+      this.selectedReason = reason;
+      this.selectedDetailAnalysis = this.getDetailAnalysis(reasonId);
+      this.selectedImprovementPlan = this.getImprovementPlan(reasonId);
+      this.showDetailPanel = true;
     }
   }
 
   viewImprovementPlan(reasonId: string): void {
     const reason = this.resignationReasons.find(r => r.id === reasonId);
     if (reason) {
-      console.log('查看改进建议:', reason);
-      // 这里可以显示针对该离职原因的改进建议
+      this.selectedReason = reason;
+      this.selectedDetailAnalysis = this.getDetailAnalysis(reasonId);
+      this.selectedImprovementPlan = this.getImprovementPlan(reasonId);
+      this.showDetailPanel = true;
     }
   }
 
+  closeDetailPanel(): void {
+    this.showDetailPanel = false;
+    this.selectedReason = null;
+    this.selectedDetailAnalysis = null;
+    this.selectedImprovementPlan = null;
+  }
+
+  exportDetailReport(): void {
+    if (this.selectedReason) {
+      // 导出详细报告的逻辑
+      this.snackBar.open(`正在导出"${this.selectedReason.name}"的详细报告...`, '关闭', {
+        duration: 3000,
+        horizontalPosition: 'center',
+        verticalPosition: 'top'
+      });
+    }
+  }
+
+  private getDetailAnalysis(reasonId: string): DetailAnalysis {
+    // 根据不同的离职原因返回对应的详细分析数据
+    const analysisData: { [key: string]: DetailAnalysis } = {
+      'salary': {
+        overview: '薪资待遇问题是当前最主要的离职原因,占比28.5%。主要体现在基本薪资偏低、绩效奖金不透明、福利待遇缺乏竞争力等方面。',
+        keyFactors: ['基本薪资偏低', '绩效考核不透明', '福利待遇单一', '薪资调整机制缺失', '市场竞争力不足'],
+        impactAnalysis: {
+          shortTerm: ['优秀员工流失加速', '招聘成本增加', '团队士气下降', '工作效率降低'],
+          longTerm: ['人才竞争力下降', '企业声誉受损', '核心技能流失', '业务发展受阻']
+        },
+        relatedDepartments: ['人力资源部', '财务部', '各业务部门'],
+        timeDistribution: [
+          { period: '第一季度', count: 3, percentage: 21.4 },
+          { period: '第二季度', count: 4, percentage: 28.6 },
+          { period: '第三季度', count: 5, percentage: 35.7 },
+          { period: '第四季度', count: 2, percentage: 14.3 }
+        ]
+      },
+      'career': {
+        overview: '职业发展问题占比22.8%,主要反映在晋升通道不明确、技能培训不足、职业规划缺乏指导等方面。',
+        keyFactors: ['晋升通道狭窄', '培训机会有限', '职业规划缺失', '技能发展停滞', '内部流动性差'],
+        impactAnalysis: {
+          shortTerm: ['员工积极性下降', '学习动力不足', '创新能力减弱'],
+          longTerm: ['组织活力下降', '人才梯队断层', '竞争优势丧失']
+        },
+        relatedDepartments: ['人力资源部', '培训部', '各业务部门'],
+        timeDistribution: [
+          { period: '第一季度', count: 2, percentage: 18.2 },
+          { period: '第二季度', count: 3, percentage: 27.3 },
+          { period: '第三季度', count: 4, percentage: 36.4 },
+          { period: '第四季度', count: 2, percentage: 18.2 }
+        ]
+      },
+      'workload': {
+        overview: '工作压力问题占比18.3%,主要表现为工作量过大、工作时间过长、工作节奏过快等。',
+        keyFactors: ['工作量过大', '加班频繁', '工作节奏快', '压力管理缺失', '工作生活平衡差'],
+        impactAnalysis: {
+          shortTerm: ['员工疲劳度增加', '工作质量下降', '健康问题增多'],
+          longTerm: ['员工流失率上升', '企业形象受损', '可持续发展受阻']
+        },
+        relatedDepartments: ['人力资源部', '运营部', '项目管理部'],
+        timeDistribution: [
+          { period: '第一季度', count: 2, percentage: 22.2 },
+          { period: '第二季度', count: 3, percentage: 33.3 },
+          { period: '第三季度', count: 2, percentage: 22.2 },
+          { period: '第四季度', count: 2, percentage: 22.2 }
+        ]
+      }
+    };
+
+    return analysisData[reasonId] || {
+      overview: '暂无详细分析数据',
+      keyFactors: [],
+      impactAnalysis: { shortTerm: [], longTerm: [] },
+      relatedDepartments: [],
+      timeDistribution: []
+    };
+  }
+
+  private getImprovementPlan(reasonId: string): ImprovementPlan {
+    // 根据不同的离职原因返回对应的改进计划
+    const improvementPlans: { [key: string]: ImprovementPlan } = {
+      'salary': {
+        priority: 'high',
+        timeline: '3-6个月',
+        actions: [
+          {
+            title: '薪酬体系重构',
+            description: '建立科学的薪酬体系,包括基本薪资、绩效奖金、福利待遇等全面优化',
+            responsible: '人力资源部',
+            deadline: '2024-04-30',
+            status: 'in_progress'
+          },
+          {
+            title: '市场薪酬调研',
+            description: '定期进行市场薪酬调研,确保薪酬水平具有市场竞争力',
+            responsible: '人力资源部',
+            deadline: '2024-03-15',
+            status: 'pending'
+          },
+          {
+            title: '绩效考核优化',
+            description: '完善绩效考核体系,建立透明公正的绩效评估机制',
+            responsible: '人力资源部',
+            deadline: '2024-05-31',
+            status: 'pending'
+          }
+        ],
+        expectedOutcome: '通过薪酬体系优化,预计可降低因薪资问题导致的离职率15-20%,提升员工满意度和忠诚度。',
+        successMetrics: [
+          '离职率下降15-20%',
+          '员工满意度调查薪酬满意度提升至80%以上',
+          '关键岗位人才保留率提升至90%以上',
+          '新员工入职率提升10%'
+        ],
+        resources: {
+          budget: '200-300万元',
+          personnel: ['人力资源总监', '薪酬专员', '财务经理', '各部门主管'],
+          tools: ['薪酬管理系统', '绩效考核平台', '市场调研工具']
+        }
+      },
+      'career': {
+        priority: 'high',
+        timeline: '6-12个月',
+        actions: [
+          {
+            title: '职业发展通道设计',
+            description: '建立清晰的职业发展通道,包括技术路线和管理路线',
+            responsible: '人力资源部',
+            deadline: '2024-06-30',
+            status: 'pending'
+          },
+          {
+            title: '培训体系建设',
+            description: '建立完善的培训体系,包括新员工培训、技能提升培训、领导力培训等',
+            responsible: '培训部',
+            deadline: '2024-08-31',
+            status: 'pending'
+          },
+          {
+            title: '导师制度建立',
+            description: '建立导师制度,为员工提供职业发展指导和支持',
+            responsible: '人力资源部',
+            deadline: '2024-05-31',
+            status: 'pending'
+          }
+        ],
+        expectedOutcome: '通过职业发展体系建设,预计可降低因职业发展问题导致的离职率20-25%,提升员工成长满意度。',
+        successMetrics: [
+          '员工职业发展满意度提升至85%以上',
+          '内部晋升比例提升至60%以上',
+          '培训参与率达到95%以上',
+          '关键人才保留率提升至95%以上'
+        ],
+        resources: {
+          budget: '150-200万元',
+          personnel: ['人力资源总监', '培训经理', '各部门主管', '资深员工导师'],
+          tools: ['学习管理系统', '职业发展平台', '在线培训工具']
+        }
+      },
+      'workload': {
+        priority: 'medium',
+        timeline: '3-6个月',
+        actions: [
+          {
+            title: '工作量评估与优化',
+            description: '对各岗位工作量进行科学评估,合理分配工作任务',
+            responsible: '运营部',
+            deadline: '2024-04-30',
+            status: 'pending'
+          },
+          {
+            title: '工作流程优化',
+            description: '优化工作流程,提高工作效率,减少不必要的工作环节',
+            responsible: '项目管理部',
+            deadline: '2024-05-31',
+            status: 'pending'
+          },
+          {
+            title: '压力管理培训',
+            description: '开展压力管理培训,帮助员工更好地应对工作压力',
+            responsible: '人力资源部',
+            deadline: '2024-03-31',
+            status: 'pending'
+          }
+        ],
+        expectedOutcome: '通过工作压力管理优化,预计可降低因工作压力导致的离职率10-15%,提升员工工作满意度。',
+        successMetrics: [
+          '员工工作压力满意度提升至75%以上',
+          '平均加班时间减少20%',
+          '员工健康指标改善',
+          '工作效率提升15%'
+        ],
+        resources: {
+          budget: '50-100万元',
+          personnel: ['运营总监', '项目经理', '人力资源专员', '心理咨询师'],
+          tools: ['工作量管理系统', '项目管理工具', '健康管理平台']
+        }
+      }
+    };
+
+    return improvementPlans[reasonId] || {
+      priority: 'medium',
+      timeline: '待定',
+      actions: [],
+      expectedOutcome: '暂无改进计划',
+      successMetrics: [],
+      resources: { budget: '待评估', personnel: [], tools: [] }
+    };
+  }
+
   getMetricClass(value: string): string {
     const numValue = parseInt(value);
     if (numValue >= 90) return 'metric-excellent';

+ 478 - 0
src/app/pages/hr/dashboard/resignation-detail-panel.component.scss

@@ -0,0 +1,478 @@
+.resignation-detail-panel {
+  display: flex;
+  flex-direction: column;
+  height: 100vh;
+  background: #ffffff;
+  border-radius: 12px;
+  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
+  overflow: hidden;
+
+  .panel-header {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 24px;
+    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+    color: white;
+
+    .header-content {
+      display: flex;
+      align-items: center;
+      gap: 16px;
+
+      .reason-icon {
+        font-size: 32px;
+        width: 32px;
+        height: 32px;
+      }
+
+      .header-text {
+        h2 {
+          margin: 0;
+          font-size: 24px;
+          font-weight: 600;
+        }
+
+        .category-chip {
+          display: inline-block;
+          background: rgba(255, 255, 255, 0.2);
+          padding: 4px 12px;
+          border-radius: 16px;
+          font-size: 12px;
+          margin-top: 4px;
+        }
+      }
+    }
+
+    button {
+      color: white;
+    }
+  }
+
+  .panel-content {
+    flex: 1;
+    overflow: auto;
+
+    ::ng-deep .mat-mdc-tab-group {
+      height: 100%;
+
+      .mat-mdc-tab-header {
+        border-bottom: 1px solid #e0e0e0;
+      }
+
+      .mat-mdc-tab-body-wrapper {
+        flex: 1;
+        overflow: auto;
+      }
+    }
+
+    .tab-content {
+      padding: 24px;
+    }
+
+    .stats-section {
+      display: grid;
+      grid-template-columns: repeat(3, 1fr);
+      gap: 16px;
+      margin-bottom: 32px;
+
+      .stat-card {
+        text-align: center;
+        padding: 20px;
+        background: #f8f9fa;
+        border-radius: 12px;
+        border: 1px solid #e9ecef;
+
+        .stat-value {
+          font-size: 28px;
+          font-weight: 700;
+          color: #2c3e50;
+          margin-bottom: 8px;
+
+          &.trend {
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            gap: 4px;
+
+            &.trend-up {
+              color: #e74c3c;
+            }
+
+            &.trend-down {
+              color: #27ae60;
+            }
+
+            &.trend-stable {
+              color: #f39c12;
+            }
+
+            mat-icon {
+              font-size: 20px;
+              width: 20px;
+              height: 20px;
+            }
+          }
+        }
+
+        .stat-label {
+          font-size: 14px;
+          color: #6c757d;
+          font-weight: 500;
+        }
+      }
+    }
+
+    .analysis-section {
+      h3 {
+        color: #2c3e50;
+        font-size: 18px;
+        font-weight: 600;
+        margin: 24px 0 12px 0;
+        border-bottom: 2px solid #667eea;
+        padding-bottom: 8px;
+      }
+
+      .overview-text {
+        line-height: 1.6;
+        color: #495057;
+        margin-bottom: 24px;
+      }
+
+      .factors-list {
+        margin-bottom: 24px;
+
+        ::ng-deep mat-chip {
+          background: #e3f2fd;
+          color: #1976d2;
+        }
+      }
+
+      .impact-analysis {
+        display: grid;
+        grid-template-columns: 1fr 1fr;
+        gap: 24px;
+        margin-bottom: 24px;
+
+        .impact-section {
+          h4 {
+            color: #495057;
+            font-size: 16px;
+            margin-bottom: 12px;
+          }
+
+          ul {
+            list-style: none;
+            padding: 0;
+
+            li {
+              padding: 8px 0;
+              padding-left: 20px;
+              position: relative;
+              color: #6c757d;
+
+              &::before {
+                content: '•';
+                color: #667eea;
+                font-weight: bold;
+                position: absolute;
+                left: 0;
+              }
+            }
+          }
+        }
+      }
+
+      .time-distribution {
+        .period-item {
+          display: grid;
+          grid-template-columns: 100px 1fr 60px;
+          align-items: center;
+          gap: 16px;
+          margin-bottom: 12px;
+
+          .period-label {
+            font-weight: 500;
+            color: #495057;
+          }
+
+          .period-bar {
+            height: 8px;
+            background: #e9ecef;
+            border-radius: 4px;
+            overflow: hidden;
+
+            .bar-fill {
+              height: 100%;
+              background: linear-gradient(90deg, #667eea, #764ba2);
+              border-radius: 4px;
+            }
+          }
+
+          .period-count {
+            text-align: right;
+            font-weight: 500;
+            color: #6c757d;
+          }
+        }
+      }
+    }
+
+    .improvement-header {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      margin-bottom: 24px;
+      padding: 16px;
+      background: #f8f9fa;
+      border-radius: 8px;
+
+      .priority-badge {
+        padding: 6px 16px;
+        border-radius: 20px;
+        font-weight: 600;
+        font-size: 12px;
+
+        &.priority-high {
+          background: #ffebee;
+          color: #c62828;
+        }
+
+        &.priority-medium {
+          background: #fff3e0;
+          color: #ef6c00;
+        }
+
+        &.priority-low {
+          background: #e8f5e8;
+          color: #2e7d32;
+        }
+      }
+
+      .timeline {
+        color: #6c757d;
+        font-weight: 500;
+      }
+    }
+
+    .expected-outcome {
+      margin-bottom: 32px;
+
+      h3 {
+        color: #2c3e50;
+        font-size: 18px;
+        font-weight: 600;
+        margin-bottom: 12px;
+      }
+
+      p {
+        line-height: 1.6;
+        color: #495057;
+      }
+    }
+
+    .action-plan {
+      margin-bottom: 32px;
+
+      h3 {
+        color: #2c3e50;
+        font-size: 18px;
+        font-weight: 600;
+        margin-bottom: 16px;
+      }
+
+      .actions-list {
+        .action-item {
+          background: #f8f9fa;
+          border-radius: 8px;
+          padding: 20px;
+          margin-bottom: 16px;
+          border-left: 4px solid #667eea;
+
+          .action-header {
+            display: flex;
+            align-items: center;
+            justify-content: space-between;
+            margin-bottom: 12px;
+
+            h4 {
+              margin: 0;
+              color: #2c3e50;
+              font-size: 16px;
+            }
+
+            .action-status {
+              padding: 4px 12px;
+              border-radius: 12px;
+              font-size: 12px;
+              font-weight: 500;
+
+              &.status-pending {
+                background: #fff3cd;
+                color: #856404;
+              }
+
+              &.status-in_progress {
+                background: #d1ecf1;
+                color: #0c5460;
+              }
+
+              &.status-completed {
+                background: #d4edda;
+                color: #155724;
+              }
+            }
+          }
+
+          .action-description {
+            color: #6c757d;
+            line-height: 1.5;
+            margin-bottom: 12px;
+          }
+
+          .action-meta {
+            display: flex;
+            gap: 24px;
+            font-size: 14px;
+            color: #6c757d;
+
+            .responsible,
+            .deadline {
+              font-weight: 500;
+            }
+          }
+        }
+      }
+    }
+
+    .success-metrics {
+      margin-bottom: 32px;
+
+      h3 {
+        color: #2c3e50;
+        font-size: 18px;
+        font-weight: 600;
+        margin-bottom: 12px;
+      }
+
+      ul {
+        list-style: none;
+        padding: 0;
+
+        li {
+          padding: 8px 0;
+          padding-left: 24px;
+          position: relative;
+          color: #495057;
+
+          &::before {
+            content: '✓';
+            color: #27ae60;
+            font-weight: bold;
+            position: absolute;
+            left: 0;
+          }
+        }
+      }
+    }
+
+    .resources {
+      h3 {
+        color: #2c3e50;
+        font-size: 18px;
+        font-weight: 600;
+        margin-bottom: 16px;
+      }
+
+      .resource-grid {
+        display: grid;
+        grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+        gap: 20px;
+
+        .resource-item {
+          background: #f8f9fa;
+          padding: 16px;
+          border-radius: 8px;
+
+          h4 {
+            margin: 0 0 12px 0;
+            color: #495057;
+            font-size: 14px;
+            font-weight: 600;
+          }
+
+          p {
+            margin: 0;
+            color: #6c757d;
+          }
+
+          .personnel-list,
+          .tools-list {
+            display: flex;
+            flex-wrap: wrap;
+            gap: 8px;
+
+            ::ng-deep mat-chip {
+              background: #e3f2fd;
+              color: #1976d2;
+              font-size: 12px;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  .panel-footer {
+    display: flex;
+    align-items: center;
+    justify-content: flex-end;
+    gap: 12px;
+    padding: 20px 24px;
+    border-top: 1px solid #e0e0e0;
+    background: #fafafa;
+
+    button {
+      min-width: 100px;
+    }
+  }
+}
+
+@media (max-width: 768px) {
+  .resignation-detail-panel {
+    .panel-content {
+      .stats-section {
+        grid-template-columns: 1fr;
+      }
+
+      .analysis-section {
+        .impact-analysis {
+          grid-template-columns: 1fr;
+        }
+
+        .time-distribution {
+          .period-item {
+            grid-template-columns: 1fr;
+            gap: 8px;
+            text-align: center;
+          }
+        }
+      }
+
+      .resources {
+        .resource-grid {
+          grid-template-columns: 1fr;
+        }
+      }
+    }
+
+    .panel-footer {
+      flex-direction: column;
+      gap: 8px;
+
+      button {
+        width: 100%;
+      }
+    }
+  }
+}

+ 291 - 0
src/app/pages/hr/dashboard/resignation-detail-panel.component.ts

@@ -0,0 +1,291 @@
+import { Component, Input, Output, EventEmitter } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { MatDialogModule } from '@angular/material/dialog';
+import { MatButtonModule } from '@angular/material/button';
+import { MatIconModule } from '@angular/material/icon';
+import { MatTabsModule } from '@angular/material/tabs';
+import { MatChipsModule } from '@angular/material/chips';
+import { MatProgressBarModule } from '@angular/material/progress-bar';
+import { MatCardModule } from '@angular/material/card';
+
+export interface ResignationReason {
+  id: string;
+  name: string;
+  category: string;
+  categoryName: string;
+  icon: string;
+  percentage: number;
+  count: number;
+  description: string;
+  trend: {
+    direction: 'up' | 'down' | 'stable';
+    value: number;
+  };
+}
+
+export interface DetailAnalysis {
+  overview: string;
+  keyFactors: string[];
+  impactAnalysis: {
+    shortTerm: string[];
+    longTerm: string[];
+  };
+  relatedDepartments: string[];
+  timeDistribution: {
+    period: string;
+    count: number;
+    percentage: number;
+  }[];
+}
+
+export interface ImprovementPlan {
+  priority: 'high' | 'medium' | 'low';
+  timeline: string;
+  actions: {
+    title: string;
+    description: string;
+    responsible: string;
+    deadline: string;
+    status: 'pending' | 'in_progress' | 'completed';
+  }[];
+  expectedOutcome: string;
+  successMetrics: string[];
+  resources: {
+    budget: string;
+    personnel: string[];
+    tools: string[];
+  };
+}
+
+@Component({
+  selector: 'app-resignation-detail-panel',
+  standalone: true,
+  imports: [
+    CommonModule,
+    MatDialogModule,
+    MatButtonModule,
+    MatIconModule,
+    MatTabsModule,
+    MatChipsModule,
+    MatProgressBarModule,
+    MatCardModule,
+   // 添加 NgFor 到 imports 数组
+  ],
+  template: `
+    <div class="resignation-detail-panel">
+      <div class="panel-header">
+        <div class="header-content">
+          <mat-icon class="reason-icon">{{ reason.icon }}</mat-icon>
+          <div class="header-text">
+            <h2>{{ reason.name }}</h2>
+            <span class="category-chip">{{ reason.categoryName }}</span>
+          </div>
+        </div>
+        <button mat-icon-button (click)="onClose()">
+          <mat-icon>close</mat-icon>
+        </button>
+      </div>
+
+      <div class="panel-content">
+        <mat-tab-group>
+          <mat-tab label="详细分析">
+            <div class="tab-content">
+              <!-- 基础统计 -->
+              <div class="stats-section">
+                <div class="stat-card">
+                  <div class="stat-value">{{ reason.percentage }}%</div>
+                  <div class="stat-label">占比</div>
+                </div>
+                <div class="stat-card">
+                  <div class="stat-value">{{ reason.count }}</div>
+                  <div class="stat-label">人数</div>
+                </div>
+                <div class="stat-card">
+                  <div class="stat-value trend" [class]="getTrendClass()">
+                    <mat-icon>{{ getTrendIcon() }}</mat-icon>
+                    {{ reason.trend.value }}%
+                  </div>
+                  <div class="stat-label">趋势</div>
+                </div>
+              </div>
+
+              <!-- 详细分析 -->
+              <div class="analysis-section">
+                <h3>分析概述</h3>
+                <p class="overview-text">{{ detailAnalysis.overview }}</p>
+
+                <h3>关键因素</h3>
+                <div class="factors-list">
+                  <mat-chip-set>
+                    @for (factor of detailAnalysis.keyFactors; track factor) {
+                      <mat-chip>{{ factor }}</mat-chip>
+                    }
+                  </mat-chip-set>
+                </div>
+
+                <h3>影响分析</h3>
+                <div class="impact-analysis">
+                  <div class="impact-section">
+                    <h4>短期影响</h4>
+                    <ul>
+                      @for (impact of detailAnalysis.impactAnalysis.shortTerm; track impact) {
+                        <li>{{ impact }}</li>
+                      }
+                    </ul>
+                  </div>
+                  <div class="impact-section">
+                    <h4>长期影响</h4>
+                    <ul>
+                      @for (impact of detailAnalysis.impactAnalysis.longTerm; track impact) {
+                        <li>{{ impact }}</li>
+                      }
+                    </ul>
+                  </div>
+                </div>
+
+                <h3>时间分布</h3>
+                <div class="time-distribution">
+                  @for (period of detailAnalysis.timeDistribution; track period.period) {
+                    <div class="period-item">
+                      <div class="period-label">{{ period.period }}</div>
+                      <div class="period-bar">
+                        <div class="bar-fill" [style.width.%]="period.percentage"></div>
+                      </div>
+                      <div class="period-count">{{ period.count }}人</div>
+                    </div>
+                  }
+                </div>
+              </div>
+            </div>
+          </mat-tab>
+
+          <mat-tab label="改进建议">
+            <div class="tab-content">
+              <div class="improvement-header">
+                <div class="priority-badge" [class]="'priority-' + improvementPlan.priority">
+                  {{ getPriorityText() }}
+                </div>
+                <div class="timeline">预计时间:{{ improvementPlan.timeline }}</div>
+              </div>
+
+              <div class="expected-outcome">
+                <h3>预期成果</h3>
+                <p>{{ improvementPlan.expectedOutcome }}</p>
+              </div>
+
+              <div class="action-plan">
+                <h3>行动计划</h3>
+                <div class="actions-list">
+                  @for (action of improvementPlan.actions; track action.title) {
+                    <div class="action-item">
+                      <div class="action-header">
+                        <h4>{{ action.title }}</h4>
+                        <div class="action-status" [class]="'status-' + action.status">
+                          {{ getStatusText(action.status) }}
+                        </div>
+                      </div>
+                      <p class="action-description">{{ action.description }}</p>
+                      <div class="action-meta">
+                        <span class="responsible">负责人:{{ action.responsible }}</span>
+                        <span class="deadline">截止时间:{{ action.deadline }}</span>
+                      </div>
+                    </div>
+                  }
+                </div>
+              </div>
+
+              <div class="success-metrics">
+                  <h3>成功指标</h3>
+                  <ul>
+                    @for (metric of improvementPlan.successMetrics; track metric) {
+                      <li>{{ metric }}</li>
+                    }
+                  </ul>
+                </div>
+
+              <div class="resources">
+                <h3>所需资源</h3>
+                <div class="resource-grid">
+                  <div class="resource-item">
+                    <h4>预算</h4>
+                    <p>{{ improvementPlan.resources.budget }}</p>
+                  </div>
+                  <div class="resource-item">
+                      <h4>人员</h4>
+                      <div class="personnel-list">
+                        @for (person of improvementPlan.resources.personnel; track person) {
+                          <mat-chip>{{ person }}</mat-chip>
+                        }
+                      </div>
+                    </div>
+                  <div class="resource-item">
+                      <h4>工具</h4>
+                      <div class="tools-list">
+                        @for (tool of improvementPlan.resources.tools; track tool) {
+                          <mat-chip>{{ tool }}</mat-chip>
+                        }
+                      </div>
+                    </div>
+                </div>
+              </div>
+            </div>
+          </mat-tab>
+        </mat-tab-group>
+      </div>
+
+      <div class="panel-footer">
+        <button mat-button (click)="onClose()">关闭</button>
+        <button mat-raised-button color="primary" (click)="onExport()">
+          <mat-icon>download</mat-icon>
+          导出报告
+        </button>
+      </div>
+    </div>
+  `,
+  styleUrls: ['./resignation-detail-panel.component.scss']
+})
+export class ResignationDetailPanelComponent {
+  @Input() reason!: ResignationReason;
+  @Input() detailAnalysis!: DetailAnalysis;
+  @Input() improvementPlan!: ImprovementPlan;
+  @Output() close = new EventEmitter<void>();
+  @Output() export = new EventEmitter<void>();
+
+  onClose(): void {
+    this.close.emit();
+  }
+
+  onExport(): void {
+    this.export.emit();
+  }
+
+  getTrendClass(): string {
+    return `trend-${this.reason.trend.direction}`;
+  }
+
+  getTrendIcon(): string {
+    switch (this.reason.trend.direction) {
+      case 'up': return 'trending_up';
+      case 'down': return 'trending_down';
+      default: return 'trending_flat';
+    }
+  }
+
+  getPriorityText(): string {
+    switch (this.improvementPlan.priority) {
+      case 'high': return '高优先级';
+      case 'medium': return '中优先级';
+      case 'low': return '低优先级';
+      default: return '未知';
+    }
+  }
+
+  getStatusText(status: string): string {
+    switch (status) {
+      case 'pending': return '待开始';
+      case 'in_progress': return '进行中';
+      case 'completed': return '已完成';
+      default: return '未知';
+    }
+  }
+}

+ 100 - 6
src/app/pages/hr/recruitment-performance/recruitment-performance.html

@@ -58,6 +58,24 @@
                 </div>
               }
 
+              @if (selectedResume()!.status === 'failed') {
+                <div class="failed-state">
+                  <mat-icon class="error-icon">error_outline</mat-icon>
+                  <h4>分析失败</h4>
+                  <p>简历分析过程中出现错误,请重试或检查文件格式</p>
+                  <div class="failed-actions">
+                    <button mat-raised-button color="primary" (click)="retryAnalysis(selectedResume()!.id)">
+                      <mat-icon>refresh</mat-icon>
+                      重新分析
+                    </button>
+                    <button mat-stroked-button color="warn" (click)="deleteAnalysis(selectedResume()!.id)">
+                      <mat-icon>delete</mat-icon>
+                      删除记录
+                    </button>
+                  </div>
+                </div>
+              }
+
               @if (selectedResume()!.status === 'completed' && selectedResume()!.analysisResult) {
                 <div class="completed-analysis">
                   <!-- 推荐结论 -->
@@ -121,8 +139,8 @@
               <h4>分析记录</h4>
               <div class="history-list">
                 @for (analysis of resumeAnalyses(); track analysis.id) {
-                  <div class="history-item" [class.selected]="selectedResume()?.id === analysis.id" (click)="selectedResume.set(analysis)">
-                    <div class="item-info">
+                  <div class="history-item" [class.selected]="selectedResume()?.id === analysis.id">
+                    <div class="item-info" (click)="selectedResume.set(analysis)">
                       <span class="file-name">{{ analysis.fileName }}</span>
                       <span class="upload-time">{{ analysis.uploadTime | date:'MM-dd HH:mm' }}</span>
                     </div>
@@ -134,7 +152,21 @@
                         <mat-icon class="status-icon completed">check_circle</mat-icon>
                       }
                       @if (analysis.status === 'failed') {
-                        <mat-icon class="status-icon failed">error</mat-icon>
+                        <div class="failed-status">
+                          <mat-icon class="status-icon failed">error</mat-icon>
+                          <div class="failed-actions">
+                            <button mat-icon-button color="primary" 
+                                    (click)="retryAnalysis(analysis.id); $event.stopPropagation()"
+                                    matTooltip="重新分析">
+                              <mat-icon>refresh</mat-icon>
+                            </button>
+                            <button mat-icon-button color="warn" 
+                                    (click)="deleteAnalysis(analysis.id); $event.stopPropagation()"
+                                    matTooltip="删除记录">
+                              <mat-icon>delete</mat-icon>
+                            </button>
+                          </div>
+                        </div>
                       }
                     </div>
                   </div>
@@ -217,9 +249,13 @@
 
           <!-- 阶段时间线 -->
           <div class="stages-timeline">
-            @for (stage of recruitmentStages(); track $index) {
-              <div class="timeline-item">
-                <div class="timeline-marker"></div>
+            @for (stage of recruitmentStages(); track $index; let i = $index) {
+              <div class="timeline-item" 
+                   [class]="getStageStatusClass(stage, i)"
+                   (click)="onStageClick(stage, i)">
+                <div class="timeline-marker">
+                  <mat-icon>{{ getStageIcon(stage.stage) }}</mat-icon>
+                </div>
                 <div class="timeline-content">
                   <div class="stage-header">
                     <h5>{{ stage.stage }}</h5>
@@ -232,9 +268,67 @@
                       <p><strong>备注:</strong>{{ stage.comments }}</p>
                     }
                   </div>
+                  @if (selectedStage() && selectedStage()?.evaluationTime === stage.evaluationTime) {
+                    <div class="stage-actions">
+                      <button mat-stroked-button color="primary" size="small">
+                        <mat-icon>edit</mat-icon>
+                        编辑
+                      </button>
+                      <button mat-stroked-button color="accent" size="small">
+                        <mat-icon>visibility</mat-icon>
+                        详情
+                      </button>
+                    </div>
+                  }
                 </div>
+                @if (!$last) {
+                  <div class="timeline-connector"></div>
+                }
               </div>
             }
+            
+            @if (recruitmentStages().length === 0) {
+              <div class="empty-timeline">
+                <mat-icon>timeline</mat-icon>
+                <p>暂无招聘流程记录,请添加第一个阶段</p>
+              </div>
+            }
+          </div>
+
+          <!-- 进度控制区域 -->
+          <div class="progress-controls">
+            <div class="progress-info">
+              <h4>流程进度控制</h4>
+              <div class="progress-stats">
+                <div class="stat-item">
+                  <span class="label">当前阶段:</span>
+                  <span class="value">{{ getCurrentStageStatus() }}</span>
+                </div>
+                <div class="stat-item">
+                  <span class="label">整体进度:</span>
+                  <span class="value">{{ getStageProgress() }}%</span>
+                </div>
+                @if (canProceedToNext()) {
+                  <div class="stat-item">
+                    <span class="label">下一阶段:</span>
+                    <span class="value">{{ getNextStageName() }}</span>
+                  </div>
+                }
+              </div>
+            </div>
+            
+            <div class="progress-actions">
+              @if (canProceedToNext()) {
+                <button mat-raised-button color="primary" (click)="proceedToNextStage()">
+                  <mat-icon>arrow_forward</mat-icon>
+                  进入下一阶段
+                </button>
+              }
+              <button mat-stroked-button color="warn" (click)="resetStageProgress()">
+                <mat-icon>refresh</mat-icon>
+                重置进度
+              </button>
+            </div>
           </div>
 
           <!-- 添加新阶段 -->

+ 245 - 4
src/app/pages/hr/recruitment-performance/recruitment-performance.scss

@@ -32,11 +32,11 @@
         
         mat-icon {
           margin-right: 6px;
-        }
+      
         
         &:hover {
           background: rgba(0, 122, 255, 0.08);
-        }
+      
         
         &:active {
           transform: scale(0.97);
@@ -781,6 +781,40 @@
           }
         }
         
+        .failed-state {
+          text-align: center;
+          padding: 48px 24px;
+          
+          .error-icon {
+            font-size: 48px;
+            color: #f44336;
+            margin-bottom: 16px;
+          }
+          
+          h4 {
+            margin: 0 0 12px 0;
+            color: #f44336;
+            font-weight: 600;
+          }
+          
+          p {
+            color: #666;
+            font-size: 16px;
+            margin-bottom: 24px;
+            line-height: 1.6;
+          }
+          
+          .failed-actions {
+            display: flex;
+            gap: 16px;
+            justify-content: center;
+            
+            button {
+              min-width: 120px;
+            }
+          }
+        }
+        
         .completed-analysis {
           .recommendation-section {
             margin-bottom: 32px;
@@ -988,6 +1022,35 @@
                   color: #f44336;
                 }
               }
+              
+              .failed-status {
+                display: flex;
+                align-items: center;
+                gap: 8px;
+                
+                .failed-actions {
+                  display: flex;
+                  gap: 4px;
+                  opacity: 0;
+                  transition: opacity 0.3s ease;
+                  
+                  button {
+                    width: 32px;
+                    height: 32px;
+                    line-height: 32px;
+                    
+                    mat-icon {
+                      font-size: 16px;
+                      width: 16px;
+                      height: 16px;
+                    }
+                  }
+                }
+              }
+            }
+            
+            &:hover .failed-status .failed-actions {
+              opacity: 1;
             }
           }
         }
@@ -1027,15 +1090,82 @@
         .timeline-item {
           display: flex;
           margin-bottom: 24px;
+          padding: 16px;
+          border-radius: 12px;
+          cursor: pointer;
+          transition: all 0.3s ease;
+          position: relative;
+          
+          &:hover {
+            background: rgba(102, 126, 234, 0.05);
+            transform: translateX(4px);
+          }
+          
+          &.selected {
+            background: rgba(102, 126, 234, 0.1);
+            border: 2px solid #667eea;
+            box-shadow: 0 4px 12px rgba(102, 126, 234, 0.2);
+          }
+          
+          &.current {
+            border-left: 4px solid #667eea;
+          }
+          
+          &.通过 {
+            border-left-color: #4CAF50;
+            
+            .timeline-marker {
+              background: #4CAF50;
+              color: white;
+            }
+          }
+          
+          &.不通过 {
+            border-left-color: #f44336;
+            
+            .timeline-marker {
+              background: #f44336;
+              color: white;
+            }
+          }
+          
+          &.待定 {
+            border-left-color: #ff9800;
+            
+            .timeline-marker {
+              background: #ff9800;
+              color: white;
+            }
+          }
           
           .timeline-marker {
-            width: 16px;
-            height: 16px;
+            width: 40px;
+            height: 40px;
             border-radius: 50%;
             background: #667eea;
             margin-right: 16px;
             margin-top: 4px;
             flex-shrink: 0;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            color: white;
+            
+            mat-icon {
+              font-size: 20px;
+              width: 20px;
+              height: 20px;
+            }
+          }
+          
+          .timeline-connector {
+            position: absolute;
+            left: 36px;
+            top: 60px;
+            width: 2px;
+            height: 40px;
+            background: #e0e0e0;
+            z-index: 1;
           }
           
           .timeline-content {
@@ -1085,6 +1215,117 @@
                 margin: 4px 0;
               }
             }
+            
+            .stage-actions {
+              margin-top: 12px;
+              display: flex;
+              gap: 8px;
+              opacity: 0;
+              transition: opacity 0.3s ease;
+              
+              button {
+                height: 32px;
+                line-height: 32px;
+                font-size: 12px;
+                
+                mat-icon {
+                  font-size: 16px;
+                  width: 16px;
+                  height: 16px;
+                  margin-right: 4px;
+                }
+              }
+            }
+          }
+          
+          &.selected .stage-actions {
+            opacity: 1;
+          }
+        }
+        
+        .empty-timeline {
+          text-align: center;
+          padding: 48px 24px;
+          color: #999;
+          
+          mat-icon {
+            font-size: 48px;
+            width: 48px;
+            height: 48px;
+            margin-bottom: 16px;
+            color: #ccc;
+          }
+          
+          p {
+            font-size: 16px;
+            margin: 0;
+          }
+        }
+      }
+      
+      .progress-controls {
+        margin-top: 32px;
+        padding: 24px;
+        background: linear-gradient(135deg, rgba(102, 126, 234, 0.05) 0%, rgba(118, 75, 162, 0.05) 100%);
+        border-radius: 16px;
+        border: 1px solid rgba(102, 126, 234, 0.1);
+        
+        .progress-info {
+          margin-bottom: 20px;
+          
+          h4 {
+            margin: 0 0 16px 0;
+            color: #333;
+            font-weight: 600;
+            display: flex;
+            align-items: center;
+            gap: 8px;
+            
+            &::before {
+              content: '';
+              width: 4px;
+              height: 20px;
+              background: linear-gradient(135deg, #667eea, #764ba2);
+              border-radius: 2px;
+            }
+          }
+          
+          .progress-stats {
+            display: grid;
+            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+            gap: 16px;
+            
+            .stat-item {
+              display: flex;
+              justify-content: space-between;
+              align-items: center;
+              padding: 12px 16px;
+              background: white;
+              border-radius: 8px;
+              border: 1px solid rgba(0, 0, 0, 0.1);
+              
+              .label {
+                font-weight: 500;
+                color: #666;
+              }
+              
+              .value {
+                font-weight: 600;
+                color: #333;
+              }
+            }
+          }
+        }
+        
+        .progress-actions {
+          display: flex;
+          gap: 12px;
+          justify-content: flex-end;
+          
+          button {
+            min-width: 140px;
+          }
+        }
           }
         }
       }

+ 250 - 23
src/app/pages/hr/recruitment-performance/recruitment-performance.ts

@@ -93,6 +93,9 @@ export class RecruitmentPerformanceComponent implements AfterViewInit, OnDestroy
   
   // 招聘流程跟踪
   recruitmentStages = signal<RecruitmentStage[]>([]);
+  selectedStage = signal<RecruitmentStage | null>(null);
+  stageProgress = signal<number>(0);
+  currentStageIndex = signal<number>(0);
   newStage = {
     stage: '简历初筛',
     result: '通过',
@@ -100,6 +103,14 @@ export class RecruitmentPerformanceComponent implements AfterViewInit, OnDestroy
     comments: ''
   };
 
+  // 四个标准阶段定义
+  standardStages = [
+    { name: '简历初筛', icon: 'description', description: '筛选符合基本要求的简历' },
+    { name: '面试评估', icon: 'person', description: '技能和文化匹配度评估' },
+    { name: '入职评定', icon: 'how_to_reg', description: '最终录用决定和入职准备' },
+    { name: '试用期跟踪', icon: 'trending_up', description: '试用期表现跟踪和评估' }
+  ];
+
   // 招聘四阶段定义
   recruitmentPhases = [
     {
@@ -454,6 +465,19 @@ export class RecruitmentPerformanceComponent implements AfterViewInit, OnDestroy
   onResumeUpload(event: any) {
     const file = event.target.files[0];
     if (file) {
+      // 验证文件类型
+      const allowedTypes = ['application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'];
+      if (!allowedTypes.includes(file.type)) {
+        alert('不支持的文件格式,请上传PDF、DOC或DOCX文件');
+        return;
+      }
+
+      // 验证文件大小 (10MB)
+      if (file.size > 10 * 1024 * 1024) {
+        alert('文件大小超过10MB限制');
+        return;
+      }
+
       const newAnalysis: ResumeAnalysis = {
         id: Date.now().toString(),
         fileName: file.name,
@@ -462,39 +486,149 @@ export class RecruitmentPerformanceComponent implements AfterViewInit, OnDestroy
       };
       
       this.resumeAnalyses.update(analyses => [...analyses, newAnalysis]);
+      this.selectedResume.set(newAnalysis);
       
       // 模拟AI分析过程
-      setTimeout(() => {
-        this.simulateAIAnalysis(newAnalysis.id);
-      }, 3000);
+      this.simulateAIAnalysis(newAnalysis.id);
     }
   }
 
-  private simulateAIAnalysis(analysisId: string) {
+  private simulateAIAnalysis(analysisId: string, retryCount: number = 0) {
+    const maxRetries = 2;
+    const analysisDelay = 3000 + (retryCount * 1000); // 重试时延长等待时间
+
+    setTimeout(() => {
+      // 模拟分析成功率(80%成功率,前两次可能失败)
+      const shouldSucceed = retryCount >= 1 || Math.random() > 0.2;
+      
+      if (shouldSucceed) {
+        // 分析成功
+        this.resumeAnalyses.update(analyses => 
+          analyses.map(analysis => 
+            analysis.id === analysisId 
+              ? {
+                  ...analysis,
+                  status: 'completed' as const,
+                  analysisResult: {
+                    recommendation: this.getRandomRecommendation(),
+                    reason: this.getRandomAnalysisReason(),
+                    coreSkills: this.generateRandomSkills(),
+                    screeningInfo: {
+                      education: this.getRandomEducation(),
+                      workYears: this.getRandomWorkYears(),
+                      coreSkills: this.getRandomCoreSkills()
+                    }
+                  }
+                }
+              : analysis
+          )
+        );
+      } else {
+        // 分析失败
+        if (retryCount < maxRetries) {
+          // 标记为失败状态,但准备重试
+          this.resumeAnalyses.update(analyses => 
+            analyses.map(analysis => 
+              analysis.id === analysisId 
+                ? { ...analysis, status: 'failed' as const }
+                : analysis
+            )
+          );
+          
+          // 1秒后自动重试
+          setTimeout(() => {
+            this.resumeAnalyses.update(analyses => 
+              analyses.map(analysis => 
+                analysis.id === analysisId 
+                  ? { ...analysis, status: 'processing' as const }
+                  : analysis
+              )
+            );
+            this.simulateAIAnalysis(analysisId, retryCount + 1);
+          }, 1000);
+        } else {
+          // 最终失败
+          this.resumeAnalyses.update(analyses => 
+            analyses.map(analysis => 
+              analysis.id === analysisId 
+                ? { ...analysis, status: 'failed' as const }
+                : analysis
+            )
+          );
+        }
+      }
+    }, analysisDelay);
+  }
+
+  // 重新分析失败的简历
+  retryAnalysis(analysisId: string) {
     this.resumeAnalyses.update(analyses => 
       analyses.map(analysis => 
         analysis.id === analysisId 
-          ? {
-              ...analysis,
-              status: 'completed' as const,
-              analysisResult: {
-                recommendation: '推荐' as const,
-                reason: '候选人技能匹配度较高,具备相关工作经验',
-                coreSkills: [
-                  { name: '专业技能', score: 85, matched: true },
-                  { name: '工作经验', score: 78, matched: true },
-                  { name: '学历背景', score: 90, matched: true }
-                ],
-                screeningInfo: {
-                  education: '本科',
-                  workYears: '2-3年',
-                  coreSkills: ['专业技能', '沟通能力']
-                }
-              }
-            }
+          ? { ...analysis, status: 'processing' as const, analysisResult: undefined }
           : analysis
       )
     );
+    this.simulateAIAnalysis(analysisId, 0);
+  }
+
+  // 删除分析记录
+  deleteAnalysis(analysisId: string) {
+    this.resumeAnalyses.update(analyses => 
+      analyses.filter(analysis => analysis.id !== analysisId)
+    );
+    
+    // 如果删除的是当前选中的分析,清空选择
+    if (this.selectedResume()?.id === analysisId) {
+      this.selectedResume.set(null);
+    }
+  }
+
+  // 辅助方法:生成随机推荐结果
+  private getRandomRecommendation(): '推荐' | '不推荐' | '待定' {
+    const recommendations: ('推荐' | '不推荐' | '待定')[] = ['推荐', '不推荐', '待定'];
+    return recommendations[Math.floor(Math.random() * recommendations.length)];
+  }
+
+  // 辅助方法:生成随机分析原因
+  private getRandomAnalysisReason(): string {
+    const reasons = [
+      '候选人技能匹配度较高,具备相关工作经验',
+      '候选人经验丰富,但部分技能需要进一步评估',
+      '候选人基础技能良好,但缺乏相关行业经验',
+      '候选人学历背景优秀,技能匹配度中等',
+      '候选人综合素质良好,建议进入面试环节'
+    ];
+    return reasons[Math.floor(Math.random() * reasons.length)];
+  }
+
+  // 辅助方法:生成随机技能评分
+  private generateRandomSkills() {
+    const skillNames = ['专业技能', '工作经验', '学历背景', '沟通能力', '团队协作'];
+    return skillNames.map(name => ({
+      name,
+      score: Math.floor(Math.random() * 40) + 60, // 60-100分
+      matched: Math.random() > 0.3 // 70%匹配率
+    }));
+  }
+
+  // 辅助方法:生成随机学历
+  private getRandomEducation(): string {
+    const educations = ['专科', '本科', '硕士', '博士'];
+    return educations[Math.floor(Math.random() * educations.length)];
+  }
+
+  // 辅助方法:生成随机工作年限
+  private getRandomWorkYears(): string {
+    const workYears = ['1年以下', '1-2年', '2-3年', '3-5年', '5年以上'];
+    return workYears[Math.floor(Math.random() * workYears.length)];
+  }
+
+  // 辅助方法:生成随机核心技能
+  private getRandomCoreSkills(): string[] {
+    const allSkills = ['专业技能', '沟通能力', '团队协作', '项目管理', '创新思维', '学习能力'];
+    const skillCount = Math.floor(Math.random() * 3) + 2; // 2-4个技能
+    return allSkills.slice(0, skillCount);
   }
 
   // 添加招聘阶段
@@ -530,7 +664,100 @@ export class RecruitmentPerformanceComponent implements AfterViewInit, OnDestroy
   getStageProgress(): number {
     const stages = this.recruitmentStages();
     const totalStages = 4; // 简历初筛、面试评估、入职评定、试用期跟踪
-    return Math.round((stages.length / totalStages) * 100);
+    const progress = Math.round((stages.length / totalStages) * 100);
+    this.stageProgress.set(progress);
+    return progress;
+  }
+
+  // 阶段点击处理
+  onStageClick(stage: RecruitmentStage, index: number): void {
+    this.selectedStage.set(stage);
+    this.currentStageIndex.set(index);
+    console.log('选中阶段:', stage);
+  }
+
+  // 获取阶段状态类
+  getStageStatusClass(stage: RecruitmentStage, index: number): string {
+    const currentIndex = this.currentStageIndex();
+    const selectedStage = this.selectedStage();
+    
+    let statusClass = stage.result.toLowerCase();
+    
+    if (selectedStage && selectedStage.evaluationTime === stage.evaluationTime) {
+      statusClass += ' selected';
+    }
+    
+    if (index === currentIndex) {
+      statusClass += ' current';
+    }
+    
+    return statusClass;
+  }
+
+  // 进入下一阶段
+  proceedToNextStage(): void {
+    const currentIndex = this.currentStageIndex();
+    const stages = this.recruitmentStages();
+    
+    if (currentIndex < this.standardStages.length - 1) {
+      const nextStage = this.standardStages[currentIndex + 1];
+      this.newStage.stage = nextStage.name;
+      this.currentStageIndex.set(currentIndex + 1);
+      
+      // 自动填充下一阶段信息
+      this.newStage.result = '待定';
+      this.newStage.evaluator = '';
+      this.newStage.comments = `准备进入${nextStage.name}阶段`;
+    }
+  }
+
+  // 重置阶段进度
+  resetStageProgress(): void {
+    this.recruitmentStages.set([]);
+    this.selectedStage.set(null);
+    this.currentStageIndex.set(0);
+    this.stageProgress.set(0);
+    this.newStage = {
+      stage: '简历初筛',
+      result: '通过',
+      evaluator: '',
+      comments: ''
+    };
+  }
+
+  // 获取阶段图标
+  getStageIcon(stageName: string): string {
+    const stage = this.standardStages.find(s => s.name === stageName);
+    return stage ? stage.icon : 'help';
+  }
+
+  // 获取阶段描述
+  getStageDescription(stageName: string): string {
+    const stage = this.standardStages.find(s => s.name === stageName);
+    return stage ? stage.description : '';
+  }
+
+  // 检查是否可以进入下一阶段
+  canProceedToNext(): boolean {
+    const currentIndex = this.currentStageIndex();
+    const stages = this.recruitmentStages();
+    
+    // 检查当前阶段是否已完成且结果为通过
+    if (stages.length > currentIndex) {
+      const currentStage = stages[currentIndex];
+      return currentStage.result === '通过';
+    }
+    
+    return false;
+  }
+
+  // 获取下一阶段名称
+  getNextStageName(): string {
+    const currentIndex = this.currentStageIndex();
+    if (currentIndex < this.standardStages.length - 1) {
+      return this.standardStages[currentIndex + 1].name;
+    }
+    return '流程完成';
   }
 
   // 计算各阶段通过率

+ 34 - 0
src/app/pages/hr/shared/hr-common.scss

@@ -1,5 +1,39 @@
 /* HR板块统一样式文件 */
 
+/* 全局下拉面板样式修复 - 适用于整个HR板块 */
+::ng-deep .mat-mdc-select-panel {
+  background: rgba(255, 255, 255, 0.98) !important;
+  backdrop-filter: blur(10px) !important;
+  border-radius: 12px !important;
+  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15) !important;
+  border: 1px solid rgba(255, 255, 255, 0.2) !important;
+  max-height: 300px !important;
+  
+  .mat-mdc-option {
+    background: transparent !important;
+    color: #333 !important;
+    font-size: 16px !important;
+    padding: 12px 16px !important;
+    border-bottom: 1px solid rgba(0, 0, 0, 0.05) !important;
+    transition: all 0.2s ease !important;
+    
+    &:hover {
+      background: rgba(30, 64, 175, 0.1) !important;
+      color: #1e40af !important;
+    }
+    
+    &.mdc-list-item--selected {
+      background: rgba(30, 64, 175, 0.15) !important;
+      color: #1e40af !important;
+      font-weight: 600 !important;
+    }
+    
+    &:last-child {
+      border-bottom: none !important;
+    }
+  }
+}
+
 /* 主题色彩变量 */
 $hr-primary: #1e40af;
 $hr-primary-light: #3b82f6;