项目-订单分配.md 57 KB

项目管理 - 订单分配阶段 PRD (基于Project表产品设计)

1. 功能概述

1.1 阶段定位

订单分配阶段是项目管理流程的第一个环节,主要负责将客户咨询转化为正式项目订单,并完成产品设计化管理。该阶段通过Project表统一管理各个空间设计产品,是连接客服端和设计师端的关键桥梁。

1.2 核心目标

  • 多产品设计识别与管理:智能识别单产品/多产品项目,基于Project表统一管理
  • 产品化项目创建:将客户需求转化为标准化的设计产品
  • 完成客户信息的结构化录入和同步
  • 确定项目报价和付款条件(支持多产品设计报价策略)
  • 匹配并分配合适的设计师资源(考虑多产品协调需求)
  • 建立项目基础档案,为后续环节提供数据支撑

1.3 涉及角色

  • 客服人员:负责创建订单、录入客户信息、初步需求沟通、产品识别
  • 设计师:接收产品分配、查看项目基础信息、产品详情
  • 组长:查看团队产品分配情况、协调设计师资源、跨产品协调

2. 基于Project表的产品设计管理

2.1 产品识别与分类

2.1.1 智能产品设计识别系统

class ProductIdentificationService {
  // 基于客户需求描述自动识别空间设计产品
  async identifyProductsFromDescription(description: string): Promise<ProductIdentificationResult> {
    const result: ProductIdentificationResult = {
      identifiedProducts: [],
      confidence: 0,
      reasoning: '',
      suggestedQuestions: [],
      recommendedConfiguration: null
    };

    // 产品类型关键词映射
    const productKeywords = {
      [ProductType.LIVING_ROOM]: ['客厅', '起居室', '会客厅', '茶室', '待客区', '客厅背景墙'],
      [ProductType.BEDROOM]: ['卧室', '主卧', '次卧', '儿童房', '老人房', '客房', '主人房', '主卧套间'],
      [ProductType.KITCHEN]: ['厨房', '开放式厨房', '中西厨', '餐厨一体', '橱柜设计'],
      [ProductType.BATHROOM]: ['卫生间', '浴室', '洗手间', '盥洗室', '主卫', '次卫', '干湿分离'],
      [ProductType.DINING_ROOM]: ['餐厅', '餐厅区', '用餐区', '就餐空间', '客餐厅'],
      [ProductType.STUDY]: ['书房', '工作室', '办公室', '学习区', '阅读区', '家庭办公'],
      [ProductType.BALCONY]: ['阳台', '露台', '花园阳台', '休闲阳台', '生活阳台'],
      [ProductType.CORRIDOR]: ['走廊', '过道', '玄关', '门厅', '入户', '玄关柜'],
      [ProductType.STORAGE]: ['储物间', '衣帽间', '杂物间', '收纳空间', '衣柜设计'],
      [ProductType.ENTRANCE]: ['门厅', '玄关', '入户花园', '门廊'],
      [ProductType.WARDROBE]: ['衣柜', '衣帽间', '储物柜', '定制柜'],
      [ProductType.TV_BACKGROUND]: ['电视背景墙', '影视墙', '电视柜'],
      [ProductType.SOFA_BACKGROUND]: ['沙发背景墙', '背景墙'],
      [ProductType.BED_BACKGROUND]: ['床头背景墙', '床背景'],
      [ProductType.OTHER]: ['其他', '定制', '特殊空间']
    };

    // 分析描述中的产品关键词
    const foundProducts: Array<{ type: ProductType; keywords: string[]; confidence: number; metadata: any }> = [];

    for (const [productType, keywords] of Object.entries(productKeywords)) {
      const matchedKeywords = keywords.filter(keyword =>
        description.toLowerCase().includes(keyword.toLowerCase())
      );

      if (matchedKeywords.length > 0) {
        // 计算产品复杂度和特征
        const complexity = this.assessProductComplexity(description, productType);
        const estimatedArea = this.estimateProductArea(description, productType);
        const specialFeatures = this.extractSpecialFeatures(description, productType);

        foundProducts.push({
          type: productType as ProductType,
          keywords: matchedKeywords,
          confidence: matchedKeywords.length / keywords.length,
          metadata: {
            complexity,
            estimatedArea,
            specialFeatures,
            priority: this.calculateProductPriority(productType, complexity)
          }
        });
      }
    }

    // 按置信度排序
    foundProducts.sort((a, b) => b.confidence - a.confidence);

    // 构建识别结果
    result.identifiedProducts = foundProducts.map(fp => ({
      type: fp.type,
      productName: this.getDefaultProductName(fp.type),
      priority: fp.metadata.priority,
      confidence: fp.confidence,
      identifiedKeywords: fp.keywords,
      estimatedArea: fp.metadata.estimatedArea,
      complexity: fp.metadata.complexity,
      specialFeatures: fp.metadata.specialFeatures
    }));

    // 计算整体置信度
    result.confidence = foundProducts.length > 0
      ? foundProducts.reduce((sum, fp) => sum + fp.confidence, 0) / foundProducts.length
      : 0;

    // 生成推理说明
    result.reasoning = this.generateIdentificationReasoning(foundProducts);

    // 生成建议问题
    result.suggestedQuestions = this.generateClarifyingQuestions(foundProducts);

    // 生成推荐配置
    result.recommendedConfiguration = this.generateRecommendedConfiguration(foundProducts);

    return result;
  }

  // 基于面积和预算推断产品设计数量和类型
  estimateProductConfiguration(totalArea: number, budget: number, description: string): ProductEstimationResult {
    const result: ProductEstimationResult = {
      estimatedProductCount: 1,
      confidence: 0.5,
      reasoning: '',
      possibleProductTypes: [],
      recommendedProducts: [],
      budgetAllocation: {},
      designComplexity: 'medium'
    };

    // 基于面积的产品数量估算
    const areaBasedCount = Math.max(1, Math.floor(totalArea / 18)); // 每18平米一个主要产品

    // 基于预算的产品数量估算
    const budgetBasedCount = Math.max(1, Math.floor(budget / 25000)); // 每2.5万一个产品

    // 综合判断
    const finalCount = Math.min(areaBasedCount, budgetBasedCount);
    result.estimatedProductCount = finalCount;

    // 推断可能的产品类型
    result.possibleProductTypes = this.inferPossibleProductTypes(totalArea, budget, description);

    // 生成推荐产品配置
    result.recommendedProducts = this.generateRecommendedProducts(
      result.possibleProductTypes,
      totalArea,
      budget,
      finalCount
    );

    // 计算预算分配
    result.budgetAllocation = this.calculateBudgetAllocation(result.recommendedProducts, budget);

    // 评估设计复杂度
    result.designComplexity = this.assessOverallDesignComplexity(result.recommendedProducts, description);

    // 生成推理
    result.reasoning = `基于面积${totalArea}平米和预算${budget}元,估算需要${finalCount}个主要产品设计产品,设计复杂度为${result.designComplexity}`;

    return result;
  }

