# 项目管理 - 订单分配阶段 PRD

## 1. 功能概述

### 1.1 阶段定位
订单分配阶段是项目管理流程的第一个环节，主要负责将客户咨询转化为正式项目订单，并完成设计师团队的初步分配。该阶段是连接客服端和设计师端的关键桥梁。

### 1.2 核心目标
- 完成客户信息的结构化录入和同步
- 确定项目报价和付款条件
- 匹配并分配合适的设计师资源
- 建立项目基础档案，为后续环节提供数据支撑

### 1.3 涉及角色
- **客服人员**：负责创建订单、录入客户信息、初步需求沟通
- **设计师**：接收订单分配、查看项目基础信息
- **组长**：查看团队订单分配情况、协调设计师资源

## 2. 核心功能模块

### 2.1 客户信息管理

#### 2.1.1 信息展示卡片
**位置**：订单分配阶段左侧面板

**展示内容**：
```typescript
interface CustomerInfoDisplay {
  // 基础信息
  name: string;           // 客户姓名
  phone: string;          // 联系电话
  wechat?: string;        // 微信号
  customerType: string;   // 客户类型：新客户/老客户/VIP客户
  source: string;         // 来源：小程序/官网咨询/推荐介绍
  remark?: string;        // 备注信息

  // 状态信息
  syncStatus: 'syncing' | 'synced' | 'error'; // 同步状态
  lastSyncTime?: Date;    // 最后同步时间

  // 需求标签
  demandType?: string;    // 需求类型：价格敏感/质量敏感/综合要求
  followUpStatus?: string; // 跟进状态：待报价/待确认需求/已失联
  preferenceTags?: string[]; // 偏好标签数组
}
```

**数据来源**：
1. **客服端同步**：通过路由查询参数 `syncData` 传递客户信息
   ```typescript
   // 客服端跳转示例
   router.navigate(['/designer/project-detail', projectId], {
     queryParams: {
       syncData: JSON.stringify({
         customerInfo: {...},
         requirementInfo: {...},
         preferenceTags: [...]
       })
     }
   });
   ```

2. **实时同步机制**：
   - 每30秒自动同步一次客户信息
   - 显示同步状态指示器（同步中/已同步/同步失败）
   - 支持手动触发同步

**交互特性**：
- 卡片可展开/收起（`isCustomerInfoExpanded`）
- 展开时显示完整客户信息和标签
- 收起时仅显示客户姓名和联系方式
- 同步状态实时更新，显示"刚刚/X分钟前/X小时前"

#### 2.1.2 客户搜索功能
**适用场景**：手动创建订单时快速选择已有客户

**搜索逻辑**：
```typescript
searchCustomer(): void {
  // 至少输入2个字符才触发搜索
  if (this.customerSearchKeyword.trim().length >= 2) {
    // 模糊搜索客户姓名、手机号、微信号
    this.customerSearchResults = this.customerService.search({
      keyword: this.customerSearchKeyword,
      fields: ['name', 'phone', 'wechat']
    });
  }
}
```

**搜索结果展示**：
- 下拉列表形式
- 每项显示：客户姓名、电话（脱敏）、客户类型、来源
- 点击选择后自动填充表单

### 2.2 核心需求表单

#### 2.2.1 必填项配置
**表单定义**：
```typescript
orderCreationForm = this.fb.group({
  orderAmount: ['', [Validators.required, Validators.min(0)]],
  smallImageDeliveryTime: ['', Validators.required],
  decorationType: ['', Validators.required],
  requirementReason: ['', Validators.required],
  isMultiDesigner: [false]
});
```

**字段详解**：

| 字段名 | 类型 | 验证规则 | 说明 | UI组件 |
|-------|------|---------|------|--------|
| `orderAmount` | number | required, min(0) | 订单金额，单位：元 | 数字输入框，支持千分位格式化 |
| `smallImageDeliveryTime` | Date | required | 小图交付时间 | 日期选择器，限制最早日期为今天 |
| `decorationType` | string | required | 装修类型：全包/半包/清包/软装 | 下拉选择框 |
| `requirementReason` | string | required | 需求原因：新房装修/旧房改造/局部翻新 | 单选框组 |
| `isMultiDesigner` | boolean | - | 是否需要多设计师协作 | 复选框 |

**表单验证提示**：
- 实时验证：失焦时触发
- 错误提示：红色边框 + 底部错误文字
- 提交验证：点击"分配订单"按钮时调用 `markAllAsTouched()` 显示所有错误

