徐福静0235668 1 dzień temu
rodzic
commit
1f162646e2

+ 2 - 0
src/app/app.routes.ts

@@ -39,6 +39,7 @@ import { HrLayout } from './pages/hr/hr-layout/hr-layout';
 import { Dashboard as HrDashboard } from './pages/hr/dashboard/dashboard';
 import { EmployeeRecords } from './pages/hr/employee-records/employee-records';
 import { Attendance } from './pages/hr/attendance/attendance';
+import { EmployeeDetailComponent } from './pages/hr/employee-detail/employee-detail';
 
 import { DesignerProfile } from './pages/hr/designer-profile/designer-profile';
 
@@ -120,6 +121,7 @@ export const routes: Routes = [
       { path: '', redirectTo: 'dashboard', pathMatch: 'full' },
       { path: 'dashboard', component: HrDashboard, title: '人事看板' },
       { path: 'employee-records', component: EmployeeRecords, title: '花名册与档案库' },
+      { path: 'employee-detail/:id', component: EmployeeDetailComponent, title: '员工详情' },
       { path: 'attendance', component: Attendance, title: '考勤统计' },
 
       { path: 'designer-profile/:id', component: DesignerProfile, title: '设计师详情' }

+ 10 - 0
src/app/models/hr.model.ts

@@ -19,6 +19,16 @@ export interface Employee {
   avatar?: string;
   contract?: Contract;
   certificates?: Certificate[];
+  // 详情页面新增字段
+  education?: string; // 学历
+  workYears?: string; // 工作年限
+  idCard?: string; // 身份证号
+  bankCard?: string; // 银行卡号
+  address?: string; // 地址
+  supervisor?: string; // 直属上级
+  screeningStatus?: 'approved' | 'rejected' | 'pending'; // 初筛状态
+  screeningComment?: string; // 初筛备注
+  screeningTime?: Date; // 初筛时间
 }
 
 // 合同信息

+ 313 - 24
src/app/pages/hr/attendance/attendance.scss

@@ -666,46 +666,335 @@ $transition: all 0.2s ease;
     align-items: stretch;
     gap: 12px;
 
-    .date-navigation,
-    .view-tabs,
-    .action-buttons {
+    .date-navigation, .view-tabs, .action-buttons {
       justify-content: center;
     }
   }
 
   .stats-cards {
-    grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
-    gap: 12px;
+    grid-template-columns: repeat(2, 1fr);
+  }
+  
+  .calendar-section {
+    position: static;
   }
+}
 
-  .stat-card {
-    padding: 16px;
+/* 修复Material图标字体和奇怪字符问题 */
+mat-icon {
+  font-family: 'Material Icons' !important;
+  font-feature-settings: 'liga' 1;
+  -webkit-font-feature-settings: 'liga';
+  -moz-font-feature-settings: 'liga';
+  font-variant-ligatures: common-ligatures;
+  text-rendering: optimizeLegibility;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+  font-size: inherit;
+  line-height: inherit;
+  vertical-align: middle;
+  
+  &::before {
+    content: none !important;
+  }
+}
 
-    .stat-value {
-      font-size: 24px;
-    }
+/* 统一按钮图标与文字对齐 */
+.export-btn, .fix-btn, .nav-btn {
+  mat-icon {
+    font-size: 18px !important;
+    width: 18px !important;
+    height: 18px !important;
+    line-height: 18px !important;
+    vertical-align: middle;
+    margin-right: 4px;
+  }
+}
 
-    .stat-label {
-      font-size: 12px;
+/* 日历图标特殊处理 */
+.calendar-icon {
+  font-size: 16px !important;
+  width: 16px !important;
+  height: 16px !important;
+  line-height: 16px !important;
+}
+
+/* 卡片美化 */
+.calendar-section, .stats-section {
+  background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
+  border: 1px solid rgba(226, 232, 240, 0.8);
+  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+  transition: all 0.3s ease;
+  
+  &:hover {
+    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+    transform: translateY(-2px);
+  }
+}
+
+/* 统计卡片优化 */
+.stat-card {
+  background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
+  border: 1px solid rgba(226, 232, 240, 0.8);
+  border-radius: 12px;
+  padding: 20px;
+  text-align: center;
+  transition: all 0.3s ease;
+  position: relative;
+  overflow: hidden;
+  
+  &::before {
+    content: '';
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    height: 4px;
+    background: linear-gradient(90deg, $primary-color, $secondary-color);
+  }
+  
+  &:hover {
+    transform: translateY(-4px);
+    box-shadow: 0 12px 20px -5px rgba(0, 0, 0, 0.15);
+  }
+  
+  &.warning::before {
+    background: linear-gradient(90deg, $warning-color, #fbbf24);
+  }
+  
+  &.danger::before {
+    background: linear-gradient(90deg, $error-color, #f87171);
+  }
+  
+  &.info::before {
+    background: linear-gradient(90deg, $info-color, #60a5fa);
+  }
+  
+  &.primary::before {
+    background: linear-gradient(90deg, $primary-color, $primary-light);
+  }
+  
+  .stat-value {
+    font-size: 28px;
+    font-weight: 700;
+    margin-bottom: 8px;
+    background: linear-gradient(135deg, $text-primary, $text-secondary);
+    -webkit-background-clip: text;
+    -webkit-text-fill-color: transparent;
+    background-clip: text;
+  }
+  
+  .stat-label {
+    font-size: 14px;
+    color: $text-secondary;
+    font-weight: 500;
+    margin-bottom: 4px;
+  }
+  
+  .stat-rate, .stat-sub {
+    font-size: 12px;
+    color: $text-tertiary;
+    font-weight: 400;
+  }
+}
+
+/* 表格优化 */
+.exception-table {
+  width: 100%;
+  background: white;
+  border-radius: 8px;
+  overflow: hidden;
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+  
+  .mat-header-cell {
+    background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
+    color: $text-primary;
+    font-weight: 600;
+    font-size: 14px;
+    border-bottom: 2px solid $border-color;
+    padding: 16px 12px;
+  }
+  
+  .mat-cell {
+    padding: 16px 12px;
+    font-size: 14px;
+    border-bottom: 1px solid rgba(226, 232, 240, 0.5);
+    
+    &:last-child {
+      text-align: center;
+    }
+  }
+  
+  .mat-row {
+    transition: all 0.2s ease;
+    
+    &:hover {
+      background-color: rgba(59, 130, 246, 0.05);
     }
   }
+  
+  .no-data {
+    text-align: center;
+    color: $text-tertiary;
+    font-style: italic;
+    padding: 40px 20px;
+  }
+}
 
-  .department-comparison {
-    .dept-bar-container {
-      flex-direction: column;
-      align-items: flex-start;
-      gap: 8px;
+/* 状态徽章优化 */
+.status-badge {
+  padding: 4px 12px;
+  border-radius: 20px;
+  font-size: 12px;
+  font-weight: 500;
+  text-align: center;
+  
+  &.normal {
+    background: linear-gradient(135deg, rgba(16, 185, 129, 0.1), rgba(16, 185, 129, 0.2));
+    color: $success-color;
+    border: 1px solid rgba(16, 185, 129, 0.3);
+  }
+  
+  &.late, &.early {
+    background: linear-gradient(135deg, rgba(245, 158, 11, 0.1), rgba(245, 158, 11, 0.2));
+    color: $warning-color;
+    border: 1px solid rgba(245, 158, 11, 0.3);
+  }
+  
+  &.absent {
+    background: linear-gradient(135deg, rgba(239, 68, 68, 0.1), rgba(239, 68, 68, 0.2));
+    color: $error-color;
+    border: 1px solid rgba(239, 68, 68, 0.3);
+  }
+  
+  &.leave {
+    background: linear-gradient(135deg, rgba(59, 130, 246, 0.1), rgba(59, 130, 246, 0.2));
+    color: $info-color;
+    border: 1px solid rgba(59, 130, 246, 0.3);
+  }
+}
+
+/* 部门对比图表优化 */
+.department-comparison {
+  margin-top: 24px;
+  
+  .dept-bar-container {
+    margin-bottom: 16px;
+    padding: 16px;
+    background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
+    border-radius: 8px;
+    border: 1px solid rgba(226, 232, 240, 0.8);
+    transition: all 0.3s ease;
+    
+    &:hover {
+      transform: translateX(4px);
+      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+    }
+  }
+  
+  .dept-info {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 8px;
+    
+    .dept-name {
+      font-weight: 600;
+      color: $text-primary;
+    }
+    
+    .dept-rate {
+      font-weight: 700;
+      color: $primary-color;
+    }
+  }
+  
+  .progress-bar {
+    height: 8px;
+    background-color: rgba(226, 232, 240, 0.5);
+    border-radius: 4px;
+    overflow: hidden;
+    margin-bottom: 8px;
+    
+    .progress-fill {
+      height: 100%;
+      background: linear-gradient(90deg, $primary-color, $secondary-color);
+      border-radius: 4px;
+      transition: width 0.6s ease;
     }
+  }
+  
+  .dept-stats {
+    font-size: 12px;
+    color: $text-tertiary;
+  }
+}
 
-    .dept-info {
-      flex-direction: row;
-      gap: 16px;
-      min-width: auto;
+/* 日历优化 */
+.calendar-day {
+  &:hover {
+    transform: scale(1.05);
+    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+    z-index: 1;
+  }
+  
+  &.today {
+    border: 2px solid $primary-color;
+    box-shadow: 0 0 0 4px rgba(30, 64, 175, 0.1);
+  }
+}
+
+/* 图例优化 */
+.calendar-legend {
+  .legend-item {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+    padding: 8px 12px;
+    border-radius: 6px;
+    transition: all 0.2s ease;
+    
+    &:hover {
+      background-color: rgba(59, 130, 246, 0.05);
+    }
+    
+    .legend-dot {
+      width: 12px;
+      height: 12px;
+      border-radius: 50%;
+      box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
     }
+    
+    span {
+      font-size: 13px;
+      font-weight: 500;
+      color: $text-secondary;
+    }
+  }
+}
 
-    .dept-stats {
-      text-align: left;
-      min-width: auto;
+/* 响应式优化 */
+@media (min-width: 1024px) {
+  .main-content {
+    grid-template-columns: 400px 1fr;
+  }
+}
+
+@media (max-width: 768px) {
+  .time-dimension-bar {
+    flex-direction: column;
+    align-items: stretch;
+    
+    .date-navigation, .view-tabs, .action-buttons {
+      justify-content: center;
     }
   }
+  
+  .stats-cards {
+    grid-template-columns: repeat(2, 1fr);
+  }
+  
+  .calendar-section {
+    position: static;
+  }
 }

+ 265 - 0
src/app/pages/hr/dashboard/dashboard.scss

@@ -701,4 +701,269 @@
       }
     }
   }
+}
+
+/* 修复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 {
+      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;
+          font-weight: 600;
+          color: #165DFF;
+        }
+        
+        .metric-target {
+          font-size: 12px;
+          color: #86909c;
+        }
+      }
+    }
+  }
+}
+
+/* 优化警告项样式 */
+.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;
+      }
+    }
+  }
+}
+
+/* 优化结构分析 */
+.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;
+      }
+      
+      .structure-count {
+        font-weight: 600;
+        color: #165DFF;
+      }
+    }
+    
+    .structure-percentage {
+      font-size: 12px;
+      color: #86909c;
+      margin-top: 4px;
+      display: block;
+    }
+  }
+}
+
+/* 响应式优化 */
+@media (max-width: 768px) {
+  .dashboard-content {
+    grid-template-columns: 1fr;
+  }
+  
+  .metrics-container {
+    grid-template-columns: 1fr;
+  }
+  
+  .department-table {
+    font-size: 14px;
+    
+    .table-cell {
+      padding: 8px 12px;
+    }
+  }
 }

