|  | @@ -0,0 +1,1638 @@
 | 
											
												
													
														|  | 
 |  | +# 组长端订单审批功能实施方案
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +## 📅 创建日期
 | 
											
												
													
														|  | 
 |  | +2025-10-28
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +## 🎯 功能目标
 | 
											
												
													
														|  | 
 |  | +实现组长端对客服提交的订单分配进行审批的完整流程,包括审批入口、审批操作、状态流转和历史记录。
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +---
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +## 一、业务流程梳理
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +### 1.1 完整流程
 | 
											
												
													
														|  | 
 |  | +```
 | 
											
												
													
														|  | 
 |  | +客服端提交订单分配
 | 
											
												
													
														|  | 
 |  | +    ↓
 | 
											
												
													
														|  | 
 |  | +项目进入"待组长确认"状态 (currentStage: '订单分配', 附带 pendingApproval 标记)
 | 
											
												
													
														|  | 
 |  | +    ↓
 | 
											
												
													
														|  | 
 |  | +组长在工作台看到待审批项目
 | 
											
												
													
														|  | 
 |  | +    ↓
 | 
											
												
													
														|  | 
 |  | +组长点击进入项目详情页
 | 
											
												
													
														|  | 
 |  | +    ↓
 | 
											
												
													
														|  | 
 |  | +组长审核订单内容(报价、设计师分配、项目信息)
 | 
											
												
													
														|  | 
 |  | +    ↓
 | 
											
												
													
														|  | 
 |  | +组长做出决策:
 | 
											
												
													
														|  | 
 |  | +    - 通过审批 → 项目进入"确认需求"阶段
 | 
											
												
													
														|  | 
 |  | +    - 驳回审批 → 项目退回"订单分配"阶段,客服端显示驳回原因
 | 
											
												
													
														|  | 
 |  | +    ↓
 | 
											
												
													
														|  | 
 |  | +记录审批历史
 | 
											
												
													
														|  | 
 |  | +    ↓
 | 
											
												
													
														|  | 
 |  | +发送通知(可选)
 | 
											
												
													
														|  | 
 |  | +```
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +### 1.2 关键状态定义
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +#### Project 数据结构扩展
 | 
											
												
													
														|  | 
 |  | +```typescript
 | 
											
												
													
														|  | 
 |  | +Project {
 | 
											
												
													
														|  | 
 |  | +  currentStage: string,  // '订单分配' | '确认需求' | '方案确认' | ...
 | 
											
												
													
														|  | 
 |  | +  status: string,        // '待分配' | '进行中' | '已完成' | ...
 | 
											
												
													
														|  | 
 |  | +  data: {
 | 
											
												
													
														|  | 
 |  | +    // 新增审批相关字段
 | 
											
												
													
														|  | 
 |  | +    approvalStatus?: 'pending' | 'approved' | 'rejected',  // 当前审批状态
 | 
											
												
													
														|  | 
 |  | +    approvalHistory: ApprovalRecord[],  // 审批历史记录
 | 
											
												
													
														|  | 
 |  | +    pendingApprovalBy?: string,  // 待审批人角色 'team-leader'
 | 
											
												
													
														|  | 
 |  | +    lastRejectionReason?: string  // 最近一次驳回原因
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +// 审批记录接口
 | 
											
												
													
														|  | 
 |  | +interface ApprovalRecord {
 | 
											
												
													
														|  | 
 |  | +  stage: string;              // 审批阶段:'订单分配'
 | 
											
												
													
														|  | 
 |  | +  submitter: {                // 提交人信息
 | 
											
												
													
														|  | 
 |  | +    id: string;
 | 
											
												
													
														|  | 
 |  | +    name: string;
 | 
											
												
													
														|  | 
 |  | +    role: string;
 | 
											
												
													
														|  | 
 |  | +  };
 | 
											
												
													
														|  | 
 |  | +  submitTime: Date;           // 提交时间
 | 
											
												
													
														|  | 
 |  | +  status: 'pending' | 'approved' | 'rejected';  // 审批状态
 | 
											
												
													
														|  | 
 |  | +  approver?: {                // 审批人信息(通过/驳回后填写)
 | 
											
												
													
														|  | 
 |  | +    id: string;
 | 
											
												
													
														|  | 
 |  | +    name: string;
 | 
											
												
													
														|  | 
 |  | +    role: string;
 | 
											
												
													
														|  | 
 |  | +  };
 | 
											
												
													
														|  | 
 |  | +  approvalTime?: Date;        // 审批时间
 | 
											
												
													
														|  | 
 |  | +  reason?: string;            // 驳回原因
 | 
											
												
													
														|  | 
 |  | +  comment?: string;           // 审批备注
 | 
											
												
													
														|  | 
 |  | +  quotationTotal: number;     // 报价总额快照
 | 
											
												
													
														|  | 
 |  | +  teams: TeamSnapshot[];      // 团队分配快照
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +interface TeamSnapshot {
 | 
											
												
													
														|  | 
 |  | +  id: string;
 | 
											
												
													
														|  | 
 |  | +  name: string;
 | 
											
												
													
														|  | 
 |  | +  spaces: string[];  // 分配的空间
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +```
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +---
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +## 二、前端实现方案
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +### 2.1 组长工作台增强(dashboard.ts)
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +#### 2.1.1 待审批项目标识优化
 | 
											
												
													
														|  | 
 |  | +当前已有 `pendingApprovalProjects` 计算属性,需要调整筛选逻辑:
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +```typescript
 | 
											
												
													
														|  | 
 |  | +// 位置:src/app/pages/team-leader/dashboard/dashboard.ts
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +// 修改现有的 getter
 | 
											
												
													
														|  | 
 |  | +get pendingApprovalProjects(): Project[] {
 | 
											
												
													
														|  | 
 |  | +  return this.projects.filter(p => {
 | 
											
												
													
														|  | 
 |  | +    const stage = (p.currentStage || '').trim();
 | 
											
												
													
														|  | 
 |  | +    const approvalStatus = p.data?.approvalStatus;
 | 
											
												
													
														|  | 
 |  | +    
 | 
											
												
													
														|  | 
 |  | +    // 1. 阶段为"订单分配"且审批状态为 pending
 | 
											
												
													
														|  | 
 |  | +    // 2. 或者阶段为"待确认"/"待审批"
 | 
											
												
													
														|  | 
 |  | +    return (stage === '订单分配' && approvalStatus === 'pending') ||
 | 
											
												
													
														|  | 
 |  | +           stage === '待审批' || 
 | 
											
												
													
														|  | 
 |  | +           stage === '待确认';
 | 
											
												
													
														|  | 
 |  | +  });
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +```
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +#### 2.1.2 待审批项目卡片视觉增强
 | 
											
												
													
														|  | 
 |  | +在项目卡片上添加醒目的"待审批"标识:
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +```html
 | 
											
												
													
														|  | 
 |  | +<!-- 位置:src/app/pages/team-leader/dashboard/dashboard.html -->
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +<div class="project-card" 
 | 
											
												
													
														|  | 
 |  | +     [class.pending-approval]="isPendingApproval(project)">
 | 
											
												
													
														|  | 
 |  | +  
 | 
											
												
													
														|  | 
 |  | +  <!-- 添加审批徽章 -->
 | 
											
												
													
														|  | 
 |  | +  @if (isPendingApproval(project)) {
 | 
											
												
													
														|  | 
 |  | +    <div class="approval-badge">
 | 
											
												
													
														|  | 
 |  | +      <span class="badge-icon">📋</span>
 | 
											
												
													
														|  | 
 |  | +      <span class="badge-text">待审批</span>
 | 
											
												
													
														|  | 
 |  | +    </div>
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +  
 | 
											
												
													
														|  | 
 |  | +  <!-- 原有项目卡片内容 -->
 | 
											
												
													
														|  | 
 |  | +  <div class="project-card-header">
 | 
											
												
													
														|  | 
 |  | +    <h4>{{ project.name }}</h4>
 | 
											
												
													
														|  | 
 |  | +    <!-- ... -->
 | 
											
												
													
														|  | 
 |  | +  </div>
 | 
											
												
													
														|  | 
 |  | +</div>
 | 
											
												
													
														|  | 
 |  | +```
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +```typescript
 | 
											
												
													
														|  | 
 |  | +// dashboard.ts 中添加辅助方法
 | 
											
												
													
														|  | 
 |  | +isPendingApproval(project: Project): boolean {
 | 
											
												
													
														|  | 
 |  | +  return project.currentStage === '订单分配' && 
 | 
											
												
													
														|  | 
 |  | +         project.data?.approvalStatus === 'pending';
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +```
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +```scss
 | 
											
												
													
														|  | 
 |  | +// dashboard.scss 样式
 | 
											
												
													
														|  | 
 |  | +.project-card {
 | 
											
												
													
														|  | 
 |  | +  &.pending-approval {
 | 
											
												
													
														|  | 
 |  | +    border: 2px solid #ff9800;
 | 
											
												
													
														|  | 
 |  | +    box-shadow: 0 0 10px rgba(255, 152, 0, 0.3);
 | 
											
												
													
														|  | 
 |  | +    position: relative;
 | 
											
												
													
														|  | 
 |  | +    
 | 
											
												
													
														|  | 
 |  | +    .approval-badge {
 | 
											
												
													
														|  | 
 |  | +      position: absolute;
 | 
											
												
													
														|  | 
 |  | +      top: 10px;
 | 
											
												
													
														|  | 
 |  | +      right: 10px;
 | 
											
												
													
														|  | 
 |  | +      background: linear-gradient(135deg, #ff9800, #ff6b00);
 | 
											
												
													
														|  | 
 |  | +      color: white;
 | 
											
												
													
														|  | 
 |  | +      padding: 4px 12px;
 | 
											
												
													
														|  | 
 |  | +      border-radius: 20px;
 | 
											
												
													
														|  | 
 |  | +      font-size: 12px;
 | 
											
												
													
														|  | 
 |  | +      font-weight: bold;
 | 
											
												
													
														|  | 
 |  | +      display: flex;
 | 
											
												
													
														|  | 
 |  | +      align-items: center;
 | 
											
												
													
														|  | 
 |  | +      gap: 4px;
 | 
											
												
													
														|  | 
 |  | +      box-shadow: 0 2px 8px rgba(255, 152, 0, 0.4);
 | 
											
												
													
														|  | 
 |  | +      animation: pulse 2s ease-in-out infinite;
 | 
											
												
													
														|  | 
 |  | +      
 | 
											
												
													
														|  | 
 |  | +      .badge-icon {
 | 
											
												
													
														|  | 
 |  | +        font-size: 14px;
 | 
											
												
													
														|  | 
 |  | +      }
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +@keyframes pulse {
 | 
											
												
													
														|  | 
 |  | +  0%, 100% {
 | 
											
												
													
														|  | 
 |  | +    transform: scale(1);
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +  50% {
 | 
											
												
													
														|  | 
 |  | +    transform: scale(1.05);
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +```
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +### 2.2 项目详情页审批组件
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +#### 2.2.1 创建订单审批组件
 | 
											
												
													
														|  | 
 |  | +位置:`src/app/shared/components/order-approval-panel/`
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +```typescript
 | 
											
												
													
														|  | 
 |  | +// order-approval-panel.component.ts
 | 
											
												
													
														|  | 
 |  | +import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';
 | 
											
												
													
														|  | 
 |  | +import { CommonModule } from '@angular/common';
 | 
											
												
													
														|  | 
 |  | +import { FormsModule } from '@angular/forms';
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +interface ApprovalData {
 | 
											
												
													
														|  | 
 |  | +  projectId: string;
 | 
											
												
													
														|  | 
 |  | +  projectName: string;
 | 
											
												
													
														|  | 
 |  | +  quotationTotal: number;
 | 
											
												
													
														|  | 
 |  | +  assignedTeams: TeamInfo[];
 | 
											
												
													
														|  | 
 |  | +  projectInfo: {
 | 
											
												
													
														|  | 
 |  | +    title: string;
 | 
											
												
													
														|  | 
 |  | +    projectType: string;
 | 
											
												
													
														|  | 
 |  | +    demoday: Date;
 | 
											
												
													
														|  | 
 |  | +    deadline?: Date;
 | 
											
												
													
														|  | 
 |  | +  };
 | 
											
												
													
														|  | 
 |  | +  submitter: {
 | 
											
												
													
														|  | 
 |  | +    id: string;
 | 
											
												
													
														|  | 
 |  | +    name: string;
 | 
											
												
													
														|  | 
 |  | +    role: string;
 | 
											
												
													
														|  | 
 |  | +  };
 | 
											
												
													
														|  | 
 |  | +  submitTime: Date;
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +interface TeamInfo {
 | 
											
												
													
														|  | 
 |  | +  id: string;
 | 
											
												
													
														|  | 
 |  | +  name: string;
 | 
											
												
													
														|  | 
 |  | +  spaces: string[];
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +@Component({
 | 
											
												
													
														|  | 
 |  | +  selector: 'app-order-approval-panel',
 | 
											
												
													
														|  | 
 |  | +  standalone: true,
 | 
											
												
													
														|  | 
 |  | +  imports: [CommonModule, FormsModule],
 | 
											
												
													
														|  | 
 |  | +  templateUrl: './order-approval-panel.component.html',
 | 
											
												
													
														|  | 
 |  | +  styleUrls: ['./order-approval-panel.component.scss']
 | 
											
												
													
														|  | 
 |  | +})
 | 
											
												
													
														|  | 
 |  | +export class OrderApprovalPanelComponent implements OnInit {
 | 
											
												
													
														|  | 
 |  | +  @Input() project: any;  // Parse Project 对象
 | 
											
												
													
														|  | 
 |  | +  @Input() currentUser: any;  // 当前组长用户
 | 
											
												
													
														|  | 
 |  | +  @Output() approvalCompleted = new EventEmitter<{
 | 
											
												
													
														|  | 
 |  | +    action: 'approved' | 'rejected';
 | 
											
												
													
														|  | 
 |  | +    reason?: string;
 | 
											
												
													
														|  | 
 |  | +    comment?: string;
 | 
											
												
													
														|  | 
 |  | +  }>();
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +  approvalData: ApprovalData | null = null;
 | 
											
												
													
														|  | 
 |  | +  showRejectModal = false;
 | 
											
												
													
														|  | 
 |  | +  rejectReason = '';
 | 
											
												
													
														|  | 
 |  | +  approvalComment = '';
 | 
											
												
													
														|  | 
 |  | +  isSubmitting = false;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +  // 驳回原因快捷选项
 | 
											
												
													
														|  | 
 |  | +  rejectReasons = [
 | 
											
												
													
														|  | 
 |  | +    '报价不合理,需要调整',
 | 
											
												
													
														|  | 
 |  | +    '设计师分配不当',
 | 
											
												
													
														|  | 
 |  | +    '项目信息不完整',
 | 
											
												
													
														|  | 
 |  | +    '需要补充项目资料',
 | 
											
												
													
														|  | 
 |  | +    '其他原因(请在下方说明)'
 | 
											
												
													
														|  | 
 |  | +  ];
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +  selectedRejectReason = '';
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +  ngOnInit() {
 | 
											
												
													
														|  | 
 |  | +    this.loadApprovalData();
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +  /**
 | 
											
												
													
														|  | 
 |  | +   * 加载审批数据
 | 
											
												
													
														|  | 
 |  | +   */
 | 
											
												
													
														|  | 
 |  | +  private loadApprovalData() {
 | 
											
												
													
														|  | 
 |  | +    if (!this.project) return;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    const data = this.project.get('data') || {};
 | 
											
												
													
														|  | 
 |  | +    const approvalHistory = data.approvalHistory || [];
 | 
											
												
													
														|  | 
 |  | +    const latestRecord = approvalHistory[approvalHistory.length - 1];
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    this.approvalData = {
 | 
											
												
													
														|  | 
 |  | +      projectId: this.project.id,
 | 
											
												
													
														|  | 
 |  | +      projectName: this.project.get('title'),
 | 
											
												
													
														|  | 
 |  | +      quotationTotal: latestRecord?.quotationTotal || 0,
 | 
											
												
													
														|  | 
 |  | +      assignedTeams: latestRecord?.teams || [],
 | 
											
												
													
														|  | 
 |  | +      projectInfo: {
 | 
											
												
													
														|  | 
 |  | +        title: this.project.get('title'),
 | 
											
												
													
														|  | 
 |  | +        projectType: this.project.get('projectType'),
 | 
											
												
													
														|  | 
 |  | +        demoday: this.project.get('demoday'),
 | 
											
												
													
														|  | 
 |  | +        deadline: this.project.get('deadline')
 | 
											
												
													
														|  | 
 |  | +      },
 | 
											
												
													
														|  | 
 |  | +      submitter: latestRecord?.submitter || {},
 | 
											
												
													
														|  | 
 |  | +      submitTime: latestRecord?.submitTime || new Date()
 | 
											
												
													
														|  | 
 |  | +    };
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +  /**
 | 
											
												
													
														|  | 
 |  | +   * 通过审批
 | 
											
												
													
														|  | 
 |  | +   */
 | 
											
												
													
														|  | 
 |  | +  async approveOrder() {
 | 
											
												
													
														|  | 
 |  | +    if (this.isSubmitting) return;
 | 
											
												
													
														|  | 
 |  | +    
 | 
											
												
													
														|  | 
 |  | +    const confirmed = confirm('确认通过此订单审批吗?');
 | 
											
												
													
														|  | 
 |  | +    if (!confirmed) return;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    this.isSubmitting = true;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    try {
 | 
											
												
													
														|  | 
 |  | +      this.approvalCompleted.emit({
 | 
											
												
													
														|  | 
 |  | +        action: 'approved',
 | 
											
												
													
														|  | 
 |  | +        comment: this.approvalComment || undefined
 | 
											
												
													
														|  | 
 |  | +      });
 | 
											
												
													
														|  | 
 |  | +    } finally {
 | 
											
												
													
														|  | 
 |  | +      this.isSubmitting = false;
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +  /**
 | 
											
												
													
														|  | 
 |  | +   * 打开驳回弹窗
 | 
											
												
													
														|  | 
 |  | +   */
 | 
											
												
													
														|  | 
 |  | +  openRejectModal() {
 | 
											
												
													
														|  | 
 |  | +    this.showRejectModal = true;
 | 
											
												
													
														|  | 
 |  | +    this.rejectReason = '';
 | 
											
												
													
														|  | 
 |  | +    this.selectedRejectReason = '';
 | 
											
												
													
														|  | 
 |  | +    this.approvalComment = '';
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +  /**
 | 
											
												
													
														|  | 
 |  | +   * 关闭驳回弹窗
 | 
											
												
													
														|  | 
 |  | +   */
 | 
											
												
													
														|  | 
 |  | +  closeRejectModal() {
 | 
											
												
													
														|  | 
 |  | +    this.showRejectModal = false;
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +  /**
 | 
											
												
													
														|  | 
 |  | +   * 选择驳回原因
 | 
											
												
													
														|  | 
 |  | +   */
 | 
											
												
													
														|  | 
 |  | +  selectRejectReason(reason: string) {
 | 
											
												
													
														|  | 
 |  | +    this.selectedRejectReason = reason;
 | 
											
												
													
														|  | 
 |  | +    if (reason !== '其他原因(请在下方说明)') {
 | 
											
												
													
														|  | 
 |  | +      this.rejectReason = reason;
 | 
											
												
													
														|  | 
 |  | +    } else {
 | 
											
												
													
														|  | 
 |  | +      this.rejectReason = '';
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +  /**
 | 
											
												
													
														|  | 
 |  | +   * 提交驳回
 | 
											
												
													
														|  | 
 |  | +   */
 | 
											
												
													
														|  | 
 |  | +  async submitRejection() {
 | 
											
												
													
														|  | 
 |  | +    const finalReason = this.selectedRejectReason === '其他原因(请在下方说明)' 
 | 
											
												
													
														|  | 
 |  | +      ? this.rejectReason 
 | 
											
												
													
														|  | 
 |  | +      : this.selectedRejectReason;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    if (!finalReason || !finalReason.trim()) {
 | 
											
												
													
														|  | 
 |  | +      alert('请填写驳回原因');
 | 
											
												
													
														|  | 
 |  | +      return;
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    if (this.isSubmitting) return;
 | 
											
												
													
														|  | 
 |  | +    this.isSubmitting = true;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    try {
 | 
											
												
													
														|  | 
 |  | +      this.approvalCompleted.emit({
 | 
											
												
													
														|  | 
 |  | +        action: 'rejected',
 | 
											
												
													
														|  | 
 |  | +        reason: finalReason,
 | 
											
												
													
														|  | 
 |  | +        comment: this.approvalComment || undefined
 | 
											
												
													
														|  | 
 |  | +      });
 | 
											
												
													
														|  | 
 |  | +      this.closeRejectModal();
 | 
											
												
													
														|  | 
 |  | +    } finally {
 | 
											
												
													
														|  | 
 |  | +      this.isSubmitting = false;
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +  /**
 | 
											
												
													
														|  | 
 |  | +   * 格式化金额
 | 
											
												
													
														|  | 
 |  | +   */
 | 
											
												
													
														|  | 
 |  | +  formatCurrency(amount: number): string {
 | 
											
												
													
														|  | 
 |  | +    return `¥${amount.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +```
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +```html
 | 
											
												
													
														|  | 
 |  | +<!-- order-approval-panel.component.html -->
 | 
											
												
													
														|  | 
 |  | +<div class="order-approval-panel" *ngIf="approvalData">
 | 
											
												
													
														|  | 
 |  | +  <!-- 审批状态头部 -->
 | 
											
												
													
														|  | 
 |  | +  <div class="approval-header">
 | 
											
												
													
														|  | 
 |  | +    <div class="header-icon">📋</div>
 | 
											
												
													
														|  | 
 |  | +    <div class="header-content">
 | 
											
												
													
														|  | 
 |  | +      <h2>订单审批</h2>
 | 
											
												
													
														|  | 
 |  | +      <p class="subtitle">请仔细审核以下订单信息</p>
 | 
											
												
													
														|  | 
 |  | +    </div>
 | 
											
												
													
														|  | 
 |  | +  </div>
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +  <!-- 审批信息卡片 -->
 | 
											
												
													
														|  | 
 |  | +  <div class="approval-info-cards">
 | 
											
												
													
														|  | 
 |  | +    <!-- 项目信息卡片 -->
 | 
											
												
													
														|  | 
 |  | +    <div class="info-card">
 | 
											
												
													
														|  | 
 |  | +      <div class="card-header">
 | 
											
												
													
														|  | 
 |  | +        <span class="card-icon">📄</span>
 | 
											
												
													
														|  | 
 |  | +        <h3>项目信息</h3>
 | 
											
												
													
														|  | 
 |  | +      </div>
 | 
											
												
													
														|  | 
 |  | +      <div class="card-body">
 | 
											
												
													
														|  | 
 |  | +        <div class="info-row">
 | 
											
												
													
														|  | 
 |  | +          <span class="label">项目名称:</span>
 | 
											
												
													
														|  | 
 |  | +          <span class="value">{{ approvalData.projectInfo.title }}</span>
 | 
											
												
													
														|  | 
 |  | +        </div>
 | 
											
												
													
														|  | 
 |  | +        <div class="info-row">
 | 
											
												
													
														|  | 
 |  | +          <span class="label">项目类型:</span>
 | 
											
												
													
														|  | 
 |  | +          <span class="value">{{ approvalData.projectInfo.projectType }}</span>
 | 
											
												
													
														|  | 
 |  | +        </div>
 | 
											
												
													
														|  | 
 |  | +        <div class="info-row">
 | 
											
												
													
														|  | 
 |  | +          <span class="label">小图日期:</span>
 | 
											
												
													
														|  | 
 |  | +          <span class="value">{{ approvalData.projectInfo.demoday | date:'yyyy-MM-dd' }}</span>
 | 
											
												
													
														|  | 
 |  | +        </div>
 | 
											
												
													
														|  | 
 |  | +        <div class="info-row" *ngIf="approvalData.projectInfo.deadline">
 | 
											
												
													
														|  | 
 |  | +          <span class="label">交付期限:</span>
 | 
											
												
													
														|  | 
 |  | +          <span class="value">{{ approvalData.projectInfo.deadline | date:'yyyy-MM-dd' }}</span>
 | 
											
												
													
														|  | 
 |  | +        </div>
 | 
											
												
													
														|  | 
 |  | +      </div>
 | 
											
												
													
														|  | 
 |  | +    </div>
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    <!-- 报价信息卡片 -->
 | 
											
												
													
														|  | 
 |  | +    <div class="info-card highlight">
 | 
											
												
													
														|  | 
 |  | +      <div class="card-header">
 | 
											
												
													
														|  | 
 |  | +        <span class="card-icon">💰</span>
 | 
											
												
													
														|  | 
 |  | +        <h3>报价总额</h3>
 | 
											
												
													
														|  | 
 |  | +      </div>
 | 
											
												
													
														|  | 
 |  | +      <div class="card-body">
 | 
											
												
													
														|  | 
 |  | +        <div class="quotation-amount">
 | 
											
												
													
														|  | 
 |  | +          {{ formatCurrency(approvalData.quotationTotal) }}
 | 
											
												
													
														|  | 
 |  | +        </div>
 | 
											
												
													
														|  | 
 |  | +      </div>
 | 
											
												
													
														|  | 
 |  | +    </div>
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    <!-- 设计师分配卡片 -->
 | 
											
												
													
														|  | 
 |  | +    <div class="info-card">
 | 
											
												
													
														|  | 
 |  | +      <div class="card-header">
 | 
											
												
													
														|  | 
 |  | +        <span class="card-icon">👥</span>
 | 
											
												
													
														|  | 
 |  | +        <h3>设计师分配</h3>
 | 
											
												
													
														|  | 
 |  | +      </div>
 | 
											
												
													
														|  | 
 |  | +      <div class="card-body">
 | 
											
												
													
														|  | 
 |  | +        <div class="team-list">
 | 
											
												
													
														|  | 
 |  | +          <div class="team-item" *ngFor="let team of approvalData.assignedTeams">
 | 
											
												
													
														|  | 
 |  | +            <div class="team-name">{{ team.name }}</div>
 | 
											
												
													
														|  | 
 |  | +            <div class="team-spaces">
 | 
											
												
													
														|  | 
 |  | +              <span class="space-tag" *ngFor="let space of team.spaces">{{ space }}</span>
 | 
											
												
													
														|  | 
 |  | +            </div>
 | 
											
												
													
														|  | 
 |  | +          </div>
 | 
											
												
													
														|  | 
 |  | +          <div class="empty-state" *ngIf="approvalData.assignedTeams.length === 0">
 | 
											
												
													
														|  | 
 |  | +            <span class="empty-icon">📦</span>
 | 
											
												
													
														|  | 
 |  | +            <p>暂无分配设计师</p>
 | 
											
												
													
														|  | 
 |  | +          </div>
 | 
											
												
													
														|  | 
 |  | +        </div>
 | 
											
												
													
														|  | 
 |  | +      </div>
 | 
											
												
													
														|  | 
 |  | +    </div>
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    <!-- 提交信息卡片 -->
 | 
											
												
													
														|  | 
 |  | +    <div class="info-card">
 | 
											
												
													
														|  | 
 |  | +      <div class="card-header">
 | 
											
												
													
														|  | 
 |  | +        <span class="card-icon">👤</span>
 | 
											
												
													
														|  | 
 |  | +        <h3>提交信息</h3>
 | 
											
												
													
														|  | 
 |  | +      </div>
 | 
											
												
													
														|  | 
 |  | +      <div class="card-body">
 | 
											
												
													
														|  | 
 |  | +        <div class="info-row">
 | 
											
												
													
														|  | 
 |  | +          <span class="label">提交人:</span>
 | 
											
												
													
														|  | 
 |  | +          <span class="value">{{ approvalData.submitter.name }} ({{ approvalData.submitter.role }})</span>
 | 
											
												
													
														|  | 
 |  | +        </div>
 | 
											
												
													
														|  | 
 |  | +        <div class="info-row">
 | 
											
												
													
														|  | 
 |  | +          <span class="label">提交时间:</span>
 | 
											
												
													
														|  | 
 |  | +          <span class="value">{{ approvalData.submitTime | date:'yyyy-MM-dd HH:mm' }}</span>
 | 
											
												
													
														|  | 
 |  | +        </div>
 | 
											
												
													
														|  | 
 |  | +      </div>
 | 
											
												
													
														|  | 
 |  | +    </div>
 | 
											
												
													
														|  | 
 |  | +  </div>
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +  <!-- 审批备注 -->
 | 
											
												
													
														|  | 
 |  | +  <div class="approval-comment-section">
 | 
											
												
													
														|  | 
 |  | +    <label for="approvalComment">审批备注(可选)</label>
 | 
											
												
													
														|  | 
 |  | +    <textarea 
 | 
											
												
													
														|  | 
 |  | +      id="approvalComment"
 | 
											
												
													
														|  | 
 |  | +      [(ngModel)]="approvalComment"
 | 
											
												
													
														|  | 
 |  | +      placeholder="可以填写审批意见或建议..."
 | 
											
												
													
														|  | 
 |  | +      rows="3"></textarea>
 | 
											
												
													
														|  | 
 |  | +  </div>
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +  <!-- 审批操作按钮 -->
 | 
											
												
													
														|  | 
 |  | +  <div class="approval-actions">
 | 
											
												
													
														|  | 
 |  | +    <button 
 | 
											
												
													
														|  | 
 |  | +      class="btn-reject" 
 | 
											
												
													
														|  | 
 |  | +      (click)="openRejectModal()"
 | 
											
												
													
														|  | 
 |  | +      [disabled]="isSubmitting">
 | 
											
												
													
														|  | 
 |  | +      <span class="btn-icon">❌</span>
 | 
											
												
													
														|  | 
 |  | +      驳回订单
 | 
											
												
													
														|  | 
 |  | +    </button>
 | 
											
												
													
														|  | 
 |  | +    <button 
 | 
											
												
													
														|  | 
 |  | +      class="btn-approve" 
 | 
											
												
													
														|  | 
 |  | +      (click)="approveOrder()"
 | 
											
												
													
														|  | 
 |  | +      [disabled]="isSubmitting">
 | 
											
												
													
														|  | 
 |  | +      <span class="btn-icon">✅</span>
 | 
											
												
													
														|  | 
 |  | +      通过审批
 | 
											
												
													
														|  | 
 |  | +    </button>
 | 
											
												
													
														|  | 
 |  | +  </div>
 | 
											
												
													
														|  | 
 |  | +</div>
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +<!-- 驳回弹窗 -->
 | 
											
												
													
														|  | 
 |  | +<div class="reject-modal-overlay" *ngIf="showRejectModal" (click)="closeRejectModal()">
 | 
											
												
													
														|  | 
 |  | +  <div class="reject-modal" (click)="$event.stopPropagation()">
 | 
											
												
													
														|  | 
 |  | +    <div class="modal-header">
 | 
											
												
													
														|  | 
 |  | +      <h3>驳回订单</h3>
 | 
											
												
													
														|  | 
 |  | +      <button class="close-btn" (click)="closeRejectModal()">×</button>
 | 
											
												
													
														|  | 
 |  | +    </div>
 | 
											
												
													
														|  | 
 |  | +    
 | 
											
												
													
														|  | 
 |  | +    <div class="modal-body">
 | 
											
												
													
														|  | 
 |  | +      <div class="form-group">
 | 
											
												
													
														|  | 
 |  | +        <label>驳回原因 <span class="required">*</span></label>
 | 
											
												
													
														|  | 
 |  | +        <div class="reason-options">
 | 
											
												
													
														|  | 
 |  | +          <label 
 | 
											
												
													
														|  | 
 |  | +            class="reason-option" 
 | 
											
												
													
														|  | 
 |  | +            *ngFor="let reason of rejectReasons"
 | 
											
												
													
														|  | 
 |  | +            [class.selected]="selectedRejectReason === reason">
 | 
											
												
													
														|  | 
 |  | +            <input 
 | 
											
												
													
														|  | 
 |  | +              type="radio" 
 | 
											
												
													
														|  | 
 |  | +              name="rejectReason" 
 | 
											
												
													
														|  | 
 |  | +              [value]="reason"
 | 
											
												
													
														|  | 
 |  | +              [(ngModel)]="selectedRejectReason"
 | 
											
												
													
														|  | 
 |  | +              (change)="selectRejectReason(reason)">
 | 
											
												
													
														|  | 
 |  | +            <span>{{ reason }}</span>
 | 
											
												
													
														|  | 
 |  | +          </label>
 | 
											
												
													
														|  | 
 |  | +        </div>
 | 
											
												
													
														|  | 
 |  | +      </div>
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +      <div class="form-group" *ngIf="selectedRejectReason === '其他原因(请在下方说明)'">
 | 
											
												
													
														|  | 
 |  | +        <label>详细说明 <span class="required">*</span></label>
 | 
											
												
													
														|  | 
 |  | +        <textarea 
 | 
											
												
													
														|  | 
 |  | +          [(ngModel)]="rejectReason"
 | 
											
												
													
														|  | 
 |  | +          placeholder="请详细说明驳回原因..."
 | 
											
												
													
														|  | 
 |  | +          rows="4"></textarea>
 | 
											
												
													
														|  | 
 |  | +      </div>
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +      <div class="form-group">
 | 
											
												
													
														|  | 
 |  | +        <label>补充说明(可选)</label>
 | 
											
												
													
														|  | 
 |  | +        <textarea 
 | 
											
												
													
														|  | 
 |  | +          [(ngModel)]="approvalComment"
 | 
											
												
													
														|  | 
 |  | +          placeholder="可以补充其他建议或要求..."
 | 
											
												
													
														|  | 
 |  | +          rows="3"></textarea>
 | 
											
												
													
														|  | 
 |  | +      </div>
 | 
											
												
													
														|  | 
 |  | +    </div>
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    <div class="modal-footer">
 | 
											
												
													
														|  | 
 |  | +      <button class="btn-cancel" (click)="closeRejectModal()">取消</button>
 | 
											
												
													
														|  | 
 |  | +      <button 
 | 
											
												
													
														|  | 
 |  | +        class="btn-submit" 
 | 
											
												
													
														|  | 
 |  | +        (click)="submitRejection()"
 | 
											
												
													
														|  | 
 |  | +        [disabled]="isSubmitting">
 | 
											
												
													
														|  | 
 |  | +        确认驳回
 | 
											
												
													
														|  | 
 |  | +      </button>
 | 
											
												
													
														|  | 
 |  | +    </div>
 | 
											
												
													
														|  | 
 |  | +  </div>
 | 
											
												
													
														|  | 
 |  | +</div>
 | 
											
												
													
														|  | 
 |  | +```
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +```scss
 | 
											
												
													
														|  | 
 |  | +// order-approval-panel.component.scss
 | 
											
												
													
														|  | 
 |  | +.order-approval-panel {
 | 
											
												
													
														|  | 
 |  | +  background: #fff;
 | 
											
												
													
														|  | 
 |  | +  border-radius: 12px;
 | 
											
												
													
														|  | 
 |  | +  padding: 24px;
 | 
											
												
													
														|  | 
 |  | +  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
 | 
											
												
													
														|  | 
 |  | +  margin-bottom: 24px;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +  .approval-header {
 | 
											
												
													
														|  | 
 |  | +    display: flex;
 | 
											
												
													
														|  | 
 |  | +    align-items: center;
 | 
											
												
													
														|  | 
 |  | +    gap: 16px;
 | 
											
												
													
														|  | 
 |  | +    padding-bottom: 20px;
 | 
											
												
													
														|  | 
 |  | +    border-bottom: 2px solid #f0f0f0;
 | 
											
												
													
														|  | 
 |  | +    margin-bottom: 24px;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    .header-icon {
 | 
											
												
													
														|  | 
 |  | +      font-size: 48px;
 | 
											
												
													
														|  | 
 |  | +      animation: float 3s ease-in-out infinite;
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    .header-content {
 | 
											
												
													
														|  | 
 |  | +      h2 {
 | 
											
												
													
														|  | 
 |  | +        margin: 0;
 | 
											
												
													
														|  | 
 |  | +        font-size: 24px;
 | 
											
												
													
														|  | 
 |  | +        color: #333;
 | 
											
												
													
														|  | 
 |  | +      }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +      .subtitle {
 | 
											
												
													
														|  | 
 |  | +        margin: 4px 0 0;
 | 
											
												
													
														|  | 
 |  | +        color: #666;
 | 
											
												
													
														|  | 
 |  | +        font-size: 14px;
 | 
											
												
													
														|  | 
 |  | +      }
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +  .approval-info-cards {
 | 
											
												
													
														|  | 
 |  | +    display: grid;
 | 
											
												
													
														|  | 
 |  | +    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
 | 
											
												
													
														|  | 
 |  | +    gap: 16px;
 | 
											
												
													
														|  | 
 |  | +    margin-bottom: 24px;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    .info-card {
 | 
											
												
													
														|  | 
 |  | +      background: #f8f9fa;
 | 
											
												
													
														|  | 
 |  | +      border-radius: 8px;
 | 
											
												
													
														|  | 
 |  | +      padding: 16px;
 | 
											
												
													
														|  | 
 |  | +      border: 1px solid #e9ecef;
 | 
											
												
													
														|  | 
 |  | +      transition: all 0.3s;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +      &:hover {
 | 
											
												
													
														|  | 
 |  | +        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
 | 
											
												
													
														|  | 
 |  | +        transform: translateY(-2px);
 | 
											
												
													
														|  | 
 |  | +      }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +      &.highlight {
 | 
											
												
													
														|  | 
 |  | +        background: linear-gradient(135deg, #fff3e0, #ffe0b2);
 | 
											
												
													
														|  | 
 |  | +        border-color: #ff9800;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +        .quotation-amount {
 | 
											
												
													
														|  | 
 |  | +          font-size: 32px;
 | 
											
												
													
														|  | 
 |  | +          font-weight: bold;
 | 
											
												
													
														|  | 
 |  | +          color: #f57c00;
 | 
											
												
													
														|  | 
 |  | +          text-align: center;
 | 
											
												
													
														|  | 
 |  | +          padding: 16px 0;
 | 
											
												
													
														|  | 
 |  | +        }
 | 
											
												
													
														|  | 
 |  | +      }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +      .card-header {
 | 
											
												
													
														|  | 
 |  | +        display: flex;
 | 
											
												
													
														|  | 
 |  | +        align-items: center;
 | 
											
												
													
														|  | 
 |  | +        gap: 8px;
 | 
											
												
													
														|  | 
 |  | +        margin-bottom: 12px;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +        .card-icon {
 | 
											
												
													
														|  | 
 |  | +          font-size: 24px;
 | 
											
												
													
														|  | 
 |  | +        }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +        h3 {
 | 
											
												
													
														|  | 
 |  | +          margin: 0;
 | 
											
												
													
														|  | 
 |  | +          font-size: 16px;
 | 
											
												
													
														|  | 
 |  | +          color: #333;
 | 
											
												
													
														|  | 
 |  | +        }
 | 
											
												
													
														|  | 
 |  | +      }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +      .card-body {
 | 
											
												
													
														|  | 
 |  | +        .info-row {
 | 
											
												
													
														|  | 
 |  | +          display: flex;
 | 
											
												
													
														|  | 
 |  | +          justify-content: space-between;
 | 
											
												
													
														|  | 
 |  | +          padding: 8px 0;
 | 
											
												
													
														|  | 
 |  | +          border-bottom: 1px solid #e9ecef;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +          &:last-child {
 | 
											
												
													
														|  | 
 |  | +            border-bottom: none;
 | 
											
												
													
														|  | 
 |  | +          }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +          .label {
 | 
											
												
													
														|  | 
 |  | +            color: #666;
 | 
											
												
													
														|  | 
 |  | +            font-size: 14px;
 | 
											
												
													
														|  | 
 |  | +          }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +          .value {
 | 
											
												
													
														|  | 
 |  | +            color: #333;
 | 
											
												
													
														|  | 
 |  | +            font-weight: 500;
 | 
											
												
													
														|  | 
 |  | +            font-size: 14px;
 | 
											
												
													
														|  | 
 |  | +          }
 | 
											
												
													
														|  | 
 |  | +        }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +        .team-list {
 | 
											
												
													
														|  | 
 |  | +          .team-item {
 | 
											
												
													
														|  | 
 |  | +            padding: 12px;
 | 
											
												
													
														|  | 
 |  | +            background: white;
 | 
											
												
													
														|  | 
 |  | +            border-radius: 6px;
 | 
											
												
													
														|  | 
 |  | +            margin-bottom: 8px;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +            &:last-child {
 | 
											
												
													
														|  | 
 |  | +              margin-bottom: 0;
 | 
											
												
													
														|  | 
 |  | +            }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +            .team-name {
 | 
											
												
													
														|  | 
 |  | +              font-weight: 500;
 | 
											
												
													
														|  | 
 |  | +              color: #333;
 | 
											
												
													
														|  | 
 |  | +              margin-bottom: 8px;
 | 
											
												
													
														|  | 
 |  | +            }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +            .team-spaces {
 | 
											
												
													
														|  | 
 |  | +              display: flex;
 | 
											
												
													
														|  | 
 |  | +              flex-wrap: wrap;
 | 
											
												
													
														|  | 
 |  | +              gap: 6px;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +              .space-tag {
 | 
											
												
													
														|  | 
 |  | +                background: #e3f2fd;
 | 
											
												
													
														|  | 
 |  | +                color: #1976d2;
 | 
											
												
													
														|  | 
 |  | +                padding: 4px 12px;
 | 
											
												
													
														|  | 
 |  | +                border-radius: 12px;
 | 
											
												
													
														|  | 
 |  | +                font-size: 12px;
 | 
											
												
													
														|  | 
 |  | +              }
 | 
											
												
													
														|  | 
 |  | +            }
 | 
											
												
													
														|  | 
 |  | +          }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +          .empty-state {
 | 
											
												
													
														|  | 
 |  | +            text-align: center;
 | 
											
												
													
														|  | 
 |  | +            padding: 24px;
 | 
											
												
													
														|  | 
 |  | +            color: #999;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +            .empty-icon {
 | 
											
												
													
														|  | 
 |  | +              font-size: 48px;
 | 
											
												
													
														|  | 
 |  | +              display: block;
 | 
											
												
													
														|  | 
 |  | +              margin-bottom: 8px;
 | 
											
												
													
														|  | 
 |  | +            }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +            p {
 | 
											
												
													
														|  | 
 |  | +              margin: 0;
 | 
											
												
													
														|  | 
 |  | +              font-size: 14px;
 | 
											
												
													
														|  | 
 |  | +            }
 | 
											
												
													
														|  | 
 |  | +          }
 | 
											
												
													
														|  | 
 |  | +        }
 | 
											
												
													
														|  | 
 |  | +      }
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +  .approval-comment-section {
 | 
											
												
													
														|  | 
 |  | +    margin-bottom: 24px;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    label {
 | 
											
												
													
														|  | 
 |  | +      display: block;
 | 
											
												
													
														|  | 
 |  | +      font-weight: 500;
 | 
											
												
													
														|  | 
 |  | +      color: #333;
 | 
											
												
													
														|  | 
 |  | +      margin-bottom: 8px;
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    textarea {
 | 
											
												
													
														|  | 
 |  | +      width: 100%;
 | 
											
												
													
														|  | 
 |  | +      padding: 12px;
 | 
											
												
													
														|  | 
 |  | +      border: 1px solid #ddd;
 | 
											
												
													
														|  | 
 |  | +      border-radius: 6px;
 | 
											
												
													
														|  | 
 |  | +      font-size: 14px;
 | 
											
												
													
														|  | 
 |  | +      resize: vertical;
 | 
											
												
													
														|  | 
 |  | +      font-family: inherit;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +      &:focus {
 | 
											
												
													
														|  | 
 |  | +        outline: none;
 | 
											
												
													
														|  | 
 |  | +        border-color: #4CAF50;
 | 
											
												
													
														|  | 
 |  | +        box-shadow: 0 0 0 3px rgba(76, 175, 80, 0.1);
 | 
											
												
													
														|  | 
 |  | +      }
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +  .approval-actions {
 | 
											
												
													
														|  | 
 |  | +    display: flex;
 | 
											
												
													
														|  | 
 |  | +    gap: 12px;
 | 
											
												
													
														|  | 
 |  | +    justify-content: flex-end;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    button {
 | 
											
												
													
														|  | 
 |  | +      padding: 12px 32px;
 | 
											
												
													
														|  | 
 |  | +      border: none;
 | 
											
												
													
														|  | 
 |  | +      border-radius: 6px;
 | 
											
												
													
														|  | 
 |  | +      font-size: 16px;
 | 
											
												
													
														|  | 
 |  | +      font-weight: 500;
 | 
											
												
													
														|  | 
 |  | +      cursor: pointer;
 | 
											
												
													
														|  | 
 |  | +      display: flex;
 | 
											
												
													
														|  | 
 |  | +      align-items: center;
 | 
											
												
													
														|  | 
 |  | +      gap: 8px;
 | 
											
												
													
														|  | 
 |  | +      transition: all 0.3s;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +      &:disabled {
 | 
											
												
													
														|  | 
 |  | +        opacity: 0.5;
 | 
											
												
													
														|  | 
 |  | +        cursor: not-allowed;
 | 
											
												
													
														|  | 
 |  | +      }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +      .btn-icon {
 | 
											
												
													
														|  | 
 |  | +        font-size: 18px;
 | 
											
												
													
														|  | 
 |  | +      }
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    .btn-reject {
 | 
											
												
													
														|  | 
 |  | +      background: white;
 | 
											
												
													
														|  | 
 |  | +      color: #f44336;
 | 
											
												
													
														|  | 
 |  | +      border: 2px solid #f44336;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +      &:hover:not(:disabled) {
 | 
											
												
													
														|  | 
 |  | +        background: #f44336;
 | 
											
												
													
														|  | 
 |  | +        color: white;
 | 
											
												
													
														|  | 
 |  | +        transform: translateY(-2px);
 | 
											
												
													
														|  | 
 |  | +        box-shadow: 0 4px 12px rgba(244, 67, 54, 0.3);
 | 
											
												
													
														|  | 
 |  | +      }
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    .btn-approve {
 | 
											
												
													
														|  | 
 |  | +      background: linear-gradient(135deg, #4CAF50, #45a049);
 | 
											
												
													
														|  | 
 |  | +      color: white;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +      &:hover:not(:disabled) {
 | 
											
												
													
														|  | 
 |  | +        background: linear-gradient(135deg, #45a049, #3d8b40);
 | 
											
												
													
														|  | 
 |  | +        transform: translateY(-2px);
 | 
											
												
													
														|  | 
 |  | +        box-shadow: 0 4px 12px rgba(76, 175, 80, 0.4);
 | 
											
												
													
														|  | 
 |  | +      }
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +// 驳回弹窗样式
 | 
											
												
													
														|  | 
 |  | +.reject-modal-overlay {
 | 
											
												
													
														|  | 
 |  | +  position: fixed;
 | 
											
												
													
														|  | 
 |  | +  top: 0;
 | 
											
												
													
														|  | 
 |  | +  left: 0;
 | 
											
												
													
														|  | 
 |  | +  right: 0;
 | 
											
												
													
														|  | 
 |  | +  bottom: 0;
 | 
											
												
													
														|  | 
 |  | +  background: rgba(0, 0, 0, 0.5);
 | 
											
												
													
														|  | 
 |  | +  display: flex;
 | 
											
												
													
														|  | 
 |  | +  align-items: center;
 | 
											
												
													
														|  | 
 |  | +  justify-content: center;
 | 
											
												
													
														|  | 
 |  | +  z-index: 10000;
 | 
											
												
													
														|  | 
 |  | +  animation: fadeIn 0.3s;
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +.reject-modal {
 | 
											
												
													
														|  | 
 |  | +  background: white;
 | 
											
												
													
														|  | 
 |  | +  border-radius: 12px;
 | 
											
												
													
														|  | 
 |  | +  width: 90%;
 | 
											
												
													
														|  | 
 |  | +  max-width: 600px;
 | 
											
												
													
														|  | 
 |  | +  max-height: 90vh;
 | 
											
												
													
														|  | 
 |  | +  overflow-y: auto;
 | 
											
												
													
														|  | 
 |  | +  animation: slideUp 0.3s;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +  .modal-header {
 | 
											
												
													
														|  | 
 |  | +    display: flex;
 | 
											
												
													
														|  | 
 |  | +    justify-content: space-between;
 | 
											
												
													
														|  | 
 |  | +    align-items: center;
 | 
											
												
													
														|  | 
 |  | +    padding: 20px 24px;
 | 
											
												
													
														|  | 
 |  | +    border-bottom: 1px solid #e9ecef;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    h3 {
 | 
											
												
													
														|  | 
 |  | +      margin: 0;
 | 
											
												
													
														|  | 
 |  | +      font-size: 20px;
 | 
											
												
													
														|  | 
 |  | +      color: #333;
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    .close-btn {
 | 
											
												
													
														|  | 
 |  | +      background: none;
 | 
											
												
													
														|  | 
 |  | +      border: none;
 | 
											
												
													
														|  | 
 |  | +      font-size: 32px;
 | 
											
												
													
														|  | 
 |  | +      color: #999;
 | 
											
												
													
														|  | 
 |  | +      cursor: pointer;
 | 
											
												
													
														|  | 
 |  | +      padding: 0;
 | 
											
												
													
														|  | 
 |  | +      width: 32px;
 | 
											
												
													
														|  | 
 |  | +      height: 32px;
 | 
											
												
													
														|  | 
 |  | +      display: flex;
 | 
											
												
													
														|  | 
 |  | +      align-items: center;
 | 
											
												
													
														|  | 
 |  | +      justify-content: center;
 | 
											
												
													
														|  | 
 |  | +      border-radius: 50%;
 | 
											
												
													
														|  | 
 |  | +      transition: all 0.3s;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +      &:hover {
 | 
											
												
													
														|  | 
 |  | +        background: #f5f5f5;
 | 
											
												
													
														|  | 
 |  | +        color: #333;
 | 
											
												
													
														|  | 
 |  | +      }
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +  .modal-body {
 | 
											
												
													
														|  | 
 |  | +    padding: 24px;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    .form-group {
 | 
											
												
													
														|  | 
 |  | +      margin-bottom: 20px;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +      &:last-child {
 | 
											
												
													
														|  | 
 |  | +        margin-bottom: 0;
 | 
											
												
													
														|  | 
 |  | +      }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +      label {
 | 
											
												
													
														|  | 
 |  | +        display: block;
 | 
											
												
													
														|  | 
 |  | +        font-weight: 500;
 | 
											
												
													
														|  | 
 |  | +        color: #333;
 | 
											
												
													
														|  | 
 |  | +        margin-bottom: 8px;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +        .required {
 | 
											
												
													
														|  | 
 |  | +          color: #f44336;
 | 
											
												
													
														|  | 
 |  | +          margin-left: 4px;
 | 
											
												
													
														|  | 
 |  | +        }
 | 
											
												
													
														|  | 
 |  | +      }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +      .reason-options {
 | 
											
												
													
														|  | 
 |  | +        display: flex;
 | 
											
												
													
														|  | 
 |  | +        flex-direction: column;
 | 
											
												
													
														|  | 
 |  | +        gap: 8px;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +        .reason-option {
 | 
											
												
													
														|  | 
 |  | +          display: flex;
 | 
											
												
													
														|  | 
 |  | +          align-items: center;
 | 
											
												
													
														|  | 
 |  | +          gap: 12px;
 | 
											
												
													
														|  | 
 |  | +          padding: 12px;
 | 
											
												
													
														|  | 
 |  | +          border: 2px solid #e9ecef;
 | 
											
												
													
														|  | 
 |  | +          border-radius: 6px;
 | 
											
												
													
														|  | 
 |  | +          cursor: pointer;
 | 
											
												
													
														|  | 
 |  | +          transition: all 0.3s;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +          &:hover {
 | 
											
												
													
														|  | 
 |  | +            border-color: #f44336;
 | 
											
												
													
														|  | 
 |  | +            background: #fff5f5;
 | 
											
												
													
														|  | 
 |  | +          }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +          &.selected {
 | 
											
												
													
														|  | 
 |  | +            border-color: #f44336;
 | 
											
												
													
														|  | 
 |  | +            background: #ffebee;
 | 
											
												
													
														|  | 
 |  | +          }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +          input[type="radio"] {
 | 
											
												
													
														|  | 
 |  | +            cursor: pointer;
 | 
											
												
													
														|  | 
 |  | +          }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +          span {
 | 
											
												
													
														|  | 
 |  | +            flex: 1;
 | 
											
												
													
														|  | 
 |  | +            font-size: 14px;
 | 
											
												
													
														|  | 
 |  | +            color: #333;
 | 
											
												
													
														|  | 
 |  | +          }
 | 
											
												
													
														|  | 
 |  | +        }
 | 
											
												
													
														|  | 
 |  | +      }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +      textarea {
 | 
											
												
													
														|  | 
 |  | +        width: 100%;
 | 
											
												
													
														|  | 
 |  | +        padding: 12px;
 | 
											
												
													
														|  | 
 |  | +        border: 1px solid #ddd;
 | 
											
												
													
														|  | 
 |  | +        border-radius: 6px;
 | 
											
												
													
														|  | 
 |  | +        font-size: 14px;
 | 
											
												
													
														|  | 
 |  | +        resize: vertical;
 | 
											
												
													
														|  | 
 |  | +        font-family: inherit;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +        &:focus {
 | 
											
												
													
														|  | 
 |  | +          outline: none;
 | 
											
												
													
														|  | 
 |  | +          border-color: #f44336;
 | 
											
												
													
														|  | 
 |  | +          box-shadow: 0 0 0 3px rgba(244, 67, 54, 0.1);
 | 
											
												
													
														|  | 
 |  | +        }
 | 
											
												
													
														|  | 
 |  | +      }
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +  .modal-footer {
 | 
											
												
													
														|  | 
 |  | +    padding: 16px 24px;
 | 
											
												
													
														|  | 
 |  | +    border-top: 1px solid #e9ecef;
 | 
											
												
													
														|  | 
 |  | +    display: flex;
 | 
											
												
													
														|  | 
 |  | +    justify-content: flex-end;
 | 
											
												
													
														|  | 
 |  | +    gap: 12px;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    button {
 | 
											
												
													
														|  | 
 |  | +      padding: 10px 24px;
 | 
											
												
													
														|  | 
 |  | +      border: none;
 | 
											
												
													
														|  | 
 |  | +      border-radius: 6px;
 | 
											
												
													
														|  | 
 |  | +      font-size: 14px;
 | 
											
												
													
														|  | 
 |  | +      font-weight: 500;
 | 
											
												
													
														|  | 
 |  | +      cursor: pointer;
 | 
											
												
													
														|  | 
 |  | +      transition: all 0.3s;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +      &:disabled {
 | 
											
												
													
														|  | 
 |  | +        opacity: 0.5;
 | 
											
												
													
														|  | 
 |  | +        cursor: not-allowed;
 | 
											
												
													
														|  | 
 |  | +      }
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    .btn-cancel {
 | 
											
												
													
														|  | 
 |  | +      background: #f5f5f5;
 | 
											
												
													
														|  | 
 |  | +      color: #666;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +      &:hover:not(:disabled) {
 | 
											
												
													
														|  | 
 |  | +        background: #e0e0e0;
 | 
											
												
													
														|  | 
 |  | +      }
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    .btn-submit {
 | 
											
												
													
														|  | 
 |  | +      background: #f44336;
 | 
											
												
													
														|  | 
 |  | +      color: white;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +      &:hover:not(:disabled) {
 | 
											
												
													
														|  | 
 |  | +        background: #d32f2f;
 | 
											
												
													
														|  | 
 |  | +        box-shadow: 0 2px 8px rgba(244, 67, 54, 0.3);
 | 
											
												
													
														|  | 
 |  | +      }
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +@keyframes float {
 | 
											
												
													
														|  | 
 |  | +  0%, 100% { transform: translateY(0); }
 | 
											
												
													
														|  | 
 |  | +  50% { transform: translateY(-10px); }
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +@keyframes fadeIn {
 | 
											
												
													
														|  | 
 |  | +  from { opacity: 0; }
 | 
											
												
													
														|  | 
 |  | +  to { opacity: 1; }
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +@keyframes slideUp {
 | 
											
												
													
														|  | 
 |  | +  from { 
 | 
											
												
													
														|  | 
 |  | +    opacity: 0;
 | 
											
												
													
														|  | 
 |  | +    transform: translateY(30px);
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +  to { 
 | 
											
												
													
														|  | 
 |  | +    opacity: 1;
 | 
											
												
													
														|  | 
 |  | +    transform: translateY(0);
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +```
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +#### 2.2.2 集成到项目详情页
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +修改组长查看的项目详情页(复用设计师详情页):
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +```typescript
 | 
											
												
													
														|  | 
 |  | +// src/app/pages/designer/project-detail/project-detail.ts
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +import { OrderApprovalPanelComponent } from '../../shared/components/order-approval-panel/order-approval-panel.component';
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +@Component({
 | 
											
												
													
														|  | 
 |  | +  // ... 其他配置
 | 
											
												
													
														|  | 
 |  | +  imports: [
 | 
											
												
													
														|  | 
 |  | +    // ... 其他导入
 | 
											
												
													
														|  | 
 |  | +    OrderApprovalPanelComponent
 | 
											
												
													
														|  | 
 |  | +  ]
 | 
											
												
													
														|  | 
 |  | +})
 | 
											
												
													
														|  | 
 |  | +export class ProjectDetail implements OnInit, OnDestroy {
 | 
											
												
													
														|  | 
 |  | +  // 添加审批相关属性
 | 
											
												
													
														|  | 
 |  | +  showApprovalPanel = false;
 | 
											
												
													
														|  | 
 |  | +  
 | 
											
												
													
														|  | 
 |  | +  ngOnInit() {
 | 
											
												
													
														|  | 
 |  | +    // ... 现有初始化代码
 | 
											
												
													
														|  | 
 |  | +    
 | 
											
												
													
														|  | 
 |  | +    // 检查是否需要显示审批面板
 | 
											
												
													
														|  | 
 |  | +    this.checkApprovalStatus();
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +  
 | 
											
												
													
														|  | 
 |  | +  /**
 | 
											
												
													
														|  | 
 |  | +   * 检查是否需要显示审批面板
 | 
											
												
													
														|  | 
 |  | +   */
 | 
											
												
													
														|  | 
 |  | +  private checkApprovalStatus() {
 | 
											
												
													
														|  | 
 |  | +    if (!this.project) return;
 | 
											
												
													
														|  | 
 |  | +    
 | 
											
												
													
														|  | 
 |  | +    const isTeamLeader = this.roleContext === 'team-leader';
 | 
											
												
													
														|  | 
 |  | +    const currentStage = this.project.get('currentStage');
 | 
											
												
													
														|  | 
 |  | +    const approvalStatus = this.project.get('data')?.approvalStatus;
 | 
											
												
													
														|  | 
 |  | +    
 | 
											
												
													
														|  | 
 |  | +    // 组长视角 + 订单分配阶段 + 待审批状态
 | 
											
												
													
														|  | 
 |  | +    this.showApprovalPanel = isTeamLeader && 
 | 
											
												
													
														|  | 
 |  | +                             currentStage === '订单分配' && 
 | 
											
												
													
														|  | 
 |  | +                             approvalStatus === 'pending';
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +  
 | 
											
												
													
														|  | 
 |  | +  /**
 | 
											
												
													
														|  | 
 |  | +   * 处理审批完成事件
 | 
											
												
													
														|  | 
 |  | +   */
 | 
											
												
													
														|  | 
 |  | +  async onApprovalCompleted(event: { action: 'approved' | 'rejected'; reason?: string; comment?: string }) {
 | 
											
												
													
														|  | 
 |  | +    if (!this.project || !this.currentUser) return;
 | 
											
												
													
														|  | 
 |  | +    
 | 
											
												
													
														|  | 
 |  | +    try {
 | 
											
												
													
														|  | 
 |  | +      const data = this.project.get('data') || {};
 | 
											
												
													
														|  | 
 |  | +      const approvalHistory = data.approvalHistory || [];
 | 
											
												
													
														|  | 
 |  | +      const latestRecord = approvalHistory[approvalHistory.length - 1];
 | 
											
												
													
														|  | 
 |  | +      
 | 
											
												
													
														|  | 
 |  | +      if (!latestRecord) {
 | 
											
												
													
														|  | 
 |  | +        alert('审批记录不存在');
 | 
											
												
													
														|  | 
 |  | +        return;
 | 
											
												
													
														|  | 
 |  | +      }
 | 
											
												
													
														|  | 
 |  | +      
 | 
											
												
													
														|  | 
 |  | +      // 更新最新的审批记录
 | 
											
												
													
														|  | 
 |  | +      latestRecord.status = event.action === 'approved' ? 'approved' : 'rejected';
 | 
											
												
													
														|  | 
 |  | +      latestRecord.approver = {
 | 
											
												
													
														|  | 
 |  | +        id: this.currentUser.id,
 | 
											
												
													
														|  | 
 |  | +        name: this.currentUser.get('name'),
 | 
											
												
													
														|  | 
 |  | +        role: this.currentUser.get('roleName')
 | 
											
												
													
														|  | 
 |  | +      };
 | 
											
												
													
														|  | 
 |  | +      latestRecord.approvalTime = new Date();
 | 
											
												
													
														|  | 
 |  | +      
 | 
											
												
													
														|  | 
 |  | +      if (event.reason) {
 | 
											
												
													
														|  | 
 |  | +        latestRecord.reason = event.reason;
 | 
											
												
													
														|  | 
 |  | +      }
 | 
											
												
													
														|  | 
 |  | +      if (event.comment) {
 | 
											
												
													
														|  | 
 |  | +        latestRecord.comment = event.comment;
 | 
											
												
													
														|  | 
 |  | +      }
 | 
											
												
													
														|  | 
 |  | +      
 | 
											
												
													
														|  | 
 |  | +      // 更新项目状态
 | 
											
												
													
														|  | 
 |  | +      if (event.action === 'approved') {
 | 
											
												
													
														|  | 
 |  | +        // 通过:推进到"确认需求"阶段
 | 
											
												
													
														|  | 
 |  | +        this.project.set('currentStage', '确认需求');
 | 
											
												
													
														|  | 
 |  | +        data.approvalStatus = 'approved';
 | 
											
												
													
														|  | 
 |  | +        delete data.pendingApprovalBy;
 | 
											
												
													
														|  | 
 |  | +      } else {
 | 
											
												
													
														|  | 
 |  | +        // 驳回:保持在"订单分配"阶段,但标记为已驳回
 | 
											
												
													
														|  | 
 |  | +        data.approvalStatus = 'rejected';
 | 
											
												
													
														|  | 
 |  | +        data.lastRejectionReason = event.reason;
 | 
											
												
													
														|  | 
 |  | +        delete data.pendingApprovalBy;
 | 
											
												
													
														|  | 
 |  | +      }
 | 
											
												
													
														|  | 
 |  | +      
 | 
											
												
													
														|  | 
 |  | +      data.approvalHistory = approvalHistory;
 | 
											
												
													
														|  | 
 |  | +      this.project.set('data', data);
 | 
											
												
													
														|  | 
 |  | +      
 | 
											
												
													
														|  | 
 |  | +      // 保存到数据库
 | 
											
												
													
														|  | 
 |  | +      await this.project.save(null, { useMasterKey: true });
 | 
											
												
													
														|  | 
 |  | +      
 | 
											
												
													
														|  | 
 |  | +      // 提示用户
 | 
											
												
													
														|  | 
 |  | +      if (event.action === 'approved') {
 | 
											
												
													
														|  | 
 |  | +        alert('✅ 审批通过!项目已进入"确认需求"阶段');
 | 
											
												
													
														|  | 
 |  | +      } else {
 | 
											
												
													
														|  | 
 |  | +        alert('❌ 已驳回订单,客服将收到驳回通知');
 | 
											
												
													
														|  | 
 |  | +      }
 | 
											
												
													
														|  | 
 |  | +      
 | 
											
												
													
														|  | 
 |  | +      // 刷新页面或返回列表
 | 
											
												
													
														|  | 
 |  | +      this.showApprovalPanel = false;
 | 
											
												
													
														|  | 
 |  | +      this.router.navigate(['/wxwork', this.companyId, 'team-leader', 'dashboard']);
 | 
											
												
													
														|  | 
 |  | +      
 | 
											
												
													
														|  | 
 |  | +    } catch (error) {
 | 
											
												
													
														|  | 
 |  | +      console.error('审批操作失败:', error);
 | 
											
												
													
														|  | 
 |  | +      alert('操作失败,请重试');
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +```
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +```html
 | 
											
												
													
														|  | 
 |  | +<!-- src/app/pages/designer/project-detail/project-detail.html -->
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +<!-- 在页面顶部添加审批面板 -->
 | 
											
												
													
														|  | 
 |  | +@if (showApprovalPanel) {
 | 
											
												
													
														|  | 
 |  | +  <app-order-approval-panel
 | 
											
												
													
														|  | 
 |  | +    [project]="project"
 | 
											
												
													
														|  | 
 |  | +    [currentUser]="currentUser"
 | 
											
												
													
														|  | 
 |  | +    (approvalCompleted)="onApprovalCompleted($event)">
 | 
											
												
													
														|  | 
 |  | +  </app-order-approval-panel>
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +<!-- 原有的项目详情内容 -->
 | 
											
												
													
														|  | 
 |  | +<!-- ... -->
 | 
											
												
													
														|  | 
 |  | +```
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +---
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +## 三、客服端适配
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +### 3.1 修改订单提交逻辑
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +```typescript
 | 
											
												
													
														|  | 
 |  | +// src/modules/project/pages/project-detail/stages/stage-order.component.ts
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +async submitForOrder() {
 | 
											
												
													
														|  | 
 |  | +  // ... 现有验证逻辑 ...
 | 
											
												
													
														|  | 
 |  | +  
 | 
											
												
													
														|  | 
 |  | +  try {
 | 
											
												
													
														|  | 
 |  | +    this.saving = true;
 | 
											
												
													
														|  | 
 |  | +    
 | 
											
												
													
														|  | 
 |  | +    // ... 现有保存逻辑 ...
 | 
											
												
													
														|  | 
 |  | +    
 | 
											
												
													
														|  | 
 |  | +    // ✨ 修改:不直接推进到"确认需求",而是标记为待审批
 | 
											
												
													
														|  | 
 |  | +    // this.project.set('currentStage', '确认需求');  // 删除这行
 | 
											
												
													
														|  | 
 |  | +    
 | 
											
												
													
														|  | 
 |  | +    // 记录审批历史(包含团队快照)
 | 
											
												
													
														|  | 
 |  | +    const data = this.project.get('data') || {};
 | 
											
												
													
														|  | 
 |  | +    const approvalHistory = data.approvalHistory || [];
 | 
											
												
													
														|  | 
 |  | +    
 | 
											
												
													
														|  | 
 |  | +    const teamSnapshot = assignedTeams.map(team => {
 | 
											
												
													
														|  | 
 |  | +      const profile = team.get('profile');
 | 
											
												
													
														|  | 
 |  | +      const spaces = team.get('data')?.spaces || [];
 | 
											
												
													
														|  | 
 |  | +      return {
 | 
											
												
													
														|  | 
 |  | +        id: profile?.id,
 | 
											
												
													
														|  | 
 |  | +        name: profile?.get('name'),
 | 
											
												
													
														|  | 
 |  | +        spaces
 | 
											
												
													
														|  | 
 |  | +      };
 | 
											
												
													
														|  | 
 |  | +    });
 | 
											
												
													
														|  | 
 |  | +    
 | 
											
												
													
														|  | 
 |  | +    approvalHistory.push({
 | 
											
												
													
														|  | 
 |  | +      stage: '订单分配',
 | 
											
												
													
														|  | 
 |  | +      submitter: {
 | 
											
												
													
														|  | 
 |  | +        id: this.currentUser?.id,
 | 
											
												
													
														|  | 
 |  | +        name: this.currentUser?.get('name'),
 | 
											
												
													
														|  | 
 |  | +        role: this.currentUser?.get('roleName')
 | 
											
												
													
														|  | 
 |  | +      },
 | 
											
												
													
														|  | 
 |  | +      submitTime: new Date(),
 | 
											
												
													
														|  | 
 |  | +      status: 'pending',  // ✨ 标记为待审批
 | 
											
												
													
														|  | 
 |  | +      quotationTotal: this.quotation.total,
 | 
											
												
													
														|  | 
 |  | +      teams: teamSnapshot
 | 
											
												
													
														|  | 
 |  | +    });
 | 
											
												
													
														|  | 
 |  | +    
 | 
											
												
													
														|  | 
 |  | +    // ✨ 新增:设置审批状态
 | 
											
												
													
														|  | 
 |  | +    data.approvalHistory = approvalHistory;
 | 
											
												
													
														|  | 
 |  | +    data.approvalStatus = 'pending';  // 待审批
 | 
											
												
													
														|  | 
 |  | +    data.pendingApprovalBy = 'team-leader';  // 待组长审批
 | 
											
												
													
														|  | 
 |  | +    this.project.set('data', data);
 | 
											
												
													
														|  | 
 |  | +    
 | 
											
												
													
														|  | 
 |  | +    // ✨ 保持在"订单分配"阶段
 | 
											
												
													
														|  | 
 |  | +    // 但可以通过 approvalStatus 字段区分是否已提交
 | 
											
												
													
														|  | 
 |  | +    
 | 
											
												
													
														|  | 
 |  | +    await this.project.save();
 | 
											
												
													
														|  | 
 |  | +    
 | 
											
												
													
														|  | 
 |  | +    alert('✅ 提交成功!等待组长审批');
 | 
											
												
													
														|  | 
 |  | +    
 | 
											
												
													
														|  | 
 |  | +  } catch (err) {
 | 
											
												
													
														|  | 
 |  | +    console.error('提交失败:', err);
 | 
											
												
													
														|  | 
 |  | +    alert('提交失败');
 | 
											
												
													
														|  | 
 |  | +  } finally {
 | 
											
												
													
														|  | 
 |  | +    this.saving = false;
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +```
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +### 3.2 显示审批状态
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +在客服端项目列表或详情页显示审批状态:
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +```html
 | 
											
												
													
														|  | 
 |  | +<!-- 审批状态徽章 -->
 | 
											
												
													
														|  | 
 |  | +@if (project.data?.approvalStatus === 'pending') {
 | 
											
												
													
														|  | 
 |  | +  <span class="status-badge pending">等待组长审批</span>
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +@else if (project.data?.approvalStatus === 'approved') {
 | 
											
												
													
														|  | 
 |  | +  <span class="status-badge approved">已通过</span>
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +@else if (project.data?.approvalStatus === 'rejected') {
 | 
											
												
													
														|  | 
 |  | +  <span class="status-badge rejected">已驳回</span>
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +```
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +### 3.3 显示驳回原因
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +```html
 | 
											
												
													
														|  | 
 |  | +<!-- 驳回提示 -->
 | 
											
												
													
														|  | 
 |  | +@if (project.data?.approvalStatus === 'rejected') {
 | 
											
												
													
														|  | 
 |  | +  <div class="rejection-notice">
 | 
											
												
													
														|  | 
 |  | +    <div class="notice-icon">⚠️</div>
 | 
											
												
													
														|  | 
 |  | +    <div class="notice-content">
 | 
											
												
													
														|  | 
 |  | +      <h4>订单已被驳回</h4>
 | 
											
												
													
														|  | 
 |  | +      <p><strong>驳回原因:</strong>{{ project.data?.lastRejectionReason }}</p>
 | 
											
												
													
														|  | 
 |  | +      <button class="btn-primary" (click)="editAndResubmit()">修改并重新提交</button>
 | 
											
												
													
														|  | 
 |  | +    </div>
 | 
											
												
													
														|  | 
 |  | +  </div>
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +```
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +```scss
 | 
											
												
													
														|  | 
 |  | +.rejection-notice {
 | 
											
												
													
														|  | 
 |  | +  background: #fff3e0;
 | 
											
												
													
														|  | 
 |  | +  border: 2px solid #ff9800;
 | 
											
												
													
														|  | 
 |  | +  border-radius: 8px;
 | 
											
												
													
														|  | 
 |  | +  padding: 16px;
 | 
											
												
													
														|  | 
 |  | +  margin-bottom: 20px;
 | 
											
												
													
														|  | 
 |  | +  display: flex;
 | 
											
												
													
														|  | 
 |  | +  gap: 16px;
 | 
											
												
													
														|  | 
 |  | +  align-items: flex-start;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +  .notice-icon {
 | 
											
												
													
														|  | 
 |  | +    font-size: 32px;
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +  .notice-content {
 | 
											
												
													
														|  | 
 |  | +    flex: 1;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    h4 {
 | 
											
												
													
														|  | 
 |  | +      margin: 0 0 8px;
 | 
											
												
													
														|  | 
 |  | +      color: #f57c00;
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    p {
 | 
											
												
													
														|  | 
 |  | +      margin: 0 0 12px;
 | 
											
												
													
														|  | 
 |  | +      color: #666;
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    .btn-primary {
 | 
											
												
													
														|  | 
 |  | +      background: #ff9800;
 | 
											
												
													
														|  | 
 |  | +      color: white;
 | 
											
												
													
														|  | 
 |  | +      border: none;
 | 
											
												
													
														|  | 
 |  | +      padding: 8px 20px;
 | 
											
												
													
														|  | 
 |  | +      border-radius: 4px;
 | 
											
												
													
														|  | 
 |  | +      cursor: pointer;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +      &:hover {
 | 
											
												
													
														|  | 
 |  | +        background: #f57c00;
 | 
											
												
													
														|  | 
 |  | +      }
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +```
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +---
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +## 四、数据库查询优化
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +### 4.1 为组长工作台创建专用查询
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +```typescript
 | 
											
												
													
														|  | 
 |  | +// src/app/pages/team-leader/services/project-data.service.ts
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +/**
 | 
											
												
													
														|  | 
 |  | + * 获取待审批项目列表
 | 
											
												
													
														|  | 
 |  | + */
 | 
											
												
													
														|  | 
 |  | +async getPendingApprovalProjects(companyId: string): Promise<any[]> {
 | 
											
												
													
														|  | 
 |  | +  const Parse = await this.ensureParse();
 | 
											
												
													
														|  | 
 |  | +  if (!Parse) return [];
 | 
											
												
													
														|  | 
 |  | +  
 | 
											
												
													
														|  | 
 |  | +  try {
 | 
											
												
													
														|  | 
 |  | +    const query = new Parse.Query('Project');
 | 
											
												
													
														|  | 
 |  | +    query.equalTo('company', companyId);
 | 
											
												
													
														|  | 
 |  | +    query.equalTo('currentStage', '订单分配');
 | 
											
												
													
														|  | 
 |  | +    query.notEqualTo('isDeleted', true);
 | 
											
												
													
														|  | 
 |  | +    query.include('contact');
 | 
											
												
													
														|  | 
 |  | +    query.descending('updatedAt');
 | 
											
												
													
														|  | 
 |  | +    query.limit(100);
 | 
											
												
													
														|  | 
 |  | +    
 | 
											
												
													
														|  | 
 |  | +    const projects = await query.find({ useMasterKey: true });
 | 
											
												
													
														|  | 
 |  | +    
 | 
											
												
													
														|  | 
 |  | +    // 过滤出待审批的项目
 | 
											
												
													
														|  | 
 |  | +    return projects.filter(p => {
 | 
											
												
													
														|  | 
 |  | +      const data = p.get('data') || {};
 | 
											
												
													
														|  | 
 |  | +      return data.approvalStatus === 'pending';
 | 
											
												
													
														|  | 
 |  | +    });
 | 
											
												
													
														|  | 
 |  | +    
 | 
											
												
													
														|  | 
 |  | +  } catch (error) {
 | 
											
												
													
														|  | 
 |  | +    console.error('获取待审批项目失败:', error);
 | 
											
												
													
														|  | 
 |  | +    return [];
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +```
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +---
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +## 五、通知机制(可选扩展)
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +### 5.1 企业微信消息通知
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +```typescript
 | 
											
												
													
														|  | 
 |  | +// src/app/services/notification.service.ts
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +@Injectable({ providedIn: 'root' })
 | 
											
												
													
														|  | 
 |  | +export class NotificationService {
 | 
											
												
													
														|  | 
 |  | +  
 | 
											
												
													
														|  | 
 |  | +  /**
 | 
											
												
													
														|  | 
 |  | +   * 发送审批通知给客服
 | 
											
												
													
														|  | 
 |  | +   */
 | 
											
												
													
														|  | 
 |  | +  async notifyCustomerServiceOfApproval(
 | 
											
												
													
														|  | 
 |  | +    project: any, 
 | 
											
												
													
														|  | 
 |  | +    approvalResult: 'approved' | 'rejected',
 | 
											
												
													
														|  | 
 |  | +    reason?: string
 | 
											
												
													
														|  | 
 |  | +  ) {
 | 
											
												
													
														|  | 
 |  | +    // 获取项目的客服人员
 | 
											
												
													
														|  | 
 |  | +    const customerServiceId = project.get('data')?.submitter?.id;
 | 
											
												
													
														|  | 
 |  | +    if (!customerServiceId) return;
 | 
											
												
													
														|  | 
 |  | +    
 | 
											
												
													
														|  | 
 |  | +    // 构建消息内容
 | 
											
												
													
														|  | 
 |  | +    const message = approvalResult === 'approved' 
 | 
											
												
													
														|  | 
 |  | +      ? `✅ 您提交的订单"${project.get('title')}"已通过审批,可以进入下一阶段。`
 | 
											
												
													
														|  | 
 |  | +      : `❌ 您提交的订单"${project.get('title')}"被驳回。\n驳回原因:${reason}`;
 | 
											
												
													
														|  | 
 |  | +    
 | 
											
												
													
														|  | 
 |  | +    // 调用企业微信API发送消息(需要后端支持)
 | 
											
												
													
														|  | 
 |  | +    try {
 | 
											
												
													
														|  | 
 |  | +      // await this.wxworkApi.sendMessage({
 | 
											
												
													
														|  | 
 |  | +      //   toUser: customerServiceId,
 | 
											
												
													
														|  | 
 |  | +      //   message: message,
 | 
											
												
													
														|  | 
 |  | +      //   agentId: 'your-agent-id'
 | 
											
												
													
														|  | 
 |  | +      // });
 | 
											
												
													
														|  | 
 |  | +      
 | 
											
												
													
														|  | 
 |  | +      console.log('📨 发送通知:', message);
 | 
											
												
													
														|  | 
 |  | +    } catch (error) {
 | 
											
												
													
														|  | 
 |  | +      console.error('发送通知失败:', error);
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +```
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +---
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +## 六、测试清单
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +### 6.1 功能测试
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +- [ ] 客服提交订单后,项目标记为"待审批"
 | 
											
												
													
														|  | 
 |  | +- [ ] 组长工作台正确显示待审批项目数量
 | 
											
												
													
														|  | 
 |  | +- [ ] 组长可以筛选查看待审批项目
 | 
											
												
													
														|  | 
 |  | +- [ ] 点击待审批项目进入详情页,显示审批面板
 | 
											
												
													
														|  | 
 |  | +- [ ] 审批面板正确展示项目信息、报价、设计师分配
 | 
											
												
													
														|  | 
 |  | +- [ ] 通过审批后,项目进入"确认需求"阶段
 | 
											
												
													
														|  | 
 |  | +- [ ] 驳回审批后,项目保持在"订单分配"阶段,显示驳回状态
 | 
											
												
													
														|  | 
 |  | +- [ ] 客服端可以看到审批状态(待审批/已通过/已驳回)
 | 
											
												
													
														|  | 
 |  | +- [ ] 驳回后客服可以查看驳回原因
 | 
											
												
													
														|  | 
 |  | +- [ ] 驳回后客服可以修改并重新提交
 | 
											
												
													
														|  | 
 |  | +- [ ] 审批历史正确记录
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +### 6.2 权限测试
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +- [ ] 只有组长角色可以看到审批按钮
 | 
											
												
													
														|  | 
 |  | +- [ ] 非组长角色无法进行审批操作
 | 
											
												
													
														|  | 
 |  | +- [ ] 审批记录中正确记录审批人信息
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +### 6.3 边界测试
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +- [ ] 项目没有审批记录时的处理
 | 
											
												
													
														|  | 
 |  | +- [ ] 多次提交审批的历史记录
 | 
											
												
													
														|  | 
 |  | +- [ ] 审批过程中项目被删除的处理
 | 
											
												
													
														|  | 
 |  | +- [ ] 并发审批的处理
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +---
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +## 七、后续优化建议
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +### 7.1 短期优化(1-2周)
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +1. **批量审批功能**
 | 
											
												
													
														|  | 
 |  | +   - 组长可以批量选择多个项目进行审批
 | 
											
												
													
														|  | 
 |  | +   - 快速通过/驳回多个订单
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +2. **审批提醒**
 | 
											
												
													
														|  | 
 |  | +   - 超过24小时未审批的项目高亮显示
 | 
											
												
													
														|  | 
 |  | +   - 每日汇总待审批项目发送给组长
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +3. **移动端适配**
 | 
											
												
													
														|  | 
 |  | +   - 审批面板响应式设计
 | 
											
												
													
														|  | 
 |  | +   - 支持企业微信内审批
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +### 7.2 中期优化(1-2月)
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +1. **审批流程可配置**
 | 
											
												
													
														|  | 
 |  | +   - 支持自定义审批规则
 | 
											
												
													
														|  | 
 |  | +   - 根据项目金额自动路由审批人
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +2. **审批数据分析**
 | 
											
												
													
														|  | 
 |  | +   - 统计审批通过率
 | 
											
												
													
														|  | 
 |  | +   - 分析驳回原因分布
 | 
											
												
													
														|  | 
 |  | +   - 监控审批时效
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +3. **智能审批建议**
 | 
											
												
													
														|  | 
 |  | +   - 基于历史数据提供审批建议
 | 
											
												
													
														|  | 
 |  | +   - 自动标记异常项目
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +---
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +## 八、实施步骤
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +### Phase 1: 核心功能(2-3天)
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +1. ✅ 创建 OrderApprovalPanelComponent
 | 
											
												
													
														|  | 
 |  | +2. ✅ 修改客服端提交逻辑
 | 
											
												
													
														|  | 
 |  | +3. ✅ 集成到项目详情页
 | 
											
												
													
														|  | 
 |  | +4. ✅ 实现审批操作(通过/驳回)
 | 
											
												
													
														|  | 
 |  | +5. ✅ 测试基本流程
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +### Phase 2: 视觉优化(1天)
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +1. ✅ 组长工作台待审批项目视觉标识
 | 
											
												
													
														|  | 
 |  | +2. ✅ 审批面板样式优化
 | 
											
												
													
														|  | 
 |  | +3. ✅ 驳回弹窗交互优化
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +### Phase 3: 客服端适配(1天)
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +1. ✅ 显示审批状态
 | 
											
												
													
														|  | 
 |  | +2. ✅ 显示驳回原因
 | 
											
												
													
														|  | 
 |  | +3. ✅ 重新提交功能
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +### Phase 4: 测试与发布(1天)
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +1. ✅ 功能测试
 | 
											
												
													
														|  | 
 |  | +2. ✅ 权限测试
 | 
											
												
													
														|  | 
 |  | +3. ✅ 用户验收测试
 | 
											
												
													
														|  | 
 |  | +4. ✅ 正式发布
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +**预计总工时:5-6天**
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +---
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +## 九、相关文档
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +- [项目管理PRD](../prd/wxwork-project-management.md)
 | 
											
												
													
														|  | 
 |  | +- [数据库表结构](../Database/database-tables-overview.md)
 | 
											
												
													
														|  | 
 |  | +- [权限管理说明](../prd/wxwork-project-management.md#权限代码)
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +---
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +**文档创建:** 2025-10-28  
 | 
											
												
													
														|  | 
 |  | +**最后更新:** 2025-10-28  
 | 
											
												
													
														|  | 
 |  | +**版本:** v1.1.0  
 | 
											
												
													
														|  | 
 |  | +**状态:** ✅ 已完成
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +---
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +## 十、问题与修复记录
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +### 问题1:路由跳转到错误的项目详情页
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +**问题描述:**  
 | 
											
												
													
														|  | 
 |  | +组长工作台点击待审批项目后,跳转到了开发版本的项目详情页(`src/app/pages/designer/project-detail`),而不是真实的项目详情页(`src/modules/project/pages/project-detail`),导致审批面板无法显示。
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +**根本原因:**  
 | 
											
												
													
														|  | 
 |  | +项目中存在两个项目详情组件:
 | 
											
												
													
														|  | 
 |  | +1. **开发版本**:`src/app/pages/designer/project-detail/project-detail.ts` - 用于开发测试
 | 
											
												
													
														|  | 
 |  | +2. **真实版本**:`src/modules/project/pages/project-detail/project-detail.component.ts` - 真实项目使用的组件
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +审批功能被误加到了开发版本,而路由配置也指向了开发版本。
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +**解决方案:**
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +1. **修改路由配置**(`src/app/app.routes.ts`):
 | 
											
												
													
														|  | 
 |  | +   ```typescript
 | 
											
												
													
														|  | 
 |  | +   // wxwork/:cid/team-leader 路由
 | 
											
												
													
														|  | 
 |  | +   {
 | 
											
												
													
														|  | 
 |  | +     path: 'team-leader',
 | 
											
												
													
														|  | 
 |  | +     children: [
 | 
											
												
													
														|  | 
 |  | +       {
 | 
											
												
													
														|  | 
 |  | +         path: 'dashboard',
 | 
											
												
													
														|  | 
 |  | +         loadComponent: () => import('./pages/team-leader/dashboard/dashboard').then(m => m.Dashboard),
 | 
											
												
													
														|  | 
 |  | +         title: '组长工作台'
 | 
											
												
													
														|  | 
 |  | +       },
 | 
											
												
													
														|  | 
 |  | +       {
 | 
											
												
													
														|  | 
 |  | +         path: 'project-detail/:projectId',
 | 
											
												
													
														|  | 
 |  | +         // ✅ 修改为真实的项目详情组件
 | 
											
												
													
														|  | 
 |  | +         loadComponent: () => import('../modules/project/pages/project-detail/project-detail.component').then(m => m.ProjectDetailComponent),
 | 
											
												
													
														|  | 
 |  | +         title: '项目详情',
 | 
											
												
													
														|  | 
 |  | +         children: [
 | 
											
												
													
														|  | 
 |  | +           // ... 四阶段子路由
 | 
											
												
													
														|  | 
 |  | +         ]
 | 
											
												
													
														|  | 
 |  | +       }
 | 
											
												
													
														|  | 
 |  | +     ]
 | 
											
												
													
														|  | 
 |  | +   }
 | 
											
												
													
														|  | 
 |  | +   ```
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +2. **将审批功能集成到真实组件**:
 | 
											
												
													
														|  | 
 |  | +   - 修改 `src/modules/project/pages/project-detail/project-detail.component.ts`:
 | 
											
												
													
														|  | 
 |  | +     - 导入 `OrderApprovalPanelComponent`
 | 
											
												
													
														|  | 
 |  | +     - 添加 `showApprovalPanel` 和 `companyId` 属性
 | 
											
												
													
														|  | 
 |  | +     - 添加 `checkApprovalStatus()` 方法
 | 
											
												
													
														|  | 
 |  | +     - 添加 `onApprovalCompleted()` 方法
 | 
											
												
													
														|  | 
 |  | +   - 修改 `src/modules/project/pages/project-detail/project-detail.component.html`:
 | 
											
												
													
														|  | 
 |  | +     - 在 `<router-outlet>` 之前添加审批面板
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +**修复后效果:**
 | 
											
												
													
														|  | 
 |  | +- ✅ 组长工作台点击待审批项目正确跳转到真实项目详情页
 | 
											
												
													
														|  | 
 |  | +- ✅ 审批面板正常显示
 | 
											
												
													
														|  | 
 |  | +- ✅ 四阶段导航正常工作
 | 
											
												
													
														|  | 
 |  | +- ✅ 审批通过/驳回功能正常
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +**相关文件:**
 | 
											
												
													
														|  | 
 |  | +- `src/app/app.routes.ts` - 路由配置
 | 
											
												
													
														|  | 
 |  | +- `src/modules/project/pages/project-detail/project-detail.component.ts` - 真实项目详情组件
 | 
											
												
													
														|  | 
 |  | +- `src/modules/project/pages/project-detail/project-detail.component.html` - 真实项目详情模板
 | 
											
												
													
														|  | 
 |  | +- `src/app/pages/team-leader/dashboard/dashboard.ts` - 组长工作台(跳转逻辑)
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +---
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +**测试验证步骤:**
 | 
											
												
													
														|  | 
 |  | +1. 访问组长工作台:`http://localhost:4200/wxwork/cDL6R1hgSi/team-leader/dashboard`
 | 
											
												
													
														|  | 
 |  | +2. 点击待审批项目(红桥新村)
 | 
											
												
													
														|  | 
 |  | +3. 验证URL:应为 `http://localhost:4200/wxwork/cDL6R1hgSi/team-leader/project-detail/B2wtFHIF6k`
 | 
											
												
													
														|  | 
 |  | +4. 验证页面显示:
 | 
											
												
													
														|  | 
 |  | +   - ✅ 四阶段导航显示
 | 
											
												
													
														|  | 
 |  | +   - ✅ 审批面板显示(橙色横幅)
 | 
											
												
													
														|  | 
 |  | +   - ✅ "通过审批"和"驳回订单"按钮显示
 | 
											
												
													
														|  | 
 |  | +   - ✅ 订单信息正确显示
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +---
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +### 功能增强2:审批时修改设计师分配
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +**新增时间:** 2025-10-28  
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +**功能描述:**  
 | 
											
												
													
														|  | 
 |  | +组长在审批订单时,可以直接在审批面板中修改设计师分配,无需返回订单页面,提高审批效率。
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +**实现内容:**
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +1. **编辑按钮**
 | 
											
												
													
														|  | 
 |  | +   - 在"设计师分配"卡片右上角添加"✏️ 编辑"按钮
 | 
											
												
													
														|  | 
 |  | +   - 点击后进入编辑模式
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +2. **编辑模式界面**
 | 
											
												
													
														|  | 
 |  | +   - 显示当前分配的设计师列表
 | 
											
												
													
														|  | 
 |  | +   - 每个设计师显示:头像、姓名、负责空间
 | 
											
												
													
														|  | 
 |  | +   - 提供操作按钮:
 | 
											
												
													
														|  | 
 |  | +     - 🗑️ 移除设计师
 | 
											
												
													
														|  | 
 |  | +     - 编辑空间 - 修改该设计师负责的空间
 | 
											
												
													
														|  | 
 |  | +     - ➕ 添加设计师 - 添加新的设计师
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +3. **编辑操作**
 | 
											
												
													
														|  | 
 |  | +   - **移除设计师**:点击🗑️按钮移除该设计师
 | 
											
												
													
														|  | 
 |  | +   - **编辑空间**:弹出输入框,修改空间列表(逗号分隔)
 | 
											
												
													
														|  | 
 |  | +   - **添加设计师**:弹出输入框,输入设计师姓名
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +4. **保存/取消**
 | 
											
												
													
														|  | 
 |  | +   - **保存修改**:更新审批数据和项目数据
 | 
											
												
													
														|  | 
 |  | +   - **取消**:放弃修改,恢复原始数据
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +**视觉效果:**
 | 
											
												
													
														|  | 
 |  | +- 编辑模式卡片:蓝色边框 + 浅蓝背景
 | 
											
												
													
														|  | 
 |  | +- 设计师项:白色背景 + 悬停高亮
 | 
											
												
													
														|  | 
 |  | +- 按钮:彩色图标 + 悬停动画
 | 
											
												
													
														|  | 
 |  | +- 操作反馈:保存后显示"设计师分配已更新"提示
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +**技术实现:**
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +```typescript
 | 
											
												
													
														|  | 
 |  | +// TypeScript 关键方法
 | 
											
												
													
														|  | 
 |  | +startEditTeams()      // 开启编辑模式
 | 
											
												
													
														|  | 
 |  | +cancelEditTeams()     // 取消编辑
 | 
											
												
													
														|  | 
 |  | +saveTeamsEdit()       // 保存修改
 | 
											
												
													
														|  | 
 |  | +removeTeam(index)     // 移除设计师
 | 
											
												
													
														|  | 
 |  | +addTeamMember()       // 添加设计师
 | 
											
												
													
														|  | 
 |  | +editTeamSpaces(team)  // 编辑空间
 | 
											
												
													
														|  | 
 |  | +```
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +**涉及文件:**
 | 
											
												
													
														|  | 
 |  | +- `src/app/shared/components/order-approval-panel/order-approval-panel.component.ts`
 | 
											
												
													
														|  | 
 |  | +- `src/app/shared/components/order-approval-panel/order-approval-panel.component.html`
 | 
											
												
													
														|  | 
 |  | +- `src/app/shared/components/order-approval-panel/order-approval-panel.component.scss`
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +**使用流程:**
 | 
											
												
													
														|  | 
 |  | +1. 组长查看待审批订单
 | 
											
												
													
														|  | 
 |  | +2. 在审批面板中点击"设计师分配"卡片右上角的"✏️ 编辑"按钮
 | 
											
												
													
														|  | 
 |  | +3. 进入编辑模式,修改设计师分配:
 | 
											
												
													
														|  | 
 |  | +   - 移除不合适的设计师
 | 
											
												
													
														|  | 
 |  | +   - 添加新的设计师(打开专业的设计师选择弹窗)
 | 
											
												
													
														|  | 
 |  | +   - 调整各设计师负责的空间
 | 
											
												
													
														|  | 
 |  | +4. 点击"保存修改"确认,或点击"取消"放弃修改
 | 
											
												
													
														|  | 
 |  | +5. 继续进行审批操作(通过/驳回)
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +**改进更新(2025-10-28):**
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +集成了项目详情页的设计师分配弹窗组件(`DesignerTeamAssignmentModalComponent`),提供更专业的设计师选择体验:
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +**弹窗功能特性:**
 | 
											
												
													
														|  | 
 |  | +- ✅ **项目组视图**:按项目组展示设计师列表
 | 
											
												
													
														|  | 
 |  | +- ✅ **设计师状态**:显示设计师工作状态(空闲/忙碌/对图中)
 | 
											
												
													
														|  | 
 |  | +- ✅ **工作量显示**:查看每个设计师当前工作量
 | 
											
												
													
														|  | 
 |  | +- ✅ **日历视图**:查看设计师时间安排和可用性
 | 
											
												
													
														|  | 
 |  | +- ✅ **空间分配**:选择设计师时同时分配负责空间
 | 
											
												
													
														|  | 
 |  | +- ✅ **跨组协作**:支持选择多个项目组的设计师
 | 
											
												
													
														|  | 
 |  | +- ✅ **实时数据**:从数据库加载真实的设计师和项目数据
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +**交互流程:**
 | 
											
												
													
														|  | 
 |  | +1. 点击 **"➕ 添加设计师"** 按钮
 | 
											
												
													
														|  | 
 |  | +2. 弹出专业的设计师选择界面
 | 
											
												
													
														|  | 
 |  | +3. 选择项目组,查看该组设计师
 | 
											
												
													
														|  | 
 |  | +4. 查看设计师状态、工作量、时间安排
 | 
											
												
													
														|  | 
 |  | +5. 选择合适的设计师
 | 
											
												
													
														|  | 
 |  | +6. 为设计师分配负责的空间
 | 
											
												
													
														|  | 
 |  | +7. 确认选择,设计师自动添加到审批面板
 | 
											
												
													
														|  | 
 |  | +8. 继续编辑或保存修改
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +**技术实现:**
 | 
											
												
													
														|  | 
 |  | +```typescript
 | 
											
												
													
														|  | 
 |  | +// 关键方法更新
 | 
											
												
													
														|  | 
 |  | +addTeamMember()              // 打开设计师选择弹窗
 | 
											
												
													
														|  | 
 |  | +closeDesignerModal()         // 关闭弹窗
 | 
											
												
													
														|  | 
 |  | +handleDesignerAssignment()   // 处理弹窗返回的选择结果
 | 
											
												
													
														|  | 
 |  | +```
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +**涉及组件:**
 | 
											
												
													
														|  | 
 |  | +- `DesignerTeamAssignmentModalComponent` - 设计师分配弹窗(复用)
 | 
											
												
													
														|  | 
 |  | +- `DesignerCalendarComponent` - 设计师日历组件(弹窗内部使用)
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +
 |