#### 2.2.2 可选信息表单
**折叠面板设计**：
```html
<div class="optional-info-section">
  <div class="section-header" (click)="isOptionalFormExpanded = !isOptionalFormExpanded">
    <span>可选信息</span>
    <span class="toggle-icon">{{ isOptionalFormExpanded ? '▼' : '▶' }}</span>
  </div>
  @if (isOptionalFormExpanded) {
    <div class="section-content">
      <!-- 可选字段表单 -->
    </div>
  }
</div>
```

**可选字段**：
```typescript
optionalForm = this.fb.group({
  largeImageDeliveryTime: [''],      // 大图交付时间
  spaceRequirements: [''],           // 空间需求描述
  designAngles: [''],                // 设计角度要求
  specialAreaHandling: [''],         // 特殊区域处理说明
  materialRequirements: [''],        // 材质要求
  lightingRequirements: ['']         // 光照需求
});
```

### 2.3 报价明细组件

#### 2.3.1 组件集成
**组件标签**：
```html
<app-quotation-details
  [initialData]="quotationData"
  [readonly]="isReadOnly()"
  (dataChange)="onQuotationDataChange($event)">
</app-quotation-details>
```

**数据结构**：
```typescript
interface QuotationData {
  items: Array<{
    id: string;
    room: string;           // 空间名称：客餐厅/主卧/次卧/厨房/卫生间
    amount: number;         // 金额
    description?: string;   // 描述
  }>;
  totalAmount: number;      // 总金额
  materialCost: number;     // 材料费
  laborCost: number;        // 人工费
  designFee: number;        // 设计费
  managementFee: number;    // 管理费
}
```

#### 2.3.2 组件功能
1. **报价项管理**：
   - 添加报价项：按空间/按项目添加
   - 删除报价项：确认后删除
   - 编辑金额：实时更新总金额

2. **AI辅助生成**（可选功能）：
   ```typescript
   generateQuotationDetails(): void {
     // 基于项目信息自动生成报价明细
     const rooms = ['客餐厅', '主卧', '次卧', '厨房', '卫生间'];
     this.quotationDetails = rooms.map((room, index) => ({
       id: `quote_${index + 1}`,
       room: room,
       amount: Math.floor(Math.random() * 1000) + 300,
       description: `${room}装修设计费用`
     }));
     this.orderAmount = this.quotationDetails.reduce((total, item) => total + item.amount, 0);
   }
   ```

3. **金额自动汇总**：
   - 实时计算总金额
   - 费用分类占比显示
   - 支持导出报价单（PDF/Excel）

### 2.4 设计师指派组件

#### 2.4.1 组件集成
**组件标签**：
```html
<app-designer-assignment
  [projectData]="projectData"
  [readonly]="isReadOnly()"
  (assignmentChange)="onDesignerAssignmentChange($event)"
  (designerClick)="onDesignerClick($event)">
</app-designer-assignment>
```

**数据结构**：
```typescript
interface DesignerAssignmentData {
  selectedDesigners: Designer[];
  teamId: string;
  teamName: string;
  leaderId: string;
  assignmentDate: Date;
  expectedStartDate: Date;
}

interface Designer {
  id: string;
  name: string;
  avatar: string;
  teamId: string;
  teamName: string;
  isTeamLeader: boolean;
  status: 'idle' | 'busy' | 'unavailable';
  currentProjects: number;
  skillMatch: number;        // 技能匹配度 0-100
  recentOrders: number;      // 近期订单数
  idleDays: number;          // 闲置天数
  workload: number;          // 工作负荷 0-100
  reviewDates: string[];     // 对图评审日期列表
}
```

#### 2.4.2 设计师选择逻辑
**智能推荐算法**：
```typescript
calculateDesignerScore(designer: Designer): number {
  let score = 0;

  // 1. 技能匹配度（权重40%）
  score += designer.skillMatch * 0.4;

  // 2. 工作负荷（权重30%，负荷越低分数越高）
  score += (100 - designer.workload) * 0.3;

  // 3. 闲置时间（权重20%，闲置越久分数越高）
  score += Math.min(designer.idleDays * 2, 100) * 0.2;

  // 4. 近期接单数（权重10%，接单越少分数越高）
  score += Math.max(0, 100 - designer.recentOrders * 10) * 0.1;

  return score;
}
```

**设计师列表展示**：
- 卡片网格布局，每行3-4个设计师卡片
- 卡片信息：头像、姓名、团队、状态标签、技能匹配度进度条
- 状态颜色：
  - `idle` 空闲 - 绿色
  - `busy` 繁忙 - 橙色
  - `unavailable` 不可用 - 灰色
- 点击卡片可查看设计师详细日历

#### 2.4.3 设计师日历弹窗
**触发条件**：点击设计师卡片时

