Merge remote-tracking branch 'origin/master'

bugfix/ai-assessment-evidence-rescore
陈沅 1 month ago
commit 1e50605b23

@ -13,16 +13,42 @@
<script setup>
import useSettingsStore from "@/store/modules/settings";
import { handleThemeStyle } from "@/utils/theme";
import cache from "@/plugins/cache";
import { removeUserInfo, removeToken } from "@/utils/auth.js";
onMounted(() => {
import cache from "@/plugins/cache";
import { removeUserInfo, removeToken } from "@/utils/auth.js";
const stepNameSelector = ".step-tab .step-copy > strong, .step-tab .step-copy > b, .step-tab .step-copy > .step-name";
let stepNameObserver;
let stepTitleRefreshQueued = false;
function addStepNameTitles() {
document.querySelectorAll(stepNameSelector).forEach((element) => {
const label = String(element.textContent || "").replace(/\s+/g, " ").trim();
if (label && !element.getAttribute("title")) element.setAttribute("title", label);
});
}
function queueStepNameTitleRefresh() {
if (stepTitleRefreshQueued) return;
stepTitleRefreshQueued = true;
requestAnimationFrame(() => {
addStepNameTitles();
stepTitleRefreshQueued = false;
});
}
onMounted(() => {
nextTick(() => {
// 初始化主题样式
handleThemeStyle(useSettingsStore().theme);
});
if (cache.session.getJSON("sessionObj") === undefined) {
removeToken();
removeUserInfo();
}
});
</script>
if (cache.session.getJSON("sessionObj") === undefined) {
removeToken();
removeUserInfo();
}
queueStepNameTitleRefresh();
stepNameObserver = new MutationObserver(queueStepNameTitleRefresh);
stepNameObserver.observe(document.body, { childList: true, subtree: true });
});
onBeforeUnmount(() => stepNameObserver?.disconnect());
</script>

@ -107,3 +107,11 @@
.el-loading-text {
color: #d7f8ff;
}
// Keep textarea character counts readable on the application's dark training surfaces.
// Element Plus uses a light fill by default, which appears as a white mask in the lower-right corner.
.el-input__count,
.el-input__count-inner {
color: #ffffff !important;
background: transparent !important;
}

