# 项目管理 - 交付执行阶段 PRD (基于Product表和NovaStorage) ## 1. 功能概述 ### 1.1 阶段定位 交付执行阶段是项目管理流程的核心执行环节,包含**白模建模、软装设计、渲染输出、后期处理**四个连续子阶段。该阶段负责将设计方案转化为可交付的视觉成果,是项目价值实现的关键环节。 ### 1.2 核心目标 - **多空间产品协同管理**:基于Product表统一管理各空间设计产品的交付执行 - **四阶段全流程管控**:白模→软装→渲染→后期的完整执行链路 - **文件管理与存储集成**:基于NovaStorage的文件上传,prefixKeys=project/:pid/,Attachment与ProjectFile同步保存 - **ProjectTeam协作管理**:根据团队成员角色和技能,合理分配各阶段任务 - **交付物分类管理**:按四大核心内容(白模\软装\渲染\后期)分类管理交付物 - **质量把控与审核**:支持组长审核和质量标准验证 - **实时进度跟踪**:多维度进度监控和状态可视化 ### 1.3 涉及角色 - **设计师**:负责白模建模、软装设计、后期处理等设计工作 - **渲染师**:专门负责渲染阶段的图片输出和质量把控 - **组长**:审核各阶段交付物、把控质量标准、团队协调 - **技术**:验收最终交付物、确认技术规范和质量标准 - **客服**:沟通客户需求、传递反馈信息、协调交付时间 ### 1.4 四大执行子阶段 ```mermaid graph LR A[方案确认] --> B[白模建模] B --> C[软装设计] C --> D[渲染输出] D --> E[后期处理] E --> F[尾款结算] style B fill:#e3f2fd style C fill:#fff3e0 style D fill:#fce4ec style E fill:#f3e5f5 ``` ### 1.5 四大核心交付内容 1. **白模建模**:空间结构建模、基础框架搭建 2. **软装设计**:家具配置、材质选择、色彩搭配 3. **渲染输出**:高清效果图、全景图、细节特写 4. **后期处理**:色彩调整、效果优化、最终成品 ## 2. 基于Product表和ProjectTeam的交付管理系统 ### 2.1 交付执行管理架构 #### 2.1.1 核心数据关系 ```typescript // Product产品表 - 空间设计产品核心 interface Product { objectId: string; project: Pointer; profile: Pointer; // 负责设计师 productName: string; // 产品名称:李总主卧设计 productType: string; // 空间类型:bedroom stage: string; // 当前阶段:modeling/softDecor/rendering/postProcess status: string; // 状态:not_started/in_progress/completed quotation: Object; // 产品报价信息 requirements: Object; // 设计需求 space: Object; // 空间信息 data: Object; // 扩展数据 } // ProjectTeam项目团队表 - 团队成员管理 interface ProjectTeam { objectId: string; project: Pointer; profile: Pointer; role: string; // designer/renderer/team_leader/technical workload: Number; // 工作量 specialties: string[]; // 专业技能 assignedProducts: string[]; // 分配的产品ID列表 } // ProjectFile项目文件表 - 交付物管理 interface ProjectFile { objectId: string; project: Pointer; product: Pointer; // 关联的空间产品 attach: Pointer; // NovaStorage附件 category: string; // white_model/soft_decor/rendering/post_process stage: string; // modeling/softDecor/rendering/postProcess uploadedBy: Pointer; data: Object; // 文件元数据、审核状态等 } ``` #### 2.1.2 基于NovaStorage的文件管理 ```typescript // 文件上传服务 - 使用NovaStorage class DeliveryFileService { private storage: NovaStorage; constructor() { const cid = localStorage.getItem('company')!; this.storage = NovaStorage.withCid(cid); } // 上传交付文件 async uploadDeliveryFile( projectId: string, productId: string, category: string, file: File ): Promise<{ attachment: Parse.Object; projectFile: Parse.Object }> { // 1. 上传到NovaStorage,使用项目前缀 const novaFile: NovaFile = await this.storage.upload(file, { prefixKey: `project/${projectId}/`, onProgress: (progress) => { console.log('Upload progress:', progress.total.percent); } }); // 2. 创建Attachment记录 const attachment = new Parse.Object("Attachment"); attachment.set("name", novaFile.name); attachment.set("url", novaFile.url); attachment.set("size", novaFile.size); attachment.set("mime", novaFile.type); attachment.set("md5", novaFile.md5); attachment.set("metadata", novaFile.metadata); attachment.set("company", { __type: "Pointer", className: "Company", objectId: cid }); await attachment.save(); // 3. 创建ProjectFile记录 const projectFile = new Parse.Object("ProjectFile"); projectFile.set("project", { __type: "Pointer", className: "Project", objectId: projectId }); projectFile.set("product", { __type: "Pointer", className: "Product", objectId: productId }); projectFile.set("attach", { __type: "Pointer", className: "Attachment", objectId: attachment.id }); projectFile.set("category", category); // white_model/soft_decor/rendering/post_process projectFile.set("stage", this.mapCategoryToStage(category)); projectFile.set("uploadedBy", { __type: "Pointer", className: "Profile", objectId: currentUser.id }); projectFile.set("data", { reviewStatus: "pending", uploadTime: new Date(), fileSize: novaFile.size, fileType: novaFile.type }); await projectFile.save(); return { attachment, projectFile }; } // 映射category到stage private mapCategoryToStage(category: string): string { const mapping = { 'white_model': 'modeling', 'soft_decor': 'softDecor', 'rendering': 'rendering', 'post_process': 'postProcess' }; return mapping[category] || 'modeling'; } } ``` ### 2.2 基于ProjectTeam的团队协作管理 #### 2.2.1 团队分配策略服务 ```typescript class DeliveryTeamManagementService { // 智能分配团队成员到产品阶段 async assignTeamMembers( projectId: string, productId: string, stage: string ): Promise { // 1. 获取项目团队成员 const teamQuery = new Parse.Query("ProjectTeam"); teamQuery.equalTo("project", { __type: "Pointer", className: "Project", objectId: projectId }); teamQuery.include("profile"); const teamMembers = await teamQuery.find(); // 2. 根据阶段和专业技能匹配 const stageRequirements = this.getStageRequirements(stage); const suitableMembers = teamMembers.filter(member => { const specialties = member.get("data")?.specialties || []; const role = member.get("role"); return this.isMemberSuitableForStage(role, specialties, stage); }); // 3. 考虑当前工作量 const availableMembers = await this.filterByWorkload(suitableMembers); // 4. 分配主负责人和协作者 const assignment = this.createTeamAssignment(availableMembers, stageRequirements); // 5. 更新Product的负责人 await this.updateProductAssignee(productId, assignment.primaryAssignee); return assignment; } // 阶段需求定义 private getStageRequirements(stage: string): StageRequirements { const requirements = { modeling: { primaryRole: 'designer', requiredSkills: ['3d_modeling', 'autocad', 'sketchup'], minHours: 8, maxHours: 40, complexity: 'medium' }, softDecor: { primaryRole: 'designer', requiredSkills: ['interior_design', 'material_selection', 'color_theory'], minHours: 6, maxHours: 32, complexity: 'high' }, rendering: { primaryRole: 'renderer', requiredSkills: ['3ds_max', 'vray', 'corona', 'lumion', 'photoshop'], minHours: 4, maxHours: 24, complexity: 'high' }, postProcess: { primaryRole: 'designer', requiredSkills: ['photoshop', 'lightroom', 'color_correction'], minHours: 3, maxHours: 16, complexity: 'low' } }; return requirements[stage] || requirements.modeling; } // 检查成员是否适合阶段 private isMemberSuitableForStage( role: string, specialties: string[], stage: string ): boolean { const requirements = this.getStageRequirements(stage); // 角色匹配 if (role !== requirements.primaryRole && role !== 'team_leader') { return false; } // 技能匹配 const hasRequiredSkills = requirements.requiredSkills.some(skill => specialties.includes(skill) ); return hasRequiredSkills; } } ``` #### 2.2.2 增强的DeliveryProcess接口 ```typescript interface ProductDeliveryProcess { id: string; // 流程ID: 'modeling' | 'softDecor' | 'rendering' | 'postProcess' name: string; // 流程名称:建模/软装/渲染/后期 type: 'modeling' | 'softDecor' | 'rendering' | 'postProcess'; isExpanded: boolean; // 是否展开 // 产品管理(基于Product表) products: ProductDelivery[]; content: Record; // 跨产品协调(基于Product表) crossProductCoordination: { dependencies: ProductDependency[]; // 产品依赖关系 batchOperations: BatchOperation[]; // 批量操作 qualityStandards: QualityStandard[]; // 质量标准 }; // 整体进度管理 overallProgress: { total: number; // 总体进度 byProduct: Record; // 各产品进度 byStage: Record; // 各阶段进度 estimatedCompletion: Date; }; } interface ProductDelivery { productId: string; // 产品ID(与Product.objectId关联) productName: string; // 产品名称:李总主卧设计 productType: string; // 产品类型:bedroom isExpanded: boolean; // 是否展开 order: number; // 排序顺序 priority: number; // 优先级 status: ProductStatus; // 产品状态 assigneeId: string; // 负责人ID(Product.profile) estimatedHours: number; // 预估工时 actualHours: number; // 实际工时 // 产品报价信息 quotation: { price: number; breakdown: { design: number; modeling: number; rendering: number; softDecor: number; }; status: string; }; } interface ProductContent { // 产品文件(基于ProjectFile分类) files: Array<{ id: string; name: string; url: string; size?: string; fileCategory: string; // 'delivery' | 'reference' | 'other' reviewStatus?: 'pending' | 'approved' | 'rejected'; synced?: boolean; // 是否已同步到客户端 uploadTime: Date; // 上传时间 uploadedBy: string; // 上传人 }>; progress: number; // 进度 0-100 status: 'pending' | 'in_progress' | 'completed' | 'approved'; notes: string; // 备注信息 lastUpdated: Date; // 最后更新时间 // 产品特定字段 productSpecific: { // 建模阶段特有 modelingComplexity?: 'simple' | 'medium' | 'complex'; structuralConstraints?: string[]; // 软装阶段特有 furnitureList?: string[]; materialSelection?: string[]; // 渲染阶段特有 renderingQuality?: 'standard' | 'high' | 'ultra'; outputResolution?: string; // 后期阶段特有 postProcessingTypes?: string[]; finalTouches?: string[]; }; } ``` #### 2.1.2 基于Product表的交付管理服务 ```typescript class ProductDeliveryService { // 获取项目的交付管理数据 async getProjectDeliveryData(projectId: string): Promise { // 1. 获取项目的所有产品 const productQuery = new Parse.Query("Product"); productQuery.equalTo("project", { __type: "Pointer", className: "Project", objectId: projectId }); productQuery.include("profile"); productQuery.ascending("order"); const products = await productQuery.find(); // 2. 构建交付管理数据结构 const deliveryProcesses: ProductDeliveryProcess[] = []; for (const stage of ['modeling', 'softDecor', 'rendering', 'postProcess']) { const process: ProductDeliveryProcess = { id: stage, name: this.getStageName(stage), type: stage as any, isExpanded: false, products: [], content: {}, crossProductCoordination: { dependencies: [], batchOperations: [], qualityStandards: [] }, overallProgress: { total: 0, byProduct: {}, byStage: {}, estimatedCompletion: new Date() } }; // 3. 为每个产品构建交付数据 for (const product of products) { const productDelivery: ProductDelivery = { productId: product.id, productName: product.get("productName"), productType: product.get("productType"), isExpanded: false, order: product.get("order") || 0, priority: product.get("space")?.priority || 5, status: product.get("status") || "not_started", assigneeId: product.get("profile")?.id, estimatedHours: product.get("estimatedDuration") * 8, // 天数转小时 actualHours: 0, quotation: product.get("quotation") || {} }; // 4. 获取产品的交付文件 const productFiles = await this.getProductDeliveryFiles(product.id, stage); // 5. 构建产品内容 const productContent: ProductContent = { files: productFiles.map(file => ({ id: file.id, name: file.get("fileName"), url: file.get("fileUrl"), size: this.formatFileSize(file.get("fileSize")), fileCategory: file.get("fileCategory"), reviewStatus: file.get("data")?.reviewStatus || "pending", uploadTime: file.get("createdAt"), uploadedBy: file.get("uploadedBy")?.get("name") })), progress: this.calculateProductProgress(product, stage), status: this.getProductStatus(product, stage), notes: product.get("data")?.deliveryNotes || "", lastUpdated: product.get("updatedAt"), productSpecific: this.getProductSpecificFields(product, stage) }; process.products.push(productDelivery); process.content[product.id] = productContent; process.overallProgress.byProduct[product.id] = productContent.progress; } deliveryProcesses.push(process); } return deliveryProcesses; } // 获取产品的交付文件 async getProductDeliveryFiles(productId: string, stage: string): Promise { const fileQuery = new Parse.Query("ProjectFile"); fileQuery.equalTo("product", { __type: "Pointer", className: "Product", objectId: productId }); fileQuery.equalTo("fileCategory", "delivery"); fileQuery.equalTo("stage", stage); fileQuery.notEqualTo("isDeleted", true); fileQuery.descending("createdAt"); return await fileQuery.find(); } // 计算产品进度 calculateProductProgress(product: Parse.Object, stage: string): number { const productFiles = product.get("deliveryFiles") || []; const stageFiles = productFiles.filter((file: any) => file.stage === stage); if (stageFiles.length === 0) return 0; const completedFiles = stageFiles.filter((file: any) => file.reviewStatus === "approved" ); return Math.round((completedFiles.length / stageFiles.length) * 100); } // 获取产品状态 getProductStatus(product: Parse.Object, stage: string): string { const currentStage = product.get("stage"); if (currentStage === stage) { return product.get("status") || "not_started"; } else if (this.isStageCompleted(stage, currentStage)) { return "completed"; } else { return "pending"; } } // 获取产品特定字段 getProductSpecificFields(product: Parse.Object, stage: string): any { const space = product.get("space") || {}; const baseFields = { structuralConstraints: space.constraints || [] }; switch (stage) { case "modeling": return { ...baseFields, modelingComplexity: space.complexity || "medium" }; case "softDecor": return { ...baseFields, furnitureList: product.get("data")?.furnitureList || [], materialSelection: product.get("data")?.materialSelection || [] }; case "rendering": return { ...baseFields, renderingQuality: product.get("data")?.renderingQuality || "standard", outputResolution: product.get("data")?.outputResolution || "1920x1080" }; case "postProcess": return { ...baseFields, postProcessingTypes: product.get("data")?.postProcessingTypes || [], finalTouches: product.get("data")?.finalTouches || [] }; default: return baseFields; } } } ``` ### 2.2 产品交付进度管理 #### 2.2.1 进度跟踪服务 ```typescript class ProductProgressService { // 更新产品进度 async updateProductProgress( productId: string, stage: string, progressData: ProgressUpdateData ): Promise { const productQuery = new Parse.Query("Product"); const product = await productQuery.get(productId); // 更新产品状态 if (progressData.status) { product.set("status", progressData.status); } if (progressData.stage) { product.set("stage", progressData.stage); } // 更新产品数据 const currentData = product.get("data") || {}; const updatedData = { ...currentData, [`${stage}Progress`]: progressData.progress, [`${stage}Notes`]: progressData.notes, [`${stage}LastUpdated`]: new Date() }; product.set("data", updatedData); await product.save(); // 触发进度更新事件 this.emitProgressUpdate(productId, stage, progressData); } // 批量更新产品进度 async batchUpdateProgress( productIds: string[], stage: string, progressData: ProgressUpdateData ): Promise { const productQuery = new Parse.Query("Product"); productQuery.containedIn("objectId", productIds); const products = await productQuery.find(); for (const product of products) { await this.updateProductProgress(product.id, stage, progressData); } } // 计算整体项目进度 calculateOverallProjectProgress( deliveryProcesses: ProductDeliveryProcess[] ): ProjectProgress { const totalProducts = deliveryProcesses.reduce((sum, process) => sum + process.products.length, 0); if (totalProducts === 0) { return { total: 0, byStage: {}, byProduct: {} }; } const progress: ProjectProgress = { total: 0, byStage: {}, byProduct: {} }; // 计算各阶段进度 deliveryProcesses.forEach(process => { const stageProgress = process.products.reduce((sum, product) => { return sum + (process.content[product.productId]?.progress || 0); }, 0); progress.byStage[process.id] = Math.round(stageProgress / process.products.length); }); // 计算各产品进度 deliveryProcesses.forEach(process => { process.products.forEach(product => { const productProgress = process.content[product.productId]?.progress || 0; progress.byProduct[product.productId] = productProgress; }); }); // 计算总体进度 const stageSum = Object.values(progress.byStage).reduce((sum, val) => sum + val, 0); progress.total = Math.round(stageSum / Object.keys(progress.byStage).length); return progress; } } ``` ### 2.3 跨产品协调管理 #### 2.3.1 产品依赖管理 ```typescript class ProductDependencyManager { // 分析产品间依赖关系 analyzeProductDependencies(products: Product[]): ProductDependency[] { const dependencies: ProductDependency[] = []; // 基于产品类型分析依赖 for (let i = 0; i < products.length; i++) { for (let j = i + 1; j < products.length; j++) { const fromProduct = products[i]; const toProduct = products[j]; const dependency = this.analyzeDependency(fromProduct, toProduct); if (dependency) { dependencies.push(dependency); } } } return dependencies; } // 分析两个产品间的依赖关系 private analyzeDependency( fromProduct: Product, toProduct: Product ): ProductDependency | null { const fromType = fromProduct.productType; const toType = toProduct.productType; // 定义产品类型间的依赖关系 const dependencyRules: Record = { 'living_room': { dependsOn: [], reason: '客厅通常是风格参考的起点' }, 'dining_room': { dependsOn: ['living_room'], reason: '餐厅通常需要与客厅风格保持一致' }, 'kitchen': { dependsOn: ['dining_room'], reason: '厨房与餐厅在空间和功能上紧密相关' }, 'bedroom': { dependsOn: ['living_room', 'corridor'], reason: '卧室通常需要参考客厅的整体风格' }, 'bathroom': { dependsOn: ['bedroom'], reason: '卫生间与卧室在功能上紧密相关' }, 'balcony': { dependsOn: ['living_room', 'bedroom'], reason: '阳台通常连接客厅或卧室' } }; const rule = dependencyRules[toType]; if (rule && rule.dependsOn.includes(fromType)) { return { id: `${fromProduct.productId}-${toProduct.productId}`, fromProductId: fromProduct.productId, toProductId: toProduct.productId, fromProductName: fromProduct.productName, toProductName: toProduct.productName, type: 'style_reference', description: rule.reason, priority: this.calculateDependencyPriority(fromType, toType), confidence: 0.8 }; } return null; } // 生成协调建议 generateCoordinationSuggestions( dependencies: ProductDependency[] ): CoordinationSuggestion[] { const suggestions: CoordinationSuggestion[] = []; dependencies.forEach(dep => { suggestions.push({ id: `coord-${dep.id}`, dependencyId: dep.id, type: 'style_consistency', title: `风格一致性建议:${dep.fromProductName} → ${dep.toProductName}`, description: `建议${dep.toProductName}在设计时参考${dep.fromProductName}的整体风格,确保空间的协调统一`, actions: [ `参考${dep.fromProductName}的色彩方案`, `保持材质选择的一致性`, `考虑空间功能的连续性` ], priority: dep.priority }); }); return suggestions; } } ``` ### 2.4 批量操作管理 #### 2.4.1 批量操作服务 ```typescript class BatchOperationService { // 批量上传文件 async batchUploadFiles( productIds: string[], files: File[], fileCategory: string, stage: string, uploaderId: string ): Promise { const results: BatchUploadResult = { successful: [], failed: [], summary: { total: files.length, uploaded: 0, failed: 0 } }; for (const file of files) { try { // 为每个产品上传文件 for (const productId of productIds) { const projectFile = new Parse.Object("ProjectFile"); projectFile.set("product", { __type: "Pointer", className: "Product", objectId: productId }); projectFile.set("fileCategory", fileCategory); projectFile.set("stage", stage); projectFile.set("uploadedBy", { __type: "Pointer", className: "Profile", objectId: uploaderId }); // 设置文件基本信息 projectFile.set("fileName", file.name); projectFile.set("fileSize", file.size); // 上传文件到存储 const fileData = await this.uploadFile(file); projectFile.set("fileUrl", fileData.url); projectFile.set("attach", fileData.attachment); await projectFile.save(); results.successful.push({ productId, fileName: file.name, fileId: projectFile.id }); } results.summary.uploaded++; } catch (error) { results.failed.push({ fileName: file.name, error: error.message }); results.summary.failed++; } } return results; } // 批量更新产品状态 async batchUpdateProductStatus( productIds: string[], status: string, stage: string ): Promise { const productQuery = new Parse.Query("Product"); productQuery.containedIn("objectId", productIds); const products = await productQuery.find(); for (const product of products) { product.set("status", status); product.set("stage", stage); await product.save(); } } // 批量发送审核通知 async batchSendReviewNotifications( productIds: string[], stage: string, reviewerIds: string[] ): Promise { // 获取需要审核的产品 const productQuery = new Parse.Query("Product"); productQuery.containedIn("objectId", productIds); productQuery.include("profile"); const products = await productQuery.find(); // 发送通知给审核人员 for (const reviewerId of reviewerIds) { for (const product of products) { await this.sendReviewNotification({ reviewerId, productId: product.id, productName: product.get("productName"), stage, designerId: product.get("profile")?.id }); } } } } ``` ## 3. 完整的交付执行界面设计 ### 3.1 交付执行主界面布局 #### 3.1.1 主界面结构 ```html