**弹窗内容**：
```html
<app-designer-calendar
  [designers]="[selectedDesigner]"
  [groups]="calendarGroups"
  [selectedDate]="selectedCalendarDate"
  (designerSelected)="onCalendarDesignerSelected($event)"
  (assignmentRequested)="onCalendarAssignmentRequested($event)">
</app-designer-calendar>
```

**日历功能**：
- 月视图展示设计师日程
- 标记对图评审日期（红点）
- 显示已分配项目时间段
- 计算下一个可用日期
- 支持按日期筛选空闲设计师

### 2.5 下单时间自动生成

**实现逻辑**：
```typescript
ngOnInit(): void {
  // 自动生成下单时间
  this.orderTime = new Date().toLocaleString('zh-CN', {
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
    second: '2-digit'
  });
}
```

**显示格式**：`2025-10-16 14:30:25`

**用途**：
- 记录订单创建的准确时间
- 作为项目开始时间的参考
- 用于计算项目周期和延期预警

## 3. 数据流转

### 3.1 客服端同步流程

```mermaid
sequenceDiagram
    participant CS as 客服端
    participant Route as 路由
    participant PD as 项目详情页
    participant Form as 订单表单

    CS->>Route: navigate with syncData
    Route->>PD: queryParams.syncData
    PD->>PD: parseJSON(syncData)
    PD->>Form: patchValue(customerInfo)
    PD->>PD: syncRequirementKeyInfo(requirementInfo)
    PD->>PD: 更新 projectData
    PD->>PD: 触发 cdr.detectChanges()
    PD-->>CS: 同步完成
```

**关键代码实现**：
```typescript
// project-detail.ts lines 741-816
this.route.queryParamMap.subscribe({
  next: (qp) => {
    const syncDataParam = qp.get('syncData');
    if (syncDataParam) {
      try {
        const syncData = JSON.parse(syncDataParam);

        // 设置同步状态
        this.isSyncingCustomerInfo = true;

        // 存储订单分配数据用于显示
        this.orderCreationData = syncData;

        // 同步客户信息到表单
        if (syncData.customerInfo) {
          this.customerForm.patchValue({
            name: syncData.customerInfo.name || '',
            phone: syncData.customerInfo.phone || '',
            wechat: syncData.customerInfo.wechat || '',
            customerType: syncData.customerInfo.customerType || '新客户',
            source: syncData.customerInfo.source || '小程序',
            remark: syncData.customerInfo.remark || ''
          });
        }

        // 同步需求信息
        if (syncData.requirementInfo) {
          this.syncRequirementKeyInfo(syncData.requirementInfo);
        }

        // 同步偏好标签
        if (syncData.preferenceTags) {
          this.project.customerTags = syncData.preferenceTags;
        }

        // 模拟同步完成
        setTimeout(() => {
          this.isSyncingCustomerInfo = false;
          this.lastSyncTime = new Date();
          this.cdr.detectChanges();
        }, 1500);

      } catch (error) {
        console.error('解析同步数据失败:', error);
        this.isSyncingCustomerInfo = false;
      }
    }
  }
});
```

### 3.2 订单创建流程

```mermaid
flowchart TD
    A[客服/设计师填写表单] --> B{表单验证}
    B -->|验证失败| C[显示错误提示]
    C --> A
    B -->|验证成功| D[调用 createOrder]
    D --> E[整合表单数据]
    E --> F[整合报价数据]
    F --> G[整合设计师分配数据]
    G --> H[调用 ProjectService.createProject]
    H --> I{API响应}
    I -->|成功| J[显示成功提示]
    J --> K[推进到需求沟通阶段]
    K --> L[展开需求沟通面板]
    L --> M[滚动到需求沟通区域]
    I -->|失败| N[显示错误信息]
```

**关键方法实现**：
```typescript
// project-detail.ts lines 4783-4808
createOrder(): void {
  if (!this.canCreateOrder()) {
    // 标记所有字段为已触摸，以显示验证错误
    this.orderCreationForm.markAllAsTouched();
    return;
  }

  const orderData = {
    ...this.orderCreationForm.value,
    ...this.optionalForm.value,
    customerInfo: this.orderCreationData?.customerInfo,
    quotationData: this.quotationData,
    designerAssignment: this.designerAssignmentData
  };

  console.log('分配订单:', orderData);

  // 调用 ProjectService 创建项目
  this.projectService.createProject(orderData).subscribe({
    next: (result) => {
      if (result.success) {
        alert('订单分配成功！');
        // 订单分配成功后自动切换到下一环节
        this.advanceToNextStage('订单分配');
      }
    },
    error: (error) => {
      console.error('订单分配失败:', error);
      alert('订单分配失败，请重试');
    }
  });
}
```

### 3.3 阶段推进机制

**推进触发条件**：
- 订单分配完成（必填项填写 + 报价确认 + 设计师分配）
- 点击"分配订单"按钮并验证通过
- 或者通过 `onProjectCreated` 事件自动推进

