2025102221-fix-project-assignee-and-spaces.md 11 KB

修复项目负责人和空间场景问题

日期: 2025-10-24
问题:

  1. 分配设计师时没有空间场景可选
  2. 项目列表中负责人显示"未分配",应该显示组长名字

🔍 问题分析

问题1: 空间场景为空

现象:
http://localhost:4200/admin/project-detail/APwk78jnrh/order 分配设计师时,"指派空间场景"没有选项。

原因:
team-assign.component.ts 通过 ProductSpaceService.getProjectProductSpaces() 获取项目空间:

// team-assign.component.ts 第94-107行
async loadProjectSpaces(): Promise<void> {
  if (!this.project) return;
  
  try {
    this.loadingSpaces = true;
    const projectId = this.project.id || '';
    // 从Product表查询该项目的空间
    this.projectSpaces = await this.productSpaceService.getProjectProductSpaces(projectId);
  } catch (err) {
    console.error('加载项目空间失败:', err);
  } finally {
    this.loadingSpaces = false;
    this.cdr.markForCheck();
  }
}

ProductSpaceService.getProjectProductSpaces()Product 表查询:

// product-space.service.ts 第125-143行
async getProjectProductSpaces(projectId: string): Promise<Project[]> {
  try {
    const query = new Parse.Query('Product');
    query.equalTo('project', {
      __type: 'Pointer',
      className: 'Project',
      objectId: projectId
    });
    query.include('profile');
    query.ascending('createdAt');

    const results = await query.find();
    return results.map(product => this.parseProductData(product));

  } catch (error) {
    console.error('获取项目产品空间失败:', error);
    return [];
  }
}

根本原因: 项目ID为 APwk78jnrh 的项目在 Product 表中没有任何记录。


问题2: 项目负责人显示"未分配"

现象:
项目列表中"负责人"列显示"未分配",应该显示组长名字。

数据流:

Parse数据库 Project表
  ↓
  assignee字段 (Pointer<Profile>) = null
  ↓
project-management.ts 第107行
  assignee: json.assigneeName || '未分配'
  ↓
前端显示 "未分配"

根本原因: 项目创建时,assignee 字段没有被设置。按业务逻辑,项目的负责人应该是项目组的组长(department.leader)。


✅ 解决方案

方案1: 空间场景问题

方案A: 在"订单分配"阶段创建空间(推荐)

stage-order.component.ts 的订单分配阶段,用户填写空间信息后自动创建 Product 记录。

位置: src/modules/project/pages/project-detail/stages/stage-order.component.ts

逻辑:

async saveSpaces() {
  // 遍历用户添加的空间
  for (const space of this.spaces) {
    // 调用 ProductSpaceService.createProductSpace() 创建Product记录
    await this.productSpaceService.createProductSpace(this.project.id, {
      name: space.name,
      type: space.type,
      area: space.area,
      priority: space.priority,
      complexity: space.complexity,
      estimatedBudget: space.budget
    });
  }
}

方案B: 在分配设计师时动态创建空间

如果没有空间,显示提示:"请先在订单分配阶段添加空间"。

方案C: 提供手动添加空间的入口

在分配设计师弹窗中添加"添加空间"按钮。


方案2: 项目负责人问题

关键修改: 在选择项目组(Department)时,自动将组长(department.leader)设置为项目的 assignee

修改1: team-assign.component.ts

位置: src/modules/project/components/team-assign/team-assign.component.ts 第128-134行

修改 selectDepartment 方法:

async selectDepartment(department: FmodeObject) {
  this.selectedDepartment = department;
  this.selectedDesigner = null;
  this.departmentMembers = [];

  // ✅ 新增:自动设置组长为项目负责人
  const leader = department.get('leader');
  if (leader && this.project) {
    try {
      // 更新项目的assignee字段为组长
      this.project.set('assignee', leader);
      this.project.set('department', department);
      await this.project.save();
      console.log('✅ 项目负责人已设置为组长:', leader.get('name'));
    } catch (error) {
      console.error('❌ 设置项目负责人失败:', error);
    }
  }

  await this.loadDepartmentMembers(department);
}

