# 项目管理 - 订单分配阶段 PRD (基于Project表产品设计) ## 1. 功能概述 ### 1.1 阶段定位 订单分配阶段是项目管理流程的第一个环节,主要负责将客户咨询转化为正式项目订单,并完成产品设计化管理。该阶段通过Project表统一管理各个空间设计产品,是连接客服端和设计师端的关键桥梁。 ### 1.2 核心目标 - **多产品设计识别与管理**:智能识别单产品/多产品项目,基于Project表统一管理 - **产品化项目创建**:将客户需求转化为标准化的设计产品 - **完成客户信息的结构化录入和同步** - **确定项目报价和付款条件(支持多产品设计报价策略)** - **匹配并分配合适的设计师资源(考虑多产品协调需求)** - **建立项目基础档案,为后续环节提供数据支撑** ### 1.3 涉及角色 - **客服人员**:负责创建订单、录入客户信息、初步需求沟通、产品识别 - **设计师**:接收产品分配、查看项目基础信息、产品详情 - **组长**:查看团队产品分配情况、协调设计师资源、跨产品协调 ## 2. 基于Project表的产品设计管理 ### 2.1 产品识别与分类 #### 2.1.1 智能产品设计识别系统 ```typescript class ProductIdentificationService { // 基于客户需求描述自动识别空间设计产品 async identifyProductsFromDescription(description: string): Promise { 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; designComplexity: 'simple' | 'medium' | 'complex'; } interface ProductConfiguration { products: ProductConfig[]; totalEstimatedBudget: number; budgetAllocation: Record; 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 产品设计管理界面 ```html

产品设计配置

项目类型: {{ isSingleProductProject ? '单产品项目' : '多产品项目' }} {{ projectProducts.length }} 个产品

识别到 {{ productIdentificationResult.identifiedProducts.length }} 个产品设计

识别置信度:
{{ Math.round(productIdentificationResult.confidence * 100) }}%

{{ productIdentificationResult.reasoning }}

@if (productIdentificationResult.recommendedConfiguration) { }
建议确认的问题:
    @for (question of productIdentificationResult.suggestedQuestions; track question) {
  • {{ question }}
  • }

产品设计列表

@for (product of projectProducts; track product.id) {
@for (feature of product.specialFeatures || []; track feature) { {{ feature }} }
设计费: ¥{{ Math.round(product.estimatedBudget * 0.3).toLocaleString() }}
建模费: ¥{{ Math.round(product.estimatedBudget * 0.3).toLocaleString() }}
渲染费: ¥{{ Math.round(product.estimatedBudget * 0.25).toLocaleString() }}
软装费: ¥{{ Math.round(product.estimatedBudget * 0.15).toLocaleString() }}
}

统计信息

产品总数: {{ projectProducts.length }}
总面积: {{ totalProductArea }}m²
总预算: ¥{{ totalProductBudget.toLocaleString() }}
平均单价: ¥{{ averagePricePerSqm.toLocaleString() }}/m²
预估总工期: {{ totalEstimatedDuration }}天

产品类型分布

