USAGE_EXAMPLE.md 14 KB

员工信息侧边栏组件使用示例

在管理员端(Admin)使用

// yss-project/src/app/pages/admin/employees/employees.ts

import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { EmployeeInfoPanelComponent, EmployeeFullInfo } from '../../../shared/components/employee-info-panel';
import { EmployeeService } from '../services/employee.service';
import { DepartmentService } from '../services/department.service';

@Component({
  selector: 'app-employees',
  standalone: true,
  imports: [
    CommonModule, 
    FormsModule,
    EmployeeInfoPanelComponent  // 导入新组件
  ],
  templateUrl: './employees.html',
  styleUrls: ['./employees.scss']
})
export class Employees {
  // 原有代码保持不变...
  employees = [];
  departments = [];
  
  // 新增:员工信息面板状态
  showEmployeeInfoPanel = false;
  selectedEmployeeForPanel: EmployeeFullInfo | null = null;

  constructor(
    private employeeService: EmployeeService,
    private departmentService: DepartmentService
  ) {}

  // 点击查看按钮时,打开新的侧边栏
  viewEmployeeInfo(emp: Employee) {
    // 将员工数据转换为 EmployeeFullInfo 格式
    this.selectedEmployeeForPanel = {
      id: emp.id,
      name: emp.name,
      realname: emp.realname,
      mobile: emp.mobile,
      userid: emp.userid,
      roleName: emp.roleName,
      department: emp.department,
      departmentId: emp.departmentId,
      isDisabled: emp.isDisabled,
      createdAt: emp.createdAt,
      avatar: emp.avatar,
      email: emp.email,
      position: emp.position,
      gender: emp.gender,
      level: emp.level,
      skills: emp.skills,
      joinDate: emp.joinDate,
      workload: emp.workload
    };
    
    this.showEmployeeInfoPanel = true;
  }

  // 关闭侧边栏
  closeEmployeeInfoPanel() {
    this.showEmployeeInfoPanel = false;
    this.selectedEmployeeForPanel = null;
  }

  // 更新员工信息
  async updateEmployeeInfo(updates: Partial<EmployeeFullInfo>) {
    try {
      await this.employeeService.updateEmployee(updates.id!, {
        name: updates.name,
        mobile: updates.mobile,
        roleName: updates.roleName,
        departmentId: updates.departmentId,
        isDisabled: updates.isDisabled,
        data: {
          realname: updates.realname
        }
      });

      // 重新加载员工列表
      await this.loadEmployees();
      
      // 关闭面板
      this.closeEmployeeInfoPanel();
      
      alert('员工信息更新成功!');
    } catch (error) {
      console.error('更新员工失败:', error);
      alert('更新员工失败,请重试');
    }
  }
}
<!-- yss-project/src/app/pages/admin/employees/employees.html -->

<!-- 原有内容保持不变... -->

<!-- 在文件末尾添加新的侧边栏组件 -->
<app-employee-info-panel
  [visible]="showEmployeeInfoPanel"
  [employee]="selectedEmployeeForPanel"
  [departments]="departments()"
  [roles]="roles"
  (close)="closeEmployeeInfoPanel()"
  (update)="updateEmployeeInfo($event)">
</app-employee-info-panel>

在表格操作按钮中添加:

<td>
  <!-- 原有按钮 -->
  <button class="btn-icon" (click)="viewEmployee(emp)" title="查看(原面板)">👁</button>
  <button class="btn-icon" (click)="editEmployee(emp)" title="编辑">✏️</button>
  
  <!-- 新增按钮:打开新的侧边栏 -->
  <button class="btn-icon" (click)="viewEmployeeInfo(emp)" title="详细信息(新面板)">📋</button>
  
  <button class="btn-icon" (click)="toggleEmployee(emp)" [title]="emp.isDisabled ? '启用' : '禁用'">
    {{ emp.isDisabled ? '✓' : '🚫' }}
  </button>
</td>

在设计师组长端(Team Leader)使用

// yss-project/src/app/pages/team-leader/dashboard/dashboard.ts

import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { EmployeeInfoPanelComponent, EmployeeFullInfo, EmployeeCalendarData } from '../../../shared/components/employee-info-panel';
import { DesignerService } from '../services/designer.service';

@Component({
  selector: 'app-dashboard',
  standalone: true,
  imports: [
    CommonModule,
    EmployeeInfoPanelComponent  // 导入新组件
  ],
  templateUrl: './dashboard.html',
  styleUrls: ['./dashboard.scss']
})
export class Dashboard {
  // 原有代码...
  designers = [];
  departments = [];
  
  // 新增:员工信息面板状态
  showEmployeeInfoPanel = false;
  selectedDesignerForPanel: EmployeeFullInfo | null = null;

  constructor(
    private designerService: DesignerService,
    private router: Router
  ) {}

