20251024-admin-team-assignment-modal-enhancement.md 10 KB

Admin项目管理 - 设计师分配弹窗增强

日期: 2025-10-24 任务: 在Admin项目管理中增强设计师分配功能 状态: 🚧 进行中


📋 需求概述

核心需求

  1. 在Admin项目管理列表中添加"分配设计师"操作按钮
  2. 复用designer-team-assignment-modal组件
  3. 对接Parse Server真实数据,显示实际项目成员
  4. 修改弹窗中的日历组件,改为按月日历显示(单屏完整显示)
  5. 增加工序分工功能:
    • 空间分配
    • 建模分配
    • 软装分配
    • 渲染分配
    • 后期分配

🎯 实现步骤

步骤1: 在项目管理列表添加"分配设计师"按钮 ✅

文件: src/app/pages/admin/project-management/project-management.html

在操作列添加新按钮:

<button mat-icon-button class="action-btn" color="accent"
        title="分配设计师"
        (click)="openTeamAssignmentModal(project)">
  <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
    <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
    <circle cx="9" cy="7" r="4"></circle>
    <path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
    <path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
  </svg>
</button>

步骤2: 在TypeScript中实现弹窗逻辑

文件: src/app/pages/admin/project-management/project-management.ts

添加:

  • 导入DesignerTeamAssignmentModalComponent
  • 添加弹窗状态管理
  • 实现openTeamAssignmentModal(project)方法
  • 实现closeTeamAssignmentModal()方法
  • 实现confirmTeamAssignment(result)方法
  • 从Parse加载真实项目数据和团队数据

步骤3: 创建工序分工数据结构

新增接口:

export interface WorkflowAssignment {
  workflowType: 'space' | 'modeling' | 'furnishing' | 'rendering' | 'postprocessing';
  workflowName: string;
  assignedDesigners: string[]; // Designer IDs
  productIds?: string[]; // 关联的产品/空间ID
  estimatedHours?: number;
  deadline?: Date;
}

export interface EnhancedDesignerAssignmentData {
  projectId: string;
  primaryTeamId: string;
  workflowAssignments: WorkflowAssignment[];
  crossTeamCollaborators: string[];
  notes?: string;
}

步骤4: 修改弹窗组件以支持工序分工

文件: src/app/pages/designer/project-detail/components/designer-team-assignment-modal/designer-team-assignment-modal.component.html

在弹窗中添加新的"工序分工"部分:

<!-- 工序分工部分 -->
@if (internalSelectedTeamId && showWorkflowAssignment) {
  <div class="workflow-assignment-section">
    <h3>工序分工</h3>
    <div class="workflow-grid">
      <div class="workflow-item" *ngFor="let workflow of workflowTypes">
        <div class="workflow-header">
          <h4>{{ workflow.name }}</h4>
          <span class="assigned-count">已分配: {{ getWorkflowAssignedCount(workflow.type) }}人</span>
        </div>
        
        <div class="designer-assignment">
          <div class="designer-chips">
            @for (designerId of getWorkflowAssignedDesigners(workflow.type); track designerId) {
              <div class="designer-chip">
                {{ getDesignerById(designerId)?.name }}
                <button class="remove-chip" (click)="removeWorkflowDesigner(workflow.type, designerId)">×</button>
              </div>
            }
          </div>
          
          <button class="add-designer-btn" (click)="openDesignerSelector(workflow.type)">
            + 添加设计师
          </button>
        </div>
      </div>
    </div>
  </div>
}

步骤5: 修改日历组件为月视图

目标: 修改designer-calendar.component,使其显示类似iOS日历的月视图

参考图片要求:

  • 单月视图,所有日期在一屏内显示
  • 清晰标注工作日、休息日、已占用日期
  • 支持点击查看当日详情
  • 不需要左右滑动

实现方案:

创建新的月历组件或修改现有组件:

<div class="month-calendar">
  <div class="calendar-header">
    <button (click)="previousMonth()">‹</button>
    <span class="month-year">{{ currentMonth }} {{ currentYear }}</span>
    <button (click)="nextMonth()">›</button>
  </div>
  
  <div class="calendar-grid">
    <div class="weekday-header" *ngFor="let day of weekDays">{{ day }}</div>
    
    <div class="calendar-day" 
         *ngFor="let date of calendarDates"
         [class.weekend]="isWeekend(date)"
         [class.busy]="isDesignerBusy(date)"
         [class.review]="isReviewDate(date)"
         [class.available]="isAvailable(date)"
         [class.today]="isToday(date)"
         (click)="selectDate(date)">
      <span class="date-number">{{ date.getDate() }}</span>
      @if (hasEvents(date)) {
        <div class="event-dots">
          <span class="dot" *ngFor="let event of getDateEvents(date)"></span>
        </div>
      }
    </div>
  </div>
  
  <!-- 选中日期的详情 -->
  @if (selectedDate) {
    <div class="date-details">
      <h4>{{ selectedDate | date:'yyyy-MM-dd' }}</h4>
      <div class="events-list">
        <div class="event-item" *ngFor="let event of selectedDateEvents">
          {{ event.description }}
        </div>
      </div>
    </div>
  }
</div>

步骤6: Parse数据对接

查询项目团队成员:

async loadProjectTeamMembers(projectId: string): Promise<Designer[]> {
  const query = new Parse.Query('ProjectTeam');
  query.equalTo('project', { __type: 'Pointer', className: 'Project', objectId: projectId });
  query.include('profile');
  query.notEqualTo('isDeleted', true);
  
  const teamMembers = await query.find();
  
  return teamMembers.map(member => {
    const profile = member.get('profile');
    return {
      id: profile.id,
      name: profile.get('name'),
      teamId: member.get('team')?.id,
      teamName: member.get('team')?.get('name'),
      // ... 其他字段
    };
  });
}

查询工序分配:

async loadWorkflowAssignments(projectId: string): Promise<WorkflowAssignment[]> {
  const query = new Parse.Query('ProjectWorkflowAssignment');
  query.equalTo('project', { __type: 'Pointer', className: 'Project', objectId: projectId });
  query.include(['assignedTo', 'product']);
  
  const assignments = await query.find();
  
  return assignments.map(assignment => ({
    workflowType: assignment.get('workflowType'),
    workflowName: assignment.get('workflowName'),
    assignedDesigners: assignment.get('assignedTo').map(d => d.id),
    productIds: assignment.get('products')?.map(p => p.id),
  }));
}

保存分配结果:

async saveTeamAssignment(projectId: string, data: EnhancedDesignerAssignmentData): Promise<void> {
  // 1. 更新ProjectTeam表
  for (const designerId of data.selectedDesigners) {
    const ProjectTeam = Parse.Object.extend('ProjectTeam');
    const teamMember = new ProjectTeam();
    teamMember.set('project', { __type: 'Pointer', className: 'Project', objectId: projectId });
    teamMember.set('profile', { __type: 'Pointer', className: 'Profile', objectId: designerId });
    teamMember.set('role', 'member');
    await teamMember.save();
  }
  
  // 2. 保存工序分配
  for (const workflow of data.workflowAssignments) {
    const WorkflowAssignment = Parse.Object.extend('ProjectWorkflowAssignment');
    const assignment = new WorkflowAssignment();
    assignment.set('project', { __type: 'Pointer', className: 'Project', objectId: projectId });
    assignment.set('workflowType', workflow.workflowType);
    assignment.set('workflowName', workflow.workflowName);
    assignment.set('assignedTo', workflow.assignedDesigners.map(id => ({ 
      __type: 'Pointer', 
      className: 'Profile', 
      objectId: id 
    })));
    await assignment.save();
  }
}

📐 UI/UX设计要点

1. 弹窗布局优化

  • 单屏显示: 所有关键信息在一屏内,避免过度滚动
  • 左右布局: 左侧团队选择+工序分工,右侧设计师列表+日历
  • 固定高度: 弹窗最大高度90vh,内容区域可滚动

2. 工序分工设计

┌─────────────┬──────────────────────────────┐
│ 工序分工    │                              │
├─────────────┼──────────────────────────────┤
│ 🏠 空间设计  │ [张三] [李四] [+ 添加]        │
│ 🔨 建模制作  │ [王五] [+ 添加]               │
│ 🪑 软装搭配  │ [赵六] [+ 添加]               │
│ 🎨 效果渲染  │ [+ 添加]                      │
│ ✨ 后期处理  │ [+ 添加]                      │
└─────────────┴──────────────────────────────┘

3. 月历视图设计

        2025年 10月
一  二  三  四  五  六  日
         1   2   3   4   5
 6   7   8   9  10  11  12
13  14  15  16  17  18  19
20  21  22  23  24  25  26
27  28  29  30  31

图例:
🟢 空闲可分配
🔴 已占用
🟡 对图日期
🔵 当前选中

🔧 技术实现细节

Parse Schema扩展

可能需要新增表:

TABLE(ProjectWorkflowAssignment, "项目工序分配表") {
  FIELD(objectId, String)
  FIELD(project, Pointer→Project)
  FIELD(company, Pointer→Company)
  FIELD(workflowType, String) // "space" | "modeling" | "furnishing" | "rendering" | "postprocessing"
  FIELD(workflowName, String)
  FIELD(assignedTo, Array<Pointer→Profile>)
  FIELD(products, Array<Pointer→Product>) // 关联的空间产品
  FIELD(estimatedHours, Number)
  FIELD(deadline, Date)
  FIELD(isDeleted, Boolean)
  FIELD(createdAt, Date)
  FIELD(updatedAt, Date)
}

✅ 验收标准

  1. ✅ 项目管理列表中有"分配设计师"按钮
  2. ✅ 点击按钮弹出设计师分配弹窗
  3. ✅ 弹窗显示真实的项目团队成员(从Parse加载)
  4. ✅ 弹窗中有工序分工区域,可分配5种工序
  5. ✅ 日历组件改为月视图,单屏完整显示
  6. ✅ 可以查看每个设计师的月度日历
  7. ✅ 保存后数据写入Parse Server
  8. ✅ 刷新页面后分配信息保持

📝 开发备注

  • 复用现有的designer-team-assignment-modal组件
  • 日历组件可参考iOS原生日历风格
  • 工序分工为新增功能,需要扩展数据模型
  • 注意多租户隔离(company字段)
  • 所有查询都要过滤isDeleted

开发者: Claude AI 预计完成时间: 2025-10-24