|
@@ -9,6 +9,11 @@ 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 { DragDropModule, CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
|
|
|
import { Chart, ChartConfiguration, registerables } from 'chart.js';
|
|
|
import { trigger, state, style, transition, animate } from '@angular/animations';
|
|
@@ -20,7 +25,7 @@ export interface TodoItem {
|
|
|
description: string;
|
|
|
priority: 'high' | 'medium' | 'low';
|
|
|
status: 'pending' | 'completed' | 'in_progress';
|
|
|
- type: 'resume' | 'onboarding' | 'promotion' | 'resignation';
|
|
|
+ type: 'resume' | 'onboarding' | 'resignation';
|
|
|
}
|
|
|
|
|
|
export interface RankDistribution {
|
|
@@ -44,29 +49,88 @@ export interface DepartmentPerformance {
|
|
|
overdueRate: number;
|
|
|
}
|
|
|
|
|
|
-export interface PromotionRule {
|
|
|
+
|
|
|
+
|
|
|
+export interface OnboardingCheckpoint {
|
|
|
id: number;
|
|
|
title: string;
|
|
|
description: string;
|
|
|
- conditions: string[];
|
|
|
+ dueDate: Date;
|
|
|
+ completed: boolean;
|
|
|
+ interviewTemplate: string[];
|
|
|
}
|
|
|
|
|
|
-export interface PromotionSuggestion {
|
|
|
- employeeId: number;
|
|
|
- employeeName: string;
|
|
|
- currentRank: string;
|
|
|
- suggestedRank: 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[];
|
|
|
- status: 'pending' | 'approved' | 'rejected';
|
|
|
+ concerns: string[];
|
|
|
}
|
|
|
|
|
|
-export interface OnboardingCheckpoint {
|
|
|
+export interface ScreeningInfo {
|
|
|
id: number;
|
|
|
title: string;
|
|
|
- description: string;
|
|
|
- dueDate: Date;
|
|
|
- completed: boolean;
|
|
|
- interviewTemplate: 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({
|
|
@@ -83,7 +147,12 @@ export interface OnboardingCheckpoint {
|
|
|
MatChipsModule,
|
|
|
MatProgressBarModule,
|
|
|
MatBadgeModule,
|
|
|
- DragDropModule
|
|
|
+ MatFormFieldModule,
|
|
|
+ MatSelectModule,
|
|
|
+ MatInputModule,
|
|
|
+ DragDropModule,
|
|
|
+ MatTableModule,
|
|
|
+ MatButtonToggleModule
|
|
|
],
|
|
|
templateUrl: './dashboard.html',
|
|
|
styleUrls: ['./dashboard.scss'],
|
|
@@ -108,7 +177,7 @@ export class Dashboard implements OnInit, AfterViewInit {
|
|
|
private lineChart!: Chart;
|
|
|
private radarChart!: Chart;
|
|
|
// 当前激活的标签页
|
|
|
- activeTab: 'visualization' | 'promotion' | 'onboarding' = 'visualization';
|
|
|
+ activeTab: 'visualization' | 'recruitment' | 'performance' | 'onboarding' = 'visualization';
|
|
|
|
|
|
// 待办事项是否展开
|
|
|
isTodoExpanded = false;
|
|
@@ -135,13 +204,7 @@ export class Dashboard implements OnInit, AfterViewInit {
|
|
|
priority: 'medium',
|
|
|
dueDate: '2024-01-28'
|
|
|
},
|
|
|
- {
|
|
|
- id: 3,
|
|
|
- title: '晋升审核',
|
|
|
- description: '审核中级设计师晋升申请',
|
|
|
- priority: 'high',
|
|
|
- dueDate: '2024-01-30'
|
|
|
- },
|
|
|
+
|
|
|
{
|
|
|
id: 4,
|
|
|
title: '离职面谈',
|
|
@@ -169,14 +232,7 @@ export class Dashboard implements OnInit, AfterViewInit {
|
|
|
status: 'pending',
|
|
|
type: 'onboarding'
|
|
|
},
|
|
|
- {
|
|
|
- id: 3,
|
|
|
- title: '晋升审核',
|
|
|
- description: '审核中级设计师晋升申请',
|
|
|
- priority: 'high',
|
|
|
- status: 'pending',
|
|
|
- type: 'promotion'
|
|
|
- },
|
|
|
+
|
|
|
{
|
|
|
id: 4,
|
|
|
title: '离职面谈',
|
|
@@ -250,60 +306,7 @@ export class Dashboard implements OnInit, AfterViewInit {
|
|
|
}
|
|
|
];
|
|
|
|
|
|
- promotionRules: PromotionRule[] = [
|
|
|
- {
|
|
|
- id: 1,
|
|
|
- title: '初级→中级晋升标准',
|
|
|
- description: '设计师晋升中级标准',
|
|
|
- conditions: [
|
|
|
- '连续3个月优秀作品率超过80%',
|
|
|
- '逾期率低于5%',
|
|
|
- '客户满意度评分4.5以上',
|
|
|
- '至少完成12个主要项目'
|
|
|
- ]
|
|
|
- },
|
|
|
- {
|
|
|
- id: 2,
|
|
|
- title: '中级→高级晋升标准',
|
|
|
- description: '设计师晋升高级标准',
|
|
|
- conditions: [
|
|
|
- '连续6个月优秀作品率超过85%',
|
|
|
- '逾期率低于3%',
|
|
|
- '客户满意度评分4.8以上',
|
|
|
- '带领过至少2个大型项目',
|
|
|
- '有 mentorship 经验'
|
|
|
- ]
|
|
|
- }
|
|
|
- ];
|
|
|
|
|
|
- promotionSuggestions: PromotionSuggestion[] = [
|
|
|
- {
|
|
|
- employeeId: 1001,
|
|
|
- employeeName: '张三',
|
|
|
- currentRank: '初级设计师',
|
|
|
- suggestedRank: '中级设计师',
|
|
|
- reasons: [
|
|
|
- '连续3个月优秀作品率85%',
|
|
|
- '逾期率仅2%',
|
|
|
- '客户满意度4.7分',
|
|
|
- '已完成15个主要项目'
|
|
|
- ],
|
|
|
- status: 'pending'
|
|
|
- },
|
|
|
- {
|
|
|
- employeeId: 1002,
|
|
|
- employeeName: '李四',
|
|
|
- currentRank: '中级设计师',
|
|
|
- suggestedRank: '高级设计师',
|
|
|
- reasons: [
|
|
|
- '连续6个月优秀作品率88%',
|
|
|
- '逾期率1.5%',
|
|
|
- '客户满意度4.9分',
|
|
|
- '成功带领3个大型项目'
|
|
|
- ],
|
|
|
- status: 'pending'
|
|
|
- }
|
|
|
- ];
|
|
|
|
|
|
onboardingCheckpoints: OnboardingCheckpoint[] = [
|
|
|
{
|
|
@@ -361,9 +364,689 @@ export class Dashboard implements OnInit, AfterViewInit {
|
|
|
}, 100);
|
|
|
}
|
|
|
|
|
|
+ ngOnDestroy() {
|
|
|
+ // 销毁所有图表实例,防止内存泄漏
|
|
|
+ if (this.pieChart) {
|
|
|
+ this.pieChart.destroy();
|
|
|
+ }
|
|
|
+ if (this.lineChart) {
|
|
|
+ this.lineChart.destroy();
|
|
|
+ }
|
|
|
+ if (this.radarChart) {
|
|
|
+ this.radarChart.destroy();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// 切换标签页
|
|
|
- switchTab(tab: 'visualization' | 'promotion' | 'onboarding') {
|
|
|
+ switchTab(tab: 'visualization' | 'recruitment' | 'performance' | 'onboarding') {
|
|
|
this.activeTab = tab;
|
|
|
+
|
|
|
+ // 如果切换到数据可视化页面,重新初始化图表
|
|
|
+ if (tab === 'visualization') {
|
|
|
+ setTimeout(() => this.initializeCharts(), 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'];
|
|
|
+ chartType: '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';
|
|
|
+ trendsTimeframe: 'monthly' | 'quarterly' | 'yearly' = 'monthly';
|
|
|
+
|
|
|
+ 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 }
|
|
|
+ ];
|
|
|
+
|
|
|
+ resignationReasons = [
|
|
|
+ {
|
|
|
+ id: 'salary',
|
|
|
+ name: '薪资待遇',
|
|
|
+ category: 'compensation',
|
|
|
+ categoryName: '薪酬福利',
|
|
|
+ icon: 'attach_money',
|
|
|
+ percentage: 28.5,
|
|
|
+ count: 13,
|
|
|
+ description: '薪资水平低于市场平均水平,缺乏有竞争力的薪酬体系',
|
|
|
+ trend: { direction: 'up', value: 5.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 }
|
|
|
+ }
|
|
|
+ ];
|
|
|
+
|
|
|
+ // AI简历分析相关属性
|
|
|
+ isDragOver: boolean = false;
|
|
|
+ showAnalysisResults: boolean = false;
|
|
|
+
|
|
|
+ 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 handleFileUpload(file: File) {
|
|
|
+ // 验证文件类型
|
|
|
+ const allowedTypes = ['application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'];
|
|
|
+ if (!allowedTypes.includes(file.type)) {
|
|
|
+ this.showUploadError('不支持的文件格式,请上传PDF、DOC或DOCX文件');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 验证文件大小 (10MB)
|
|
|
+ if (file.size > 10 * 1024 * 1024) {
|
|
|
+ this.showUploadError('文件大小超过10MB限制');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 开始上传和分析
|
|
|
+ this.showUploadFeedback(file.name);
|
|
|
+ setTimeout(() => {
|
|
|
+ this.showAnalysisResults = true;
|
|
|
+ console.log('简历分析完成:', file.name);
|
|
|
+ }, 3000);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 招聘阶段相关方法
|
|
|
+ refreshRecruitmentData(): void {
|
|
|
+ // 刷新招聘数据
|
|
|
+ console.log('刷新招聘数据');
|
|
|
+ // 这里可以调用API刷新数据
|
|
|
+ }
|
|
|
+
|
|
|
+ openStageDetails(stage: RecruitmentStage): void {
|
|
|
+ console.log('打开阶段详情:', stage);
|
|
|
+ // 这里可以打开详情弹窗或导航到详情页面
|
|
|
+ }
|
|
|
+
|
|
|
+ navigateToOnboarding(): void {
|
|
|
+ console.log('导航到新人跟进模块');
|
|
|
+ // 这里可以使用Router导航到新人跟进页面
|
|
|
+ }
|
|
|
+
|
|
|
+ viewProbationReports(): void {
|
|
|
+ console.log('查看试用期报告');
|
|
|
+ // 这里可以打开试用期报告页面
|
|
|
+ }
|
|
|
+
|
|
|
+ // 绩效筛选相关方法
|
|
|
+ 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) {
|
|
|
+ // 创建错误提示元素
|
|
|
+ const errorDiv = document.createElement('div');
|
|
|
+ errorDiv.className = 'upload-error-feedback';
|
|
|
+ errorDiv.innerHTML = `
|
|
|
+ <div class="error-content">
|
|
|
+ <mat-icon>error</mat-icon>
|
|
|
+ <span>${message}</span>
|
|
|
+ </div>
|
|
|
+ `;
|
|
|
+
|
|
|
+ document.body.appendChild(errorDiv);
|
|
|
+
|
|
|
+ // 3秒后移除
|
|
|
+ setTimeout(() => {
|
|
|
+ if (errorDiv.parentNode) {
|
|
|
+ errorDiv.parentNode.removeChild(errorDiv);
|
|
|
+ }
|
|
|
+ }, 3000);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 显示上传反馈
|
|
|
+ showUploadFeedback(fileName: string) {
|
|
|
+ // 创建临时反馈元素
|
|
|
+ const feedback = document.createElement('div');
|
|
|
+ feedback.className = 'upload-feedback';
|
|
|
+ feedback.innerHTML = `
|
|
|
+ <mat-icon>check_circle</mat-icon>
|
|
|
+ <span>简历 "${fileName}" 上传成功!</span>
|
|
|
+ `;
|
|
|
+ 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 = `
|
|
|
+ <mat-icon>filter_list</mat-icon>
|
|
|
+ <span>筛选条件已应用</span>
|
|
|
+ `;
|
|
|
+ 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);
|
|
|
}
|
|
|
|
|
|
|
|
@@ -613,11 +1296,10 @@ export class Dashboard implements OnInit, AfterViewInit {
|
|
|
|
|
|
getTypeLabel(type: string): string {
|
|
|
switch (type) {
|
|
|
- case 'resume': return '简历初筛';
|
|
|
- case 'onboarding': return '入职评定';
|
|
|
- case 'promotion': return '晋升审核';
|
|
|
- case 'resignation': return '离职面谈';
|
|
|
- default: return '其他';
|
|
|
+ case 'resume': return '简历筛选';
|
|
|
+ case 'onboarding': return '入职跟进';
|
|
|
+ case 'resignation': return '离职处理';
|
|
|
+ default: return type;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -647,4 +1329,593 @@ export class Dashboard implements OnInit, AfterViewInit {
|
|
|
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 = `
|
|
|
+ <mat-icon>${completed ? 'check_circle' : 'radio_button_unchecked'}</mat-icon>
|
|
|
+ <span>${completed ? '已完成' : '已取消'}: ${title}</span>
|
|
|
+ `;
|
|
|
+ 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 = `
|
|
|
+ <mat-icon>trending_up</mat-icon>
|
|
|
+ <span>${name} 的进度已更新至 ${progress}%</span>
|
|
|
+ `;
|
|
|
+ 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) {
|
|
|
+ // 这里可以打开详情对话框或导航到详情页面
|
|
|
+ console.log('指标详情:', metric);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ viewMetricTrend(metricId: string) {
|
|
|
+ console.log('查看指标趋势:', metricId);
|
|
|
+ const metric = this.performanceMetrics.find(m => m.id === metricId);
|
|
|
+ if (metric) {
|
|
|
+ // 这里可以打开趋势图表对话框
|
|
|
+ console.log('指标趋势:', metric);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private showMetricsRefreshFeedback() {
|
|
|
+ const feedback = document.createElement('div');
|
|
|
+ feedback.className = 'metrics-feedback';
|
|
|
+ feedback.innerHTML = `
|
|
|
+ <mat-icon>refresh</mat-icon>
|
|
|
+ <span>绩效指标已刷新</span>
|
|
|
+ `;
|
|
|
+ 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 = `
|
|
|
+ <mat-icon>file_download</mat-icon>
|
|
|
+ <span>报告导出成功</span>
|
|
|
+ `;
|
|
|
+ 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 newId = Math.max(...this.horizontalComparisonData.map(item => item.id)) + 1;
|
|
|
+ const newItem = {
|
|
|
+ id: newId,
|
|
|
+ name: `新增项目${newId}`,
|
|
|
+ icon: 'add_circle',
|
|
|
+ iconClass: 'new-icon',
|
|
|
+ completion: '85%',
|
|
|
+ quality: '80%',
|
|
|
+ efficiency: '82%',
|
|
|
+ satisfaction: '85%',
|
|
|
+ innovation: '78%'
|
|
|
+ };
|
|
|
+
|
|
|
+ this.horizontalComparisonData.push(newItem);
|
|
|
+ this.verticalComparisonData.push({
|
|
|
+ ...newItem,
|
|
|
+ category: '新增项目',
|
|
|
+ overallScore: 82,
|
|
|
+ rank: this.verticalComparisonData.length + 1
|
|
|
+ });
|
|
|
+
|
|
|
+ this.showAddItemFeedback();
|
|
|
+ }
|
|
|
+
|
|
|
+ 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) {
|
|
|
+ console.log('查看离职原因详情:', reason);
|
|
|
+ // 这里可以打开详情弹窗显示更多信息
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ viewImprovementPlan(reasonId: string): void {
|
|
|
+ const reason = this.resignationReasons.find(r => r.id === reasonId);
|
|
|
+ if (reason) {
|
|
|
+ console.log('查看改进建议:', reason);
|
|
|
+ // 这里可以显示针对该离职原因的改进建议
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ 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(): void {
|
|
|
+ const feedback = document.createElement('div');
|
|
|
+ feedback.className = 'add-item-feedback';
|
|
|
+ feedback.innerHTML = `
|
|
|
+ <div class="feedback-content">
|
|
|
+ <mat-icon>add_circle</mat-icon>
|
|
|
+ <span>对比项添加成功!</span>
|
|
|
+ </div>
|
|
|
+ `;
|
|
|
+ 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 = `
|
|
|
+ <div class="feedback-content">
|
|
|
+ <mat-icon>remove_circle</mat-icon>
|
|
|
+ <span>对比项删除成功!</span>
|
|
|
+ </div>
|
|
|
+ `;
|
|
|
+ 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);
|
|
|
+ }
|
|
|
}
|