  // 生成产品设计配置建议
  generateProductConfiguration(
    identifiedProducts: IdentifiedProduct[],
    totalArea: number,
    budget: number
  ): ProductConfiguration {
    const configuration: ProductConfiguration = {
      products: [],
      totalEstimatedBudget: 0,
      budgetAllocation: {},
      recommendations: [],
      designStyle: null,
      complexityAnalysis: null,
      timelineEstimate: null
    };

    // 为识别出的产品创建配置
    for (const product of identifiedProducts) {
      const productConfig = this.createProductConfiguration(product, totalArea, budget);
      configuration.products.push(productConfig);
      configuration.budgetAllocation[product.type] = productConfig.estimatedBudget;
    }

    // 如果没有识别出产品,创建默认配置
    if (configuration.products.length === 0) {
      const defaultProduct = this.createDefaultProductConfiguration(totalArea, budget);
      configuration.products.push(defaultProduct);
      configuration.budgetAllocation[defaultProduct.type] = defaultProduct.estimatedBudget;
    }

    // 计算总预算
    configuration.totalEstimatedBudget = Object.values(configuration.budgetAllocation)
      .reduce((sum, budget) => sum + budget, 0);

    // 分析设计风格
    configuration.designStyle = this.analyzeDesignStyle(identifiedProducts);

    // 分析复杂度
    configuration.complexityAnalysis = this.analyzeComplexity(configuration.products);

    // 估算时间线
    configuration.timelineEstimate = this.estimateTimeline(configuration.products);

    // 生成建议
    configuration.recommendations = this.generateConfigurationRecommendations(configuration);

    return configuration;
  }

  private createProductConfiguration(
    product: IdentifiedProduct,
    totalArea: number,
    budget: number
  ): ProductConfig {
    const basePrice = this.getBasePriceForProductType(product.type, product.estimatedArea || 0);
    const complexityMultiplier = this.getComplexityMultiplier(product.complexity);
    const styleFactor = this.getStyleFactor(product.type);

    return {
      type: product.type,
      productName: product.productName,
      estimatedArea: product.estimatedArea || this.getDefaultAreaForType(product.type),
      estimatedBudget: Math.round(basePrice * complexityMultiplier * styleFactor),
      priority: product.priority,
      complexity: product.complexity,
      specialFeatures: product.specialFeatures || [],
      estimatedDuration: this.estimateProductDuration(product.type, product.complexity),
      designerSkills: this.getRequiredDesignerSkills(product.type, product.complexity),
      deliverables: this.getStandardDeliverables(product.type)
    };
  }
}

interface ProductIdentificationResult {
  identifiedProducts: IdentifiedProduct[];
  confidence: number;
  reasoning: string;
  suggestedQuestions: string[];
  recommendedConfiguration: ProductConfiguration | null;
}

interface IdentifiedProduct {
  type: ProductType;
  productName: string;
  priority: number;
  confidence: number;
  identifiedKeywords: string[];
  estimatedArea?: number;
  complexity: 'simple' | 'medium' | 'complex';
  specialFeatures?: string[];
}

interface ProductEstimationResult {
  estimatedProductCount: number;
  confidence: number;
  reasoning: string;
  possibleProductTypes: ProductType[];
  recommendedProducts: ProductConfig[];
  budgetAllocation: Record<ProductType, number>;
  designComplexity: 'simple' | 'medium' | 'complex';
}

interface ProductConfiguration {
  products: ProductConfig[];
  totalEstimatedBudget: number;
  budgetAllocation: Record<ProductType, number>;
  recommendations: string[];
  designStyle: DesignStyleAnalysis | null;
  complexityAnalysis: ComplexityAnalysis | null;
  timelineEstimate: TimelineEstimate | null;
}

interface ProductConfig {
  type: ProductType;
  productName: string;
  estimatedArea: number;
  estimatedBudget: number;
  priority: number;
  complexity: 'simple' | 'medium' | 'complex';
  specialFeatures: string[];
  estimatedDuration: number;
  designerSkills: string[];
  deliverables: string[];
}

2.1.2 产品设计管理界面

