feat: add product factors step four summary

dev-QQq
chenyuan 3 weeks ago
parent 6cf335de0d
commit 91b7a0409c

@ -275,3 +275,13 @@ export function checkProductDevelopmentFactorsStepThree(payload) {
}
return request({ url: "/api/student-training-answers/product-development-factors/step-3/validate", method: "post", data: payload, headers: { repeatSubmit: false } });
}
export function checkProductDevelopmentFactorsStepFour(payload) {
const value = payload || {};
if (isStudentDemo()) {
if (!String(value.successFactors || "").trim()) return Promise.reject(new Error("请填写产品成功的关键因素"));
if (!String(value.failedFactors || "").trim()) return Promise.reject(new Error("请填写产品失败的关键因素"));
return Promise.resolve({ code: 200, data: { valid: true, message: "校验通过" } });
}
return request({ url: "/api/student-training-answers/product-development-factors/step-4/validate", method: "post", data: payload, headers: { repeatSubmit: false } });
}

@ -112,6 +112,12 @@
<div class="task-actions"><button type="button" class="btn-nav btn-submit" :disabled="saving" @click="saveAndGoToStepFour"> 4 </button><button type="button" class="btn-nav" :disabled="saving" @click="resetTraining"><el-icon><RefreshLeft /></el-icon></button></div>
</template>
<template v-else-if="currentStep === 3">
<p class="factor-intro">结合案例信息及前述步骤分析案例资料中产品一成功的关键因素是什么产品二失败的关键因素是什么</p>
<label class="factor-intro">产品成功的关键因素</label><textarea v-model="stepFourForm.successFactors" class="table-textarea summary-textarea" />
<label class="factor-intro">产品失败的关键因素</label><textarea v-model="stepFourForm.failedFactors" class="table-textarea summary-textarea" />
<div class="task-actions"><button type="button" class="btn-nav btn-submit" :disabled="saving" @click="submitTraining"></button><button type="button" class="btn-nav" :disabled="saving" @click="resetTraining"></button></div>
</template>
<div v-else class="step-placeholder">
<p> {{ currentStep + 1 }} 步内容待配置</p>
<span>已保存的分析可从上方步骤栏返回查看</span>
@ -133,7 +139,7 @@
<script setup>
import { Download, Operation, RefreshLeft } from "@element-plus/icons-vue";
import { getTrainingTaskByKey } from "@/api/trainingTask";
import { checkProductDevelopmentFactorsStepOne, checkProductDevelopmentFactorsStepTwo, checkProductDevelopmentFactorsStepThree, getStudentTrainingAnswer, saveStudentTrainingAnswer } from "@/api/studentTrainingAnswer";
import { checkProductDevelopmentFactorsStepOne, checkProductDevelopmentFactorsStepTwo, checkProductDevelopmentFactorsStepThree, checkProductDevelopmentFactorsStepFour, getStudentTrainingAnswer, saveStudentTrainingAnswer } from "@/api/studentTrainingAnswer";
import useUserStore from "@/store/modules/user";
import TrainingAiSidebar from "@/views/components/TrainingAiSidebar.vue";
import TrainingMaterialButton from "@/views/components/TrainingMaterialButton.vue";
@ -206,6 +212,7 @@ const createStepThreeRows = () => STEP_THREE_FACTOR_ROWS.map((row) => ({ ...row,
const stepOneRows = ref(createStepOneRows());
const stepTwoRows = ref(createStepTwoRows());
const stepThreeRows = ref(createStepThreeRows());
const stepFourForm = ref({ successFactors: "", failedFactors: "" });
const progressItems = [
{ text: "进行中:成功/失败产品对比", status: "doing" },
@ -246,6 +253,7 @@ async function loadSavedAnswer() {
}
if (answer?.step2Answer) stepTwoRows.value = restoreRows(answer.step2Answer, createStepTwoRows);
if (answer?.step3Answer) stepThreeRows.value = restoreRows(answer.step3Answer, createStepThreeRows);
if (answer?.step4Answer) stepFourForm.value = { ...stepFourForm.value, ...parseSavedAnswer(answer.step4Answer) };
restoreCurrentStep(answer?.currentStep);
} catch (error) {
// Keep local draft when the backend answer cannot be loaded.
@ -327,6 +335,7 @@ function loadDraft() {
else stepOneRows.value = restoreRows(raw, createStepOneRows);
if (draft?.stepTwo) stepTwoRows.value = restoreRows(draft.stepTwo, createStepTwoRows);
if (draft?.stepThree) stepThreeRows.value = restoreRows(draft.stepThree, createStepThreeRows);
if (draft?.stepFour) stepFourForm.value = { ...stepFourForm.value, ...draft.stepFour };
}
} catch (error) {
clearDraft();
@ -354,7 +363,8 @@ function buildStepOneOutcome() {
}
function buildStepTwoOutcome() { return { title: taskTitle.value, factors: stepTwoRows.value.map((row) => ({ dimension: row.dimension, criteria: row.criteria, successAnalysis: row.successAnalysis, failedAnalysis: row.failedAnalysis })) }; }
function buildStepThreeOutcome() { return { title: taskTitle.value, factors: stepThreeRows.value.map((row) => ({ dimension: row.dimension, criteria: row.criteria, successAnalysis: row.successAnalysis, failedAnalysis: row.failedAnalysis })) }; }
function buildDraft() { return { stepOne: buildStepOneOutcome(), stepTwo: buildStepTwoOutcome(), stepThree: buildStepThreeOutcome(), currentStep: currentStep.value + 1 }; }
function buildStepFourOutcome() { return { successFactors: stepFourForm.value.successFactors, failedFactors: stepFourForm.value.failedFactors }; }
function buildDraft() { return { stepOne: buildStepOneOutcome(), stepTwo: buildStepTwoOutcome(), stepThree: buildStepThreeOutcome(), stepFour: buildStepFourOutcome(), currentStep: currentStep.value + 1 }; }
function buildStepOneValidationPayload() {
return {
@ -378,6 +388,8 @@ function buildStepTwoValidationPayload() { return { factors: stepTwoRows.value.m
function buildStepTwoAnswerPayload(saveAction = "SAVE", persistedStep = currentStep.value + 1) { return { step2Answer: JSON.stringify(buildStepTwoOutcome()), currentStep: persistedStep, submitted: false, saveAction }; }
function buildStepThreeValidationPayload() { return { factors: stepThreeRows.value.map(({ dimension, successAnalysis, failedAnalysis }) => ({ dimension, successAnalysis, failedAnalysis })) }; }
function buildStepThreeAnswerPayload(saveAction = "SAVE", persistedStep = currentStep.value + 1) { return { step3Answer: JSON.stringify(buildStepThreeOutcome()), currentStep: persistedStep, submitted: false, saveAction }; }
function buildStepFourValidationPayload() { return buildStepFourOutcome(); }
function buildStepFourAnswerPayload(saveAction = "SUBMIT", persistedStep = 4) { return { step4Answer: JSON.stringify(buildStepFourOutcome()), currentStep: persistedStep, submitted: true, saveAction }; }
async function saveAndGoToStepTwo() {
if (saving.value) return;
@ -424,11 +436,13 @@ async function saveAndGoToStepFour() {
proxy?.$modal?.msgSuccess("已保存,已进入第 4 步");
} catch (error) { proxy?.$modal?.msgError?.(error?.message || "请完成供应链与成本控制、商业模式和市场反馈分析"); } finally { saving.value = false; }
}
async function submitTraining() { if (saving.value) return; saving.value = true; try { await checkProductDevelopmentFactorsStepFour(buildStepFourValidationPayload()); await saveStudentTrainingAnswer(TASK_KEY, buildStepFourAnswerPayload("SUBMIT", 4)); clearDraft(); proxy?.$modal?.msgSuccess("任务提交成功"); } catch (error) { proxy?.$modal?.msgError?.(error?.message || "请完成关键因素分析"); } finally { saving.value = false; } }
async function resetTraining() {
stepOneRows.value = createStepOneRows();
stepTwoRows.value = createStepTwoRows();
stepThreeRows.value = createStepThreeRows();
stepFourForm.value = { successFactors: "", failedFactors: "" };
currentStep.value = 0;
clearDraft();
try {

@ -40,5 +40,12 @@ assert.match(page, /buildStepThreeAnswerPayload\("SAVE", 4\)/, "step three must
assert.doesNotMatch(page, /buildStepThreeAnswerPayload\("SUBMIT"/, "step three must not submit the task");
assert.match(api, /export function checkProductDevelopmentFactorsStepThree\(/, "the student API must expose the step-three validator");
assert.match(api, /product-development-factors\/step-3\/validate/, "the student API must call the step-three validation route");
assert.match(page, /v-else-if="currentStep === 3"/, "the fourth form must render only on step four");
assert.match(page, /产品成功的关键因素/, "step four must include success factors");
assert.match(page, /产品失败的关键因素/, "step four must include failure factors");
assert.match(page, /step4Answer/, "step four must save its own answer field");
assert.match(page, /await checkProductDevelopmentFactorsStepFour\(buildStepFourValidationPayload\(\)\)/, "step four must validate before submit");
assert.match(page, /buildStepFourAnswerPayload\("SUBMIT", 4\)/, "step four must submit the task");
assert.match(api, /product-development-factors\/step-4\/validate/, "the student API must call the step-four validation route");
console.log("product development factors step-one contract passed");

Loading…
Cancel
Save