1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481 |
- 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<HTMLCanvasElement>;
- @ViewChild('lineChart', { static: false }) lineChartRef!: ElementRef<HTMLCanvasElement>;
- @ViewChild('radarChart', { static: false }) radarChartRef!: ElementRef<HTMLCanvasElement>;
- @ViewChild('resignationChart', { static: false }) resignationChartRef!: ElementRef<HTMLCanvasElement>;
- @ViewChild('comparisonChart', { static: false }) comparisonChartRef!: ElementRef<HTMLCanvasElement>;
- 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 = `
- <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);
- }
- // 初始化图表(这里需要后续集成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<TodoItem[]>) {
- 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<TodoItem[]>): 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 = `
- <div class="ios-feedback-content">
- <mat-icon>swap_vert</mat-icon>
- <span>任务顺序已更新</span>
- </div>
- `;
-
- // 添加样式
- 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 = `
- <div class="ios-feedback-content">
- <mat-icon style="color: ${colorMap[newStatus as keyof typeof colorMap]}">${iconMap[newStatus as keyof typeof iconMap]}</mat-icon>
- <div class="ios-feedback-text">
- <div class="ios-feedback-title">${title}</div>
- <div class="ios-feedback-subtitle">${statusMap[oldStatus as keyof typeof statusMap]} → ${statusMap[newStatus as keyof typeof statusMap]}</div>
- </div>
- </div>
- `;
-
- // 添加样式
- 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 = `
- <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) {
- 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 = `
- <div class="feedback-header">
- <mat-icon>${metric.icon}</mat-icon>
- <span>查看${metric.title}详情</span>
- </div>
- <div class="feedback-content">
- <div class="metric-info">
- <div class="info-item">
- <span class="label">当前值:</span>
- <span class="value">${metric.value}${metric.unit}</span>
- </div>
- <div class="info-item">
- <span class="label">目标值:</span>
- <span class="value">${metric.target}</span>
- </div>
- <div class="info-item">
- <span class="label">完成率:</span>
- <span class="value ${metric.achievementClass}">${metric.achievement}</span>
- </div>
- <div class="info-item">
- <span class="label">趋势:</span>
- <span class="value trend-${metric.trend.type}">
- <mat-icon>${metric.trend.icon}</mat-icon>
- ${metric.trend.value}
- </span>
- </div>
- </div>
- </div>
- `;
- 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 = `
- <div class="feedback-header">
- <mat-icon>trending_up</mat-icon>
- <span>${metric.title}趋势分析</span>
- </div>
- <div class="feedback-content">
- <div class="trend-chart-placeholder">
- <mat-icon>show_chart</mat-icon>
- <p>趋势图表正在加载...</p>
- </div>
- <div class="trend-summary">
- <div class="summary-item">
- <span class="label">当前趋势:</span>
- <span class="value trend-${metric.trend.type}">
- <mat-icon>${metric.trend.icon}</mat-icon>
- ${metric.trend.type === 'positive' ? '上升' : metric.trend.type === 'negative' ? '下降' : '平稳'}
- </span>
- </div>
- <div class="summary-item">
- <span class="label">变化幅度:</span>
- <span class="value">${metric.trend.value}</span>
- </div>
- <div class="summary-item">
- <span class="label">对比周期:</span>
- <span class="value">较上月</span>
- </div>
- </div>
- </div>
- `;
- 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 = `
- <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 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 = `
- <div class="feedback-content">
- <mat-icon>add_circle</mat-icon>
- <span>对比项"${itemName}"添加成功!</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);
- }
- }
|