<!-- 产品设计管理面板 -->
<div class="product-management-panel">
  <div class="panel-header">
    <h3>产品设计配置</h3>
    <div class="project-type-indicator">
      <span class="indicator-label">项目类型:</span>
      <span class="indicator-value"
            [class.single-product]="isSingleProductProject"
            [class.multi-product]="!isSingleProductProject">
        {{ isSingleProductProject ? '单产品项目' : '多产品项目' }}
      </span>
      <span class="product-count">{{ projectProducts.length }} 个产品</span>
    </div>
  </div>

  <!-- 产品识别结果 -->
  <div class="product-identification-result" *ngIf="productIdentificationResult">
    <div class="identification-summary">
      <h4>识别到 {{ productIdentificationResult.identifiedProducts.length }} 个产品设计</h4>
      <div class="confidence-indicator">
        <span class="confidence-label">识别置信度:</span>
        <div class="confidence-bar">
          <div class="confidence-fill"
               [style.width.%]="productIdentificationResult.confidence * 100"
               [class.high]="productIdentificationResult.confidence >= 0.8"
               [class.medium]="productIdentificationResult.confidence >= 0.5 && productIdentificationResult.confidence < 0.8"
               [class.low]="productIdentificationResult.confidence < 0.5">
          </div>
        </div>
        <span class="confidence-value">{{ Math.round(productIdentificationResult.confidence * 100) }}%</span>
      </div>
    </div>

    <div class="identification-reasoning">
      <p>{{ productIdentificationResult.reasoning }}</p>
    </div>

    <!-- 推荐配置 -->
    @if (productIdentificationResult.recommendedConfiguration) {
      <div class="recommended-configuration">
        <h5>推荐配置</h5>
        <div class="configuration-summary">
          <div class="summary-item">
            <label>设计风格:</label>
            <span>{{ productIdentificationResult.recommendedConfiguration.designStyle?.primaryStyle || '现代简约' }}</span>
          </div>
          <div class="summary-item">
            <label>设计复杂度:</label>
            <span class="complexity-badge complexity-{{ productIdentificationResult.recommendedConfiguration.complexityAnalysis?.overallComplexity }}">
              {{ getComplexityName(productIdentificationResult.recommendedConfiguration.complexityAnalysis?.overallComplexity) }}
            </span>
          </div>
          <div class="summary-item">
            <label>预估工期:</label>
            <span>{{ productIdentificationResult.recommendedConfiguration.timelineEstimate?.totalDays || 30 }} 天</span>
          </div>
        </div>
      </div>
    }

    <!-- 建议问题 -->
    <div class="suggested-questions" *ngIf="productIdentificationResult.suggestedQuestions.length > 0">
      <h5>建议确认的问题:</h5>
      <ul>
        @for (question of productIdentificationResult.suggestedQuestions; track question) {
          <li>{{ question }}</li>
        }
      </ul>
    </div>
  </div>

  <!-- 产品列表 -->
  <div class="products-list">
    <div class="list-header">
      <h4>产品设计列表</h4>
      <div class="list-actions">
        <button class="btn-add-product" (click)="showAddProductDialog()">
          <i class="icon-plus"></i> 添加产品
        </button>
        <button class="btn-secondary" (click)="showBatchProductConfig = true">
          <i class="icon-settings"></i> 批量配置
        </button>
      </div>
    </div>

    <div class="products-grid">
      @for (product of projectProducts; track product.id) {
        <div class="product-card"
             [class.priority-high]="product.priority >= 8"
             [class.priority-medium]="product.priority >= 5 && product.priority < 8"
             [class.priority-low]="product.priority < 5"
             [class.complexity-simple]="product.complexity === 'simple'"
             [class.complexity-medium]="product.complexity === 'medium'"
             [class.complexity-complex]="product.complexity === 'complex'">

          <!-- 产品头部 -->
          <div class="product-header">
            <div class="product-icon">
              <i class="icon-{{ getProductIcon(product.type) }}"></i>
            </div>
            <div class="product-info">
              <input type="text"
                     [(ngModel)]="product.name"
                     class="product-name-input"
                     placeholder="产品名称">
              <select [(ngModel)]="product.type" class="product-type-select">
                <option value="{{ ProductType.LIVING_ROOM }}">客厅</option>
                <option value="{{ ProductType.BEDROOM }}">卧室</option>
                <option value="{{ ProductType.KITCHEN }}">厨房</option>
                <option value="{{ ProductType.BATHROOM }}">卫生间</option>
                <option value="{{ ProductType.DINING_ROOM }}">餐厅</option>
                <option value="{{ ProductType.STUDY }}">书房</option>
                <option value="{{ ProductType.BALCONY }}">阳台</option>
                <option value="{{ ProductType.CORRIDOR }}">走廊/玄关</option>
                <option value="{{ ProductType.STORAGE }}">储物间</option>
                <option value="{{ ProductType.ENTRANCE }}">门厅</option>
                <option value="{{ ProductType.TV_BACKGROUND }}">电视背景墙</option>
                <option value="{{ ProductType.SOFA_BACKGROUND }}">沙发背景墙</option>
                <option value="{{ ProductType.BED_BACKGROUND }}">床头背景墙</option>
                <option value="{{ ProductType.WARDROBE }}">衣柜</option>
                <option value="{{ ProductType.OTHER }}">其他</option>
              </select>
            </div>
            <div class="product-actions">
              <button class="btn-icon" (click)="editProduct(product.id)" title="编辑">
                <i class="icon-edit"></i>
              </button>
              <button class="btn-icon" (click)="duplicateProduct(product.id)" title="复制">
                <i class="icon-copy"></i>
              </button>
              <button class="btn-icon danger" (click)="removeProduct(product.id)" title="删除">
                <i class="icon-delete"></i>
              </button>
            </div>
          </div>

          <!-- 产品详情 -->
          <div class="product-details">
            <div class="detail-row">
              <label>面积:</label>
              <input type="number"
                     [(ngModel)]="product.area"
                     min="1"
                     step="0.1"
                     class="detail-input">
              <span class="unit">m²</span>
            </div>

            <div class="detail-row">
              <label>优先级:</label>
              <select [(ngModel)]="product.priority" class="priority-select">
                <option [ngValue]="10">最高优先级</option>
                <option [ngValue]="8">高优先级</option>
                <option [ngValue]="5">中优先级</option>
                <option [ngValue]="3">低优先级</option>
                <option [ngValue]="1">最低优先级</option>
              </select>
            </div>

            <div class="detail-row">
              <label>复杂度:</label>
              <select [(ngModel)]="product.complexity" class="complexity-select">
                <option value="simple">简单</option>
                <option value="medium">中等</option>
                <option value="complex">复杂</option>
              </select>
            </div>

            <div class="detail-row">
              <label>预估工期:</label>
              <input type="number"
                     [(ngModel)]="product.estimatedDuration"
                     min="1"
                     class="detail-input">
              <span class="unit">天</span>
            </div>

            <!-- 特殊特征 -->
            <div class="special-features">
              <label>特殊特征:</label>
              <div class="features-tags">
                @for (feature of product.specialFeatures || []; track feature) {
                  <span class="feature-tag">
                    {{ feature }}
                    <button class="remove-feature" (click)="removeFeature(product.id, feature)">
                      <i class="icon-close"></i>
                    </button>
                  </span>
                }
                <button class="add-feature-btn" (click)="showAddFeatureDialog(product.id)">
                  <i class="icon-plus"></i>
                </button>
              </div>
            </div>
          </div>

          <!-- 产品预算 -->
          <div class="product-budget">
            <label>预估预算:</label>
            <div class="budget-input-group">
              <input type="number"
                     [(ngModel)]="product.estimatedBudget"
                     min="0"
                     step="100"
                     class="budget-input">
              <span class="currency">元</span>
            </div>
            <div class="budget-breakdown" *ngIf="product.estimatedBudget">
              <div class="breakdown-item">
                <span class="label">设计费:</span>
                <span class="value">¥{{ Math.round(product.estimatedBudget * 0.3).toLocaleString() }}</span>
              </div>
              <div class="breakdown-item">
                <span class="label">建模费:</span>
                <span class="value">¥{{ Math.round(product.estimatedBudget * 0.3).toLocaleString() }}</span>
              </div>
              <div class="breakdown-item">
                <span class="label">渲染费:</span>
                <span class="value">¥{{ Math.round(product.estimatedBudget * 0.25).toLocaleString() }}</span>
              </div>
              <div class="breakdown-item">
                <span class="label">软装费:</span>
                <span class="value">¥{{ Math.round(product.estimatedBudget * 0.15).toLocaleString() }}</span>
              </div>
            </div>
          </div>
        </div>
      }
    </div>
  </div>

  <!-- 产品统计信息 -->
  <div class="product-statistics">
    <div class="stat-group">
      <h4>统计信息</h4>
      <div class="stat-items">
        <div class="stat-item">
          <span class="stat-label">产品总数:</span>
          <span class="stat-value">{{ projectProducts.length }}</span>
        </div>
        <div class="stat-item">
          <span class="stat-label">总面积:</span>
          <span class="stat-value">{{ totalProductArea }}m²</span>
        </div>
        <div class="stat-item">
          <span class="stat-label">总预算:</span>
          <span class="stat-value">¥{{ totalProductBudget.toLocaleString() }}</span>
        </div>
        <div class="stat-item">
          <span class="stat-label">平均单价:</span>
          <span class="stat-value">¥{{ averagePricePerSqm.toLocaleString() }}/m²</span>
        </div>
        <div class="stat-item">
          <span class="stat-label">预估总工期:</span>
          <span class="stat-value">{{ totalEstimatedDuration }}天</span>
        </div>
      </div>
    </div>

    <!-- 产品类型分布 -->
    <div class="product-distribution">
      <h4>产品类型分布</h4>
      <div class="distribution-chart">
        @for (type of getProductTypeDistribution(); track type.type) {
          <div class="distribution-item">
            <div class="type-info">
              <i class="icon-{{ getProductIcon(type.type) }}"></i>
              <span class="type-name">{{ getProductTypeName(type.type) }}</span>
            </div>
            <div class="type-stats">
              <span class="count">{{ type.count }}</span>
              <span class="percentage">{{ type.percentage }}%</span>
            </div>
          </div>
        }
      </div>
    </div>
  </div>
