@ -14,6 +14,18 @@
/ >
< section class = "price-workbench" >
< div class = "workbench-head workbench-head--actions-only" >
< div class = "workbench-actions" >
< TrainingMaterialButton
: material - name = "taskConfig?.materialName"
: material - url = "taskConfig?.materialUrl"
/ >
< button type = "button" class = "outline-action" @click ="exportPriceOutcome" >
< el -icon > < Download / > < / e l - i c o n > 导 出 实 训 成 果
< / button >
< / div >
< / div >
< nav class = "step-indicator" aria -label = " 新 产 品 定 价 步 骤 " >
< template v-for ="step in pricingSteps" :key="step.no" >
< button
@ -38,15 +50,12 @@
< / template >
< / nav >
< header class = "workbench-head" >
< section class = "step-card" >
< header class = "step-title-row" >
< div >
< p class = "section-eyebrow" > STEP { { String ( currentStep ) . padStart ( 2 , "0" ) } } < / p >
< h2 > { { currentStepTitle } } < / h 2 >
< h3 > { { currentStepTitle } } < / h 3 >
< / div >
< TrainingMaterialButton
: material - name = "taskConfig?.materialName"
: material - url = "taskConfig?.materialUrl"
/ >
< / header >
< template v-if ="currentStep === 1" >
@ -439,6 +448,7 @@
< / div >
< / footer >
< / template >
< / section >
< / section >
< / main >
@ -454,6 +464,7 @@
< / template >
< script setup >
import { Download } from "@element-plus/icons-vue" ;
import { computed , getCurrentInstance , onMounted , reactive , ref } from "vue" ;
import TrainingAiSidebar from "@/views/components/TrainingAiSidebar.vue" ;
import TrainingMaterialButton from "@/views/components/TrainingMaterialButton.vue" ;
@ -934,6 +945,25 @@ function exportPsmTable() {
URL . revokeObjectURL ( url ) ;
}
function exportPriceOutcome ( ) {
const payload = {
task : taskTitle . value ,
exportedAt : new Date ( ) . toISOString ( ) ,
stepOne : buildStepOneProgress ( ) ,
stepTwo : buildStepTwoProgress ( ) ,
stepThree : buildStepThreeProgress ( ) ,
stepFour : buildStepFourProgress ( ) ,
stepFive : buildStepFiveProgress ( ) ,
} ;
const blob = new Blob ( [ JSON . stringify ( payload , null , 2 ) ] , { type : "application/json;charset=utf-8" } ) ;
const url = URL . createObjectURL ( blob ) ;
const link = document . createElement ( "a" ) ;
link . href = url ;
link . download = ` ${ taskTitle . value } -实训成果.json ` ;
link . click ( ) ;
URL . revokeObjectURL ( url ) ;
}
function downloadPsmGuide ( ) {
const content = [
"PSM价格敏感度测试实训操作说明" ,
@ -1560,20 +1590,59 @@ onMounted(async () => {
}
. price - workbench {
overflow : hidden ;
margin - top : 18 px ;
border : 1 px solid var ( -- line - strong ) ;
border - radius : 18 px ;
background : var ( -- surface ) ;
box - shadow : 0 20 px 45 px rgba ( 0 , 0 , 0 , 0.2 ) ;
margin - top : 28 px ;
padding : 18 px 22 px 22 px ;
border : 1 px solid rgba ( 82 , 174 , 226 , 0.42 ) ;
border - radius : 26 px ;
background : rgba ( 0 , 25 , 42 , 0.64 ) ;
}
. workbench - head -- actions - only {
display : flex ;
justify - content : flex - end ;
margin - bottom : 18 px ;
}
. workbench - actions {
display : flex ;
flex - wrap : wrap ;
justify - content : flex - end ;
gap : 10 px ;
margin - left : auto ;
}
. outline - action {
display : inline - flex ;
align - items : center ;
justify - content : center ;
gap : 8 px ;
min - height : 40 px ;
padding : 0 18 px ;
color : # e9fbff ;
cursor : pointer ;
border : 1 px solid rgba ( 99 , 217 , 255 , 0.55 ) ;
border - radius : 999 px ;
background : rgba ( 17 , 126 , 178 , 0.36 ) ;
font : inherit ;
font - size : 14 px ;
font - weight : 800 ;
& : hover ,
& : focus - visible {
border - color : rgba ( 116 , 235 , 255 , 0.85 ) ;
outline : none ;
background : rgba ( 18 , 146 , 204 , 0.5 ) ;
transform : translateY ( - 1 px ) ;
}
}
. step - indicator {
display : flex ;
align - items : center ;
width : calc ( 100 % - 48 px ) ;
margin : 18 px 24 px 0 ;
padding : 12 px 14 px ;
gap : 0 ;
width : calc ( 100 % + 24 px ) ;
margin : 0 - 12 px 32 px ;
padding : 18 px 22 px ;
overflow - x : auto ;
border : 1 px solid rgba ( 0 , 180 , 255 , 0.3 ) ;
border - radius : 14 px ;
@ -1583,11 +1652,11 @@ onMounted(async () => {
. step - tab {
display : flex ;
flex : 1 0 1 36 px ;
flex : 1 0 1 45 px ;
align - items : center ;
min - width : 0 ;
min - height : 52 px ;
padding : 7px 9 px ;
min - height : 66 px ;
padding : 8px 10 px ;
color : # 9 ab3d0 ;
cursor : pointer ;
border : 1 px solid rgba ( 68 , 141 , 177 , 0.35 ) ;
@ -1619,11 +1688,11 @@ onMounted(async () => {
. step - marker {
display : inline - flex ;
flex : 0 0 3 0 px ;
flex : 0 0 3 8 px ;
align - items : center ;
justify - content : center ;
width : 3 0 px ;
height : 3 0 px ;
width : 3 8 px ;
height : 3 8 px ;
color : # a6c4d9 ;
border : 1 px solid rgba ( 101 , 176 , 208 , 0.6 ) ;
border - radius : 50 % ;
@ -1637,14 +1706,14 @@ onMounted(async () => {
. step - copy {
display : grid ;
min - width : 0 ;
gap : 3 px ;
margin - left : 8 px ;
gap : 4 px ;
margin - left : 10 px ;
strong {
display : - webkit - box ;
overflow : hidden ;
color : inherit ;
font - size : 1 3 px ;
font - size : 1 4 px ;
line - height : 1.25 ;
- webkit - box - orient : vertical ;
- webkit - line - clamp : 2 ;
@ -1749,22 +1818,31 @@ onMounted(async () => {
gap : 10 px ;
}
. workbench - head {
. step - card {
padding : 18 px 22 px 22 px ;
border : 1 px solid rgba ( 82 , 174 , 226 , 0.42 ) ;
border - radius : 26 px ;
background : rgba ( 0 , 25 , 42 , 0.64 ) ;
}
. step - title - row {
display : flex ;
align - items : center ;
justify - content : space - between ;
gap : 16 px ;
padding : 24 px 26 px 18 px ;
border - bottom : 1 px solid var ( -- line ) ;
margin - bottom : 14 px ;
h 2 {
h 3 {
margin : 4 px 0 0 ;
font - size : 24 px ;
color : # fff ;
font - size : 22 px ;
font - weight : 900 ;
line - height : 1.3 ;
}
}
. intro - copy {
margin : 20 px 26px ;
margin : 20 px 0 ;
color : var ( -- muted ) ;
line - height : 1.75 ;
}
@ -1774,7 +1852,7 @@ onMounted(async () => {
align - items : center ;
justify - content : space - between ;
gap : 20 px ;
margin : 0 26px 22 px ;
margin : 0 0 22 px ;
padding : 18 px 20 px ;
border : 1 px solid var ( -- line ) ;
border - radius : 14 px ;
@ -1813,7 +1891,7 @@ onMounted(async () => {
. pricing - table - wrap {
overflow - x : auto ;
margin : 0 26 px ;
margin : 0 ;
border : 1 px solid var ( -- line ) ;
border - radius : 14 px ;
}
@ -1855,7 +1933,7 @@ onMounted(async () => {
display : flex ;
align - items : center ;
gap : 10 px ;
margin : 0 26px 12 px ;
margin : 0 0 12 px ;
span {
display : grid ;
@ -1930,7 +2008,7 @@ onMounted(async () => {
}
. validation - message {
margin : 14 px 26px 0 ;
margin : 14 px 0 0 ;
color : # ff9e9e ;
line - height : 1.5 ;
}
@ -2073,7 +2151,7 @@ onMounted(async () => {
align - items : center ;
justify - content : space - between ;
gap : 20 px ;
margin : 0 26 px ;
margin : 0 ;
padding : 16 px 18 px ;
border : 1 px solid var ( -- line ) ;
border - radius : 12 px ;
@ -2117,7 +2195,7 @@ onMounted(async () => {
}
. final - analysis - section {
margin : 0 26 px ;
margin : 0 ;
h3 {
margin : 0 0 12 px ;
@ -2160,7 +2238,7 @@ onMounted(async () => {
. psm - section {
display : grid ;
gap : 20 px ;
padding : 0 26 px 26 px ;
padding : 0 ;
}
. psm - toolbar ,