**推进逻辑**：
```typescript
// project-detail.ts lines 1391-1423
advanceToNextStage(afterStage: ProjectStage): void {
  const idx = this.stageOrder.indexOf(afterStage);
  if (idx >= 0 && idx < this.stageOrder.length - 1) {
    const next = this.stageOrder[idx + 1];

    // 更新项目阶段
    this.updateProjectStage(next);

    // 更新展开状态，折叠当前、展开下一阶段
    this.expandedStages[afterStage] = false;
    this.expandedStages[next] = true;

    // 更新板块展开状态
    const nextSection = this.getSectionKeyForStage(next);
    this.expandedSection = nextSection;

    // 触发变更检测以更新导航栏颜色
    this.cdr.detectChanges();
  }
}

// project-detail.ts lines 3038-3069
onProjectCreated(projectData: any): void {
  console.log('项目创建完成:', projectData);
  this.projectData = projectData;

  // 团队分配已在子组件中完成并触发该事件：推进到需求沟通阶段
  this.updateProjectStage('需求沟通');

  // 更新项目对象的当前阶段，确保四大板块状态正确显示
  if (this.project) {
    this.project.currentStage = '需求沟通';
  }

  // 展开需求沟通阶段，收起订单分配阶段
  this.expandedStages['需求沟通'] = true;
  this.expandedStages['订单分配'] = false;

  // 自动展开确认需求板块
  this.expandedSection = 'requirements';

  // 强制触发变更检测，确保UI更新
  this.cdr.detectChanges();

  // 延迟滚动到需求沟通阶段，确保DOM更新完成
  setTimeout(() => {
    this.scrollToStage('需求沟通');
    this.cdr.detectChanges();
  }, 100);
}
```

## 4. 权限控制

### 4.1 角色权限矩阵

| 操作 | 客服 | 设计师 | 组长 | 技术 |
|-----|------|--------|------|------|
| 查看订单分配阶段 | ✅ | ✅ | ✅ | ✅ |
| 创建订单 | ✅ | ❌ | ✅ | ❌ |
| 编辑客户信息 | ✅ | ❌ | ✅ | ❌ |
| 填写报价明细 | ✅ | ❌ | ✅ | ❌ |
| 选择设计师 | ✅ | ❌ | ✅ | ❌ |
| 接收订单通知 | ❌ | ✅ | ✅ | ❌ |
| 查看设计师日历 | ✅ | ✅ | ✅ | ❌ |
| 推进到下一阶段 | ✅ | ❌ | ✅ | ❌ |

### 4.2 权限检查方法

```typescript
// project-detail.ts lines 890-936
private detectRoleContextFromUrl(): 'customer-service' | 'designer' | 'team-leader' | 'technical' {
  const url = this.router.url || '';

  // 首先检查查询参数中的role
  const queryParams = this.route.snapshot.queryParamMap;
  const roleParam = queryParams.get('roleName');
  if (roleParam === 'customer-service') {
    return 'customer-service';
  }
  if (roleParam === 'technical') {
    return 'technical';
  }

  // 如果没有role查询参数，则根据URL路径判断
  if (url.includes('/customer-service/')) return 'customer-service';
  if (url.includes('/team-leader/')) return 'team-leader';
  if (url.includes('/technical/')) return 'technical';
  return 'designer';
}

isDesignerView(): boolean { return this.roleContext === 'designer'; }
isTeamLeaderView(): boolean { return this.roleContext === 'team-leader'; }
isCustomerServiceView(): boolean { return this.roleContext === 'customer-service'; }
isTechnicalView(): boolean { return this.roleContext === 'technical'; }
isReadOnly(): boolean { return this.isCustomerServiceView(); }

canEditSection(sectionKey: SectionKey): boolean {
  if (this.isCustomerServiceView()) {
    return sectionKey === 'order' || sectionKey === 'requirements' || sectionKey === 'aftercare';
  }
  return true; // 设计师和组长可以编辑所有板块
}

canEditStage(stage: ProjectStage): boolean {
  if (this.isCustomerServiceView()) {
    const editableStages: ProjectStage[] = [
      '订单分配', '需求沟通', '方案确认', // 订单分配和确认需求板块
      '尾款结算', '客户评价', '投诉处理' // 售后板块
    ];
    return editableStages.includes(stage);
  }
  return true; // 设计师和组长可以编辑所有阶段
}
```

### 4.3 UI权限控制

