|
|
|
|
|
<!--
|
|
|
|
|
|
* @Author: qinzhenpen qzp1807@126.com
|
|
|
|
|
|
* @Date: 2025-05-03 17:28:31
|
|
|
|
|
|
* @LastEditors: qinzhenpen qzp1807@126.com
|
|
|
|
|
|
* @LastEditTime: 2025-05-23 14:33:52
|
|
|
|
|
|
* @FilePath: \e-commerce-internet\src\views\channel\supplier.vue
|
|
|
|
|
|
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
|
|
|
|
|
-->
|
|
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
|
<div class="supplier student-training-shell">
|
|
|
|
|
|
<main class="training-core">
|
|
|
|
|
|
<section class="task-header">
|
|
|
|
|
|
<div class="task-badge"><el-icon><Connection /></el-icon> 核心实训 · 供应渠道管理</div>
|
|
|
|
|
|
<h1 class="task-title">{{ taskTitle }}</h1>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
<TrainingTaskBrief :background="taskBackground" :goals="taskGoals" :requirements="taskRequirement" :show-material="false" />
|
|
|
|
|
|
<section class="task-card">
|
|
|
|
|
|
<div class="workbench-head">
|
|
|
|
|
|
<div><p class="section-eyebrow">Training Task</p><h3>任务实训</h3><p class="workbench-description">{{ taskRequirement }}</p></div>
|
|
|
|
|
|
<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>
|
|
|
|
|
|
<nav v-if="hasConfiguredSteps" class="step-indicator" aria-label="供应商评估步骤">
|
|
|
|
|
|
<button v-for="(step, index) in configuredSteps" :key="`${step}-${index}`" type="button" class="step-tab" :class="{ active: activeStep === index + 1 }" :aria-current="activeStep === index + 1 ? 'step' : undefined" @click="setCurrentStep(index + 1)">{{ step }}</button>
|
|
|
|
|
|
</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>
|
|
|
|
|
|
<div class="caseBacktitle">
|
|
|
|
|
|
<img src="@/assets/images/步骤.png" alt="" />
|
|
|
|
|
|
<span>任务目标</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p>掌握供应商渠道评估的要素,评分办法的设置,学会使用线性权重法计算各供应渠道综合得分,确定供应渠道级别并给出综合评估意见。</p>
|
|
|
|
|
|
<div class="caseBacktitle">
|
|
|
|
|
|
<img src="@/assets/images/步骤.png" alt="" />
|
|
|
|
|
|
<span>任务背景</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p>电商选品中常见的供应渠道包括以下类型:品牌直供:直接与品牌方或总代理合作,确保正品授权和稳定供应。批发市场:如义乌小商品市场、1688平台,适合小批量采购和多样化选品。代工厂/OEM:定制化生产,适合有独特设计需求的产品。跨境电商平台:如亚马逊全球开店、速卖通,提供国际供应链资源。垂直类供应商:专注于特定品类(如母婴、3C)的供应商,具备专业性和深度服务能力</p>
|
|
|
|
|
|
<el-table
|
|
|
|
|
|
:data="channelComparisonData"
|
|
|
|
|
|
border
|
|
|
|
|
|
style="width: 90%"
|
|
|
|
|
|
:header-cell-style="{
|
|
|
|
|
|
color: '#fff',
|
|
|
|
|
|
fontSize: '14px',
|
|
|
|
|
|
backgroundColor: '#6A7DE9!important',
|
|
|
|
|
|
}"
|
|
|
|
|
|
>
|
|
|
|
|
|
<el-table-column prop="item" label="项目" align="center" width="120"></el-table-column>
|
|
|
|
|
|
<el-table-column prop="channelA" label="渠道A:品牌直供" align="center"></el-table-column>
|
|
|
|
|
|
<el-table-column prop="channelB" label="渠道B:批发市场" align="center"></el-table-column>
|
|
|
|
|
|
<el-table-column prop="channelC" label="渠道C:代工厂/OEM" align="center"></el-table-column>
|
|
|
|
|
|
<el-table-column prop="channelD" label="渠道D:跨境电商平台" align="center"></el-table-column>
|
|
|
|
|
|
<el-table-column prop="channelE" label="渠道E:垂直类供应商" align="center"></el-table-column>
|
|
|
|
|
|
</el-table>
|
|
|
|
|
|
<div class="caseBacktitle">
|
|
|
|
|
|
<img src="@/assets/images/步骤.png" alt="" />
|
|
|
|
|
|
<span>任务要求</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="caseBacktitle">
|
|
|
|
|
|
<span>任务1:设置评估维度及权重</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<div style="display: flex; margin-top: 10px">
|
|
|
|
|
|
<el-table
|
|
|
|
|
|
:data="evaluationWeights"
|
|
|
|
|
|
border
|
|
|
|
|
|
style="width: 30%"
|
|
|
|
|
|
:header-cell-style="{
|
|
|
|
|
|
color: '#fff',
|
|
|
|
|
|
fontSize: '14px',
|
|
|
|
|
|
backgroundColor: '#6A7DE9!important',
|
|
|
|
|
|
}"
|
|
|
|
|
|
>
|
|
|
|
|
|
<el-table-column prop="dimension" label="评估维度" align="center" width="120"></el-table-column>
|
|
|
|
|
|
<el-table-column prop="currentWeight" label="权重设置" align="center">
|
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
|
<el-input v-if="row.dimension !== '合计'" v-model="row.currentWeight" placeholder="请输入内容"></el-input>
|
|
|
|
|
|
<span v-else>{{ total }}</span>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column prop="recommendedRange" label="推荐设置" align="center"></el-table-column>
|
|
|
|
|
|
</el-table>
|
|
|
|
|
|
<el-button @click="onClick" class="ml50" type="primary">提交</el-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="caseBacktitle">
|
|
|
|
|
|
<span>任务2:设置评分标准</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<el-table
|
|
|
|
|
|
:data="evaluationCriteria"
|
|
|
|
|
|
border
|
|
|
|
|
|
style="width: 50%; margin-top: 10px"
|
|
|
|
|
|
:header-cell-style="{
|
|
|
|
|
|
color: '#fff',
|
|
|
|
|
|
fontSize: '14px',
|
|
|
|
|
|
backgroundColor: '#6A7DE9!important',
|
|
|
|
|
|
}"
|
|
|
|
|
|
>
|
|
|
|
|
|
<el-table-column prop="category" label="项目" align="center" width="120"></el-table-column>
|
|
|
|
|
|
<el-table-column label="评分标准" align="left">
|
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
|
<div v-for="standard in row.standards" :key="standard.score" class="standard-item">
|
|
|
|
|
|
<span>{{ standard.score }}分:{{ standard.description }}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
</el-table>
|
|
|
|
|
|
</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>
|
|
|
|
|
|
<div class="caseBacktitle">
|
|
|
|
|
|
<span>任务3:供应渠道评分</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div style="display: flex">
|
|
|
|
|
|
<el-table
|
|
|
|
|
|
:data="supplierEvaluationData"
|
|
|
|
|
|
border
|
|
|
|
|
|
style="width: 80%"
|
|
|
|
|
|
:header-cell-style="{
|
|
|
|
|
|
color: '#fff',
|
|
|
|
|
|
fontSize: '14px',
|
|
|
|
|
|
backgroundColor: '#6A7DE9!important',
|
|
|
|
|
|
}"
|
|
|
|
|
|
>
|
|
|
|
|
|
<el-table-column prop="supplierType" label="供应渠道" align="center" width="120"></el-table-column>
|
|
|
|
|
|
<el-table-column prop="quality" label="产品质量(分)" align="center">
|
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
|
<el-input v-model="row.quality" placeholder=""></el-input>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column prop="priceCompetitiveness" label="价格竞争力(分)" align="center">
|
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
|
<el-input v-model="row.priceCompetitiveness" placeholder=""></el-input>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column prop="supplyStability" label="供货稳定性(分)" align="center">
|
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
|
<el-input v-model="row.supplyStability" placeholder=""></el-input>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column prop="reputation" label="合作信誉(分)" align="center">
|
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
|
<el-input v-model="row.reputation" placeholder=""></el-input>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column prop="afterSales" label="售后支持(分)" align="center">
|
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
|
<el-input v-model="row.afterSales" placeholder=""></el-input>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column prop="minOrderQuantity" label="最小订单量(分)" align="center">
|
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
|
<el-input v-model="row.minOrderQuantity" placeholder=""></el-input>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column prop="productDiversity" label="产品多样性(分)" align="center">
|
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
|
<el-input v-model="row.productDiversity" placeholder=""></el-input>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column prop="logistics" label="物流配送(分)" align="center">
|
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
|
<el-input v-model="row.logistics" placeholder=""></el-input>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
</el-table>
|
|
|
|
|
|
<el-button class="ml50" type="primary" @click="onSubmit">提交</el-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</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>
|
|
|
|
|
|
<div class="caseBacktitle">
|
|
|
|
|
|
<span>任务4:使用线性权重法计算各供应渠道综合得分</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<el-table
|
|
|
|
|
|
:data="supplierEvaluationData1"
|
|
|
|
|
|
border
|
|
|
|
|
|
style="width: 100%; margin-top: 10px"
|
|
|
|
|
|
:header-cell-style="{
|
|
|
|
|
|
color: '#fff',
|
|
|
|
|
|
fontSize: '14px',
|
|
|
|
|
|
backgroundColor: '#6A7DE9!important',
|
|
|
|
|
|
}"
|
|
|
|
|
|
>
|
|
|
|
|
|
<el-table-column prop="supplierType" label="供应渠道" align="center" width="120"></el-table-column>
|
|
|
|
|
|
<el-table-column label="产品质量" align="center">
|
|
|
|
|
|
<el-table-column prop="quality" label="得分" align="center"></el-table-column>
|
|
|
|
|
|
<el-table-column label="权重" align="center" prop="weight">
|
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
|
{{ getWeightByDimension("产品质量") }}
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="价格竞争力" align="center">
|
|
|
|
|
|
<el-table-column prop="priceCompetitiveness" label="得分" align="center"></el-table-column>
|
|
|
|
|
|
<el-table-column label="权重" align="center" prop="weight">
|
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
|
{{ getWeightByDimension("价格竞争力") }}
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="供货稳定性" align="center">
|
|
|
|
|
|
<el-table-column prop="supplyStability" label="得分" align="center"></el-table-column>
|
|
|
|
|
|
<el-table-column label="权重" align="center" prop="weight">
|
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
|
{{ getWeightByDimension("供货稳定性") }}
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="合作信誉" align="center">
|
|
|
|
|
|
<el-table-column prop="reputation" label="得分" align="center"></el-table-column>
|
|
|
|
|
|
<el-table-column label="权重" align="center" prop="weight">
|
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
|
{{ getWeightByDimension("合作信誉") }}
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="售后支持" align="center">
|
|
|
|
|
|
<el-table-column prop="afterSales" label="得分" align="center"></el-table-column>
|
|
|
|
|
|
<el-table-column label="权重" align="center" prop="weight">
|
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
|
{{ getWeightByDimension("售后支持") }}
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="最小订单量" align="center">
|
|
|
|
|
|
<el-table-column prop="minOrderQuantity" label="得分" align="center"></el-table-column>
|
|
|
|
|
|
<el-table-column label="权重" align="center" prop="weight">
|
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
|
{{ getWeightByDimension("最小订单量") }}
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="产品多样性" align="center">
|
|
|
|
|
|
<el-table-column prop="productDiversity" label="得分" align="center"></el-table-column>
|
|
|
|
|
|
<el-table-column label="权重" align="center" prop="weight">
|
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
|
{{ getWeightByDimension("产品多样性") }}
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="物流配送" align="center">
|
|
|
|
|
|
<el-table-column prop="logistics" label="得分" align="center"></el-table-column>
|
|
|
|
|
|
<el-table-column label="权重" align="center" prop="weight">
|
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
|
{{ getWeightByDimension("物流配送") }}
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="综合得分" align="center" prop="weight">
|
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
|
<el-input v-model="row.comprehensiveScore" placeholder=""></el-input>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
</el-table>
|
|
|
|
|
|
</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>
|
|
|
|
|
|
<div class="caseBacktitle">
|
|
|
|
|
|
<span>任务5:选择供应渠道级别</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div style="display: flex; gap: 5vw">
|
|
|
|
|
|
<div style="padding-left: 1vw">
|
|
|
|
|
|
<span>
|
|
|
|
|
|
根据综合得分,可以将供应渠道分为以下级别:
|
|
|
|
|
|
<br />
|
|
|
|
|
|
8分以上:优秀供应渠道,优先合作。
|
|
|
|
|
|
<br />
|
|
|
|
|
|
7-8分:良好供应渠道,可考虑合作。
|
|
|
|
|
|
<br />
|
|
|
|
|
|
6-7分:一般供应渠道,需谨慎合作。
|
|
|
|
|
|
<br />
|
|
|
|
|
|
6分以下:较差供应渠道,不建议合作。
|
|
|
|
|
|
<br />
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<span>根据计算结果:</span>
|
|
|
|
|
|
<div class="online">
|
|
|
|
|
|
<span>品牌直供:{{ supplierEvaluationData1[0].comprehensiveScore || 0 }}分,</span>
|
|
|
|
|
|
<el-select placeholder="请选择" v-model="form.option1" @change="onChange">
|
|
|
|
|
|
<el-option v-for="item in option" :key="item" :value="item" :label="item"></el-option>
|
|
|
|
|
|
</el-select>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="online mt10">
|
|
|
|
|
|
<span>垂直类供应商:{{ supplierEvaluationData1[1].comprehensiveScore || 0 }}分,</span>
|
|
|
|
|
|
<el-select placeholder="请选择" v-model="form.option2" @change="onChange">
|
|
|
|
|
|
<el-option v-for="item in option" :key="item" :value="item" :label="item"></el-option>
|
|
|
|
|
|
</el-select>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="online mt10">
|
|
|
|
|
|
<span>代工厂/OEM:{{ supplierEvaluationData1[2].comprehensiveScore || 0 }}分,</span>
|
|
|
|
|
|
<el-select placeholder="请选择" v-model="form.option3" @change="onChange">
|
|
|
|
|
|
<el-option v-for="item in option" :key="item" :value="item" :label="item"></el-option>
|
|
|
|
|
|
</el-select>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="online mt10">
|
|
|
|
|
|
<span>批发市场:{{ supplierEvaluationData1[3].comprehensiveScore || 0 }}分,</span>
|
|
|
|
|
|
<el-select placeholder="请选择" v-model="form.option4" @change="onChange">
|
|
|
|
|
|
<el-option v-for="item in option" :key="item" :value="item" :label="item"></el-option>
|
|
|
|
|
|
</el-select>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="online mt10">
|
|
|
|
|
|
<span>跨境电商平台:{{ supplierEvaluationData1[4].comprehensiveScore || 0 }}分,</span>
|
|
|
|
|
|
<el-select placeholder="请选择" v-model="form.option5" @change="onChange">
|
|
|
|
|
|
<el-option v-for="item in option" :key="item" :value="item" :label="item"></el-option>
|
|
|
|
|
|
</el-select>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="caseBacktitle">
|
|
|
|
|
|
<span>任务6:给出综合评估意见</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p style="padding-left: 1vw">
|
|
|
|
|
|
品牌直供:综合得分最高,产品质量和合作信誉表现优秀,建议优先合作。
|
|
|
|
|
|
<br />
|
|
|
|
|
|
垂直类供应商:综合得分较高,售后支持表现突出,可作为主要合作渠道之一。
|
|
|
|
|
|
<br />
|
|
|
|
|
|
代工厂/OEM:综合得分中等,产品多样性表现较好,但价格竞争力一般,可作为补充渠道。
|
|
|
|
|
|
<br />
|
|
|
|
|
|
批发市场:综合得分中等,价格竞争力较强,但产品质量和供货稳定性一般,可作为备选渠道。
|
|
|
|
|
|
<br />
|
|
|
|
|
|
跨境电商平台:综合得分较低,物流配送和供货稳定性表现一般,建议谨慎合作或作为短期应急渠道。
|
|
|
|
|
|
<br />
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
<div v-if="hasConfiguredSteps" class="step-navigation">
|
|
|
|
|
|
<button type="button" class="btn-nav" :disabled="activeStep === 1" @click="goPreviousStep"><el-icon><ArrowLeft /></el-icon>上一步</button>
|
|
|
|
|
|
<button type="button" class="btn-nav btn-save" :disabled="saving" @click="saveCurrentProgress"><el-icon><CircleCheck /></el-icon>保存当前进度</button>
|
|
|
|
|
|
<button type="button" class="btn-nav btn-primary" :disabled="activeStep === configuredSteps.length" @click="goNextStep">下一步<el-icon><ArrowRight /></el-icon></button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="taskfooter"><el-button @click="submitTask" :loading="saving">提交任务</el-button><el-button @click="resetTask" :loading="saving">清空填写</el-button></div>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
</main>
|
|
|
|
|
|
<TrainingAiSidebar study-tip="当前任务建议:先设定评估权重,再完成渠道评分、综合得分和合作级别判断。" :progress-items="progressItems" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import TrainingAiSidebar from "@/views/components/TrainingAiSidebar.vue";
|
|
|
|
|
|
import TrainingMaterialButton from "@/views/components/TrainingMaterialButton.vue";
|
|
|
|
|
|
import TrainingTaskBrief from "@/views/components/TrainingTaskBrief.vue";
|
|
|
|
|
|
import useUserStore from "@/store/modules/user";
|
|
|
|
|
|
const userStore = useUserStore();
|
|
|
|
|
|
import * as analysisAPI from "@/api/market-demand-mining.js";
|
|
|
|
|
|
import { ArrowLeft, ArrowRight, CircleCheck, Connection, Download } from "@element-plus/icons-vue";
|
|
|
|
|
|
import { getTrainingTaskByKey } from "@/api/trainingTask";
|
|
|
|
|
|
import { getStudentTrainingAnswer, saveStudentTrainingAnswer } from "@/api/studentTrainingAnswer";
|
|
|
|
|
|
import { parseTrainingArray } from "@/views/training/taskKeyMap";
|
|
|
|
|
|
const { proxy } = getCurrentInstance();
|
|
|
|
|
|
let currentStep = ref(0);
|
|
|
|
|
|
const TASK_KEY = "supplier-evaluation";
|
|
|
|
|
|
const taskConfig = ref(null);
|
|
|
|
|
|
const activeStep = ref(1);
|
|
|
|
|
|
const saving = ref(false);
|
|
|
|
|
|
const taskTitle = computed(() => taskConfig.value?.taskName || "供应商评估与选择");
|
|
|
|
|
|
const taskBackground = computed(() => taskConfig.value?.background || "电商选品中存在品牌直供、批发市场、代工厂、跨境电商平台、垂直类供应商等多种供应渠道,需要综合评估各渠道能力。");
|
|
|
|
|
|
const taskRequirement = computed(() => taskConfig.value?.requirements || "掌握供应商渠道评估要素和评分办法,使用线性权重法计算各供应渠道综合得分,确定供应渠道级别并给出综合评估意见。");
|
|
|
|
|
|
const taskGoals = computed(() => parseTrainingArray(taskConfig.value?.objectives, ["掌握供应商渠道评估要素和评分办法", "能够使用线性权重法计算供应渠道综合得分", "形成供应渠道级别与综合评估意见"]));
|
|
|
|
|
|
const configuredSteps = computed(() => parseTrainingArray(taskConfig.value?.steps));
|
|
|
|
|
|
const hasConfiguredSteps = computed(() => configuredSteps.value.length > 0);
|
|
|
|
|
|
const activeStepName = computed(() => configuredSteps.value[activeStep.value - 1] || "供应商评估与选择");
|
|
|
|
|
|
const progressItems = computed(() => configuredSteps.value.map((text, index) => ({ text, status: index + 1 < activeStep.value ? "done" : index + 1 === activeStep.value ? "doing" : "" })));
|
|
|
|
|
|
const form = ref({
|
|
|
|
|
|
option1: "",
|
|
|
|
|
|
option2: "",
|
|
|
|
|
|
option3: "",
|
|
|
|
|
|
option4: "",
|
|
|
|
|
|
option5: "",
|
|
|
|
|
|
});
|
|
|
|
|
|
const option = ["优秀供应渠道", "良好供应渠道", "一般供应渠道", "较差供应渠道"];
|
|
|
|
|
|
const channelComparisonData = [
|
|
|
|
|
|
{
|
|
|
|
|
|
item: "产品质量",
|
|
|
|
|
|
channelA: "通常提供高质量产品,符合品牌标准",
|
|
|
|
|
|
channelB: "产品质量参差不齐,需仔细甄别",
|
|
|
|
|
|
channelC: "产品质量可靠,符合定制要求",
|
|
|
|
|
|
channelD: "产品质量一般,需仔细甄别",
|
|
|
|
|
|
channelE: "产品质量较高,专业领域优势明显",
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
item: "价格竞争力",
|
|
|
|
|
|
channelA: "价格较高,但具有一定市场竞争力",
|
|
|
|
|
|
channelB: "价格较低,具有明显竞争力",
|
|
|
|
|
|
channelC: "定制产品价格较高",
|
|
|
|
|
|
channelD: "价格具有一定的竞争力,但受关税影响",
|
|
|
|
|
|
channelE: "价格具有竞争力,专业采购渠道",
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
item: "供应链稳定性",
|
|
|
|
|
|
channelA: "通常有稳定的供应链",
|
|
|
|
|
|
channelB: "供应链稳定性一般,受市场因素影响较大",
|
|
|
|
|
|
channelC: "供应链稳定性较好,但受生产计划影响",
|
|
|
|
|
|
channelD: "国际运输存在不确定性",
|
|
|
|
|
|
channelE: "供应链稳定性高,专业供应链管理",
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
item: "合作信誉",
|
|
|
|
|
|
channelA: "品牌方信誉良好,长期合作关系",
|
|
|
|
|
|
channelB: "信誉一般,需谨慎选择供应商",
|
|
|
|
|
|
channelC: "信誉良好,专业生产定制产品",
|
|
|
|
|
|
channelD: "平台信誉较好,但供应商个体差异大",
|
|
|
|
|
|
channelE: "信誉良好,专业服务",
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
item: "售后支持",
|
|
|
|
|
|
channelA: "提供全面的售后服务",
|
|
|
|
|
|
channelB: "售后服务一般,部分供应商提供有限支持",
|
|
|
|
|
|
channelC: "提供一定的售后服务,但可能有限",
|
|
|
|
|
|
channelD: "提供一定的售后服务,但受国际贸易影响",
|
|
|
|
|
|
channelE: "提供全面的售后服务",
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
item: "最小订单量",
|
|
|
|
|
|
channelA: "通常要求一定的最小订单量",
|
|
|
|
|
|
channelB: "支持小批量采购",
|
|
|
|
|
|
channelC: "通常要求较高的最小订单量",
|
|
|
|
|
|
channelD: "支持一件代发",
|
|
|
|
|
|
channelE: "最小订单量适中",
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
item: "产品多样性",
|
|
|
|
|
|
channelA: "产品种类相对有限,专注于品牌产品",
|
|
|
|
|
|
channelB: "产品种类丰富,选择多样",
|
|
|
|
|
|
channelC: "可根据需求定制多样化产品",
|
|
|
|
|
|
channelD: "产品种类丰富",
|
|
|
|
|
|
channelE: "专注于特定品类,种类较为丰富",
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
item: "物流配送",
|
|
|
|
|
|
channelA: "物流服务较为高效",
|
|
|
|
|
|
channelB: "物流服务较为便捷,但运费可能较高",
|
|
|
|
|
|
channelC: "物流服务一般,需自行安排",
|
|
|
|
|
|
channelD: "国际物流时间长,运费高",
|
|
|
|
|
|
channelE: "物流服务较为高效",
|
|
|
|
|
|
},
|
|
|
|
|
|
];
|
|
|
|
|
|
const evaluationWeights = ref([
|
|
|
|
|
|
{
|
|
|
|
|
|
dimension: "产品质量",
|
|
|
|
|
|
currentWeight: null,
|
|
|
|
|
|
recommendedRange: "0.15~0.25",
|
|
|
|
|
|
min: 0.15,
|
|
|
|
|
|
max: 0.25,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
dimension: "价格竞争力",
|
|
|
|
|
|
currentWeight: null,
|
|
|
|
|
|
recommendedRange: "0.10~0.20",
|
|
|
|
|
|
min: 0.1,
|
|
|
|
|
|
max: 0.2,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
dimension: "供货稳定性",
|
|
|
|
|
|
currentWeight: null,
|
|
|
|
|
|
recommendedRange: "0.10~0.20",
|
|
|
|
|
|
min: 0.1,
|
|
|
|
|
|
max: 0.2,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
dimension: "合作信誉",
|
|
|
|
|
|
currentWeight: null,
|
|
|
|
|
|
recommendedRange: "0.10~0.20",
|
|
|
|
|
|
min: 0.1,
|
|
|
|
|
|
max: 0.2,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
dimension: "售后支持",
|
|
|
|
|
|
currentWeight: null,
|
|
|
|
|
|
recommendedRange: "0.05~0.15",
|
|
|
|
|
|
min: 0.05,
|
|
|
|
|
|
max: 0.15,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
dimension: "最小订单量",
|
|
|
|
|
|
currentWeight: null,
|
|
|
|
|
|
recommendedRange: "0.05~0.15",
|
|
|
|
|
|
min: 0.05,
|
|
|
|
|
|
max: 0.15,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
dimension: "产品多样性",
|
|
|
|
|
|
currentWeight: null,
|
|
|
|
|
|
recommendedRange: "0.05~0.15",
|
|
|
|
|
|
min: 0.05,
|
|
|
|
|
|
max: 0.15,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
dimension: "物流配送",
|
|
|
|
|
|
currentWeight: null,
|
|
|
|
|
|
recommendedRange: "0.05~0.15",
|
|
|
|
|
|
min: 0.05,
|
|
|
|
|
|
max: 0.15,
|
|
|
|
|
|
},
|
|
|
|
|
|
// 合计行(特殊标记)
|
|
|
|
|
|
{
|
|
|
|
|
|
dimension: "合计",
|
|
|
|
|
|
currentWeight: 1.0,
|
|
|
|
|
|
recommendedRange: "(权重合计为1)",
|
|
|
|
|
|
isTotal: true,
|
|
|
|
|
|
},
|
|
|
|
|
|
]);
|
|
|
|
|
|
const evaluationCriteria = [
|
|
|
|
|
|
{
|
|
|
|
|
|
category: "产品质量",
|
|
|
|
|
|
standards: [
|
|
|
|
|
|
{ score: 10, description: "产品符合或超过行业标准,质量稳定,无缺陷" },
|
|
|
|
|
|
{ score: 9, description: "产品质量良好,偶尔有小问题,但不影响使用" },
|
|
|
|
|
|
{ score: 7, description: "产品质量一般,可能存在小问题,但总体可接受" },
|
|
|
|
|
|
{ score: 5, description: "产品质量较差,存在较多问题,但基本可用" },
|
|
|
|
|
|
{ score: 3, description: "产品质量差,存在严重问题,不建议使用" },
|
|
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
category: "价格竞争力",
|
|
|
|
|
|
standards: [
|
|
|
|
|
|
{ score: 10, description: "价格远低于市场平均水平,竞争力极高" },
|
|
|
|
|
|
{ score: 9, description: "价格低于市场平均水平,具有一定竞争力" },
|
|
|
|
|
|
{ score: 7, description: "价格与市场平均水平持平,竞争力一般" },
|
|
|
|
|
|
{ score: 5, description: "价格高于市场平均水平,竞争力较低" },
|
|
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
category: "供货稳定性",
|
|
|
|
|
|
standards: [
|
|
|
|
|
|
{ score: 10, description: "供货非常稳定,从未出现延迟或缺货" },
|
|
|
|
|
|
{ score: 9, description: "供货较为稳定,偶尔有轻微延迟" },
|
|
|
|
|
|
{ score: 7, description: "供货稳定性一般,有时会出现延迟或缺货" },
|
|
|
|
|
|
{ score: 5, description: "供货不稳定,经常出现延迟或缺货" },
|
|
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
category: "合作信誉",
|
|
|
|
|
|
standards: [
|
|
|
|
|
|
{ score: 10, description: "销售表现优秀,客户评价极高" },
|
|
|
|
|
|
{ score: 9, description: "销售良好,偶尔有小问题,但总体可靠" },
|
|
|
|
|
|
{ score: 7, description: "销售一般,存在一些客户投诉,但问题不大" },
|
|
|
|
|
|
{ score: 5, description: "销售较差,存在较多客户投诉或不良记录" },
|
|
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
category: "售后支持",
|
|
|
|
|
|
standards: [
|
|
|
|
|
|
{ score: 10, description: "售后服务优秀,响应及时,解决问题迅速" },
|
|
|
|
|
|
{ score: 9, description: "售后服务较好,响应及时,但可能有轻微不足" },
|
|
|
|
|
|
{ score: 7, description: "售后服务一般,响应时间较长,但基本满足需求" },
|
|
|
|
|
|
{ score: 5, description: "售后服务较差,响应缓慢,客户满意度低" },
|
|
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
category: "最小订单量",
|
|
|
|
|
|
standards: [
|
|
|
|
|
|
{ score: 10, description: "无最低要求,支持单件代发" },
|
|
|
|
|
|
{ score: 9, description: "最小订单量较低(如10-20件)" },
|
|
|
|
|
|
{ score: 7, description: "最小订单量适中(如30-50件)" },
|
|
|
|
|
|
{ score: 5, description: "最小订单量较高(如100-200件)" },
|
|
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
category: "产品多样性",
|
|
|
|
|
|
standards: [
|
|
|
|
|
|
{ score: 10, description: "产品种类丰富,涵盖多个类别和款式" },
|
|
|
|
|
|
{ score: 9, description: "产品种类较多,但主要集中在某一类别" },
|
|
|
|
|
|
{ score: 7, description: "产品种类一般,有一定多样性,但不够丰富" },
|
|
|
|
|
|
{ score: 5, description: "产品种类较少,缺乏多样性" },
|
|
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
category: "物流配送",
|
|
|
|
|
|
standards: [
|
|
|
|
|
|
{ score: 10, description: "物流配送速度快,运费低,覆盖范围广" },
|
|
|
|
|
|
{ score: 9, description: "物流配送较快,运费适中,覆盖范围较广" },
|
|
|
|
|
|
{ score: 7, description: "物流配送一般,运费适中,覆盖范围有限" },
|
|
|
|
|
|
{ score: 5, description: "物流配送较慢,运费较高,覆盖范围有限" },
|
|
|
|
|
|
{ score: 3, description: "物流配送非常慢,运费高,覆盖范围小" },
|
|
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
];
|
|
|
|
|
|
const supplierEvaluationData = ref([
|
|
|
|
|
|
{
|
|
|
|
|
|
supplierType: "品牌直供",
|
|
|
|
|
|
quality: null,
|
|
|
|
|
|
priceCompetitiveness: null,
|
|
|
|
|
|
supplyStability: null,
|
|
|
|
|
|
reputation: null,
|
|
|
|
|
|
afterSales: null,
|
|
|
|
|
|
minOrderQuantity: null,
|
|
|
|
|
|
productDiversity: null,
|
|
|
|
|
|
logistics: null,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
supplierType: "批发市场",
|
|
|
|
|
|
quality: null,
|
|
|
|
|
|
priceCompetitiveness: null,
|
|
|
|
|
|
supplyStability: null,
|
|
|
|
|
|
reputation: null,
|
|
|
|
|
|
afterSales: null,
|
|
|
|
|
|
minOrderQuantity: null,
|
|
|
|
|
|
productDiversity: null,
|
|
|
|
|
|
logistics: null,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
supplierType: "代工厂/OEM",
|
|
|
|
|
|
quality: null,
|
|
|
|
|
|
priceCompetitiveness: null,
|
|
|
|
|
|
supplyStability: null,
|
|
|
|
|
|
reputation: null,
|
|
|
|
|
|
afterSales: null,
|
|
|
|
|
|
minOrderQuantity: null,
|
|
|
|
|
|
productDiversity: null,
|
|
|
|
|
|
logistics: null,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
supplierType: "跨境电商平台",
|
|
|
|
|
|
quality: null,
|
|
|
|
|
|
priceCompetitiveness: null,
|
|
|
|
|
|
supplyStability: null,
|
|
|
|
|
|
reputation: null,
|
|
|
|
|
|
afterSales: null,
|
|
|
|
|
|
minOrderQuantity: null,
|
|
|
|
|
|
productDiversity: null,
|
|
|
|
|
|
logistics: null,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
supplierType: "垂直类供应商",
|
|
|
|
|
|
quality: null,
|
|
|
|
|
|
priceCompetitiveness: null,
|
|
|
|
|
|
supplyStability: null,
|
|
|
|
|
|
reputation: null,
|
|
|
|
|
|
afterSales: null,
|
|
|
|
|
|
minOrderQuantity: null,
|
|
|
|
|
|
productDiversity: null,
|
|
|
|
|
|
logistics: null,
|
|
|
|
|
|
},
|
|
|
|
|
|
]);
|
|
|
|
|
|
// 映射字段字典
|
|
|
|
|
|
const weightFieldMapping = {
|
|
|
|
|
|
产品质量: "qualityWeight",
|
|
|
|
|
|
价格竞争力: "priceWeight",
|
|
|
|
|
|
供货稳定性: "stabilityWeight",
|
|
|
|
|
|
合作信誉: "reputationWeight",
|
|
|
|
|
|
售后支持: "supportWeight",
|
|
|
|
|
|
最小订单量: "moqWeight",
|
|
|
|
|
|
产品多样性: "varietyWeight",
|
|
|
|
|
|
物流配送: "logisticsWeight",
|
|
|
|
|
|
};
|
|
|
|
|
|
const scoreFieldMapping = {
|
|
|
|
|
|
qualityWeight: "产品质量",
|
|
|
|
|
|
priceWeight: "价格竞争力",
|
|
|
|
|
|
stabilityWeight: "供货稳定性",
|
|
|
|
|
|
reputationWeight: "合作信誉",
|
|
|
|
|
|
supportWeight: "售后支持",
|
|
|
|
|
|
moqWeight: "最小订单量",
|
|
|
|
|
|
varietyWeight: "产品多样性",
|
|
|
|
|
|
logisticsWeight: "物流配送",
|
|
|
|
|
|
};
|
|
|
|
|
|
const comprehensiveScore = {
|
|
|
|
|
|
crossborderScore: null,
|
|
|
|
|
|
directSupplyScore: null,
|
|
|
|
|
|
oemScore: null,
|
|
|
|
|
|
verticalScore: null,
|
|
|
|
|
|
wholesaleScore: null,
|
|
|
|
|
|
};
|
|
|
|
|
|
const mapping = {
|
|
|
|
|
|
跨境电商平台: "crossborderScore",
|
|
|
|
|
|
品牌直供: "directSupplyScore",
|
|
|
|
|
|
"代工厂/OEM": "oemScore",
|
|
|
|
|
|
垂直类供应商: "verticalScore",
|
|
|
|
|
|
批发市场: "wholesaleScore",
|
|
|
|
|
|
};
|
|
|
|
|
|
const mappingkey = {
|
|
|
|
|
|
crossborderScore: "跨境电商平台",
|
|
|
|
|
|
directSupplyScore: "品牌直供",
|
|
|
|
|
|
oemScore: "代工厂/OEM",
|
|
|
|
|
|
verticalScore: "垂直类供应商",
|
|
|
|
|
|
wholesaleScore: "批发市场",
|
|
|
|
|
|
};
|
|
|
|
|
|
// 提取权重
|
|
|
|
|
|
const getWeightMap = () => {
|
|
|
|
|
|
const map = {};
|
|
|
|
|
|
evaluationWeights.value.forEach((item) => {
|
|
|
|
|
|
if (!item.isTotal) {
|
|
|
|
|
|
const key = weightFieldMapping[item.dimension];
|
|
|
|
|
|
if (key) {
|
|
|
|
|
|
map[key] = item.currentWeight;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
return map;
|
|
|
|
|
|
};
|
|
|
|
|
|
const supplierEvaluationData1 = ref([
|
|
|
|
|
|
{
|
|
|
|
|
|
supplierType: "品牌直供",
|
|
|
|
|
|
quality: null,
|
|
|
|
|
|
priceCompetitiveness: null,
|
|
|
|
|
|
supplyStability: null,
|
|
|
|
|
|
reputation: null,
|
|
|
|
|
|
afterSales: null,
|
|
|
|
|
|
minOrderQuantity: null,
|
|
|
|
|
|
productDiversity: null,
|
|
|
|
|
|
logistics: null,
|
|
|
|
|
|
comprehensiveScore: "",
|
|
|
|
|
|
weight: "",
|
|
|
|
|
|
type: "",
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
supplierType: "批发市场",
|
|
|
|
|
|
quality: null,
|
|
|
|
|
|
priceCompetitiveness: null,
|
|
|
|
|
|
supplyStability: null,
|
|
|
|
|
|
reputation: null,
|
|
|
|
|
|
afterSales: null,
|
|
|
|
|
|
minOrderQuantity: null,
|
|
|
|
|
|
productDiversity: null,
|
|
|
|
|
|
logistics: null,
|
|
|
|
|
|
comprehensiveScore: "",
|
|
|
|
|
|
weight: "",
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
supplierType: "代工厂/OEM",
|
|
|
|
|
|
quality: null,
|
|
|
|
|
|
priceCompetitiveness: null,
|
|
|
|
|
|
supplyStability: null,
|
|
|
|
|
|
reputation: null,
|
|
|
|
|
|
afterSales: null,
|
|
|
|
|
|
minOrderQuantity: null,
|
|
|
|
|
|
productDiversity: null,
|
|
|
|
|
|
logistics: null,
|
|
|
|
|
|
comprehensiveScore: "",
|
|
|
|
|
|
weight: "",
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
supplierType: "跨境电商平台",
|
|
|
|
|
|
quality: null,
|
|
|
|
|
|
priceCompetitiveness: null,
|
|
|
|
|
|
supplyStability: null,
|
|
|
|
|
|
reputation: null,
|
|
|
|
|
|
afterSales: null,
|
|
|
|
|
|
minOrderQuantity: null,
|
|
|
|
|
|
productDiversity: null,
|
|
|
|
|
|
logistics: null,
|
|
|
|
|
|
comprehensiveScore: "",
|
|
|
|
|
|
weight: "",
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
supplierType: "垂直类供应商",
|
|
|
|
|
|
quality: null,
|
|
|
|
|
|
priceCompetitiveness: null,
|
|
|
|
|
|
supplyStability: null,
|
|
|
|
|
|
reputation: null,
|
|
|
|
|
|
afterSales: null,
|
|
|
|
|
|
minOrderQuantity: null,
|
|
|
|
|
|
productDiversity: null,
|
|
|
|
|
|
logistics: null,
|
|
|
|
|
|
comprehensiveScore: "",
|
|
|
|
|
|
weight: "",
|
|
|
|
|
|
},
|
|
|
|
|
|
]);
|
|
|
|
|
|
const total = computed(() => {
|
|
|
|
|
|
return evaluationWeights.value.filter((item) => !item.isTotal).reduce((sum, item) => sum + parseFloat(Number(item.currentWeight).toFixed(2)), 0);
|
|
|
|
|
|
});
|
|
|
|
|
|
// 权重提交
|
|
|
|
|
|
const onClick = () => {
|
|
|
|
|
|
// 需要判断权重是否在推荐范围内
|
|
|
|
|
|
const isValid = evaluationWeights.value.every((item) => {
|
|
|
|
|
|
if (item.dimension === "合计") return true;
|
|
|
|
|
|
const weight = parseFloat(item.currentWeight);
|
|
|
|
|
|
return weight >= item.min && weight <= item.max;
|
|
|
|
|
|
});
|
|
|
|
|
|
if (isValid && total.value === 1) {
|
|
|
|
|
|
proxy.$modal.msgSuccess("权重设置成功");
|
|
|
|
|
|
supplierEvaluationData1.value.forEach((item, index) => {
|
|
|
|
|
|
item.weight = evaluationWeights.value[index].currentWeight;
|
|
|
|
|
|
});
|
|
|
|
|
|
currentStep.value = 1;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
proxy.$modal.msgWarning("权重设置超出范围,请重新设置");
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
// 计算综合得分
|
|
|
|
|
|
const onSubmit = () => {
|
|
|
|
|
|
if (currentStep.value < 1) {
|
|
|
|
|
|
proxy.$modal.msgWarning("请先完成权重设置");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
// 查看全部填写完毕了吗
|
|
|
|
|
|
const isAllFilled = supplierEvaluationData.value.every((item) => {
|
|
|
|
|
|
return item.quality !== "" && item.priceCompetitiveness !== "" && item.supplyStability !== "" && item.reputation !== "" && item.afterSales !== "" && item.minOrderQuantity !== "" && item.productDiversity !== "" && item.logistics !== "";
|
|
|
|
|
|
});
|
|
|
|
|
|
if (!isAllFilled) {
|
|
|
|
|
|
proxy.$modal.msgWarning("请填写完整");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
proxy.$modal.msgSuccess("提交成功");
|
|
|
|
|
|
currentStep.value = 3;
|
|
|
|
|
|
syncData();
|
|
|
|
|
|
};
|
|
|
|
|
|
const onInput = (index) => {
|
|
|
|
|
|
if (currentStep.value < 3) {
|
|
|
|
|
|
proxy.$modal.msgWarning("请先供应渠道评分");
|
|
|
|
|
|
supplierEvaluationData1.value[index].comprehensiveScore = "";
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
currentStep.value = 4;
|
|
|
|
|
|
};
|
|
|
|
|
|
const onChange = (index) => {
|
|
|
|
|
|
if (currentStep.value < 3) {
|
|
|
|
|
|
proxy.$modal.msgWarning("请先供应渠道综合得分计算");
|
|
|
|
|
|
for (const key in form.value) {
|
|
|
|
|
|
form.value[key] = "";
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
// 同步数据方法
|
|
|
|
|
|
const syncData = () => {
|
|
|
|
|
|
supplierEvaluationData1.value.forEach((item1) => {
|
|
|
|
|
|
const matchingItem = supplierEvaluationData.value.find((item2) => item2.supplierType === item1.supplierType);
|
|
|
|
|
|
if (matchingItem) {
|
|
|
|
|
|
item1.quality = matchingItem.quality;
|
|
|
|
|
|
item1.priceCompetitiveness = matchingItem.priceCompetitiveness;
|
|
|
|
|
|
item1.supplyStability = matchingItem.supplyStability;
|
|
|
|
|
|
item1.reputation = matchingItem.reputation;
|
|
|
|
|
|
item1.afterSales = matchingItem.afterSales;
|
|
|
|
|
|
item1.minOrderQuantity = matchingItem.minOrderQuantity;
|
|
|
|
|
|
item1.productDiversity = matchingItem.productDiversity;
|
|
|
|
|
|
item1.logistics = matchingItem.logistics;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
function normalizeActiveStep(value) {
|
|
|
|
|
|
if (!configuredSteps.value.length) return 1;
|
|
|
|
|
|
return Math.min(Math.max(Number(value) || 1, 1), configuredSteps.value.length);
|
|
|
|
|
|
}
|
|
|
|
|
|
function isSectionVisible(section) { return !hasConfiguredSteps.value || activeStep.value === section; }
|
|
|
|
|
|
function buildProgress() {
|
|
|
|
|
|
return {
|
|
|
|
|
|
activeStep: activeStep.value,
|
|
|
|
|
|
form: form.value,
|
|
|
|
|
|
evaluationWeights: evaluationWeights.value,
|
|
|
|
|
|
supplierEvaluationData: supplierEvaluationData.value,
|
|
|
|
|
|
supplierEvaluationData1: supplierEvaluationData1.value,
|
|
|
|
|
|
currentStep: currentStep.value,
|
|
|
|
|
|
updatedAt: new Date().toISOString(),
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
function applyProgress(value) {
|
|
|
|
|
|
if (!value || typeof value !== "object") return;
|
|
|
|
|
|
activeStep.value = normalizeActiveStep(value.activeStep);
|
|
|
|
|
|
form.value = { ...form.value, ...(value.form || {}) };
|
|
|
|
|
|
if (Array.isArray(value.evaluationWeights)) evaluationWeights.value = value.evaluationWeights;
|
|
|
|
|
|
if (Array.isArray(value.supplierEvaluationData)) supplierEvaluationData.value = value.supplierEvaluationData;
|
|
|
|
|
|
if (Array.isArray(value.supplierEvaluationData1)) supplierEvaluationData1.value = value.supplierEvaluationData1;
|
|
|
|
|
|
currentStep.value = Number(value.currentStep) || currentStep.value;
|
|
|
|
|
|
}
|
|
|
|
|
|
async function persistProgress(saveAction = "SAVE", silent = false) {
|
|
|
|
|
|
await saveStudentTrainingAnswer(TASK_KEY, { saveAction, currentStep: activeStep.value, step1Answer: JSON.stringify(buildProgress()) });
|
|
|
|
|
|
if (!silent) proxy?.$modal?.msgSuccess(saveAction === "SUBMIT" ? "提交成功" : "保存成功");
|
|
|
|
|
|
}
|
|
|
|
|
|
function setCurrentStep(step) { activeStep.value = normalizeActiveStep(step); persistProgress("SAVE", true).catch(() => {}); }
|
|
|
|
|
|
function goPreviousStep() { setCurrentStep(activeStep.value - 1); }
|
|
|
|
|
|
function goNextStep() { setCurrentStep(activeStep.value + 1); }
|
|
|
|
|
|
async function saveCurrentProgress() { if (saving.value) return; saving.value = true; try { await persistProgress(); } finally { saving.value = false; } }
|
|
|
|
|
|
async function loadTaskConfig() {
|
|
|
|
|
|
try { const res = await getTrainingTaskByKey(TASK_KEY); taskConfig.value = res?.data || null; activeStep.value = normalizeActiveStep(activeStep.value); }
|
|
|
|
|
|
catch (error) { taskConfig.value = null; activeStep.value = 1; }
|
|
|
|
|
|
}
|
|
|
|
|
|
async function loadSavedProgress() {
|
|
|
|
|
|
try { const res = await getStudentTrainingAnswer(TASK_KEY); const raw = res?.data?.step1Answer; if (raw) applyProgress(JSON.parse(raw)); }
|
|
|
|
|
|
catch (error) { /* Historical progress is optional. */ }
|
|
|
|
|
|
}
|
|
|
|
|
|
function exportOutcome() {
|
|
|
|
|
|
const blob = new Blob([JSON.stringify(buildProgress(), 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);
|
|
|
|
|
|
}
|
|
|
|
|
|
const submitTask = async () => {
|
|
|
|
|
|
// 判断form是否全部填写
|
|
|
|
|
|
const isFormFilled = Object.values(form.value).every((value) => value !== "");
|
|
|
|
|
|
if (!isFormFilled) {
|
|
|
|
|
|
proxy.$modal.msgWarning("请先完成实训!");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
supplierEvaluationData1.value.forEach((item) => {
|
|
|
|
|
|
const key = mapping[item.supplierType];
|
|
|
|
|
|
if (key) {
|
|
|
|
|
|
comprehensiveScore[key] = item.comprehensiveScore;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
let params = {
|
|
|
|
|
|
...getWeightMap(),
|
|
|
|
|
|
...comprehensiveScore,
|
|
|
|
|
|
userId: userStore.userInfo.userId,
|
|
|
|
|
|
channelScores: JSON.stringify({
|
|
|
|
|
|
list: supplierEvaluationData.value,
|
|
|
|
|
|
form: form.value,
|
|
|
|
|
|
}),
|
|
|
|
|
|
};
|
|
|
|
|
|
if (saving.value) return;
|
|
|
|
|
|
saving.value = true;
|
|
|
|
|
|
try {
|
|
|
|
|
|
await analysisAPI.supplierEvaluationAndSelectionSubmit(params);
|
|
|
|
|
|
await persistProgress("SUBMIT");
|
|
|
|
|
|
} finally { saving.value = false; }
|
|
|
|
|
|
};
|
|
|
|
|
|
// 回显方法
|
|
|
|
|
|
const echo = async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const res = await analysisAPI.supplierEvaluationAndSelectionEcho({ userId: userStore.userInfo.userId });
|
|
|
|
|
|
if (!res.data) return;
|
|
|
|
|
|
let data = JSON.parse(res.data.channelScores);
|
|
|
|
|
|
supplierEvaluationData.value = data.list;
|
|
|
|
|
|
form.value = data.form;
|
|
|
|
|
|
Object.entries(scoreFieldMapping).forEach(([key, value]) => {
|
|
|
|
|
|
const match = evaluationWeights.value.find((item) => item.dimension === value);
|
|
|
|
|
|
if (match) {
|
|
|
|
|
|
match.currentWeight = res.data[key];
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
Object.entries(mappingkey).forEach(([key, value]) => {
|
|
|
|
|
|
const match = supplierEvaluationData1.value.find((item) => item.supplierType === value);
|
|
|
|
|
|
if (match) {
|
|
|
|
|
|
match.comprehensiveScore = res.data[key];
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
supplierEvaluationData1.value.forEach((item, index) => {
|
|
|
|
|
|
item.weight = evaluationWeights.value[index].currentWeight;
|
|
|
|
|
|
});
|
|
|
|
|
|
currentStep.value = 5;
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
// Existing task data is optional when no prior submission exists.
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
//重新实训
|
|
|
|
|
|
const resetTask = async () => {
|
|
|
|
|
|
if (saving.value) return;
|
|
|
|
|
|
saving.value = true;
|
|
|
|
|
|
try {
|
|
|
|
|
|
await analysisAPI.supplierEvaluationAndSelectionReTrain({ userId: userStore.userInfo.userId });
|
|
|
|
|
|
proxy.$modal.msgSuccess("重新实训成功");
|
|
|
|
|
|
// 清空数据
|
|
|
|
|
|
evaluationWeights.value.forEach((item) => {
|
|
|
|
|
|
item.currentWeight = "";
|
|
|
|
|
|
});
|
|
|
|
|
|
supplierEvaluationData1.value.forEach((item) => {
|
|
|
|
|
|
item.quality = "";
|
|
|
|
|
|
item.priceCompetitiveness = "";
|
|
|
|
|
|
item.supplyStability = "";
|
|
|
|
|
|
item.reputation = "";
|
|
|
|
|
|
item.afterSales = "";
|
|
|
|
|
|
item.minOrderQuantity = "";
|
|
|
|
|
|
item.productDiversity = "";
|
|
|
|
|
|
item.logistics = "";
|
|
|
|
|
|
item.comprehensiveScore = "";
|
|
|
|
|
|
item.weight = "";
|
|
|
|
|
|
item.type = "";
|
|
|
|
|
|
});
|
|
|
|
|
|
supplierEvaluationData.value.forEach((item) => {
|
|
|
|
|
|
item.quality = "";
|
|
|
|
|
|
item.priceCompetitiveness = "";
|
|
|
|
|
|
item.supplyStability = "";
|
|
|
|
|
|
item.reputation = "";
|
|
|
|
|
|
item.afterSales = "";
|
|
|
|
|
|
item.minOrderQuantity = "";
|
|
|
|
|
|
item.productDiversity = "";
|
|
|
|
|
|
item.logistics = "";
|
|
|
|
|
|
});
|
|
|
|
|
|
for (const key in form.value) {
|
|
|
|
|
|
form.value[key] = "";
|
|
|
|
|
|
}
|
|
|
|
|
|
currentStep.value = 0;
|
|
|
|
|
|
activeStep.value = 1;
|
|
|
|
|
|
await persistProgress("RESET", true);
|
|
|
|
|
|
proxy.$modal.msgSuccess("清空填写成功");
|
|
|
|
|
|
} finally { saving.value = false; }
|
|
|
|
|
|
};
|
|
|
|
|
|
// 获取权重的函数
|
|
|
|
|
|
function getWeightByDimension(label) {
|
|
|
|
|
|
if (!currentStep.value == 2) return;
|
|
|
|
|
|
const match = evaluationWeights.value.find((item) => item.dimension === label);
|
|
|
|
|
|
return match ? match.currentWeight : "-";
|
|
|
|
|
|
}
|
|
|
|
|
|
onMounted(async () => {
|
|
|
|
|
|
await loadTaskConfig();
|
|
|
|
|
|
await echo();
|
|
|
|
|
|
await loadSavedProgress();
|
|
|
|
|
|
});
|
|
|
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
|
.supplier {
|
|
|
|
|
|
--supplier-panel: rgba(4, 31, 46, 0.78);
|
|
|
|
|
|
--supplier-panel-strong: rgba(3, 20, 31, 0.95);
|
|
|
|
|
|
--supplier-line: rgba(54, 174, 221, 0.42);
|
|
|
|
|
|
|
|
|
|
|
|
min-height: calc(100vh - 84px);
|
|
|
|
|
|
padding: 0 0 32px;
|
|
|
|
|
|
color: #d7ecff;
|
|
|
|
|
|
background:
|
|
|
|
|
|
radial-gradient(circle at 10% 0%, rgba(15, 129, 189, 0.16), transparent 34%),
|
|
|
|
|
|
linear-gradient(180deg, rgba(3, 17, 27, 0.94), rgba(5, 19, 30, 0.98));
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.training-step-area) {
|
|
|
|
|
|
margin-top: 0 !important;
|
|
|
|
|
|
border-color: var(--supplier-line) !important;
|
|
|
|
|
|
background: var(--supplier-panel) !important;
|
|
|
|
|
|
box-shadow: inset 0 1px 0 rgba(129, 231, 255, 0.12), 0 18px 38px rgba(0, 0, 0, 0.2) !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.el-table) {
|
|
|
|
|
|
--el-table-border-color: rgba(63, 164, 207, 0.36);
|
|
|
|
|
|
--el-table-header-bg-color: #0b577f;
|
|
|
|
|
|
--el-table-header-text-color: #edfbff;
|
|
|
|
|
|
--el-table-row-hover-bg-color: rgba(15, 107, 149, 0.32);
|
|
|
|
|
|
--el-table-bg-color: rgba(2, 20, 31, 0.82);
|
|
|
|
|
|
--el-table-tr-bg-color: rgba(2, 20, 31, 0.82);
|
|
|
|
|
|
--el-table-text-color: #d7ecff;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
margin-top: 12px;
|
|
|
|
|
|
border: 1px solid var(--supplier-line);
|
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
|
background: var(--supplier-panel-strong);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.el-table::before),
|
|
|
|
|
|
:deep(.el-table__inner-wrapper::before) {
|
|
|
|
|
|
background-color: transparent;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.el-table th.el-table__cell) {
|
|
|
|
|
|
color: #edfbff !important;
|
|
|
|
|
|
background: linear-gradient(180deg, #0f76a9, #0a567e) !important;
|
|
|
|
|
|
font-weight: 800;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.el-table td.el-table__cell),
|
|
|
|
|
|
:deep(.el-table tr) {
|
|
|
|
|
|
color: #d7ecff !important;
|
|
|
|
|
|
background: rgba(2, 20, 31, 0.82) !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.el-table .cell) {
|
|
|
|
|
|
line-height: 1.65;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.el-input__wrapper) {
|
|
|
|
|
|
min-height: 36px;
|
|
|
|
|
|
border: 1px solid rgba(76, 169, 211, 0.48);
|
|
|
|
|
|
border-radius: 7px;
|
|
|
|
|
|
box-shadow: none !important;
|
|
|
|
|
|
background: var(--supplier-panel-strong);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.el-input__inner) {
|
|
|
|
|
|
color: #ebf8ff;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.el-input__inner::placeholder) {
|
|
|
|
|
|
color: #7194a8;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.el-input__wrapper.is-focus) {
|
|
|
|
|
|
border-color: #4edbff;
|
|
|
|
|
|
box-shadow: 0 0 0 3px rgba(50, 205, 255, 0.12) !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.el-button) {
|
|
|
|
|
|
border-color: rgba(55, 174, 221, 0.72) !important;
|
|
|
|
|
|
color: #e9faff !important;
|
|
|
|
|
|
background: linear-gradient(180deg, #168ab9, #0c628d) !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.el-button:hover) {
|
|
|
|
|
|
border-color: #65e5ff !important;
|
|
|
|
|
|
color: #ffffff !important;
|
|
|
|
|
|
background: linear-gradient(180deg, #1b9ece, #0f709f) !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.online {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 10px;
|
|
|
|
|
|
padding: 10px 12px;
|
|
|
|
|
|
border: 1px solid rgba(44, 141, 182, 0.3);
|
|
|
|
|
|
border-radius: 10px;
|
|
|
|
|
|
background: rgba(1, 17, 27, 0.3);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.online span {
|
|
|
|
|
|
width: auto;
|
|
|
|
|
|
color: #d7ecff;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.online :deep(.el-select) {
|
|
|
|
|
|
min-width: 180px;
|
|
|
|
|
|
flex: 1 1 180px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.taskfooter {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
gap: 28px;
|
|
|
|
|
|
margin-top: 32px;
|
|
|
|
|
|
padding-top: 28px;
|
|
|
|
|
|
border-top: 1px solid rgba(55, 174, 221, 0.24);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.taskfooter :deep(.el-button) {
|
|
|
|
|
|
min-width: 172px;
|
|
|
|
|
|
height: 56px;
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
font-size: 18px;
|
|
|
|
|
|
font-weight: 800;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.supplier {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: minmax(720px, 1fr) 340px;
|
|
|
|
|
|
gap: 24px;
|
|
|
|
|
|
min-height: calc(100vh - 76px);
|
|
|
|
|
|
padding: 24px 24px 32px;
|
|
|
|
|
|
color: #eef5ff;
|
|
|
|
|
|
background: #06111d;
|
|
|
|
|
|
|
|
|
|
|
|
.training-core { min-width: 0; padding: 28px; border: 1px solid rgba(0, 180, 255, .25); border-radius: 36px; background: rgba(8, 18, 28, .6); box-shadow: inset 0 1px 0 rgba(169, 229, 255, .08), 0 22px 48px rgba(0, 0, 0, .22); }
|
|
|
|
|
|
.task-header { margin-bottom: 24px; }
|
|
|
|
|
|
.task-badge { display: inline-flex; align-items: center; gap: 8px; min-height: 28px; padding: 4px 14px; border: 1px solid rgba(99, 217, 255, .55); border-radius: 999px; color: #dff5ff; background: rgba(17, 126, 178, .36); font-size: 12px; font-weight: 700; }
|
|
|
|
|
|
.task-title { margin: 12px 0 0; color: #fff; font-size: 30px; font-weight: 900; line-height: 1.3; }
|
|
|
|
|
|
.task-card, .step-card { border: 1px solid rgba(82, 174, 226, .42); border-radius: 26px; background: rgba(0, 25, 42, .64); }
|
|
|
|
|
|
.task-card { margin-top: 28px; padding: 18px 22px 22px; }
|
|
|
|
|
|
.workbench-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 18px; margin-bottom: 22px; padding-bottom: 18px; border-bottom: 1px solid rgba(0, 180, 255, .2); }
|
|
|
|
|
|
.section-eyebrow { margin: 0; color: #71dfff; font-size: 12px; font-weight: 800; line-height: 1.4; }
|
|
|
|
|
|
.workbench-head h3 { margin: 4px 0 8px; color: #fff; font-size: 26px; font-weight: 800; line-height: 1.3; }
|
|
|
|
|
|
.workbench-description { margin: 0; color: #d7ecff; font-size: 15px; line-height: 1.8; }
|
|
|
|
|
|
.workbench-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 10px; flex-shrink: 0; }
|
|
|
|
|
|
.outline-action, .btn-nav { display: inline-flex; align-items: center; justify-content: center; gap: 8px; min-height: 40px; padding: 0 18px; border: 1px solid rgba(99, 217, 255, .55); border-radius: 999px; color: #e9fbff; background: rgba(17, 126, 178, .36); font: inherit; font-size: 14px; font-weight: 800; cursor: pointer; }
|
|
|
|
|
|
.step-indicator { display: flex; gap: 6px; min-height: 58px; margin: 0 0 32px; padding: 6px; overflow-x: auto; border: 1px solid rgba(0, 180, 255, .3); border-radius: 999px; background: rgba(0, 20, 35, .65); }
|
|
|
|
|
|
.step-tab { flex: 1 0 max-content; min-height: 46px; padding: 0 18px; border: 1px solid transparent; border-radius: 999px; color: #9ab3d0; background: transparent; font: inherit; font-size: 14px; font-weight: 800; cursor: pointer; }
|
|
|
|
|
|
.step-tab.active { border-color: rgba(92, 224, 255, .7); color: #fff; background: linear-gradient(95deg, #0a6b9e, #0a5c86); }
|
|
|
|
|
|
.step-card { margin-top: 32px; padding: 18px 22px 22px; }
|
|
|
|
|
|
.step-indicator + .step-card, .workbench-head + .step-card { margin-top: 0; }
|
|
|
|
|
|
.step-title-row { margin-bottom: 14px; }
|
|
|
|
|
|
.step-title-row h3 { margin: 4px 0 0; color: #fff; font-size: 22px; font-weight: 900; line-height: 1.3; }
|
|
|
|
|
|
.caseBacktitle { display: flex; align-items: center; gap: 8px; margin: 20px 0 10px; color: #dff5ff; font-size: 16px; font-weight: 800; }
|
|
|
|
|
|
.caseBacktitle img { display: none; }
|
|
|
|
|
|
.step-card > p { color: #d7ecff; font-size: 15px; line-height: 1.8; }
|
|
|
|
|
|
:deep(.el-table) { width: 100% !important; max-width: 100%; margin-top: 12px; border-radius: 18px; }
|
|
|
|
|
|
:deep(.el-table th.el-table__cell) { background: rgba(8, 55, 82, .98) !important; }
|
|
|
|
|
|
:deep(.el-input__wrapper) { min-height: 42px; border-radius: 14px; background: #0f1a24; }
|
|
|
|
|
|
:deep(.el-select__wrapper) { min-height: 42px; border: 1px solid rgba(45, 95, 126, .95); border-radius: 14px; color: #eef5ff; background: #0f1a24; }
|
|
|
|
|
|
.step-navigation { display: flex; justify-content: center; gap: 14px; margin-top: 22px; }
|
|
|
|
|
|
.btn-nav.btn-save, .btn-nav.btn-primary { border-color: rgba(92, 224, 255, .7); color: #fff; background: linear-gradient(95deg, #0b84bd, #096491); }
|
|
|
|
|
|
.btn-nav:disabled { cursor: not-allowed; opacity: .45; }
|
|
|
|
|
|
.taskfooter { gap: 52px; margin-top: 36px; padding-top: 0; border-top: 0; }
|
|
|
|
|
|
.taskfooter :deep(.el-button) { min-width: 172px; height: 64px; border: 1px solid #2c83aa !important; border-radius: 6px; color: #dff7ff !important; background: rgba(13, 64, 88, .62) !important; font-size: 24px; font-weight: 900; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@media (max-width: 1100px) { .supplier { grid-template-columns: 1fr; } }
|
|
|
|
|
|
@media (max-width: 900px) { .supplier { padding: 14px; .training-core { padding: 18px; border-radius: 24px; } .workbench-head, .workbench-actions, .step-navigation { flex-direction: column; align-items: stretch; } .workbench-actions, .outline-action, .btn-nav, .taskfooter :deep(.el-button) { width: 100%; } .step-indicator { flex-direction: column; border-radius: 26px; } .step-tab { flex: none; } .taskfooter { gap: 14px; } } }
|
|
|
|
|
|
</style>
|