# 设计师分配弹窗数据同步问题分析
## 问题描述
总管理员端的项目管理中,点击"分配设计师"按钮后,设计师的**繁忙情况**和**接单情况**没有显示,与其他端看到的数据不一致。
---
## 数据来源分析
### 1️⃣ 设计师基础数据来源
#### **Department 表** (项目组信息)
- `name` - 项目组名称
- `leader` - 组长 (Pointer → Profile)
- `type` - 类型 ('project' = 项目组)
- `company` - 所属公司
- `data.description` - 项目组描述
#### **Profile 表** (设计师信息)
- `name` - 设计师姓名
- `department` - 所属项目组 (String, departmentId)
- `data.avatar` - 头像
- `data.skills` - 技能列表
- `data.status` - 初始状态 (通常为空,由项目数据计算)
- `data.workload` - 初始工作量 (通常为空,由项目数据计算)
- `data.currentProjects` - 初始项目数 (通常为空,由项目数据计算)
---
### 2️⃣ 设计师项目数据来源 (关键!)
设计师分配弹窗在 `enrichMembersWithProjectAssignments()` 方法中动态加载项目数据:
#### **查询方案1:ProjectTeam 表 (优先)**
```typescript
// 查询 ProjectTeam 表
const teamQuery = new Parse.Query('ProjectTeam');
teamQuery.matchesQuery('project', companyProjectQuery);
teamQuery.include('project');
teamQuery.include('profile');
```
**字段说明:**
- `project` - 关联的项目 (Pointer → Project)
- `profile` - 关联的设计师 (Pointer → Profile)
- `role` - 项目角色
- `isDeleted` - 是否删除
**聚合逻辑:**
- 遍历 `ProjectTeam` 记录
- 按 `profile.id` 分组统计项目数量
- **不过滤项目状态**,统计所有项目
#### **查询方案2:Project 表 (降级方案)**
如果 ProjectTeam 表为空,使用 Project 表的 `assignee` 字段:
```typescript
const projectQuery = new Parse.Query('Project');
projectQuery.equalTo('company', companyId);
projectQuery.include('assignee');
```
**字段说明:**
- `assignee` - 项目负责人 (Pointer → Profile)
- `status` - 项目状态
- `currentStage` / `stage` - 项目阶段
- `createdAt` - 创建时间
- `title` / `name` - 项目名称
---
### 3️⃣ 状态计算逻辑
设计师的状态根据**项目数量**自动计算:
```typescript
// 第808-818行
if (member.currentProjects === 0) {
member.status = 'idle'; // 🟢 绿色 - 空闲
} else if (member.currentProjects >= 1 && member.currentProjects <= 5) {
member.status = 'reviewing'; // 🟠 橙色 - 有项目
} else {
member.status = 'stagnant'; // 🔴 红色 - 繁忙 (超过5个项目)
}
```
**关键指标:**
- `currentProjects` - 当前项目总数(所有状态的项目)
- `workload` - 工作量 = `Math.min(100, currentProjects * 20)`
- `idleDays` - 闲置天数(如果有项目则为0)
- `recentOrders` - 近30天接单数
- `lastOrderDate` - 最后接单日期
- `status` - 状态 (idle/reviewing/stagnant)
---
## 核心问题定位
### ❌ 问题根源:项目管理端缺少关键输入
查看 `project-management.ts` 第322-340行:
```typescript
openTeamAssignmentModal(project: Project): void {
this.selectedProject = project;
// ❌ 使用的是模拟数据
this.projectTeams = this.mockProjectTeams;
// ❌ 没有传递 loadRealData 参数
// ❌ 没有传递 projectId 参数
this.showTeamAssignmentModal = true;
}
```
查看 `project-management.html` 第268-278行:
```html
```
---
## 对比其他端的调用方式
### ✅ 正确的调用方式(参考设计师端/组长端)
```html
```
### 🔑 关键参数说明
| 参数 | 类型 | 默认值 | 作用 |
|------|------|--------|------|
| `loadRealData` | boolean | true | 是否从数据库加载真实的项目组和成员数据 |
| `projectId` | string | '' | 项目ID,用于查询项目相关数据 |
| `loadRealSpaces` | boolean | true | 是否自动加载项目的空间数据 |
| `enableSpaceAssignment` | boolean | false | 是否启用空间分配功能 |
---
## 数据加载流程对比
### ❌ 当前项目管理端(错误流程)
```
打开弹窗
↓
loadRealData = undefined (默认 true)
↓
调用 loadRealProjectTeams()
↓
✅ 从 Department/Profile 表加载设计师基础信息
↓
❌ projectId 未传递 (空字符串)
↓
❌ enrichMembersWithProjectAssignments() 方法中
查询 ProjectTeam 时没有项目上下文
↓
❌ 无法统计设计师的项目数量
↓
❌ currentProjects = 0 (默认值)
↓
❌ status = 'idle' (默认值)
↓
❌ workload = 0 (默认值)
↓
显示:所有设计师都是"空闲"状态 ❌
```
### ✅ 其他端(正确流程)
```
打开弹窗
↓
[loadRealData]="true"
[projectId]="currentProjectId"
↓
调用 loadRealProjectTeams()
↓
✅ 从 Department/Profile 表加载设计师基础信息
↓
✅ enrichMembersWithProjectAssignments()
↓
✅ 查询 ProjectTeam 表(或 Project 表)
按公司ID查询所有项目
↓
✅ 为每个设计师统计项目数量
currentProjects = 实际项目数
↓
✅ 根据项目数量计算状态
0个项目 → idle (绿色)
1-5个项目 → reviewing (橙色)
>5个项目 → stagnant (红色)
↓
✅ 计算工作量
workload = Math.min(100, currentProjects * 20)
↓
✅ 计算闲置天数
有项目 → idleDays = 0
无项目 → 根据 lastOrderDate 计算
↓
显示:设计师真实的繁忙情况 ✅
```
---
## 修复方案
### 方案1:修改 HTML 模板(推荐)
```html
```
### 方案2:修改 TypeScript 逻辑(可选)
```typescript
// project-management.ts
openTeamAssignmentModal(project: Project): void {
this.selectedProject = project;
// ✅ 不再使用模拟数据,让弹窗组件自动加载
// ❌ this.projectTeams = this.mockProjectTeams;
this.projectTeams = []; // 让弹窗组件自动加载真实数据
// 初始化当前团队分配信息
this.currentTeamAssignment = {
primaryTeamId: project.assigneeId || null,
quotationAssignments: [],
crossTeamCollaborators: []
};
// TODO: 从Parse Server加载报价项数据
this.currentQuotationItems = [];
this.showTeamAssignmentModal = true;
console.log('✅ 打开团队分配弹窗:', project.id, project.title);
}
```
---
## 验证步骤
### 1️⃣ 修改后重新打开弹窗
控制台应该输出:
```
🚀 [设计师分配弹窗] 初始化,loadRealData: true
🔄 [设计师分配弹窗] 弹窗打开,重新加载项目数据...
✅ [项目数据加载] ProjectTeam 查询成功!
📊 [项目数据加载] 查询结果: X 条记录
🔍 [张佳乐] Member ID: designer-1
从 profileIdToProjects 获取到 3 个项目
✅ 已设置 currentProjects(所有项目) = 3
🟠 [张佳乐] 3个项目 - 有项目 (工作量:60%, 闲置:0天)
```
### 2️⃣ 检查设计师卡片显示
- **状态指示器**:
- 🟢 绿色 = 0个项目(空闲)
- 🟠 橙色 = 1-5个项目(有项目)
- 🔴 红色 = >5个项目(繁忙)
- **项目数量**:显示实际项目数,例如 "当前项目: 3个"
- **工作量进度条**:根据项目数量计算,例如 60%
- **闲置天数**:有项目时为0天,无项目时显示实际天数
---
## 潜在风险
### ⚠️ 如果 ProjectTeam 表为空
系统会自动启用降级方案,从 `Project.assignee` 字段查询:
```typescript
// 降级方案:查询 Project 表
const projectQuery = new Parse.Query('Project');
projectQuery.equalTo('company', companyId);
projectQuery.include('assignee');
const allProjects = await projectQuery.find();
const projects = allProjects.filter(p => {
const assignee = p.get('assignee');
return assignee && profileIds.has(assignee.id);
});
```
**注意事项:**
1. 确保 `Project.assignee` 字段已正确设置
2. 只统计角色为"组员"的项目
3. 项目必须属于当前公司
---
## 数据持久化位置
### ✅ Profile 表(设计师基础信息)
- **位置**:Parse Server → Profile 表
- **字段**:name, department, data.avatar, data.skills
### ✅ ProjectTeam 表(项目-设计师关联)
- **位置**:Parse Server → ProjectTeam 表
- **字段**:project (Pointer), profile (Pointer), role
### ✅ Project 表(项目信息)
- **位置**:Parse Server → Project 表
- **字段**:assignee (Pointer), status, currentStage, title
### ❌ 动态计算(不持久化)
- `currentProjects` - 每次打开弹窗时动态查询
- `workload` - 根据项目数量计算
- `status` - 根据项目数量判断
- `idleDays` - 根据最后接单日期计算
- `recentOrders` - 统计近30天项目
---
## 总结
### 问题根源
项目管理端调用设计师分配弹窗时,**没有传递 `projectId` 参数**,导致弹窗无法查询设计师的项目数据,所有设计师都显示为默认的空闲状态。
### 解决方案
在 `project-management.html` 中添加 `[projectId]="selectedProject?.id || ''"` 输入属性,让弹窗组件能够查询真实的项目数据。
### 关键参数
- `loadRealData="true"` - 启用真实数据加载(默认已是true)
- `projectId="项目ID"` - **关键!** 必须传递,用于查询项目数据
### 数据流程
```
传递 projectId
↓
查询 ProjectTeam/Project 表
↓
统计每个设计师的项目数量
↓
计算状态 (idle/reviewing/stagnant)
↓
显示繁忙情况和接单情况
```