**模板权限指令**：
```html
<!-- 只读模式：客服查看设计师视角 -->
@if (isReadOnly()) {
  <div class="readonly-banner">
    当前为只读模式，您可以查看项目信息但无法编辑
  </div>
}

<!-- 编辑权限检查：表单禁用状态 -->
<app-quotation-details
  [readonly]="!canEditSection('order')"
  ...>
</app-quotation-details>

<!-- 按钮显示控制：只有客服和组长可以看到分配订单按钮 -->
@if (canEditSection('order')) {
  <button
    class="btn-primary"
    [disabled]="!canCreateOrder()"
    (click)="createOrder()">
    分配订单
  </button>
}
```

## 5. 关键交互设计

### 5.1 表单验证交互

**实时验证**：
```typescript
// 失焦时触发验证
<input
  formControlName="orderAmount"
  (blur)="orderCreationForm.get('orderAmount')?.markAsTouched()"
  ...>

// 错误信息显示
@if (orderCreationForm.get('orderAmount')?.invalid &&
      orderCreationForm.get('orderAmount')?.touched) {
  <div class="error-message">
    @if (orderCreationForm.get('orderAmount')?.errors?.['required']) {
      订单金额不能为空
    }
    @if (orderCreationForm.get('orderAmount')?.errors?.['min']) {
      订单金额不能小于0
    }
  </div>
}
```

**提交验证**：
```typescript
createOrder(): void {
  if (!this.canCreateOrder()) {
    // 标记所有字段为已触摸，显示所有验证错误
    this.orderCreationForm.markAllAsTouched();

    // 滚动到第一个错误字段
    const firstError = document.querySelector('.error-message');
    if (firstError) {
      firstError.scrollIntoView({ behavior: 'smooth', block: 'center' });
    }
    return;
  }
  // ... 继续提交逻辑
}
```

### 5.2 按钮状态控制

**分配订单按钮状态**：
```typescript
canCreateOrder(): boolean {
  // 检查必填表单是否有效
  const formValid = this.orderCreationForm?.valid;

  // 检查是否已选择设计师
  const designerAssigned = this.designerAssignmentData?.selectedDesigners?.length > 0;

  // 检查是否已填写报价明细
  const quotationFilled = this.quotationData?.items?.length > 0 && this.quotationData?.totalAmount > 0;

  return formValid && designerAssigned && quotationFilled;
}
```

**按钮样式**：
```html
<button
  class="btn-primary"
  [class.disabled]="!canCreateOrder()"
  [disabled]="!canCreateOrder()"
  (click)="createOrder()">
  @if (isSubmitting) {
    <span class="spinner"></span>
    分配中...
  } @else {
    分配订单
  }
</button>
```

### 5.3 阶段推进动画

**滚动动画**：
```typescript
// project-detail.ts lines 2253-2260
scrollToStage(stage: ProjectStage): void {
  const anchor = this.stageToAnchor(stage);
  const el = document.getElementById(anchor);
  if (el) {
    el.scrollIntoView({ behavior: 'smooth', block: 'start' });
  }
}
```

**展开动画**：
```scss
.stage-content {
  transition: max-height 0.3s ease-in-out, opacity 0.2s ease-in-out;

  &.collapsed {
    max-height: 0;
    opacity: 0;
    overflow: hidden;
  }

  &.expanded {
    max-height: 5000px;
    opacity: 1;
  }
}
```

## 6. 组件依赖关系

### 6.1 父子组件通信

```mermaid
graph TD
    A[ProjectDetail 父组件] --> B[QuotationDetailsComponent]
    A --> C[DesignerAssignmentComponent]
    A --> D[DesignerCalendarComponent]

    B -->|dataChange| A
    C -->|assignmentChange| A
    C -->|designerClick| A
    D -->|designerSelected| A
    D -->|assignmentRequested| A
```

### 6.2 Input/Output 接口

**QuotationDetailsComponent**：
```typescript
@Component({
  selector: 'app-quotation-details',
  ...
})
export class QuotationDetailsComponent {
  @Input() initialData?: QuotationData;
  @Input() readonly: boolean = false;
  @Output() dataChange = new EventEmitter<QuotationData>();

  // 当报价数据变化时触发
  onDataChange(): void {
    this.dataChange.emit(this.quotationData);
  }
}
```

**DesignerAssignmentComponent**：
```typescript
@Component({
  selector: 'app-designer-assignment',
  ...
})
export class DesignerAssignmentComponent {
  @Input() projectData?: any;
  @Input() readonly: boolean = false;
  @Output() assignmentChange = new EventEmitter<DesignerAssignmentData>();
  @Output() designerClick = new EventEmitter<Designer>();

  // 当设计师分配变化时触发
  onAssignmentChange(): void {
    this.assignmentChange.emit(this.assignmentData);
  }

  // 当点击设计师卡片时触发
  onDesignerCardClick(designer: Designer): void {
    this.designerClick.emit(designer);
  }
}
```

