import { Component, OnInit, signal, computed, Inject } from '@angular/core'; import { CommonModule, NgIf, NgFor } 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 { MatDatepickerModule } from '@angular/material/datepicker'; import { MatNativeDateModule } from '@angular/material/core'; import { MatDialogModule, MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MatTableModule } from '@angular/material/table'; import { MatPaginatorModule } from '@angular/material/paginator'; import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatSnackBarModule, MatSnackBar } from '@angular/material/snack-bar'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; import { MatTabsModule } from '@angular/material/tabs'; 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'; // 新增员工对话框组件 @Component({ selector: 'app-add-employee-dialog', standalone: true, imports: [ CommonModule, NgFor, FormsModule, ReactiveFormsModule, MatButtonModule, MatInputModule, MatSelectModule, MatDatepickerModule, MatNativeDateModule, MatDialogModule, MatIconModule ], template: `

{{data.isEdit ? '编辑员工信息' : '新增员工'}}

姓名 姓名不能为空 工号 工号不能为空
部门 {{dept.name}} 请选择部门 职位 {{pos.name}} 请选择职位
手机号码 手机号码不能为空 请输入有效的手机号码 邮箱 邮箱不能为空 请输入有效的邮箱地址
性别 请选择性别 出生日期
入职日期 入职日期不能为空 员工状态 在职 离职 试用期 请选择员工状态
`, styles: [` .employee-form { display: flex; flex-direction: column; gap: 16px; padding: 8px; } .form-row { display: flex; gap: 16px; mat-form-field { flex: 1; } } `] }) export class AddEmployeeDialog implements OnInit { employeeForm: FormGroup; departments: Department[] = [ { id: '1', name: '设计部', employeeCount: 0 }, { id: '2', name: '客服部', employeeCount: 0 }, { id: '3', name: '技术部', employeeCount: 0 }, { id: '4', name: '行政部', employeeCount: 0 } ]; positions: Position[] = [ { id: '1', name: '设计师', departmentId: '1', departmentName: '设计部', level: '中级' }, { id: '2', name: '客服专员', departmentId: '2', departmentName: '客服部', level: '初级' }, { id: '3', name: '技术组长', departmentId: '3', departmentName: '技术部', level: '高级' }, { id: '4', name: '前端开发', departmentId: '3', departmentName: '技术部', level: '中级' }, { id: '5', name: '行政专员', departmentId: '4', departmentName: '行政部', level: '初级' } ]; constructor( private fb: FormBuilder, public dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public data: {employee?: Employee, isEdit: boolean} ) { this.employeeForm = this.fb.group({ name: ['', Validators.required], employeeId: ['', Validators.required], department: ['', Validators.required], position: ['', Validators.required], phone: ['', [Validators.required, Validators.pattern(/^1[3-9]\d{9}$/)]], email: ['', [Validators.required, Validators.email]], gender: ['', Validators.required], birthDate: [null], hireDate: [new Date(), Validators.required], status: ['试用期', Validators.required] }); if (data.isEdit && data.employee) { this.employeeForm.patchValue(data.employee); } } ngOnInit() {} saveEmployee() { if (this.employeeForm.valid) { const employeeData = this.employeeForm.value; this.dialogRef.close(employeeData); } } } // 入职流程对话框组件 @Component({ selector: 'app-onboarding-dialog', standalone: true, imports: [ CommonModule, NgFor, FormsModule, ReactiveFormsModule, MatButtonModule, MatInputModule, MatSelectModule, MatDatepickerModule, MatNativeDateModule, MatDialogModule, MatIconModule, MatCheckboxModule ], template: `

入职流程

员工信息

姓名: {{data.employee.name}}

部门: {{data.employee.department}}

职位: {{data.employee.position}}

入职日期: {{data.employee.hireDate | date:'yyyy-MM-dd'}}

入职任务清单

