# 项目阶段切换验证问题分析 ## 问题描述 **用户反馈**:订单分配阶段还没有填写必填信息和分配组员,点击顶部导航栏就可以直接进入其他阶段。 ## 问题根源 ### 1️⃣ switchStage 方法取消了权限验证 **文件**:`project-detail.component.ts` 第504-535行 ```typescript /** * 切换阶段(点击顶部导航栏,无权限限制) * ❌ 允许自由访问所有阶段,无论状态如何 */ switchStage(stageId: string) { console.log('🔄 用户点击切换阶段:', stageId); // ❌ 取消权限限制,允许访问所有阶段 console.log(`✅ 允许访问阶段: ${stageId} (状态: ${status})`); // ❌ 直接更新状态,不验证 this.currentStage = stageId; // ❌ 直接导航,不检查前置条件 this.router.navigate([stageId], { relativeTo: this.route }); } ``` **问题**: - ❌ 没有检查当前阶段是否已完成必填项 - ❌ 没有检查是否已分配设计师 - ❌ 没有检查审批状态 - ❌ 直接允许跳转到任意阶段 ### 2️⃣ 导航栏设置为可点击 **文件**:`project-detail.component.html` 第10-11行 ```html
(click)="switchStage(stage.id)"> ``` **问题**:所有阶段都标记为 `clickable=true`,没有根据阶段状态禁用未完成的阶段。 --- ## 正确的阶段切换逻辑 ### ✅ 应该的验证流程 ``` 用户点击导航栏阶段 ↓ 检查目标阶段状态 ↓ 【情况1】目标阶段状态 = 'pending'(未开始) → ❌ 禁止跳转 → 提示:"请先完成当前阶段" ↓ 【情况2】目标阶段状态 = 'active'(当前阶段) → ✅ 允许跳转(刷新当前页面) ↓ 【情况3】目标阶段状态 = 'completed'(已完成) → ✅ 允许跳转(可以回顾已完成的阶段) ``` ### ✅ 各阶段的完成条件 #### 订单分配阶段(order)→ 确认需求(requirements) **必填项验证**:(`stage-order.component.ts` 第1192-1223行) ```typescript // 1. 项目名称 if (!this.projectInfo.title.trim()) { alert('请填写项目名称'); return; } // 2. 项目类型 if (!this.projectInfo.projectType) { alert('请选择项目类型'); return; } // 3. 小图日期 if (!this.projectInfo.demoday) { alert('请选择小图日期'); return; } // 4. 报价明细 if (this.quotation.total === 0) { alert('请配置报价明细'); return; } ``` **设计师分配验证**:(第1228-1237行) ```typescript // 检查是否已分配设计师 const query = new Parse.Query('ProjectTeam'); query.equalTo('project', this.project.toPointer()); query.notEqualTo('isDeleted', true); const assignedTeams = await query.find(); const hasAssignedDesigners = assignedTeams.length > 0; ``` **完成条件**: - ✅ 已填写必填信息 + 已分配设计师 → 自动通过,进入"确认需求" - ⚠️ 已填写必填信息 + 未分配设计师 → 提交组长审批,等待批准 #### 确认需求阶段(requirements)→ 交付执行(delivery) **完成条件**:(`stage-requirements.component.ts`) - ✅ 所有空间都已确认需求 - ✅ 已保存需求数据 #### 交付执行阶段(delivery)→ 售后归档(aftercare) **完成条件**:(`stage-delivery.component.ts`) - ✅ 所有交付阶段(建模、软装、渲染、后期)都已审批通过 --- ## 修复方案 ### 方案1:添加阶段切换验证 **修改文件**:`project-detail.component.ts` ```typescript /** * 切换阶段(添加验证逻辑) */ switchStage(stageId: string) { console.log('🔄 用户点击切换阶段:', stageId); const status = this.getStageStatus(stageId); // ✅ 验证1:只允许访问当前阶段或已完成的阶段 if (status === 'pending') { console.warn('❌ 无法访问未开始的阶段:', stageId); window?.fmode?.alert('请先完成当前阶段,再进入下一阶段'); return; } // ✅ 验证2:检查当前阶段是否已完成必填项(可选,根据需求) // const canLeaveCurrentStage = await this.checkCurrentStageCompletion(); // if (!canLeaveCurrentStage) { // window?.fmode?.alert('当前阶段还有未完成的必填项'); // return; // } // ✅ 允许访问当前阶段或已完成的阶段 console.log(`✅ 允许访问阶段: ${stageId} (状态: ${status})`); this.currentStage = stageId; this.router.navigate([stageId], { relativeTo: this.route }) .then(success => { if (success) { console.log('✅ 导航成功:', stageId); } else { console.warn('⚠️ 导航失败:', stageId); } }) .catch(err => { console.error('❌ 导航出错:', err); }); } ``` ### 方案2:禁用未开始的阶段点击 **修改文件**:`project-detail.component.html` ```html
[class.disabled]="getStageStatus(stage.id) === 'pending'" (click)="getStageStatus(stage.id) !== 'pending' && switchStage(stage.id)"> ``` **对应CSS**:(在 `project-detail.component.scss` 中) ```scss .stage-item { &.disabled { cursor: not-allowed; opacity: 0.5; pointer-events: none; // 禁用点击事件 } &.clickable:not(.disabled) { cursor: pointer; &:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); } } } ``` --- ## 完整修复代码 ### 1️⃣ 修改 TypeScript 文件 **文件**:`project-detail.component.ts` 第504-535行 ```typescript /** * 切换阶段(添加权限验证) * 只允许访问当前阶段或已完成的阶段 */ switchStage(stageId: string) { console.log('🔄 用户点击切换阶段:', stageId, { currentRoute: this.router.url, currentStage: this.currentStage, workflowStage: this.project?.get('currentStage') }); // 获取点击阶段的状态 const status = this.getStageStatus(stageId); // ✅ 关键验证:只允许访问当前阶段或已完成的阶段 if (status === 'pending') { console.warn(`❌ 阶段 "${stageId}" 尚未开始,无法访问`); // 获取阶段的友好名称 const stageName = this.stages.find(s => s.id === stageId)?.name || stageId; const currentStageName = this.stages.find(s => s.id === this.currentStage)?.name || this.currentStage; window?.fmode?.alert( `无法进入"${stageName}"阶段\n\n` + `请先完成"${currentStageName}"阶段的必填项:\n` + `1. 填写项目基本信息\n` + `2. 配置报价明细\n` + `3. 分配设计师(或提交组长审批)` ); return; } // ✅ 允许访问当前阶段或已完成的阶段 console.log(`✅ 允许访问阶段: ${stageId} (状态: ${status})`); // 更新本地显示状态 this.currentStage = stageId; // 导航到指定阶段 this.router.navigate([stageId], { relativeTo: this.route }) .then(success => { if (success) { console.log('✅ 导航成功:', stageId); } else { console.warn('⚠️ 导航失败:', stageId); } }) .catch(err => { console.error('❌ 导航出错:', err); }); } ``` ### 2️⃣ 修改 HTML 模板 **文件**:`project-detail.component.html` 第4-11行 ```html @for (stage of stages; track stage.id) {
} ``` ### 3️⃣ 添加禁用样式 **文件**:`project-detail.component.scss` ```scss .stage-item { // ... 原有样式 ... // ✅ 新增:禁用状态 &.disabled { cursor: not-allowed !important; opacity: 0.5; pointer-events: none; .stage-circle { background: #e0e0e0; border-color: #e0e0e0; } .stage-label { color: #999; } } // ✅ 修改:只有非禁用的可点击项才有hover效果 &.clickable:not(.disabled) { cursor: pointer; &:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); } } } ``` --- ## 验证步骤 ### 测试1:订单分配阶段未完成 1. 创建新项目,进入订单分配阶段 2. **不填写**任何信息 3. 点击顶部导航栏的"确认需求" 4. **预期结果**:❌ 弹出提示"请先完成当前阶段" ### 测试2:订单分配阶段已完成 1. 填写项目名称、类型、日期 2. 配置报价明细 3. 分配设计师 4. 点击"确认订单"按钮 5. 项目自动进入"确认需求"阶段 6. **预期结果**:✅ 顶部导航栏"订单分配"显示为绿色(已完成),"确认需求"显示为红色(当前) ### 测试3:回顾已完成的阶段 1. 在"确认需求"阶段 2. 点击顶部导航栏的"订单分配"(已完成) 3. **预期结果**:✅ 可以访问,查看已填写的信息 ### 测试4:尝试跳过阶段 1. 在"确认需求"阶段 2. 点击顶部导航栏的"交付执行"(未开始) 3. **预期结果**:❌ 弹出提示"请先完成当前阶段" --- ## 总结 ### ❌ 当前问题 1. `switchStage` 方法完全取消了权限验证 2. 所有阶段都可以随意点击 3. 用户可以绕过必填项验证直接进入其他阶段 ### ✅ 修复后效果 1. 只能访问当前阶段或已完成的阶段 2. 未开始的阶段显示为禁用状态 3. 点击未开始的阶段会弹出友好提示 4. 必须通过提交/审批流程才能推进到下一阶段 ### 📝 关键改进 - **严格验证**:根据 `getStageStatus()` 的返回值进行权限判断 - **友好提示**:告知用户需要完成哪些必填项 - **视觉反馈**:禁用状态的阶段显示为灰色且不可点击 - **保持灵活性**:允许回顾已完成的阶段