</div>

2.2 多产品报价管理

2.2.1 产品报价计算

class MultiProductQuotationService {
  // 计算多产品项目报价
  calculateMultiProductQuotation(
    products: Project[],
    globalOptions: QuotationOptions
  ): MultiProductQuotation {

    const quotation: MultiProductQuotation = {
      projectId: this.getProjectId(),
      quotationDate: new Date(),
      currency: 'CNY',

      // 产品报价明细
      productQuotations: [],

      // 全局折扣和优惠
      globalDiscounts: [],

      // 汇总信息
      summary: {
        totalBaseAmount: 0,
        totalDiscountAmount: 0,
        finalAmount: 0,
        averagePricePerSqm: 0,
        totalArea: 0,
        productCount: products.length
      },

      // 报价策略
      pricingStrategy: this.determinePricingStrategy(products),

      // 时间估算
      timeline: this.calculateProjectTimeline(products)
    };

    // 计算各产品报价
    for (const product of products) {
      const productQuotation = this.calculateProductQuotation(product, globalOptions);
      quotation.productQuotations.push(productQuotation);
      quotation.summary.totalArea += product.area || 0;
    }

    // 计算基础总额
    quotation.summary.totalBaseAmount = quotation.productQuotations
      .reduce((sum, pq) => sum + pq.totalAmount, 0);

    // 应用多产品折扣
    quotation.globalDiscounts = this.calculateMultiProductDiscounts(
      products,
      quotation.summary.totalBaseAmount,
      quotation.pricingStrategy
    );

    // 计算折扣总额
    quotation.summary.totalDiscountAmount = quotation.globalDiscounts
      .reduce((sum, discount) => sum + discount.value, 0);

    // 计算最终金额
    quotation.summary.finalAmount = quotation.summary.totalBaseAmount - quotation.summary.totalDiscountAmount;

    // 计算平均单价
    quotation.summary.averagePricePerSqm = quotation.summary.totalArea > 0
      ? quotation.summary.finalAmount / quotation.summary.totalArea
      : 0;

    return quotation;
  }

  // 计算单个产品报价
  private calculateProductQuotation(
    product: Project,
    options: QuotationOptions
  ): ProductQuotation {

    // 基础价格计算
    const basePrice = this.calculateBasePrice(product, options);

    // 复杂度调整
    const complexityMultiplier = this.getComplexityMultiplier(product.complexity);

    // 优先级调整
    const priorityMultiplier = this.getPriorityMultiplier(product.priority);

    // 面积系数
    const areaCoefficient = this.getAreaCoefficient(product.area || 0);

    // 设计风格系数
    const styleCoefficient = this.getStyleCoefficient(options.designStyle);

    // 产品类型系数
    const typeCoefficient = this.getTypeCoefficient(product.type);

    // 计算最终价格
    const finalPrice = basePrice * complexityMultiplier * priorityMultiplier * areaCoefficient * styleCoefficient * typeCoefficient;

    const productQuotation: ProductQuotation = {
      productId: product.id,
      productName: product.name,
      productType: product.type as ProductType,
      area: product.area || 0,

      // 价格明细
      priceBreakdown: {
        basePrice: basePrice,
        complexityAdjustment: basePrice * (complexityMultiplier - 1),
        priorityAdjustment: basePrice * (priorityMultiplier - 1),
        areaAdjustment: basePrice * (areaCoefficient - 1),
        styleAdjustment: basePrice * (styleCoefficient - 1),
        typeAdjustment: basePrice * (typeCoefficient - 1),
      },

      // 总价
      totalAmount: finalPrice,

      // 单价
      unitPrice: product.area ? finalPrice / product.area : 0,

      // 时间预估
      estimatedDays: this.calculateEstimatedDays(product, finalPrice),

      // 设计师配置
      designerRequirements: this.getDesignerRequirements(product),

      // 交付物清单
      deliverables: this.getDeliverablesForProduct(product),

      // 风险评估
      riskAssessment: this.assessProductRisk(product),

      // 质量标准
      qualityStandards: this.getQualityStandardsForProduct(product)
    };

    return productQuotation;
  }