**父组件处理**：
```typescript
// project-detail.ts lines 2843-2873
onQuotationDataChange(data: QuotationData): void {
  this.quotationData = { ...data };
  this.orderAmount = data.totalAmount || 0;
}

onDesignerAssignmentChange(data: DesignerAssignmentData): void {
  this.designerAssignmentData = { ...data };
}

onDesignerClick(designer: AssignmentDesigner): void {
  // 映射为日历组件需要的数据格式
  const mapped = this.mapAssignmentDesignerToCalendar(designer);
  this.calendarDesigners = [mapped];
  this.calendarGroups = [{
    id: designer.teamId,
    name: designer.teamName,
    leaderId: designer.id,
    memberIds: [designer.id]
  }];
  this.selectedCalendarDate = new Date();
  this.showDesignerCalendar = true;
}
```

## 7. API集成

### 7.1 项目创建接口

**接口地址**：`POST /api/projects`

**请求参数**：
```typescript
interface CreateProjectRequest {
  // 客户信息
  customerId: string;
  customerName: string;
  customerPhone: string;
  customerWechat?: string;
  customerType: string;
  customerSource: string;
  customerRemark?: string;

  // 订单信息
  orderAmount: number;
  smallImageDeliveryTime: Date;
  largeImageDeliveryTime?: Date;
  decorationType: string;
  requirementReason: string;
  isMultiDesigner: boolean;

  // 空间需求（可选）
  spaceRequirements?: string;
  designAngles?: string;
  specialAreaHandling?: string;
  materialRequirements?: string;
  lightingRequirements?: string;

  // 报价明细
  quotation: {
    items: Array<{
      room: string;
      amount: number;
      description?: string;
    }>;
    totalAmount: number;
    materialCost: number;
    laborCost: number;
    designFee: number;
    managementFee: number;
  };

  // 设计师分配
  assignment: {
    designerIds: string[];
    teamId: string;
    leaderId: string;
    assignmentDate: Date;
    expectedStartDate: Date;
  };

  // 偏好标签
  preferenceTags?: string[];
}
```

**响应数据**：
```typescript
interface CreateProjectResponse {
  success: boolean;
  message: string;
  projectId: string;
  project: {
    id: string;
    name: string;
    currentStage: ProjectStage;
    createdAt: Date;
    assignedDesigners: string[];
  };
}
```

### 7.2 设计师列表接口

**接口地址**：`GET /api/designers/available`

**查询参数**：
```typescript
interface DesignerQueryParams {
  projectType?: string;      // 项目类型，用于技能匹配
  requiredSkills?: string[]; // 必需技能
  startDate?: Date;          // 项目预计开始日期
  teamId?: string;           // 指定团队ID
  sortBy?: 'skillMatch' | 'workload' | 'idleDays'; // 排序方式
}
```

**响应数据**：
```typescript
interface DesignerListResponse {
  success: boolean;
  designers: Designer[];
  recommendedDesignerId?: string; // AI推荐的最佳设计师
}
```

## 8. 异常处理

### 8.1 表单验证失败
```typescript
createOrder(): void {
  if (!this.canCreateOrder()) {
    // 1. 标记所有字段为已触摸
    this.orderCreationForm.markAllAsTouched();

    // 2. 收集所有错误信息
    const errors: string[] = [];
    Object.keys(this.orderCreationForm.controls).forEach(key => {
      const control = this.orderCreationForm.get(key);
      if (control?.invalid) {
        errors.push(this.getFieldLabel(key));
      }
    });

    // 3. 显示错误提示
    alert(`请完善以下必填项：\n${errors.join('\n')}`);

    // 4. 滚动到第一个错误字段
    const firstError = document.querySelector('.error-message');
    firstError?.scrollIntoView({ behavior: 'smooth', block: 'center' });

    return;
  }
  // ... 继续提交
}
```

### 8.2 API调用失败
```typescript
this.projectService.createProject(orderData).subscribe({
  next: (result) => {
    if (result.success) {
      alert('订单分配成功！');
      this.advanceToNextStage('订单分配');
    } else {
      // 服务端返回失败
      alert(`订单分配失败：${result.message || '未知错误'}`);
    }
  },
  error: (error) => {
    // 网络错误或服务端异常
    console.error('订单分配失败:', error);

    let errorMessage = '订单分配失败，请重试';

    if (error.status === 400) {
      errorMessage = '请求参数有误，请检查表单填写';
    } else if (error.status === 401) {
      errorMessage = '未登录或登录已过期，请重新登录';
    } else if (error.status === 403) {
      errorMessage = '没有权限执行此操作';
    } else if (error.status === 500) {
      errorMessage = '服务器错误，请稍后重试';
    }

    alert(errorMessage);
  }
});
```