{{ project.title }}

{{ project.customer.name }} 截止:{{ formatDate(project.deadline) }}

{{ getStageDisplayName(stage.id) }}

{{ stage.progress }}%
{{ stage.productCount }}个产品 {{ stage.remainingDays }}天
{{ getTeamStatusText(stage.teamStatus) }}
已选择 {{ selectedProducts.length }} 个产品

{{ getStageDisplayName(activeStage) }} - 产品管理

{{ product.productName }}

{{ getProductTypeName(product.productType) }}
{{ product.space?.area }}m² 高优先级
{{ getStatusText(product.status) }}
{{ product.stageProgress[activeStage] }}%
{{ product.assignee?.name }}
预计 {{ product.estimatedHours }}h 实际 {{ product.actualHours }}h
{{ getFileCount(product, category.id) }}
交付文件管理
{{ category.name }} ({{ getFileCount(product, category.id) }})
{{ file.name }} {{ formatFileSize(file.size) }} • {{ formatDate(file.uploadTime) }}
阶段备注
最后更新: {{ formatDateTime(product.lastUpdated) }} {{ product.lastUpdatedBy }}
活动日志
{{ formatTime(log.timestamp) }}
{{ log.message }}
{{ log.user }}
产品名称 空间类型 负责人 进度 文件数量 状态 预计时间 操作
{{ product.productName }}
{{ getProductTypeName(product.productType) }}
{{ product.assignee?.name }}
{{ product.stageProgress[activeStage] }}%
{{ getTotalFileCount(product) }} {{ getStatusText(product.status) }} {{ product.estimatedHours }}h
``` ### 3.2 核心组件设计 #### 3.2.1 文件上传组件 ```typescript // 智能文件上传组件 @Component({ selector: 'app-delivery-file-upload', standalone: true, imports: [CommonModule, FormsModule, IonIcon] }) export class DeliveryFileUploadComponent { @Input() projectId: string = ''; @Input() productId: string = ''; @Input() category: 'white_model' | 'soft_decor' | 'rendering' | 'post_process' = 'white_model'; @Output() fileUploaded = new EventEmitter(); uploading: boolean = false; uploadProgress: number = 0; dragOver: boolean = false; constructor(private fileService: DeliveryFileService) {} async onFileSelect(event: Event): Promise { const files = (event.target as HTMLInputElement).files; if (!files?.length) return; await this.uploadFiles(Array.from(files)); } async uploadFiles(files: File[]): Promise { this.uploading = true; this.uploadProgress = 0; try { for (const file of files) { const result = await this.fileService.uploadDeliveryFile( this.projectId, this.productId, this.category, file ); this.fileUploaded.emit({ file: result.projectFile, attachment: result.attachment, category: this.category }); } } catch (error) { console.error('File upload failed:', error); // 显示错误提示 } finally { this.uploading = false; this.uploadProgress = 0; } } getFileIcon(fileName: string): string { const ext = fileName.split('.').pop()?.toLowerCase(); const iconMap = { 'jpg': 'fas fa-image', 'png': 'fas fa-image', 'gif': 'fas fa-image', 'pdf': 'fas fa-file-pdf', 'dwg': 'fas fa-file-cad', 'dxf': 'fas fa-file-cad', 'skp': 'fas fa-file-cad', 'max': 'fas fa-file-cad' }; return iconMap[ext] || 'fas fa-file'; } } ``` #### 3.2.2 团队分配组件 ```typescript // 团队成员分配组件 @Component({ selector: 'app-team-assignment', standalone: true, imports: [CommonModule, FormsModule, IonIcon] }) export class TeamAssignmentComponent { @Input() projectId: string = ''; @Input() stage: string = ''; @Input() selectedProducts: string[] = []; availableTeamMembers: TeamMember[] = []; assignmentSuggestions: AssignmentSuggestion[] = []; constructor(private teamService: DeliveryTeamManagementService) {} async loadTeamMembers(): Promise { const teamQuery = new Parse.Query("ProjectTeam"); teamQuery.equalTo("project", { __type: "Pointer", className: "Project", objectId: this.projectId }); teamQuery.include("profile"); const members = await teamQuery.find(); this.availableTeamMembers = members.map(member => ({ id: member.id, name: member.get("profile").get("name"), avatar: member.get("profile").get("data")?.avatar, role: member.get("role"), specialties: member.get("data")?.specialties || [], currentWorkload: member.get("workload") || 0, assignedProducts: member.get("data")?.assignedProducts || [] })); } async generateAssignmentSuggestions(): Promise { const requirements = this.getStageRequirements(this.stage); this.assignmentSuggestions = this.selectedProducts.map(productId => { const suitableMembers = this.availableTeamMembers.filter(member => this.isMemberSuitable(member, requirements) ); return { productId, productName: this.getProductName(productId), primaryAssignee: this.selectBestMember(suitableMembers), collaborators: this.selectCollaborators(suitableMembers, 2), reasoning: this.generateAssignmentReasoning(suitableMembers[0], requirements) }; }); } async applyAssignments(): Promise { for (const suggestion of this.assignmentSuggestions) { await this.teamService.assignTeamMembers( this.projectId, suggestion.productId, this.stage, suggestion.primaryAssignee, suggestion.collaborators ); } } } ``` #### 3.2.3 进度跟踪组件 ```typescript // 进度跟踪组件 @Component({ selector: 'app-progress-tracker', standalone: true, imports: [CommonModule, FormsModule] }) export class ProgressTrackerComponent { @Input() productId: string = ''; @Input() stage: string = ''; progressData: ProgressData = { current: 0, target: 100, milestones: [], activities: [] }; constructor(private progressService: ProductProgressService) {} async loadProgressData(): Promise { this.progressData = await this.progressService.getProductProgress( this.productId, this.stage ); } updateProgress(newProgress: number): void { this.progressService.updateProductProgress( this.productId, this.stage, { progress: newProgress } ); } addMilestone(milestone: Milestone): void { this.progressData.milestones.push(milestone); this.progressService.addMilestone(this.productId, this.stage, milestone); } } ``` ### 3.3 交互流程设计 #### 3.3.1 文件上传交互 1. **拖拽上传**:支持拖拽文件到指定区域 2. **批量选择**:支持多文件同时上传 3. **进度显示**:实时显示上传进度和状态 4. **自动分类**:根据文件类型自动选择category 5. **重试机制**:上传失败时支持重试 #### 3.3.2 团队协作交互 1. **智能推荐**:根据阶段需求自动推荐合适的团队成员 2. **工作量平衡**:考虑当前工作量,避免过度分配 3. **技能匹配**:基于专业技能进行精准匹配 4. **协作支持**:支持主负责人+协作者模式 #### 3.3.3 审核流程交互 1. **预览功能**:支持文件预览和快速查看 2. **审核操作**:通过/驳回/重新审核 3. **批注功能**:支持在文件上添加批注 4. **版本管理**:支持文件版本比较和回滚 ### 3.4 数据结构定义 #### 3.4.1 文件分类结构 ```typescript interface FileCategory { id: 'white_model' | 'soft_decor' | 'rendering' | 'post_process'; name: string; icon: string; description: string; allowedTypes: string[]; maxSize: number; // MB } export const FILE_CATEGORIES: FileCategory[] = [ { id: 'white_model', name: '白模建模', icon: 'fas fa-cube', description: '空间结构建模、基础框架', allowedTypes: ['.skp', '.max', '.dwg', '.dxf'], maxSize: 50 }, { id: 'soft_decor', name: '软装设计', icon: 'fas fa-couch', description: '家具配置、材质选择、色彩搭配', allowedTypes: ['.jpg', '.png', '.pdf', '.psd'], maxSize: 20 }, { id: 'rendering', name: '渲染输出', icon: 'fas fa-image', description: '高清效果图、全景图、细节特写', allowedTypes: ['.jpg', '.png', '.tiff', '.hdr'], maxSize: 30 }, { id: 'post_process', name: '后期处理', icon: 'fas fa-magic', description: '色彩调整、效果优化、最终成品', allowedTypes: ['.jpg', '.png', '.psd', '.tiff'], maxSize: 25 } ]; ``` #### 3.4.2 团队角色定义 ```typescript interface TeamRole { id: string; name: string; description: string; requiredSkills: string[]; icon: string; color: string; } export const TEAM_ROLES: TeamRole[] = [ { id: 'designer', name: '设计师', description: '负责建模、软装、后期设计工作', requiredSkills: ['autocad', 'sketchup', '3ds_max', 'photoshop'], icon: 'fas fa-pencil-ruler', color: '#007bff' }, { id: 'renderer', name: '渲染师', description: '负责效果图渲染和输出', requiredSkills: ['3ds_max', 'vray', 'corona', 'lumion'], icon: 'fas fa-palette', color: '#28a745' }, { id: 'team_leader', name: '组长', description: '负责团队协调和质量把控', requiredSkills: ['project_management', 'quality_control'], icon: 'fas fa-users-cog', color: '#ffc107' }, { id: 'technical', name: '技术', description: '负责技术验收和质量标准', requiredSkills: ['technical_review', 'standards'], icon: 'fas fa-tools', color: '#6c757d' } ]; ``` ## 4. 技术实现要点 ### 4.1 性能优化策略 - **虚拟滚动**:处理大量产品时的性能优化 - **懒加载**:文件和详情按需加载 - **缓存策略**:产品状态和文件信息缓存 - **CDN加速**:文件预览和下载使用CDN ### 4.2 用户体验优化 - **响应式设计**:适配不同屏幕尺寸 - **离线支持**:基本的离线操作和数据同步 - **快捷键支持**:提高操作效率 - **实时通知**:进度更新和状态变更通知 ### 4.3 数据一致性保障 - **事务处理**:确保批量操作的原子性 - **乐观锁**:防止并发修改冲突 - **版本控制**:文件版本管理和回滚 - **数据验证**:严格的前后端数据验证 ### 4.4 安全性考虑 - **权限控制**:基于角色的访问控制 - **文件安全**:文件上传安全检查和病毒扫描 - **数据加密**:敏感数据传输和存储加密 - **审计日志**:操作日志记录和追踪 --- **文档版本**: v4.0 (完整交付执行设计) **最后更新**: 2025-10-21 **维护者**: YSS Development Team :key="stage.id" class="stage-tab" :class="{ active: activeStage === stage.id }" @click="switchStage(stage.id)">