  // 点击设计师时,打开新的侧边栏(包含项目负载信息)
  async viewDesignerFullInfo(designer: any) {
    // 查询设计师的项目负载信息
    const projects = await this.loadDesignerProjects(designer.id);
    const calendarData = await this.loadDesignerCalendar(designer.id);
    const leaveRecords = await this.loadLeaveRecords(designer.id);
    const surveyData = await this.loadEmployeeSurvey(designer.id);
    
    // 构建完整的员工信息
    this.selectedDesignerForPanel = {
      // 基本信息
      id: designer.id,
      name: designer.name,
      realname: designer.realname,
      mobile: designer.mobile,
      userid: designer.userid,
      roleName: designer.roleName || '组员',
      department: designer.department,
      departmentId: designer.departmentId,
      isDisabled: designer.isDisabled,
      createdAt: designer.createdAt,
      avatar: designer.avatar,
      email: designer.email,
      position: designer.position,
      gender: designer.gender,
      level: designer.level,
      skills: designer.skills,
      joinDate: designer.joinDate,
      workload: designer.workload,
      
      // 项目负载信息
      currentProjects: projects.length,
      projectNames: projects.map(p => p.name),
      projectData: projects.map(p => ({ id: p.id, name: p.name })),
      calendarData: calendarData,
      leaveRecords: leaveRecords,
      redMarkExplanation: this.getRedMarkExplanation(designer, projects.length),
      
      // 能力问卷
      surveyCompleted: !!surveyData,
      surveyData: surveyData,
      profileId: designer.profileId
    };
    
    this.showEmployeeInfoPanel = true;
  }

  // 关闭侧边栏
  closeEmployeeInfoPanel() {
    this.showEmployeeInfoPanel = false;
    this.selectedDesignerForPanel = null;
  }

  // 切换月份
  async handleCalendarMonthChange(direction: number) {
    if (!this.selectedDesignerForPanel) return;
    
    const currentMonth = this.selectedDesignerForPanel.calendarData!.currentMonth;
    const newMonth = new Date(currentMonth);
    newMonth.setMonth(newMonth.getMonth() + direction);
    
    // 重新加载该月的日历数据
    const newCalendarData = await this.loadDesignerCalendar(
      this.selectedDesignerForPanel.id,
      newMonth
    );
    
    // 更新日历数据
    this.selectedDesignerForPanel = {
      ...this.selectedDesignerForPanel,
      calendarData: newCalendarData
    };
  }

  // 点击项目
  handleProjectClick(projectId: string) {
    // 跳转到项目详情页
    this.router.navigate(['/project-detail', projectId]);
  }

  // 刷新问卷
  async handleRefreshSurvey() {
    if (!this.selectedDesignerForPanel) return;
    
    try {
      const surveyData = await this.loadEmployeeSurvey(this.selectedDesignerForPanel.id);
      
      this.selectedDesignerForPanel = {
        ...this.selectedDesignerForPanel,
        surveyCompleted: !!surveyData,
        surveyData: surveyData
      };
    } catch (error) {
      console.error('刷新问卷失败:', error);
    }
  }

  // 辅助方法:加载设计师项目
  private async loadDesignerProjects(designerId: string) {
    // 实现查询逻辑
    return [];
  }

  // 辅助方法:加载设计师日历
  private async loadDesignerCalendar(designerId: string, month?: Date): Promise<EmployeeCalendarData> {
    const targetMonth = month || new Date();
    
    // 实现查询逻辑,生成日历数据
    return {
      currentMonth: targetMonth,
      days: [] // 生成日历天数数据
    };
  }

  // 辅助方法:加载请假记录
  private async loadLeaveRecords(designerId: string) {
    // 实现查询逻辑
    return [];
  }

  // 辅助方法:加载员工问卷
  private async loadEmployeeSurvey(designerId: string) {
    // 实现查询逻辑
    return null;
  }

  // 辅助方法:生成红色标记说明
  private getRedMarkExplanation(designer: any, projectCount: number): string {
    if (projectCount >= 5) {
      return `${designer.name}当前负载过高(${projectCount}个项目),建议暂缓分配新项目`;
    } else if (projectCount >= 3) {
      return `${designer.name}当前负载较高(${projectCount}个项目),可分配轻量级项目`;
    } else {
      return `${designer.name}当前负载正常(${projectCount}个项目)`;
    }
  }
}
<!-- yss-project/src/app/pages/team-leader/dashboard/dashboard.html -->

<!-- 原有内容保持不变... -->

<!-- 在设计师卡片中添加按钮 -->
<div class="designer-card" *ngFor="let designer of designers">
  <div class="designer-info">
    <h3>{{ designer.name }}</h3>
    <p>当前项目: {{ designer.currentProjects }}</p>
  </div>
  
  <div class="designer-actions">
    <!-- 原有按钮 -->
    <button (click)="viewDesignerDetail(designer)">查看(原面板)</button>
    
    <!-- 新增按钮:打开新的侧边栏 -->
    <button (click)="viewDesignerFullInfo(designer)">完整信息(新面板)</button>
  </div>