  // 计算多产品折扣
  private calculateMultiProductDiscounts(
    products: Project[],
    baseAmount: number,
    strategy: PricingStrategy
  ): QuotationDiscount[] {
    const discounts: QuotationDiscount[] = [];

    // 1. 产品数量折扣
    const productCount = products.length;
    if (productCount >= 8) {
      discounts.push({
        type: 'product_count',
        name: '8产品及以上项目折扣',
        description: '8个及以上产品项目享受15%折扣',
        value: baseAmount * 0.15,
        isApplicable: true,
        tier: 'platinum'
      });
    } else if (productCount >= 5) {
      discounts.push({
        type: 'product_count',
        name: '5-7产品项目折扣',
        description: '5-7个产品项目享受12%折扣',
        value: baseAmount * 0.12,
        isApplicable: true,
        tier: 'gold'
      });
    } else if (productCount >= 3) {
      discounts.push({
        type: 'product_count',
        name: '3-4产品项目折扣',
        description: '3-4个产品项目享受8%折扣',
        value: baseAmount * 0.08,
        isApplicable: true,
        tier: 'silver'
      });
    } else if (productCount >= 2) {
      discounts.push({
        type: 'product_count',
        name: '双产品项目折扣',
        description: '双产品项目享受5%折扣',
        value: baseAmount * 0.05,
        isApplicable: true,
        tier: 'bronze'
      });
    }

    // 2. 总额折扣
    if (baseAmount >= 500000) {
      discounts.push({
        type: 'total_amount',
        name: '高额度项目折扣',
        description: '项目总额超过50万享受额外5%折扣',
        value: baseAmount * 0.05,
        isApplicable: true,
        tier: 'vip'
      });
    } else if (baseAmount >= 200000) {
      discounts.push({
        type: 'total_amount',
        name: '中高额度项目折扣',
        description: '项目总额超过20万享受额外3%折扣',
        value: baseAmount * 0.03,
        isApplicable: true,
        tier: 'premium'
      });
    }

    // 3. 复杂度折扣(针对高复杂度产品组合)
    const allHighComplexity = products.every(product => product.complexity === 'complex');
    if (allHighComplexity && productCount >= 3) {
      discounts.push({
        type: 'complexity_bonus',
        name: '高复杂度项目折扣',
        description: '全高复杂度多产品项目享受4%折扣',
        value: baseAmount * 0.04,
        isApplicable: true,
        tier: 'complexity'
      });
    }

    // 4. 策略折扣
    if (strategy === 'premium' && productCount >= 3) {
      discounts.push({
        type: 'strategy_bonus',
        name: '高端项目策略折扣',
        description: '高端多产品项目额外3%折扣',
        value: baseAmount * 0.03,
        isApplicable: true,
        tier: 'strategy'
      });
    }

    // 5. 季节性折扣
    const seasonalDiscount = this.getSeasonalDiscount();
    if (seasonalDiscount) {
      discounts.push(seasonalDiscount);
    }

    return discounts;
  }

  // 生成报价单
  async generateQuotationDocument(quotation: MultiProductQuotation): Promise<QuotationDocument> {
    const document: QuotationDocument = {
      id: `quotation_${Date.now()}`,
      projectId: quotation.projectId,
      documentNumber: this.generateQuotationNumber(),
      issueDate: new Date(),
      validUntil: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000), // 30天有效期
      currency: quotation.currency,
      quotation: quotation,

      // 客户信息
      customerInfo: await this.getCustomerInfo(),

      // 项目信息
      projectInfo: await this.getProjectInfo(),

      // 支付条款
      paymentTerms: this.generatePaymentTerms(quotation),

      // 服务内容
      serviceScope: this.generateServiceScope(quotation),

      // 注意事项
      notes: this.generateQuotationNotes(quotation),

      // 产品汇总
      productSummary: this.generateProductSummary(quotation),

      // 设计团队信息
      designTeam: await this.getDesignTeamInfo()
    };

    return document;
  }

  // 确定报价策略
  private determinePricingStrategy(products: Project[]): PricingStrategy {
    const avgBudget = products.reduce((sum, p) => sum + (p.estimatedBudget || 0), 0) / products.length;
    const avgComplexity = products.reduce((sum, p) => sum + this.getComplexityScore(p.complexity), 0) / products.length;

    if (avgBudget > 100000 && avgComplexity > 2) {
      return 'premium';
    } else if (avgBudget > 50000) {
      return 'standard';
    } else {
      return 'economy';
    }
  }
}

interface MultiProductQuotation {
  projectId: string;
  quotationDate: Date;
  currency: string;
  productQuotations: ProductQuotation[];
  globalDiscounts: QuotationDiscount[];
  summary: {
    totalBaseAmount: number;
    totalDiscountAmount: number;
    finalAmount: number;
    averagePricePerSqm: number;
    totalArea: number;
    productCount: number;
  };
  pricingStrategy: PricingStrategy;
  timeline: ProjectTimeline;
}

interface ProductQuotation {
  productId: string;
  productName: string;
  productType: ProductType;
  area: number;
  priceBreakdown: {
    basePrice: number;
    complexityAdjustment: number;
    priorityAdjustment: number;
    areaAdjustment: number;
    styleAdjustment: number;
    typeAdjustment: number;
  };
  totalAmount: number;
  unitPrice: number;
  estimatedDays: number;
  designerRequirements: string[];
  deliverables: string[];
  riskAssessment: RiskAssessment;
  qualityStandards: QualityStandard[];
}

interface QuotationDiscount {
  type: 'product_count' | 'total_amount' | 'complexity_bonus' | 'strategy_bonus' | 'seasonal' | 'special_offer';
  name: string;
  description: string;
  value: number;
  isApplicable: boolean;
  tier: 'bronze' | 'silver' | 'gold' | 'platinum' | 'vip' | 'premium' | 'complexity' | 'strategy';
}

type PricingStrategy = 'economy' | 'standard' | 'premium';

3. 核心功能模块

3.1 客户信息管理

3.1.1 信息展示卡片

位置:订单分配阶段左侧面板

展示内容

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

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

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

  // 项目需求特征
  projectType?: string;   // 项目类型倾向:全屋设计/局部改造/软装设计
  budgetRange?: {         // 预算范围
    min: number;
    max: number;
    currency: string;
  };
  timeline?: {            // 时间要求
    preferredStart?: Date;
    deadline?: Date;
    urgency: 'low' | 'medium' | 'high';
  };
}

