|
|
|
@ -2,11 +2,11 @@
|
|
|
|
<div class="channel student-training-shell">
|
|
|
|
<div class="channel student-training-shell">
|
|
|
|
<main class="training-core">
|
|
|
|
<main class="training-core">
|
|
|
|
<section class="task-header"><div class="task-badge"><el-icon><TrendCharts /></el-icon> 核心实训 · 供应渠道管理</div><h1 class="task-title">{{ taskTitle }}</h1></section>
|
|
|
|
<section class="task-header"><div class="task-badge"><el-icon><TrendCharts /></el-icon> 核心实训 · 供应渠道管理</div><h1 class="task-title">{{ taskTitle }}</h1></section>
|
|
|
|
<TrainingTaskBrief :background="taskBackground" :goals="taskGoals" :requirements="taskRequirement" :show-material="false" />
|
|
|
|
<TrainingTaskBrief :background="taskBackground" :goals="taskGoals" :requirements="taskRequirement" :show-material="false" :show-toolbar="false" />
|
|
|
|
<section class="task-card">
|
|
|
|
<section class="task-card">
|
|
|
|
<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="exportOutcome"><el-icon><Download /></el-icon>导出实训成果</button></div></div>
|
|
|
|
<div class="workbench-head"><div><p class="section-eyebrow">PURCHASE DEMAND FORECAST</p><h2>{{ activeStepName }}</h2></div><TrainingTaskToolbar class="purchase-forecast-toolbar" :task-key="TASK_KEY" :on-export="exportOutcome" /></div>
|
|
|
|
<nav v-if="hasConfiguredSteps" class="step-indicator" aria-label="采购需求预测步骤"><template v-for="(step,index) in configuredSteps" :key="`${step}-${index}`"><button type="button" :class="['step-tab',{active:activeStep===index+1,complete:activeStep>index+1}]" :aria-current="activeStep===index+1?'step':undefined" :aria-label="`第${index+1}步:${step}`" @click="setCurrentStep(index+1)"><span class="step-marker">{{ String(index+1).padStart(2,'0') }}</span><span class="step-copy"><strong class="step-name" :title="step">{{ step }}</strong><span class="step-action">{{ activeStep===index+1?'当前步骤':'点击切换' }} <span aria-hidden="true">→</span></span></span></button><span v-if="index<configuredSteps.length-1" class="step-connector" aria-hidden="true"></span></template></nav>
|
|
|
|
<nav v-if="hasConfiguredSteps" class="step-indicator" aria-label="采购需求预测步骤"><template v-for="(step,index) in configuredSteps" :key="`${step}-${index}`"><button type="button" :class="['step-tab',{active:activeStep===index+1,complete:activeStep>index+1}]" :aria-current="activeStep===index+1?'step':undefined" :aria-label="`第${index+1}步:${step}`" @click="setCurrentStep(index+1)"><span class="step-marker">{{ String(index+1).padStart(2,'0') }}</span><span class="step-copy"><strong class="step-name" :title="step">{{ step }}</strong><span class="step-action">{{ activeStep===index+1?'当前步骤':'点击切换' }} <span aria-hidden="true">→</span></span></span></button><span v-if="index<configuredSteps.length-1" class="step-connector" aria-hidden="true"></span></template></nav>
|
|
|
|
<section v-show="isSectionVisible(1)" class="step-card"><div class="step-title-row"><div><p class="section-eyebrow">Step {{ String(activeStep).padStart(2,'0') }}</p><h3>{{ activeStepName }}</h3></div></div>
|
|
|
|
<section v-show="isSectionVisible(1)" class="step-card">
|
|
|
|
|
|
|
|
|
|
|
|
<p class="linear-forecast-intro">线性预测的核心是拟合趋势线(y=a+bt),是基于历史数据预测未来销量的有效方法。根据案例资料及数据,预测案例店铺产品未来 3 个月市场需求数据。</p>
|
|
|
|
<p class="linear-forecast-intro">线性预测的核心是拟合趋势线(y=a+bt),是基于历史数据预测未来销量的有效方法。根据案例资料及数据,预测案例店铺产品未来 3 个月市场需求数据。</p>
|
|
|
|
<div class="linear-forecast-panel">
|
|
|
|
<div class="linear-forecast-panel">
|
|
|
|
@ -29,7 +29,7 @@
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<el-button type="primary" class="practice-guide-button" @click="downloadPracticeGuide">下载实训操作说明</el-button>
|
|
|
|
<el-button type="primary" class="practice-guide-button" @click="downloadPracticeGuide">下载实训操作说明</el-button>
|
|
|
|
</section>
|
|
|
|
</section>
|
|
|
|
</section><section v-show="isSectionVisible(2)" class="step-card"><div class="step-title-row"><div><p class="section-eyebrow">Step {{ String(activeStep).padStart(2,'0') }}</p><h3>{{ activeStepName }}</h3></div></div>
|
|
|
|
</section><section v-show="isSectionVisible(2)" class="step-card">
|
|
|
|
<p class="forecast-visual-intro">计算未来 3 个月需求量后,使用散点图 + 趋势线绘制需求量预测趋势图,呈现已知的 12 个月销量以及预测数据的整体走势。</p>
|
|
|
|
<p class="forecast-visual-intro">计算未来 3 个月需求量后,使用散点图 + 趋势线绘制需求量预测趋势图,呈现已知的 12 个月销量以及预测数据的整体走势。</p>
|
|
|
|
<section class="forecast-visual-upload-card">
|
|
|
|
<section class="forecast-visual-upload-card">
|
|
|
|
<label v-if="!demandForecastChart.url" class="forecast-visual-dropzone" :class="{ 'is-uploading': uploadingDemandForecastChart }">
|
|
|
|
<label v-if="!demandForecastChart.url" class="forecast-visual-dropzone" :class="{ 'is-uploading': uploadingDemandForecastChart }">
|
|
|
|
@ -57,7 +57,7 @@
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<el-button type="primary" class="practice-guide-button" @click="downloadPracticeGuide">下载实训操作说明</el-button>
|
|
|
|
<el-button type="primary" class="practice-guide-button" @click="downloadPracticeGuide">下载实训操作说明</el-button>
|
|
|
|
</section>
|
|
|
|
</section>
|
|
|
|
</section><section v-show="isSectionVisible(3)" class="step-card"><div class="step-title-row"><div><p class="section-eyebrow">Step {{ String(activeStep).padStart(2,'0') }}</p><h3>{{ activeStepName }}</h3></div></div>
|
|
|
|
</section><section v-show="isSectionVisible(3)" class="step-card">
|
|
|
|
<p class="procurement-plan-intro">基于步骤一预测的未来3个月销量,结合安全库存和期初库存,计算各月采购量,填报采购预测表。</p>
|
|
|
|
<p class="procurement-plan-intro">基于步骤一预测的未来3个月销量,结合安全库存和期初库存,计算各月采购量,填报采购预测表。</p>
|
|
|
|
<section class="procurement-plan-rules">
|
|
|
|
<section class="procurement-plan-rules">
|
|
|
|
<h4>采购规则</h4>
|
|
|
|
<h4>采购规则</h4>
|
|
|
|
@ -94,7 +94,7 @@
|
|
|
|
</el-table>
|
|
|
|
</el-table>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<p class="procurement-plan-hint">预测销量由第 1 步同步带入。提交时将按题设规则校验安全库存、期初库存和采购量。</p>
|
|
|
|
<p class="procurement-plan-hint">预测销量由第 1 步同步带入。提交时将按题设规则校验安全库存、期初库存和采购量。</p>
|
|
|
|
</section><section v-show="isSectionVisible(4)" class="step-card"><div class="step-title-row"><div><p class="section-eyebrow">Step {{ String(activeStep).padStart(2,'0') }}</p><h3>{{ activeStepName }}</h3></div></div>
|
|
|
|
</section><section v-show="isSectionVisible(4)" class="step-card">
|
|
|
|
<p>未来需求预测,使用线性回归模型预测2025年采购量:</p>
|
|
|
|
<p>未来需求预测,使用线性回归模型预测2025年采购量:</p>
|
|
|
|
<el-table
|
|
|
|
<el-table
|
|
|
|
:data="forecastData"
|
|
|
|
:data="forecastData"
|
|
|
|
@ -138,12 +138,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import TrainingAiSidebar from "@/views/components/TrainingAiSidebar.vue";
|
|
|
|
import TrainingAiSidebar from "@/views/components/TrainingAiSidebar.vue";
|
|
|
|
import TrainingMaterialButton from "@/views/components/TrainingMaterialButton.vue";
|
|
|
|
import TrainingTaskToolbar from "@/views/components/TrainingTaskToolbar.vue";
|
|
|
|
import TrainingTaskBrief from "@/views/components/TrainingTaskBrief.vue";
|
|
|
|
import TrainingTaskBrief from "@/views/components/TrainingTaskBrief.vue";
|
|
|
|
import * as analysisAPI from "@/api/market-demand-mining.js";
|
|
|
|
import * as analysisAPI from "@/api/market-demand-mining.js";
|
|
|
|
import * as echarts from "echarts";
|
|
|
|
import * as echarts from "echarts";
|
|
|
|
import useUserStore from "@/store/modules/user";
|
|
|
|
import useUserStore from "@/store/modules/user";
|
|
|
|
import { ArrowLeft, ArrowRight, CircleCheck, Download, TrendCharts } from "@element-plus/icons-vue";
|
|
|
|
import { ArrowLeft, ArrowRight, CircleCheck, TrendCharts } from "@element-plus/icons-vue";
|
|
|
|
import { getTrainingTaskByKey } from "@/api/trainingTask";
|
|
|
|
import { getTrainingTaskByKey } from "@/api/trainingTask";
|
|
|
|
import { getStudentTrainingAnswer, saveStudentTrainingAnswer, uploadStudentTrainingFile, validatePurchaseDemandForecastStepOne } from "@/api/studentTrainingAnswer";
|
|
|
|
import { getStudentTrainingAnswer, saveStudentTrainingAnswer, uploadStudentTrainingFile, validatePurchaseDemandForecastStepOne } from "@/api/studentTrainingAnswer";
|
|
|
|
import { parseTrainingArray } from "@/views/training/taskKeyMap";
|
|
|
|
import { parseTrainingArray } from "@/views/training/taskKeyMap";
|
|
|
|
@ -959,6 +959,27 @@ onMounted(async () => {
|
|
|
|
border-bottom: 0;
|
|
|
|
border-bottom: 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.workbench-head h2 {
|
|
|
|
|
|
|
|
margin: 4px 0 0;
|
|
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
|
|
font-size: 24px;
|
|
|
|
|
|
|
|
font-weight: 800;
|
|
|
|
|
|
|
|
line-height: 1.3;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.purchase-forecast-toolbar {
|
|
|
|
|
|
|
|
align-self: center;
|
|
|
|
|
|
|
|
justify-content: flex-end;
|
|
|
|
|
|
|
|
max-width: 100%;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@media (max-width:900px) {
|
|
|
|
|
|
|
|
.purchase-forecast-toolbar {
|
|
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
|
|
justify-content: flex-start;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.linear-forecast-intro {
|
|
|
|
.linear-forecast-intro {
|
|
|
|
margin: 0 0 22px;
|
|
|
|
margin: 0 0 22px;
|
|
|
|
color: #d7ecff;
|
|
|
|
color: #d7ecff;
|
|
|
|
|