{{ stage.name }}

{{ stage.progress }}%
已选择 {{ selectedProducts.length }} 个产品

产品交付管理 - {{ getStageName(activeStage) }}

{{ product.productName }}

{{ getProductTypeLabel(product.productType) }}
{{ product.designerName }}
{{ getStatusLabel(product.status) }}
{{ product.content.progress }}%
{{ product.content.files.length }}
{{ product.actualHours }}/{{ product.estimatedHours }}h
¥{{ product.quotation.price.toLocaleString() }} {{ getQuotationStatusLabel(product.quotation.status) }}
{{ getBreakdownTypeLabel(key) }}: ¥{{ item.toLocaleString() }}
交付文件
{{ file.name }} {{ file.size }}
产品特定信息
{{ formatFieldValue(key, value) }}
备注信息
最后更新: {{ formatDateTime(product.content.lastUpdated) }}

产品协调报告

{{ coordinationReport.dependencies.length }}个
{{ coordinationReport.suggestions.length }}个

产品依赖关系

{{ dep.fromProductName }} {{ dep.toProductName }}
{{ dep.description }}

协调建议

{{ suggestion.title }}
{{ suggestion.description }}
• {{ action }}
``` ### 3.2 批量操作界面 ```html ``` ## 4. 技术实现要点 ### 4.1 性能优化 - **懒加载**:按需加载产品详情和文件列表 - **虚拟滚动**:处理大量产品时的性能问题 - **缓存机制**:缓存产品状态和进度数据 ### 4.2 用户体验优化 - **拖拽上传**:支持文件拖拽批量上传 - **实时同步**:进度更新实时推送到界面 - **离线支持**:基本的离线操作支持 ### 4.3 数据一致性 - **事务处理**:确保批量操作的数据一致性 - **冲突检测**:检测并发修改冲突 - **版本控制**:文件版本管理和回滚 --- **文档版本**: v3.0 (Product表统一空间管理) **最后更新**: 2025-10-20 **维护者**: YSS Development Team