### 8.3 设计师资源不足
```typescript
onDesignerAssignmentChange(data: DesignerAssignmentData): void {
  if (!data.selectedDesigners || data.selectedDesigners.length === 0) {
    // 没有选择设计师
    this.showWarning('请至少选择一位设计师');
    return;
  }

  // 检查设计师是否可用
  const unavailableDesigners = data.selectedDesigners.filter(d => d.status === 'unavailable');
  if (unavailableDesigners.length > 0) {
    const names = unavailableDesigners.map(d => d.name).join('、');
    this.showWarning(`以下设计师当前不可用：${names}\n请重新选择`);
    return;
  }

  // 检查工作负荷
  const overloadedDesigners = data.selectedDesigners.filter(d => d.workload > 90);
  if (overloadedDesigners.length > 0) {
    const names = overloadedDesigners.map(d => d.name).join('、');
    const confirmed = confirm(`以下设计师工作负荷较高（>90%）：${names}\n确定要继续分配吗？`);
    if (!confirmed) {
      return;
    }
  }

  this.designerAssignmentData = { ...data };
}
```

### 8.4 数据同步失败
```typescript
// 客服端同步数据解析失败
this.route.queryParamMap.subscribe({
  next: (qp) => {
    const syncDataParam = qp.get('syncData');
    if (syncDataParam) {
      try {
        const syncData = JSON.parse(syncDataParam);
        // ... 同步逻辑
      } catch (error) {
        console.error('解析同步数据失败:', error);
        this.isSyncingCustomerInfo = false;

        // 显示错误提示
        alert('客户信息同步失败，请手动填写表单');

        // 回退到手动模式
        this.orderCreationMethod = 'manual';
      }
    }
  }
});
```

## 9. 性能优化

### 9.1 变更检测优化
```typescript
// 使用 OnPush 策略的子组件
@Component({
  selector: 'app-quotation-details',
  changeDetection: ChangeDetectionStrategy.OnPush,
  ...
})

// 父组件手动触发变更检测
onQuotationDataChange(data: QuotationData): void {
  this.quotationData = { ...data }; // 不可变更新
  this.orderAmount = data.totalAmount || 0;
  this.cdr.markForCheck(); // 标记需要检查
}
```

### 9.2 懒加载子组件
```typescript
// 只有展开订单分配阶段时才加载子组件
@if (expandedSection === 'order' || getSectionStatus('order') === 'active') {
  <div class="order-assignment-section">
    <app-quotation-details ...></app-quotation-details>
    <app-designer-assignment ...></app-designer-assignment>
  </div>
}
```

### 9.3 防抖处理
```typescript
// 客户搜索防抖
private searchDebounce = new Subject<string>();

ngOnInit(): void {
  this.searchDebounce
    .pipe(debounceTime(300), distinctUntilChanged())
    .subscribe(keyword => {
      this.performSearch(keyword);
    });
}

onSearchInputChange(keyword: string): void {
  this.searchDebounce.next(keyword);
}
```

## 10. 测试用例

### 10.1 单元测试

**表单验证测试**：
```typescript
describe('OrderCreationForm Validation', () => {
  it('should require orderAmount', () => {
    const control = component.orderCreationForm.get('orderAmount');
    control?.setValue('');
    expect(control?.valid).toBeFalsy();
    expect(control?.errors?.['required']).toBeTruthy();
  });

  it('should reject negative orderAmount', () => {
    const control = component.orderCreationForm.get('orderAmount');
    control?.setValue(-100);
    expect(control?.valid).toBeFalsy();
    expect(control?.errors?.['min']).toBeTruthy();
  });

  it('should accept valid orderAmount', () => {
    const control = component.orderCreationForm.get('orderAmount');
    control?.setValue(5000);
    expect(control?.valid).toBeTruthy();
  });
});
```

**权限控制测试**：
```typescript
describe('Permission Control', () => {
  it('should allow customer-service to edit order section', () => {
    component.roleContext = 'customer-service';
    expect(component.canEditSection('order')).toBeTruthy();
  });

  it('should not allow designer to edit order section', () => {
    component.roleContext = 'designer';
    expect(component.canEditSection('order')).toBeFalsy();
  });

  it('should allow team-leader to edit all sections', () => {
    component.roleContext = 'team-leader';
    expect(component.canEditSection('order')).toBeTruthy();
    expect(component.canEditSection('requirements')).toBeTruthy();
    expect(component.canEditSection('delivery')).toBeTruthy();
    expect(component.canEditSection('aftercare')).toBeTruthy();
  });
});
```

### 10.2 集成测试