+ 121 - 3
src/app/pages/hr/designer-profile/designer-profile.html

@@ -7,6 +7,11 @@
       <div class="info-section">
         <h1>{{ d.name }} <span class="position-badge">{{ d.position }}</span></h1>
         <div class="basic-info">
+          <!-- 头部信息:补充联系电话 -->
+          <div class="info-item">
+            <mat-icon>phone</mat-icon>
+            <span>{{ maskPhone(d.phone) }}</span>
+          </div>
           <div class="info-item">
             <mat-icon>business</mat-icon>
             <span>{{ d.department }}</span>
@@ -44,19 +49,122 @@
               <div class="section-title">技术技能</div>
               <div class="skill-chips">
                 <mat-chip-set>
-                  <mat-chip *ngFor="let skill of d.skills">{{ skill }}</mat-chip>
+                  <!-- 技能与风格:改用 @for -->
+                  @for (skill of d.skills; track skill) {
+                    <mat-chip>{{ skill }}</mat-chip>
+                  }
                 </mat-chip-set>
               </div>
               
               <div class="section-title">擅长风格</div>
               <div class="style-chips">
                 <mat-chip-set>
-                  <mat-chip *ngFor="let style of d.styles" color="accent" selected>{{ style }}</mat-chip>
+                  <!-- 技能与风格:改用 @for -->
+                  @for (style of d.styles; track style) {
+                    <mat-chip color="accent" selected>{{ style }}</mat-chip>
+                  }
                 </mat-chip-set>
               </div>
             </mat-card-content>
           </mat-card>
 
+          <!-- 简历初筛卡片 -->
+          <mat-card class="info-card screening-card">
+            <mat-card-header>
+              <mat-card-title>简历初筛</mat-card-title>
+              <div class="spacer"></div>
+              @if (!editingScreening()) {
+                <button mat-stroked-button color="primary" (click)="startEditScreening()">
+                  <mat-icon>edit</mat-icon>
+                  编辑
+                </button>
+              } @else {
+                <button mat-stroked-button color="primary" (click)="saveScreening()">
+                  <mat-icon>save</mat-icon>
+                  保存
+                </button>
+                <button mat-button color="basic" (click)="cancelEditScreening()">取消</button>
+              }
+            </mat-card-header>
+            <mat-card-content>
+              <div class="resume-section">
+                <div class="section-title">基本信息</div>
+                <div class="kv-list">
+                  <div class="kv-item"><span class="k">姓名</span><span class="v">{{ d.name }}</span></div>
+                  <div class="kv-item"><span class="k">年龄</span><span class="v">{{ d.screening?.basic?.age || '-' }}</span></div>
+                  <div class="kv-item"><span class="k">学历</span><span class="v">{{ d.screening?.basic?.education || '-' }}</span></div>
+                  <div class="kv-item"><span class="k">工作年限</span><span class="v">{{ d.screening?.basic?.years || '-' }}</span></div>
+                  <div class="kv-item"><span class="k">求职岗位</span><span class="v">{{ d.screening?.basic?.targetRole || d.position }}</span></div>
+                </div>
+
+                <div class="section-title mt">核心能力</div>
+                <div class="kv-list">
+                  <div class="kv-item full">
+                    <span class="k">技能匹配度</span>
+                    <span class="v">
+                      @if (d.screening?.core?.skills?.length) {
+                        @for (s of d.screening?.core?.skills || []; track s) {
+                          <mat-chip color="primary" selected>{{ s }}</mat-chip>
+                        }
+                      } @else {
+                        暂无
+                      }
+                    </span>
+                  </div>
+                  <div class="kv-item"><span class="k">相关项目经验</span><span class="v">{{ d.screening?.core?.projects || '-' }}</span></div>
+                  <div class="kv-item"><span class="k">证书资质</span><span class="v">{{ d.screening?.core?.certs || '-' }}</span></div>
+                </div>
+
+                <div class="section-title mt">求职意向</div>
+                <div class="kv-list">
+                  <div class="kv-item"><span class="k">期望薪资</span><span class="v">{{ d.screening?.intent?.salary || '-' }}</span></div>
+                  <div class="kv-item"><span class="k">到岗时间</span><span class="v">{{ d.screening?.intent?.onboard || '-' }}</span></div>
+                  <div class="kv-item"><span class="k">工作地点</span><span class="v">{{ d.screening?.intent?.location || '-' }}</span></div>
+                </div>
+
+                <div class="section-title mt">初筛结果</div>
+
+                @if (!editingScreening()) {
+                  <div class="result-row">
+                    <div class="result-status" [ngClass]="getScreeningStatusClass(d.screening?.result?.status)">
+                      <mat-icon>{{ getScreeningStatusIcon(d.screening?.result?.status) }}</mat-icon>
+                      <span>{{ getScreeningStatusText(d.screening?.result?.status) }}</span>
+                    </div>
+                    @if (d.screening?.result?.comment) {
+                      <div class="result-comment">{{ d.screening?.result?.comment }}</div>
+                    }
+                  </div>
+                } @else {
+                  <form [formGroup]="screeningForm" class="screening-form">
+                    <div class="form-row">
+                      <mat-button-toggle-group formControlName="status" class="status-toggle">
+                        <mat-button-toggle value="pass">通过</mat-button-toggle>
+                        <mat-button-toggle value="reject">不通过</mat-button-toggle>
+                        <mat-button-toggle value="pending">待定</mat-button-toggle>
+                      </mat-button-toggle-group>
+                    </div>
+                    
+                    @if (screeningForm.get('status')?.value === 'reject') {
+                      <div class="form-row">
+                        <mat-form-field appearance="outline" class="full-width">
+                          <mat-label>不通过原因</mat-label>
+                          <input matInput formControlName="reason" placeholder="请输入不通过的具体原因">
+                        </mat-form-field>
+                      </div>
+                    }
+                    
+                    <div class="form-row">
+                      <mat-form-field appearance="outline" class="full-width">
+                        <mat-label>备注说明</mat-label>
+                        <textarea matInput formControlName="comment" rows="3" placeholder="请输入初筛备注或建议"></textarea>
+                      </mat-form-field>
+                    </div>
+                  </form>
+                }
+              </div>
+            </mat-card-content>
+          </mat-card>
+
           <!-- 资质证书卡片 -->
           <mat-card class="info-card certificates-card">
             <mat-card-header>
@@ -101,7 +209,17 @@
                   <div class="section-title">可接单日期</div>
                   <div class="date-chips">
                     <mat-chip-set>
-                      <mat-chip *ngFor="let date of d.availableDates || []" color="primary" selected>{{ date }}</mat-chip>
+                      <!-- 空档期:改用 @if/@for -->
+                      <div class="available-dates">
+                        <div class="section-title">可接单日期</div>
+                        <div class="date-chips">
+                          <mat-chip-set>
+                            @for (date of d.availableDates || []; track date) {
+                              <mat-chip color="primary" selected>{{ date }}</mat-chip>
+                            }
+                          </mat-chip-set>
+                        </div>
+                      </div>
                     </mat-chip-set>
                   </div>
                 </div>

+ 304 - 0
src/app/pages/hr/designer-profile/designer-profile.scss

@@ -729,4 +729,308 @@ button.mat-mdc-button, button.mat-mdc-stroked-button, button.mat-mdc-raised-butt
       grid-template-columns: 1fr;
     }
   }