数据来源

  1. 客服端同步:通过路由查询参数 syncData 传递客户信息

    // 客服端跳转示例
    router.navigate(['/designer/project-detail', projectId], {
     queryParams: {
       syncData: JSON.stringify({
         customerInfo: {...},
         requirementInfo: {...},
         preferenceTags: [...],
         projectAnalysis: {...}
       })
     }
    });
    
  2. 实时同步机制

    • 每30秒自动同步一次客户信息
    • 显示同步状态指示器(同步中/已同步/同步失败)
    • 支持手动触发同步

交互特性

  • 卡片可展开/收起(isCustomerInfoExpanded
  • 展开时显示完整客户信息和标签
  • 收起时仅显示客户姓名和联系方式
  • 同步状态实时更新,显示"刚刚/X分钟前/X小时前"

3.1.2 客户搜索功能

适用场景:手动创建订单时快速选择已有客户

搜索逻辑

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

搜索结果展示

  • 下拉列表形式,支持分页加载
  • 每项显示:客户姓名、电话(脱敏)、客户类型、来源、最近项目数
  • 点击选择后自动填充表单
  • 支持客户历史项目查看

3.2 核心需求表单

3.2.1 必填项配置

表单定义

orderCreationForm = this.fb.group({
  orderAmount: ['', [Validators.required, Validators.min(0)]],
  smallImageDeliveryTime: ['', Validators.required],
  decorationType: ['', Validators.required],
  requirementReason: ['', Validators.required],
  isMultiDesigner: [false],
  projectComplexity: ['medium'], // 新增:项目复杂度
  designStyle: [''],           // 新增:设计风格
  specialRequirements: ['']    // 新增:特殊要求
});

字段详解

字段名 类型 验证规则 说明 UI组件
orderAmount number required, min(0) 订单金额,单位:元 数字输入框,支持千分位格式化
smallImageDeliveryTime Date required 小图交付时间 日期选择器,限制最早日期为今天
decorationType string required 装修类型:全包/半包/清包/软装 下拉选择框
requirementReason string required 需求原因:新房装修/旧房改造/局部翻新 单选框组
isMultiDesigner boolean - 是否需要多设计师协作 复选框
projectComplexity string - 项目复杂度:简单/中等/复杂 下拉选择框
designStyle string - 设计风格偏好 下拉选择框或标签选择
specialRequirements string - 特殊要求说明 文本域

表单验证提示

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

3.2.2 可选信息表单

折叠面板设计

<div class="optional-info-section">
  <div class="section-header" (click)="isOptionalFormExpanded = !isOptionalFormExpanded">
    <span>可选信息</span>
    <span class="toggle-icon">{{ isOptionalFormExpanded ? '▼' : '▶' }}</span>
  </div>
  @if (isOptionalFormExpanded) {
    <div class="section-content">
      <!-- 可选字段表单 -->
    </div>
  }
</div>

可选字段

optionalForm = this.fb.group({
  largeImageDeliveryTime: [''],      // 大图交付时间
  spaceRequirements: [''],           // 空间需求描述(已移至产品设计)
  designAngles: [''],                // 设计角度要求
  specialAreaHandling: [''],         // 特殊区域处理说明
  materialRequirements: [''],        // 材质要求
  lightingRequirements: [''],         // 光照需求
  budgetRange: [''],                  // 预算范围
  timelinePreference: [''],           // 时间偏好
  qualityStandards: [''],             // 质量标准要求
});

3.3 产品报价明细组件

3.3.1 组件集成

组件标签

<app-product-quotation-details
  [initialData]="quotationData"
  [products]="projectProducts"
  [readonly]="isReadOnly()"
  (dataChange)="onQuotationDataChange($event)"
  (productChange)="onProductChange($event)">
</app-product-quotation-details>

数据结构

interface ProductQuotationData {
  products: Array<{
    id: string;
    name: string;                    // 产品名称:客厅设计、主卧设计
    type: ProductType;               // 产品类型
    area?: number;                   // 面积
    amount: number;                  // 金额
    description?: string;            // 描述
    estimatedDays: number;           // 预估工期
    complexity: string;             // 复杂度
    priority: number;                // 优先级
  }>;
  totalAmount: number;               // 总金额
  materialCost: number;              // 材料费
  laborCost: number;                 // 人工费
  designFee: number;                 // 设计费
  modelingFee: number;               // 建模费
  renderingFee: number;              // 渲染费
  softDecorFee: number;              // 软装费
  managementFee: number;             // 管理费
  discounts: DiscountItem[];         // 折扣项
  paymentSchedule: PaymentSchedule[]; // 付款计划
}

3.3.2 组件功能

  1. 产品报价管理

    • 基于产品设计自动生成报价
    • 支持手动调整各产品报价
    • 实时计算总金额和折扣
    • 支持批量调整价格策略
  2. AI辅助生成(增强功能):

    generateProductQuotations(): void {
     // 基于产品设计配置自动生成报价明细
     const productQuotations = this.projectProducts.map((product, index) => ({
       id: product.id,
       name: product.name,
       type: product.type,
       area: product.area || this.getDefaultAreaForType(product.type),
       amount: this.calculateProductPrice(product),
       estimatedDays: product.estimatedDuration || this.getDefaultDaysForType(product.type),
       description: `${product.name}设计费用`,
       complexity: product.complexity,
       priority: product.priority
     }));
    
     this.quotationData = {
       products: productQuotations,
       totalAmount: productQuotations.reduce((total, item) => total + item.amount, 0),
       // ... 其他费用计算
     };
    
     // 自动应用折扣策略
     this.applyDiscountStrategy();
    }
    
  3. 报价策略管理

    • 支持多种定价策略(经济型/标准型/高端型)
    • 自动计算多产品折扣
    • 支持季节性折扣和促销活动
    • 提供报价模板和快速复制功能
  4. 付款计划配置

    generatePaymentSchedule(totalAmount: number): PaymentSchedule[] {
     const schedules: PaymentSchedule[] = [
       {
         stage: '签约定金',
         percentage: 30,
         amount: totalAmount * 0.3,
         dueDate: new Date(),
         status: 'pending',
         description: '签约时支付定金'
       },
       {
         stage: '方案确认',
         percentage: 40,
         amount: totalAmount * 0.4,
         dueDate: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
         status: 'pending',
         description: '方案确认后支付'
       },
       {
         stage: '设计完成',
         percentage: 30,
         amount: totalAmount * 0.3,
         dueDate: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
         status: 'pending',
         description: '设计完成后支付尾款'
       }
     ];
    
     return schedules;
    }
    
  5. 报价单导出

    • 支持PDF格式报价单导出
    • 支持Excel格式明细导出
    • 提供多种报价单模板
    • 支持自定义公司品牌信息

3.4 设计师指派组件

3.4.1 组件集成

组件标签

<app-product-designer-assignment
  [projectData]="projectData"
  [products]="projectProducts"
  [readonly]="isReadOnly()"
  (assignmentChange)="onDesignerAssignmentChange($event)"
  (designerClick)="onDesignerClick($event)"
  (batchAssignmentChange)="onBatchAssignmentChange($event)">
</app-product-designer-assignment>

数据结构

interface ProductDesignerAssignmentData {
  assignments: ProductDesignerAssignment[];
  teamId: string;
  teamName: string;
  leaderId: string;
  assignmentDate: Date;
  expectedStartDate: Date;
  batchAssignmentMode: boolean;
  assignmentStrategy: AssignmentStrategy;
}

interface ProductDesignerAssignment {
  productId: string;
  productName: string;
  productType: ProductType;
  assignedDesigners: Designer[];
  primaryDesigner?: Designer;
  requiredSkills: string[];
  estimatedWorkload: number;
  assignmentDate: Date;
  status: 'pending' | 'confirmed' | 'in_progress' | 'completed';
}

interface Designer {
  id: string;
  name: string;
  avatar: string;
  teamId: string;
  teamName: string;
  isTeamLeader: boolean;
  status: 'idle' | 'busy' | 'unavailable' | 'on_vacation';
  currentProjects: number;
  skillMatch: number;              // 技能匹配度 0-100
  recentOrders: number;            // 近期订单数
  idleDays: number;                // 闲置天数
  workload: number;                // 工作负荷 0-100
  reviewDates: string[];           // 对图评审日期列表
  specialties: ProductType[];     // 专业领域
  experience: {
    years: number;
    completedProjects: number;
    clientSatisfaction: number;
  };
  rating: {
    overall: number;
    communication: number;
    quality: number;
    timeliness: number;
  };
}

3.4.2 产品设计师选择逻辑

智能推荐算法

calculateProductDesignerScore(designer: Designer, product: Project): number {
  let score = 0;

  // 1. 专业匹配度(权重40%)
  const specialtyMatch = designer.specialties.includes(product.type as ProductType) ? 100 : 0;
  score += specialtyMatch * 0.4;

  // 2. 技能匹配度(权重25%)
  const requiredSkills = this.getRequiredSkillsForProduct(product);
  const skillMatch = this.calculateSkillMatch(designer, requiredSkills);
  score += skillMatch * 0.25;

  // 3. 工作负荷(权重15%,负荷越低分数越高)
  score += (100 - designer.workload) * 0.15;

  // 4. 经验和评价(权重10%)
  const experienceScore = this.calculateExperienceScore(designer);
  score += experienceScore * 0.1;

  // 5. 近期活跃度(权重5%,活跃度适中分数越高)
  const activityScore = this.calculateActivityScore(designer);
  score += activityScore * 0.05;

  // 6. 产品复杂度适配(权重5%)
  const complexityScore = this.calculateComplexityMatch(designer, product.complexity);
  score += complexityScore * 0.05;

  return Math.min(100, Math.max(0, score));
}

// 产品批量分配算法
assignDesignersToProducts(products: Project[], availableDesigners: Designer[]): ProductDesignerAssignment[] {
  const assignments: ProductDesignerAssignment[] = [];
  const usedDesigners = new Set<string>();

  for (const product of products) {
    // 为每个产品筛选合适的设计师
    const suitableDesigners = availableDesigners
      .filter(designer => !usedDesigners.has(designer.id))
      .filter(designer => designer.status === 'idle' || designer.workload < 80)
      .map(designer => ({
        designer,
        score: this.calculateProductDesignerScore(designer, product)
      }))
      .sort((a, b) => b.score - a.score);

    // 选择最佳匹配设计师
    if (suitableDesigners.length > 0) {
      const bestDesigner = suitableDesigners[0].designer;

      // 如果产品复杂,考虑分配助理设计师
      const assistantDesigners = product.complexity === 'complex'
        ? this.findAssistantDesigners(bestDesigner, availableDesigners, usedDesigners)
        : [];

      const assignment: ProductDesignerAssignment = {
        productId: product.id,
        productName: product.name,
        productType: product.type as ProductType,
        assignedDesigners: [bestDesigner, ...assistantDesigners],
        primaryDesigner: bestDesigner,
        requiredSkills: this.getRequiredSkillsForProduct(product),
        estimatedWorkload: this.calculateProductWorkload(product),
        assignmentDate: new Date(),
        status: 'pending'
      };

      assignments.push(assignment);

      // 标记已使用的设计师
      usedDesigners.add(bestDesigner.id);
      assistantDesigners.forEach(ad => usedDesigners.add(ad.id));
    }
  }

  return assignments;
}

设计师列表展示

  • 产品分组展示,每个产品显示推荐设计师列表
  • 设计师卡片信息:头像、姓名、团队、状态标签、技能匹配度进度条、专业领域标签
  • 状态颜色:
    • idle 空闲 - 绿色
    • busy 繁忙 - 橙色
    • unavailable 不可用 - 灰色
    • on_vacation 休假 - 蓝色
  • 点击卡片可查看设计师详细信息和作品集

3.4.3 批量分配功能

class BatchProductAssignmentService {
  async executeBatchAssignment(
    assignments: ProductDesignerAssignment[],
    options: BatchAssignmentOptions
  ): Promise<BatchAssignmentResult> {
    const result: BatchAssignmentResult = {
      successCount: 0,
      failureCount: 0,
      results: [],
      conflicts: [],
      warnings: []
    };

    // 检查设计师时间冲突
    const conflicts = this.checkDesignerConflicts(assignments);
    result.conflicts = conflicts;

    if (conflicts.length > 0 && options.resolveConflicts) {
      // 自动解决冲突
      const resolvedAssignments = await this.resolveConflicts(assignments, conflicts);
      assignments = resolvedAssignments;
    }

    // 执行批量分配
    for (const assignment of assignments) {
      try {
        await this.executeAssignment(assignment, options);
        result.successCount++;
        result.results.push({
          assignmentId: assignment.productId,
          success: true,
          message: `成功为${assignment.productName}分配设计师`
        });
      } catch (error) {
        result.failureCount++;
        result.results.push({
          assignmentId: assignment.productId,
          success: false,
          message: `分配失败: ${error.message}`
        });
      }
    }

    return result;
  }

  private checkDesignerConflicts(assignments: ProductDesignerAssignment[]): DesignerConflict[] {
    const conflicts: DesignerConflict[] = [];
    const designerUsage = new Map<string, ProductDesignerAssignment[]>();

    // 统计每个设计师的分配情况
    for (const assignment of assignments) {
      for (const designer of assignment.assignedDesigners) {
        if (!designerUsage.has(designer.id)) {
          designerUsage.set(designer.id, []);
        }
        designerUsage.get(designer.id)!.push(assignment);
      }
    }

    // 检查冲突
    for (const [designerId, designerAssignments] of designerUsage.entries()) {
      if (designerAssignments.length > 3) {
        conflicts.push({
          designerId,
          designerName: designerAssignments[0].assignedDesigners.find(d => d.id === designerId)?.name || '',
          conflictType: 'over_assignment',
          assignments: designerAssignments,
          recommendation: '建议减少分配数量或分配助理设计师'
        });
      }

      // 检查时间冲突
      const timeConflicts = this.checkTimeConflicts(designerAssignments);
      conflicts.push(...timeConflicts);
    }

    return conflicts;
  }
}

3.5 下单时间自动生成

实现逻辑

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

  // 生成订单编号
  this.orderNumber = this.generateOrderNumber();
}