说明:

  • 当选择项目组时,自动获取组长(department.leader
  • 将组长设置为项目的 assignee 字段
  • 同时设置项目的 department 字段
  • 保存到数据库

修改2: 项目创建时的默认逻辑

位置: src/app/pages/admin/services/project.service.ts 第65-110行

createProject 方法中添加逻辑:

async createProject(data: {
  title: string;
  customerId?: string;
  assigneeId?: string; // 可以是组长ID
  departmentId?: string; // 新增:项目组ID
  status?: string;
  currentStage?: string;
  deadline?: Date;
  data?: any;
}): Promise<FmodeObject> {
  const projectData: any = {
    title: data.title,
    status: data.status || '待分配',
    currentStage: data.currentStage || '订单分配'
  };

  // 设置客户指针
  if (data.customerId) {
    projectData.customer = {
      __type: 'Pointer',
      className: 'ContactInfo',
      objectId: data.customerId
    };
  }

  // ✅ 新增:如果提供了项目组,获取组长作为默认负责人
  if (data.departmentId) {
    const departmentQuery = new Parse.Query('Department');
    departmentQuery.include('leader');
    const department = await departmentQuery.get(data.departmentId);
    
    if (department) {
      projectData.department = department.toPointer();
      
      // 获取组长
      const leader = department.get('leader');
      if (leader && !data.assigneeId) {
        projectData.assignee = leader.toPointer();
        console.log('✅ 项目负责人默认为组长:', leader.get('name'));
      }
    }
  }

  // 设置负责人指针(如果明确指定)
  if (data.assigneeId) {
    projectData.assignee = {
      __type: 'Pointer',
      className: 'Profile',
      objectId: data.assigneeId
    };
  }

  if (data.deadline) {
    projectData.deadline = data.deadline;
  }

  if (data.data) {
    projectData.data = data.data;
  }

  const project = this.adminData.createObject('Project', projectData);
  return await this.adminData.save(project);
}

📊 数据库结构

Project 表

Product 表

字段 类型 说明 新增/修改
department Pointer 项目组 ✅ 确保填充
assignee Pointer 项目负责人(组长) ✅ 自动设置为组长
title String 项目名称 已有
customer Pointer 客户 已有
status String 项目状态 已有
currentStage String 当前阶段 已有

Department 表

字段 类型 说明
project Pointer 所属项目
productName String 空间名称(如"客厅")
productType String 空间类型(如"living_room")
space Object 空间详细信息
space.area Number 面积
space.priority Number 优先级
space.complexity String 复杂度
quotation Object 报价信息
requirements Object 需求信息
profile Pointer 负责该空间的设计师

Profile 表

字段 类型 说明
name String 项目组名称
leader Pointer 组长
type String 'project' (项目组)
company String 公司ID
字段 类型 说明
name String 姓名
roleName String '组长' 或 '组员'
department String 部门ID
company String 公司ID

🧪 测试步骤

测试1: 空间场景

  1. 进入订单分配阶段

    http://localhost:4200/admin/project-detail/APwk78jnrh/order
    
  2. 添加空间

    • 点击"添加空间"按钮
    • 填写空间信息(客厅、卧室等)
    • 保存
  3. 分配设计师

    • 选择项目组
    • 选择设计师
    • 预期: 能看到刚才添加的空间列表
    • 选择空间并确认分配

测试2: 项目负责人

  1. 创建新项目时指定项目组

    POST /Project
    {
     "title": "测试项目",
     "departmentId": "xxx", // 项目组ID
     "status": "待分配"
    }
    
  2. 验证数据库

    // Parse Dashboard 查看 Project 表
    - assignee字段应该指向组长的Profile
    - department字段应该指向该项目组
    
  3. 在项目详情页选择项目组

    • 进入项目详情页订单分配阶段
    • 选择一个项目组
    • 预期:
      • 项目的assignee自动更新为该组长
      • 刷新项目列表,负责人列显示组长名字
  4. 验证项目列表

    http://localhost:4200/admin/project-management
    
    • 预期: "负责人"列显示组长名字,而不是"未分配"

🎯 核心逻辑总结

项目负责人(assignee)的设置规则

  1. 项目创建时:

    • 如果指定了 departmentId,自动获取组长作为 assignee
    • 如果明确指定了 assigneeId,使用指定的人员
  2. 选择项目组时:

    • team-assign 组件中选择项目组
    • 自动将组长设置为项目的 assignee
    • 更新 department 字段
  3. 项目列表显示:

    • assignee.name 获取负责人名字
    • 如果为空,显示"未分配"

空间场景的创建流程

  1. 订单分配阶段 (stage-order):

    • 用户填写空间信息(客厅、卧室等)
    • 调用 ProductSpaceService.createProductSpace() 创建 Product 记录
  2. 分配设计师时 (team-assign):

    • Product 表查询该项目的所有空间
    • 显示空间列表供选择
    • 将选中的空间保存到 ProjectTeam.data.spaces

📁 需要修改的文件

  1. src/modules/project/components/team-assign/team-assign.component.ts

    • 第128-134行:修改 selectDepartment 方法
  2. src/app/pages/admin/services/project.service.ts

    • 第65-110行:修改 createProject 方法
  3. ⚠️ src/modules/project/pages/project-detail/stages/stage-order.component.ts

    • 需要确保空间信息保存时创建 Product 记录

🎉 预期效果

修改前

项目列表:

项目名称                             | 客户      | 负责人    | 状态
张家界凤凰城三期项目 紫空居..        | 未知客户  | 未分配    | 待分配

分配设计师:

指派空间场景
(空)

修改后

项目列表:

项目名称                             | 客户      | 负责人    | 状态
张家界凤凰城三期项目 紫空居..        | 张先生    | 汪奥      | 进行中

分配设计师:

指派空间场景 *
☑ 客厅
☐ 主卧
☐ 次卧
☐ 厨房

修改完成后,项目负责人将自动设置为组长,且分配设计师时能看到项目的所有空间!