+}
+
+// 简历初筛卡片样式
+.screening-card {
+  .card-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 16px;
+
+    h3 {
+      margin: 0;
+      font-size: 16px;
+      font-weight: 500;
+      color: #1a3a6e;
+    }
+
+    .edit-actions {
+      display: flex;
+      gap: 8px;
+
+      .mat-mdc-button {
+        min-width: auto;
+        padding: 0 12px;
+        height: 32px;
+        font-size: 13px;
+      }
+    }
+  }
+
+  .screening-content {
+    .info-section {
+      margin-bottom: 20px;
+
+      .section-title {
+        font-size: 14px;
+        font-weight: 500;
+        color: #1a3a6e;
+        margin-bottom: 8px;
+        display: flex;
+        align-items: center;
+        gap: 6px;
+
+        mat-icon {
+          font-size: 16px;
+          width: 16px;
+          height: 16px;
+          color: #666;
+        }
+      }
+
+      .info-content {
+        padding-left: 22px;
+        color: #666;
+        font-size: 13px;
+        line-height: 1.5;
+
+        .info-item {
+          margin-bottom: 4px;
+
+          .label {
+            font-weight: 500;
+            color: #333;
+            margin-right: 8px;
+          }
+        }
+
+        .skills-list {
+          display: flex;
+          flex-wrap: wrap;
+          gap: 6px;
+          margin-top: 4px;
+
+          .skill-chip {
+            background-color: #e6f7ff;
+            color: #1a3a6e;
+            padding: 2px 8px;
+            border-radius: 12px;
+            font-size: 12px;
+          }
+        }
+      }
+    }
+
+    .result-section {
+      border-top: 1px solid #f0f0f0;
+      padding-top: 16px;
+
+      .status-display {
+        display: flex;
+        align-items: center;
+        gap: 8px;
+        margin-bottom: 8px;
+
+        .status-icon {
+          font-size: 18px;
+          width: 18px;
+          height: 18px;
+
+          &.status-pass {
+            color: #52c41a;
+          }
+
+          &.status-reject {
+            color: #ff4d4f;
+          }
+
+          &.status-pending {
+            color: #faad14;
+          }
+        }
+
+        .status-text {
+          font-weight: 500;
+          font-size: 14px;
+
+          &.status-pass {
+            color: #52c41a;
+          }
+
+          &.status-reject {
+            color: #ff4d4f;
+          }
+
+          &.status-pending {
+            color: #faad14;
+          }
+        }
+      }
+
+      .comment-text {
+        color: #666;
+        font-size: 13px;
+        line-height: 1.5;
+        margin-top: 8px;
+        padding: 8px 12px;
+        background-color: #f8f9fa;
+        border-radius: 6px;
+        border-left: 3px solid #e6f7ff;
+      }
+    }
+  }
+
+  .screening-form {
+    .form-row {
+      display: flex;
+      gap: 16px;
+      margin-bottom: 16px;
+
+      .form-field {
+        flex: 1;
+
+        .mat-mdc-form-field {
+          width: 100%;
+        }
+      }
+    }
+
+    .status-toggle {
+      margin-bottom: 16px;
+
+      .mat-button-toggle-group {
+        border: 1px solid #d9d9d9;
+        border-radius: 6px;
+        overflow: hidden;
+
+        .mat-button-toggle {
+          border: none;
+          font-size: 13px;
+          padding: 0 16px;
+          height: 36px;
+
+          &.mat-button-toggle-checked {
+            background-color: #1a3a6e;
+            color: white;
+
+            &:hover {
+              background-color: #1a3a6e;
+            }
+          }
+
+          &:not(.mat-button-toggle-checked) {
+            background-color: white;
+            color: #666;
+
+            &:hover {
+              background-color: #f5f5f5;
+            }
+          }
+        }
+      }
+    }
+
+    .form-actions {
+      display: flex;
+      justify-content: flex-end;
+      gap: 8px;
+      margin-top: 16px;
+      padding-top: 16px;
+      border-top: 1px solid #f0f0f0;
+
+      .mat-mdc-button {
+        min-width: 80px;
+        height: 36px;
+        font-size: 13px;
+      }
+    }
+  }
+}
+
+// 状态样式
+.status-pass {
+  color: #52c41a !important;
+}
+
+.status-reject {
+  color: #ff4d4f !important;
+}
+
+.status-pending {
+  color: #faad14 !important;
+}
+
+/* 修复Material图标字体和奇怪字符问题 */
+mat-icon {
+  font-family: 'Material Icons' !important;
+  font-feature-settings: 'liga' 1;
+  -webkit-font-feature-settings: 'liga';
+  -moz-font-feature-settings: 'liga';
+  font-variant-ligatures: common-ligatures;
+  text-rendering: optimizeLegibility;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+  font-size: inherit;
+  line-height: inherit;
+  vertical-align: middle;
+  
+  &::before {
+    content: none !important;
+  }
+}
+
+/* 统一按钮图标与文字对齐 */
+button mat-icon {
+  font-size: 18px !important;
+  width: 18px !important;
+  height: 18px !important;
+  line-height: 18px !important;
+  vertical-align: middle;
+}
+
+/* 信息项图标特殊处理 */
+.info-item mat-icon {
+  font-size: 18px !important;
+  width: 18px !important;
+  height: 18px !important;
+  line-height: 18px !important;
+  margin-right: 6px;
+  color: #5a6a85;
+}
+
+/* 趋势图标优化 */
+.trend-icon {
+  font-size: 16px !important;
+  width: 16px !important;
+  height: 16px !important;
+  line-height: 16px !important;
+  
+  &.trending_up {
+    color: #4caf50;
+  }
+  
+  &.trending_down {
+    color: #f44336;
+  }
+  
+  &.trending_flat {
+    color: #ff9800;
+  }
+}
+
+/* 评级星星图标优化 */
+.rating-stars mat-icon {
+  font-size: 16px !important;
+  width: 16px !important;
+  height: 16px !important;
+  line-height: 16px !important;
+  color: #ffc107;
+}
+
+/* 表格操作按钮图标优化 */
+.mat-table mat-icon {
+  font-size: 16px !important;
+  width: 16px !important;
+  height: 16px !important;
+  line-height: 16px !important;
+}
+
+/* 卡片头像图标优化 */
+mat-card-avatar mat-icon {
+  font-size: 24px !important;
+  width: 24px !important;
+  height: 24px !important;
+  line-height: 24px !important;
 }

+ 159 - 6
src/app/pages/hr/designer-profile/designer-profile.ts

@@ -13,7 +13,10 @@ import { MatBadgeModule } from '@angular/material/badge';
 import { MatDialogModule, MatDialog } from '@angular/material/dialog';
 import { MatTooltipModule } from '@angular/material/tooltip';
 import { ActivatedRoute } from '@angular/router';
-import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { FormsModule, ReactiveFormsModule, FormGroup, FormControl } from '@angular/forms';
+import { MatFormFieldModule } from '@angular/material/form-field';
+import { MatInputModule } from '@angular/material/input';
+import { MatButtonToggleModule } from '@angular/material/button-toggle';
 
 // 模拟数据接口
 interface Designer {
@@ -24,6 +27,9 @@ interface Designer {
   level: string;
   department: string;
   joinDate: string;
+  phone?: string;
+  idNumber?: string;
+  bankCard?: string;
   skills: string[];
   styles: string[];
   availableDates: string[];
@@ -54,6 +60,30 @@ interface Designer {
     }[];
     finalGrade: string;
   }[];
+  // 新增:简历初筛结构
+  screening?: {
+    basic?: {
+      age?: number;
+      education?: string;
+      years?: string | number;
+      targetRole?: string;
+    };
+    core?: {
+      skills?: string[];
+      projects?: string;
+      certs?: string;
+    };
+    intent?: {
+      salary?: string;
+      onboard?: string;
+      location?: string;
+    };
+    result?: {
+      status: 'pass' | 'reject' | 'pending';
+      reason?: string;
+      comment?: string;
+    };
+  };
 }
 
 // 作品集预览对话框组件
@@ -207,7 +237,10 @@ export class PortfolioPreviewDialog {
     MatDialogModule,
     MatTooltipModule,
     FormsModule,
-    ReactiveFormsModule
+    ReactiveFormsModule,
+    MatFormFieldModule,
+    MatInputModule,
+    MatButtonToggleModule
   ],
   templateUrl: './designer-profile.html',
   styleUrls: ['./designer-profile.scss']
@@ -221,6 +254,14 @@ export class DesignerProfile implements OnInit {
   projectsLoading = signal(true);
   performanceLoading = signal(true);
   
+  // 编辑状态与表单
+  editingScreening = signal(false);
+  screeningForm = new FormGroup({
+    status: new FormControl<'pass' | 'reject' | 'pending'>('pending', { nonNullable: true }),
+    reason: new FormControl<string>(''),
+    comment: new FormControl<string>('')
+  });
+
   private route = inject(ActivatedRoute);
   private dialog = inject(MatDialog);
 
@@ -260,6 +301,9 @@ export class DesignerProfile implements OnInit {
         level: '高级',
         department: '设计部-效果图组',
         joinDate: '2020-05-15',
+        phone: '13800138006',
+        idNumber: '110101199001011234',
+        bankCard: '6222021234567890123',
         skills: ['3D建模', '渲染', '后期处理', 'CAD制图'],
         styles: ['现代简约', '北欧风格', '新中式'],
         availableDates: ['2023-06-15', '2023-06-16', '2023-06-17'],
@@ -328,19 +372,53 @@ export class DesignerProfile implements OnInit {
             ],
             finalGrade: 'B+'
           }
-        ]
+        ],
+        // 新增:简历初筛模拟数据(英文枚举)
+        screening: {
+          basic: { age: 30, education: '本科', years: '7年', targetRole: '高级效果图设计师' },
+          core: {
+            skills: ['3D建模', '渲染', '后期处理'],
+            projects: '住宅/商业空间等10+项目,擅长现代与新中式风格',
+            certs: '3D效果图高级设计师认证'
+          },
+          intent: { salary: '18k-22k', onboard: '1周内', location: '杭州/远程' },
+          result: { status: 'pass', comment: '技能匹配度高,作品质量稳定,可进入面试环节' }
+        }
+      });
+
+      // 预填编辑表单
+      const scr = this.designer()?.screening?.result;
+      this.screeningForm.patchValue({
+        status: scr?.status ?? 'pending',
+        reason: scr?.reason ?? '',
+        comment: scr?.comment ?? ''
       });
-    }, 500);
+
+      this.isLoading.set(false);
+    }, 800);
   }
 
   openPortfolioPreview() {
     this.dialog.open(PortfolioPreviewDialog, {
-      width: '80%',
-      maxWidth: '1200px',
+      width: '960px',
       panelClass: 'portfolio-dialog-container'
     });
   }
 