**订单创建流程测试**：
```typescript
describe('Order Creation Flow', () => {
  it('should complete full order creation flow', async () => {
    // 1. 填写客户信息
    component.customerForm.patchValue({
      name: '测试客户',
      phone: '13800138000',
      customerType: '新客户'
    });

    // 2. 填写必填项
    component.orderCreationForm.patchValue({
      orderAmount: 10000,
      smallImageDeliveryTime: new Date(),
      decorationType: '全包',
      requirementReason: '新房装修'
    });

    // 3. 添加报价明细
    component.quotationData = {
      items: [{ id: '1', room: '客厅', amount: 5000 }],
      totalAmount: 10000,
      materialCost: 6000,
      laborCost: 3000,
      designFee: 1000,
      managementFee: 0
    };

    // 4. 分配设计师
    component.designerAssignmentData = {
      selectedDesigners: [mockDesigner],
      teamId: 'team1',
      teamName: '设计一组',
      leaderId: 'designer1',
      assignmentDate: new Date(),
      expectedStartDate: new Date()
    };

    // 5. 验证可以提交
    expect(component.canCreateOrder()).toBeTruthy();

    // 6. 提交订单
    spyOn(component.projectService, 'createProject').and.returnValue(
      of({ success: true, projectId: 'proj-001' })
    );

    component.createOrder();

    expect(component.projectService.createProject).toHaveBeenCalled();
  });
});
```

### 10.3 E2E测试

**完整用户流程测试**：
```typescript
describe('Order Assignment E2E', () => {
  it('should complete order assignment as customer-service', async () => {
    // 1. 客服登录
    await page.goto('/customer-service/login');
    await page.fill('#username', 'cs_user');
    await page.fill('#password', 'password');
    await page.click('button[type="submit"]');

    // 2. 创建咨询订单
    await page.goto('/customer-service/consultation-order/create');
    await page.fill('#customerName', '张三');
    await page.fill('#customerPhone', '13800138000');
    await page.click('button[text="创建订单"]');

    // 3. 跳转到项目详情页
    await page.waitForURL(/\/designer\/project-detail/);

    // 4. 填写订单分配表单
    await page.fill('#orderAmount', '10000');
    await page.fill('#smallImageDeliveryTime', '2025-11-01');
    await page.selectOption('#decorationType', '全包');
    await page.check('#requirementReason[value="新房装修"]');

    // 5. 添加报价明细
    await page.click('button[text="添加报价项"]');
    await page.fill('.quotation-item:last-child #room', '客厅');
    await page.fill('.quotation-item:last-child #amount', '5000');

    // 6. 选择设计师
    await page.click('.designer-card:first-child');

    // 7. 提交订单
    await page.click('button[text="分配订单"]');

    // 8. 验证成功跳转到需求沟通阶段
    await expect(page.locator('.stage-nav-item.active')).toContainText('确认需求');
  });
});
```

## 11. 附录

### 11.1 字段标签映射
```typescript
private fieldLabelMap: Record<string, string> = {
  'orderAmount': '订单金额',
  'smallImageDeliveryTime': '小图交付时间',
  'decorationType': '装修类型',
  'requirementReason': '需求原因',
  'isMultiDesigner': '多设计师协作',
  'largeImageDeliveryTime': '大图交付时间',
  'spaceRequirements': '空间需求',
  'designAngles': '设计角度',
  'specialAreaHandling': '特殊区域处理',
  'materialRequirements': '材质要求',
  'lightingRequirements': '光照需求'
};

private getFieldLabel(fieldName: string): string {
  return this.fieldLabelMap[fieldName] || fieldName;
}
```

### 11.2 阶段锚点映射
```typescript
// project-detail.ts lines 2237-2251
stageToAnchor(stage: ProjectStage): string {
  const map: Record<ProjectStage, string> = {
    '订单分配': 'order',
    '需求沟通': 'requirements-talk',
    '方案确认': 'proposal-confirm',
    '建模': 'modeling',
    '软装': 'softdecor',
    '渲染': 'render',
    '后期': 'postprocess',
    '尾款结算': 'settlement',
    '客户评价': 'customer-review',
    '投诉处理': 'complaint'
  };
  return `stage-${map[stage] || 'unknown'}`;
}
```

### 11.3 设计师状态颜色映射
```scss
.designer-card {
  &.status-idle {
    border-left: 4px solid #10b981; // 绿色 - 空闲
  }

  &.status-busy {
    border-left: 4px solid #f59e0b; // 橙色 - 繁忙
  }

  &.status-unavailable {
    border-left: 4px solid #9ca3af; // 灰色 - 不可用
    opacity: 0.6;
    cursor: not-allowed;
  }
}
```

---

**文档版本**：v1.0.0
**创建日期**：2025-10-16
**最后更新**：2025-10-16
**维护人**：产品团队
