部分项目的 currentStage 已经推进到后续阶段,但实际上没有完成前面阶段的必填工作。需要将这些项目回退到正确的阶段。
必填条件:
project.title 存在且不为空project.projectType 存在(家装/工装)project.demoday 存在(小图日期)project.data.quotation.total > 0(报价总额)data.approvalStatus === 'approved'(组长已审批)验证逻辑:
async function isOrderStageCompleted(project: any): Promise<boolean> {
// 1. 检查基本信息
if (!project.get('title')?.trim()) return false;
if (!project.get('projectType')) return false;
if (!project.get('demoday')) return false;
// 2. 检查报价
const data = project.get('data') || {};
if (!data.quotation || data.quotation.total <= 0) return false;
// 3. 检查设计师分配或审批状态
const query = new Parse.Query('ProjectTeam');
query.equalTo('project', project.toPointer());
query.notEqualTo('isDeleted', true);
const teams = await query.find();
const hasDesigner = teams.length > 0;
const isApproved = data.approvalStatus === 'approved';
return hasDesigner || isApproved;
}
必填条件:
project.data.requirementsAnalysis 存在)验证逻辑:
async function isRequirementsStageCompleted(project: any): Promise<boolean> {
const data = project.get('data') || {};
// 检查是否有需求分析数据
if (data.requirementsAnalysis && Object.keys(data.requirementsAnalysis).length > 0) {
return true;
}
// 检查是否有空间需求数据
if (data.spaceRequirements && Object.keys(data.spaceRequirements).length > 0) {
return true;
}
return false;
}
必填条件:
data.deliveryStages 中所有阶段的 approvalStatus === 'approved'验证逻辑:
async function isDeliveryStageCompleted(project: any): Promise<boolean> {
const data = project.get('data') || {};
const deliveryStages = data.deliveryStages || {};
// 检查所有交付阶段
const requiredStages = ['modeling', 'softDecor', 'rendering', 'postProcess'];
for (const stage of requiredStages) {
const stageData = deliveryStages[stage];
if (!stageData || stageData.approvalStatus !== 'approved') {
return false;
}
}
return true;
}
文件:创建新文件 repair-project-stages.ts
import { FmodeParse } from 'fmode-ng/core';
const Parse = FmodeParse.with('nova');
/**
* 修复项目阶段数据
* 将没有完成阶段工作的项目回退到正确的阶段
*/
export async function repairProjectStages(): Promise<void> {
console.log('🔧 开始修复项目阶段数据...');
try {
// 查询所有项目
const query = new Parse.Query('Project');
query.notEqualTo('isDeleted', true);
query.limit(1000);
const projects = await query.find();
console.log(`📊 共找到 ${projects.length} 个项目`);
let fixedCount = 0;
const fixedProjects: Array<{
id: string;
title: string;
oldStage: string;
newStage: string;
reason: string;
}> = [];
for (const project of projects) {
const currentStage = project.get('currentStage');
const correctStage = await getCorrectStage(project);
if (currentStage !== correctStage) {
console.log(`\n🔄 项目需要修复: ${project.get('title')}`);
console.log(` 当前阶段: ${currentStage}`);
console.log(` 应该阶段: ${correctStage}`);
// 记录修复信息
const fixInfo = {
id: project.id,
title: project.get('title'),
oldStage: currentStage,
newStage: correctStage,
reason: await getFixReason(project, correctStage)
};
// 回退到正确的阶段
project.set('currentStage', correctStage);
// 清除不合理的审批状态
const data = project.get('data') || {};
if (correctStage === '订单分配' && data.approvalStatus === 'approved') {
// 如果回退到订单分配,但没有完成工作,清除approved状态
const isCompleted = await isOrderStageCompleted(project);
if (!isCompleted) {
data.approvalStatus = null;
project.set('data', data);
}
}
await project.save();
fixedProjects.push(fixInfo);
fixedCount++;
console.log(` ✅ 已修复`);
}
}
console.log(`\n✅ 修复完成!共修复 ${fixedCount} 个项目`);
if (fixedProjects.length > 0) {
console.log('\n📋 修复详情:');
console.table(fixedProjects);
}
return;
} catch (error) {
console.error('❌ 修复失败:', error);
throw error;
}
}
/**
* 获取项目应该处于的正确阶段
*/
async function getCorrectStage(project: any): Promise<string> {
// 检查订单分配阶段是否完成
const orderCompleted = await isOrderStageCompleted(project);
if (!orderCompleted) {
return '订单分配';
}
// 检查确认需求阶段是否完成
const requirementsCompleted = await isRequirementsStageCompleted(project);
if (!requirementsCompleted) {
return '确认需求';
}
// 检查交付执行阶段是否完成
const deliveryCompleted = await isDeliveryStageCompleted(project);
if (!deliveryCompleted) {
return '交付执行';
}
// 所有阶段都完成了,应该在售后归档
return '售后归档';
}
/**
* 获取修复原因说明
*/
async function getFixReason(project: any, correctStage: string): Promise<string> {
const reasons: string[] = [];
if (correctStage === '订单分配') {
if (!project.get('title')?.trim()) reasons.push('缺少项目名称');
if (!project.get('projectType')) reasons.push('缺少项目类型');
if (!project.get('demoday')) reasons.push('缺少小图日期');
const data = project.get('data') || {};
if (!data.quotation || data.quotation.total <= 0) reasons.push('缺少报价');
const query = new Parse.Query('ProjectTeam');
query.equalTo('project', project.toPointer());
query.notEqualTo('isDeleted', true);
const teams = await query.find();
if (teams.length === 0 && data.approvalStatus !== 'approved') {
reasons.push('未分配设计师且未审批');
}
}
if (correctStage === '确认需求') {
const data = project.get('data') || {};
if (!data.requirementsAnalysis || Object.keys(data.requirementsAnalysis).length === 0) {
reasons.push('缺少需求分析数据');
}
}
if (correctStage === '交付执行') {
const data = project.get('data') || {};
const deliveryStages = data.deliveryStages || {};
const requiredStages = ['modeling', 'softDecor', 'rendering', 'postProcess'];
for (const stage of requiredStages) {
if (!deliveryStages[stage] || deliveryStages[stage].approvalStatus !== 'approved') {
reasons.push(`${stage}阶段未完成`);
}
}
}
return reasons.join(', ');
}
/**
* 检查订单分配阶段是否完成
*/
async function isOrderStageCompleted(project: any): Promise<boolean> {
// 1. 检查基本信息
if (!project.get('title')?.trim()) {
console.log(' ❌ 缺少项目名称');
return false;
}
if (!project.get('projectType')) {
console.log(' ❌ 缺少项目类型');
return false;
}
if (!project.get('demoday')) {
console.log(' ❌ 缺少小图日期');
return false;
}
// 2. 检查报价
const data = project.get('data') || {};
if (!data.quotation || data.quotation.total <= 0) {
console.log(' ❌ 缺少报价数据');
return false;
}
// 3. 检查设计师分配或审批状态
const query = new Parse.Query('ProjectTeam');
query.equalTo('project', project.toPointer());
query.notEqualTo('isDeleted', true);
const teams = await query.find();
const hasDesigner = teams.length > 0;
const isApproved = data.approvalStatus === 'approved';
if (!hasDesigner && !isApproved) {
console.log(' ❌ 未分配设计师且未审批');
return false;
}
console.log(' ✅ 订单分配阶段已完成');
return true;
}
/**
* 检查确认需求阶段是否完成
*/
async function isRequirementsStageCompleted(project: any): Promise<boolean> {
const data = project.get('data') || {};
// 检查是否有需求分析数据
if (data.requirementsAnalysis && Object.keys(data.requirementsAnalysis).length > 0) {
console.log(' ✅ 确认需求阶段已完成(有需求分析)');
return true;
}
// 检查是否有空间需求数据
if (data.spaceRequirements && Object.keys(data.spaceRequirements).length > 0) {
console.log(' ✅ 确认需求阶段已完成(有空间需求)');
return true;
}
console.log(' ❌ 缺少需求数据');
return false;
}
/**
* 检查交付执行阶段是否完成
*/
async function isDeliveryStageCompleted(project: any): Promise<boolean> {
const data = project.get('data') || {};
const deliveryStages = data.deliveryStages || {};
// 检查所有交付阶段
const requiredStages = ['modeling', 'softDecor', 'rendering', 'postProcess'];
for (const stage of requiredStages) {
const stageData = deliveryStages[stage];
if (!stageData || stageData.approvalStatus !== 'approved') {
console.log(` ❌ ${stage}阶段未完成`);
return false;
}
}
console.log(' ✅ 交付执行阶段已完成');
return true;
}
复制粘贴以下代码:
// 数据修复脚本(浏览器版本)
(async function repairProjectStages() {
console.log('🔧 开始修复项目阶段数据...');
const Parse = window.Parse || (await import('parse')).default;
// 查询所有项目
const query = new Parse.Query('Project');
query.notEqualTo('isDeleted', true);
query.limit(1000);
const projects = await query.find();
console.log(`📊 共找到 ${projects.length} 个项目`);
let fixedCount = 0;
for (const project of projects) {
const currentStage = project.get('currentStage');
const data = project.get('data') || {};
// 检查订单分配阶段
if (currentStage === '确认需求' || currentStage === '交付执行' || currentStage === '售后归档') {
// 验证订单分配是否完成
const hasTitle = !!project.get('title')?.trim();
const hasType = !!project.get('projectType');
const hasDemoday = !!project.get('demoday');
const hasQuotation = data.quotation && data.quotation.total > 0;
if (!hasTitle || !hasType || !hasDemoday || !hasQuotation) {
console.log(`\n🔄 项目需要回退: ${project.get('title')}`);
console.log(` 原阶段: ${currentStage} → 新阶段: 订单分配`);
console.log(` 原因: 订单分配阶段未完成`);
project.set('currentStage', '订单分配');
data.approvalStatus = null;
project.set('data', data);
await project.save();
fixedCount++;
console.log(` ✅ 已回退`);
continue;
}
}
// 检查确认需求阶段
if (currentStage === '交付执行' || currentStage === '售后归档') {
const hasRequirements = (data.requirementsAnalysis && Object.keys(data.requirementsAnalysis).length > 0) ||
(data.spaceRequirements && Object.keys(data.spaceRequirements).length > 0);
if (!hasRequirements) {
console.log(`\n🔄 项目需要回退: ${project.get('title')}`);
console.log(` 原阶段: ${currentStage} → 新阶段: 确认需求`);
console.log(` 原因: 确认需求阶段未完成`);
project.set('currentStage', '确认需求');
await project.save();
fixedCount++;
console.log(` ✅ 已回退`);
continue;
}
}
// 检查交付执行阶段
if (currentStage === '售后归档') {
const deliveryStages = data.deliveryStages || {};
const requiredStages = ['modeling', 'softDecor', 'rendering', 'postProcess'];
let allCompleted = true;
for (const stage of requiredStages) {
if (!deliveryStages[stage] || deliveryStages[stage].approvalStatus !== 'approved') {
allCompleted = false;
break;
}
}
if (!allCompleted) {
console.log(`\n🔄 项目需要回退: ${project.get('title')}`);
console.log(` 原阶段: ${currentStage} → 新阶段: 交付执行`);
console.log(` 原因: 交付执行阶段未完成`);
project.set('currentStage', '交付执行');
await project.save();
fixedCount++;
console.log(` ✅ 已回退`);
}
}
}
console.log(`\n✅ 修复完成!共回退 ${fixedCount} 个项目到正确阶段`);
})();
在管理员后台添加一个"数据修复"按钮:
文件:src/app/pages/admin/dashboard/dashboard.html
<div class="admin-tools">
<button class="btn btn-warning" (click)="repairProjectStages()">
<svg class="icon">...</svg>
修复项目阶段数据
</button>
</div>
文件:src/app/pages/admin/dashboard/dashboard.ts
async repairProjectStages() {
const confirmed = await window?.fmode?.confirm(
'此操作将检查所有项目的阶段状态,\n' +
'并将未完成工作的项目回退到正确阶段。\n\n' +
'是否继续?'
);
if (!confirmed) return;
try {
this.loading = true;
// 调用修复方法
await repairProjectStages();
window?.fmode?.toast?.success('项目阶段数据修复完成!');
// 刷新列表
await this.loadProjects();
} catch (error) {
console.error('修复失败:', error);
window?.fmode?.alert('修复失败:' + error.message);
} finally {
this.loading = false;
}
}
修复前:
currentStage: "确认需求"title: nullprojectType: nullquotation.total: 0修复后:
currentStage: "订单分配" ← ✅ 回退data.approvalStatus: null ← ✅ 清除错误状态原因:缺少项目名称、类型和报价
修复前:
currentStage: "交付执行"data.requirementsAnalysis: {}(空对象)data.spaceRequirements: undefined修复后:
currentStage: "确认需求" ← ✅ 回退原因:缺少需求分析数据
修复前:
currentStage: "售后归档"data.deliveryStages.modeling.approvalStatus: "pending"data.deliveryStages.softDecor.approvalStatus: null修复后:
currentStage: "交付执行" ← ✅ 回退原因:交付子阶段未全部审批通过
// 导出项目数据(修复前)
const query = new Parse.Query('Project');
const projects = await query.find();
const backup = projects.map(p => ({
id: p.id,
title: p.get('title'),
currentStage: p.get('currentStage'),
data: p.get('data')
}));
console.log('备份数据:', JSON.stringify(backup, null, 2));
修改脚本,添加 dryRun 参数:
async function repairProjectStages(dryRun = true) {
// ... 检查逻辑 ...
if (dryRun) {
console.log(` 🔍 [预览] 需要回退到: ${correctStage}`);
// 不保存,只输出
} else {
project.set('currentStage', correctStage);
await project.save();
console.log(` ✅ 已回退`);
}
}
// 先预览
await repairProjectStages(true);
// 确认后执行
await repairProjectStages(false);
currentStage 反映真实完成状态