+  // 隐私字段脱敏
+  maskIdNumber(id?: string): string {
+    if (!id) return '-';
+    return id.replace(/(\d{4})\d{10}(\d{4})/, '$1**********$2');
+  }
+  maskBankCard(card?: string): string {
+    if (!card) return '-';
+    return card.replace(/(\d{4})\d+(\d{4})/, '$1 **** **** **** $2');
+  }
+  maskPhone(phone?: string): string {
+    if (!phone) return '-';
+    return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
+  }
+
   getPerformanceTrend(metric: 'customerSatisfaction' | 'excellentWorkRate' | 'performanceValue'): string {
     if (!this.designer() || this.designer()!.performance.length < 2) {
       return 'stable';
@@ -398,4 +476,79 @@ export class DesignerProfile implements OnInit {
       ...Array(emptyStars).fill('star_border')
     ];
   }
+
+  // 简历初筛相关方法
+  startEditScreening() {
+    this.editingScreening.set(true);
+    const scr = this.designer()?.screening?.result;
+    this.screeningForm.patchValue({
+      status: scr?.status ?? 'pending',
+      reason: scr?.reason ?? '',
+      comment: scr?.comment ?? ''
+    });
+  }
+
+  saveScreening() {
+    const formValue = this.screeningForm.value;
+    const currentDesigner = this.designer();
+    
+    if (currentDesigner) {
+      // 更新设计师数据
+      const updatedDesigner = {
+        ...currentDesigner,
+        screening: {
+          ...currentDesigner.screening,
+          result: {
+            status: formValue.status as 'pass' | 'reject' | 'pending',
+            reason: formValue.reason || '',
+            comment: formValue.comment || ''
+          }
+        }
+      };
+      
+      this.designer.set(updatedDesigner);
+      this.editingScreening.set(false);
+      
+      // 这里可以调用API保存数据
+      console.log('保存简历初筛结果:', formValue);
+    }
+  }
+
+  cancelEditScreening() {
+    this.editingScreening.set(false);
+    // 重置表单到原始值
+    const scr = this.designer()?.screening?.result;
+    this.screeningForm.patchValue({
+      status: scr?.status ?? 'pending',
+      reason: scr?.reason ?? '',
+      comment: scr?.comment ?? ''
+    });
+  }
+
+  getScreeningStatusClass(status?: 'pass' | 'reject' | 'pending'): string {
+    switch (status) {
+      case 'pass': return 'status-pass';
+      case 'reject': return 'status-reject';
+      case 'pending': return 'status-pending';
+      default: return 'status-pending';
+    }
+  }
+
+  getScreeningStatusIcon(status?: 'pass' | 'reject' | 'pending'): string {
+    switch (status) {
+      case 'pass': return 'check_circle';
+      case 'reject': return 'cancel';
+      case 'pending': return 'schedule';
+      default: return 'schedule';
+    }
+  }
+
+  getScreeningStatusText(status?: 'pass' | 'reject' | 'pending'): string {
+    switch (status) {
+      case 'pass': return '通过';
+      case 'reject': return '不通过';
+      case 'pending': return '待定';
+      default: return '待定';
+    }
+  }
 }

+ 262 - 0
src/app/pages/hr/employee-detail/employee-detail.html

@@ -0,0 +1,262 @@
+<div class="employee-detail-container" *ngIf="employee() as emp">
+  <!-- 页面头部 -->
+  <div class="detail-header">
+    <button mat-icon-button (click)="goBack()" class="back-button">
+      <mat-icon>arrow_back_ios</mat-icon>
+    </button>
+    <h1>员工详情</h1>
+    <div class="header-actions">
+      <button mat-icon-button (click)="shareEmployee()" class="action-button">
+        <mat-icon>share</mat-icon>
+      </button>
+      <button mat-icon-button [matMenuTriggerFor]="headerMenu" class="action-button">
+        <mat-icon>more_horiz</mat-icon>
+      </button>
+      <mat-menu #headerMenu="matMenu" class="ios-action-menu">
+        <button mat-menu-item (click)="editEmployee()" class="ios-menu-item">
+          <mat-icon>edit</mat-icon>
+          <span>编辑信息</span>
+        </button>
+        <button mat-menu-item (click)="exportEmployee()" class="ios-menu-item">
+          <mat-icon>file_download</mat-icon>
+          <span>导出档案</span>
+        </button>
+        <mat-divider></mat-divider>
+        <button mat-menu-item (click)="deleteEmployee()" class="ios-menu-item danger">
+          <mat-icon>delete</mat-icon>
+          <span>删除员工</span>
+        </button>
+      </mat-menu>
+    </div>
+  </div>
+
+  <!-- 员工头像和基本信息卡片 -->
+  <div class="profile-card">
+    <div class="avatar-section">
+      <div class="avatar-container">
+        <img [src]="emp.avatar || 'assets/images/default-avatar.svg'" alt="员工头像" class="avatar">
+        <div class="status-indicator" [ngClass]="getStatusClass(emp.status)"></div>
+      </div>
+    </div>
+    <div class="basic-info">
+      <h2 class="employee-name">{{emp.name}}</h2>
+      <p class="employee-position">{{emp.position}} · {{emp.department}}</p>
+      <div class="employee-meta">
+        <span class="employee-id">工号: {{emp.employeeId}}</span>
+        <span class="join-date">入职: {{emp.hireDate | date:'yyyy年MM月dd日'}}</span>
+      </div>
+    </div>
+  </div>
+
+  <!-- 基础信息卡片 -->
+  <div class="info-card">
+    <div class="card-header">
+      <h3>基础信息</h3>
+      <button mat-icon-button (click)="editBasicInfo()" class="edit-button">
+        <mat-icon>edit</mat-icon>
+      </button>
+    </div>
+    <div class="info-grid">
+      <div class="info-item">
+        <div class="info-label">姓名</div>
+        <div class="info-value">{{emp.name}}</div>
+      </div>
+      <div class="info-item">
+        <div class="info-label">年龄</div>
+        <div class="info-value">{{calculateAge(emp.birthDate)}}岁</div>
+      </div>
+      <div class="info-item">
+        <div class="info-label">学历</div>
+        <div class="info-value">{{emp.education || '未填写'}}</div>
+      </div>
+      <div class="info-item">
+        <div class="info-label">工作年限</div>
+        <div class="info-value">{{emp.workYears || '未填写'}}</div>
+      </div>
+      <div class="info-item">
+        <div class="info-label">求职岗位</div>
+        <div class="info-value">{{emp.position}}</div>
+      </div>
+      <div class="info-item">
+        <div class="info-label">身份证号</div>
+        <div class="info-value">{{maskIdCard(emp.idCard)}}</div>
+      </div>
+      <div class="info-item">
+        <div class="info-label">银行卡号</div>
+        <div class="info-value">{{maskBankCard(emp.bankCard)}}</div>
+      </div>
+      <div class="info-item">
+        <div class="info-label">手机号码</div>
+        <div class="info-value">{{emp.phone}}</div>
+      </div>
+    </div>
+  </div>
+
+  <!-- 审核初筛状态卡片 -->
+  <div class="screening-card">
+    <div class="card-header">
+      <h3>审核初筛</h3>
+      <div class="screening-status" [ngClass]="getScreeningStatusClass(emp.screeningStatus)">
+        <mat-icon>{{getScreeningStatusIcon(emp.screeningStatus)}}</mat-icon>
+        <span>{{getScreeningStatusText(emp.screeningStatus)}}</span>
+      </div>
+    </div>
+    
+    @if (emp.screeningStatus === 'pending') {
+      <div class="screening-actions">
+        <button mat-raised-button color="primary" (click)="approveScreening()" class="approve-button">
+          <mat-icon>check_circle</mat-icon>
+          通过初筛
+        </button>
+        <button mat-stroked-button color="warn" (click)="rejectScreening()" class="reject-button">
+          <mat-icon>cancel</mat-icon>
+          不通过
+        </button>
+      </div>
+    } @else {
+      <div class="screening-result">
+        <div class="result-info">
+          <div class="result-label">审核结果:</div>
+          <div class="result-value" [ngClass]="getScreeningStatusClass(emp.screeningStatus)">
+            {{getScreeningStatusText(emp.screeningStatus)}}
+          </div>
+        </div>
+        @if (emp.screeningComment) {
+          <div class="result-comment">
+            <div class="comment-label">审核备注:</div>
+            <div class="comment-value">{{emp.screeningComment}}</div>
+          </div>
+        }
+        <div class="result-time">
+          审核时间: {{emp.screeningTime | date:'yyyy-MM-dd HH:mm'}}
+        </div>
+      </div>
+    }
+  </div>
+
+  <!-- 联系信息卡片 -->
+  <div class="contact-card">
+    <div class="card-header">
+      <h3>联系信息</h3>
+      <button mat-icon-button (click)="editContactInfo()" class="edit-button">
+        <mat-icon>edit</mat-icon>
+      </button>
+    </div>
+    <div class="contact-list">
+      <div class="contact-item">
+        <mat-icon>phone</mat-icon>
+        <div class="contact-info">
+          <div class="contact-label">手机号码</div>
+          <div class="contact-value">{{emp.phone}}</div>
+        </div>
+        <button mat-icon-button (click)="callEmployee(emp.phone)">
+          <mat-icon>call</mat-icon>
+        </button>
+      </div>
+      <div class="contact-item">
+        <mat-icon>email</mat-icon>
+        <div class="contact-info">
+          <div class="contact-label">邮箱地址</div>
+          <div class="contact-value">{{emp.email || '未填写'}}</div>
+        </div>
+        <button mat-icon-button (click)="emailEmployee(emp.email)" [disabled]="!emp.email">
+          <mat-icon>send</mat-icon>
+        </button>
+      </div>
+      <div class="contact-item">
+        <mat-icon>location_on</mat-icon>
+        <div class="contact-info">
+          <div class="contact-label">家庭住址</div>
+          <div class="contact-value">{{emp.address || '未填写'}}</div>
+        </div>
+      </div>
+    </div>
+  </div>
+
+  <!-- 工作信息卡片 -->
+  <div class="work-card">
+    <div class="card-header">
+      <h3>工作信息</h3>
+      <button mat-icon-button (click)="editWorkInfo()" class="edit-button">
+        <mat-icon>edit</mat-icon>
+      </button>
+    </div>
+    <div class="work-grid">
+      <div class="work-item">
+        <div class="work-label">所属部门</div>
+        <div class="work-value">{{emp.department}}</div>
+      </div>
+      <div class="work-item">
+        <div class="work-label">职位</div>
+        <div class="work-value">{{emp.position}}</div>
+      </div>
+      <div class="work-item">
+        <div class="work-label">员工状态</div>
+        <div class="work-value">
+          <span class="status-badge" [ngClass]="getStatusClass(emp.status)">
+            {{emp.status}}
+          </span>
+        </div>
+      </div>
+      <div class="work-item">
+        <div class="work-label">入职日期</div>
+        <div class="work-value">{{emp.hireDate | date:'yyyy年MM月dd日'}}</div>
+      </div>
+      <div class="work-item">
+        <div class="work-label">工作年限</div>
+        <div class="work-value">{{calculateWorkYears(emp.hireDate)}}年</div>
+      </div>
+      <div class="work-item">
+        <div class="work-label">直属上级</div>
+        <div class="work-value">{{emp.supervisor || '未指定'}}</div>
+      </div>
+    </div>
+  </div>
+
+  <!-- 底部操作按钮 -->
+  <div class="bottom-actions">
+    <button mat-raised-button color="primary" (click)="editEmployee()" class="action-btn">
+      <mat-icon>edit</mat-icon>
+      编辑信息
+    </button>
+    <button mat-stroked-button (click)="viewAttendance()" class="action-btn">
+      <mat-icon>event_note</mat-icon>
+      考勤记录
+    </button>
+    <button mat-stroked-button (click)="viewSalary()" class="action-btn">
+      <mat-icon>account_balance_wallet</mat-icon>
+      薪资记录
+    </button>
+  </div>
+</div>
+
+<!-- 审核对话框 -->
+<ng-template #screeningDialog>
+  <div class="screening-dialog">
+    <h2 mat-dialog-title>审核初筛</h2>
+    <mat-dialog-content>
+      <form [formGroup]="screeningForm" class="screening-form">
+        <div class="form-group">
+          <label>审核结果</label>
+          <mat-button-toggle-group formControlName="status" class="status-toggle">
+            <mat-button-toggle value="approved">通过</mat-button-toggle>
+            <mat-button-toggle value="rejected">不通过</mat-button-toggle>
+          </mat-button-toggle-group>
+        </div>
+        
+        <div class="form-group">
+          <mat-form-field appearance="outline" class="full-width">
+            <mat-label>审核备注</mat-label>
+            <textarea matInput formControlName="comment" rows="4" placeholder="请输入审核备注或建议"></textarea>
+          </mat-form-field>
+        </div>
+      </form>
+    </mat-dialog-content>
+    <mat-dialog-actions align="end">
+      <button mat-button mat-dialog-close>取消</button>
+      <button mat-raised-button color="primary" (click)="submitScreening()" [disabled]="screeningForm.invalid">
+        提交审核
+      </button>
+    </mat-dialog-actions>
+  </div>
+</ng-template>

