import { Component, OnInit, AfterViewInit, ViewChild, ElementRef } from '@angular/core'; import { CommonModule } from '@angular/common'; import { RouterModule } from '@angular/router'; import { MatCardModule } from '@angular/material/card'; import { MatButtonModule } from '@angular/material/button'; import { MatTabsModule } from '@angular/material/tabs'; import { MatIconModule } from '@angular/material/icon'; import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatChipsModule } from '@angular/material/chips'; import { MatProgressBarModule } from '@angular/material/progress-bar'; import { MatBadgeModule } from '@angular/material/badge'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatSelectModule } from '@angular/material/select'; import { MatInputModule } from '@angular/material/input'; import { MatTableModule } from '@angular/material/table'; import { MatButtonToggleModule } from '@angular/material/button-toggle'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSnackBarModule, MatSnackBar } from '@angular/material/snack-bar'; import { MatDialogModule, MatDialog } from '@angular/material/dialog'; import { DragDropModule, CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; 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'; import { AddComparisonDialogComponent, ComparisonItemData } from './add-comparison-dialog.component'; Chart.register(...registerables); // 数据模型定义 export interface TodoItem { id: number; title: string; description: string; priority: 'high' | 'medium' | 'low'; status: 'pending' | 'completed' | 'in_progress'; type: 'resume' | 'onboarding' | 'resignation'; } export interface RankDistribution { junior: number; intermediate: number; senior: number; } export interface MonthlyData { month: string; hired: number; left: number; notes?: string; } export interface DepartmentPerformance { department: string; completionRate: number; excellentWorkRate: number; satisfactionRate: number; overdueRate: number; } export interface OnboardingCheckpoint { id: number; title: string; description: string; dueDate: Date; completed: boolean; interviewTemplate: string[]; } // AI简历分析相关接口 export interface MatchDimension { id: number; name: string; score: number; level: 'high' | 'medium' | 'low'; icon: string; } export interface Recommendation { title: string; level: 'recommend' | 'consider' | 'reject'; levelText: string; icon: string; summary: string; reasons: string[]; concerns: string[]; } export interface ScreeningInfo { id: number; title: string; detail: string; status: 'pass' | 'warning' | 'fail'; statusText: string; icon: string; } export interface RecruitmentStage { id: string; title: string; status: 'completed' | 'active' | 'pending' | 'blocked'; statusText: string; icon: string; candidateCount: number; passRate: number; evaluator?: string; lastUpdate: Date; nextAction?: string; evaluationResults?: EvaluationResult[]; } export interface EvaluationResult { candidateName: string; result: 'pass' | 'fail' | 'pending'; evaluator: string; evaluationTime: Date; score?: number; comments?: string; } export interface PerformanceMetric { id: string; title: string; value: string; unit: string; target: string; achievement: string; achievementClass: string; period: string; icon: string; iconClass: string; status: 'excellent' | 'good' | 'warning' | 'poor'; trend: { type: 'positive' | 'negative' | 'neutral'; value: string; label: string; icon: string; }; progressValue: number; progressClass: string; } @Component({ selector: 'app-hr-dashboard', standalone: true, imports: [ CommonModule, RouterModule, MatCardModule, MatButtonModule, MatTabsModule, MatIconModule, MatCheckboxModule, MatChipsModule, MatProgressBarModule, MatBadgeModule, MatFormFieldModule, MatSelectModule, MatInputModule, DragDropModule, MatTableModule, MatButtonToggleModule, MatProgressSpinnerModule, MatSnackBarModule, MatDialogModule, ResignationDetailPanelComponent ], templateUrl: './dashboard.html', styleUrls: ['./dashboard.scss'], animations: [ trigger('slideInOut', [ transition(':enter', [ style({ height: '0', opacity: 0, overflow: 'hidden' }), animate('300ms ease-in-out', style({ height: '*', opacity: 1 })) ]), transition(':leave', [ animate('300ms ease-in-out', style({ height: '0', opacity: 0, overflow: 'hidden' })) ]) ]) ] }) export class Dashboard implements OnInit, AfterViewInit { @ViewChild('pieChart', { static: false }) pieChartRef!: ElementRef; @ViewChild('lineChart', { static: false }) lineChartRef!: ElementRef; @ViewChild('radarChart', { static: false }) radarChartRef!: ElementRef; @ViewChild('resignationChart', { static: false }) resignationChartRef!: ElementRef; @ViewChild('comparisonChart', { static: false }) comparisonChartRef!: ElementRef; constructor( private doubaoAiService: DoubaoAiService, private snackBar: MatSnackBar, private dialog: MatDialog ) { Chart.register(...registerables); } private pieChart!: Chart; private lineChart!: Chart; private radarChart!: Chart; private resignationChart!: Chart; private comparisonChart!: Chart; // 当前激活的标签页 activeTab: 'visualization' | 'recruitment' | 'performance' | 'onboarding' = 'visualization'; // 对比图表类型 chartType: 'bar' | 'line' | 'radar' = 'line'; // 待办事项是否展开 isTodoExpanded = false; // 当前页面 currentPage: string = 'dashboard'; // 待办事项展开状态 showTodoList: boolean = false; // 待办事项列表(用于右侧展示) todoList = [ { id: 1, title: '简历初筛', description: '筛选新收到的设计师简历', priority: 'high', dueDate: '2024-01-25' }, { id: 2, title: '入职评定', description: '完成新员工入职评定表', priority: 'medium', dueDate: '2024-01-28' }, { id: 4, title: '离职面谈', description: '安排资深设计师离职面谈', priority: 'medium', dueDate: '2024-02-02' } ]; // 模拟数据 todoItems: TodoItem[] = [ { id: 1, title: '简历初筛', description: '筛选新收到的设计师简历', priority: 'high', status: 'pending', type: 'resume' }, { id: 2, title: '入职评定', description: '完成新员工入职评定表', priority: 'medium', status: 'pending', type: 'onboarding' }, { id: 4, title: '离职面谈', description: '安排资深设计师离职面谈', priority: 'medium', status: 'pending', type: 'resignation' } ]; // 职级分布数据 rankDistribution = [ { level: '初级设计师', count: 15, percentage: 45, color: '#4CAF50' }, { level: '中级设计师', count: 12, percentage: 36, color: '#FF9800' }, { level: '高级设计师', count: 6, percentage: 19, color: '#2196F3' } ]; monthlyHireData: MonthlyData[] = [ { month: '10月', hired: 8, left: 3, notes: '秋季招聘高峰' }, { month: '11月', hired: 5, left: 2, notes: '' }, { month: '12月', hired: 3, left: 4, notes: '年底离职高峰' }, { month: '1月', hired: 10, left: 1, notes: '新年新气象' }, { month: '2月', hired: 6, left: 2, notes: '' }, { month: '3月', hired: 12, left: 3, notes: '春季招聘启动' } ]; // 关键节点数据 keyNotes = [ { month: '3月', description: '入职10人:因春季招聘' }, { month: '5月', description: '离职5人:因项目不饱和' }, { month: '8月', description: '入职15人:暑期实习转正' }, { month: '12月', description: '离职8人:年底跳槽高峰' } ]; // 关键岗位空缺数据 keyVacancies = [ { position: '高级UI设计师', count: 2, priority: 'high', duration: 45 }, { position: '3D建模师', count: 1, priority: 'medium', duration: 20 }, { position: '前端开发工程师', count: 1, priority: 'medium', duration: 15 }, { position: '产品经理', count: 1, priority: 'high', duration: 60 } ]; // 新人列表数据 newbieList = [ { id: 1, name: '张三', position: 'UI设计师', joinDate: '2024-01-15', progress: 75 }, { id: 2, name: '李四', position: '前端开发', joinDate: '2024-01-20', progress: 60 }, { id: 3, name: '王五', position: '3D建模师', joinDate: '2024-02-01', progress: 40 } ]; departmentPerformance: DepartmentPerformance[] = [ { department: 'UI设计部', completionRate: 92, excellentWorkRate: 78, satisfactionRate: 88, overdueRate: 8 }, { department: '3D建模部', completionRate: 85, excellentWorkRate: 82, satisfactionRate: 90, overdueRate: 15 }, { department: '前端开发部', completionRate: 88, excellentWorkRate: 75, satisfactionRate: 85, overdueRate: 12 } ]; onboardingCheckpoints: OnboardingCheckpoint[] = [ { id: 1, title: '入职1周面谈', description: '了解新人工作适应情况', dueDate: new Date(), completed: false, interviewTemplate: [ '工作环境是否适应?', '是否有不清楚的工作流程?', '团队沟通是否顺畅?', '是否需要额外的工作工具?' ] }, { id: 2, title: '入职1个月评估', description: '评估新人技能掌握情况', dueDate: new Date(Date.now() + 21 * 24 * 60 * 60 * 1000), completed: false, interviewTemplate: [ '主要工作技能掌握程度?', '遇到的最大挑战是什么?', '对团队文化的感受?', '个人职业发展期望?' ] }, { id: 3, title: '入职3个月总结', description: '全面评估新人表现', dueDate: new Date(Date.now() + 83 * 24 * 60 * 60 * 1000), completed: false, interviewTemplate: [ '工作成果总体评价', '需要改进的技能领域', '长期职业规划讨论', '转正评估准备' ] } ]; ngOnInit() { // 注册Chart.js组件 Chart.register(...registerables); // 初始化图表数据 this.initCharts(); } ngAfterViewInit() { // 延迟初始化图表,确保DOM已渲染 setTimeout(() => { this.initializeCharts(); this.initScrollIndicator(); }, 100); } ngOnDestroy() { // 销毁所有图表实例,防止内存泄漏 if (this.pieChart) { this.pieChart.destroy(); } if (this.lineChart) { this.lineChart.destroy(); } if (this.radarChart) { this.radarChart.destroy(); } if (this.comparisonChart) { this.comparisonChart.destroy(); } } // 切换标签页 switchTab(tab: 'visualization' | 'recruitment' | 'performance' | 'onboarding') { this.activeTab = tab; // 如果切换到数据可视化页面,重新初始化图表 if (tab === 'visualization') { setTimeout(() => this.initializeCharts(), 100); } // 如果切换到绩效统计页面,初始化对比图表 if (tab === 'performance') { setTimeout(() => this.initComparisonChart(), 100); } } // 绩效指标数据 performanceMetrics: PerformanceMetric[] = [ { id: 'project-completion', title: '项目完成率', value: '89', unit: '%', target: '85%', achievement: '104.7%', achievementClass: 'excellent', period: '本月', icon: 'assignment_turned_in', iconClass: 'success-icon', status: 'excellent', trend: { type: 'positive', value: '+5.2%', label: '较上月', icon: 'trending_up' }, progressValue: 89, progressClass: 'success-progress' }, { id: 'quality-rate', title: '优秀作品率', value: '76', unit: '%', target: '80%', achievement: '95.0%', achievementClass: 'good', period: '本月', icon: 'star', iconClass: 'warning-icon', status: 'good', trend: { type: 'positive', value: '+2.8%', label: '较上月', icon: 'trending_up' }, progressValue: 76, progressClass: 'warning-progress' }, { id: 'satisfaction', title: '客户满意度', value: '4.6', unit: '/5.0', target: '4.5', achievement: '102.2%', achievementClass: 'excellent', period: '本月', icon: 'sentiment_satisfied', iconClass: 'success-icon', status: 'excellent', trend: { type: 'positive', value: '+0.3', label: '较上月', icon: 'trending_up' }, progressValue: 92, progressClass: 'success-progress' }, { id: 'overdue-rate', title: '项目逾期率', value: '8', unit: '%', target: '≤10%', achievement: '120.0%', achievementClass: 'good', period: '本月', icon: 'schedule', iconClass: 'success-icon', status: 'good', trend: { type: 'positive', value: '-1.5%', label: '较上月', icon: 'trending_down' }, progressValue: 20, progressClass: 'success-progress' }, { id: 'team-efficiency', title: '团队效率指数', value: '92', unit: '分', target: '90分', achievement: '102.2%', achievementClass: 'excellent', period: '本月', icon: 'groups', iconClass: 'success-icon', status: 'excellent', trend: { type: 'positive', value: '+4.1', label: '较上月', icon: 'trending_up' }, progressValue: 92, progressClass: 'success-progress' } ]; // 筛选相关属性 selectedDepartment: string = ''; selectedLevel: string = ''; selectedTimeRange: string = 'month'; isFilterLoading: boolean = false; quickFilter: string = ''; // 绩效对比相关属性 comparisonMode: 'horizontal' | 'vertical' = 'horizontal'; selectedComparisonDimension: string = 'department'; selectedComparisonMetric: string[] = ['completion', 'quality']; comparisonChartType: 'bar' | 'line' | 'radar' = 'bar'; horizontalComparisonData: any[] = [ { id: 1, name: '技术部', icon: 'code', iconClass: 'tech-icon', completion: '92%', quality: '88%', efficiency: '85%', satisfaction: '90%', innovation: '95%' }, { id: 2, name: '设计部', icon: 'palette', iconClass: 'design-icon', completion: '88%', quality: '92%', efficiency: '82%', satisfaction: '87%', innovation: '90%' }, { id: 3, name: '产品部', icon: 'lightbulb', iconClass: 'product-icon', completion: '85%', quality: '85%', efficiency: '88%', satisfaction: '85%', innovation: '88%' }, { id: 4, name: '运营部', icon: 'trending_up', iconClass: 'operation-icon', completion: '90%', quality: '83%', efficiency: '90%', satisfaction: '88%', innovation: '82%' } ]; verticalComparisonData: any[] = [ { id: 1, name: '技术部', category: '研发部门', icon: 'code', iconClass: 'tech-icon', completion: '92%', quality: '88%', efficiency: '85%', satisfaction: '90%', innovation: '95%', overallScore: 90, rank: 1 }, { id: 2, name: '设计部', category: '创意部门', icon: 'palette', iconClass: 'design-icon', completion: '88%', quality: '92%', efficiency: '82%', satisfaction: '87%', innovation: '90%', overallScore: 88, rank: 2 }, { id: 3, name: '产品部', category: '策略部门', icon: 'lightbulb', iconClass: 'product-icon', completion: '85%', quality: '85%', efficiency: '88%', satisfaction: '85%', innovation: '88%', overallScore: 86, rank: 3 }, { id: 4, name: '运营部', category: '执行部门', icon: 'trending_up', iconClass: 'operation-icon', completion: '90%', quality: '83%', efficiency: '90%', satisfaction: '88%', innovation: '82%', overallScore: 87, rank: 4 } ]; horizontalDisplayedColumns: string[] = ['name', 'completion', 'quality', 'actions']; // 离职原因分析相关属性 resignationTimeRange: string = 'quarter'; reasonsChartType: 'pie' | 'doughnut' | 'bar' = 'pie'; totalResignations: number = 45; resignationRate: number = 8.5; averageTenure: number = 18; resignationCost: number = 125; resignationDepartments = [ { id: 'tech', name: '技术部', count: 15, selected: true }, { id: 'design', name: '设计部', count: 8, selected: true }, { id: 'product', name: '产品部', count: 12, selected: true }, { id: 'operation', name: '运营部', count: 10, selected: true } ]; resignationLevels = [ { id: 'junior', name: '初级', count: 18, selected: true }, { id: 'middle', name: '中级', count: 20, selected: true }, { 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: 'payments', percentage: 28.5, count: 14, description: '薪资水平低于市场平均水平,缺乏有竞争力的薪酬体系', trend: { direction: 'up', value: 3.2 } }, { id: 'career', name: '职业发展', category: 'development', categoryName: '发展机会', icon: 'trending_up', percentage: 22.3, count: 10, description: '缺乏明确的职业发展路径和晋升机会', trend: { direction: 'down', value: 2.1 } }, { id: 'workload', name: '工作压力', category: 'worklife', categoryName: '工作环境', icon: 'work', percentage: 18.7, count: 8, description: '工作强度过大,工作与生活平衡难以维持', trend: { direction: 'up', value: 3.5 } }, { id: 'management', name: '管理问题', category: 'management', categoryName: '管理层面', icon: 'supervisor_account', percentage: 15.2, count: 7, description: '管理方式不当,缺乏有效的沟通和反馈机制', trend: { direction: 'down', value: 1.8 } }, { id: 'culture', name: '企业文化', category: 'culture', categoryName: '文化氛围', icon: 'groups', percentage: 10.1, count: 5, description: '企业文化与个人价值观不匹配,团队氛围不佳', trend: { direction: 'up', value: 2.3 } }, { id: 'personal', name: '个人原因', category: 'personal', categoryName: '个人因素', icon: 'person', percentage: 5.2, count: 2, description: '个人家庭、健康等因素导致的离职', trend: { direction: 'down', value: 0.8 } } ]; // 简历分析相关属性 isDragOver: boolean = false; showAnalysisResults: boolean = false; isAnalyzing: boolean = false; currentAnalysisFile: File | null = null; analysisProgress: number = 0; matchDimensions: MatchDimension[] = [ { id: 1, name: '建模经验', score: 92, level: 'high', icon: 'view_in_ar' }, { id: 2, name: 'UI设计', score: 85, level: 'high', icon: 'design_services' }, { id: 3, name: '用户体验', score: 78, level: 'medium', icon: 'psychology' }, { id: 4, name: '团队协作', score: 88, level: 'high', icon: 'groups' }, { id: 5, name: '项目管理', score: 65, level: 'medium', icon: 'task_alt' } ]; recommendation: Recommendation = { title: '强烈推荐进入面试环节', level: 'recommend', levelText: '推荐', icon: 'thumb_up', summary: '候选人在核心技能方面表现优秀,具备丰富的建模经验和良好的设计基础,建议安排技术面试。', reasons: [ '3年以上3D建模经验,熟练掌握Maya、Blender等主流软件', 'UI设计基础扎实,有完整的项目作品集', '具备良好的团队协作能力和沟通技巧', '学习能力强,能够快速适应新技术和工具' ], concerns: [ '项目管理经验相对较少,需要在实际工作中加强', '对公司业务领域了解有限,需要一定的适应期' ] }; screeningInfo: ScreeningInfo[] = [ { id: 1, title: '学历要求', detail: '本科及以上学历', status: 'pass', statusText: '符合', icon: 'school' }, { id: 2, title: '工作经验', detail: '3年相关工作经验', status: 'pass', statusText: '符合', icon: 'work' }, { id: 3, title: '技能匹配', detail: '核心技能匹配度85%', status: 'pass', statusText: '优秀', icon: 'star' }, { id: 4, title: '薪资期望', detail: '期望薪资15K-18K', status: 'warning', statusText: '偏高', icon: 'payments' }, { id: 5, title: '到岗时间', detail: '可立即到岗', status: 'pass', statusText: '符合', icon: 'schedule' } ]; // 招聘阶段数据 recruitmentStages: RecruitmentStage[] = [ { id: 'resume-screening', title: '简历初筛', status: 'completed', statusText: '已完成', icon: 'description', candidateCount: 45, passRate: 65, evaluator: '张经理', lastUpdate: new Date('2024-01-15T10:30:00'), nextAction: '进入面试环节', evaluationResults: [ { candidateName: '李小明', result: 'pass', evaluator: '张经理', evaluationTime: new Date('2024-01-15T09:00:00'), score: 85, comments: '技能匹配度高,经验丰富' } ] }, { id: 'interview-assessment', title: '面试评估', status: 'active', statusText: '进行中', icon: 'record_voice_over', candidateCount: 29, passRate: 72, evaluator: '王总监', lastUpdate: new Date('2024-01-16T14:20:00'), nextAction: '安排技术面试', evaluationResults: [] }, { id: 'onboarding-evaluation', title: '入职评定', status: 'pending', statusText: '待开始', icon: 'how_to_reg', candidateCount: 21, passRate: 90, evaluator: '人事部', lastUpdate: new Date('2024-01-14T16:45:00'), nextAction: '准备入职材料' }, { id: 'probation-tracking', title: '试用期跟踪', status: 'active', statusText: '跟踪中', icon: 'trending_up', candidateCount: 18, passRate: 88, evaluator: '直属主管', lastUpdate: new Date('2024-01-16T11:15:00'), nextAction: '月度评估' } ]; // 打开晋升规则弹窗 openPromotionRules(): void { // 这里可以打开一个对话框显示晋升规则 console.log('打开晋升规则弹窗'); // 临时实现:显示alert alert('晋升规则:\n\n初级→中级:连续3个月优秀作品率超过80%,逾期率低于5%,客户满意度4.5以上\n\n中级→高级:连续6个月优秀作品率超过85%,逾期率低于3%,客户满意度4.8以上,有mentorship经验'); } // 招聘相关方法 uploadResume() { // 模拟文件上传过程 const input = document.createElement('input'); input.type = 'file'; input.accept = '.pdf,.doc,.docx'; input.onchange = (event: any) => { const file = event.target.files[0]; if (file) { this.handleFileUpload(file); } }; input.click(); } // 拖放相关方法 onDragOver(event: DragEvent) { event.preventDefault(); event.stopPropagation(); this.isDragOver = true; } onDragLeave(event: DragEvent) { event.preventDefault(); event.stopPropagation(); this.isDragOver = false; } onFileDrop(event: DragEvent) { event.preventDefault(); event.stopPropagation(); this.isDragOver = false; const files = event.dataTransfer?.files; if (files && files.length > 0) { const file = files[0]; this.handleFileUpload(file); } } private async handleFileUpload(file: File) { // 验证文件类型 const allowedTypes = [ 'application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'text/plain' ]; if (!allowedTypes.includes(file.type)) { this.showUploadError('不支持的文件格式,请上传PDF、DOC、DOCX或TXT文件'); return; } // 验证文件大小 (10MB) if (file.size > 10 * 1024 * 1024) { this.showUploadError('文件大小超过10MB限制'); return; } // 开始分析流程 this.currentAnalysisFile = file; this.isAnalyzing = true; this.analysisProgress = 0; this.showAnalysisResults = false; try { // 显示开始分析的反馈 this.snackBar.open(`正在分析简历 "${file.name}"...`, '关闭', { duration: 3000, horizontalPosition: 'center', verticalPosition: 'top' }); // 模拟进度更新 this.updateAnalysisProgress(0); // 提取文件文本内容 const resumeText = await this.doubaoAiService.extractTextFromFile(file); // 构建分析请求 const analysisRequest: ResumeAnalysisRequest = { resumeText: resumeText, jobPosition: '前端开发工程师', // 可以从当前招聘岗位获取 jobRequirements: [ '3年以上前端开发经验', '熟练掌握JavaScript、TypeScript', '熟悉Angular、React或Vue.js框架', '具备良好的团队协作能力', '本科及以上学历' ] }; // 调用豆包AI进行分析 const analysisResult = await this.doubaoAiService.analyzeResume(analysisRequest).toPromise(); if (analysisResult) { // 更新分析结果 this.updateAnalysisResults(analysisResult); this.showAnalysisResults = true; this.snackBar.open('简历分析完成!', '查看结果', { duration: 5000, horizontalPosition: 'center', verticalPosition: 'top' }); } } catch (error) { console.error('简历分析失败:', error); this.showUploadError('简历分析失败,请稍后重试'); } finally { this.isAnalyzing = false; this.analysisProgress = 100; } } // 招聘阶段相关方法 refreshRecruitmentData(): void { // 显示加载状态 this.snackBar.open('正在刷新招聘数据...', '', { duration: 1000, horizontalPosition: 'center', verticalPosition: 'top' }); // 模拟API调用刷新数据 setTimeout(() => { // 更新招聘阶段数据 this.recruitmentStages.forEach(stage => { stage.lastUpdate = new Date(); // 随机更新一些数据以模拟真实变化 if (Math.random() > 0.5) { stage.candidateCount += Math.floor(Math.random() * 3); stage.passRate = Math.min(100, stage.passRate + Math.floor(Math.random() * 5)); } }); this.snackBar.open('招聘数据已更新', '关闭', { duration: 3000, horizontalPosition: 'center', verticalPosition: 'top', panelClass: ['success-snackbar'] }); }, 1000); } openStageDetails(stage: RecruitmentStage): void { // 显示阶段详情信息 const stageInfo = this.getStageDetailInfo(stage); this.snackBar.open(`${stage.title} - ${stageInfo}`, '查看详情', { duration: 5000, horizontalPosition: 'center', verticalPosition: 'top', panelClass: ['info-snackbar'] }).onAction().subscribe(() => { // 这里可以打开详情弹窗或导航到详情页面 console.log('导航到详情页面:', stage); this.showStageDetailDialog(stage); }); } private getStageDetailInfo(stage: RecruitmentStage): string { switch (stage.status) { case 'completed': return `已完成,通过率${stage.passRate}%`; case 'active': return `进行中,当前${stage.candidateCount}人`; case 'pending': return `待开始,预计${stage.candidateCount}人`; case 'blocked': return `已暂停,需要处理`; default: return '状态未知'; } } private showStageDetailDialog(stage: RecruitmentStage): void { // 显示详细的阶段信息弹窗 const detailMessage = ` 阶段:${stage.title} 状态:${stage.statusText} 候选人数量:${stage.candidateCount}人 通过率:${stage.passRate}% 评估人:${stage.evaluator || '待分配'} 最近更新:${stage.lastUpdate.toLocaleString()} 下一步行动:${stage.nextAction || '无'} `; this.snackBar.open(detailMessage, '关闭', { duration: 8000, horizontalPosition: 'center', verticalPosition: 'top', panelClass: ['detail-snackbar'] }); } navigateToOnboarding(): void { this.snackBar.open('正在跳转到新人跟进模块...', '', { duration: 2000, horizontalPosition: 'center', verticalPosition: 'top' }); // 切换到入职跟进标签页 setTimeout(() => { this.switchTab('onboarding'); this.snackBar.open('已切换到新人跟进模块', '关闭', { duration: 3000, horizontalPosition: 'center', verticalPosition: 'top', panelClass: ['success-snackbar'] }); }, 1000); } viewProbationReports(): void { // 显示试用期报告信息 const reportSummary = ` 试用期跟踪报告: - 当前试用期员工:${this.recruitmentStages.find(s => s.id === 'probation-tracking')?.candidateCount || 0}人 - 通过率:${this.recruitmentStages.find(s => s.id === 'probation-tracking')?.passRate || 0}% - 本月评估:3人待评估 - 转正推荐:2人 `; this.snackBar.open(reportSummary, '查看详细报告', { duration: 6000, horizontalPosition: 'center', verticalPosition: 'top', panelClass: ['info-snackbar'] }).onAction().subscribe(() => { // 这里可以打开详细的试用期报告页面 console.log('打开试用期详细报告'); this.showProbationDetailReport(); }); } private showProbationDetailReport(): void { // 显示详细的试用期报告 this.snackBar.open('试用期详细报告功能开发中,敬请期待!', '关闭', { duration: 3000, horizontalPosition: 'center', verticalPosition: 'top', panelClass: ['warning-snackbar'] }); } // 滑动功能相关属性和方法 currentScrollPosition = 0; maxScrollPosition = 0; scrollIndicatorDots: number[] = []; initScrollIndicator(): void { // 计算滚动指示器点数 const container = document.querySelector('.stages-timeline-container'); const timeline = document.querySelector('.stages-timeline'); if (container && timeline) { const containerHeight = container.clientHeight; const timelineHeight = timeline.scrollHeight; if (timelineHeight > containerHeight) { this.maxScrollPosition = timelineHeight - containerHeight; const dotsCount = Math.ceil(timelineHeight / containerHeight); this.scrollIndicatorDots = Array.from({ length: dotsCount }, (_, i) => i); } } } onTimelineScroll(event: Event): void { const target = event.target as HTMLElement; this.currentScrollPosition = target.scrollTop; this.updateScrollIndicator(); } updateScrollIndicator(): void { const container = document.querySelector('.stages-timeline-container'); if (container) { const scrollPercentage = this.currentScrollPosition / this.maxScrollPosition; const activeIndex = Math.floor(scrollPercentage * this.scrollIndicatorDots.length); // 更新指示器状态 document.querySelectorAll('.scroll-dot').forEach((dot, index) => { if (index <= activeIndex) { dot.classList.add('active'); } else { dot.classList.remove('active'); } }); } } scrollToPosition(index: number): void { const container = document.querySelector('.stages-timeline-container'); if (container) { const scrollPosition = (index / this.scrollIndicatorDots.length) * this.maxScrollPosition; container.scrollTo({ top: scrollPosition, behavior: 'smooth' }); } } // 绩效筛选相关方法 onDepartmentChange(event: any): void { console.log('部门筛选变更:', event.value); this.selectedDepartment = event.value; } onLevelChange(event: any): void { console.log('职级筛选变更:', event.value); this.selectedLevel = event.value; } onTimeRangeChange(event: any): void { console.log('时间范围变更:', event.value); this.selectedTimeRange = event.value; } resetFilters(): void { this.selectedDepartment = ''; this.selectedLevel = ''; this.selectedTimeRange = 'month'; this.quickFilter = ''; console.log('重置筛选条件'); this.applyFilters(); } exportData(): void { console.log('导出数据'); // 这里可以实现数据导出功能 } applyQuickFilter(filterType: string): void { this.quickFilter = this.quickFilter === filterType ? '' : filterType; console.log('应用快速筛选:', filterType); this.applyFilters(); } private showUploadError(message: string) { this.snackBar.open(message, '关闭', { duration: 3000, panelClass: ['error-snackbar'] }); } private updateAnalysisProgress(progress: number) { this.analysisProgress = progress; } private updateAnalysisResults(response: ResumeAnalysisResponse) { // 更新匹配维度 this.matchDimensions = response.matchDimensions.map(dim => ({ id: dim.id, name: dim.name, score: dim.score, level: dim.score >= 80 ? 'high' : dim.score >= 60 ? 'medium' : 'low', icon: this.getSkillIcon(dim.name) })); // 更新推荐结论 this.recommendation = { title: response.recommendation.title, level: response.recommendation.level, levelText: this.getRecommendationLevelText(response.recommendation.level), icon: this.getRecommendationIcon(response.recommendation.level), summary: response.recommendation.summary, reasons: response.recommendation.reasons, concerns: response.recommendation.concerns }; // 更新筛选信息 this.screeningInfo = response.screeningInfo.map(info => ({ id: info.id, title: info.title, detail: info.detail, status: info.status, statusText: this.getStatusText(info.status), icon: this.getScreeningIcon(info.title) })); this.showAnalysisResults = true; } private getSkillIcon(skillName: string): string { const iconMap: { [key: string]: string } = { '建模经验': 'view_in_ar', 'UI设计': 'design_services', '用户体验': 'psychology', '团队协作': 'groups', '项目管理': 'task_alt', '技术能力': 'code', '沟通能力': 'chat', '学习能力': 'school' }; return iconMap[skillName] || 'star'; } private getRecommendationLevelText(level: string): string { const levelMap: { [key: string]: string } = { 'recommend': '推荐', 'consider': '考虑', 'reject': '不推荐' }; return levelMap[level] || '待定'; } private getRecommendationIcon(level: string): string { const iconMap: { [key: string]: string } = { 'recommend': 'thumb_up', 'consider': 'help', 'reject': 'thumb_down' }; return iconMap[level] || 'help'; } private getStatusText(status: string): string { const statusMap: { [key: string]: string } = { 'pass': '符合', 'warning': '注意', 'fail': '不符合' }; return statusMap[status] || '未知'; } private getScreeningIcon(title: string): string { const iconMap: { [key: string]: string } = { '学历要求': 'school', '工作经验': 'work', '技能匹配': 'star', '薪资期望': 'payments', '到岗时间': 'schedule' }; return iconMap[title] || 'info'; } // 显示上传反馈 showUploadFeedback(fileName: string) { // 创建临时反馈元素 const feedback = document.createElement('div'); feedback.className = 'upload-feedback'; feedback.innerHTML = ` check_circle 简历 "${fileName}" 上传成功! `; feedback.style.cssText = ` position: fixed; top: 20px; right: 20px; background: #4CAF50; color: white; padding: 12px 20px; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 1000; display: flex; align-items: center; gap: 8px; animation: slideInRight 0.3s ease-out; `; document.body.appendChild(feedback); // 3秒后移除反馈 setTimeout(() => { feedback.style.animation = 'slideOutRight 0.3s ease-in'; setTimeout(() => { document.body.removeChild(feedback); }, 300); }, 3000); } // 绩效相关方法 applyFilters() { // 显示加载状态 this.isFilterLoading = true; // 模拟筛选过程 setTimeout(() => { this.isFilterLoading = false; // 应用筛选条件 console.log('应用筛选条件:', { department: this.selectedDepartment, timeRange: this.selectedTimeRange }); // 显示筛选成功反馈 this.showFilterFeedback(); }, 1000); } // 显示筛选反馈 showFilterFeedback() { const feedback = document.createElement('div'); feedback.className = 'filter-feedback'; feedback.innerHTML = ` filter_list 筛选条件已应用 `; feedback.style.cssText = ` position: fixed; top: 20px; right: 20px; background: #2196F3; color: white; padding: 12px 20px; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 1000; display: flex; align-items: center; gap: 8px; animation: slideInRight 0.3s ease-out; `; document.body.appendChild(feedback); setTimeout(() => { feedback.style.animation = 'slideOutRight 0.3s ease-in'; setTimeout(() => { document.body.removeChild(feedback); }, 300); }, 2000); } // 初始化图表(这里需要后续集成ECharts) private initCharts() { // 初始化离职原因图表 this.initResignationChart(); // 初始化对比图表 this.initComparisonChart(); } // 初始化离职原因图表 private initResignationChart() { if (!this.resignationChartRef?.nativeElement) return; const ctx = this.resignationChartRef.nativeElement.getContext('2d'); if (!ctx) return; // 销毁现有图表 if (this.resignationChart) { this.resignationChart.destroy(); } const chartData = { labels: this.resignationReasons.map(reason => reason.name), datasets: [{ data: this.resignationReasons.map(reason => reason.percentage), backgroundColor: [ '#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7', '#DDA0DD', '#98D8C8', '#F7DC6F' ], borderWidth: 2, borderColor: '#fff' }] }; let config: ChartConfiguration; // 通用动画配置 const animationConfig = { duration: 800, easing: 'easeInOutQuart' as const, delay: (context: any) => context.dataIndex * 50 }; switch (this.reasonsChartType) { case 'pie': config = { type: 'pie', data: chartData, options: { responsive: true, maintainAspectRatio: false, animation: animationConfig, plugins: { legend: { position: 'right', labels: { usePointStyle: true, padding: 20, font: { size: 12 } } }, tooltip: { backgroundColor: 'rgba(0, 0, 0, 0.8)', titleColor: '#fff', bodyColor: '#fff', borderColor: '#4ECDC4', borderWidth: 1, callbacks: { label: (context) => { const label = context.label || ''; const value = context.parsed; const reason = this.resignationReasons[context.dataIndex]; return `${label}: ${value}% (${reason.count}人)`; } } } } } }; break; case 'doughnut': config = { type: 'doughnut', data: chartData, options: { responsive: true, maintainAspectRatio: false, animation: animationConfig, plugins: { legend: { position: 'right', labels: { usePointStyle: true, padding: 20, font: { size: 12 } } }, tooltip: { backgroundColor: 'rgba(0, 0, 0, 0.8)', titleColor: '#fff', bodyColor: '#fff', borderColor: '#4ECDC4', borderWidth: 1, callbacks: { label: (context) => { const label = context.label || ''; const value = context.parsed; const reason = this.resignationReasons[context.dataIndex]; return `${label}: ${value}% (${reason.count}人)`; } } } } } }; break; case 'bar': config = { type: 'bar', data: { labels: this.resignationReasons.map(reason => reason.name), datasets: [{ label: '离职占比 (%)', data: this.resignationReasons.map(reason => reason.percentage), backgroundColor: '#4ECDC4', borderColor: '#45B7D1', borderWidth: 1, borderRadius: 4, borderSkipped: false }] }, options: { responsive: true, maintainAspectRatio: false, animation: { duration: 800, easing: 'easeInOutQuart' as const, delay: (context: any) => context.dataIndex * 100 }, plugins: { legend: { display: false }, tooltip: { backgroundColor: 'rgba(0, 0, 0, 0.8)', titleColor: '#fff', bodyColor: '#fff', borderColor: '#4ECDC4', borderWidth: 1, callbacks: { label: (context) => { const value = context.parsed.y; const reason = this.resignationReasons[context.dataIndex]; return `${reason.name}: ${value}% (${reason.count}人)`; } } } }, scales: { y: { beginAtZero: true, max: 35, grid: { color: 'rgba(0, 0, 0, 0.1)' }, ticks: { font: { size: 11 }, callback: function(value) { return value + '%'; } } }, x: { grid: { display: false }, ticks: { maxRotation: 45, minRotation: 0, font: { size: 11 } } } } } }; break; default: return; } this.resignationChart = new Chart(ctx, config); } // 切换图表类型 - 优化性能 onChartTypeChange() { // 添加加载状态 const chartContainer = this.resignationChartRef?.nativeElement?.parentElement; if (chartContainer) { chartContainer.style.opacity = '0.7'; chartContainer.style.transition = 'opacity 0.3s ease'; } // 使用 setTimeout 确保 UI 更新 setTimeout(() => { this.initResignationChart(); // 恢复透明度 if (chartContainer) { setTimeout(() => { chartContainer.style.opacity = '1'; }, 100); } }, 50); } // 初始化对比图表 private initComparisonChart() { if (!this.comparisonChartRef?.nativeElement) { return; } const ctx = this.comparisonChartRef.nativeElement.getContext('2d'); if (!ctx) return; // 销毁现有图表 if (this.comparisonChart) { this.comparisonChart.destroy(); } // 根据图表类型创建不同的配置 let config: ChartConfiguration; if (this.comparisonChartType === 'bar') { config = { type: 'bar', data: { labels: ['1月', '2月', '3月', '4月', '5月', '6月'], datasets: [ { label: 'UI设计部', data: [85, 88, 92, 89, 91, 94], backgroundColor: 'rgba(76, 175, 80, 0.8)', borderColor: 'rgba(76, 175, 80, 1)', borderWidth: 1 }, { label: '3D建模部', data: [78, 82, 85, 87, 84, 89], backgroundColor: 'rgba(33, 150, 243, 0.8)', borderColor: 'rgba(33, 150, 243, 1)', borderWidth: 1 }, { label: '前端开发部', data: [82, 85, 88, 86, 90, 92], backgroundColor: 'rgba(255, 152, 0, 0.8)', borderColor: 'rgba(255, 152, 0, 1)', borderWidth: 1 } ] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'top', labels: { usePointStyle: true, padding: 20 } }, tooltip: { mode: 'index', intersect: false, callbacks: { label: (context) => { return `${context.dataset.label}: ${context.parsed.y}%`; } } } }, scales: { x: { grid: { display: false } }, y: { beginAtZero: true, max: 100, ticks: { callback: (value) => `${value}%` } } } } }; } else if (this.comparisonChartType === 'line') { config = { type: 'line', data: { labels: ['1月', '2月', '3月', '4月', '5月', '6月'], datasets: [ { label: 'UI设计部', data: [85, 88, 92, 89, 91, 94], borderColor: 'rgba(76, 175, 80, 1)', backgroundColor: 'rgba(76, 175, 80, 0.1)', borderWidth: 3, fill: true, tension: 0.4, pointBackgroundColor: 'rgba(76, 175, 80, 1)', pointBorderColor: '#fff', pointBorderWidth: 2, pointRadius: 6 }, { label: '3D建模部', data: [78, 82, 85, 87, 84, 89], borderColor: 'rgba(33, 150, 243, 1)', backgroundColor: 'rgba(33, 150, 243, 0.1)', borderWidth: 3, fill: true, tension: 0.4, pointBackgroundColor: 'rgba(33, 150, 243, 1)', pointBorderColor: '#fff', pointBorderWidth: 2, pointRadius: 6 }, { label: '前端开发部', data: [82, 85, 88, 86, 90, 92], borderColor: 'rgba(255, 152, 0, 1)', backgroundColor: 'rgba(255, 152, 0, 0.1)', borderWidth: 3, fill: true, tension: 0.4, pointBackgroundColor: 'rgba(255, 152, 0, 1)', pointBorderColor: '#fff', pointBorderWidth: 2, pointRadius: 6 } ] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'top', labels: { usePointStyle: true, padding: 20 } }, tooltip: { mode: 'index', intersect: false, callbacks: { label: (context) => { return `${context.dataset.label}: ${context.parsed.y}%`; } } } }, scales: { x: { grid: { display: false } }, y: { beginAtZero: true, max: 100, ticks: { callback: (value) => `${value}%` } } }, interaction: { mode: 'nearest', axis: 'x', intersect: false } } }; } else { // radar config = { type: 'radar', data: { labels: ['完成率', '优秀率', '满意度', '按时率', '创新度', '协作度'], datasets: [ { label: 'UI设计部', data: [92, 78, 88, 92, 85, 90], borderColor: 'rgba(76, 175, 80, 1)', backgroundColor: 'rgba(76, 175, 80, 0.2)', borderWidth: 2, pointBackgroundColor: 'rgba(76, 175, 80, 1)', pointBorderColor: '#fff', pointBorderWidth: 2 }, { label: '3D建模部', data: [85, 82, 90, 88, 92, 85], borderColor: 'rgba(33, 150, 243, 1)', backgroundColor: 'rgba(33, 150, 243, 0.2)', borderWidth: 2, pointBackgroundColor: 'rgba(33, 150, 243, 1)', pointBorderColor: '#fff', pointBorderWidth: 2 }, { label: '前端开发部', data: [88, 75, 85, 88, 88, 92], borderColor: 'rgba(255, 152, 0, 1)', backgroundColor: 'rgba(255, 152, 0, 0.2)', borderWidth: 2, pointBackgroundColor: 'rgba(255, 152, 0, 1)', pointBorderColor: '#fff', pointBorderWidth: 2 } ] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'top', labels: { usePointStyle: true, padding: 20 } } }, scales: { r: { beginAtZero: true, max: 100, ticks: { stepSize: 20, callback: (value) => `${value}%` }, grid: { color: 'rgba(0, 0, 0, 0.1)' }, angleLines: { color: 'rgba(0, 0, 0, 0.1)' } } } } }; } this.comparisonChart = new Chart(ctx, config); } // 切换对比图表类型 onComparisonChartTypeChange() { // 添加加载状态 const chartContainer = this.comparisonChartRef?.nativeElement?.parentElement; if (chartContainer) { chartContainer.style.opacity = '0.7'; chartContainer.style.transition = 'opacity 0.3s ease'; } // 使用 setTimeout 确保 UI 更新 setTimeout(() => { this.initComparisonChart(); // 恢复透明度 if (chartContainer) { setTimeout(() => { chartContainer.style.opacity = '1'; }, 100); } }, 50); } // 拖拽排序 drop(event: CdkDragDrop) { moveItemInArray(this.todoItems, event.previousIndex, event.currentIndex); } // 获取优先级颜色 getPriorityColor(priority: string): string { switch (priority) { case 'high': return '#ff4757'; case 'medium': return '#ffa502'; case 'low': return '#2ed573'; default: return '#a4b0be'; } } // 获取空缺岗位图标 getVacancyIcon(urgency: string): string { switch (urgency) { case 'urgent': return 'warning'; case 'normal': return 'info'; default: return 'help'; } } // 获取部门颜色 getDepartmentColor(department: string): string { const colors: { [key: string]: string } = { 'UI设计部': '#2196F3', '3D建模部': '#4CAF50', '前端开发部': '#FF9800', '产品部': '#9C27B0' }; return colors[department] || '#757575'; } // 获取优先级类名 getPriorityClass(priority: string): string { return `priority-${priority}`; } toggleTodoList(): void { this.showTodoList = !this.showTodoList; } // 悬浮待办事项面板相关属性和方法 isTodoPanelOpen: boolean = false; get todoCount(): number { return this.todoList.length; } toggleTodoPanel(): void { this.isTodoPanelOpen = !this.isTodoPanelOpen; } // 获取甜甜圈图表特定选项 private getDoughnutOptions(): any { return { cutout: '60%' }; } // 初始化所有图表 private initializeCharts() { this.initPieChart(); this.initLineChart(); this.initRadarChart(); this.initResignationChart(); this.initComparisonChart(); } // 初始化职级分布饼图 private initPieChart() { if (!this.pieChartRef?.nativeElement) return; const ctx = this.pieChartRef.nativeElement.getContext('2d'); if (!ctx) return; const config: ChartConfiguration = { type: 'doughnut', data: { labels: this.rankDistribution.map(item => item.level), datasets: [{ data: this.rankDistribution.map(item => item.percentage), backgroundColor: this.rankDistribution.map(item => item.color), borderWidth: 0, hoverBorderWidth: 2, hoverBorderColor: '#fff' }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, tooltip: { callbacks: { label: (context) => { const label = context.label || ''; const value = context.parsed; const count = this.rankDistribution[context.dataIndex].count; return `${label}: ${value}% (${count}人)`; } } } }, ...(this.getDoughnutOptions()) } }; this.pieChart = new Chart(ctx, config); } // 初始化入职离职趋势折线图 private initLineChart() { if (!this.lineChartRef?.nativeElement) return; const ctx = this.lineChartRef.nativeElement.getContext('2d'); if (!ctx) return; const config: ChartConfiguration = { type: 'line', data: { labels: this.monthlyHireData.map(item => item.month), datasets: [ { label: '入职人数', data: this.monthlyHireData.map(item => item.hired), borderColor: '#4CAF50', backgroundColor: 'rgba(76, 175, 80, 0.1)', tension: 0.4, fill: true }, { label: '离职人数', data: this.monthlyHireData.map(item => item.left), borderColor: '#f44336', backgroundColor: 'rgba(244, 67, 54, 0.1)', tension: 0.4, fill: true } ] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'top' }, tooltip: { mode: 'index', intersect: false } }, scales: { y: { beginAtZero: true, grid: { color: 'rgba(0, 0, 0, 0.1)' } }, x: { grid: { color: 'rgba(0, 0, 0, 0.1)' } } }, interaction: { mode: 'nearest', axis: 'x', intersect: false } } }; this.lineChart = new Chart(ctx, config); } // 初始化绩效总览雷达图 private initRadarChart() { if (!this.radarChartRef?.nativeElement) return; const ctx = this.radarChartRef.nativeElement.getContext('2d'); if (!ctx) return; const config: ChartConfiguration = { type: 'radar', data: { labels: ['项目完成率', '优秀作品率', '客户满意度', '逾期率'], datasets: this.departmentPerformance.map((dept, index) => ({ label: dept.department, data: [ dept.completionRate, dept.excellentWorkRate, dept.satisfactionRate, 100 - dept.overdueRate // 逾期率转换为正向指标 ], borderColor: this.getDepartmentColor(dept.department), backgroundColor: this.getDepartmentColor(dept.department) + '20', pointBackgroundColor: this.getDepartmentColor(dept.department), pointBorderColor: '#fff', pointHoverBackgroundColor: '#fff', pointHoverBorderColor: this.getDepartmentColor(dept.department) })) }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'bottom' } }, scales: { r: { beginAtZero: true, max: 100, grid: { color: 'rgba(0, 0, 0, 0.1)' }, angleLines: { color: 'rgba(0, 0, 0, 0.1)' }, pointLabels: { font: { size: 12 } } } } } }; this.radarChart = new Chart(ctx, config); } getPriorityLabel(priority: string): string { switch (priority) { case 'high': return '紧急'; case 'medium': return '重要'; case 'low': return '一般'; default: return '未知'; } } getTypeLabel(type: string): string { switch (type) { case 'resume': return '简历筛选'; case 'onboarding': return '入职跟进'; case 'resignation': return '离职处理'; default: return type; } } getStatusLabel(status: string): string { const statusMap: { [key: string]: string } = { 'pending': '待处理', 'in_progress': '进行中', 'completed': '已完成', 'urgent': '紧急', 'normal': '普通' }; return statusMap[status] || status; } // 拖拽排序功能 onTodoDrop(event: CdkDragDrop): void { if (event.previousIndex !== event.currentIndex) { moveItemInArray(this.todoItems, event.previousIndex, event.currentIndex); this.showTodoDragFeedback(); } } // 更新待办事项状态 updateTodoStatus(todo: TodoItem, status: 'pending' | 'completed' | 'in_progress'): void { const oldStatus = todo.status; todo.status = status; this.showTodoStatusFeedback(todo.title, oldStatus, status); } private showTodoDragFeedback(): void { // 创建反馈元素 const feedback = document.createElement('div'); feedback.className = 'ios-drag-feedback'; feedback.innerHTML = `
swap_vert 任务顺序已更新
`; // 添加样式 feedback.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(0, 122, 255, 0.95); color: white; padding: 12px 20px; border-radius: 12px; box-shadow: 0 8px 32px rgba(0, 122, 255, 0.3); z-index: 10000; display: flex; align-items: center; gap: 8px; font-size: 14px; font-weight: 500; backdrop-filter: blur(20px); animation: iosFeedbackIn 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); `; document.body.appendChild(feedback); // 2秒后移除 setTimeout(() => { feedback.style.animation = 'iosFeedbackOut 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94)'; setTimeout(() => { if (feedback.parentNode) { feedback.parentNode.removeChild(feedback); } }, 300); }, 2000); } private showTodoStatusFeedback(title: string, oldStatus: string, newStatus: string): void { const statusMap = { 'pending': '待处理', 'in_progress': '进行中', 'completed': '已完成' }; const iconMap = { 'pending': 'schedule', 'in_progress': 'play_circle', 'completed': 'check_circle' }; const colorMap = { 'pending': '#8E8E93', 'in_progress': '#FF9500', 'completed': '#34C759' }; // 创建反馈元素 const feedback = document.createElement('div'); feedback.className = 'ios-status-feedback'; feedback.innerHTML = `
${iconMap[newStatus as keyof typeof iconMap]}
${title}
${statusMap[oldStatus as keyof typeof statusMap]} → ${statusMap[newStatus as keyof typeof statusMap]}
`; // 添加样式 feedback.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(255, 255, 255, 0.95); color: #1D1D1F; padding: 16px 20px; border-radius: 16px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15); z-index: 10000; backdrop-filter: blur(20px); animation: iosFeedbackIn 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); min-width: 280px; `; // 添加内部样式 const style = document.createElement('style'); style.textContent = ` .ios-feedback-content { display: flex; align-items: center; gap: 12px; } .ios-feedback-text { flex: 1; } .ios-feedback-title { font-size: 16px; font-weight: 600; margin-bottom: 4px; } .ios-feedback-subtitle { font-size: 14px; color: #8E8E93; } @keyframes iosFeedbackIn { from { opacity: 0; transform: translate(-50%, -50%) scale(0.8); } to { opacity: 1; transform: translate(-50%, -50%) scale(1); } } @keyframes iosFeedbackOut { from { opacity: 1; transform: translate(-50%, -50%) scale(1); } to { opacity: 0; transform: translate(-50%, -50%) scale(0.8); } } `; document.head.appendChild(style); document.body.appendChild(feedback); // 2.5秒后移除 setTimeout(() => { feedback.style.animation = 'iosFeedbackOut 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94)'; setTimeout(() => { if (feedback.parentNode) { feedback.parentNode.removeChild(feedback); } if (style.parentNode) { style.parentNode.removeChild(style); } }, 300); }, 2500); } // 处理按钮按压效果 isButtonPressed = false; handleButtonPress(action: 'press' | 'release') { this.isButtonPressed = action === 'press'; } // 处理检查点状态变化 onCheckpointChange(checkpoint: OnboardingCheckpoint, event: any) { checkpoint.completed = event.checked; // 显示状态变化反馈 this.showCheckpointFeedback(checkpoint.title, checkpoint.completed); // 如果完成,添加完成动画效果 if (checkpoint.completed) { this.animateCheckpointCompletion(checkpoint.id); } } // 显示检查点反馈 showCheckpointFeedback(title: string, completed: boolean) { const feedback = document.createElement('div'); feedback.className = 'checkpoint-feedback'; feedback.innerHTML = ` ${completed ? 'check_circle' : 'radio_button_unchecked'} ${completed ? '已完成' : '已取消'}: ${title} `; feedback.style.cssText = ` position: fixed; top: 20px; right: 20px; background: ${completed ? '#4CAF50' : '#FF9800'}; color: white; padding: 12px 20px; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 1000; display: flex; align-items: center; gap: 8px; animation: slideInRight 0.3s ease-out; max-width: 300px; `; document.body.appendChild(feedback); setTimeout(() => { feedback.style.animation = 'slideOutRight 0.3s ease-in'; setTimeout(() => { document.body.removeChild(feedback); }, 300); }, 2500); } // 检查点完成动画 animateCheckpointCompletion(checkpointId: number) { const element = document.querySelector(`[data-checkpoint-id="${checkpointId}"]`); if (element) { element.classList.add('completed-animation'); setTimeout(() => { element.classList.remove('completed-animation'); }, 600); } } // 新人进度更新 updateNewbieProgress(newbieId: number, progress: number) { const newbie = this.newbieList.find(n => n.id === newbieId); if (newbie) { newbie.progress = Math.min(100, Math.max(0, progress)); this.showProgressFeedback(newbie.name, newbie.progress); } } // 显示进度反馈 showProgressFeedback(name: string, progress: number) { const feedback = document.createElement('div'); feedback.className = 'progress-feedback'; feedback.innerHTML = ` trending_up ${name} 的进度已更新至 ${progress}% `; feedback.style.cssText = ` position: fixed; top: 20px; right: 20px; background: #9C27B0; color: white; padding: 12px 20px; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 1000; display: flex; align-items: center; gap: 8px; animation: slideInRight 0.3s ease-out; `; document.body.appendChild(feedback); setTimeout(() => { feedback.style.animation = 'slideOutRight 0.3s ease-in'; setTimeout(() => { document.body.removeChild(feedback); }, 300); }, 2000); } // 绩效指标相关方法 refreshMetrics() { console.log('刷新绩效指标数据'); // 模拟数据刷新 this.performanceMetrics.forEach(metric => { // 随机更新数值以模拟实时数据 const currentValue = parseInt(metric.value); const variation = Math.random() * 4 - 2; // -2 到 +2 的随机变化 const newValue = Math.max(0, Math.min(100, currentValue + variation)); metric.value = Math.round(newValue).toString(); // 更新进度条值 if (metric.id === 'overdue-rate') { metric.progressValue = Math.max(0, 100 - newValue); // 逾期率反向显示 } else { metric.progressValue = newValue; } }); this.showMetricsRefreshFeedback(); } exportMetrics() { console.log('导出绩效指标报告'); // 模拟导出功能 const reportData = { exportTime: new Date().toISOString(), metrics: this.performanceMetrics.map(metric => ({ title: metric.title, value: metric.value + metric.unit, target: metric.target, achievement: metric.achievement, trend: metric.trend.value + ' ' + metric.trend.label })) }; console.log('报告数据:', reportData); this.showExportFeedback(); } viewMetricDetails(metricId: string) { console.log('查看指标详情:', metricId); const metric = this.performanceMetrics.find(m => m.id === metricId); if (metric) { this.showMetricDetailsFeedback(metric); } } viewMetricTrend(metricId: string) { console.log('查看指标趋势:', metricId); const metric = this.performanceMetrics.find(m => m.id === metricId); if (metric) { this.showMetricTrendFeedback(metric); } } private showMetricDetailsFeedback(metric: PerformanceMetric) { const feedback = document.createElement('div'); feedback.className = 'metric-details-feedback'; feedback.innerHTML = ` `; feedback.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; border-radius: 12px; box-shadow: 0 8px 32px rgba(0,0,0,0.15); padding: 24px; z-index: 10000; min-width: 400px; max-width: 500px; animation: slideInScale 0.3s ease-out; `; const style = document.createElement('style'); style.textContent = ` @keyframes slideInScale { from { opacity: 0; transform: translate(-50%, -50%) scale(0.9); } to { opacity: 1; transform: translate(-50%, -50%) scale(1); } } .metric-details-feedback .feedback-header { display: flex; align-items: center; gap: 16px; margin-bottom: 20px; font-size: 18px; font-weight: 600; color: #333; line-height: 1.4; } .metric-details-feedback .feedback-header mat-icon { color: #6366f1; font-size: 24px; width: 24px; height: 24px; flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; } .metric-details-feedback .metric-info { display: flex; flex-direction: column; gap: 12px; } .metric-details-feedback .info-item { display: flex; justify-content: space-between; align-items: center; padding: 8px 0; border-bottom: 1px solid #f0f0f0; } .metric-details-feedback .info-item:last-child { border-bottom: none; } .metric-details-feedback .label { color: #666; font-weight: 500; } .metric-details-feedback .value { font-weight: 600; display: flex; align-items: center; gap: 8px; line-height: 1.4; } .metric-details-feedback .value mat-icon { font-size: 18px; width: 18px; height: 18px; flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; } .metric-details-feedback .value.excellent { color: #10b981; } .metric-details-feedback .value.good { color: #3b82f6; } .metric-details-feedback .value.warning { color: #f59e0b; } .metric-details-feedback .value.poor { color: #ef4444; } .metric-details-feedback .trend-positive { color: #10b981; } .metric-details-feedback .trend-negative { color: #ef4444; } .metric-details-feedback .trend-neutral { color: #6b7280; } `; document.head.appendChild(style); const overlay = document.createElement('div'); overlay.style.cssText = ` position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 9999; animation: fadeIn 0.3s ease-out; `; overlay.addEventListener('click', () => { document.body.removeChild(overlay); document.body.removeChild(feedback); document.head.removeChild(style); }); document.body.appendChild(overlay); document.body.appendChild(feedback); setTimeout(() => { if (document.body.contains(overlay)) { document.body.removeChild(overlay); document.body.removeChild(feedback); document.head.removeChild(style); } }, 5000); } private showMetricTrendFeedback(metric: PerformanceMetric) { const feedback = document.createElement('div'); feedback.className = 'metric-trend-feedback'; feedback.innerHTML = ` `; feedback.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; border-radius: 12px; box-shadow: 0 8px 32px rgba(0,0,0,0.15); padding: 24px; z-index: 10000; min-width: 450px; max-width: 550px; animation: slideInScale 0.3s ease-out; `; const style = document.createElement('style'); style.textContent = ` .metric-trend-feedback .feedback-header { display: flex; align-items: center; gap: 16px; margin-bottom: 20px; font-size: 18px; font-weight: 600; color: #333; line-height: 1.4; } .metric-trend-feedback .feedback-header mat-icon { color: #6366f1; font-size: 24px; width: 24px; height: 24px; flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; } .metric-trend-feedback .trend-chart-placeholder { background: #f8fafc; border: 2px dashed #cbd5e1; border-radius: 8px; padding: 40px; text-align: center; margin-bottom: 20px; } .metric-trend-feedback .trend-chart-placeholder mat-icon { font-size: 48px; width: 48px; height: 48px; color: #94a3b8; margin-bottom: 12px; } .metric-trend-feedback .trend-chart-placeholder p { color: #64748b; margin: 0; font-size: 14px; } .metric-trend-feedback .trend-summary { display: flex; flex-direction: column; gap: 12px; } .metric-trend-feedback .summary-item { display: flex; justify-content: space-between; align-items: center; padding: 8px 0; border-bottom: 1px solid #f0f0f0; } .metric-trend-feedback .summary-item:last-child { border-bottom: none; } .metric-trend-feedback .label { color: #666; font-weight: 500; } .metric-trend-feedback .value { font-weight: 600; display: flex; align-items: center; gap: 8px; line-height: 1.4; } .metric-trend-feedback .value mat-icon { font-size: 18px; width: 18px; height: 18px; flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; } .metric-trend-feedback .trend-positive { color: #10b981; } .metric-trend-feedback .trend-negative { color: #ef4444; } .metric-trend-feedback .trend-neutral { color: #6b7280; } `; document.head.appendChild(style); const overlay = document.createElement('div'); overlay.style.cssText = ` position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 9999; animation: fadeIn 0.3s ease-out; `; overlay.addEventListener('click', () => { document.body.removeChild(overlay); document.body.removeChild(feedback); document.head.removeChild(style); }); document.body.appendChild(overlay); document.body.appendChild(feedback); setTimeout(() => { if (document.body.contains(overlay)) { document.body.removeChild(overlay); document.body.removeChild(feedback); document.head.removeChild(style); } }, 5000); } private showMetricsRefreshFeedback() { const feedback = document.createElement('div'); feedback.className = 'metrics-feedback'; feedback.innerHTML = ` refresh 绩效指标已刷新 `; feedback.style.cssText = ` position: fixed; top: 20px; right: 20px; background: #4CAF50; color: white; padding: 12px 20px; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 1000; display: flex; align-items: center; gap: 8px; animation: slideInRight 0.3s ease-out; `; document.body.appendChild(feedback); setTimeout(() => { feedback.style.animation = 'slideOutRight 0.3s ease-in'; setTimeout(() => { document.body.removeChild(feedback); }, 300); }, 2000); } private showExportFeedback() { const feedback = document.createElement('div'); feedback.className = 'export-feedback'; feedback.innerHTML = ` file_download 报告导出成功 `; feedback.style.cssText = ` position: fixed; top: 20px; right: 20px; background: #2196F3; color: white; padding: 12px 20px; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 1000; display: flex; align-items: center; gap: 8px; animation: slideInRight 0.3s ease-out; `; document.body.appendChild(feedback); setTimeout(() => { feedback.style.animation = 'slideOutRight 0.3s ease-in'; setTimeout(() => { document.body.removeChild(feedback); }, 300); }, 2000); } // 绩效对比相关方法 onComparisonModeChange(event: any): void { this.comparisonMode = event.value; this.updateComparison(); } updateComparison(): void { // 更新显示的列 this.horizontalDisplayedColumns = ['name', ...this.selectedComparisonMetric, 'actions']; // 根据对比维度更新数据 this.updateComparisonData(); } private updateComparisonData(): void { // 模拟根据不同维度更新数据 switch (this.selectedComparisonDimension) { case 'department': // 部门对比数据已经设置 break; case 'period': this.updatePeriodComparisonData(); break; case 'individual': this.updateIndividualComparisonData(); break; case 'project': this.updateProjectComparisonData(); break; } } private updatePeriodComparisonData(): void { this.horizontalComparisonData = [ { id: 1, name: '2024年Q1', icon: 'calendar_today', iconClass: 'period-icon', completion: '88%', quality: '85%', efficiency: '82%', satisfaction: '87%', innovation: '80%' }, { id: 2, name: '2024年Q2', icon: 'calendar_today', iconClass: 'period-icon', completion: '90%', quality: '88%', efficiency: '85%', satisfaction: '89%', innovation: '85%' }, { id: 3, name: '2024年Q3', icon: 'calendar_today', iconClass: 'period-icon', completion: '92%', quality: '90%', efficiency: '88%', satisfaction: '91%', innovation: '88%' } ]; this.verticalComparisonData = this.horizontalComparisonData.map((item, index) => ({ ...item, category: '季度数据', overallScore: 85 + index * 3, rank: index + 1 })); } private updateIndividualComparisonData(): void { this.horizontalComparisonData = [ { id: 1, name: '张三', icon: 'person', iconClass: 'person-icon', completion: '95%', quality: '92%', efficiency: '88%', satisfaction: '94%', innovation: '90%' }, { id: 2, name: '李四', icon: 'person', iconClass: 'person-icon', completion: '88%', quality: '90%', efficiency: '92%', satisfaction: '87%', innovation: '85%' }, { id: 3, name: '王五', icon: 'person', iconClass: 'person-icon', completion: '90%', quality: '85%', efficiency: '90%', satisfaction: '88%', innovation: '92%' } ]; this.verticalComparisonData = this.horizontalComparisonData.map((item, index) => ({ ...item, category: '员工个人', overallScore: 90 - index * 2, rank: index + 1 })); } private updateProjectComparisonData(): void { this.horizontalComparisonData = [ { id: 1, name: '项目Alpha', icon: 'work', iconClass: 'project-icon', completion: '95%', quality: '90%', efficiency: '85%', satisfaction: '92%', innovation: '88%' }, { id: 2, name: '项目Beta', icon: 'work', iconClass: 'project-icon', completion: '88%', quality: '88%', efficiency: '90%', satisfaction: '85%', innovation: '90%' }, { id: 3, name: '项目Gamma', icon: 'work', iconClass: 'project-icon', completion: '92%', quality: '85%', efficiency: '88%', satisfaction: '90%', innovation: '85%' } ]; this.verticalComparisonData = this.horizontalComparisonData.map((item, index) => ({ ...item, category: '项目数据', overallScore: 88 + index, rank: index + 1 })); } addComparisonItem(): void { const dialogRef = this.dialog.open(AddComparisonDialogComponent, { width: '700px', panelClass: 'hr-dialog', backdropClass: 'hr-dialog-backdrop', data: { dimension: this.selectedComparisonDimension, availableMetrics: this.selectedComparisonMetric } }); dialogRef.afterClosed().subscribe((result: ComparisonItemData) => { if (result) { const newId = Math.max(...this.horizontalComparisonData.map(item => item.id)) + 1; // 计算综合评分 const scores = Object.values(result.metrics).map(value => parseInt(value.replace('%', '')) ); const overallScore = Math.round(scores.reduce((sum, score) => sum + score, 0) / scores.length); const newItem = { id: newId, name: result.name, icon: result.icon, iconClass: result.iconClass, ...result.metrics }; this.horizontalComparisonData.push(newItem); this.verticalComparisonData.push({ ...newItem, category: result.category, overallScore: overallScore, rank: this.verticalComparisonData.length + 1 }); // 重新排序纵向对比数据 this.verticalComparisonData.sort((a, b) => b.overallScore - a.overallScore); this.verticalComparisonData.forEach((item, index) => { item.rank = index + 1; }); this.showAddItemFeedback(result.name); } }); } removeComparisonItem(id: number): void { this.horizontalComparisonData = this.horizontalComparisonData.filter(item => item.id !== id); this.verticalComparisonData = this.verticalComparisonData.filter(item => item.id !== id); this.showRemoveItemFeedback(); } viewComparisonDetails(id: number): void { const item = this.horizontalComparisonData.find(item => item.id === id); if (item) { console.log('查看详情:', item); // 这里可以打开详情弹窗或跳转到详情页面 } } getMetricDisplayName(metric: string): string { const metricNames: { [key: string]: string } = { completion: '完成率', quality: '质量评分', efficiency: '效率指数', satisfaction: '满意度', innovation: '创新度' }; return metricNames[metric] || metric; } // 离职原因分析相关方法 updateResignationData(): void { // 根据时间范围更新数据 console.log('更新离职数据,时间范围:', this.resignationTimeRange); // 这里可以调用API获取对应时间范围的数据 } toggleDepartmentFilter(deptId: string): void { const dept = this.resignationDepartments.find(d => d.id === deptId); if (dept) { dept.selected = !dept.selected; this.updateFilteredResignationData(); } } toggleLevelFilter(levelId: string): void { const level = this.resignationLevels.find(l => l.id === levelId); if (level) { level.selected = !level.selected; this.updateFilteredResignationData(); } } updateFilteredResignationData(): void { // 根据筛选条件更新数据 const selectedDepts = this.resignationDepartments.filter(d => d.selected); const selectedLevels = this.resignationLevels.filter(l => l.selected); console.log('筛选条件更新:', { selectedDepts, selectedLevels }); // 这里可以重新计算统计数据和图表数据 } exportResignationAnalysis(): void { console.log('导出离职分析报告'); // 这里可以生成Excel或PDF报告 this.showExportFeedback(); } viewReasonDetails(reasonId: string): void { const reason = this.resignationReasons.find(r => r.id === reasonId); if (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) { 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'; if (numValue >= 80) return 'metric-good'; if (numValue >= 70) return 'metric-average'; return 'metric-poor'; } getMetricPercentage(value: string, metric: string): number { return parseInt(value); } getProgressBarClass(value: string): string { const numValue = parseInt(value); if (numValue >= 90) return 'progress-excellent'; if (numValue >= 80) return 'progress-good'; if (numValue >= 70) return 'progress-average'; return 'progress-poor'; } getOverallScoreClass(score: number): string { if (score >= 90) return 'score-excellent'; if (score >= 80) return 'score-good'; if (score >= 70) return 'score-average'; return 'score-poor'; } private showAddItemFeedback(itemName: string): void { const feedback = document.createElement('div'); feedback.className = 'add-item-feedback'; feedback.innerHTML = ` `; feedback.style.cssText = ` position: fixed; top: 20px; right: 20px; background: linear-gradient(135deg, #2196F3, #1976D2); color: white; padding: 16px 24px; border-radius: 12px; box-shadow: 0 8px 32px rgba(33, 150, 243, 0.3); z-index: 1000; animation: slideInRight 0.3s ease-out; `; document.body.appendChild(feedback); setTimeout(() => { feedback.style.animation = 'slideOutRight 0.3s ease-in'; setTimeout(() => { document.body.removeChild(feedback); }, 300); }, 2000); } private showRemoveItemFeedback(): void { const feedback = document.createElement('div'); feedback.className = 'remove-item-feedback'; feedback.innerHTML = ` `; feedback.style.cssText = ` position: fixed; top: 20px; right: 20px; background: linear-gradient(135deg, #FF5722, #D84315); color: white; padding: 16px 24px; border-radius: 12px; box-shadow: 0 8px 32px rgba(255, 87, 34, 0.3); z-index: 1000; animation: slideInRight 0.3s ease-out; `; document.body.appendChild(feedback); setTimeout(() => { feedback.style.animation = 'slideOutRight 0.3s ease-in'; setTimeout(() => { document.body.removeChild(feedback); }, 300); }, 2000); } }