# 员工信息侧边栏组件 (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**: 初始版本,集成基本信息和项目负载两个视角