当项目进入"售后归档"阶段时,系统自动将项目数据转换并添加到Case(案例库)表中,实现项目到案例的无缝转换。
2025-10-29
ProjectService.updateProjectStage()方法从Project表 + Product表 → Case表的完整映射:
company: Project.company // 企业指针
isDeleted: false // 软删除标记(默认false)
name: Project.title // 案例名称来自项目标题
project: Project.objectId // 关联原项目
designer: Project.assignee || Project.designer // 设计师
team: Project.team || Project.department // 团队
address: Project.address // 客户地址(可选)
coverImage: images[0] // 第一张图片作为封面
images: [
...Project.data.images, // 项目图片
...Product.attachments[].url // 产品附件图片
]
totalPrice: Project.totalAmount || Project.orderAmount || Project.data.budget.total
completionDate: new Date() // 当前时间作为完工时间
tag: [
...Project.tags, // 项目标签
Project.style // 风格
]
isPublished: false // 默认不发布,需要审核
isExcellent: false // 默认非优秀案例
index: 0 // 默认排序
info: {
area: Project.data.area || Project.area || 100,
projectType: Project.data.projectType || "家装",
roomType: Project.data.roomType || "三居室",
spaceType: Project.data.spaceType || "平层",
renderingLevel: Project.data.renderingLevel || "中端"
}
data: {
// 装修规格信息
renovationSpec: {
roomAreas: Project.data.specMap.roomAreas,
renovationType: Project.data.renovationType,
renovationScope: Project.data.specMap.renovationScope,
constructionItems: Project.data.specMap.constructionItems
},
// 产品详细信息
productsDetail: Product[].map(p => ({
productId: p.id,
productName: p.name,
category: p.category,
brand: p.brand,
quantity: p.quantity,
unitPrice: p.price,
totalPrice: p.quantity * p.price,
spaceArea: p.spaceArea,
specifications: p.specifications
})),
// 预算信息
budget: Project.data.budget,
// 时间线信息
timeline: Project.data.timeline,
// 设计亮点
highlights: Project.data.highlights,
// 设计特色
features: Project.data.features,
// 设计挑战
challenges: Project.data.challenges,
// 材料信息
materials: Project.data.materials,
// 客户信息
clientInfo: Project.data.clientInfo,
// 图片详细信息
imagesDetail: {
beforeRenovation: [],
afterRenovation: Product.attachments[],
videos: Product.attachments[type='video'],
panoramas: Product.attachments[type='panorama']
}
}
文件: yss-project/src/app/services/project-to-case.service.ts
核心方法:
onProjectStageChanged(projectId, newStage) - 监听阶段变化createCaseFromProject(projectId) - 从项目创建案例checkCaseExists(projectId) - 检查是否已创建过案例文件: yss-project/src/app/services/project.service.ts
修改内容:
import { ProjectToCaseService } from './project-to-case.service';
constructor(private projectToCaseService: ProjectToCaseService) {}
.then(savedProject => {
console.log(`✅ 项目阶段已更新并保存到数据库: ${stage}`);
// 🎯 如果进入"售后归档"阶段,自动创建案例
if (stage === '售后归档') {
this.projectToCaseService.onProjectStageChanged(projectId, stage)
.then(() => {
console.log('✅ 项目已自动添加到案例库');
})
.catch(err => {
console.error('❌ 自动创建案例失败:', err);
});
}
observer.next(project);
observer.complete();
})
用户操作:项目推进到"售后归档"阶段
↓
ProjectService.updateProjectStage('售后归档')
↓
保存阶段到Parse数据库
- Project.currentStage = '售后归档'
- Project.stage = '售后归档'
- Project.status = '已完成'
↓
触发自动创建案例逻辑
↓
ProjectToCaseService.onProjectStageChanged()
↓
检查是否已创建过案例
- Query Case表 where project = projectId
↓
如果未创建,执行创建
↓
获取项目数据
- Query Project表 include(assignee, designer, team, customer, address)
↓
获取产品数据
- Query Product表 where project = projectId include(attachments)
↓
提取并映射数据
- 基础信息
- 关联关系
- 媒体资源(图片、视频)
- 数值信息(价格)
- 标签分类
- info对象(必填字段)
- data对象(扩展字段)
↓
创建Case对象并保存
- const caseObj = new CaseClass()
- caseObj.set(...)
- await caseObj.save()
↓
控制台输出
✅ 案例创建成功: caseId
✅ 项目已自动添加到案例库
↓
案例库自动显示新案例
- 访问 @case-library/ 即可看到
const query = new Parse.Query('Case');
query.equalTo('company', companyPointer);
query.equalTo('project', projectPointer);
query.notEqualTo('isDeleted', true);
const cases = await query.find();
const query = new Parse.Query('Case');
query.equalTo('company', companyPointer);
query.equalTo('isPublished', true);
query.notEqualTo('isDeleted', true);
query.descending('createdAt');
const cases = await query.find();
const query = new Parse.Query('Case');
query.equalTo('company', companyPointer);
query.containsAll('tag', ['现代简约', '北欧风']);
query.notEqualTo('isDeleted', true);
const cases = await query.find();
案例库页面(@case-library/)已经实现了完整的显示逻辑:
创建案例时会验证以下必填字段:
✅ name: 案例名称(来自项目标题)
✅ project: 关联项目指针
✅ designer: 设计师指针
✅ team: 团队指针
✅ coverImage: 封面图片
✅ images: 图片数组(至少1张)
✅ totalPrice: 总价
✅ completionDate: 完工时间
✅ tag: 标签数组(至少1个)
✅ info.area: 面积
✅ info.projectType: 项目类型
✅ info.roomType: 户型
✅ info.spaceType: 空间类型
✅ info.renderingLevel: 渲染水平
如果某些字段缺失,使用默认值:
area: projectData.area || project.get('area') || 100
projectType: projectData.projectType || '家装'
roomType: projectData.roomType || '三居室'
spaceType: projectData.spaceType || '平层'
renderingLevel: projectData.renderingLevel || '中端'
if (!project) {
throw new Error('项目不存在');
}
if (!designer) {
throw new Error('项目缺少设计师信息');
}
if (!team) {
throw new Error('项目缺少团队信息');
}
const existingCase = await this.checkCaseExists(projectId);
if (existingCase) {
console.log('项目已有关联案例,跳过创建');
return;
}
this.projectToCaseService.onProjectStageChanged(projectId, stage)
.catch(err => {
console.error('❌ 自动创建案例失败:', err);
// 不阻塞主流程,仅记录错误
});
步骤:
预期结果:
✅ 项目阶段已更新并保存到数据库: 售后归档📦 项目 xxx 进入售后归档阶段,准备创建案例...✅ 案例创建成功: caseId✅ 项目已自动添加到案例库步骤:
预期结果:
⚠️ 项目 xxx 已有关联案例,跳过创建步骤:
预期结果:
❌ 创建案例失败: 项目缺少设计师信息步骤:
预期结果:
images字段包含所有产品的附件图片coverImagedata.imagesDetail正确分类图片(装修前/后、视频、全景)访问:http://localhost:4200/customer-service/case-library
案例卡片
详情面板
筛选功能
reviewStatus: '待审核' | '已通过' | '已拒绝'company字段实现多租户隔离isDeleted字段实现软删除isPublished控制案例是否对外显示yss-project/src/app/services/project-to-case.service.tsyss-project/src/app/services/project.service.tsyss-project/src/app/services/case.service.ts - 案例CRUD服务yss-project/src/app/pages/customer-service/case-library/case-library.ts - 案例库页面yss-project/src/app/pages/customer-service/case-library/case-library.html - 案例库模板