@ -20,7 +20,13 @@
< p class = "section-eyebrow" > BOM cost workspace < / p >
< h2 > { { currentStepTitle } } < / h2 >
< / div >
< TrainingTaskToolbar class = "hardware-cost-toolbar" :task-key ="TASK_KEY" / >
< TrainingTaskToolbar
class = "hardware-cost-toolbar"
: task - key = "TASK_KEY"
: on - submit = "submitTask"
: on - reset = "resetTraining"
: on - export = "exportTrainingOutcome"
/ >
< / div >
< nav class = "step-indicator" aria -label = " 智 能 硬 件 成 本 估 算 步 骤 " >
@ -283,7 +289,7 @@ import TrainingTaskToolbar from "@/views/components/TrainingTaskToolbar.vue";
import TrainingTaskBrief from "@/views/components/TrainingTaskBrief.vue" ;
import { getTrainingTaskByKey } from "@/api/trainingTask" ;
import { parseTrainingArray } from "@/views/training/taskKeyMap" ;
import { getStudentTrainingAnswer, saveStudentTrainingAnswer , uploadStudentTrainingFile , validateHardwareCostStepOne , validateHardwareCostStepTwo , validateHardwareCostStepThree , validateHardwareCostStepFour } from "@/api/studentTrainingAnswer" ;
import { deleteStudentTrainingAnswer, getStudentTrainingAnswer, saveStudentTrainingAnswer , uploadStudentTrainingFile , validateHardwareCostStepOne , validateHardwareCostStepTwo , validateHardwareCostStepThree , validateHardwareCostStepFour } from "@/api/studentTrainingAnswer" ;
import { isStudentDemo } from "@/utils/studentDemo" ;
const TASK _KEY = "hardware-cost" ;
@ -294,7 +300,7 @@ const MAX_ROWS = 40;
const { proxy } = getCurrentInstance ( ) ;
const taskConfig = ref ( { } ) ;
const currentStep = ref ( 1 ) ;
const isStepSaving = computed ( ( ) => saving . value || savingStepTwo . value || savingStepThree . value || savingStepFour . value || Boolean ( uploadingChart . value ) ) ;
const isStepSaving = computed ( ( ) => saving . value || savingStepTwo . value || savingStepThree . value || savingStepFour . value || submittingTraining . value || Boolean ( uploadingChart . value ) ) ;
const currentStepTitle = computed ( ( ) => ( {
1 : "核心元器件选型组合" ,
2 : "批量成本核算与约束验证" ,
@ -312,6 +318,7 @@ const savingStepThree = ref(false);
const exportingStepThree = ref ( false ) ;
const stepThreeSaved = ref ( false ) ;
const savingStepFour = ref ( false ) ;
const submittingTraining = ref ( false ) ;
const uploadingChart = ref ( "" ) ;
const stepTwoValidation = ref ( null ) ;
const stepTwo = reactive ( { smallBatchConstraint : "" , largeBatchConstraint : "" , items : [ ] } ) ;
@ -595,7 +602,7 @@ async function handleCostChartUpload(event, type) {
uploadingChart . value = "" ;
}
}
async function saveStepFour ( ) {
async function saveStepFour ( saveAction = "SAVE" ) {
savingStepFour . value = true ;
try {
const payload = stepFourPayload ( ) ;
@ -605,17 +612,19 @@ async function saveStepFour() {
if ( ! validated . valid ) { notifyError ( validated . message || "请完善成本结构图和优化方案" ) ; return ; }
await saveStudentTrainingAnswer ( TASK _KEY , {
currentStep : 4 ,
saveAction : "SAVE" ,
submitted : false ,
saveAction ,
submitted : saveAction === "SUBMIT" ,
step4Answer : JSON . stringify ( {
smallBatchChart : { ... stepFour . smallBatchChart } ,
largeBatchChart : { ... stepFour . largeBatchChart } ,
optimizationPlan : payload . optimizationPlan ,
} ) ,
} ) ;
notifySuccess ( "成本优化决策已保存" ) ;
notifySuccess ( saveAction === "SUBMIT" ? "实训任务已提交" : "成本优化决策已保存" ) ;
return true ;
} catch ( error ) {
notifyError ( error ? . message || "成本优化决策保存失败,请检查填写内容" ) ;
return false ;
} finally {
savingStepFour . value = false ;
}
@ -624,9 +633,147 @@ async function saveCurrentProgress() {
if ( currentStep . value === 1 ) { await saveStepOne ( false ) ; return Boolean ( savedStepOneItems . value . length ) ; }
if ( currentStep . value === 2 ) { await saveStepTwo ( ) ; return stepTwoSaved . value ; }
if ( currentStep . value === 3 ) { await saveStepThree ( false ) ; return stepThreeSaved . value ; }
await saveStepFour ( ) ;
return true ;
return saveStepFour ( ) ;
}
function buildStepOneAnswer ( items , total ) {
return JSON . stringify ( { items , totalAmount : Number ( total ) . toFixed ( 2 ) } ) ;
}
function buildStepTwoAnswer ( validated ) {
return JSON . stringify ( {
items : stepTwo . items ,
smallBatchConstraint : validated . smallBatchConstraint ,
largeBatchConstraint : validated . largeBatchConstraint ,
smallBatchTotal : validated . smallBatchTotal ,
largeBatchTotal : validated . largeBatchTotal ,
} ) ;
}
function buildStepThreeAnswer ( validated ) {
return JSON . stringify ( {
items : stepThree . items ,
smallBatchManufacturingCost : validated . smallBatchManufacturingCost ,
largeBatchManufacturingCost : validated . largeBatchManufacturingCost ,
smallBatchBomCost : validated . smallBatchBomCost ,
largeBatchBomCost : validated . largeBatchBomCost ,
smallBatchUnitTotalCost : validated . smallBatchUnitTotalCost ,
largeBatchUnitTotalCost : validated . largeBatchUnitTotalCost ,
} ) ;
}
function buildStepFourAnswer ( ) {
return JSON . stringify ( {
smallBatchChart : { ... stepFour . smallBatchChart } ,
largeBatchChart : { ... stepFour . largeBatchChart } ,
optimizationPlan : stepFourPayload ( ) . optimizationPlan ,
} ) ;
}
async function submitTask ( ) {
if ( isStepSaving . value ) return false ;
submittingTraining . value = true ;
try {
/ / 顶 部 “ 提 交 任 务 ” 必 须 使 用 本 页 四 步 真 实 数 据 , 不 能 走 通 用 D O M 快 照 提 交 。
const stepOneResult = ( await validateHardwareCostStepOne ( stepOnePayload ( ) ) ) ? . data || { } ;
if ( stepOneResult . valid === false ) throw new Error ( stepOneResult . message || "请完善第 1 步 BOM 物料选型" ) ;
const normalizedRows = Array . isArray ( stepOneResult . items ) && stepOneResult . items . length
? normalizeRows ( stepOneResult . items )
: normalizeRows ( stepOnePayload ( ) . items ) ;
rows . value = normalizedRows ;
const stepOneItems = stepOnePayload ( normalizedRows ) . items ;
savedStepOneItems . value = stepOneItems ;
syncStepTwoRows ( stepOneItems ) ;
await saveStudentTrainingAnswer ( TASK _KEY , {
currentStep : 1 ,
saveAction : "SAVE" ,
step1Answer : buildStepOneAnswer ( stepOneItems , stepOneResult . totalAmount ? ? totalAmount . value ) ,
} ) ;
const stepTwoResult = ( await validateHardwareCostStepTwo ( stepTwoPayload ( ) ) ) ? . data || { } ;
applyValidationResult ( stepTwoResult ) ;
if ( ! stepTwoResult . valid ) throw new Error ( stepTwoResult . message || "请完善第 2 步批量成本核算" ) ;
await saveStudentTrainingAnswer ( TASK _KEY , {
currentStep : 2 ,
saveAction : "SAVE" ,
step2Answer : buildStepTwoAnswer ( stepTwoResult ) ,
} ) ;
stepTwoSaved . value = true ;
const stepThreeResult = ( await validateHardwareCostStepThree ( stepThreePayload ( ) ) ) ? . data || { } ;
applyStepThreeValidation ( stepThreeResult ) ;
if ( ! stepThreeResult . valid ) throw new Error ( stepThreeResult . message || "请完善第 3 步制造成本核算" ) ;
await saveStudentTrainingAnswer ( TASK _KEY , {
currentStep : 3 ,
saveAction : "SAVE" ,
step3Answer : buildStepThreeAnswer ( stepThreeResult ) ,
} ) ;
stepThreeSaved . value = true ;
const stepFourResult = ( await validateHardwareCostStepFour ( stepFourPayload ( ) ) ) ? . data || { } ;
stepFourValidation . value = stepFourResult ;
if ( ! stepFourResult . valid ) throw new Error ( stepFourResult . message || "请完善第 4 步成本优化决策" ) ;
await saveStudentTrainingAnswer ( TASK _KEY , {
currentStep : 4 ,
saveAction : "SUBMIT" ,
submitted : true ,
step4Answer : buildStepFourAnswer ( ) ,
} ) ;
currentStep . value = 4 ;
notifySuccess ( "实训任务已提交" ) ;
return true ;
} catch ( error ) {
notifyError ( error ? . message || "提交失败,请检查四个步骤的填写内容" ) ;
return false ;
} finally {
submittingTraining . value = false ;
}
}
async function resetTraining ( ) {
if ( isStepSaving . value ) return false ;
try {
await deleteStudentTrainingAnswer ( TASK _KEY ) ;
rows . value = [ createExampleRow ( ) ] ;
savedStepOneItems . value = [ ] ;
stepTwo . smallBatchConstraint = "" ;
stepTwo . largeBatchConstraint = "" ;
stepTwo . items = [ ] ;
stepTwoSaved . value = false ;
stepTwoValidation . value = null ;
stepThree . items = createDefaultManufacturingRows ( ) ;
stepThreeSaved . value = false ;
stepThreeValidation . value = null ;
resetStepFour ( ) ;
currentStep . value = 1 ;
notifySuccess ( "已清空全部实训步骤,可以重新开始" ) ;
return true ;
} catch ( error ) {
notifyError ( error ? . message || "重新实训失败,请稍后重试" ) ;
return false ;
}
}
function exportTrainingOutcome ( ) {
const outcome = {
taskName : taskTitle . value ,
exportedAt : new Date ( ) . toISOString ( ) ,
step1 : { items : stepOnePayload ( ) . items , totalAmount : totalAmount . value } ,
step2 : { ... stepTwoPayload ( ) , smallBatchTotal : smallBatchTotal . value , largeBatchTotal : largeBatchTotal . value } ,
step3 : { ... stepThreePayload ( ) , smallBatchUnitTotalCost : smallBatchUnitTotalCost . value , largeBatchUnitTotalCost : largeBatchUnitTotalCost . value } ,
step4 : JSON . parse ( buildStepFourAnswer ( ) ) ,
} ;
const url = URL . createObjectURL ( new Blob ( [ JSON . stringify ( outcome , null , 2 ) ] , { type : "application/json;charset=utf-8" } ) ) ;
const link = document . createElement ( "a" ) ;
link . href = url ;
link . download = "智能硬件成本估算-实训成果.json" ;
document . body . appendChild ( link ) ;
link . click ( ) ;
document . body . removeChild ( link ) ;
URL . revokeObjectURL ( url ) ;
notifySuccess ( "实训成果已导出" ) ;
}
function previousStep ( ) { if ( currentStep . value > 1 ) currentStep . value -= 1 ; }
async function nextStep ( ) { if ( currentStep . value < 4 && await saveCurrentProgress ( ) ) currentStep . value += 1 ; }
function resultClass ( field ) { return stepTwoValidation . value ? . [ field ] === true ? "passed" : stepTwoValidation . value ? . [ field ] === false ? "failed" : "" ; }