generateOrderNumber(): string {
  const date = new Date();
  const dateStr = date.toISOString().slice(0, 10).replace(/-/g, '');
  const random = Math.floor(Math.random() * 10000).toString().padStart(4, '0');
  return `ORD${dateStr}${random}`;
}

显示格式2025-10-20 14:30:25,订单编号:ORD202510201234

4. 数据流转

4.1 客服端同步流程

sequenceDiagram
    participant CS as 客服端
    participant Route as 路由
    participant PD as 项目详情页
    participant Form as 订单表单
    participant ProductService as 产品服务

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

关键代码实现

// project-detail.ts lines 741-816
this.route.queryParamMap.subscribe({
  next: (qp) => {
    const syncDataParam = qp.get('syncData');
    if (syncDataParam) {
      try {
        const syncData = JSON.parse(syncDataParam);

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

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

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

        // 产品识别和配置
        if (syncData.requirementInfo) {
          this.identifyAndConfigureProducts(syncData.requirementInfo);
        }

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

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

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

4.2 订单创建流程

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

关键方法实现

// project-detail.ts lines 4783-4808
createProductOrder(): void {
  if (!this.canCreateOrder()) {
    // 标记所有字段为已触摸,以显示验证错误
    this.orderCreationForm.markAllAsTouched();
    return;
  }

  const orderData = {
    ...this.orderCreationForm.value,
    ...this.optionalForm.value,
    customerInfo: this.orderCreationData?.customerInfo,
    productData: {
      products: this.projectProducts,
      productConfiguration: this.productConfiguration,
      productDependencies: this.productDependencies
    },
    quotationData: this.quotationData,
    designerAssignment: this.designerAssignmentData,
    orderInfo: {
      orderNumber: this.orderNumber,
      orderTime: this.orderTime,
      pricingStrategy: this.pricingStrategy
    }
  };

  console.log('创建产品订单:', orderData);

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

4.3 产品化项目管理流程

flowchart TD
    A[客户需求] --> B[AI产品识别]
    B --> C[生成产品配置]
    C --> D[确认产品列表]
    D --> E{是否多产品?}
    E -->|是| F[产品依赖分析]
    E -->|否| G[单产品配置]
    F --> H[跨产品协调配置]
    G --> I[报价计算]
    H --> I
    I --> J[设计师分配]
    J --> K[创建项目]
    K --> L[推进到需求阶段]

5. API集成

5.1 项目创建接口

接口地址POST /api/projects

请求参数

interface CreateProductProjectRequest {
  // 客户信息
  customerId: string;
  customerName: string;
  customerPhone: string;
  customerWechat?: string;
  customerType: string;
  customerSource: string;
  customerRemark?: string;

  // 订单信息
  orderNumber: string;
  orderTime: Date;
  orderAmount: number;
  smallImageDeliveryTime: Date;
  largeImageDeliveryTime?: Date;
  decorationType: string;
  requirementReason: string;
  isMultiDesigner: boolean;
  projectComplexity?: string;
  designStyle?: string;
  specialRequirements?: string;

  // 产品数据
  productData: {
    products: ProductConfig[];
    productConfiguration: ProductConfiguration;
    productDependencies: ProductDependency[];
    productCount: number;
    totalArea: number;
    totalBudget: number;
    designComplexity: string;
  };

  // 报价明细
  quotation: {
    products: ProductQuotationItem[];
    totalAmount: number;
    materialCost: number;
    laborCost: number;
    designFee: number;
    modelingFee: number;
    renderingFee: number;
    softDecorFee: number;
    managementFee: number;
    discounts: DiscountItem[];
    paymentSchedule: PaymentSchedule[];
    pricingStrategy: PricingStrategy;
  };

  // 设计师分配
  assignment: {
    assignments: ProductDesignerAssignment[];
    teamId: string;
    leaderId: string;
    assignmentDate: Date;
    expectedStartDate: Date;
    batchAssignmentMode: boolean;
    assignmentStrategy: AssignmentStrategy;
  };

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

响应数据

interface CreateProductProjectResponse {
  success: boolean;
  message: string;
  projectId: string;
  project: {
    id: string;
    name: string;
    currentStage: ProjectStage;
    createdAt: Date;
    assignedProducts: string[];
    productCount: number;
  };
}

5.2 产品识别服务接口

接口地址POST /api/products/identify

请求参数

interface IdentifyProductsRequest {
  description: string;
  totalArea?: number;
  budget?: number;
  projectType?: string;
  existingProducts?: ProductType[];
}

interface IdentifyProductsResponse {
  success: boolean;
  result: ProductIdentificationResult;
  recommendedProducts: ProductConfig[];
  suggestions: string[];
}

5.3 产品报价计算接口

接口地址POST /api/products/quotation

请求参数

interface CalculateProductQuotationRequest {
  products: ProductConfig[];
  options: QuotationOptions;
  strategy?: PricingStrategy;
}

interface CalculateProductQuotationResponse {
  success: boolean;
  quotation: MultiProductQuotation;
  recommendations: string[];
}

文档版本:v2.0 (基于Project表产品设计管理) 创建日期:2025-10-20 最后更新:2025-10-20 维护人:产品团队