@for (type of getProductTypeDistribution(); track type.type) {
{{ getProductTypeName(type.type) }}
{{ type.count }} {{ type.percentage }}%
}
``` ### 2.2 多产品报价管理 #### 2.2.1 产品报价计算 ```typescript 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 { 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 信息展示卡片 **位置**:订单分配阶段左侧面板 **展示内容**: ```typescript interface CustomerInfoDisplay { // 基础信息 name: string; // 客户姓名 phone: string; // 联系电话 wechat?: string; // 微信号 customerType: string; // 客户类型:新客户/老客户/VIP客户 source: string; // 来源:小程序/官网咨询/推荐介绍 remark?: string; // 备注信息 // 状态信息 syncStatus: 'syncing' | 'synced' | 'error'; // 同步状态 lastSyncTime?: Date; // 最后同步时间 // 需求标签 demandType?: string; // 需求类型:价格敏感/质量敏感/综合要求 followUpStatus?: string; // 跟进状态:待报价/待确认需求/已失联 preferenceTags?: string[]; // 偏好标签数组 // 项目需求特征 projectType?: string; // 项目类型倾向:全屋设计/局部改造/软装设计 budgetRange?: { // 预算范围 min: number; max: number; currency: string; }; timeline?: { // 时间要求 preferredStart?: Date; deadline?: Date; urgency: 'low' | 'medium' | 'high'; }; } ``` **数据来源**: 1. **客服端同步**:通过路由查询参数 `syncData` 传递客户信息 ```typescript // 客服端跳转示例 router.navigate(['/designer/project-detail', projectId], { queryParams: { syncData: JSON.stringify({ customerInfo: {...}, requirementInfo: {...}, preferenceTags: [...], projectAnalysis: {...} }) } }); ``` 2. **实时同步机制**: - 每30秒自动同步一次客户信息 - 显示同步状态指示器(同步中/已同步/同步失败) - 支持手动触发同步 **交互特性**: - 卡片可展开/收起(`isCustomerInfoExpanded`) - 展开时显示完整客户信息和标签 - 收起时仅显示客户姓名和联系方式 - 同步状态实时更新,显示"刚刚/X分钟前/X小时前" #### 3.1.2 客户搜索功能 **适用场景**:手动创建订单时快速选择已有客户 **搜索逻辑**: ```typescript 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 必填项配置 **表单定义**: ```typescript 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 可选信息表单 **折叠面板设计**: ```html
可选信息 {{ isOptionalFormExpanded ? '▼' : '▶' }}
@if (isOptionalFormExpanded) {
}
``` **可选字段**: ```typescript optionalForm = this.fb.group({ largeImageDeliveryTime: [''], // 大图交付时间 spaceRequirements: [''], // 空间需求描述(已移至产品设计) designAngles: [''], // 设计角度要求 specialAreaHandling: [''], // 特殊区域处理说明 materialRequirements: [''], // 材质要求 lightingRequirements: [''], // 光照需求 budgetRange: [''], // 预算范围 timelinePreference: [''], // 时间偏好 qualityStandards: [''], // 质量标准要求 }); ``` ### 3.3 产品报价明细组件 #### 3.3.1 组件集成 **组件标签**: ```html ``` **数据结构**: ```typescript 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辅助生成**(增强功能): ```typescript 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. **付款计划配置**: ```typescript 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 组件集成 **组件标签**: ```html ``` **数据结构**: ```typescript 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 产品设计师选择逻辑 **智能推荐算法**: ```typescript 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(); 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 批量分配功能 ```typescript class BatchProductAssignmentService { async executeBatchAssignment( assignments: ProductDesignerAssignment[], options: BatchAssignmentOptions ): Promise { 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(); // 统计每个设计师的分配情况 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 下单时间自动生成 **实现逻辑**: ```typescript ngOnInit(): void { // 自动生成下单时间 this.orderTime = new Date().toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' }); // 生成订单编号 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 客服端同步流程 ```mermaid 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: 同步完成 ``` **关键代码实现**: ```typescript // project-detail.ts lines 741-816 this.route.queryParamMap.subscribe({ next: (qp) => { const syncDataParam = qp.get('syncData'); if (syncDataParam) { try { const syncData = JSON.parse(syncDataParam); // 设置同步状态 this.isSyncingCustomerInfo = true; // 存储订单分配数据用于显示 this.orderCreationData = syncData; // 同步客户信息到表单 if (syncData.customerInfo) { this.customerForm.patchValue({ name: syncData.customerInfo.name || '', phone: syncData.customerInfo.phone || '', wechat: syncData.customerInfo.wechat || '', customerType: syncData.customerInfo.customerType || '新客户', source: syncData.customerInfo.source || '小程序', remark: syncData.customerInfo.remark || '' }); } // 产品识别和配置 if (syncData.requirementInfo) { this.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 订单创建流程 ```mermaid 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[显示错误信息] ``` **关键方法实现**: ```typescript // 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 产品化项目管理流程 ```mermaid 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` **请求参数**: ```typescript 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[]; } ``` **响应数据**: ```typescript 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` **请求参数**: ```typescript 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` **请求参数**: ```typescript 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 **维护人**:产品团队