# 员工信息侧边栏组件 (EmployeeInfoPanel) ## 概述 这是一个集成了**基本信息**和**项目负载**两个视角的员工信息侧边栏组件。通过顶部导航标签可以在两个板块之间切换查看。 ## 功能特性 ### 📋 基本信息标签页(管理员视角) - ✅ 员工头像、姓名、职位展示 - ✅ 联系方式(手机、邮箱、企微ID) - ✅ 组织信息(身份、部门、职级) - ✅ 技能标签 - ✅ 工作量统计 - ✅ 在线编辑基本信息(姓名、手机号、身份、部门、状态) ### 📊 项目负载标签页(组长视角) - ✅ 负载概况(当前项目数、核心项目列表) - ✅ 负载详细日历(月视图、项目数量可视化) - ✅ 请假明细(未来7天) - ✅ 红色标记说明 - ✅ 能力问卷展示(摘要/完整) - ✅ 详细工作日历(复用设计师日历组件) ## 使用方法 ### 1. 导入组件 ```typescript import { EmployeeInfoPanelComponent, EmployeeFullInfo } from '@shared/components/employee-info-panel'; @Component({ // ... imports: [EmployeeInfoPanelComponent] }) export class YourComponent { showEmployeePanel = false; selectedEmployee: EmployeeFullInfo | null = null; departments = [ { id: 'dept1', name: '设计一组' }, { id: 'dept2', name: '设计二组' } ]; } ``` ### 2. 在模板中使用 ```html ``` ### 3. 准备员工数据 ```typescript // 基础信息(必填) const employee: EmployeeFullInfo = { id: 'emp001', name: '张三', realname: '张三丰', mobile: '13800138000', userid: 'zhangsan', roleName: '组员', department: '设计一组', departmentId: 'dept1', isDisabled: false, avatar: 'https://example.com/avatar.jpg', email: 'zhangsan@example.com', position: '高级设计师', gender: '1', level: 'P5', skills: ['现代风格', '中式风格', '3D建模'], joinDate: '2023-01-01', createdAt: new Date('2023-01-01'), // 项目负载信息(可选,用于"项目负载"标签页) currentProjects: 3, projectNames: ['项目A', '项目B', '项目C'], projectData: [ { id: 'proj1', name: '项目A' }, { id: 'proj2', name: '项目B' }, { id: 'proj3', name: '项目C' } ], // 请假记录(可选) leaveRecords: [ { id: 'leave1', employeeName: '张三', date: '2024-01-15', isLeave: true, leaveType: 'annual', reason: '年假' } ], // 日历数据(可选) calendarData: { currentMonth: new Date(), days: [ { date: new Date('2024-01-15'), projectCount: 2, projects: [ { id: 'proj1', name: '项目A', deadline: new Date('2024-01-20') }, { id: 'proj2', name: '项目B' } ], isToday: true, isCurrentMonth: true } // ... more days ] }, // 红色标记说明(可选) redMarkExplanation: '该设计师项目负载较高,建议暂缓分配新项目', // 工作量统计(可选) workload: { currentProjects: 3, completedProjects: 15, averageQuality: 8.5 }, // 能力问卷(可选) surveyCompleted: true, surveyData: { createdAt: new Date('2023-12-01'), answers: [ { questionId: 'q1_expertise_styles', question: '您擅长的设计风格?', type: 'multiple', answer: ['现代', '中式', '北欧'] }, // ... more answers ] }, profileId: 'profile123' }; ``` ### 4. 处理事件 ```typescript export class YourComponent { // 关闭面板 handleClose() { this.showEmployeePanel = false; this.selectedEmployee = null; } // 更新员工信息 async handleUpdate(updates: Partial) { try { await this.employeeService.updateEmployee(updates.id!, { name: updates.name, realname: updates.realname, mobile: updates.mobile, roleName: updates.roleName, departmentId: updates.departmentId, isDisabled: updates.isDisabled }); // 重新加载员工列表 await this.loadEmployees(); // 关闭面板 this.handleClose(); alert('更新成功!'); } catch (error) { console.error('更新失败:', error); alert('更新失败,请重试'); } } // 切换月份 handleMonthChange(direction: number) { // direction: -1 (上月) | 1 (下月) // 重新计算日历数据 const currentMonth = this.selectedEmployee!.calendarData!.currentMonth; const newMonth = new Date(currentMonth); newMonth.setMonth(newMonth.getMonth() + direction); // 更新日历数据 this.updateCalendarData(newMonth); } // 项目点击 handleProjectClick(projectId: string) { // 跳转到项目详情页 this.router.navigate(['/project-detail', projectId]); } // 刷新问卷 async handleRefreshSurvey() { try { // 重新查询员工的问卷数据 const surveyData = await this.surveyService.getEmployeeSurvey(this.selectedEmployee!.id); // 更新员工信息 this.selectedEmployee = { ...this.selectedEmployee!, surveyCompleted: !!surveyData, surveyData: surveyData }; } catch (error) { console.error('刷新问卷失败:', error); } } } ``` ## Props(输入属性) | 属性名 | 类型 | 必填 | 默认值 | 说明 | |--------|------|------|--------|------| | `visible` | `boolean` | 是 | `false` | 面板是否可见 | | `employee` | `EmployeeFullInfo \| null` | 是 | `null` | 员工完整信息 | | `departments` | `Array<{id: string; name: string}>` | 否 | `[]` | 部门列表(用于编辑时选择) | | `roles` | `string[]` | 否 | `['客服', '组员', '组长', '人事', '财务', '管理员']` | 角色列表 | ## Events(输出事件) | 事件名 | 参数类型 | 说明 | |--------|----------|------| | `close` | `void` | 关闭面板 | | `update` | `Partial` | 更新员工信息 | | `calendarMonthChange` | `number` | 切换日历月份(-1或1) | | `calendarDayClick` | `EmployeeCalendarDay` | 点击日历日期 | | `projectClick` | `string` | 点击项目(项目ID) | | `refreshSurvey` | `void` | 刷新问卷数据 | ## 数据接口 ### EmployeeFullInfo 完整的员工信息接口,整合了基本信息和项目负载信息。 ```typescript interface EmployeeFullInfo { // === 基础信息(必填) === id: string; name: string; realname?: string; mobile: string; userid: string; roleName: string; department: string; departmentId?: string; isDisabled?: boolean; createdAt?: Date; // === 扩展信息(可选) === avatar?: string; email?: string; position?: string; gender?: string; level?: string; skills?: string[]; joinDate?: Date | string; // === 工作量统计(可选) === workload?: { currentProjects?: number; completedProjects?: number; averageQuality?: number; }; // === 项目负载信息(可选) === currentProjects?: number; projectNames?: string[]; projectData?: Array<{ id: string; name: string }>; leaveRecords?: LeaveRecord[]; redMarkExplanation?: string; calendarData?: EmployeeCalendarData; // === 能力问卷(可选) === surveyCompleted?: boolean; surveyData?: any; profileId?: string; } ``` ## 样式定制 组件使用渐变紫色主题,可以通过修改 SCSS 变量自定义: ```scss // 主色调 $primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%); // 状态颜色 $color-active: #4caf50; // 正常/在职 $color-disabled: #f44336; // 禁用/离职 $color-warning: #ff9800; // 高负载/今天 // 圆角 $border-radius: 12px; // 阴影 $box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); ``` ## 注意事项 1. **DesignerCalendar 依赖**: 组件依赖 `DesignerCalendarComponent`,确保该组件可用。 2. **响应式设计**: 移动端会自动调整为单列布局。 3. **数据完整性**: 基本信息字段为必填,项目负载字段为可选(如果不需要"项目负载"标签页,可以不提供相关数据)。 4. **性能优化**: 日历数据较大时,建议按需加载(只加载当前月份)。 ## 与原有组件的关系 - ✅ 不影响原有的管理员端 `employees.html` 面板 - ✅ 不影响原有的组长端 `employee-detail-panel` 组件 - ✅ 作为独立组件,可在任何需要展示员工完整信息的地方复用 - ✅ 通过顶部导航切换,集成了两个视角的所有信息 ## 示例场景 ### 场景1:管理员查看员工详情 ```typescript // 只需要基本信息,不需要项目负载数据 viewEmployee(emp: Employee) { this.selectedEmployee = { ...emp, // 不提供 projectData、calendarData 等字段 }; this.showEmployeePanel = true; } ``` ### 场景2:组长查看设计师负载 ```typescript // 需要完整信息,包括项目负载 async viewDesignerWorkload(designerId: string) { // 查询基本信息 const basicInfo = await this.employeeService.getEmployee(designerId); // 查询项目负载 const projects = await this.projectService.getDesignerProjects(designerId); // 查询日历数据 const calendarData = await this.calendarService.getDesignerCalendar(designerId); // 查询请假记录 const leaveRecords = await this.leaveService.getLeaveRecords(designerId); // 查询问卷 const surveyData = await this.surveyService.getEmployeeSurvey(designerId); this.selectedEmployee = { ...basicInfo, currentProjects: projects.length, projectData: projects, calendarData, leaveRecords, surveyCompleted: !!surveyData, surveyData }; this.showEmployeePanel = true; } ``` ## 更新日志 - **2024-01-08**: 初始版本,集成基本信息和项目负载两个视角