+ 547 - 0
src/app/pages/hr/employee-detail/employee-detail.scss

@@ -0,0 +1,547 @@
+// iOS风格员工详情页面样式
+.employee-detail-container {
+  min-height: 100vh;
+  background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
+  padding: 20px;
+  
+  @media (max-width: 768px) {
+    padding: 16px;
+  }
+}
+
+// 页面头部
+.page-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin-bottom: 24px;
+  padding: 0 8px;
+
+  .header-left {
+    display: flex;
+    align-items: center;
+    gap: 16px;
+
+    .back-button {
+      width: 40px;
+      height: 40px;
+      border-radius: 20px;
+      background: rgba(255, 255, 255, 0.9);
+      backdrop-filter: blur(10px);
+      border: none;
+      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      transition: all 0.3s ease;
+
+      &:hover {
+        transform: translateY(-2px);
+        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+      }
+
+      mat-icon {
+        color: #333;
+        font-size: 20px;
+      }
+    }
+
+    .page-title {
+      font-size: 28px;
+      font-weight: 700;
+      color: #1a1a1a;
+      margin: 0;
+      letter-spacing: -0.5px;
+    }
+  }
+
+  .header-actions {
+    display: flex;
+    gap: 12px;
+
+    .action-button {
+      width: 44px;
+      height: 44px;
+      border-radius: 22px;
+      background: rgba(255, 255, 255, 0.9);
+      backdrop-filter: blur(10px);
+      border: none;
+      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      transition: all 0.3s ease;
+
+      &:hover {
+        transform: translateY(-2px);
+        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+      }
+
+      mat-icon {
+        color: #007AFF;
+        font-size: 20px;
+      }
+    }
+  }
+}
+
+// 员工头像和基本信息卡片
+.employee-header-card {
+  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+  border-radius: 24px;
+  padding: 32px;
+  margin-bottom: 24px;
+  box-shadow: 0 8px 32px rgba(102, 126, 234, 0.3);
+  color: white;
+  position: relative;
+  overflow: hidden;
+
+  &::before {
+    content: '';
+    position: absolute;
+    top: -50%;
+    right: -50%;
+    width: 200%;
+    height: 200%;
+    background: radial-gradient(circle, rgba(255, 255, 255, 0.1) 0%, transparent 70%);
+    pointer-events: none;
+  }
+
+  .employee-header-content {
+    display: flex;
+    align-items: center;
+    gap: 24px;
+    position: relative;
+    z-index: 1;
+
+    @media (max-width: 768px) {
+      flex-direction: column;
+      text-align: center;
+      gap: 20px;
+    }
+
+    .employee-avatar {
+      width: 100px;
+      height: 100px;
+      border-radius: 50%;
+      background: rgba(255, 255, 255, 0.2);
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      border: 4px solid rgba(255, 255, 255, 0.3);
+      backdrop-filter: blur(10px);
+
+      mat-icon {
+        font-size: 48px;
+        width: 48px;
+        height: 48px;
+        color: rgba(255, 255, 255, 0.9);
+      }
+    }
+
+    .employee-info {
+      flex: 1;
+
+      .employee-name {
+        font-size: 32px;
+        font-weight: 700;
+        margin: 0 0 8px 0;
+        letter-spacing: -0.5px;
+      }
+
+      .employee-position {
+        font-size: 18px;
+        opacity: 0.9;
+        margin: 0 0 16px 0;
+        font-weight: 500;
+      }
+
+      .employee-meta {
+        display: flex;
+        gap: 24px;
+        flex-wrap: wrap;
+
+        @media (max-width: 768px) {
+          justify-content: center;
+        }
+
+        .meta-item {
+          display: flex;
+          align-items: center;
+          gap: 8px;
+          font-size: 14px;
+          opacity: 0.9;
+
+          mat-icon {
+            font-size: 16px;
+            width: 16px;
+            height: 16px;
+          }
+        }
+      }
+    }
+  }
+}
+
+// 信息卡片通用样式
+.info-card {
+  background: rgba(255, 255, 255, 0.95);
+  backdrop-filter: blur(20px);
+  border-radius: 20px;
+  padding: 24px;
+  margin-bottom: 20px;
+  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
+  border: 1px solid rgba(255, 255, 255, 0.2);
+  transition: all 0.3s ease;
+
+  &:hover {
+    transform: translateY(-4px);
+    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
+  }
+
+  .card-header {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    margin-bottom: 20px;
+
+    .card-title {
+      display: flex;
+      align-items: center;
+      gap: 12px;
+      font-size: 20px;
+      font-weight: 600;
+      color: #1a1a1a;
+      margin: 0;
+
+      mat-icon {
+        color: #007AFF;
+        font-size: 24px;
+        width: 24px;
+        height: 24px;
+      }
+    }
+
+    .card-action {
+      min-width: 36px;
+      height: 36px;
+      border-radius: 18px;
+      background: rgba(0, 122, 255, 0.1);
+      border: none;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      transition: all 0.3s ease;
+
+      &:hover {
+        background: rgba(0, 122, 255, 0.2);
+        transform: scale(1.1);
+      }
+
+      mat-icon {
+        color: #007AFF;
+        font-size: 18px;
+        width: 18px;
+        height: 18px;
+      }
+    }
+  }
+
+  .card-content {
+    .info-grid {
+      display: grid;
+      grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+      gap: 16px;
+
+      @media (max-width: 768px) {
+        grid-template-columns: 1fr;
+      }
+
+      .info-item {
+        display: flex;
+        flex-direction: column;
+        gap: 6px;
+
+        .info-label {
+          font-size: 14px;
+          color: #666;
+          font-weight: 500;
+        }
+
+        .info-value {
+          font-size: 16px;
+          color: #1a1a1a;
+          font-weight: 600;
+          word-break: break-all;
+
+          &.clickable {
+            color: #007AFF;
+            cursor: pointer;
+            transition: color 0.3s ease;
+
+            &:hover {
+              color: #0056b3;
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+// 审核状态卡片
+.screening-card {
+  .screening-status {
+    display: flex;
+    align-items: center;
+    gap: 12px;
+    padding: 16px;
+    border-radius: 16px;
+    margin-bottom: 16px;
+    font-weight: 600;
+
+    &.screening-approved {
+      background: linear-gradient(135deg, #4CAF50, #45a049);
+      color: white;
+    }
+
+    &.screening-rejected {
+      background: linear-gradient(135deg, #f44336, #d32f2f);
+      color: white;
+    }
+
+    &.screening-pending {
+      background: linear-gradient(135deg, #ff9800, #f57c00);
+      color: white;
+    }
+
+    mat-icon {
+      font-size: 24px;
+      width: 24px;
+      height: 24px;
+    }
+
+    .status-text {
+      font-size: 18px;
+    }
+  }
+
+  .screening-comment {
+    background: rgba(0, 122, 255, 0.05);
+    border-radius: 12px;
+    padding: 16px;
+    margin-bottom: 16px;
+    border-left: 4px solid #007AFF;
+
+    .comment-label {
+      font-size: 14px;
+      color: #666;
+      margin-bottom: 8px;
+      font-weight: 500;
+    }
+
+    .comment-text {
+      font-size: 16px;
+      color: #1a1a1a;
+      line-height: 1.5;
+    }
+  }
+
+  .screening-actions {
+    display: flex;
+    gap: 12px;
+    flex-wrap: wrap;
+
+    .screening-button {
+      flex: 1;
+      min-width: 120px;
+      height: 48px;
+      border-radius: 24px;
+      font-weight: 600;
+      font-size: 16px;
+      border: none;
+      transition: all 0.3s ease;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      gap: 8px;
+
+      &.approve-button {
+        background: linear-gradient(135deg, #4CAF50, #45a049);
+        color: white;
+
+        &:hover {
+          transform: translateY(-2px);
+          box-shadow: 0 6px 20px rgba(76, 175, 80, 0.4);
+        }
+      }
+
+      &.reject-button {
+        background: linear-gradient(135deg, #f44336, #d32f2f);
+        color: white;
+
+        &:hover {
+          transform: translateY(-2px);
+          box-shadow: 0 6px 20px rgba(244, 67, 54, 0.4);
+        }
+      }
+
+      mat-icon {
+        font-size: 20px;
+        width: 20px;
+        height: 20px;
+      }
+    }
+  }
+}
+
+// 底部操作按钮
+.bottom-actions {
+  display: flex;
+  gap: 16px;
+  margin-top: 32px;
+  flex-wrap: wrap;
+
+  .action-button {
+    flex: 1;
+    min-width: 140px;
+    height: 52px;
+    border-radius: 26px;
+    font-weight: 600;
+    font-size: 16px;
+    border: none;
+    transition: all 0.3s ease;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    gap: 8px;
+
+    &.primary {
+      background: linear-gradient(135deg, #007AFF, #0056b3);
+      color: white;
+
+      &:hover {
+        transform: translateY(-2px);
+        box-shadow: 0 6px 20px rgba(0, 122, 255, 0.4);
+      }
+    }
+
+    &.secondary {
+      background: rgba(0, 122, 255, 0.1);
+      color: #007AFF;
+      border: 2px solid rgba(0, 122, 255, 0.2);
+
+      &:hover {
+        background: rgba(0, 122, 255, 0.2);
+        transform: translateY(-2px);
+      }
+    }
+
+    mat-icon {
+      font-size: 20px;
+      width: 20px;
+      height: 20px;
+    }
+  }
+}
+
+// 状态徽章
+.status-badge {
+  display: inline-flex;
+  align-items: center;
+  padding: 6px 12px;
+  border-radius: 20px;
+  font-size: 12px;
+  font-weight: 600;
+  text-transform: uppercase;
+  letter-spacing: 0.5px;
+
+  &.status-active {
+    background: linear-gradient(135deg, #4CAF50, #45a049);
+    color: white;
+  }
+
+  &.status-probation {
+    background: linear-gradient(135deg, #ff9800, #f57c00);
+    color: white;
+  }
+
+  &.status-resigned {
+    background: linear-gradient(135deg, #9e9e9e, #757575);
+    color: white;
+  }
+}
+
+// 响应式设计
+@media (max-width: 768px) {
+  .employee-detail-container {
+    padding: 16px;
+  }
+
+  .page-header {
+    margin-bottom: 20px;
+
+    .header-left .page-title {
+      font-size: 24px;
+    }
+  }
+
+  .employee-header-card {
+    padding: 24px;
+    
+    .employee-header-content {
+      .employee-info .employee-name {
+        font-size: 28px;
+      }
+    }
+  }
+
+  .info-card {
+    padding: 20px;
+    margin-bottom: 16px;
+  }
+
+  .bottom-actions {
+    flex-direction: column;
+    
+    .action-button {
+      min-width: 100%;
+    }
+  }
+}
+
+// 动画效果
+@keyframes fadeInUp {
+  from {
+    opacity: 0;
+    transform: translateY(30px);
+  }
+  to {
+    opacity: 1;
+    transform: translateY(0);
+  }
+}
+
+.info-card {
+  animation: fadeInUp 0.6s ease-out;
+}
+
+.employee-header-card {
+  animation: fadeInUp 0.6s ease-out 0.1s both;
+}
+
+// 对话框样式
+:host ::ng-deep .mat-mdc-dialog-container {
+  border-radius: 20px !important;
+  overflow: hidden;
+}
+
+:host ::ng-deep .mat-mdc-dialog-content {
+  padding: 24px !important;
+}
+
+:host ::ng-deep .mat-mdc-dialog-actions {
+  padding: 16px 24px 24px !important;
+  justify-content: flex-end;
+  gap: 12px;
+}

+ 320 - 0
src/app/pages/hr/employee-detail/employee-detail.ts

@@ -0,0 +1,320 @@
+import { Component, OnInit, signal, computed, inject } from '@angular/core';
+import { CommonModule, Location } from '@angular/common';
+import { FormsModule, ReactiveFormsModule, FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { MatButtonModule } from '@angular/material/button';
+import { MatInputModule } from '@angular/material/input';
+import { MatSelectModule } from '@angular/material/select';
+import { MatDialogModule, MatDialog } from '@angular/material/dialog';
+import { MatCardModule } from '@angular/material/card';
+import { MatIconModule } from '@angular/material/icon';
+import { MatChipsModule } from '@angular/material/chips';
+import { MatTooltipModule } from '@angular/material/tooltip';
+import { MatButtonToggleModule } from '@angular/material/button-toggle';
+import { MatDividerModule } from '@angular/material/divider';
+import { MatMenuModule } from '@angular/material/menu';
+import { MatSnackBar } from '@angular/material/snack-bar';
+import { Router, ActivatedRoute } from '@angular/router';
+import { Employee } from '../../../models/hr.model';
+
+@Component({
+  selector: 'app-employee-detail',
+  standalone: true,
+  imports: [
+    CommonModule,
+    FormsModule,
+    ReactiveFormsModule,
+    MatButtonModule,
+    MatInputModule,
+    MatSelectModule,
+    MatDialogModule,
+    MatCardModule,
+    MatIconModule,
+    MatChipsModule,
+    MatTooltipModule,
+    MatButtonToggleModule,
+    MatDividerModule,
+    MatMenuModule
+  ],
+  templateUrl: './employee-detail.html',
+  styleUrls: ['./employee-detail.scss']
+})
+export class EmployeeDetailComponent implements OnInit {
+  private route = inject(ActivatedRoute);
+  private router = inject(Router);
+  private location = inject(Location);
+  private dialog = inject(MatDialog);
+  private snackBar = inject(MatSnackBar);
+  private fb = inject(FormBuilder);
+
+  employee = signal<Employee | null>(null);
+  screeningForm: FormGroup;
+
+  // 模拟员工数据
+  private mockEmployees: Employee[] = [
+    {
+      id: '1',
+      name: '张三',
+      department: '设计部',
+      position: '设计师',
+      employeeId: 'DS001',
+      phone: '13800138001',
+      email: 'zhangsan@example.com',
+      gender: '男',
+      birthDate: new Date('1990-05-15'),
+      hireDate: new Date('2022-01-15'),
+      status: '在职',
+      avatar: 'assets/images/avatar1.jpg',
+      education: '本科',
+      workYears: '3年',
+      idCard: '110101199005151234',
+      bankCard: '6222021234567890123',
+      address: '北京市朝阳区xxx街道xxx号',
+      supervisor: '李经理',
+      screeningStatus: 'approved',
+      screeningComment: '技能匹配度高,工作经验丰富',
+      screeningTime: new Date('2022-01-10')
+    },
+    {
+      id: '2',
+      name: '李四',
+      department: '客服部',
+      position: '客服专员',
+      employeeId: 'CS001',
+      phone: '13800138002',
+      email: 'lisi@example.com',
+      gender: '女',
+      birthDate: new Date('1992-08-20'),
+      hireDate: new Date('2022-03-10'),
+      status: '在职',
+      education: '大专',
+      workYears: '2年',
+      idCard: '110101199208201234',
+      bankCard: '6222021234567890124',
+      address: '北京市海淀区xxx街道xxx号',
+      supervisor: '王经理',
+      screeningStatus: 'pending',
+      screeningTime: undefined
+    },
+    {
+      id: '3',
+      name: '王五',
+      department: '技术部',
+      position: '前端开发',
+      employeeId: 'FE001',
+      phone: '13800138003',
+      email: 'wangwu@example.com',
+      gender: '男',
+      birthDate: new Date('1988-11-15'),
+      hireDate: new Date('2023-01-05'),
+      status: '试用期',
+      education: '硕士',
+      workYears: '5年',
+      idCard: '110101198811151234',
+      bankCard: '6222021234567890125',
+      address: '北京市西城区xxx街道xxx号',
+      supervisor: '赵总监',
+      screeningStatus: 'rejected',
+      screeningComment: '技术能力需要进一步评估',
+      screeningTime: new Date('2023-01-02')
+    }
+  ];
+
+  constructor() {
+    this.screeningForm = this.fb.group({
+      status: ['', Validators.required],
+      comment: ['']
+    });
+  }
+
+  ngOnInit() {
+    const employeeId = this.route.snapshot.paramMap.get('id');
+    if (employeeId) {
+      this.loadEmployee(employeeId);
+    }
+  }
+
+  private loadEmployee(id: string) {
+    // 模拟从服务加载员工数据
+    const emp = this.mockEmployees.find(e => e.id === id);
+    if (emp) {
+      this.employee.set(emp);
+    } else {
+      this.showSnackBar('员工不存在');
+      this.goBack();
+    }
+  }
+
+  goBack() {
+    this.location.back();
+  }
+
+  calculateAge(birthDate: Date): number {
+    if (!birthDate) return 0;
+    const today = new Date();
+    const birth = new Date(birthDate);
+    let age = today.getFullYear() - birth.getFullYear();
+    const monthDiff = today.getMonth() - birth.getMonth();
+    if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birth.getDate())) {
+      age--;
+    }
+    return age;
+  }
+
+  calculateWorkYears(hireDate: Date): number {
+    if (!hireDate) return 0;
+    const today = new Date();
+    const hire = new Date(hireDate);
+    const diffTime = Math.abs(today.getTime() - hire.getTime());
+    const diffYears = Math.floor(diffTime / (1000 * 60 * 60 * 24 * 365));
+    return diffYears;
+  }
+
+  maskIdCard(idCard: string | undefined): string {
+    if (!idCard) return '未提供';
+    return idCard.replace(/(\d{6})\d{8}(\d{4})/, '$1********$2');
+  }
+
+  maskBankCard(bankCard: string | undefined): string {
+    if (!bankCard) return '未提供';
+    return bankCard.replace(/(\d{4})\d{12}(\d{3})/, '$1 **** **** $2');
+  }
+
+  getStatusClass(status: string): string {
+    switch (status) {
+      case '在职': return 'status-active';
+      case '试用期': return 'status-probation';
+      case '离职': return 'status-resigned';
+      default: return '';
+    }
+  }
+
+  getScreeningStatusClass(status: string | undefined): string {
+    switch (status) {
+      case 'approved': return 'status-approved';
+      case 'rejected': return 'status-rejected';
+      case 'pending': return 'status-pending';
+      default: return 'status-unknown';
+    }
+  }
+
+  getScreeningStatusIcon(status: string | undefined): string {
+    switch (status) {
+      case 'approved': return 'check_circle';
+      case 'rejected': return 'cancel';
+      case 'pending': return 'schedule';
+      default: return 'help';
+    }
+  }
+
+  getScreeningStatusText(status: string | undefined): string {
+    switch (status) {
+      case 'approved': return '初筛通过';
+      case 'rejected': return '初筛不通过';
+      case 'pending': return '待审核';
+      default: return '未知状态';
+    }
+  }
+
+  shareEmployee() {
+    if (navigator.share) {
+      navigator.share({
+        title: `员工档案 - ${this.employee()?.name}`,
+        text: `查看${this.employee()?.name}的员工档案信息`,
+        url: window.location.href
+      });
+    } else {
+      // 复制链接到剪贴板
+      navigator.clipboard.writeText(window.location.href);
+      this.showSnackBar('链接已复制到剪贴板');
+    }
+  }
+
+  editEmployee() {
+    this.showSnackBar('编辑功能开发中...');
+  }
+
+  exportEmployee() {
+    this.showSnackBar('导出功能开发中...');
+  }
+
+  deleteEmployee() {
+    this.showSnackBar('删除功能开发中...');
+  }
+
+  editBasicInfo() {
+    this.showSnackBar('编辑基础信息功能开发中...');
+  }
+
+  editContactInfo() {
+    this.showSnackBar('编辑联系信息功能开发中...');
+  }
+
+  editWorkInfo() {
+    this.showSnackBar('编辑工作信息功能开发中...');
+  }
+
+  callEmployee(phone: string) {
+    if (phone) {
+      window.open(`tel:${phone}`);
+    }
+  }
+
+  emailEmployee(email: string) {
+    if (email) {
+      window.open(`mailto:${email}`);
+    }
+  }
+
+  approveScreening() {
+    this.screeningForm.patchValue({ status: 'approved' });
+    this.openScreeningDialog();
+  }
+
+  rejectScreening() {
+    this.screeningForm.patchValue({ status: 'rejected' });
+    this.openScreeningDialog();
+  }
+
+  private openScreeningDialog() {
+    // 这里应该打开对话框,简化处理直接提交
+    this.submitScreening();
+  }
+
+  submitScreening() {
+    if (this.screeningForm.valid) {
+      const formValue = this.screeningForm.value;
+      const currentEmployee = this.employee();
+      
+      if (currentEmployee) {
+        const updatedEmployee = {
+          ...currentEmployee,
+          screeningStatus: formValue.status,
+          screeningComment: formValue.comment,
+          screeningTime: new Date()
+        };
+        
+        this.employee.set(updatedEmployee);
+        this.showSnackBar('审核结果已保存');
+        this.screeningForm.reset();
+      }
+    }
+  }
+
+  viewAttendance() {
+    this.router.navigate(['/hr/attendance'], { 
+      queryParams: { employee: this.employee()?.id } 
+    });
+  }
+
+  viewSalary() {
+    this.showSnackBar('薪资记录功能开发中...');
+  }
+
+  private showSnackBar(message: string) {
+    this.snackBar.open(message, '关闭', {
+      duration: 3000,
+      horizontalPosition: 'center',
+      verticalPosition: 'top'
+    });
+  }
+}

+ 9 - 1
src/app/pages/hr/employee-records/employee-records.html

@@ -138,11 +138,19 @@
       <!-- 操作列 -->
       <ng-container matColumnDef="actions">
         <th mat-header-cell *matHeaderCellDef>操作</th>
-        <td mat-cell *matCellDef="let employee">
+        <td mat-cell *matCellDef="let employee" class="actions-cell">
+          <mat-chip color="primary" selected class="view-detail-chip" (click)="goToDetails(employee)" matTooltip="查看该员工详情">
+            <mat-icon>visibility</mat-icon>
+            查看详情
+          </mat-chip>
           <button mat-icon-button [matMenuTriggerFor]="actionMenu" aria-label="操作菜单">
             <mat-icon>more_vert</mat-icon>
           </button>
           <mat-menu #actionMenu="matMenu">
+            <button mat-menu-item (click)="goToDetails(employee)">
+              <mat-icon>visibility</mat-icon>
+              <span>查看详情</span>
+            </button>
             <button mat-menu-item (click)="openEditEmployeeDialog(employee)">
               <mat-icon>edit</mat-icon>
               <span>编辑</span>

+ 118 - 0
src/app/pages/hr/employee-records/employee-records.scss

@@ -244,4 +244,122 @@
       grid-template-columns: 1fr;
     }
   }
+}
+
+.actions-cell {
+  display: flex;
+  align-items: center;
+  gap: 6px;
+  min-height: 56px; // 统一最小高度
+}
+
+.view-detail-chip {
+  cursor: pointer;
+  height: 32px; // 统一芯片高度
+  line-height: 32px;
+  border-radius: 16px;
+  padding: 0 12px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  
+  mat-icon {
+    font-size: 18px;
+    height: 18px;
+    width: 18px;
+    margin-right: 4px;
+  }
+}
+
+// 修复表格行高度对齐问题
+.employee-table-container {
+  background-color: #fff;
+  border-radius: 12px;
+  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.06);
+  margin-bottom: 24px;
+  overflow: auto;
+  
+  .employee-table {
+    width: 100%;
+    
+    th.mat-header-cell {
+      background: linear-gradient(180deg, #f7f9fc 0%, #eef3ff 100%);
+      color: #1a3a6e;
+      font-weight: 600;
+      padding: 12px 16px;
+      min-height: 56px; // 统一表头高度
+      vertical-align: middle;
+    }
+    
+    td.mat-cell {
+      padding: 12px 16px;
+      min-height: 56px; // 统一单元格高度
+      vertical-align: middle;
+      border-bottom: 1px solid #f0f0f0; // 确保分割线清晰
+    }
+    
+    tr.mat-row {
+      min-height: 56px; // 统一行高度
+      
+      &:hover {
+        background-color: #f8f9fa;
+      }
+    }
+    
+    // 确保所有单元格内容垂直居中
+    .mat-cell, .mat-header-cell {
+      display: flex;
+      align-items: center;
+    }
+    
+    .status-badge {
+      display: inline-flex;
+      align-items: center;
+      justify-content: center;
+      padding: 6px 12px;
+      border-radius: 12px;
+      font-size: 12px;
+      font-weight: 500;
+      text-align: center;
+      min-width: 60px;
+      height: 24px; // 固定高度
+      
+      &.status-active {
+        background-color: #e6f7ee;
+        color: #00a854;
+      }
+      
+      &.status-probation {
+        background-color: #fff7e6;
+        color: #fa8c16;
+      }
+      
+      &.status-resigned {
+        background-color: #f5f5f5;
+        color: #999;
+      }
+    }
+  }
+  
+  .empty-state {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    padding: 48px 24px;
+    text-align: center;
+    
+    mat-icon {
+      font-size: 48px;
+      height: 48px;
+      width: 48px;
+      color: #ccc;
+      margin-bottom: 16px;
+    }
+    
+    p {
+      color: #666;
+      margin-bottom: 16px;
+    }
+  }
 }

+ 14 - 1
src/app/pages/hr/employee-records/employee-records.ts

@@ -18,6 +18,7 @@ import { MatChipsModule } from '@angular/material/chips';
 import { MatMenuModule } from '@angular/material/menu';
 import { MatCardModule } from '@angular/material/card';
 import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
+import { Router } from '@angular/router';
 import { Employee, Department, Position, Contract, Certificate, EmployeeStatus } from '../../../models/hr.model';
 
 // 新增员工对话框组件
@@ -629,7 +630,8 @@ export class EmployeeRecords implements OnInit {
   
   constructor(
     private dialog: MatDialog,
-    private snackBar: MatSnackBar
+    private snackBar: MatSnackBar,
+    private router: Router
   ) {}
   
   ngOnInit() {}
@@ -754,6 +756,17 @@ export class EmployeeRecords implements OnInit {
         break;
     }
   }
+
+  // 进入员工详情(根据角色跳转至相应详情页)
+  goToDetails(employee: Employee) {
+    // 目前仅实现设计师角色详情,后续可按职位扩展其它角色
+    const isDesigner = employee.department.includes('设计') || employee.position.includes('设计');
+    if (isDesigner) {
+      this.router.navigate(['/hr/designer-profile', employee.id]);
+      return;
+    }
+    this.showSnackBar('该角色详情页正在建设中');
+  }
   
   // 选择/取消选择单个员工
   toggleSelection(employeeId: string) {

+ 3 - 1
src/app/pages/hr/hr-layout/hr-layout.html

@@ -25,11 +25,13 @@
           <span matListItemTitle *ngIf="isExpanded">员工信息管理</span>
         </a>
 
-        <!-- 设计师详情 -->
+        <!-- 设计师详情入口已移除:根据需求改为从列表进入,无需单独导航 -->
+        <!--
         <a mat-list-item routerLink="/hr/designer-profile/1" routerLinkActive="active-link">
           <mat-icon matListItemIcon>person</mat-icon>
           <span matListItemTitle *ngIf="isExpanded">设计师详情</span>
         </a>
+        -->
 
         <!-- 考勤统计 -->
         <a mat-list-item routerLink="/hr/attendance" routerLinkActive="active-link">

+ 2 - 0
src/app/pages/hr/hr-layout/hr-layout.scss

@@ -1,3 +1,5 @@
+@use '../shared/hr-common.scss';
+
 .hr-layout-container {
   height: 100vh;
   display: flex;

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

@@ -0,0 +1,405 @@
+/* HR板块统一样式文件 */
+
+/* 主题色彩变量 */
+$hr-primary: #1e40af;
+$hr-primary-light: #3b82f6;
+$hr-secondary: #0d9488;
+$hr-success: #10b981;
+$hr-warning: #f59e0b;
+$hr-error: #ef4444;
+$hr-info: #3b82f6;
+$hr-text-primary: #1f2937;
+$hr-text-secondary: #4b5563;
+$hr-text-tertiary: #9ca3af;
+$hr-bg-primary: #ffffff;
+$hr-bg-secondary: #f9fafb;
+$hr-bg-tertiary: #f3f4f6;
+$hr-border: #e5e7eb;
+
+/* 统一卡片样式 */
+.hr-card {
+  background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
+  border: 1px solid rgba(226, 232, 240, 0.8);
+  border-radius: 12px;
+  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+  transition: all 0.3s ease;
+  overflow: hidden;
+  
+  &:hover {
+    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+    transform: translateY(-2px);
+  }
+  
+  .hr-card-header {
+    background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
+    padding: 16px 20px;
+    border-bottom: 1px solid $hr-border;
+    
+    .hr-card-title {
+      display: flex;
+      align-items: center;
+      gap: 12px;
+      margin: 0;
+      font-size: 18px;
+      font-weight: 600;
+      color: $hr-text-primary;
+      
+      mat-icon {
+        background: linear-gradient(135deg, #e6f7ff 0%, #f3e8ff 100%);
+        color: $hr-primary;
+        padding: 8px;
+        border-radius: 50%;
+        font-size: 20px;
+        width: 36px;
+        height: 36px;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+      }
+    }
+  }
+  
+  .hr-card-content {
+    padding: 20px;
+  }
+}
+
+/* 统一表格样式 */
+.hr-table {
+  width: 100%;
+  background: white;
+  border-radius: 8px;
+  overflow: hidden;
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+  
+  .mat-header-cell {
+    background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
+    color: $hr-text-primary;
+    font-weight: 600;
+    font-size: 14px;
+    border-bottom: 2px solid $hr-border;
+    padding: 16px 12px;
+  }
+  
+  .mat-cell {
+    padding: 16px 12px;
+    font-size: 14px;
+    border-bottom: 1px solid rgba(226, 232, 240, 0.5);
+    
+    &:last-child {
+      text-align: center;
+    }
+  }
+  
+  .mat-row {
+    transition: all 0.2s ease;
+    
+    &:hover {
+      background-color: rgba(59, 130, 246, 0.05);
+    }
+    
+    &:last-child .mat-cell {
+      border-bottom: none;
+    }
+  }
+  
+  .no-data {
+    text-align: center;
+    color: $hr-text-tertiary;
+    font-style: italic;
+    padding: 40px 20px;
+  }
+}
+
+/* 统一按钮样式 */
+.hr-btn {
+  border-radius: 8px;
+  font-weight: 500;
+  transition: all 0.3s ease;
+  
+  &.hr-btn-primary {
+    background: linear-gradient(135deg, $hr-primary 0%, $hr-primary-light 100%);
+    color: white;
+    border: none;
+    
+    &:hover {
+      transform: translateY(-1px);
+      box-shadow: 0 4px 12px rgba(30, 64, 175, 0.3);
+    }
+  }
+  
+  &.hr-btn-secondary {
+    background: linear-gradient(135deg, $hr-secondary 0%, #14b8a6 100%);
+    color: white;
+    border: none;
+    
+    &:hover {
+      transform: translateY(-1px);
+      box-shadow: 0 4px 12px rgba(13, 148, 136, 0.3);
+    }
+  }
+  
+  &.hr-btn-outline {
+    background: transparent;
+    border: 2px solid $hr-primary;
+    color: $hr-primary;
+    
+    &:hover {
+      background: $hr-primary;
+      color: white;
+      transform: translateY(-1px);
+    }
+  }
+  
+  &.hr-btn-icon {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+    
+    mat-icon {
+      font-size: 18px !important;
+      width: 18px !important;
+      height: 18px !important;
+      line-height: 18px !important;
+    }
+  }
+}
+
+/* 统一状态徽章样式 */
+.hr-badge {
+  padding: 4px 12px;
+  border-radius: 20px;
+  font-size: 12px;
+  font-weight: 500;
+  text-align: center;
+  display: inline-block;
+  
+  &.hr-badge-success {
+    background: linear-gradient(135deg, rgba(16, 185, 129, 0.1), rgba(16, 185, 129, 0.2));
+    color: $hr-success;
+    border: 1px solid rgba(16, 185, 129, 0.3);
+  }
+  
+  &.hr-badge-warning {
+    background: linear-gradient(135deg, rgba(245, 158, 11, 0.1), rgba(245, 158, 11, 0.2));
+    color: $hr-warning;
+    border: 1px solid rgba(245, 158, 11, 0.3);
+  }
+  
+  &.hr-badge-error {
+    background: linear-gradient(135deg, rgba(239, 68, 68, 0.1), rgba(239, 68, 68, 0.2));
+    color: $hr-error;
+    border: 1px solid rgba(239, 68, 68, 0.3);
+  }
+  
+  &.hr-badge-info {
+    background: linear-gradient(135deg, rgba(59, 130, 246, 0.1), rgba(59, 130, 246, 0.2));
+    color: $hr-info;
+    border: 1px solid rgba(59, 130, 246, 0.3);
+  }
+}
+
+/* 统一表单样式 */
+.hr-form {
+  .mat-form-field {
+    width: 100%;
+    margin-bottom: 16px;
+    
+    .mat-form-field-outline {
+      border-radius: 8px;
+    }
+    
+    &.mat-focused .mat-form-field-outline-thick {
+      border-color: $hr-primary;
+    }
+  }
+  
+  .form-row {
+    display: grid;
+    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+    gap: 16px;
+    margin-bottom: 16px;
+  }
+  
+  .form-actions {
+    display: flex;
+    justify-content: flex-end;
+    gap: 12px;
+    margin-top: 24px;
+    padding-top: 16px;
+    border-top: 1px solid $hr-border;
+  }
+}
+
+/* 统一进度条样式 */
+.hr-progress {
+  height: 8px;
+  border-radius: 4px;
+  overflow: hidden;
+  background-color: rgba(226, 232, 240, 0.5);
+  
+  .progress-fill {
+    height: 100%;
+    border-radius: 4px;
+    transition: width 0.6s ease;
+    
+    &.hr-progress-primary {
+      background: linear-gradient(90deg, $hr-primary, $hr-primary-light);
+    }
+    
+    &.hr-progress-success {
+      background: linear-gradient(90deg, $hr-success, #34d399);
+    }
+    
+    &.hr-progress-warning {
+      background: linear-gradient(90deg, $hr-warning, #fbbf24);
+    }
+    
+    &.hr-progress-error {
+      background: linear-gradient(90deg, $hr-error, #f87171);
+    }
+  }
+}
+
+/* 统一统计卡片样式 */
+.hr-stat-card {
+  background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
+  border: 1px solid rgba(226, 232, 240, 0.8);
+  border-radius: 12px;
+  padding: 20px;
+  text-align: center;
+  transition: all 0.3s ease;
+  position: relative;
+  overflow: hidden;
+  
+  &::before {
+    content: '';
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    height: 4px;
+    background: linear-gradient(90deg, $hr-primary, $hr-secondary);
+  }
+  
+  &:hover {
+    transform: translateY(-4px);
+    box-shadow: 0 12px 20px -5px rgba(0, 0, 0, 0.15);
+  }
+  
+  &.hr-stat-warning::before {
+    background: linear-gradient(90deg, $hr-warning, #fbbf24);
+  }
+  
+  &.hr-stat-error::before {
+    background: linear-gradient(90deg, $hr-error, #f87171);
+  }
+  
+  &.hr-stat-info::before {
+    background: linear-gradient(90deg, $hr-info, #60a5fa);
+  }
+  
+  &.hr-stat-success::before {
+    background: linear-gradient(90deg, $hr-success, #34d399);
+  }
+  
+  .stat-value {
+    font-size: 28px;
+    font-weight: 700;
+    margin-bottom: 8px;
+    background: linear-gradient(135deg, $hr-text-primary, $hr-text-secondary);
+    -webkit-background-clip: text;
+    -webkit-text-fill-color: transparent;
+    background-clip: text;
+  }
+  
+  .stat-label {
+    font-size: 14px;
+    color: $hr-text-secondary;
+    font-weight: 500;
+    margin-bottom: 4px;
+  }
+  
+  .stat-sub {
+    font-size: 12px;
+    color: $hr-text-tertiary;
+    font-weight: 400;
+  }
+}
+
+/* 统一Material图标修复 */
+mat-icon {
+  font-family: 'Material Icons' !important;
+  font-feature-settings: 'liga' 1;
+  -webkit-font-feature-settings: 'liga';
+  -moz-font-feature-settings: 'liga';
+  font-variant-ligatures: common-ligatures;
+  text-rendering: optimizeLegibility;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+  font-size: inherit;
+  line-height: inherit;
+  vertical-align: middle;
+  
+  &::before {
+    content: none !important;
+  }
+}
+
+/* 响应式设计 */
+@media (max-width: 768px) {
+  .hr-card {
+    margin: 0 8px;
+    border-radius: 8px;
+  }
+  
+  .hr-table {
+    font-size: 12px;
+    
+    .mat-header-cell,
+    .mat-cell {
+      padding: 8px 6px;
+    }
+  }
+  
+  .hr-form .form-row {
+    grid-template-columns: 1fr;
+  }
+  
+  .hr-stat-card {
+    .stat-value {
+      font-size: 24px;
+    }
+  }
+}
+
+/* 动画效果 */
+@keyframes fadeIn {
+  from {
+    opacity: 0;
+    transform: translateY(20px);
+  }
+  to {
+    opacity: 1;
+    transform: translateY(0);
+  }
+}
+
+@keyframes slideInUp {
+  from {
+    opacity: 0;
+    transform: translateY(30px);
+  }
+  to {
+    opacity: 1;
+    transform: translateY(0);
+  }
+}
+
+.hr-fade-in {
+  animation: fadeIn 0.3s ease-in-out;
+}
+
+.hr-slide-in-up {
+  animation: slideInUp 0.3s ease-out;
+}