@ -654,6 +654,31 @@
background: linear-gradient(95deg, #0a6b9e, #0a5c86) !important;
}
// Keep long step names inside the navigation rail. Their complete name remains
// available through the native title tooltip applied by App.vue.
.student-training-shell .step-tab .step-copy,
.legacy-training-page .step-tab .step-copy,
.step-tabs .step-tab .step-copy,
.step-indicator .step-tab .step-copy {
min-width: 0 !important;
overflow: hidden;
}
.student-training-shell .step-tab .step-copy > strong,
.student-training-shell .step-tab .step-copy > b,
.legacy-training-page .step-tab .step-copy > strong,
.legacy-training-page .step-tab .step-copy > b,
.step-tabs .step-tab .step-copy > strong,
.step-tabs .step-tab .step-copy > b,
.step-indicator .step-tab .step-copy > strong,
.step-indicator .step-tab .step-copy > b {
display: block !important;
min-width: 0;
overflow: hidden !important;
text-overflow: ellipsis !important;
white-space: nowrap !important;
}
@media (max-width: 1100px) {
#app .studentLayout .app-main:has(.student-training-shell) .student-training-shell,
#app .teacherLayout .app-main:has(.student-training-shell) .student-training-shell {

@ -5,40 +5,35 @@
<TrainingTaskBrief :background="taskBackground" :goals="taskGoals" :requirements="taskRequirement" :show-material="false" />
<section class="workbench">
<div class="workbench-top"><div><p>产品问题诊断与迭代</p><h2>{{ currentStep === 1 ? '全链路转化漏斗诊断' : currentStep === 2 ? '用户评论根因分析' : 'AB 测试验证方案' }}</h2></div><TrainingMaterialButton :material-name="taskConfig?.materialName" :material-url="taskConfig?.materialUrl" /></div>
<nav class="step-indicator" aria-label="产品问题诊断步骤">
<button type="button" class="step-tab" :class="{ active: currentStep === 1 }" @click="currentStep = 1"><b>01</b><span>全链路漏斗诊断<small>转化率与异常节点</small></span></button>
<span class="step-connector" aria-hidden="true"></span>
<button type="button" class="step-tab" :class="{ active: currentStep === 2 }" @click="currentStep = 2"><b>02</b><span>用户评论根因分析<small>词云与情感倾向</small></span></button>
<span class="step-connector" aria-hidden="true"></span>
<button type="button" class="step-tab" :class="{ active: currentStep === 3 }" @click="currentStep = 3"><b>03</b><span>AB 测试验证<small>改进方案有效性</small></span></button>
</nav>
<nav class="step-tabs" aria-label="产品问题诊断步骤"><button type="button" :class="['step-tab', { active: currentStep === 1, complete: currentStep > 1 }]" :aria-current="currentStep === 1 ? 'step' : undefined" @click="currentStep = 1"><span class="step-marker">01</span><span class="step-copy"><b>全链路漏斗诊断</b><small>{{ currentStep === 1 ? '当前步骤 →' : '点击切换 →' }}</small></span></button><span class="step-connector" aria-hidden="true" /><button type="button" :class="['step-tab', { active: currentStep === 2, complete: currentStep > 2 }]" :aria-current="currentStep === 2 ? 'step' : undefined" @click="currentStep = 2"><span class="step-marker">02</span><span class="step-copy"><b>用户评论根因分析</b><small>{{ currentStep === 2 ? '当前步骤 →' : '点击切换 →' }}</small></span></button><span class="step-connector" aria-hidden="true" /><button type="button" :class="['step-tab', { active: currentStep === 3 }]" :aria-current="currentStep === 3 ? 'step' : undefined" @click="currentStep = 3"><span class="step-marker">03</span><span class="step-copy"><b>AB测试验证</b><small>{{ currentStep === 3 ? '当前步骤 →' : '点击切换 →' }}</small></span></button></nav>
<template v-if="currentStep === 1">
<p class="lead">从用户触达产品的全路径出发,搭建从曝光到复购的完整数据漏斗,逐层计算各环节转化率并对比行业均值,定位产品在哪个环节出现了明显异常流失。</p>
<p class="lead">从用户触达产品的全路径出发,搭建从曝光到复购的完整数据漏斗,逐层计算各环节转化率并对比行业均值,定位产品在哪个环节出现了明显异常流失,精准锁定问题所在的运营环节。</p>
<div class="diagnosis-materials" aria-label="第 1 步参考资料">
<div class="diagnosis-materials__item"><div><strong>节点转化率计算</strong><span>使用案例数据计算从曝光到复购的各节点转化率。</span></div><div class="diagnosis-materials__actions"><button type="button" class="secondary-button" @click="downloadNodeGuide">下载实训操作说明</button><button type="button" class="secondary-button" @click="downloadNodeTemplate">下载Excel模板</button></div></div>
<div class="diagnosis-materials__item"><div><strong>全链路转化漏斗可视化</strong><span>根据节点转化率绘制各渠道与行业均值的漏斗对比图。</span></div><div class="diagnosis-materials__actions"><button type="button" class="secondary-button" @click="downloadFunnelGuide">下载实训操作说明</button><button type="button" class="secondary-button" @click="downloadFunnelTemplate">下载Excel模板</button></div></div>
<div class="diagnosis-materials__item"><strong>1.下载案例数据表,使用Excel计算各节点的转化率,参考《实训操作说明》。</strong><div class="diagnosis-materials__actions"><button type="button" class="secondary-button" @click="downloadNodeGuide">下载实训操作说明</button></div></div>
<div class="diagnosis-materials__item"><strong>2.下载Excel模版,填写关键信息,自动计算各环节转化率。提供自动化模版。</strong><div class="diagnosis-materials__actions"><button type="button" class="secondary-button" @click="downloadNodeTemplate">下载Excel模板</button></div></div>
<p class="diagnosis-materials__ai">3.导出数据表上传到通用AI大模型,通过AI大模型辅助完成计算,参考步骤及提示词如下:请根据表格数据,计算从曝光到复购链路节点转化率。</p>
</div>
<section class="section-block"><div class="section-heading"><span>一</span><div><h3>计算各节点转化率</h3><p>根据案例数据填写各渠道链路节点转化率,数值以百分比(%)填写。</p></div></div><div class="table-wrap"><table class="funnel-table"><thead><tr><th>链路节点</th><th>天猫</th><th>京东</th><th>抖音</th><th>拼多多</th><th>行业均值</th></tr></thead><tbody><tr v-for="row in conversionRates" :key="row.linkNode"><th>{{ row.linkNode }}</th><td v-for="field in rateFields" :key="field"><el-input-number v-model="row[field]" :min="0" :max="100" :precision="2" :controls="false" placeholder="请输入" /><span class="unit">%</span></td></tr></tbody></table></div></section>
<section class="section-block visual-section"><div class="section-heading"><span>二</span><div><h3>全链路转化漏斗可视化</h3><p>根据上述转化率绘制各渠道与行业均值的节点转化漏斗对比图。</p></div></div><input ref="chartInput" class="visually-hidden" type="file" accept="image/png,image/jpeg,image/webp" @change="handleChartUpload" /><div class="chart-upload-area"><template v-if="funnelChart.url"><div class="chart-preview"><el-image :src="funnelChart.url" :preview-src-list="[funnelChart.url]" fit="cover" preview-teleported /><div><strong>{{ funnelChart.name || '节点转化漏斗对比图' }}</strong><span>点击缩略图可放大预览</span><button type="button" @click="clearChart">移除图片</button></div></div><button type="button" class="secondary-button" :disabled="uploading" @click="chartInput?.click()">{{ uploading ? '上传中…' : '更换对比图' }}</button></template><template v-else><button type="button" class="upload-button" :disabled="uploading" @click="chartInput?.click()">{{ uploading ? '上传中…' : '上传节点转化漏斗对比图' }}</button><p>支持 PNG、JPG、WEBP 格式,图片不超过 5MB;上传后可点击预览。</p></template></div></section>
<section class="section-block diagnosis-section"><div class="section-heading"><span>三</span><div><h3>定位异常节点</h3><p>对比行业均值数据,定位异常节点并填写分析与判定。</p></div></div><div class="table-toolbar"><span>默认 1 行,最多 5 行;渠道必须来源于上表。</span><div><button type="button" class="table-button" :disabled="anomalyRows.length >= 5" @click="addAnomalyRow">增加一行</button><button type="button" class="table-button ghost" :disabled="anomalyRows.length <= 1" @click="removeAnomalyRow">删除一行</button></div></div><div class="table-wrap"><table class="diagnosis-table"><thead><tr><th>渠道</th><th>链路节点</th><th>店铺转化率</th><th>行业均值转化率</th><th>异常分析与判定</th></tr></thead><tbody><tr v-for="(row, index) in anomalyRows" :key="index"><td><el-select v-model="row.channel" placeholder="请选择"><el-option v-for="channel in channels" :key="channel.key" :label="channel.label" :value="channel.label" /></el-select></td><td><el-select v-model="row.linkNode" placeholder="请选择"><el-option v-for="node in nodes" :key="node" :label="node" :value="node" /></el-select></td><td><el-input-number v-model="row.storeConversionRate" :min="0" :max="100" :precision="2" :controls="false" placeholder="请输入" /><span class="unit">%</span></td><td><el-input-number v-model="row.industryAverageConversionRate" :min="0" :max="100" :precision="2" :controls="false" placeholder="请输入" /><span class="unit">%</span></td><td><el-input v-model="row.anomalyAnalysis" type="textarea" :rows="2" maxlength="300" show-word-limit placeholder="请填写异常原因或正常判定" /></td></tr></tbody></table></div><p class="diagnosis-step-one-notice">说明:默认展示一行可填写,最少一行,可增加行,最多 5 行;诊断异常节点须从上表已填写渠道中选择。</p></section>
<section class="section-block"><div class="section-heading"><span>一</span><div><h3>计算各节点转化率</h3><p>根据案例数据计算各个渠道链路节点转化率,参考方法及工具如下:</p></div></div><div class="table-wrap"><table class="funnel-table"><thead><tr><th>链路节点</th><th>天猫</th><th>京东</th><th>抖音</th><th>拼多多</th><th>行业均值</th></tr></thead><tbody><tr v-for="row in conversionRates" :key="row.linkNode"><th>{{ row.linkNode }}</th><td v-for="field in rateFields" :key="field"><el-input-number v-model="row[field]" :min="0" :max="100" :precision="2" :controls="false" :placeholder="row.linkNode === '曝光-点击' && field === 'tmall' ? '示例:11.5%' : '请输入'" /><span class="unit">%</span></td></tr></tbody></table></div></section>
<section class="section-block visual-section"><div class="section-heading"><span>二</span><div><h3>全链路转化漏斗可视化</h3><p>根据节点转化率计算结果,绘制各渠道转化率与行业均值的转化漏斗对比图,可视化呈现多环节情况,参考方法及工具如下:</p></div></div><div class="diagnosis-materials" aria-label="第 2 步参考资料"><div class="diagnosis-materials__item"><strong>1.使用Excel绘图功能,自主完成转化漏斗对比图,可参考《实训操作说明》。</strong><div class="diagnosis-materials__actions"><button type="button" class="secondary-button" @click="downloadFunnelGuide">下载实训操作说明</button></div></div><div class="diagnosis-materials__item"><strong>2.下载Excel模版,填写关键信息,自动计算各环节转化率并生成转化漏斗对比图。</strong><div class="diagnosis-materials__actions"><button type="button" class="secondary-button" @click="downloadFunnelTemplate">下载Excel模板</button></div></div><p class="diagnosis-materials__ai">3.导出数据表上传到通用AI大模型,通过AI大模型辅助完成,参考步骤及提示词如下:请根据表格数据绘制漏斗图,并与行业均值漏斗图进行对比。</p></div><input ref="chartInput" class="visually-hidden" type="file" accept="image/png,image/jpeg,image/webp" @change="handleChartUpload" /><div class="chart-upload-area"><template v-if="funnelChart.url"><div class="chart-preview"><el-image :src="funnelChart.url" :preview-src-list="[funnelChart.url]" fit="cover" preview-teleported /><div><strong>{{ funnelChart.name || '节点转化漏斗对比图' }}</strong><span>点击缩略图可放大预览</span><button type="button" @click="clearChart">移除图片</button></div></div><button type="button" class="secondary-button" :disabled="uploading" @click="chartInput?.click()">{{ uploading ? '上传中…' : '更换对比图' }}</button></template><template v-else><button type="button" class="upload-button" :disabled="uploading" @click="chartInput?.click()">{{ uploading ? '上传中…' : '上传节点转化漏斗对比图' }}</button><p>支持 PNG、JPG、WEBP 格式,图片不超过 5MB;上传后可点击预览。</p></template></div></section>
<section class="section-block diagnosis-section"><div class="section-heading"><span>三</span><div><h3>定位异常节点</h3><p>对比案例行业均值数据,定位异常节点。</p></div></div><div class="table-toolbar"><span>对比案例行业均值数据,定位异常节点。</span><div><button type="button" class="table-button" :disabled="anomalyRows.length >= 5" @click="addAnomalyRow">增加一行</button><button type="button" class="table-button ghost" :disabled="anomalyRows.length <= 1" @click="removeAnomalyRow">删除一行</button></div></div><div class="table-wrap"><table class="diagnosis-table"><thead><tr><th>渠道</th><th>链路节点</th><th>店铺转化率</th><th>行业均值转化率</th><th>异常分析与判定</th></tr></thead><tbody><tr v-for="(row, index) in anomalyRows" :key="index"><td><el-select v-model="row.channel" placeholder="请选择"><el-option v-for="channel in channels" :key="channel.key" :label="channel.label" :value="channel.label" /></el-select></td><td><el-select v-model="row.linkNode" placeholder="请选择"><el-option v-for="node in nodes" :key="node" :label="node" :value="node" /></el-select></td><td><el-input-number v-model="row.storeConversionRate" :min="0" :max="100" :precision="2" :controls="false" placeholder="请输入" /><span class="unit">%</span></td><td><el-input-number v-model="row.industryAverageConversionRate" :min="0" :max="100" :precision="2" :controls="false" placeholder="请输入" /><span class="unit">%</span></td><td><el-input v-model="row.anomalyAnalysis" type="textarea" :rows="2" maxlength="300" show-word-limit placeholder="请填写异常原因或正常判定" /></td></tr></tbody></table></div><p class="diagnosis-step-one-notice">说明:这个表格默认展示一行可填写,最少一行,可以增加行,最多5行,这里填写的渠道需要来源于第一个表的渠道。</p></section>
<div class="actions"><button type="button" class="secondary-button" :disabled="saving" @click="saveStepOneDraft">保存草稿</button><button type="button" class="submit-button" :disabled="saving || uploading" @click="validateStepOne">{{ saving ? '校验中…' : '校验并进入第 2 步' }}</button></div>
</template>
<template v-else-if="currentStep === 2">
<p class="lead">漏斗只能定位“哪个环节出了问题”,本步骤通过词云和情感倾向分析,从真实用户评价中提取高频问题,验证异常背后的根本原因。</p>
<section class="section-block"><div class="section-heading"><span>一</span><div><h3>词云分析</h3><p>导入评论数据,系统自动生成高频问题词云。</p></div></div><input ref="commentInput" class="visually-hidden" type="file" accept=".csv,.txt,.xls,.xlsx,text/csv,text/plain,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" @change="handleCommentImport" /><div class="comment-import"><button type="button" class="upload-button" :disabled="analyzing" @click="commentInput?.click()">{{ analyzing ? '正在生成词云…' : '导入评论数据生成词云' }}</button><p>支持 CSV、TXT、XLS、XLSX,文件不超过 5MB。</p><div v-if="commentPreview.fileName" class="data-preview"><div><strong>{{ commentPreview.fileName }}</strong><span>已解析 {{ commentPreview.totalComments }} 条评论 · 数据预览</span></div><ul><li v-for="(comment, index) in commentPreview.comments" :key="index">{{ comment }}</li></ul></div></div><div v-if="wordCloud.length" class="word-cloud-card"><div class="word-cloud-title"><span>高频问题词云</span><small>基于导入评论自动生成</small></div><div ref="wordCloudRef" class="word-cloud" aria-label="评论高频问题词云"></div></div><div class="top-issues"><p>请根据词云图填写词云中出现频次最高的 TOP3 问题。</p><div><label v-for="(_, index) in topIssues" :key="index">TOP{{ index + 1 }}<el-input v-model="topIssues[index]" :placeholder="`填写第 ${index + 1} 个高频问题`" /></label></div></div></section>
<section class="section-block"><div class="section-heading"><span>二</span><div><h3>情感倾向分析</h3><p>点击分析后,系统自动填充评论数量;请自行计算占比,保留两位小数。</p></div></div><div class="sentiment-actions"><button type="button" class="upload-button" :disabled="!analysisResult" @click="runSentimentAnalysis">情感倾向分析</button><span>评论数量由系统填充;系统仅校验学生填写的占比。</span></div><div class="table-wrap"><table class="sentiment-table"><thead><tr><th>情感类型</th><th>评论数量(条)</th><th>占比</th></tr></thead><tbody><tr v-for="row in sentiments" :key="row.type"><th>{{ row.type }}</th><td><span class="auto-count">{{ row.count ?? '—' }}</span></td><td><el-input-number v-model="row.percentage" :min="0" :max="100" :precision="2" :controls="false" :disabled="row.count === null" placeholder="请输入" /><span class="unit">%</span></td></tr></tbody></table></div></section>
<section class="section-block root-cause"><p>请结合词云 TOP3 与情感分析结果,说明本案例的核心问题根本原因;说明高频问题与情感数据是如何验证步骤一漏斗异常结论的。</p><el-input v-model="rootCauseAnalysis" type="textarea" :rows="6" maxlength="1000" show-word-limit placeholder="请填写根因分析与对步骤一异常结论的验证说明" /></section>
<p class="lead">漏斗只能定位“哪个环节出了问题”,要回答为什么出问题,需要分析用户最真实的反馈。本步骤通过词云分析和情感倾向分析,从用户评价中提取高频问题,判断整体口碑倾向,验证漏斗异常背后的真实根因。</p>
<section class="section-block"><div class="section-heading"><span>一</span><div><h3>词云分析</h3><p>导入数据,生成词云,基于案例评论数据自动生成高频问题词云。</p></div></div><input ref="commentInput" class="visually-hidden" type="file" accept=".csv,.txt,.xls,.xlsx,text/csv,text/plain,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" @change="handleCommentImport" /><div class="comment-import"><button type="button" class="upload-button" :disabled="analyzing" @click="commentInput?.click()">{{ analyzing ? '正在生成词云…' : '导入评论数据生成词云' }}</button><p>支持 CSV、TXT、XLS、XLSX,文件不超过 5MB。</p><div v-if="commentPreview.fileName" class="data-preview"><div><strong>{{ commentPreview.fileName }}</strong><span>已解析 {{ commentPreview.totalComments }} 条评论 · 数据预览</span></div><ul><li v-for="(comment, index) in commentPreview.comments" :key="index">{{ comment }}</li></ul></div></div><div v-if="wordCloud.length" class="word-cloud-card"><div class="word-cloud-title"><span>高频问题词云</span><small>基于导入评论自动生成</small></div><div ref="wordCloudRef" class="word-cloud" aria-label="评论高频问题词云"></div></div><div class="top-issues"><p>请根据词云图填写词云中出现频次最高的TOP3问题。</p><div><label v-for="(_, index) in topIssues" :key="index">TOP{{ index + 1 }}:<el-input v-model="topIssues[index]" :placeholder="`填写第 ${index + 1} 个高频问题`" /></label></div></div></section>
<section class="section-block"><div class="section-heading"><span>二</span><div><h3>情感倾向分析</h3></div></div><div class="sentiment-actions"><span>请点击情感倾向分析按钮,系统自动判断各情感类型评论数量,请计算各情感类型占比,正面评论占比=正面评论数量/总评论数量,以此类推,计算结果用百分数表示,保留两位小数。</span><button type="button" class="upload-button" :disabled="!analysisResult" @click="runSentimentAnalysis">情感倾向分析</button></div><div class="table-wrap"><table class="sentiment-table"><thead><tr><th>情感类型</th><th>评论数量(条)</th><th>占比</th></tr></thead><tbody><tr v-for="row in sentiments" :key="row.type"><th>{{ row.type }}</th><td><span class="auto-count">{{ row.count ?? '—' }}</span></td><td><el-input-number v-model="row.percentage" :min="0" :max="100" :precision="2" :controls="false" :disabled="row.count === null" placeholder="请输入" /><span class="unit">%</span></td></tr></tbody></table></div></section>
<section class="section-block root-cause"><p>请结合词云TOP3与情感分析结果,说明本案例的核心问题根本原因;说明高频问题与情感数据是如何验证步骤一漏斗异常结论的</p><el-input v-model="rootCauseAnalysis" type="textarea" :rows="6" maxlength="1000" show-word-limit placeholder="请填写根因分析与对步骤一异常结论的验证说明" /></section>
<div class="actions"><button type="button" class="secondary-button" :disabled="saving" @click="saveStepTwoDraft">保存草稿</button><button type="button" class="submit-button" :disabled="saving || analyzing" @click="validateStepTwo">{{ saving ? '校验中…' : '校验并保存第 2 步' }}</button></div>
</template>
<template v-else>
<p class="lead">找到根因后,还需验证“改进方案是否真的有效”,避免拍脑袋迭代。AB测试对照诊断法将用户随机分为对照组(维持原方案)和实验组(应用改进方案),对比两组关键指标差异,用数据判断改进方案是否值得落地。</p>
<section class="section-block"><div class="section-heading"><span>一</span><div><h3>确定测试方案</h3><p>请基于案例资料,针对步骤一和步骤二分析结果,设计 AB 测试方案。</p></div></div><el-input v-model="abTestPlan" type="textarea" :rows="6" maxlength="1000" show-word-limit placeholder="填写对照组与实验组设置、改进方案、随机分流方式、观察周期和成功判定标准" /></section>
<section class="section-block"><div class="section-heading"><span>二</span><div><h3>AB测试数据填报</h3><p>请从案例资料中获取 AB 测试两组数据,计算后填入下表并逐项判定。</p></div></div><div class="table-wrap"><table class="ab-table"><thead><tr><th>测试指标</th><th>对照组数据</th><th>实验组数据</th><th>判定与分析</th></tr></thead><tbody><tr v-for="row in abRows" :key="row.metric"><th>{{ row.metric }}</th><td><el-input-number v-model="row.controlValue" :min="0" :max="100" :precision="2" :controls="false" placeholder="如:56" /><span class="unit">%</span></td><td><el-input-number v-model="row.experimentValue" :min="0" :max="100" :precision="2" :controls="false" placeholder="如:64" /><span class="unit">%</span></td><td><el-input v-model="row.analysis" type="textarea" :rows="2" maxlength="300" show-word-limit placeholder="如:提升8pp,显著提升,采纳" /></td></tr></tbody></table></div></section>
<section class="section-block"><div class="section-heading"><span>三</span><div><h3>可视化呈现</h3><p>绘制 AB 测试结果对比图并上传,作为改进方案的可视化证据。</p></div></div><input ref="abChartInput" class="visually-hidden" type="file" accept="image/png,image/jpeg,image/webp" @change="handleAbChartUpload" /><div class="chart-upload-area"><template v-if="abChart.url"><div class="chart-preview"><el-image :src="abChart.url" :preview-src-list="[abChart.url]" fit="cover" preview-teleported /><div><strong>{{ abChart.name || 'AB 测试结果对比图' }}</strong><span>点击缩略图可放大预览</span><button type="button" @click="clearAbChart">移除图片</button></div></div><button type="button" class="secondary-button" :disabled="uploading" @click="abChartInput?.click()">{{ uploading ? '上传中…' : '更换对比图' }}</button></template><template v-else><button type="button" class="upload-button" :disabled="uploading" @click="abChartInput?.click()">{{ uploading ? '上传中…' : '上传AB测试结果对比图' }}</button><p>支持 PNG、JPG、WEBP 格式,图片不超过 5MB;上传后可点击预览。</p></template></div><div class="ab-materials"><div><ol><li>使用 Excel/WPS 绘图功能,自主完成 AB 测试结果对比图,可参考《实训操作说明》。</li><li>下载 Excel 模板,填写关键数据,自动计算 AB 测试数据并生成测试结果对比图。</li><li>也可导出数据表上传到通用 AI 大模型,通过对照组和实验组数据对比生成组合柱状图。</li></ol></div><div class="ab-materials__actions"><button type="button" class="secondary-button" @click="downloadAbGuide">下载实训操作说明</button><button type="button" class="secondary-button" @click="downloadAbTemplate">下载Excel模板</button></div></div></section>
<section class="section-block"><div class="section-heading"><span>四</span><div><h3>诊断结论与迭代优化建议</h3><p>结合全链路数据漏斗诊断、售后评论分析法及 AB 测试对照诊断,分析产品存在的问题,并提出具体迭代优化建议。</p></div></div><el-input v-model="iterationRecommendation" type="textarea" :rows="6" maxlength="1200" show-word-limit placeholder="请填写产品问题诊断结论及具体迭代优化建议" /></section>
<p class="lead">找到根因后,还需验证“改进方案是否真的有效”,避免拍脑袋迭代。AB测试对照诊断法将用户随机分为对照组(维持原方案)和实验组(应用改进方案),对比两组的关键指标差异,用数据判断改进方案是否值得落地。</p>
<section class="section-block"><div class="section-heading"><span>(1)</span><div><h3>确定测试方案</h3><p>请基于案例资料,针对步骤一和步骤二分析结果,设计AB测试方案</p></div></div><el-input v-model="abTestPlan" type="textarea" :rows="6" maxlength="1000" show-word-limit placeholder="填写对照组与实验组设置、改进方案、随机分流方式、观察周期和成功判定标准" /></section>
<section class="section-block"><div class="section-heading"><span>(2)</span><div><h3>AB测试数据填报</h3><p>请从案例资料中获取AB测试两组数据,计算后填入下表并逐项判定:</p></div></div><div class="table-wrap"><table class="ab-table"><thead><tr><th>测试指标</th><th>对照组数据</th><th>实验组数据</th><th>判定与分析</th></tr></thead><tbody><tr v-for="row in abRows" :key="row.metric"><th>{{ row.metric }}</th><td><el-input-number v-model="row.controlValue" :min="0" :max="100" :precision="2" :controls="false" placeholder="示例:56%" /><span class="unit">%</span></td><td><el-input-number v-model="row.experimentValue" :min="0" :max="100" :precision="2" :controls="false" placeholder="示例:64%" /><span class="unit">%</span></td><td><el-input v-model="row.analysis" type="textarea" :rows="2" maxlength="300" show-word-limit placeholder="示例:提升8pp,显著提升,采纳" /></td></tr></tbody></table></div></section>
<section class="section-block"><div class="section-heading"><span>(3)</span><div><h3>可视化呈现</h3><p>绘制AB测试结果对比图,参考方法及工具如下:</p></div></div><input ref="abChartInput" class="visually-hidden" type="file" accept="image/png,image/jpeg,image/webp" @change="handleAbChartUpload" /><div class="chart-upload-area"><template v-if="abChart.url"><div class="chart-preview"><el-image :src="abChart.url" :preview-src-list="[abChart.url]" fit="cover" preview-teleported /><div><strong>{{ abChart.name || 'AB 测试结果对比图' }}</strong><span>点击缩略图可放大预览</span><button type="button" @click="clearAbChart">移除图片</button></div></div><button type="button" class="secondary-button" :disabled="uploading" @click="abChartInput?.click()">{{ uploading ? '上传中…' : '更换对比图' }}</button></template><template v-else><button type="button" class="upload-button" :disabled="uploading" @click="abChartInput?.click()">{{ uploading ? '上传中…' : '上传AB测试结果对比图' }}</button><p>支持 PNG、JPG、WEBP 格式,图片不超过 5MB;上传后可点击预览。</p></template></div><div class="ab-materials"><div><ol><li>1.使用Excel/WPS绘图功能,自主完成AB测试结果对比图,参考《实训操作说明》。</li><li>2.下载Excel模版,填写关键信息,自动计算AB测试数据,并生成测试结果对比图。</li><li>3.导出数据表上传到通用AI大模型,通过AI大模型辅助完成,参考步骤及提示词如下:请根据表格数据,进行对照组和实验组数据对比,采用组合柱状图生成可视化图形。</li></ol></div><div class="ab-materials__actions"><button type="button" class="secondary-button" @click="downloadAbGuide">下载实训操作说明</button><button type="button" class="secondary-button" @click="downloadAbTemplate">下载Excel模板</button></div></div></section>
<section class="section-block"><div class="section-heading"><span>(4)</span><div><h3>诊断结论与迭代优化建议</h3><p>结合全链路数据漏斗诊断、售后评论分析法及AB测试对照诊断,分析产品存在的问题,并提出具体的迭代优化建议。</p></div></div><el-input v-model="iterationRecommendation" type="textarea" :rows="6" maxlength="1200" show-word-limit placeholder="请填写产品问题诊断结论及具体迭代优化建议" /></section>
<div class="actions"><button type="button" class="secondary-button" :disabled="saving" @click="saveStepThreeDraft">保存草稿</button><button type="button" class="submit-button" :disabled="saving || uploading" @click="validateStepThree">{{ saving ? '校验中…' : '校验并保存第 3 步' }}</button></div>
</template>
</section>
@ -96,13 +91,40 @@ onMounted(async () => { try { taskConfig.value = (await getTrainingTaskByKey(TAS
<style scoped lang="scss">
.diagnosis-materials{display:grid;gap:10px;margin:14px 0}.diagnosis-materials__item{display:flex;align-items:center;justify-content:space-between;gap:20px;padding:12px 14px;border:1px solid rgba(76,202,242,.32);border-radius:10px;background:rgba(3,42,61,.45)}.diagnosis-materials__item strong,.diagnosis-materials__item span{display:block}.diagnosis-materials__item strong{color:#e5faff;font-size:15px}.diagnosis-materials__item span{margin-top:4px;color:#93baca;font-size:13px}.diagnosis-materials__actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:8px}.diagnosis-step-one-notice{margin:12px 0 0;color:#ff7178;font-size:13px;line-height:1.6}@media (max-width:860px){.diagnosis-materials__item{align-items:flex-start;flex-direction:column}.diagnosis-materials__actions{justify-content:flex-start}}
.diagnosis-materials__ai{margin:0;padding:3px 2px;color:#cce8f2;font-size:14px;line-height:1.75}.diagnosis-section .table-toolbar>span{display:none}
.diagnosis-step-one-notice{display:none}
.section-heading>span{flex:0 0 auto;min-width:30px;width:auto;padding:0 5px}
.step-tabs{padding:18px 22px;border-radius:22px}.step-tabs .step-tab{display:flex;flex:1 1 0;align-items:center;min-width:145px;min-height:66px;gap:10px;padding:8px 12px;border:1px solid rgba(68,141,177,.35);border-radius:12px;color:#9ab3d0;background:rgba(3,27,43,.52);font:inherit;text-align:left;cursor:pointer;transition:.2s ease}.step-tabs .step-tab:hover{border-color:rgba(89,223,255,.82);color:#fff;background:rgba(8,92,131,.52);transform:translateY(-1px)}.step-tabs .step-tab:focus-visible{outline:2px solid #72e8ff;outline-offset:3px}.step-tabs .step-marker{display:grid;flex:0 0 38px;width:38px;height:38px;place-items:center;border:1px solid rgba(101,176,208,.6);border-radius:50%;color:#a6c4d9;background:rgba(3,25,40,.8);font-size:12px;font-weight:900}.step-tabs .step-copy{display:grid;min-width:0;gap:4px}.step-tabs .step-copy b{overflow:hidden;font-size:14px;font-weight:800;text-overflow:ellipsis;white-space:nowrap}.step-tabs .step-copy small{color:#66b6d4;font-size:10px;font-weight:800;letter-spacing:.06em}.step-tabs .step-tab.active{border-color:#53d8fb;color:#fff;background:linear-gradient(135deg,rgba(7,156,207,.85),rgba(13,105,161,.92));box-shadow:inset 0 0 24px rgba(77,213,255,.22)}.step-tabs .step-tab.active .step-marker{border-color:#72e8ff;color:#fff;background:linear-gradient(135deg,#08b7e8,#1375d0);box-shadow:0 0 0 5px rgba(25,179,237,.14),0 0 18px rgba(46,208,255,.58)}.step-tabs .step-tab.complete{color:#dffbff}.step-tabs .step-tab.complete .step-marker{border-color:rgba(84,230,221,.85);color:#062b37;background:#6ef0e1}.step-tabs .step-connector{position:relative;flex:0 0 24px;height:2px;background:rgba(86,198,235,.48)}.step-tabs .step-connector::after{position:absolute;top:50%;right:0;width:7px;height:7px;border-top:1px solid #73b1ce;border-right:1px solid #73b1ce;content:'';transform:translateY(-50%) rotate(45deg)}
.step-tabs .step-copy b{display:block;flex:none;width:auto;height:auto;place-items:initial;border:0;border-radius:0;color:inherit;background:transparent;box-shadow:none}
@media(max-width:720px){.step-tabs .step-connector{flex:0 0 18px;width:2px;height:18px;margin-left:18px}.step-tabs .step-connector::after{top:auto;right:auto;bottom:0;left:50%;transform:translateX(-50%) rotate(135deg)}}
.ab-materials{display:flex;align-items:center;justify-content:space-between;gap:20px;margin-top:18px;padding:14px 16px;border-left:3px solid #19c9ff;background:rgba(3,42,61,.45);color:#cde9f2}.ab-materials ol{margin:0;padding-left:20px;line-height:1.8}.ab-materials__actions{display:grid;flex:0 0 178px;gap:8px}@media (max-width:860px){.ab-materials{align-items:stretch;flex-direction:column}.ab-materials__actions{grid-template-columns:repeat(2,minmax(0,1fr));flex-basis:auto}}
.ab-table{width:100%;min-width:860px;border-collapse:collapse}.ab-table th,.ab-table td{border:1px solid rgba(139,213,241,.45)}.ab-table thead th{padding:14px 10px;color:#fff;background:#079ed9;font-size:16px}.ab-table tbody th{width:25%;padding:14px 10px;color:#dff8ff;background:rgba(6,67,94,.34);text-align:center}.ab-table td{padding:9px 11px;text-align:center}.ab-table td:nth-child(2),.ab-table td:nth-child(3){width:18%}.ab-table td:last-child{width:39%}
.step-indicator { display: flex; align-items: center; margin: 0 0 24px; padding: 16px 20px; overflow-x: auto; border: 1px solid rgba(0, 180, 255, .3); border-radius: 18px; background: rgba(1, 18, 31, .62); }
.step-tab { display: flex; flex: 1 0 145px; min-width: 0; align-items: center; gap: 10px; min-height: 66px; padding: 8px; border: 1px solid rgba(68, 141, 177, .35); border-radius: 8px; color: #9ab3d0; background: rgba(3, 27, 43, .52); font: inherit; text-align: left; cursor: pointer; }
.step-tab b { display: grid; place-items: center; width: 34px; height: 34px; border: 1px solid currentColor; border-radius: 50%; font-size: 12px; }
.step-tab span { display: grid; gap: 3px; font-weight: 800; }.step-tab small { color: inherit; font-size: 11px; font-weight: 400; }
.step-tab.active { border-color: #53d8fb; color: #fff; background: linear-gradient(115deg, rgba(4, 109, 155, 0.78), rgba(5, 62, 102, 0.64)); }
.step-connector { position: relative; flex: 0 1 32px; min-width: 18px; height: 2px; overflow: visible; background: rgba(98, 169, 201, .34); }
.step-connector::after { position: absolute; top: 50%; right: -1px; width: 6px; height: 6px; border-top: 1px solid #73b1ce; border-right: 1px solid #73b1ce; content: ""; transform: translateY(-50%) rotate(45deg); }
.diagnosis-page{--diagnosis-ink:#edfaff;--diagnosis-muted:#9bc1d1;--diagnosis-line:rgba(97,209,247,.3);--diagnosis-accent:#36d2fb;display:grid;grid-template-columns:minmax(0,1fr) 340px;gap:24px;min-height:calc(100vh - 76px);padding:24px;color:var(--diagnosis-ink);background:radial-gradient(circle at 7% 0%,rgba(16,155,204,.17),transparent 32rem),linear-gradient(135deg,#061923,#082633 48%,#061923)}
.diagnosis-core{min-width:0;padding:28px;border:1px solid var(--diagnosis-line);border-radius:28px;background:rgba(4,24,37,.82);box-shadow:0 22px 48px rgba(0,0,0,.22)}
.task-header{margin-bottom:20px}.task-header>span{display:inline-flex;padding:5px 12px;border:1px solid rgba(99,217,255,.52);border-radius:999px;color:#dff5ff;background:rgba(17,126,178,.3);font-size:12px;font-weight:800;letter-spacing:.05em}.task-header h1{margin:10px 0 0;color:#fff;font-size:30px;line-height:1.25}
.workbench{margin-top:24px;padding:24px;border:1px solid var(--diagnosis-line);border-radius:22px;background:rgba(2,20,32,.62)}
.workbench-top{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;padding-bottom:18px;border-bottom:1px solid rgba(97,209,247,.22)}.workbench-top p{margin:0;color:#6de0ff;font-size:12px;font-weight:900;letter-spacing:.12em;text-transform:uppercase}.workbench-top h2{margin:6px 0 0;color:#fff;font-size:25px;line-height:1.3}
.step-tabs{display:flex;align-items:center;gap:8px;margin:20px 0 26px;padding:14px;overflow-x:auto;border:1px solid rgba(67,183,227,.3);border-radius:18px;background:rgba(1,18,31,.58)}.step-tabs button{display:flex;flex:1 1 0;align-items:center;min-width:170px;gap:10px;padding:10px 12px;border:1px solid rgba(76,164,205,.38);border-radius:12px;color:#a6c8d8;background:rgba(3,27,43,.55);font:inherit;text-align:left;cursor:pointer;transition:.2s ease}.step-tabs button:hover{border-color:#72e8ff;color:#fff;background:rgba(8,92,131,.52);transform:translateY(-1px)}.step-tabs button:focus-visible{outline:2px solid #72e8ff;outline-offset:3px}.step-tabs button b{display:grid;flex:0 0 34px;width:34px;height:34px;place-items:center;border:1px solid rgba(101,176,208,.65);border-radius:50%;color:#bceeff;background:rgba(3,25,40,.86);font-size:12px}.step-tabs button span{display:grid;min-width:0;gap:3px;font-size:14px;font-weight:800}.step-tabs button small{color:#6db7d3;font-size:10px;font-weight:700;letter-spacing:.04em}.step-tabs button.active{border-color:#53d8fb;color:#fff;background:linear-gradient(135deg,rgba(7,156,207,.85),rgba(13,105,161,.92));box-shadow:inset 0 0 24px rgba(77,213,255,.22)}.step-tabs button.active b{border-color:#72e8ff;color:#fff;background:linear-gradient(135deg,#08b7e8,#1375d0);box-shadow:0 0 0 5px rgba(25,179,237,.14)}.step-tabs i{flex:0 0 18px;height:2px;background:rgba(86,198,235,.48)}
.lead{margin:0 0 20px;color:#d4edf7;font-size:15px;line-height:1.9}.section-block{margin-top:20px;padding:20px;border:1px solid rgba(97,209,247,.25);border-radius:16px;background:rgba(3,28,43,.42)}.section-heading{display:flex;align-items:flex-start;gap:12px;margin-bottom:16px}.section-heading>span{display:grid;flex:0 0 30px;width:30px;height:30px;place-items:center;border-radius:8px;color:#eaffff;background:linear-gradient(135deg,#11b8e4,#1373c4);font-size:14px;font-weight:900}.section-heading h3{margin:1px 0 4px;color:#f0fbff;font-size:18px}.section-heading p{margin:0;color:var(--diagnosis-muted);font-size:13px;line-height:1.65}
.secondary-button,.table-button,.upload-button,.submit-button{min-height:36px;padding:0 13px;border:1px solid rgba(99,203,239,.58);border-radius:8px;color:#e7fbff;background:rgba(12,111,153,.46);font:inherit;font-size:13px;font-weight:800;cursor:pointer;transition:.2s ease}.secondary-button:hover,.table-button:hover,.upload-button:hover{border-color:#7deaff;background:rgba(19,145,192,.62)}.submit-button{border-color:#72e8ff;background:linear-gradient(100deg,#0d80b7,#09638f)}.submit-button:hover{filter:brightness(1.12)}button:disabled{cursor:not-allowed;opacity:.45}.table-button.ghost{border-color:rgba(163,197,211,.36);color:#bed6e0;background:rgba(75,101,112,.25)}
.table-wrap{overflow-x:auto;border:1px solid rgba(99,183,222,.43);border-radius:12px}.funnel-table,.diagnosis-table,.sentiment-table{width:100%;border-collapse:collapse}.funnel-table{min-width:900px}.diagnosis-table{min-width:1060px}.sentiment-table{min-width:640px}.funnel-table th,.funnel-table td,.diagnosis-table th,.diagnosis-table td,.sentiment-table th,.sentiment-table td{padding:11px;border-right:1px solid rgba(99,183,222,.31);border-bottom:1px solid rgba(99,183,222,.31);vertical-align:middle;text-align:center}.funnel-table thead th,.diagnosis-table thead th,.sentiment-table thead th{color:#effaff;background:linear-gradient(100deg,#0a96cc,#087daf);font-size:14px;white-space:nowrap}.funnel-table tbody th,.sentiment-table tbody th{color:#dff8ff;background:rgba(8,72,99,.34);font-weight:800}.funnel-table td,.diagnosis-table td,.sentiment-table td{background:rgba(2,18,29,.34)}.funnel-table :deep(.el-input-number),.diagnosis-table :deep(.el-input-number),.sentiment-table :deep(.el-input-number),.diagnosis-table :deep(.el-select){width:100%}.unit{margin-left:5px;color:#74ddf6;font-size:12px;font-weight:800}.auto-count{color:#82e7ff;font-size:16px;font-weight:900}
.table-toolbar{display:flex;align-items:center;justify-content:space-between;gap:14px;margin:0 0 12px;color:var(--diagnosis-muted);font-size:13px}.table-toolbar>div,.actions,.diagnosis-materials__actions,.ab-materials__actions{display:flex;flex-wrap:wrap;gap:8px}.actions{justify-content:center;margin-top:24px}.actions .secondary-button,.actions .submit-button{min-width:142px;min-height:42px}
.chart-upload-area,.comment-import{display:grid;place-items:center;gap:10px;min-height:190px;padding:22px;border:1px dashed rgba(113,223,255,.66);border-radius:13px;background:linear-gradient(135deg,rgba(5,47,69,.66),rgba(4,23,35,.52));text-align:center}.chart-upload-area p,.comment-import>p{margin:0;color:var(--diagnosis-muted);font-size:13px}.upload-button{min-height:42px;padding:0 18px}.chart-preview{display:flex;align-items:center;gap:16px;width:100%;text-align:left}.chart-preview :deep(.el-image){width:180px;height:110px;border:1px solid var(--diagnosis-line);border-radius:8px}.chart-preview>div{display:grid;gap:7px}.chart-preview strong{color:#e8fbff}.chart-preview span{color:var(--diagnosis-muted);font-size:13px}.chart-preview button{width:max-content;padding:0;border:0;color:#ffadb1;background:transparent;font:inherit;cursor:pointer}
.data-preview,.word-cloud-card,.top-issues,.root-cause{margin-top:16px;padding:16px;border:1px solid rgba(99,183,222,.31);border-radius:12px;background:rgba(3,25,39,.58)}.data-preview>div,.word-cloud-title{display:flex;justify-content:space-between;gap:12px;color:#e6f9ff}.data-preview span,.word-cloud-title small{color:var(--diagnosis-muted);font-size:12px}.data-preview ul{max-height:130px;margin:12px 0 0;padding-left:20px;overflow:auto;color:#c8e5f1;line-height:1.7;text-align:left}.word-cloud{height:280px}.top-issues>p,.root-cause>p{margin:0 0 13px;color:#c8e5f1;line-height:1.7}.top-issues>div{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px}.top-issues label{display:grid;gap:7px;color:#8bdff5;font-weight:800}.sentiment-actions{display:flex;align-items:center;gap:14px;margin-bottom:16px}.sentiment-actions>span{color:var(--diagnosis-muted);font-size:13px}.visual-section,.diagnosis-section{margin-top:20px}
.diagnosis-page :deep(.el-input__wrapper),.diagnosis-page :deep(.el-textarea__inner){background:rgba(1,15,25,.78);box-shadow:0 0 0 1px rgba(94,213,245,.28) inset}.diagnosis-page :deep(.el-input__inner),.diagnosis-page :deep(.el-textarea__inner){color:#f0fbff}.diagnosis-page :deep(.el-textarea__inner){line-height:1.65}.visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;clip-path:inset(50%)}
/* 标准实训页步骤条:仅当前步骤使用胶囊底色,其余步骤保持轻量。 */
.workbench .step-tabs{gap:14px;margin:20px 0 26px;padding:6px 14px;border-color:rgba(56,172,222,.5);border-radius:999px;background:rgba(1,19,32,.72)}
.workbench .step-tabs .step-tab{flex:1 1 0;min-width:0;min-height:48px;gap:12px;padding:7px 14px;border:1px solid transparent;border-radius:999px;background:transparent;color:#c0d4e2;transform:none}
.workbench .step-tabs .step-tab:hover{border-color:rgba(82,207,247,.45);background:rgba(9,78,111,.3);transform:none}
.workbench .step-tabs .step-marker{flex:0 0 38px;width:38px;height:38px;border-color:rgba(87,182,220,.66);color:#b7d2e2;background:transparent}
.workbench .step-tabs .step-copy{gap:2px}.workbench .step-tabs .step-copy b{display:block;flex:none;width:auto;height:auto;overflow:visible;border:0;border-radius:0;color:inherit;background:transparent;box-shadow:none;font-size:14px;font-weight:800;text-overflow:clip;white-space:nowrap}.workbench .step-tabs .step-copy small{color:#74b8d5;font-size:10px;line-height:1.2}
.workbench .step-tabs .step-tab.active{border-color:#53d8fb;background:linear-gradient(100deg,rgba(9,164,214,.92),rgba(10,105,163,.9));box-shadow:inset 0 0 18px rgba(114,232,255,.2)}
.workbench .step-tabs .step-tab.active .step-marker{border-color:#72e8ff;background:linear-gradient(135deg,#10bee9,#1579d2);box-shadow:0 0 0 4px rgba(43,198,246,.14),0 0 15px rgba(46,208,255,.52)}
.workbench .step-tabs .step-tab.complete .step-marker{border-color:#79eee4;color:#083342;background:#6deee1;box-shadow:none}
.workbench .step-tabs .step-connector{flex:0 0 30px;width:30px;height:1px;margin:0;background:rgba(94,193,229,.58)}
.workbench .step-tabs .step-connector::after{width:8px;height:8px;border-color:#73c8e7}
.sentiment-table td:last-child{white-space:nowrap}.sentiment-table td:last-child :deep(.el-input-number){display:inline-flex;width:calc(100% - 32px);vertical-align:middle}.sentiment-table td:last-child .unit{display:inline-flex;align-items:center;height:32px;margin-left:6px;vertical-align:middle}
@media(max-width:1100px){.diagnosis-page{grid-template-columns:1fr}}@media(max-width:720px){.diagnosis-page{padding:12px}.diagnosis-core{padding:16px;border-radius:20px}.workbench{padding:16px}.workbench-top,.table-toolbar,.sentiment-actions{align-items:stretch;flex-direction:column}.step-tabs{align-items:stretch;flex-direction:column}.step-tabs button{flex:0 0 auto;width:100%}.step-tabs i{flex:0 0 12px;width:2px;height:12px;margin-left:17px}.diagnosis-materials__item,.ab-materials{align-items:stretch;flex-direction:column}.top-issues>div{grid-template-columns:1fr}.chart-preview{align-items:flex-start;flex-direction:column}.chart-preview :deep(.el-image){width:100%;height:170px}}
</style>

@ -65,7 +65,6 @@
<tfoot><tr><th colspan="2">当前渠道预算占比合计</th><td :class="{ 'total-error': budgetPercentageTotal !== 100 }">{{ displayMoney(budgetPercentageTotal) }}%</td><td :class="{ 'total-error': rowBudgetTotal !== normalizedTotalBudget }">{{ displayMoney(rowBudgetTotal) }}</td><td colspan="3" class="table-note">预算金额由总预算 × 预算占比自动计算</td></tr></tfoot>
</table>
</div>
<p class="hint">预算占比由学生填写,各渠道占比合计必须等于 100%。预算金额由系统按“试错预算总额 × 占比”自动计算;每个渠道的冷启动期、增长期和稳定期投放预算合计必须等于该渠道预算金额。默认显示一行,保存并进入下一步时至少填写两行、最多五行;渠道仅可从产品发布渠道评估已填写的渠道中选择。</p>
<p v-if="!availableChannels.length" class="source-warning">尚未读取到产品发布渠道评估的渠道数据。请先完成并保存该模块的渠道数据。</p>
<footer class="footer-actions"><el-button :loading="saving" @click="saveBudgetDraft">保存当前进度</el-button><el-button type="primary" :loading="validating" @click="validateBudgetAndContinue">保存并进入下一步 <el-icon><ArrowRight /></el-icon></el-button></footer>
</section>
@ -89,7 +88,6 @@
</tbody>
</table>
</div>
<p class="hint">渠道由第一步自动同步,不可手动编辑;冷启动期、增长期、稳定期固定。每个“阶段 × 渠道”必须填写两项不重复的投放策略。策略池是参考内容,也可在下拉框中输入符合渠道实际情况的策略。</p>
<el-collapse class="strategy-reference">
<el-collapse-item title="查看策略选项池参考" name="strategy-pool">
<div class="reference-table-wrap">
@ -131,7 +129,6 @@
</tbody>
</table>
</div>
<p class="hint">该表渠道与第一步一致,合计行为固定最后一行。点击率 = 点击量 ÷ 曝光量 × 100%;实际 ROI = 成交额 ÷ 推广花费。合计行的曝光量、点击量、成交额和推广花费必须为各渠道数据合计,点击率和实际 ROI 均按合计值重新计算。</p>
</section>
<footer class="footer-actions"><el-button @click="activeStep = 2">上一步</el-button><el-button :loading="saving" @click="savePerformanceDraft">保存当前进度</el-button><el-button type="primary" :loading="validating" @click="validatePerformance">保存并校验 <el-icon><ArrowRight /></el-icon></el-button></footer>
</section>
@ -157,7 +154,6 @@
</tbody>
</table>
</div>
<p class="hint">渠道和 ROI 从前序步骤自动同步。ROI 不低于保本 ROI 即为达标;学生自行选择达标结果,系统提交时校验。达标渠道的问题诊断和优化动作可不填,未达标渠道必须填写。</p>
<section class="decision-section">
<div class="subsection-title">(二)优化决策</div>
<p>结合整体 ROI 与各渠道达标情况,总结本方案是否需要调整、调整方向,以及优先调整的渠道。</p>

Loading…
Cancel
Save