</div>

<!-- 在文件末尾添加新的侧边栏组件 -->
<app-employee-info-panel
  [visible]="showEmployeeInfoPanel"
  [employee]="selectedDesignerForPanel"
  [departments]="departments"
  [roles]="['客服', '组员', '组长', '人事', '财务', '管理员']"
  (close)="closeEmployeeInfoPanel()"
  (update)="updateEmployeeInfo($event)"
  (calendarMonthChange)="handleCalendarMonthChange($event)"
  (projectClick)="handleProjectClick($event)"
  (refreshSurvey)="handleRefreshSurvey()">
</app-employee-info-panel>

日历数据生成示例

// 生成员工日历数据的辅助函数
function generateEmployeeCalendar(
  targetMonth: Date,
  projects: Array<{ id: string; name: string; startDate: Date; endDate: Date }>
): EmployeeCalendarData {
  const days: EmployeeCalendarDay[] = [];
  
  // 获取当月第一天和最后一天
  const firstDay = new Date(targetMonth.getFullYear(), targetMonth.getMonth(), 1);
  const lastDay = new Date(targetMonth.getFullYear(), targetMonth.getMonth() + 1, 0);
  
  // 获取第一天是星期几(0-6,0是周日)
  const firstDayOfWeek = firstDay.getDay();
  
  // 获取上个月需要显示的天数
  const prevMonthDays = firstDayOfWeek;
  const prevMonthLastDay = new Date(targetMonth.getFullYear(), targetMonth.getMonth(), 0).getDate();
  
  // 添加上个月的天数
  for (let i = prevMonthDays - 1; i >= 0; i--) {
    const date = new Date(
      targetMonth.getFullYear(),
      targetMonth.getMonth() - 1,
      prevMonthLastDay - i
    );
    
    days.push({
      date,
      projectCount: 0,
      projects: [],
      isToday: false,
      isCurrentMonth: false
    });
  }
  
  // 添加当月的天数
  for (let day = 1; day <= lastDay.getDate(); day++) {
    const date = new Date(targetMonth.getFullYear(), targetMonth.getMonth(), day);
    const today = new Date();
    today.setHours(0, 0, 0, 0);
    
    // 查找该日期有哪些项目
    const dayProjects = projects.filter(p => {
      return date >= p.startDate && date <= p.endDate;
    });
    
    days.push({
      date,
      projectCount: dayProjects.length,
      projects: dayProjects.map(p => ({
        id: p.id,
        name: p.name,
        deadline: p.endDate
      })),
      isToday: date.getTime() === today.getTime(),
      isCurrentMonth: true
    });
  }
  
  // 添加下个月的天数(填充到42格,6行x7列)
  const remainingDays = 42 - days.length;
  for (let day = 1; day <= remainingDays; day++) {
    const date = new Date(
      targetMonth.getFullYear(),
      targetMonth.getMonth() + 1,
      day
    );
    
    days.push({
      date,
      projectCount: 0,
      projects: [],
      isToday: false,
      isCurrentMonth: false
    });
  }
  
  return {
    currentMonth: targetMonth,
    days
  };
}

完整示例:集成到现有项目

步骤1:在 shared/components 下创建组件

# 组件文件已经创建在:
# yss-project/src/app/shared/components/employee-info-panel/

步骤2:在需要使用的页面导入

import { EmployeeInfoPanelComponent, EmployeeFullInfo } from '@shared/components/employee-info-panel';

步骤3:添加到 imports 数组

@Component({
  // ...
  imports: [
    CommonModule,
    FormsModule,
    EmployeeInfoPanelComponent  // 添加这行
  ]
})

步骤4:在模板中使用

<app-employee-info-panel
  [visible]="showPanel"
  [employee]="selectedEmployee"
  [departments]="departments"
  (close)="handleClose()"
  (update)="handleUpdate($event)">
</app-employee-info-panel>

注意事项

  1. 不影响原有功能:新组件是独立的,不会影响原有的 employees.html 面板和 employee-detail-panel
  2. 按需使用:可以只在需要展示完整信息的地方使用新组件
  3. 数据灵活:基本信息是必填的,项目负载信息是可选的
  4. 样式独立:新组件有自己的样式,不会影响原有样式

渐进式迁移建议

  1. 阶段1:先在一个页面测试(如管理员端),只使用基本信息功能
  2. 阶段2:逐步添加项目负载数据,测试组长端功能
  3. 阶段3:验证无误后,可以考虑在其他页面也使用此组件
  4. 阶段4(可选):如果新组件完全满足需求,可以考虑废弃旧的面板组件