{{task.name}} 负责人: {{task.assignee}}
`, styles: [` .employee-info { margin-bottom: 24px; padding: 16px; background-color: #f5f5f5; border-radius: 4px; h3 { margin-top: 0; margin-bottom: 16px; color: #1a3a6e; } p { margin: 8px 0; } } .onboarding-tasks { h3 { margin-bottom: 16px; color: #1a3a6e; } } .task-list { display: flex; flex-direction: column; gap: 12px; } .task-item { display: flex; justify-content: space-between; align-items: center; padding: 8px 0; border-bottom: 1px solid #e0e0e0; &:last-child { border-bottom: none; } .task-assignee { color: #757575; font-size: 12px; } } `] }) export class OnboardingDialog { onboardingTasks = [ { name: '签署劳动合同', completed: false, assignee: '人事' }, { name: '录入系统权限', completed: false, assignee: '技术支持' }, { name: '领取办公用品', completed: false, assignee: '行政' }, { name: '参加新员工培训', completed: false, assignee: '培训部' }, { name: '技术组长审核岗位匹配度', completed: false, assignee: '技术组长' } ]; constructor( public dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public data: {employee: Employee} ) {} saveOnboarding() { this.dialogRef.close(this.onboardingTasks); } } // 离职流程对话框组件 @Component({ selector: 'app-offboarding-dialog', standalone: true, imports: [ CommonModule, NgFor, FormsModule, ReactiveFormsModule, MatButtonModule, MatInputModule, MatSelectModule, MatDatepickerModule, MatNativeDateModule, MatDialogModule, MatIconModule, MatCheckboxModule ], template: `

离职流程

员工信息

姓名: {{data.employee.name}}

部门: {{data.employee.department}}

职位: {{data.employee.position}}

入职日期: {{data.employee.hireDate | date:'yyyy-MM-dd'}}

离职信息

离职原因 个人发展 薪资问题 工作环境 家庭原因 健康原因 其他 请选择离职原因 离职日期 离职日期不能为空 工作交接要求

离职任务清单

{{task.name}} 负责人: {{task.assignee}}
`, styles: [` .offboarding-form { display: flex; flex-direction: column; gap: 24px; } .employee-info { padding: 16px; background-color: #f5f5f5; border-radius: 4px; h3 { margin-top: 0; margin-bottom: 16px; color: #1a3a6e; } p { margin: 8px 0; } } .offboarding-details, .offboarding-tasks { h3 { margin-bottom: 16px; color: #1a3a6e; } } .full-width { width: 100%; } .task-list { display: flex; flex-direction: column; gap: 12px; } .task-item { display: flex; justify-content: space-between; align-items: center; padding: 8px 0; border-bottom: 1px solid #e0e0e0; &:last-child { border-bottom: none; } .task-assignee { color: #757575; font-size: 12px; } } `] }) export class OffboardingDialog implements OnInit { offboardingForm: FormGroup; offboardingTasks = [ { name: '项目交接', completed: false, assignee: '技术组长' }, { name: '客户信息同步', completed: false, assignee: '客服主管' }, { name: '薪资结算', completed: false, assignee: '财务' }, { name: '归还办公设备', completed: false, assignee: '行政' }, { name: '系统权限冻结', completed: false, assignee: '技术支持' } ]; constructor( private fb: FormBuilder, public dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public data: {employee: Employee} ) { this.offboardingForm = this.fb.group({ reason: ['', Validators.required], resignDate: [new Date(), Validators.required], handoverRequirements: [''] }); } ngOnInit() {} saveOffboarding() { if (this.offboardingForm.valid) { const offboardingData = { ...this.offboardingForm.value, tasks: this.offboardingTasks }; this.dialogRef.close(offboardingData); } } } @Component({ selector: 'app-employee-records', standalone: true, imports: [ CommonModule, NgIf, NgFor, FormsModule, ReactiveFormsModule, MatButtonModule, MatInputModule, MatSelectModule, MatDatepickerModule, MatNativeDateModule, MatDialogModule, MatTableModule, MatPaginatorModule, MatCheckboxModule, MatSnackBarModule, MatIconModule, MatTooltipModule, MatTabsModule, MatChipsModule, MatMenuModule, MatCardModule, MatProgressSpinnerModule ], templateUrl: './employee-records.html', styleUrls: ['./employee-records.scss'] }) export class EmployeeRecords implements OnInit { // 员工数据 employees = signal([ { id: '1', name: '张三', department: '设计部', position: '设计师', employeeId: 'DS001', phone: '13800138001', email: 'zhangsan@example.com', gender: '男', birthDate: new Date('1990-01-01'), hireDate: new Date('2022-01-15'), status: '在职', idCard: '110105199001011234', bankCard: '6222021234567890' }, { id: '2', name: '李四', department: '客服部', position: '客服专员', employeeId: 'CS001', phone: '13800138002', email: 'lisi@example.com', gender: '女', birthDate: new Date('1992-05-20'), hireDate: new Date('2022-03-10'), status: '在职', idCard: '110105199205201256', bankCard: '6216619876543210' }, { 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: '试用期', idCard: '110105198811151278', bankCard: '6225880011223344' }, { id: '4', name: '赵六', department: '设计部', position: '高级设计师', employeeId: 'DS002', phone: '13800138004', email: 'zhaoliu@example.com', gender: '男', birthDate: new Date('1985-07-22'), hireDate: new Date('2021-06-18'), status: '在职', idCard: '110105198507221299', bankCard: '6217009988776655' }, { id: '5', name: '钱七', department: '技术部', position: '技术组长', employeeId: 'TL001', phone: '13800138005', email: 'qianqi@example.com', gender: '男', birthDate: new Date('1983-03-30'), hireDate: new Date('2020-09-01'), status: '在职', idCard: '110105198303301233', bankCard: '6222035566778899' } ]); // 筛选条件 filterName = signal(''); filterDepartment = signal(''); filterPosition = signal(''); filterStatus = signal(''); // 表格列定义 displayedColumns = ['select', 'name', 'employeeId', 'department', 'position', 'phone', 'idCard', 'bankCard', 'hireDate', 'status', 'actions']; // 选中的员工 selectedEmployees = signal([]); // 展示敏感信息的行(按员工id) sensitiveExpandedIds = signal([]); // 是否有任意行展开敏感信息 isAnySensitiveExpanded = computed(() => this.sensitiveExpandedIds().length > 0); // 部门和职位数据 departments = [ { id: '1', name: '设计部', employeeCount: 25 }, { id: '2', name: '客服部', employeeCount: 18 }, { id: '3', name: '技术部', employeeCount: 30 }, { id: '4', name: '行政部', employeeCount: 10 } ]; positions = [ { id: '1', name: '设计师', departmentId: '1', departmentName: '设计部', level: '中级' }, { id: '2', name: '高级设计师', departmentId: '1', departmentName: '设计部', level: '高级' }, { id: '3', name: '客服专员', departmentId: '2', departmentName: '客服部', level: '初级' }, { id: '4', name: '客服主管', departmentId: '2', departmentName: '客服部', level: '高级' }, { id: '5', name: '前端开发', departmentId: '3', departmentName: '技术部', level: '中级' }, { id: '6', name: '技术组长', departmentId: '3', departmentName: '技术部', level: '高级' }, { id: '7', name: '行政专员', departmentId: '4', departmentName: '行政部', level: '初级' } ]; // 计算过滤后的员工列表 filteredEmployees = computed(() => { return this.employees().filter(employee => { const nameMatch = this.filterName() === '' || employee.name.includes(this.filterName()); const departmentMatch = this.filterDepartment() === '' || employee.department === this.filterDepartment(); const positionMatch = this.filterPosition() === '' || employee.position === this.filterPosition(); const statusMatch = this.filterStatus() === '' || employee.status === this.filterStatus(); return nameMatch && departmentMatch && positionMatch && statusMatch; }); }); constructor( private dialog: MatDialog, private snackBar: MatSnackBar, private router: Router ) {} ngOnInit() {} // 掩码与格式化工具 maskIdCard(id: string): string { if (!id) return ''; if (id.length >= 18) return `${id.slice(0, 6)}********${id.slice(-4)}`; if (id.length > 6) return `${id.slice(0, 3)}****${id.slice(-2)}`; return id; } maskBankCard(card: string): string { if (!card) return ''; const compact = card.replace(/\s+/g, ''); if (compact.length <= 8) return compact; const first4 = compact.slice(0, 4); const last4 = compact.slice(-4); return `${first4} **** **** ${last4}`; } formatBankCard(card: string): string { if (!card) return ''; return card.replace(/\s+/g, '').replace(/(\d{4})(?=\d)/g, '$1 ').trim(); } isSensitiveExpanded(id: string): boolean { return this.sensitiveExpandedIds().includes(id); } toggleSensitive(id: string) { const list = this.sensitiveExpandedIds(); if (list.includes(id)) { this.sensitiveExpandedIds.set(list.filter(x => x !== id)); } else { this.sensitiveExpandedIds.set([...list, id]); } } // 打开新增员工对话框 openAddEmployeeDialog() { const dialogRef = this.dialog.open(AddEmployeeDialog, { width: '700px', panelClass: 'hr-dialog', data: { isEdit: false } }); dialogRef.afterClosed().subscribe(result => { if (result) { const newEmployee: Employee = { id: (this.employees().length + 1).toString(), ...result }; this.employees.update(employees => [...employees, newEmployee]); this.showSnackBar('员工添加成功'); } }); } // 打开编辑员工对话框 openEditEmployeeDialog(employee: Employee) { const dialogRef = this.dialog.open(AddEmployeeDialog, { width: '700px', panelClass: 'hr-dialog', data: { employee, isEdit: true } }); dialogRef.afterClosed().subscribe(result => { if (result) { this.employees.update(employees => employees.map(emp => emp.id === employee.id ? { ...emp, ...result } : emp) ); this.showSnackBar('员工信息更新成功'); } }); } // 打开入职流程对话框 openOnboardingDialog(employee: Employee) { const dialogRef = this.dialog.open(OnboardingDialog, { width: '600px', panelClass: 'hr-dialog', data: { employee } }); dialogRef.afterClosed().subscribe(result => { if (result) { this.showSnackBar('入职流程更新成功'); } }); } // 打开离职流程对话框 openOffboardingDialog(employee: Employee) { const dialogRef = this.dialog.open(OffboardingDialog, { width: '600px', panelClass: 'hr-dialog', data: { employee } }); dialogRef.afterClosed().subscribe(result => { if (result) { // 更新员工状态为离职 this.employees.update(employees => employees.map(emp => emp.id === employee.id ? { ...emp, status: '离职' } : emp) ); this.showSnackBar('离职流程已启动'); } }); } // 删除员工 deleteEmployee(employee: Employee) { if (confirm(`确定要删除员工 ${employee.name} 吗?`)) { this.employees.update(employees => employees.filter(emp => emp.id !== employee.id) ); this.showSnackBar('员工已删除'); } } // 批量删除员工 batchDelete() { if (this.selectedEmployees().length === 0) { this.showSnackBar('请先选择要删除的员工'); return; } if (confirm(`确定要删除选中的 ${this.selectedEmployees().length} 名员工吗?`)) { this.employees.update(employees => employees.filter(emp => !this.selectedEmployees().includes(emp.id)) ); this.selectedEmployees.set([]); this.showSnackBar('批量删除成功'); } } // 导出员工数据 exportEmployees(type: string) { // 实际项目中这里会调用导出服务 this.showSnackBar(`已导出${type}`); } // 快捷卡片查看详情 openQuickAction(type: 'onboarding' | 'offboarding' | 'probation') { switch (type) { case 'onboarding': // 这里可以导航或弹窗,先给出提示 this.showSnackBar('前往入职流程列表'); break; case 'offboarding': this.showSnackBar('前往离职流程列表'); break; case 'probation': this.showSnackBar('查看试用期即将到期员工'); break; } } // 进入员工详情(根据角色跳转至相应详情页) goToDetails(employee: Employee) { // 目前仅实现设计师角色详情,后续可按职位扩展其它角色 this.showSnackBar('该角色详情页正在建设中'); } // 选择/取消选择单个员工 toggleSelection(employeeId: string) { if (this.isSelected(employeeId)) { this.selectedEmployees.update(selected => selected.filter(id => id !== employeeId)); } else { this.selectedEmployees.update(selected => [...selected, employeeId]); } } // 选择/取消选择所有员工 toggleSelectAll(checked: boolean) { if (checked) { const allIds = this.filteredEmployees().map(emp => emp.id); this.selectedEmployees.set(allIds); } else { this.selectedEmployees.set([]); } } // 检查员工是否被选中 isSelected(employeeId: string): boolean { return this.selectedEmployees().includes(employeeId); } // 检查是否所有员工都被选中 isAllSelected(): boolean { return this.filteredEmployees().length > 0 && this.filteredEmployees().every(emp => this.selectedEmployees().includes(emp.id)); } // 显示提示消息 showSnackBar(message: string) { this.snackBar.open(message, '关闭', { duration: 3000, horizontalPosition: 'center', verticalPosition: 'bottom', panelClass: ['success-snackbar'] }); } // 重置筛选条件 resetFilters() { this.filterName.set(''); this.filterDepartment.set(''); this.filterPosition.set(''); this.filterStatus.set(''); } }