徐福静0235668 2 days ago
parent
commit
c24ae1b4c9

+ 20 - 4
package-lock.json

@@ -8,7 +8,8 @@
       "name": "yss-project",
       "version": "0.0.0",
       "dependencies": {
-        "@angular/cdk": "^20.2.2",
+        "@angular/animations": "^20.3.1",
+        "@angular/cdk": "^20.2.4",
         "@angular/common": "^20.1.0",
         "@angular/compiler": "^20.1.0",
         "@angular/core": "^20.1.0",
@@ -433,6 +434,21 @@
         "typescript": "*"
       }
     },
+    "node_modules/@angular/animations": {
+      "version": "20.3.1",
+      "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-20.3.1.tgz",
+      "integrity": "sha512-mexSwaikVE2s+GDhB9fuagEvxbnKHWsqLlO7/R2nY9tTUxBO3drWe3p0D5GxG/EsEyzZU+86ED867q/JmAiVvw==",
+      "license": "MIT",
+      "dependencies": {
+        "tslib": "^2.3.0"
+      },
+      "engines": {
+        "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
+      },
+      "peerDependencies": {
+        "@angular/core": "20.3.1"
+      }
+    },
     "node_modules/@angular/build": {
       "version": "20.2.2",
       "resolved": "https://registry.npmjs.org/@angular/build/-/build-20.2.2.tgz",
@@ -533,9 +549,9 @@
       }
     },
     "node_modules/@angular/cdk": {
-      "version": "20.2.2",
-      "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-20.2.2.tgz",
-      "integrity": "sha512-jLvIMmFI8zoi6vAu1Aszua59GmhqBOtsVfkwLUGg5Hi86DI/inJr9BznNX2EKDtaulYMGZCmDgsltXQXeqP5Lg==",
+      "version": "20.2.4",
+      "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-20.2.4.tgz",
+      "integrity": "sha512-5UzrN854pnQH+Qw6XZRxx2zWkcOxKrzWPLXe+gHFxFhxWUZfJKGcTJeAj8bnmyb+C3lqBbGpoNQPQ8pFXQGEaQ==",
       "license": "MIT",
       "dependencies": {
         "parse5": "^8.0.0",

+ 2 - 1
package.json

@@ -21,7 +21,8 @@
   },
   "private": true,
   "dependencies": {
-    "@angular/cdk": "^20.2.2",
+    "@angular/animations": "^20.3.1",
+    "@angular/cdk": "^20.2.4",
     "@angular/common": "^20.1.0",
     "@angular/compiler": "^20.1.0",
     "@angular/core": "^20.1.0",

+ 3 - 3
src/app/app.routes.ts

@@ -117,10 +117,10 @@ export const routes: Routes = [
     path: 'hr',
     component: HrLayout,
     children: [
-      { path: '', redirectTo: 'dashboard', pathMatch: 'full' },
       { path: 'dashboard', component: HrDashboard, title: '人事看板' },
-      { path: 'employee-records', component: EmployeeRecords, title: '花名册与档案库' },
-      { path: 'attendance', component: Attendance, title: '考勤统计' }
+      { path: 'employee-records', component: EmployeeRecords, title: '员工档案' },
+      { path: 'attendance', component: Attendance, title: '考勤管理' },
+      { path: '', redirectTo: 'dashboard', pathMatch: 'full' }
     ]
   },
 

+ 3 - 2
src/app/pages/customer-service/project-list/project-list.html

@@ -284,14 +284,15 @@
       <!-- 视图:监控大盘模式 -->
       @if (viewMode() === 'dashboard') {
         <div class="dashboard-container">
-          <!-- 使用iframe嵌入组长端监控大盘,保持样式和功能完整 -->
+          <!-- 使用iframe嵌入组长端监控大盘,通过CSS隐藏待办任务栏 -->
           <iframe 
             src="/team-leader/dashboard" 
             frameborder="0" 
             width="100%" 
             height="100%"
             style="min-height: 800px;"
-            title="项目监控大盘">
+            title="项目监控大盘"
+            onload="const doc = this.contentDocument; doc.querySelector('.todo-section').style.display = 'none'; const header = doc.querySelector('.dashboard-header h1'); if (header && header.textContent.includes('设计组长工作台')) { header.style.display = 'none'; doc.querySelector('.dashboard-metrics').style.marginTop = '20px'; }">
           </iframe>
         </div>
       }

+ 311 - 215
src/app/pages/hr/dashboard/dashboard.html

@@ -1,245 +1,341 @@
-<div class="dashboard-container">
-  <header class="page-header">
-    <h1>人事看板</h1>
-    <p class="page-description">人事数据总览,聚焦入职/离职动态、绩效核心指标、风险预警三大维度</p>
-  </header>
-
-  <!-- 时间维度筛选 -->
-  <div class="filter-bar">
-    <div class="time-filter">
-      <span class="filter-label">时间维度:</span>
-      <div class="filter-buttons">
-        <button mat-button [class.active]="timeFilter() === 'day'" (click)="changeTimeFilter('day')">日</button>
-        <button mat-button [class.active]="timeFilter() === 'week'" (click)="changeTimeFilter('week')">周</button>
-        <button mat-button [class.active]="timeFilter() === 'month'" (click)="changeTimeFilter('month')">月</button>
-      </div>
+<div class="hr-dashboard-container">
+  <!-- 顶部导航按钮 -->
+  <div class="top-navigation">
+    <div class="nav-buttons">
+      <button 
+        mat-raised-button 
+        [class.active]="activeTab === 'visualization'"
+        (click)="switchTab('visualization')"
+        class="nav-button">
+        <mat-icon>analytics</mat-icon>
+        数据可视化
+      </button>
+      <button 
+        mat-raised-button 
+        [class.active]="activeTab === 'promotion'"
+        (click)="switchTab('promotion')"
+        class="nav-button">
+        <mat-icon>trending_up</mat-icon>
+        职级与晋升管理
+      </button>
+      <button 
+        mat-raised-button 
+        [class.active]="activeTab === 'onboarding'"
+        (click)="switchTab('onboarding')"
+        class="nav-button">
+        <mat-icon>person_add</mat-icon>
+        新人跟进管理
+      </button>
     </div>
   </div>
 
-  <!-- 主内容区 -->
-  <div class="dashboard-content">
-    <!-- 入职离职看板 -->
-    <mat-card class="dashboard-card">
-      <mat-card-header>
-        <mat-card-title>
-          <div class="card-title-container">
-            <mat-icon>trending_up</mat-icon>
-            <span>入职离职看板</span>
-          </div>
-        </mat-card-title>
-      </mat-card-header>
-      <mat-card-content>
-        <div class="card-section">
-          <h3>月度入职离职数据</h3>
-          <!-- 用 ECharts 展示分组柱状图(入职/离职) -->
-          <div id="hrMovementChart" class="chart-container"></div>
-        </div>
+  <!-- 数据可视化页面 -->
+  @if (activeTab === 'visualization') {
+    <div class="visualization-page">
+      <div class="main-layout">
+        <!-- 左侧数据展示区域 -->
+        <div class="left-panel">
+          <!-- 职级分布饼图 -->
+          <mat-card class="chart-card">
+            <mat-card-header>
+              <mat-card-title>
+                <mat-icon>pie_chart</mat-icon>
+                职级分布
+              </mat-card-title>
+              <mat-card-subtitle>设计师职级占比分析</mat-card-subtitle>
+            </mat-card-header>
+            <mat-card-content>
+              <div class="pie-chart-container">
+                <canvas #pieChart></canvas>
+              </div>
+              <div class="chart-legend">
+                @for (item of rankDistribution; track item.level) {
+                  <div class="legend-item">
+                    <div class="legend-color" [style.background-color]="item.color"></div>
+                    <span>{{item.level}}: {{item.percentage}}% ({{item.count}}人)</span>
+                  </div>
+                }
+              </div>
+            </mat-card-content>
+          </mat-card>
 
-        <div class="card-section">
-          <h3>流程进度追踪</h3>
-          <div class="process-tracking">
-            @for (process of pendingProcesses(); track process) {
-              <div class="process-item">
-                <div class="process-header">
-                  <span class="process-title">
-                    {{process.type === 'hire' ? '待审核入职申请' : '待办理离职手续'}}
-                  </span>
-                  <span class="process-count">{{process.count}}个</span>
-                </div>
-                <mat-progress-bar [value]="process.progress" color="primary"></mat-progress-bar>
-                <button mat-button color="primary" class="process-action" (click)="navigateToProcess(process.type)">
-                  查看详情
-                </button>
+          <!-- 入职离职趋势折线图 -->
+          <mat-card class="chart-card">
+            <mat-card-header>
+              <mat-card-title>
+                <mat-icon>trending_up</mat-icon>
+                入职离职趋势
+              </mat-card-title>
+              <mat-card-subtitle>近6个月人员流动情况</mat-card-subtitle>
+            </mat-card-header>
+            <mat-card-content>
+              <div class="line-chart-container">
+                <canvas #lineChart></canvas>
               </div>
-            }
-          </div>
-        </div>
+              <div class="key-notes">
+                <h4>关键节点</h4>
+                @for (note of keyNotes; track note.month) {
+                  <div class="note-item">
+                    <mat-icon class="note-icon">info</mat-icon>
+                    <span class="note-text">{{note.month}}:{{note.description}}</span>
+                  </div>
+                }
+              </div>
+            </mat-card-content>
+          </mat-card>
 
-        <div class="card-section">
-          <h3>风险预警</h3>
-          <div class="risk-alerts">
-            @for (alert of riskAlerts(); track alert) {
-              <div class="risk-alert">
-                <div class="alert-header">
-                  <mat-icon color="warn">warning</mat-icon>
-                  <span class="alert-title">{{alert.type}}</span>
-                  <span class="alert-count">{{alert.count}}人</span>
-                </div>
-                <div class="alert-employees">
-                  @for (employee of alert.employees; track employee) {
-                    <div class="employee-item">
-                      <span class="employee-name">{{employee.name}}</span>
-                      <span class="employee-position">{{employee.position}}</span>
-                      <span class="employee-department">{{employee.department}}</span>
-                      @if (employee.daysLeft) {
-                        <span class="days-left">剩余{{employee.daysLeft}}天</span>
-                      }
-                    </div>
-                  }
-                </div>
+          <!-- 绩效总览雷达图 -->
+          <mat-card class="chart-card">
+            <mat-card-header>
+              <mat-card-title>
+                <mat-icon>radar</mat-icon>
+                绩效总览
+              </mat-card-title>
+              <mat-card-subtitle>各部门4个维度绩效对比</mat-card-subtitle>
+            </mat-card-header>
+            <mat-card-content>
+              <div class="radar-chart-container">
+                <canvas #radarChart></canvas>
               </div>
-            }
-          </div>
-        </div>
-      </mat-card-content>
-    </mat-card>
+              <div class="radar-legend">
+                @for (dept of departmentPerformance; track dept.department) {
+                  <div class="legend-item">
+                    <div class="legend-color" [style.background-color]="getDepartmentColor(dept.department)"></div>
+                    <span>{{dept.department}}</span>
+                  </div>
+                }
+              </div>
+            </mat-card-content>
+          </mat-card>
 
-    <!-- 绩效总览看板 -->
-    <mat-card class="dashboard-card">
-      <mat-card-header>
-        <mat-card-title>
-          <div class="card-title-container">
-            <mat-icon>assessment</mat-icon>
-            <span>绩效总览看板</span>
-          </div>
-        </mat-card-title>
-      </mat-card-header>
-      <mat-card-content>
-        <div class="card-section">
-          <h3>核心指标展示</h3>
-          <div class="metrics-container">
-            @for (metric of performanceMetrics(); track metric) {
-              <div class="metric-item">
-                <div class="metric-header">
-                  <span class="metric-name">{{metric.name}}</span>
-                  <div class="metric-values">
-                    <span class="metric-actual">{{metric.actual}}{{metric.unit}}</span>
-                    <span class="metric-target">目标: {{metric.target}}{{metric.unit}}</span>
+          <!-- 关键岗位空缺数 -->
+          <mat-card class="chart-card">
+            <mat-card-header>
+              <mat-card-title>
+                <mat-icon>work_off</mat-icon>
+                关键岗位空缺
+              </mat-card-title>
+              <mat-card-subtitle>紧急招聘需求</mat-card-subtitle>
+            </mat-card-header>
+            <mat-card-content>
+              <div class="vacancy-list">
+                @for (vacancy of keyVacancies; track vacancy.position) {
+                  <div class="vacancy-item" [class]="getPriorityClass(vacancy.priority)">
+                    <div class="vacancy-icon">
+                      <mat-icon>{{getVacancyIcon(vacancy.priority)}}</mat-icon>
+                    </div>
+                    <div class="vacancy-info">
+                      <h4>{{vacancy.position}}</h4>
+                      <p>空缺:{{vacancy.count}}人</p>
+                      <span class="priority-badge" [class]="'priority-' + vacancy.priority">
+                        {{vacancy.priority === 'high' ? '紧急' : vacancy.priority === 'medium' ? '一般' : '低'}}
+                      </span>
+                    </div>
                   </div>
-                </div>
-                <mat-progress-bar
-                  [value]="(metric.actual / metric.target) * 100"
-                  [color]="getProgressColor(metric.actual, metric.target)">
-                </mat-progress-bar>
+                }
               </div>
-            }
-          </div>
+            </mat-card-content>
+          </mat-card>
+        </div>
 
-          <div class="department-performance">
-            <h4>部门绩效对比</h4>
-            <div class="department-table">
-              <div class="table-header">
-                <div class="table-cell">部门</div>
-                <div class="table-cell">优秀作品率</div>
-                <div class="table-cell">准时交付率</div>
-                <div class="table-cell">客户满意度</div>
+        <!-- 右侧待办事项区域 -->
+        <div class="right-panel">
+          <!-- 待办事项按钮卡片 -->
+          <mat-card class="todo-card">
+            <mat-card-header>
+              <mat-card-title>
+                <mat-icon>assignment</mat-icon>
+                待办事项列表
+              </mat-card-title>
+              <mat-card-subtitle>点击按钮查看详情</mat-card-subtitle>
+            </mat-card-header>
+            <mat-card-content>
+              <!-- 待办事项按钮卡片 -->
+              <div class="todo-buttons">
+                <button mat-raised-button 
+                        class="todo-button priority-high" 
+                        (click)="toggleTodoList()"
+                        [class.expanded]="showTodoList">
+                  <mat-icon>assignment_turned_in</mat-icon>
+                  <span>查看待办事项</span>
+                  <mat-icon class="expand-icon">{{showTodoList ? 'expand_less' : 'expand_more'}}</mat-icon>
+                </button>
               </div>
-              @for (dept of departmentPerformance(); track dept) {
-                <div class="table-row">
-                  <div class="table-cell">{{dept.name}}</div>
-                  <div class="table-cell">{{dept.excellentWorkRate}}%</div>
-                  <div class="table-cell">{{dept.deliveryOnTimeRate}}%</div>
-                  <div class="table-cell">{{dept.customerSatisfaction}}</div>
+              
+              <!-- 可展开的待办事项列表 -->
+              @if (showTodoList) {
+                <div class="todo-list" [@slideInOut]>
+                  @for (todo of todoList; track todo.id) {
+                    <div class="todo-item" [class]="getPriorityClass(todo.priority)">
+                      <div class="todo-content">
+                        <h4>{{todo.title}}</h4>
+                        <p>{{todo.description}}</p>
+                        <div class="todo-meta">
+                          <span class="priority-badge" [class]="'priority-' + todo.priority">
+                            {{todo.priority === 'high' ? '紧急' : todo.priority === 'medium' ? '一般' : '低'}}
+                          </span>
+                          <span class="due-date">{{todo.dueDate}}</span>
+                        </div>
+                      </div>
+                      <div class="todo-actions">
+                        <button mat-icon-button color="primary">
+                          <mat-icon>edit</mat-icon>
+                        </button>
+                        <button mat-icon-button color="accent">
+                          <mat-icon>check</mat-icon>
+                        </button>
+                      </div>
+                    </div>
+                  }
                 </div>
               }
-            </div>
-          </div>
-        </div>
-
-        <div class="card-section">
-          <h3>绩效等级分布</h3>
-          <!-- 用 ECharts 饼图替换占位块 -->
-          <div id="hrPerformanceDistributionChart" class="chart-container"></div>
+            </mat-card-content>
+          </mat-card>
         </div>
+      </div>
+    </div>
+  }
 
-        <div class="card-section">
-          <h3>扣分项预警</h3>
-          <div class="penalty-warnings">
-            @for (warning of penaltyWarnings(); track warning) {
-              <div class="warning-item">
-                <div class="warning-header">
-                  <mat-icon color="warn">error</mat-icon>
-                  <span class="warning-issue">{{warning.issue}}</span>
-                </div>
-                <div class="warning-details">
-                  <div class="warning-departments">
-                    <h4>高发部门</h4>
-                    @for (dept of warning.departments; track dept) {
-                      <div class="department-item">
-                        <span class="department-name">{{dept.name}}</span>
-                        <span class="department-count">{{dept.count}}次</span>
-                      </div>
-                    }
-                  </div>
-                  <div class="warning-employees">
-                    <h4>高发个人</h4>
-                    @for (emp of warning.employees; track emp) {
-                      <div class="employee-item">
-                        <span class="employee-name">{{emp.name}}</span>
-                        <span class="employee-department">{{emp.department}}</span>
-                        <span class="employee-count">{{emp.count}}次</span>
-                      </div>
+  <!-- 职级与晋升管理页面 -->
+  @if (activeTab === 'promotion') {
+    <div class="promotion-page">
+      <div class="content-layout">
+        <!-- 晋升规则卡片 -->
+        <div class="promotion-rules-section">
+          <mat-card class="rules-card">
+            <mat-card-header>
+              <mat-card-title>
+                <mat-icon>rule</mat-icon>
+                晋升规则
+              </mat-card-title>
+            </mat-card-header>
+            <mat-card-content>
+              @for (rule of promotionRules; track rule.id) {
+                <div class="rule-item">
+                  <h4 class="rule-title">{{rule.title}}</h4>
+                  <p class="rule-description">{{rule.description}}</p>
+                  <div class="rule-conditions">
+                    @for (condition of rule.conditions; track condition) {
+                      <mat-chip class="condition-chip">{{condition}}</mat-chip>
                     }
                   </div>
                 </div>
-              </div>
-            }
-          </div>
+              }
+            </mat-card-content>
+          </mat-card>
         </div>
-      </mat-card-content>
-    </mat-card>
 
-    <!-- 关键数据看板 -->
-    <mat-card class="dashboard-card">
-      <mat-card-header>
-        <mat-card-title>
-          <div class="card-title-container">
-            <mat-icon>insights</mat-icon>
-            <span>关键数据看板</span>
-          </div>
-        </mat-card-title>
-      </mat-card-header>
-      <mat-card-content>
-        <div class="card-section">
-          <h3>员工结构分析</h3>
-          <div class="structure-analysis">
-            <mat-tab-group>
-              @for (structure of employeeStructures(); track structure) {
-                <mat-tab [label]="structure.category">
-                  <div class="structure-data">
-                    @for (item of structure.data; track item) {
-                      <div class="structure-item">
-                        <div class="structure-header">
-                          <span class="structure-name">{{item.name}}</span>
-                          <span class="structure-count">{{item.count}}人</span>
-                        </div>
-                        <mat-progress-bar value="{{item.percentage}}" color="primary"></mat-progress-bar>
-                        <span class="structure-percentage">{{item.percentage}}%</span>
-                      </div>
-                    }
+        <!-- 部门绩效展示 -->
+        <div class="performance-section">
+          <mat-card class="performance-card">
+            <mat-card-header>
+              <mat-card-title>
+                <mat-icon>assessment</mat-icon>
+                部门绩效概览
+              </mat-card-title>
+            </mat-card-header>
+            <mat-card-content>
+              @for (dept of departmentPerformance; track dept.department) {
+                <div class="dept-performance-item">
+                  <div class="dept-header">
+                    <h4 class="dept-name">{{dept.department}}</h4>
+                    <div class="overall-score">{{dept.completionRate}}%</div>
+                  </div>
+                  <div class="performance-metrics">
+                    <div class="metric">
+                      <span class="metric-label">项目完成率</span>
+                      <mat-progress-bar mode="determinate" [value]="dept.completionRate"></mat-progress-bar>
+                      <span class="metric-value">{{dept.completionRate}}%</span>
+                    </div>
+                    <div class="metric">
+                      <span class="metric-label">优秀作品率</span>
+                      <mat-progress-bar mode="determinate" [value]="dept.excellentWorkRate"></mat-progress-bar>
+                      <span class="metric-value">{{dept.excellentWorkRate}}%</span>
+                    </div>
+                    <div class="metric">
+                      <span class="metric-label">客户满意度</span>
+                      <mat-progress-bar mode="determinate" [value]="dept.satisfactionRate"></mat-progress-bar>
+                      <span class="metric-value">{{dept.satisfactionRate}}%</span>
+                    </div>
+                    <div class="metric">
+                      <span class="metric-label">逾期率</span>
+                      <mat-progress-bar mode="determinate" [value]="dept.overdueRate" color="warn"></mat-progress-bar>
+                      <span class="metric-value">{{dept.overdueRate}}%</span>
+                    </div>
                   </div>
-                </mat-tab>
+                </div>
               }
-            </mat-tab-group>
-          </div>
+            </mat-card-content>
+          </mat-card>
         </div>
+      </div>
+    </div>
+  }
 
-        <div class="card-section">
-          <h3>异动趋势</h3>
-          <!-- 用 ECharts 折线图替换占位块 -->
-          <div id="hrTrendChart" class="chart-container"></div>
+  <!-- 新人跟进管理页面 -->
+  @if (activeTab === 'onboarding') {
+    <div class="onboarding-page">
+      <div class="content-layout">
+        <!-- 新人列表 -->
+        <div class="newbie-list-section">
+          <mat-card class="newbie-card">
+            <mat-card-header>
+              <mat-card-title>
+                <mat-icon>group_add</mat-icon>
+                新人列表
+              </mat-card-title>
+            </mat-card-header>
+            <mat-card-content>
+              @for (newbie of newbieList; track newbie.id) {
+                <div class="newbie-item">
+                  <div class="newbie-avatar">
+                    <mat-icon>person</mat-icon>
+                  </div>
+                  <div class="newbie-info">
+                    <h4 class="newbie-name">{{newbie.name}}</h4>
+                    <p class="newbie-position">{{newbie.position}}</p>
+                    <p class="newbie-date">入职日期:{{newbie.joinDate}}</p>
+                  </div>
+                  <div class="newbie-progress">
+                    <mat-progress-bar mode="determinate" [value]="newbie.progress"></mat-progress-bar>
+                    <span class="progress-text">{{newbie.progress}}%</span>
+                  </div>
+                </div>
+              }
+            </mat-card-content>
+          </mat-card>
         </div>
 
-        <div class="card-section">
-          <h3>待办事项提醒</h3>
-          <div class="todo-items">
-            @for (item of todoItems(); track item) {
-              <div class="todo-item" [ngClass]="getPriorityClass(item.priority)">
-                <div class="todo-header">
-                  <span class="todo-task">{{item.task}}</span>
-                  <mat-chip-set>
-                    <mat-chip>{{item.type}}</mat-chip>
-                  </mat-chip-set>
-                </div>
-                <div class="todo-footer">
-                  <span class="todo-due-date">截止日期: {{item.dueDate | date:'yyyy-MM-dd'}}</span>
-                  <span class="todo-priority">{{item.priority === 'high' ? '紧急' : item.priority === 'medium' ? '中等' : '普通'}}</span>
+        <!-- 跟进检查点 -->
+        <div class="checkpoint-section">
+          <mat-card class="checkpoint-card">
+            <mat-card-header>
+              <mat-card-title>
+                <mat-icon>checklist</mat-icon>
+                跟进检查点
+              </mat-card-title>
+            </mat-card-header>
+            <mat-card-content>
+              @for (checkpoint of onboardingCheckpoints; track checkpoint.id) {
+                <div class="checkpoint-item">
+                  <mat-checkbox
+                    [checked]="checkpoint.completed"
+                    (change)="checkpoint.completed = $any($event).checked">
+                  </mat-checkbox>
+                  <div class="checkpoint-content">
+                    <h4 class="checkpoint-title">{{checkpoint.title}}</h4>
+                    <p class="checkpoint-description">{{checkpoint.description}}</p>
+                    <div class="checkpoint-timeline">
+                      <mat-icon>schedule</mat-icon>
+                      <span>{{checkpoint.dueDate | date:'yyyy-MM-dd'}}</span>
+                    </div>
+                  </div>
                 </div>
-              </div>
-            }
-          </div>
+              }
+            </mat-card-content>
+          </mat-card>
         </div>
-      </mat-card-content>
-    </mat-card>
-  </div>
+      </div>
+    </div>
+  }
 </div>

+ 803 - 880
src/app/pages/hr/dashboard/dashboard.scss

@@ -1,969 +1,892 @@
-.dashboard-container {
+.hr-dashboard-container {
   padding: 20px;
-}
-
-.page-header {
-  margin-bottom: 24px;
-  
-  h1 {
-    margin: 0 0 8px 0;
-    font-size: 28px;
-    font-weight: 700; /* 提升标题权重以贴近 Admin 风格 */
-    background: linear-gradient(135deg, #165DFF, #7c3aed);
-    -webkit-background-clip: text;
-    -webkit-text-fill-color: transparent;
-    background-clip: text;
-  }
-  
-  .page-description {
-    margin: 0;
-    color: #4E5969; /* 更接近 Admin 次级文字色 */
-    font-size: 16px;
-  }
-}
-
-.filter-bar {
-  display: flex;
-  align-items: center;
-  margin-bottom: 24px;
-  background-color: white;
-  padding: 12px 16px;
-  border-radius: 8px;
-  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
-  
-  .time-filter {
-    display: flex;
-    align-items: center;
-    
-    .filter-label {
-      margin-right: 12px;
-      color: #616161;
-    }
+  background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
+  min-height: 100vh;
+  font-family: 'Roboto', sans-serif;
+
+  .top-navigation {
+    margin-bottom: 30px;
     
-    .filter-buttons {
+    .nav-buttons {
       display: flex;
+      gap: 15px;
+      justify-content: center;
+      flex-wrap: wrap;
       
-      button {
-        min-width: 40px;
-        border-radius: 4px;
-        margin-right: 8px;
+      .nav-button {
+        padding: 12px 24px;
+        border-radius: 25px;
+        font-weight: 500;
+        transition: all 0.3s ease;
+        box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
+        
+        &:hover {
+          transform: translateY(-2px);
+          box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
+        }
         
         &.active {
-          background-color: #1a3a6e;
+          background: linear-gradient(45deg, #2196F3, #21CBF3);
           color: white;
+          box-shadow: 0 6px 20px rgba(33, 150, 243, 0.3);
+        }
+        
+        mat-icon {
+          margin-right: 8px;
         }
       }
     }
   }
-}
-
-.dashboard-content {
-  display: grid;
-  grid-template-columns: 1fr;
-  gap: 24px;
-  
-  @media (min-width: 1200px) {
-    grid-template-columns: repeat(2, 1fr);
-    
-    .dashboard-card:last-child {
-      grid-column: span 2;
-    }
-  }
-}
-
-.dashboard-card {
-  border-radius: 12px; /* 统一圆角 */
-  background: #FFFFFF;
-  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.06); /* $shadow-sm */
-  border: 1px solid rgba(229, 230, 235, 0.8);
-  overflow: hidden;
-  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
-  
-  mat-card-header {
-    background-color: #FFFFFF; /* 与 Admin 统一 */
-    padding: 16px 20px;
-    border-bottom: 1px solid #E5E6EB;
-  }
-  
-  .chart-container {
-    width: 100%;
-    height: 320px;
-  }
-  
-  .card-title-container {
-    display: flex;
-    align-items: center;
-    
-    mat-icon {
-      margin-right: 10px;
-      color: #165DFF; /* 主色调 */
-    }
-    
-    span {
-      font-size: 18px;
-      font-weight: 600;
-      color: #1D2129; /* 标题主文字色 */
+
+  // 数据可视化页面样式
+  .visualization-page {
+    .main-layout {
+      display: grid;
+      grid-template-columns: 2fr 1fr;
+      gap: 30px;
+      min-height: 80vh;
+
+      @media (max-width: 1200px) {
+        grid-template-columns: 1fr;
+        gap: 20px;
+      }
     }
-  }
-  
-  mat-card-content {
-    padding: 20px;
-  }
-  
-  &:hover {
-    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12); /* $shadow-md */
-    transform: translateY(-4px);
-    border-color: rgba(22, 93, 255, 0.3);
-  }
-}
 
-.card-section {
-  margin-bottom: 24px;
-  
-  &:last-child {
-    margin-bottom: 0;
-  }
-  
-  h3 {
-    margin: 0 0 16px 0;
-    font-size: 16px;
-    font-weight: 600;
-    color: #4E5969;
-    border-left: 4px solid #165DFF; /* 与 Admin 主色一致 */
-    padding-left: 8px;
-  }
-  
-  h4 {
-    margin: 16px 0 8px 0;
-    font-size: 14px;
-    font-weight: 500;
-    color: #616161;
-  }
-}
-
-/* 入职离职看板样式 */
-.data-visualization {
-  display: flex;
-  flex-direction: column;
-  height: 200px;
-  
-  .chart-placeholder {
-    display: flex;
-    justify-content: space-between;
-    align-items: flex-end;
-    height: 160px;
-    margin-bottom: 16px;
-    
-    .chart-bar-container {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      flex: 1;
+    .left-panel {
+      display: grid;
+      grid-template-columns: 1fr 1fr;
+      gap: 20px;
       
-      .chart-label {
-        margin-top: 8px;
-        font-size: 12px;
-        color: #757575;
+      @media (max-width: 768px) {
+        grid-template-columns: 1fr;
       }
-      
-      .chart-bars {
-        display: flex;
-        align-items: flex-end;
-        height: 100%;
-        width: 100%;
-        justify-content: center;
-        gap: 4px;
-        
-        .chart-bar {
-          width: 20px;
-          position: relative;
-          border-radius: 4px 4px 0 0;
-          
-          &.hired {
-            background-color: #4caf50;
-          }
-          
-          &.resigned {
-            background-color: #f44336;
+
+      .chart-card {
+        background: rgba(255, 255, 255, 0.95);
+        backdrop-filter: blur(10px);
+        border-radius: 20px;
+        box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
+        transition: all 0.3s ease;
+        border: 1px solid rgba(255, 255, 255, 0.2);
+
+        &:hover {
+          transform: translateY(-5px);
+          box-shadow: 0 15px 40px rgba(0, 0, 0, 0.15);
+        }
+
+        mat-card-header {
+          background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+          color: white;
+          border-radius: 20px 20px 0 0;
+          padding: 20px;
+
+          mat-card-title {
+            display: flex;
+            align-items: center;
+            font-size: 18px;
+            font-weight: 600;
+            margin: 0;
+
+            mat-icon {
+              margin-right: 10px;
+              font-size: 24px;
+            }
           }
-          
-          .bar-value {
-            position: absolute;
-            top: -20px;
-            left: 50%;
-            transform: translateX(-50%);
-            font-size: 12px;
-            color: #616161;
+
+          mat-card-subtitle {
+            color: rgba(255, 255, 255, 0.8);
+            margin-top: 5px;
           }
         }
-      }
-    }
-  }
-  
-  .chart-legend {
-    display: flex;
-    justify-content: center;
-    gap: 16px;
-    
-    .legend-item {
-      display: flex;
-      align-items: center;
-      
-      .legend-color {
-        width: 12px;
-        height: 12px;
-        margin-right: 4px;
-        border-radius: 2px;
-        
-        &.hired {
-          background-color: #4caf50;
-        }
-        
-        &.resigned {
-          background-color: #f44336;
-        }
-        
-        &.top-tier {
-          background-color: #4caf50;
-        }
-        
-        &.mid-tier {
-          background-color: #2196f3;
-        }
-        
-        &.bottom-tier {
-          background-color: #f44336;
+
+        mat-card-content {
+          padding: 25px;
         }
       }
-      
-      span {
-        font-size: 12px;
-        color: #616161;
-      }
-    }
-  }
-}
-
-.process-tracking {
-  display: flex;
-  flex-direction: column;
-  gap: 16px;
-  
-  .process-item {
-    background-color: #f5f5f5;
-    padding: 12px;
-    border-radius: 4px;
-    
-    .process-header {
-      display: flex;
-      justify-content: space-between;
-      margin-bottom: 8px;
-      
-      .process-title {
-        font-weight: 500;
-        color: #424242;
-      }
-      
-      .process-count {
-        font-weight: 500;
-        color: #1a3a6e;
-      }
-    }
-    
-    mat-progress-bar {
-      margin-bottom: 8px;
-    }
-    
-    .process-action {
-      width: 100%;
-    }
-  }
-}
-
-.risk-alerts {
-  display: flex;
-  flex-direction: column;
-  gap: 16px;
-  
-  .risk-alert {
-    background-color: #fff8e1;
-    padding: 12px;
-    border-radius: 4px;
-    border-left: 4px solid #ffc107;
-    
-    .alert-header {
-      display: flex;
-      align-items: center;
-      margin-bottom: 8px;
-      
-      mat-icon {
-        margin-right: 8px;
-        font-size: 20px;
-        height: 20px;
-        width: 20px;
-      }
-      
-      .alert-title {
-        flex: 1;
-        font-weight: 500;
-        color: #424242;
+
+      // 职级分布饼图样式
+      .pie-chart-container {
+        height: 200px;
+        margin-bottom: 20px;
+        background: radial-gradient(circle, #f8f9fa 0%, #e9ecef 100%);
+        border-radius: 15px;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        font-size: 14px;
+        color: #666;
       }
-      
-      .alert-count {
-        font-weight: 500;
-        color: #f44336;
+
+      .chart-legend {
+        display: flex;
+        flex-direction: column;
+        gap: 10px;
+
+        .legend-item {
+          display: flex;
+          align-items: center;
+          gap: 10px;
+          font-size: 14px;
+
+          .legend-color {
+            width: 16px;
+            height: 16px;
+            border-radius: 50%;
+            
+            &.junior { background: #4CAF50; }
+            &.intermediate { background: #FF9800; }
+            &.senior { background: #2196F3; }
+          }
+        }
       }
-    }
-    
-    .alert-employees {
-      display: flex;
-      flex-direction: column;
-      gap: 8px;
-      
-      .employee-item {
+
+      // 折线图样式
+      .line-chart-container {
+        height: 200px;
+        margin-bottom: 20px;
+        background: radial-gradient(circle, #f8f9fa 0%, #e9ecef 100%);
+        border-radius: 15px;
         display: flex;
         align-items: center;
-        background-color: white;
-        padding: 8px;
-        border-radius: 4px;
-        
-        .employee-name {
-          font-weight: 500;
-          margin-right: 8px;
-        }
-        
-        .employee-position,
-        .employee-department {
-          color: #757575;
-          margin-right: 8px;
-          font-size: 12px;
+        justify-content: center;
+        font-size: 14px;
+        color: #666;
+      }
+
+      .key-notes {
+        h4 {
+          color: #333;
+          font-size: 16px;
+          margin-bottom: 15px;
+          font-weight: 600;
         }
-        
-        .days-left {
-          margin-left: auto;
-          background-color: #f44336;
-          color: white;
-          padding: 2px 6px;
-          border-radius: 4px;
-          font-size: 12px;
+
+        .note-item {
+          display: flex;
+          align-items: center;
+          gap: 10px;
+          margin-bottom: 10px;
+          padding: 10px;
+          background: rgba(33, 150, 243, 0.1);
+          border-radius: 10px;
+          border-left: 4px solid #2196F3;
+
+          .note-icon {
+            color: #2196F3;
+            font-size: 18px;
+          }
+
+          .note-text {
+            font-size: 14px;
+            color: #555;
+          }
         }
       }
-    }
-  }
-}
-
-/* 绩效总览看板样式 */
-.metrics-container {
-  display: flex;
-  flex-direction: column;
-  gap: 16px;
-  
-  .metric-item {
-    .metric-header {
-      display: flex;
-      justify-content: space-between;
-      margin-bottom: 8px;
-      
-      .metric-name {
-        font-weight: 500;
-        color: #424242;
+
+      // 雷达图样式
+      .radar-chart-container {
+        height: 200px;
+        margin-bottom: 20px;
+        background: radial-gradient(circle, #f8f9fa 0%, #e9ecef 100%);
+        border-radius: 15px;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        font-size: 14px;
+        color: #666;
       }
-      
-      .metric-values {
+
+      .radar-legend {
         display: flex;
-        gap: 8px;
-        
-        .metric-actual {
-          font-weight: 500;
-          color: #1a3a6e;
-        }
-        
-        .metric-target {
-          color: #757575;
-          font-size: 12px;
+        flex-wrap: wrap;
+        gap: 15px;
+
+        .legend-item {
+          display: flex;
+          align-items: center;
+          gap: 8px;
+          font-size: 14px;
+
+          .legend-color {
+            width: 16px;
+            height: 16px;
+            border-radius: 50%;
+          }
         }
       }
-    }
-  }
-}
-
-.department-table {
-  width: 100%;
-  border-collapse: collapse;
-  margin-top: 16px;
-  
-  .table-header {
-    display: flex;
-    background-color: #f5f5f5;
-    font-weight: 500;
-    color: #424242;
-  }
-  
-  .table-row {
-    display: flex;
-    border-bottom: 1px solid #e0e0e0;
-    
-    &:last-child {
-      border-bottom: none;
-    }
-  }
-  
-  .table-cell {
-    flex: 1;
-    padding: 8px;
-    text-align: center;
-    
-    &:first-child {
-      text-align: left;
-    }
-  }
-}
-
-.performance-distribution {
-  .pie-chart-placeholder {
-    display: flex;
-    height: 40px;
-    border-radius: 20px;
-    overflow: hidden;
-    margin-bottom: 16px;
-    
-    .pie-segment {
-      height: 100%;
-      display: flex;
-      justify-content: center;
-      align-items: center;
-      position: relative;
-      
-      &.top-tier {
-        background-color: #4caf50;
-      }
-      
-      &.mid-tier {
-        background-color: #2196f3;
-      }
-      
-      &.bottom-tier {
-        background-color: #f44336;
-      }
-      
-      .segment-label,
-      .segment-value {
-        color: white;
-        font-size: 12px;
-        font-weight: 500;
-        margin: 0 4px;
-      }
-    }
-  }
-}
-
-.penalty-warnings {
-  display: flex;
-  flex-direction: column;
-  gap: 16px;
-  
-  .warning-item {
-    background-color: #f5f5f5;
-    padding: 12px;
-    border-radius: 4px;
-    
-    .warning-header {
-      display: flex;
-      align-items: center;
-      margin-bottom: 12px;
-      
-      mat-icon {
-        margin-right: 8px;
-        font-size: 20px;
-        height: 20px;
-        width: 20px;
-      }
-      
-      .warning-issue {
-        font-weight: 500;
-        color: #424242;
+
+      // 空缺岗位样式
+      .vacancy-grid {
+        display: grid;
+        grid-template-columns: 1fr;
+        gap: 15px;
+
+        .vacancy-item {
+          padding: 15px;
+          border-radius: 12px;
+          border-left: 4px solid;
+          transition: all 0.3s ease;
+
+          &.urgent {
+            background: rgba(244, 67, 54, 0.1);
+            border-left-color: #f44336;
+          }
+
+          &.normal {
+            background: rgba(76, 175, 80, 0.1);
+            border-left-color: #4caf50;
+          }
+
+          &:hover {
+            transform: translateX(5px);
+            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
+          }
+
+          .vacancy-header {
+            display: flex;
+            align-items: center;
+            gap: 10px;
+            margin-bottom: 8px;
+
+            mat-icon {
+              font-size: 20px;
+            }
+
+            .position-name {
+              font-weight: 600;
+              color: #333;
+            }
+          }
+
+          .vacancy-count {
+            font-size: 18px;
+            font-weight: 700;
+            color: #2196F3;
+            margin-bottom: 5px;
+          }
+
+          .vacancy-duration {
+            font-size: 12px;
+            color: #666;
+          }
+        }
       }
     }
-    
-    .warning-details {
-      display: flex;
-      gap: 24px;
-      
-      .warning-departments,
-      .warning-employees {
-        flex: 1;
-        
-        h4 {
-          margin-top: 0;
+
+    .right-panel {
+      .todo-card {
+        background: rgba(255, 255, 255, 0.95);
+        backdrop-filter: blur(10px);
+        border-radius: 20px;
+        box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
+        border: 1px solid rgba(255, 255, 255, 0.2);
+        height: fit-content;
+
+        mat-card-header {
+          background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+          color: white;
+          border-radius: 20px 20px 0 0;
+          padding: 20px;
+
+          .todo-toggle-btn {
+            width: 100%;
+            background: rgba(255, 255, 255, 0.2);
+            color: white;
+            border: 1px solid rgba(255, 255, 255, 0.3);
+            border-radius: 15px;
+            padding: 15px 20px;
+            font-size: 16px;
+            font-weight: 600;
+            transition: all 0.3s ease;
+
+            &:hover {
+              background: rgba(255, 255, 255, 0.3);
+              transform: scale(1.02);
+            }
+
+            mat-icon {
+              margin-right: 10px;
+            }
+
+            .todo-count-chip {
+              margin-left: 10px;
+              background: rgba(255, 255, 255, 0.9);
+              color: #333;
+              font-weight: 700;
+            }
+          }
         }
-        
-        .department-item,
-        .employee-item {
+
+        mat-card-content {
+          padding: 0;
+        }
+
+        .todo-list {
+          max-height: 600px;
+          overflow-y: auto;
+
+          .todo-item {
+            border-bottom: 1px solid rgba(0, 0, 0, 0.1);
+            transition: all 0.3s ease;
+
+            &:hover {
+              background: rgba(33, 150, 243, 0.05);
+            }
+
+            &:last-child {
+              border-bottom: none;
+            }
+
+            &.priority-high {
+              border-left: 4px solid #f44336;
+            }
+
+            &.priority-medium {
+              border-left: 4px solid #ff9800;
+            }
+
+            &.priority-low {
+              border-left: 4px solid #4caf50;
+            }
+
+            .todo-content {
+              padding: 20px;
+
+              .todo-header-row {
+                display: flex;
+                align-items: flex-start;
+                gap: 15px;
+
+                .todo-priority-indicator {
+                  width: 8px;
+                  height: 8px;
+                  border-radius: 50%;
+                  margin-top: 8px;
+                  flex-shrink: 0;
+                }
+
+                .todo-main {
+                  flex: 1;
+                  display: flex;
+                  justify-content: space-between;
+                  align-items: flex-start;
+
+                  .todo-actions {
+                    display: flex;
+                    gap: 15px;
+                    flex: 1;
+
+                    .todo-info {
+                      flex: 1;
+
+                      .todo-title {
+                        margin: 0 0 8px 0;
+                        font-size: 16px;
+                        font-weight: 600;
+                        color: #333;
+                        transition: all 0.3s ease;
+
+                        &.completed {
+                          text-decoration: line-through;
+                          color: #999;
+                        }
+                      }
+
+                      .todo-description {
+                        margin: 0 0 12px 0;
+                        font-size: 14px;
+                        color: #666;
+                        line-height: 1.4;
+                      }
+
+                      .todo-meta {
+                        display: flex;
+                        gap: 8px;
+                        flex-wrap: wrap;
+
+                        .priority-chip {
+                          color: white;
+                          font-size: 12px;
+                          font-weight: 600;
+                          padding: 4px 8px;
+                          border-radius: 12px;
+                        }
+
+                        .type-chip {
+                          background: rgba(33, 150, 243, 0.1);
+                          color: #2196F3;
+                          font-size: 12px;
+                          font-weight: 500;
+                          padding: 4px 8px;
+                          border-radius: 12px;
+                        }
+                      }
+                    }
+                  }
+
+                  .drag-handle {
+                    color: #ccc;
+                    cursor: grab;
+                    padding: 5px;
+                    border-radius: 5px;
+                    transition: all 0.3s ease;
+
+                    &:hover {
+                      color: #999;
+                      background: rgba(0, 0, 0, 0.05);
+                    }
+
+                    &:active {
+                      cursor: grabbing;
+                    }
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+
+      // 关键岗位空缺样式
+      .vacancy-list {
+        .vacancy-item {
           display: flex;
-          justify-content: space-between;
-          padding: 4px 0;
+          align-items: center;
+          padding: 16px;
+          margin-bottom: 12px;
+          border-radius: 8px;
+          background: #fafafa;
+          border-left: 4px solid #e0e0e0;
+          transition: all 0.3s ease;
           
-          .department-name,
-          .employee-name {
-            font-weight: 500;
+          &:hover {
+            transform: translateY(-2px);
+            box-shadow: 0 4px 12px rgba(0,0,0,0.1);
           }
           
-          .department-count,
-          .employee-count {
-            color: #f44336;
-            font-weight: 500;
+          &.priority-high {
+            border-left-color: #f44336;
+            background: #ffebee;
           }
           
-          .employee-department {
-            color: #757575;
-            font-size: 12px;
-            margin-left: 8px;
+          &.priority-medium {
+            border-left-color: #ff9800;
+            background: #fff3e0;
+          }
+          
+          &.priority-low {
+            border-left-color: #4caf50;
+            background: #e8f5e8;
+          }
+          
+          .vacancy-icon {
+            margin-right: 16px;
+            
+            mat-icon {
+              font-size: 32px;
+              width: 32px;
+              height: 32px;
+              color: #666;
+            }
+          }
+          
+          .vacancy-info {
+            flex: 1;
+            
+            h4 {
+              margin: 0 0 8px 0;
+              font-size: 16px;
+              font-weight: 500;
+              color: #333;
+            }
+            
+            p {
+              margin: 0 0 8px 0;
+              color: #666;
+              font-size: 14px;
+            }
+            
+            .priority-badge {
+              padding: 4px 8px;
+              border-radius: 12px;
+              font-size: 12px;
+              font-weight: 500;
+              
+              &.priority-high {
+                background: #ffcdd2;
+                color: #c62828;
+              }
+              
+              &.priority-medium {
+                background: #ffe0b2;
+                color: #ef6c00;
+              }
+              
+              &.priority-low {
+                background: #c8e6c9;
+                color: #2e7d32;
+              }
+            }
           }
         }
       }
     }
   }
-}
-
-/* 关键数据看板样式 */
-.structure-data {
-  padding: 16px 0;
-  
-  .structure-item {
-    margin-bottom: 16px;
-    
-    &:last-child {
-      margin-bottom: 0;
+
+  // 职级与晋升管理页面样式
+  .promotion-page {
+    .content-layout {
+      display: grid;
+      grid-template-columns: 1fr 1fr;
+      gap: 30px;
+
+      @media (max-width: 1200px) {
+        grid-template-columns: 1fr;
+      }
     }
-    
-    .structure-header {
-      display: flex;
-      justify-content: space-between;
-      margin-bottom: 8px;
-      
-      .structure-name {
-        font-weight: 500;
-        color: #424242;
+
+    .rules-card, .performance-card {
+      background: rgba(255, 255, 255, 0.95);
+      backdrop-filter: blur(10px);
+      border-radius: 20px;
+      box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
+      border: 1px solid rgba(255, 255, 255, 0.2);
+
+      mat-card-header {
+        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+        color: white;
+        border-radius: 20px 20px 0 0;
+        padding: 20px;
+
+        mat-card-title {
+          display: flex;
+          align-items: center;
+          font-size: 18px;
+          font-weight: 600;
+          margin: 0;
+
+          mat-icon {
+            margin-right: 10px;
+          }
+        }
       }
-      
-      .structure-count {
-        font-weight: 500;
-        color: #1a3a6e;
+
+      mat-card-content {
+        padding: 25px;
       }
     }
-    
-    .structure-percentage {
-      display: block;
-      text-align: right;
-      margin-top: 4px;
-      font-size: 12px;
-      color: #757575;
+
+    .rule-item {
+      margin-bottom: 25px;
+      padding: 20px;
+      background: rgba(33, 150, 243, 0.05);
+      border-radius: 15px;
+      border-left: 4px solid #2196F3;
+
+      .rule-title {
+        color: #333;
+        font-size: 16px;
+        font-weight: 600;
+        margin-bottom: 10px;
+      }
+
+      .rule-description {
+        color: #666;
+        font-size: 14px;
+        margin-bottom: 15px;
+        line-height: 1.5;
+      }
+
+      .rule-conditions {
+        display: flex;
+        flex-wrap: wrap;
+        gap: 8px;
+
+        .condition-chip {
+          background: rgba(76, 175, 80, 0.1);
+          color: #4caf50;
+          font-size: 12px;
+          padding: 6px 12px;
+          border-radius: 15px;
+          border: 1px solid rgba(76, 175, 80, 0.3);
+        }
+      }
     }
-  }
-}
-
-.movement-trends {
-  height: 200px;
-  
-  .line-chart-placeholder {
-    position: relative;
-    height: 160px;
-    margin-bottom: 16px;
-    border-bottom: 1px solid #e0e0e0;
-    border-left: 1px solid #e0e0e0;
-    
-    .chart-lines {
-      position: absolute;
-      top: 0;
-      left: 0;
-      width: 100%;
-      height: 100%;
-      
-      .chart-line {
-        position: absolute;
-        height: 2px;
-        
-        &.hired {
-          background-color: #4caf50;
-          width: 100%;
-          top: 30%;
+
+    .dept-performance-item {
+      margin-bottom: 25px;
+      padding: 20px;
+      background: rgba(255, 255, 255, 0.8);
+      border-radius: 15px;
+      box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
+
+      .dept-header {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        margin-bottom: 20px;
+
+        .dept-name {
+          color: #333;
+          font-size: 18px;
+          font-weight: 600;
+          margin: 0;
         }
-        
-        &.resigned {
-          background-color: #f44336;
-          width: 100%;
-          top: 60%;
+
+        .overall-score {
+          background: linear-gradient(45deg, #4CAF50, #8BC34A);
+          color: white;
+          padding: 8px 16px;
+          border-radius: 20px;
+          font-weight: 700;
+          font-size: 16px;
         }
       }
-    }
-    
-    .chart-points {
-      position: absolute;
-      top: 0;
-      left: 0;
-      width: 100%;
-      height: 100%;
-      
-      .chart-point-group {
-        position: absolute;
-        bottom: 0;
-        transform: translateX(-50%);
-        
-        .chart-point {
-          position: absolute;
-          width: 8px;
-          height: 8px;
-          border-radius: 50%;
-          transform: translate(-50%, 50%);
-          
-          &.hired {
-            background-color: #4caf50;
+
+      .performance-metrics {
+        .metric {
+          display: flex;
+          align-items: center;
+          gap: 15px;
+          margin-bottom: 15px;
+
+          .metric-label {
+            min-width: 100px;
+            font-size: 14px;
+            color: #666;
+            font-weight: 500;
           }
-          
-          &.resigned {
-            background-color: #f44336;
+
+          mat-progress-bar {
+            flex: 1;
+            height: 8px;
+            border-radius: 4px;
+          }
+
+          .metric-value {
+            min-width: 50px;
+            text-align: right;
+            font-weight: 600;
+            color: #333;
           }
-        }
-        
-        .chart-label {
-          position: absolute;
-          bottom: -20px;
-          left: 50%;
-          transform: translateX(-50%);
-          font-size: 12px;
-          color: #757575;
         }
       }
     }
   }
-}
-
-.todo-items {
-  display: flex;
-  flex-direction: column;
-  gap: 12px;
-  
-  .todo-item {
-    background-color: #f5f5f5;
-    padding: 12px;
-    border-radius: 4px;
-    border-left: 4px solid #9e9e9e;
-    
-    &.priority-high {
-      border-left-color: #f44336;
-    }
-    
-    &.priority-medium {
-      border-left-color: #ff9800;
-    }
-    
-    &.priority-low {
-      border-left-color: #4caf50;
+
+  // 新人跟进管理页面样式
+  .onboarding-page {
+    .content-layout {
+      display: grid;
+      grid-template-columns: 1fr 1fr;
+      gap: 30px;
+
+      @media (max-width: 1200px) {
+        grid-template-columns: 1fr;
+      }
     }
-    
-    .todo-header {
-      display: flex;
-      justify-content: space-between;
-      align-items: flex-start;
-      margin-bottom: 8px;
-      
-      .todo-task {
-        font-weight: 500;
-        color: #424242;
-        margin-right: 8px;
+
+    .newbie-card, .checkpoint-card {
+      background: rgba(255, 255, 255, 0.95);
+      backdrop-filter: blur(10px);
+      border-radius: 20px;
+      box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
+      border: 1px solid rgba(255, 255, 255, 0.2);
+
+      mat-card-header {
+        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+        color: white;
+        border-radius: 20px 20px 0 0;
+        padding: 20px;
+
+        mat-card-title {
+          display: flex;
+          align-items: center;
+          font-size: 18px;
+          font-weight: 600;
+          margin: 0;
+
+          mat-icon {
+            margin-right: 10px;
+          }
+        }
+      }
+
+      mat-card-content {
+        padding: 25px;
       }
     }
-    
-    .todo-footer {
+
+    .newbie-item {
       display: flex;
-      justify-content: space-between;
-      
-      .todo-due-date {
-        font-size: 12px;
-        color: #757575;
+      align-items: center;
+      gap: 15px;
+      padding: 15px;
+      margin-bottom: 15px;
+      background: rgba(33, 150, 243, 0.05);
+      border-radius: 15px;
+      transition: all 0.3s ease;
+
+      &:hover {
+        background: rgba(33, 150, 243, 0.1);
+        transform: translateX(5px);
       }
-      
-      .todo-priority {
-        font-size: 12px;
-        font-weight: 500;
-        
-        .priority-high & {
-          color: #f44336;
+
+      .newbie-avatar {
+        width: 50px;
+        height: 50px;
+        background: linear-gradient(45deg, #2196F3, #21CBF3);
+        border-radius: 50%;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        color: white;
+
+        mat-icon {
+          font-size: 24px;
         }
-        
-        .priority-medium & {
-          color: #ff9800;
+      }
+
+      .newbie-info {
+        flex: 1;
+
+        .newbie-name {
+          margin: 0 0 5px 0;
+          font-size: 16px;
+          font-weight: 600;
+          color: #333;
         }
-        
-        .priority-low & {
-          color: #4caf50;
+
+        .newbie-position {
+          margin: 0 0 5px 0;
+          font-size: 14px;
+          color: #666;
+        }
+
+        .newbie-date {
+          margin: 0;
+          font-size: 12px;
+          color: #999;
+        }
+      }
+
+      .newbie-progress {
+        display: flex;
+        align-items: center;
+        gap: 10px;
+        min-width: 120px;
+
+        mat-progress-bar {
+          flex: 1;
+          height: 6px;
+          border-radius: 3px;
+        }
+
+        .progress-text {
+          font-size: 14px;
+          font-weight: 600;
+          color: #333;
         }
       }
     }
-  }
-}
-
-/* 修复Material图标显示问题 */
-.dashboard-container mat-icon {
-  font-family: 'Material Icons' !important;
-  -webkit-font-feature-settings: 'liga';
-  font-feature-settings: 'liga';
-  -webkit-font-smoothing: antialiased;
-  font-size: 22px;
-  line-height: 1;
-}
-
-/* 优化卡片标题区域 */
-.card-title-container {
-  display: flex;
-  align-items: center;
-  gap: 12px;
-  
-  mat-icon {
-    background: linear-gradient(135deg, #e6f7ff 0%, #f3e8ff 100%);
-    color: #165DFF;
-    padding: 8px;
-    border-radius: 50%;
-    font-size: 20px;
-    width: 36px;
-    height: 36px;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-  }
-}
-
-/* 美化进度条 */
-mat-progress-bar {
-  border-radius: 8px;
-  overflow: hidden;
-  height: 8px;
-  
-  .mdc-linear-progress__bar-inner {
-    border-radius: 8px;
-  }
-}
-
-/* 优化表格样式 */
-.department-table {
-  background: #fff;
-  border-radius: 8px;
-  overflow: hidden;
-  border: 1px solid #e5e6eb;
-  
-  .table-header {
-    background: linear-gradient(180deg, #f7f9fc 0%, #eef3ff 100%);
-    font-weight: 600;
-    color: #1a3a6e;
-  }
-  
-  .table-row {
-    border-bottom: 1px solid #f0f0f0;
-    
-    &:hover {
-      background-color: #f8f9fa;
-    }
-    
-    &:last-child {
-      border-bottom: none;
-    }
-  }
-  
-  .table-cell {
-    padding: 12px 16px;
-    text-align: center;
-    
-    &:first-child {
-      text-align: left;
-      font-weight: 500;
-    }
-  }
-}
-
-/* 优化指标卡片 */
-.metrics-container {
-  display: grid;
-  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
-  gap: 16px;
-  margin-bottom: 24px;
-  
-  .metric-item {
-    background: #fff;
-    border: 1px solid #e5e6eb;
-    border-radius: 8px;
-    padding: 16px;
-    transition: all 0.2s ease;
-    
-    &:hover {
-      box-shadow: 0 4px 12px rgba(22, 93, 255, 0.1);
-      border-color: #165DFF;
-    }
-    
-    .metric-header {
+
+    .checkpoint-item {
       display: flex;
-      justify-content: space-between;
-      align-items: flex-start;
-      margin-bottom: 12px;
-      
-      .metric-name {
-        font-weight: 500;
-        color: #1d2129;
-      }
-      
-      .metric-values {
-        display: flex;
-        flex-direction: column;
-        align-items: flex-end;
-        
-        .metric-actual {
-          font-size: 18px;
+      gap: 15px;
+      padding: 15px;
+      margin-bottom: 15px;
+      background: rgba(76, 175, 80, 0.05);
+      border-radius: 15px;
+      border-left: 4px solid #4caf50;
+
+      .checkpoint-content {
+        flex: 1;
+
+        .checkpoint-title {
+          margin: 0 0 8px 0;
+          font-size: 16px;
           font-weight: 600;
-          color: #165DFF;
+          color: #333;
         }
-        
-        .metric-target {
+
+        .checkpoint-description {
+          margin: 0 0 10px 0;
+          font-size: 14px;
+          color: #666;
+          line-height: 1.4;
+        }
+
+        .checkpoint-timeline {
+          display: flex;
+          align-items: center;
+          gap: 5px;
           font-size: 12px;
-          color: #86909c;
+          color: #999;
+
+          mat-icon {
+            font-size: 16px;
+          }
         }
       }
     }
   }
-}
-
-/* 优化警告项样式 */
-.warning-item, .risk-alert {
-  border-radius: 8px;
-  border: 1px solid #ffd666;
-  background: linear-gradient(135deg, #fff7e6 0%, #fffbf0 100%);
-  
-  .warning-header, .alert-header {
-    display: flex;
-    align-items: center;
-    gap: 8px;
-    margin-bottom: 12px;
-    
-    mat-icon {
-      color: #fa8c16;
-      background: rgba(250, 140, 22, 0.1);
-      padding: 4px;
-      border-radius: 50%;
-    }
-    
-    .warning-issue, .alert-title {
-      font-weight: 500;
-      color: #d46b08;
-    }
-  }
-}
-
-/* 优化待办事项 */
-.todo-items {
-  display: flex;
-  flex-direction: column;
-  gap: 12px;
-  
-  .todo-item {
-    background: #fff;
-    border: 1px solid #e5e6eb;
-    border-radius: 8px;
-    padding: 16px;
-    transition: all 0.2s ease;
-    
-    &.high-priority {
-      border-left: 4px solid #ff4d4f;
-    }
-    
-    &.medium-priority {
-      border-left: 4px solid #faad14;
-    }
-    
-    &.low-priority {
-      border-left: 4px solid #52c41a;
-    }
-    
-    &:hover {
-      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
-    }
-    
-    .todo-header {
-      display: flex;
-      justify-content: space-between;
-      align-items: center;
-      margin-bottom: 8px;
-      
-      .todo-task {
-        font-weight: 500;
-        color: #1d2129;
-      }
-    }
-    
-    .todo-footer {
-      display: flex;
-      justify-content: space-between;
-      align-items: center;
-      font-size: 12px;
-      color: #86909c;
-      
-      .todo-priority {
-        padding: 2px 8px;
-        border-radius: 12px;
-        background: #f2f3f5;
-        color: #4e5969;
+
+  // 响应式设计
+  @media (max-width: 768px) {
+    padding: 15px;
+
+    .top-navigation {
+      margin-bottom: 20px;
+
+      .nav-buttons {
+        .nav-button {
+          padding: 10px 16px;
+          font-size: 14px;
+        }
       }
     }
-  }
-}
-
-/* 优化结构分析 */
-.structure-analysis {
-  .structure-item {
-    background: #fff;
-    border: 1px solid #e5e6eb;
-    border-radius: 8px;
-    padding: 16px;
-    margin-bottom: 12px;
-    
-    .structure-header {
-      display: flex;
-      justify-content: space-between;
-      align-items: center;
-      margin-bottom: 8px;
-      
-      .structure-name {
-        font-weight: 500;
-        color: #1d2129;
+
+    .visualization-page {
+      .main-layout {
+        gap: 15px;
       }
-      
-      .structure-count {
-        font-weight: 600;
-        color: #165DFF;
+
+      .left-panel {
+        gap: 15px;
+
+        .chart-card {
+          mat-card-header {
+            padding: 15px;
+
+            mat-card-title {
+              font-size: 16px;
+            }
+          }
+
+          mat-card-content {
+            padding: 15px;
+          }
+        }
       }
     }
-    
-    .structure-percentage {
-      font-size: 12px;
-      color: #86909c;
-      margin-top: 4px;
-      display: block;
-    }
   }
-}
 
-/* 响应式优化 */
-@media (max-width: 768px) {
-  .dashboard-content {
-    grid-template-columns: 1fr;
+  // 滚动条样式
+  ::-webkit-scrollbar {
+    width: 8px;
   }
-  
-  .metrics-container {
-    grid-template-columns: 1fr;
+
+  ::-webkit-scrollbar-track {
+    background: rgba(0, 0, 0, 0.1);
+    border-radius: 4px;
   }
-  
-  .department-table {
-    font-size: 14px;
-    
-    .table-cell {
-      padding: 8px 12px;
+
+  ::-webkit-scrollbar-thumb {
+    background: rgba(0, 0, 0, 0.3);
+    border-radius: 4px;
+
+    &:hover {
+      background: rgba(0, 0, 0, 0.5);
     }
   }
 }

+ 578 - 309
src/app/pages/hr/dashboard/dashboard.ts

@@ -1,84 +1,72 @@
-import { Component, OnInit, AfterViewInit, OnDestroy, signal } from '@angular/core';
+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 { MatIconModule } from '@angular/material/icon';
 import { MatTabsModule } from '@angular/material/tabs';
-import { MatProgressBarModule } from '@angular/material/progress-bar';
+import { MatIconModule } from '@angular/material/icon';
+import { MatCheckboxModule } from '@angular/material/checkbox';
 import { MatChipsModule } from '@angular/material/chips';
-import { MatSelectModule } from '@angular/material/select';
-import { FormsModule } from '@angular/forms';
-
-// 模拟数据接口
-interface EmployeeMovement {
-  month: string;
-  hired: number;
-  resigned: number;
-  turnoverRate: number;
-}
-
-interface DepartmentData {
-  name: string;
-  hired: number;
-  resigned: number;
-  turnoverRate: number;
-}
+import { MatProgressBarModule } from '@angular/material/progress-bar';
+import { MatBadgeModule } from '@angular/material/badge';
+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';
 
-interface PendingProcess {
-  type: 'hire' | 'resign';
-  count: number;
-  progress: number;
+// 数据模型定义
+export interface TodoItem {
+  id: number;
+  title: string;
+  description: string;
+  priority: 'high' | 'medium' | 'low';
+  status: 'pending' | 'completed' | 'in_progress';
+  type: 'resume' | 'onboarding' | 'promotion' | 'resignation';
 }
 
-interface RiskAlert {
-  type: string;
-  employees: {name: string; position: string; department: string; daysLeft?: number}[];
-  count: number;
+export interface RankDistribution {
+  junior: number;
+  intermediate: number;
+  senior: number;
 }
 
-interface PerformanceMetric {
-  name: string;
-  target: number;
-  actual: number;
-  unit: string;
+export interface MonthlyData {
+  month: string;
+  hired: number;
+  left: number;
+  notes?: string;
 }
 
-interface DepartmentPerformance {
-  name: string;
+export interface DepartmentPerformance {
+  department: string;
+  completionRate: number;
   excellentWorkRate: number;
-  deliveryOnTimeRate: number;
-  customerSatisfaction: number;
+  satisfactionRate: number;
+  overdueRate: number;
 }
 
-interface PerformanceTier {
-  name: string;
-  percentage: number;
-  count: number;
+export interface PromotionRule {
+  id: number;
+  title: string;
+  description: string;
+  conditions: string[];
 }
 
-interface PenaltyWarning {
-  issue: string;
-  departments: {name: string; count: number}[];
-  employees: {name: string; department: string; count: number}[];
+export interface PromotionSuggestion {
+  employeeId: number;
+  employeeName: string;
+  currentRank: string;
+  suggestedRank: string;
+  reasons: string[];
+  status: 'pending' | 'approved' | 'rejected';
 }
 
-interface EmployeeStructure {
-  category: string;
-  data: {name: string; count: number; percentage: number}[];
-}
-
-interface MovementTrend {
-  month: string;
-  hired: number;
-  resigned: number;
-}
-
-interface TodoItem {
-  task: string;
+export interface OnboardingCheckpoint {
+  id: number;
+  title: string;
+  description: string;
   dueDate: Date;
-  priority: 'high' | 'medium' | 'low';
-  type: string;
+  completed: boolean;
+  interviewTemplate: string[];
 }
 
 @Component({
@@ -89,293 +77,574 @@ interface TodoItem {
     RouterModule,
     MatCardModule,
     MatButtonModule,
-    MatIconModule,
     MatTabsModule,
-    MatProgressBarModule,
+    MatIconModule,
+    MatCheckboxModule,
     MatChipsModule,
-    MatSelectModule,
-    FormsModule
+    MatProgressBarModule,
+    MatBadgeModule,
+    DragDropModule
   ],
   templateUrl: './dashboard.html',
-  styleUrls: ['./dashboard.scss']
+  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, OnDestroy {
-  // 时间筛选
-  timeFilter = signal('month');
-
-  // 图表实例
-  private movementChart: any | null = null;
-  private performanceChart: any | null = null;
-  private trendChart: any | null = null;
-  // 入职离职数据
-  employeeMovements = signal<EmployeeMovement[]>([
-    { month: '1月', hired: 12, resigned: 5, turnoverRate: 3.2 },
-    { month: '2月', hired: 8, resigned: 4, turnoverRate: 2.5 },
-    { month: '3月', hired: 15, resigned: 6, turnoverRate: 3.8 },
-    { month: '4月', hired: 10, resigned: 8, turnoverRate: 5.1 },
-    { month: '5月', hired: 14, resigned: 7, turnoverRate: 4.2 },
-    { month: '6月', hired: 18, resigned: 5, turnoverRate: 2.9 }
-  ]);
+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>;
   
-  departmentData = signal<DepartmentData[]>([
-    { name: '设计部', hired: 8, resigned: 3, turnoverRate: 4.2 },
-    { name: '客服部', hired: 5, resigned: 2, turnoverRate: 3.1 },
-    { name: '技术部', hired: 12, resigned: 4, turnoverRate: 5.3 },
-    { name: '行政部', hired: 3, resigned: 1, turnoverRate: 2.8 }
-  ]);
+  private pieChart!: Chart;
+  private lineChart!: Chart;
+  private radarChart!: Chart;
+  // 当前激活的标签页
+  activeTab: 'visualization' | 'promotion' | 'onboarding' = 'visualization';
   
-  pendingProcesses = signal<PendingProcess[]>([
-    { type: 'hire', count: 8, progress: 65 },
-    { type: 'resign', count: 3, progress: 40 }
-  ]);
+  // 待办事项是否展开
+  isTodoExpanded = false;
   
-  riskAlerts = signal<RiskAlert[]>([
-    { 
-      type: '试用期即将到期', 
-      employees: [
-        { name: '张三', position: '设计师', department: '设计部', daysLeft: 7 },
-        { name: '李四', position: '客服专员', department: '客服部', daysLeft: 10 },
-        { name: '王五', position: '前端开发', department: '技术部', daysLeft: 15 }
-      ],
-      count: 3
+  // 当前页面
+  currentPage: string = 'dashboard';
+  
+  // 待办事项展开状态
+  showTodoList: boolean = false;
+
+  // 待办事项列表(用于右侧展示)
+  todoList = [
+    {
+      id: 1,
+      title: '简历初筛',
+      description: '筛选新收到的设计师简历',
+      priority: 'high',
+      dueDate: '2024-01-25'
     },
-    { 
-      type: '离职关键岗位', 
-      employees: [
-        { name: '赵六', position: '高级设计师', department: '设计部' },
-        { name: '钱七', position: '技术组长', department: '技术部' }
-      ],
-      count: 2
+    {
+      id: 2,
+      title: '入职评定',
+      description: '完成新员工入职评定表',
+      priority: 'medium',
+      dueDate: '2024-01-28'
+    },
+    {
+      id: 3,
+      title: '晋升审核',
+      description: '审核中级设计师晋升申请',
+      priority: 'high',
+      dueDate: '2024-01-30'
+    },
+    {
+      id: 4,
+      title: '离职面谈',
+      description: '安排资深设计师离职面谈',
+      priority: 'medium',
+      dueDate: '2024-02-02'
     }
-  ]);
-  
-  // 绩效数据
-  performanceMetrics = signal<PerformanceMetric[]>([
-    { name: '优秀作品率', target: 80, actual: 76, unit: '%' },
-    { name: '项目交付准时率', target: 95, actual: 92, unit: '%' },
-    { name: '客户满意度评分', target: 4.5, actual: 4.3, unit: '分' }
-  ]);
-  
-  departmentPerformance = signal<DepartmentPerformance[]>([
-    { name: '设计部', excellentWorkRate: 78, deliveryOnTimeRate: 94, customerSatisfaction: 4.4 },
-    { name: '客服部', excellentWorkRate: 82, deliveryOnTimeRate: 96, customerSatisfaction: 4.6 },
-    { name: '技术部', excellentWorkRate: 75, deliveryOnTimeRate: 90, customerSatisfaction: 4.2 },
-    { name: '行政部', excellentWorkRate: 80, deliveryOnTimeRate: 95, customerSatisfaction: 4.5 }
-  ]);
+  ];
   
-  performanceTiers = signal<PerformanceTier[]>([
-    { name: '头部20%', percentage: 20, count: 24 },
-    { name: '中部70%', percentage: 70, count: 84 },
-    { name: '尾部10%', percentage: 10, count: 12 }
-  ]);
-  
-  penaltyWarnings = signal<PenaltyWarning[]>([
-    { 
-      issue: '项目延期', 
-      departments: [
-        { name: '设计部', count: 5 },
-        { name: '技术部', count: 3 }
-      ],
-      employees: [
-        { name: '张三', department: '设计部', count: 2 },
-        { name: '李四', department: '技术部', count: 2 }
+  // 模拟数据
+  todoItems: TodoItem[] = [
+    {
+      id: 1,
+      title: '简历初筛',
+      description: '筛选新收到的设计师简历',
+      priority: 'high',
+      status: 'pending',
+      type: 'resume'
+    },
+    {
+      id: 2,
+      title: '入职评定',
+      description: '完成新员工入职评定表',
+      priority: 'medium',
+      status: 'pending',
+      type: 'onboarding'
+    },
+    {
+      id: 3,
+      title: '晋升审核',
+      description: '审核中级设计师晋升申请',
+      priority: 'high',
+      status: 'pending',
+      type: 'promotion'
+    },
+    {
+      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
+    }
+  ];
+
+  promotionRules: PromotionRule[] = [
+    {
+      id: 1,
+      title: '初级→中级晋升标准',
+      description: '设计师晋升中级标准',
+      conditions: [
+        '连续3个月优秀作品率超过80%',
+        '逾期率低于5%',
+        '客户满意度评分4.5以上',
+        '至少完成12个主要项目'
       ]
     },
-    { 
-      issue: '投诉差评', 
-      departments: [
-        { name: '客服部', count: 4 },
-        { name: '设计部', count: 2 }
-      ],
-      employees: [
-        { name: '王五', department: '客服部', count: 3 },
-        { name: '赵六', department: '设计部', count: 1 }
+    {
+      id: 2,
+      title: '中级→高级晋升标准',
+      description: '设计师晋升高级标准',
+      conditions: [
+        '连续6个月优秀作品率超过85%',
+        '逾期率低于3%',
+        '客户满意度评分4.8以上',
+        '带领过至少2个大型项目',
+        '有 mentorship 经验'
       ]
     }
-  ]);
-  
-  // 关键数据
-  employeeStructures = signal<EmployeeStructure[]>([
+  ];
+
+  promotionSuggestions: PromotionSuggestion[] = [
     {
-      category: '技术等级',
-      data: [
-        { name: '初级', count: 45, percentage: 37.5 },
-        { name: '中级', count: 55, percentage: 45.8 },
-        { name: '高级', count: 20, percentage: 16.7 }
+      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[] = [
+    {
+      id: 1,
+      title: '入职1周面谈',
+      description: '了解新人工作适应情况',
+      dueDate: new Date(),
+      completed: false,
+      interviewTemplate: [
+        '工作环境是否适应?',
+        '是否有不清楚的工作流程?',
+        '团队沟通是否顺畅?',
+        '是否需要额外的工作工具?'
       ]
     },
     {
-      category: '岗位类型',
-      data: [
-        { name: '设计师', count: 60, percentage: 50 },
-        { name: '客服', count: 30, percentage: 25 },
-        { name: '技术组长', count: 15, percentage: 12.5 },
-        { name: '其他', count: 15, percentage: 12.5 }
+      id: 2,
+      title: '入职1个月评估',
+      description: '评估新人技能掌握情况',
+      dueDate: new Date(Date.now() + 21 * 24 * 60 * 60 * 1000),
+      completed: false,
+      interviewTemplate: [
+        '主要工作技能掌握程度?',
+        '遇到的最大挑战是什么?',
+        '对团队文化的感受?',
+        '个人职业发展期望?'
       ]
     },
     {
-      category: '部门',
-      data: [
-        { name: '设计部', count: 65, percentage: 54.2 },
-        { name: '客服部', count: 35, percentage: 29.2 },
-        { name: '技术部', count: 20, percentage: 16.6 }
+      id: 3,
+      title: '入职3个月总结',
+      description: '全面评估新人表现',
+      dueDate: new Date(Date.now() + 83 * 24 * 60 * 60 * 1000),
+      completed: false,
+      interviewTemplate: [
+        '工作成果总体评价',
+        '需要改进的技能领域',
+        '长期职业规划讨论',
+        '转正评估准备'
       ]
     }
-  ]);
-  
-  movementTrends = signal<MovementTrend[]>([
-    { month: '1月', hired: 12, resigned: 5 },
-    { month: '2月', hired: 8, resigned: 4 },
-    { month: '3月', hired: 15, resigned: 6 },
-    { month: '4月', hired: 10, resigned: 8 },
-    { month: '5月', hired: 14, resigned: 7 },
-    { month: '6月', hired: 18, resigned: 5 }
-  ]);
-  
-  todoItems = signal<TodoItem[]>([
-    { task: '审核张三绩效表', dueDate: new Date('2023-06-15'), priority: 'high', type: '绩效审核' },
-    { task: '签署李四离职协议', dueDate: new Date('2023-06-12'), priority: 'high', type: '离职手续' },
-    { task: '跟进王五试用期评估', dueDate: new Date('2023-06-20'), priority: 'medium', type: '试用期评估' },
-    { task: '审核新员工入职材料', dueDate: new Date('2023-06-18'), priority: 'medium', type: '入职手续' },
-    { task: '更新部门人员架构图', dueDate: new Date('2023-06-25'), priority: 'low', type: '文档更新' }
-  ]);
-  
+  ];
+
   ngOnInit() {
-    // 实际项目中这里会调用服务获取数据
-  }
-  
-  ngAfterViewInit(): void {
+    // 注册Chart.js组件
+    Chart.register(...registerables);
+    // 初始化图表数据
     this.initCharts();
-    window.addEventListener('resize', this.handleResize);
   }
-  
-  ngOnDestroy(): void {
-    window.removeEventListener('resize', this.handleResize);
-    this.disposeCharts();
+
+  ngAfterViewInit() {
+    // 延迟初始化图表,确保DOM已渲染
+    setTimeout(() => {
+      this.initializeCharts();
+    }, 100);
   }
-  
-  // 切换时间维度
-  changeTimeFilter(filter: string) {
-    this.timeFilter.set(filter);
-    // 实际项目中这里会根据筛选条件重新获取数据
-    // 刷新图表
-    this.initCharts();
+
+  // 切换标签页
+  switchTab(tab: 'visualization' | 'promotion' | 'onboarding') {
+    this.activeTab = tab;
   }
-  
-  // 跳转到对应处理页面
-  navigateToProcess(type: 'hire' | 'resign') {
-    // 实际项目中这里会导航到对应页面
-    console.log(`Navigate to ${type} process page`);
+
+
+
+  // 初始化图表(这里需要后续集成ECharts)
+  private initCharts() {
+    // 图表初始化逻辑将在后续实现
+    console.log('初始化图表数据');
   }
-  
-  // 获取进度条颜色
-  getProgressColor(value: number, target: number): string {
-    const percentage = (value / target) * 100;
-    if (percentage >= 90) return 'primary';
-    if (percentage >= 70) return 'accent';
-    return 'warn';
+
+  // 拖拽排序
+  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;
+  }
+
+  // 获取甜甜圈图表特定选项
+  private getDoughnutOptions(): any {
+    return {
+      cutout: '60%'
+    };
+  }
+
+  // 初始化所有图表
+  private initializeCharts() {
+    this.initPieChart();
+    this.initLineChart();
+    this.initRadarChart();
+  }
+
+  // 初始化职级分布饼图
+  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 'priority-high';
-      case 'medium': return 'priority-medium';
-      case 'low': return 'priority-low';
-      default: return '';
+      case 'high': return '紧急';
+      case 'medium': return '重要';
+      case 'low': return '一般';
+      default: return '未知';
     }
   }
-  
-  // ====== 图表相关 ======
-  private initCharts(): void {
-    this.initMovementChart();
-    this.initPerformanceDistributionChart();
-    this.initTrendChart();
+
+  getTypeLabel(type: string): string {
+    switch (type) {
+      case 'resume': return '简历初筛';
+      case 'onboarding': return '入职评定';
+      case 'promotion': return '晋升审核';
+      case 'resignation': return '离职面谈';
+      default: return '其他';
+    }
   }
-  
-  private disposeCharts(): void {
-    if (this.movementChart) { this.movementChart.dispose(); this.movementChart = null; }
-    if (this.performanceChart) { this.performanceChart.dispose(); this.performanceChart = null; }
-    if (this.trendChart) { this.trendChart.dispose(); this.trendChart = null; }
+
+  getStatusLabel(status: string): string {
+    const statusMap: { [key: string]: string } = {
+      'pending': '待处理',
+      'in_progress': '进行中',
+      'completed': '已完成',
+      'urgent': '紧急',
+      'normal': '普通'
+    };
+    return statusMap[status] || status;
   }
-  
-  private initMovementChart(): void {
-    const el = document.getElementById('hrMovementChart');
-    if (!el) return;
-    this.movementChart?.dispose();
-    // 使用全局 echarts 变量
-    // @ts-ignore
-    this.movementChart = echarts.init(el);
-  
-    const data = this.employeeMovements();
-    const months = data.map(d => d.month);
-    const hired = data.map(d => d.hired);
-    const resigned = data.map(d => d.resigned);
-  
-    this.movementChart.setOption({
-      title: { text: '月度入职/离职', left: 'center', textStyle: { fontSize: 16 } },
-      tooltip: { trigger: 'axis' },
-      legend: { data: ['入职', '离职'] },
-      grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
-      xAxis: { type: 'category', data: months },
-      yAxis: { type: 'value' },
-      series: [
-        { name: '入职', type: 'bar', data: hired, itemStyle: { color: '#165DFF' }, barGap: 0 },
-        { name: '离职', type: 'bar', data: resigned, itemStyle: { color: '#F53F3F' } }
-      ]
-    });
+
+  // 拖拽排序功能
+  onTodoDrop(event: CdkDragDrop<TodoItem[]>): void {
+    moveItemInArray(this.todoItems, event.previousIndex, event.currentIndex);
   }
-  
-  private initPerformanceDistributionChart(): void {
-    const el = document.getElementById('hrPerformanceDistributionChart');
-    if (!el) return;
-    this.performanceChart?.dispose();
-    // @ts-ignore
-    this.performanceChart = echarts.init(el);
-  
-    const tiers = this.performanceTiers();
-    const data = tiers.map(t => ({ value: t.count, name: `${t.name}(${t.count}人)` }));
-  
-    this.performanceChart.setOption({
-      title: { text: '绩效等级分布', left: 'center', textStyle: { fontSize: 16 } },
-      tooltip: { trigger: 'item', formatter: '{b}: {c} 人 ({d}%)' },
-      legend: { orient: 'horizontal', bottom: 0 },
-      series: [{
-        type: 'pie', radius: '65%', center: ['50%', '50%'],
-        data,
-        emphasis: { itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0,0,0,0.3)' } }
-      }]
-    });
+
+  // 更新待办事项状态
+  updateTodoStatus(todo: TodoItem, status: 'pending' | 'completed' | 'in_progress'): void {
+    todo.status = status;
   }
-  
-  private initTrendChart(): void {
-    const el = document.getElementById('hrTrendChart');
-    if (!el) return;
-    this.trendChart?.dispose();
-    // @ts-ignore
-    this.trendChart = echarts.init(el);
-  
-    const trend = this.movementTrends();
-    const months = trend.map(t => t.month);
-    const hired = trend.map(t => t.hired);
-    const resigned = trend.map(t => t.resigned);
-  
-    this.trendChart.setOption({
-      title: { text: '异动趋势', left: 'center', textStyle: { fontSize: 16 } },
-      tooltip: { trigger: 'axis' },
-      legend: { data: ['入职', '离职'] },
-      xAxis: { type: 'category', data: months },
-      yAxis: { type: 'value' },
-      series: [
-        { name: '入职', type: 'line', data: hired, smooth: true, lineStyle: { color: '#00B42A' }, itemStyle: { color: '#00B42A' } },
-        { name: '离职', type: 'line', data: resigned, smooth: true, lineStyle: { color: '#F53F3F' }, itemStyle: { color: '#F53F3F' } }
-      ]
-    });
+
+  // 处理按钮按压效果
+  isButtonPressed = false;
+  handleButtonPress(action: 'press' | 'release') {
+    this.isButtonPressed = action === 'press';
   }
-  
-  private handleResize = (): void => {
-    this.movementChart?.resize();
-    this.performanceChart?.resize();
-    this.trendChart?.resize();
-  };
 }