Merge remote-tracking branch 'origin/master' into dev-QQq

fix/exam-question-access-material-excel-delete-draft
qinzhenpen 2 weeks ago
commit df71737209

@ -532,7 +532,7 @@ onMounted(() => {
echo();
});
</script>
<style lang="scss" scoped>
<style scoped lang="scss">
/* ============================================================
* 教育蓝极简 · 卡拉杰克模型分析页
* ============================================================ */

@ -1,10 +1,11 @@
<template>

<template>
<div class="product-process student-training-shell">
<main class="process-core">
<section class="task-header">
<div class="task-head-main">
<h1 class="task-title">{{ taskTitle }}</h1>
<p class="task-brief">
<div class="task-brief">
<span class="task-brief-text">{{ taskBackground }}</span>
<button type="button" class="task-brief-toggle" @click="briefOpen = !briefOpen">
{{ briefOpen ? "收起" : "展开目标与要求" }}
@ -17,54 +18,49 @@
<b>实训要求</b>
<p>{{ taskRequirement }}</p>
</span>
</p>
</div>
</div>
<div class="workbench-head workbench-head--actions-only">
<div class="workbench-actions">
<TrainingMaterialButton
:material-name="taskConfig?.materialName"
:material-url="taskConfig?.materialUrl"
:training-data-name="taskConfig?.trainingDataName"
:training-data-url="taskConfig?.trainingDataUrl"
:guide-manual-name="taskConfig?.guideManualName"
:guide-manual-url="taskConfig?.guideManualUrl"
:ai-manual-name="taskConfig?.aiManualName"
:ai-manual-url="taskConfig?.aiManualUrl"
/>
<button type="button" class="outline-action" @click="exportOutcome"><el-icon><Download /></el-icon>导出实训成果</button>
<TrainingMaterialButton :material-name="taskConfig?.materialName" :material-url="taskConfig?.materialUrl" :training-data-name="taskConfig?.trainingDataName" :training-data-url="taskConfig?.trainingDataUrl" :guide-manual-name="taskConfig?.guideManualName" :guide-manual-url="taskConfig?.guideManualUrl" :ai-manual-name="taskConfig?.aiManualName" :ai-manual-url="taskConfig?.aiManualUrl" />
<button type="button" class="outline-action" @click="exportOutcome">
<el-icon><Download /></el-icon>
导出实训成果
</button>
</div>
</div>
</section>
<section class="process-workbench">
<nav v-if="trainingSteps.length" class="step-indicator" aria-label="产品开发主要流程步骤">
<button
v-for="(step, index) in trainingSteps"
:key="`${step}-${index}`"
type="button"
:class="['step-tab', { active: currentStep === index + 1, complete: currentStep > index + 1 }]"
:aria-label="`切换至第 ${index + 1} 步:${step}`"
:aria-current="currentStep === index + 1 ? 'step' : undefined"
:title="step"
@click="currentStep = index + 1"
>
<button v-for="(step, index) in trainingSteps" :key="`${step}-${index}`" type="button" :class="['step-tab', { active: currentStep === index + 1, complete: currentStep > index + 1 }]" :aria-label="`切换至第 ${index + 1} 步:${step}`" :aria-current="currentStep === index + 1 ? 'step' : undefined" :title="step" @click="currentStep = index + 1">
<span class="step-bar" aria-hidden="true"></span>
<span class="step-no" aria-hidden="true">{{ String(index + 1).padStart(2, '0') }}</span>
<span class="step-no" aria-hidden="true">{{ String(index + 1).padStart(2, "0") }}</span>
<span class="step-name">{{ step }}</span>
</button>
</nav>
</section>
<section class="process-step-card">
<template v-if="currentStep === 1">
<div class="step-title-row">
<div><p class="section-eyebrow">Step 01</p><h3>{{ trainingSteps[0] }}</h3></div>
<div>
<p class="section-eyebrow">Step 01</p>
<h3>{{ trainingSteps[0] }}</h3>
</div>
<span class="step-chip">关键任务识别</span>
</div>
<p class="process-intro">互联网产品开发在拆解任务之前,团队必须先明确做什么样的产品、开发过程中有哪些卡点。这是后续排期的前提。请判断哪些开发环节需要特别关注:关键任务会影响全项目进度与最终落地效果,非关键任务可在核心任务约束下灵活协调。</p>
<div class="training-table-wrap">
<table class="process-table">
<thead><tr><th>阶段</th><th>任务编号</th><th>具体工作任务</th><th>是否关键任务</th></tr></thead>
<thead>
<tr>
<th>阶段</th>
<th>任务编号</th>
<th>具体工作任务</th>
<th>是否关键任务</th>
</tr>
</thead>
<tbody>
<tr v-for="row in processRows" :key="row.taskNo" :class="feedbackClass(row.taskNo)">
<td class="stage-cell">{{ row.stage }}</td>
@ -83,15 +79,24 @@
</div>
<p v-if="checkResult" class="classification-summary" :class="{ 'is-success': checkResult.allCorrect }">本次判题:{{ checkResult.correctCount }} / {{ checkResult.totalCount }} 项分类正确</p>
<div class="task-actions">
<button type="button" class="btn-nav btn-submit" :disabled="checking" @click="saveAndGoToStepTwo">{{ checking ? '判题中…' : '保存并进入第 2 步' }}</button>
<button type="button" class="btn-nav" :disabled="checking" @click="resetTraining"><el-icon><RefreshLeft /></el-icon>清空填写</button>
<button type="button" class="btn-nav btn-submit" :disabled="checking" @click="saveAndGoToStepTwo">{{ checking ? "判题中…" : "保存并进入第 2 步" }}</button>
<button type="button" class="btn-nav" :disabled="checking" @click="resetTraining">
<el-icon><RefreshLeft /></el-icon>
清空填写
</button>
</div>
</template>
<template v-else-if="currentStep === 2">
<div class="step-title-row">
<div><p class="section-eyebrow">Step 02</p><h3>{{ trainingSteps[1] }}</h3></div>
<div>
<p class="section-eyebrow">Step 02</p>
<h3>{{ trainingSteps[1] }}</h3>
</div>
<div class="step-title-actions">
<button type="button" class="outline-action schedule-export-action" @click="exportScheduleList"><el-icon><Download /></el-icon>导出列表</button>
<button type="button" class="outline-action schedule-export-action" @click="exportScheduleList">
<el-icon><Download /></el-icon>
导出列表
</button>
<span class="step-chip">项目排期练习</span>
</div>
</div>
@ -99,13 +104,23 @@
<p v-if="!hasCompletedStepOne" class="step-warning">请先完成第 1 步并判题通过;关键任务结果会自动带入本表。</p>
<div class="training-table-wrap">
<table class="process-table schedule-table">
<thead><tr><th>阶段</th><th>任务编号</th><th>具体工作任务</th><th>是否关键任务</th><th>开始时间</th><th>完成时间</th><th>项目依赖关系</th></tr></thead>
<thead>
<tr>
<th>阶段</th>
<th>任务编号</th>
<th>具体工作任务</th>
<th>是否关键任务</th>
<th>开始时间</th>
<th>完成时间</th>
<th>项目依赖关系</th>
</tr>
</thead>
<tbody>
<tr v-for="row in processRows" :key="row.taskNo">
<td class="stage-cell">{{ row.stage }}</td>
<td class="task-no-cell">{{ row.taskNo }}</td>
<td class="task-cell">{{ row.task }}</td>
<td class="classification-cell">{{ row.classification || '—' }}</td>
<td class="classification-cell">{{ row.classification || "—" }}</td>
<td><input v-model="row.startDate" type="date" :disabled="!hasCompletedStepOne || checking" /></td>
<td><input v-model="row.endDate" type="date" :disabled="!hasCompletedStepOne || checking" /></td>
<td><input v-model.trim="row.dependency" class="dependency-input" :disabled="!hasCompletedStepOne || checking" placeholder="示例:开始-开始(SS)" maxlength="100" /></td>
@ -114,13 +129,19 @@
</table>
</div>
<div class="task-actions">
<button type="button" class="btn-nav btn-submit" :disabled="checking || !hasCompletedStepOne" @click="saveAndGoToStepThree">{{ checking ? '校验中…' : '保存并进入第 3 步' }}</button>
<button type="button" class="btn-nav" :disabled="checking" @click="resetStepTwo"><el-icon><RefreshLeft /></el-icon>清空本步</button>
<button type="button" class="btn-nav btn-submit" :disabled="checking || !hasCompletedStepOne" @click="saveAndGoToStepThree">{{ checking ? "校验中…" : "保存并进入第 3 步" }}</button>
<button type="button" class="btn-nav" :disabled="checking" @click="resetStepTwo">
<el-icon><RefreshLeft /></el-icon>
清空本步
</button>
</div>
</template>
<template v-else-if="currentStep === 3">
<div class="step-title-row">
<div><p class="section-eyebrow">Step 03</p><h3>{{ trainingSteps[2] }}</h3></div>
<div>
<p class="section-eyebrow">Step 03</p>
<h3>{{ trainingSteps[2] }}</h3>
</div>
<span class="step-chip">甘特图绘制</span>
</div>
<p class="process-intro">根据上表数据,使用工具绘制甘特图,并上传。</p>
@ -129,7 +150,7 @@
<label class="gantt-upload-trigger" :class="{ disabled: !stepTwoPassed || uploadingGantt }">
<input type="file" accept="image/png,image/jpeg,image/webp,image/gif" :disabled="!stepTwoPassed || uploadingGantt" @change="uploadGanttImage" />
<el-icon><Upload /></el-icon>
<strong>{{ uploadingGantt ? '上传中…' : ganttImage.url ? '替换甘特图' : '上传甘特图' }}</strong>
<strong>{{ uploadingGantt ? "上传中…" : ganttImage.url ? "替换甘特图" : "上传甘特图" }}</strong>
<span>支持 PNG、JPG、JPEG、WEBP、GIF</span>
</label>
<figure v-if="ganttImage.url" class="gantt-preview">
@ -140,49 +161,73 @@
<section class="gantt-guide">
<h4>推荐工具如下,自行选择作图工具:</h4>
<ol>
<li>Excel/WPS表格,利用堆积条形图制作,可参考《实训操作说明》完成。<button type="button" @click="downloadPracticeGuide">下载实训操作说明</button></li>
<li>下载Excel模板,填写关键信息,完成甘特图绘制。<button type="button" @click="downloadGanttTemplate">下载甘特图绘制模板</button></li>
<li>
Excel/WPS表格,利用堆积条形图制作,可参考《实训操作说明》完成。
<button type="button" @click="downloadPracticeGuide">下载实训操作说明</button>
</li>
<li>
下载Excel模板,填写关键信息,完成甘特图绘制。
<button type="button" @click="downloadGanttTemplate">下载甘特图绘制模板</button>
</li>
<li>导出步骤二信息表,上传到通用AI绘图模型,通过大模型绘制甘特图。参考提示词:请根据产品WBS任务拆解与排期表生成甘特图用于后续项目管理。</li>
</ol>
</section>
<div class="task-actions">
<button type="button" class="btn-nav btn-submit" :disabled="uploadingGantt || !stepTwoPassed" @click="saveAndGoToStepFour">保存并进入第 4 步<el-icon><ArrowRight /></el-icon></button>
<button type="button" class="btn-nav" :disabled="uploadingGantt" @click="resetStepThree"><el-icon><RefreshLeft /></el-icon>清空本步</button>
<button type="button" class="btn-nav btn-submit" :disabled="uploadingGantt || !stepTwoPassed" @click="saveAndGoToStepFour">
保存并进入第 4 步
<el-icon><ArrowRight /></el-icon>
</button>
<button type="button" class="btn-nav" :disabled="uploadingGantt" @click="resetStepThree">
<el-icon><RefreshLeft /></el-icon>
清空本步
</button>
</div>
</template>
<template v-else-if="currentStep === 4">
<div class="step-title-row">
<div><p class="section-eyebrow">Step 04</p><h3>{{ trainingSteps[3] }}</h3></div>
<div>
<p class="section-eyebrow">Step 04</p>
<h3>{{ trainingSteps[3] }}</h3>
</div>
<span class="step-chip">并行协同复盘</span>
</div>
<p v-if="!stepThreePassed" class="step-warning">请先上传并保存第 3 步甘特图,再完成本步复盘。</p>
<section class="process-reflection" :class="{ disabled: !stepThreePassed }">
<label class="reflection-field">
<span>(1)观察甘特图,思考电商运营在产品开发过程中,可以提前介入同步完成哪些工作,以缩短运营周期、提高效率。(如“预热蓄水”“内容种草”等)</span>
<textarea v-model.trim="stepFourForm.parallelWorkAnalysis" :disabled="!stepThreePassed || checking" placeholder="请结合甘特图说明可同步推进的电商运营工作、介入节点及预期效率提升" />
<textarea v-model.trim="stepFourForm.parallelWorkAnalysis" :disabled="!stepThreePassed || checking" placeholder="请结合甘特图说明可同步推进的电商运营工作、介入节点及预期效率提升"></textarea>
</label>
<div class="reflection-question">(2)在互联网产品开发管理中,你认为哪些环节可以借助AI工具提升效率?请列举至少2个具体应用场景。</div>
<div class="scenario-grid">
<label class="reflection-field"><span>场景1:</span><textarea v-model.trim="stepFourForm.aiScenarioOne" :disabled="!stepThreePassed || checking" placeholder="例如:使用 AI 汇总竞品评价与用户需求,辅助生成PRD初稿" /></label>
<label class="reflection-field"><span>场景2:</span><textarea v-model.trim="stepFourForm.aiScenarioTwo" :disabled="!stepThreePassed || checking" placeholder="例如:使用 AI 生成电商详情页文案、主图脚本或运营素材" /></label>
<label class="reflection-field">
<span>场景1:</span>
<textarea v-model.trim="stepFourForm.aiScenarioOne" :disabled="!stepThreePassed || checking" placeholder="例如:使用 AI 汇总竞品评价与用户需求,辅助生成PRD初稿"></textarea>
</label>
<label class="reflection-field">
<span>场景2:</span>
<textarea v-model.trim="stepFourForm.aiScenarioTwo" :disabled="!stepThreePassed || checking" placeholder="例如:使用 AI 生成电商详情页文案、主图脚本或运营素材"></textarea>
</label>
</div>
</section>
<div class="task-actions">
<button type="button" class="btn-nav btn-submit" :disabled="checking || !stepThreePassed" @click="submitTraining">提交任务<el-icon><CircleCheck /></el-icon></button>
<button type="button" class="btn-nav" :disabled="checking" @click="resetStepFour"><el-icon><RefreshLeft /></el-icon>清空本步</button>
<button type="button" class="btn-nav btn-submit" :disabled="checking || !stepThreePassed" @click="submitTraining">
提交任务
<el-icon><CircleCheck /></el-icon>
</button>
<button type="button" class="btn-nav" :disabled="checking" @click="resetStepFour">
<el-icon><RefreshLeft /></el-icon>
清空本步
</button>
</div>
</template>
<div v-else class="step-placeholder"><p>第 {{ currentStep }} 步内容待配置</p><span>第 1 步答案已保存,可从上方步骤栏返回查看。</span></div>
</section>
<div v-else class="step-placeholder">
<p>第 {{ currentStep }} 步内容待配置</p>
<span>第 1 步答案已保存,可从上方步骤栏返回查看。</span>
</div>
</section>
</main>
<TrainingAiSidebar
study-tip="请根据产品开发流程中任务对总进度和最终落地效果的影响,判断其是否为关键任务。"
reference-title="关键任务判断提示"
reference-text="· 关键任务:直接影响产品定义、核心技术验证、量产交付或上市节奏<br />· 非关键任务:可在核心任务约束下进行资源协调,不直接阻塞整体排期"
:progress-items="progressItems"
/>
<TrainingAiSidebar study-tip="请根据产品开发流程中任务对总进度和最终落地效果的影响,判断其是否为关键任务。" reference-title="关键任务判断提示" reference-text="· 关键任务:直接影响产品定义、核心技术验证、量产交付或上市节奏<br />· 非关键任务:可在核心任务约束下进行资源协调,不直接阻塞整体排期" :progress-items="progressItems"></TrainingAiSidebar>
</div>
</template>
@ -207,11 +252,25 @@ const GANTT_TEMPLATE_NAME = "3.项目进度管理表-项目进度甘特图模版
const CLASSIFICATION_OPTIONS = ["关键任务", "非关键任务"];
const DEFAULT_STEPS = ["产品开发关键任务识别", "任务依赖关系分析", "开发排期制定", "流程复盘优化"];
const PROCESS_TASKS = [
["需求锚定", "1.1", "竞品调研与用户需求分析"], ["", "1.2", "产品概念确认与核心卖点提炼"],
["产品定义", "2.1", "产品需求文档(PRD)撰写与评审"], ["", "2.2", "外观ID设计"], ["", "2.3", "功能规格确认"],
["开发验证", "3.1", "硬件方案选型与原理图设计"], ["", "3.2", "软件架构及软硬件协同设计"], ["", "3.3", "结构设计评审"], ["", "3.4", "功能手板制作与外观评审"], ["", "3.5", "工程验证测试与设计验证测试"], ["", "3.6", "PVT小批量试产"],
["供应链对接", "4.1", "供应商评估与选定"], ["", "4.2", "模具正式开模与试模修模"], ["", "4.3", "产品认证及质量检测"], ["", "4.4", "BOM成本核算与采购合同签订"],
["上市推广", "5.1", "产品量产及入库"], ["", "5.2", "电商详情页全渠道上线"], ["", "5.3", "正式开售与首销爆发"], ["", "5.4", "首批用户评价监控与差评预警"],
["需求锚定", "1.1", "竞品调研与用户需求分析"],
["", "1.2", "产品概念确认与核心卖点提炼"],
["产品定义", "2.1", "产品需求文档(PRD)撰写与评审"],
["", "2.2", "外观ID设计"],
["", "2.3", "功能规格确认"],
["开发验证", "3.1", "硬件方案选型与原理图设计"],
["", "3.2", "软件架构及软硬件协同设计"],
["", "3.3", "结构设计评审"],
["", "3.4", "功能手板制作与外观评审"],
["", "3.5", "工程验证测试与设计验证测试"],
["", "3.6", "PVT小批量试产"],
["供应链对接", "4.1", "供应商评估与选定"],
["", "4.2", "模具正式开模与试模修模"],
["", "4.3", "产品认证及质量检测"],
["", "4.4", "BOM成本核算与采购合同签订"],
["上市推广", "5.1", "产品量产及入库"],
["", "5.2", "电商详情页全渠道上线"],
["", "5.3", "正式开售与首销爆发"],
["", "5.4", "首批用户评价监控与差评预警"],
];
const defaultTask = {
title: "产品开发主要流程",
@ -266,13 +325,20 @@ function parseArray(value, fallback) {
const parsed = JSON.parse(value);
return Array.isArray(parsed) && parsed.length ? parsed.map((item) => String(item?.name ?? item).trim()).filter(Boolean) : fallback;
} catch (error) {
const list = String(value).split(/[;;|]/).map((item) => item.trim()).filter(Boolean);
const list = String(value)
.split(/[;;|]/)
.map((item) => item.trim())
.filter(Boolean);
return list.length ? list : fallback;
}
}
async function loadTaskConfig() {
try { taskConfig.value = (await getTrainingTaskByKey(TASK_KEY))?.data || null; } catch (error) { taskConfig.value = null; }
try {
taskConfig.value = (await getTrainingTaskByKey(TASK_KEY))?.data || null;
} catch (error) {
taskConfig.value = null;
}
}
async function loadSavedAnswer() {
@ -379,11 +445,17 @@ function buildStepFourOutcome() {
return { ...stepFourForm.value };
}
function buildDraft() { return { step1: buildOutcome(), step2: buildStepTwoOutcome(), step3: buildStepThreeOutcome(), step4: buildStepFourOutcome(), currentStep: currentStep.value }; }
function buildDraft() {
return { step1: buildOutcome(), step2: buildStepTwoOutcome(), step3: buildStepThreeOutcome(), step4: buildStepFourOutcome(), currentStep: currentStep.value };
}
function persistDraft() {
if (!draftReady.value) return;
try { localStorage.setItem(getLocalDraftKey(), JSON.stringify(buildDraft())); } catch (error) { /* Browser draft is optional. */ }
try {
localStorage.setItem(getLocalDraftKey(), JSON.stringify(buildDraft()));
} catch (error) {
/* Browser draft is optional. */
}
}
function loadDraft() {
@ -397,11 +469,17 @@ function loadDraft() {
if (draft?.step4) restoreStepFourOutcome(draft.step4);
if (Number(draft?.currentStep) > 0) currentStep.value = Number(draft.currentStep);
}
} catch (error) { clearDraft(); }
} catch (error) {
clearDraft();
}
}
function clearDraft() {
try { localStorage.removeItem(getLocalDraftKey()); } catch (error) { /* Ignore storage failures. */ }
try {
localStorage.removeItem(getLocalDraftKey());
} catch (error) {
/* Ignore storage failures. */
}
}
function feedbackClass(taskNo) {
@ -602,9 +680,7 @@ function exportScheduleList() {
项目依赖关系: row.dependency,
}));
const worksheet = XLSX.utils.json_to_sheet(rows);
worksheet["!cols"] = [
{ wch: 12 }, { wch: 10 }, { wch: 28 }, { wch: 14 }, { wch: 14 }, { wch: 14 }, { wch: 22 },
];
worksheet["!cols"] = [{ wch: 12 }, { wch: 10 }, { wch: 28 }, { wch: 14 }, { wch: 14 }, { wch: 14 }, { wch: 22 }];
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, "WBS排期表");
XLSX.writeFile(workbook, "产品开发WBS任务拆解与排期表.xlsx");
@ -712,10 +788,6 @@ function exportOutcome() {
min-width: 0;
}
.task-title {
margin: 0;
color: var(--student-text);
@ -820,7 +892,11 @@ function exportOutcome() {
font-weight: 600;
line-height: 1;
cursor: pointer;
transition: border-color 0.16s ease, background 0.16s ease, color 0.16s ease, box-shadow 0.16s ease;
transition:
border-color 0.16s ease,
background 0.16s ease,
color 0.16s ease,
box-shadow 0.16s ease;
&:hover {
border-color: #2563eb;
@ -1046,7 +1122,12 @@ tr.is-incorrect .classification-feedback {
line-height: 1;
text-shadow: none;
cursor: pointer;
transition: transform 0.16s ease, border-color 0.16s ease, color 0.16s ease, background 0.16s ease, box-shadow 0.16s ease;
transition:
transform 0.16s ease,
border-color 0.16s ease,
color 0.16s ease,
background 0.16s ease,
box-shadow 0.16s ease;
}
.btn-nav:hover:not(:disabled) {
@ -1119,10 +1200,18 @@ tr.is-incorrect .classification-feedback {
}
/* 排期表列宽 */
.schedule-table { min-width: 1020px; }
.schedule-table .stage-cell { width: 7%; }
.schedule-table .task-no-cell { width: 7%; }
.schedule-table .task-cell { width: 18%; }
.schedule-table {
min-width: 1020px;
}
.schedule-table .stage-cell {
width: 7%;
}
.schedule-table .task-no-cell {
width: 7%;
}
.schedule-table .task-cell {
width: 18%;
}
.classification-cell {
width: 12%;
@ -1135,14 +1224,20 @@ tr.is-incorrect .classification-feedback {
.schedule-table th:nth-child(5),
.schedule-table td:nth-child(5),
.schedule-table th:nth-child(6),
.schedule-table td:nth-child(6) { width: 18%; }
.schedule-table .dependency-input { min-width: 0; }
.schedule-table td:nth-child(6) {
width: 18%;
}
.schedule-table .dependency-input {
min-width: 0;
}
.schedule-table .dependency-input::placeholder {
color: #9ca3af;
opacity: 1;
}
.schedule-table th:nth-child(7),
.schedule-table td:nth-child(7) { width: 20%; }
.schedule-table td:nth-child(7) {
width: 20%;
}
.schedule-table input:disabled {
cursor: not-allowed;
opacity: 0.6;
@ -1268,7 +1363,9 @@ tr.is-incorrect .classification-feedback {
font-weight: 700;
line-height: 1.5;
cursor: pointer;
transition: border-color 0.16s ease, background 0.16s ease;
transition:
border-color 0.16s ease,
background 0.16s ease;
}
.gantt-guide button:hover {
@ -1443,7 +1540,9 @@ tr.is-incorrect .classification-feedback {
}
/* 不显示顶部引导线 */
.step-bar { display: none !important; }
.step-bar {
display: none !important;
}
/* 编号小徽章 */
.step-no {
@ -1464,10 +1563,14 @@ tr.is-incorrect .classification-feedback {
line-height: 1 !important;
transition: all 0.16s ease !important;
&::after { display: none !important; }
&::after {
display: none !important;
}
}
.step-name { color: inherit !important; }
.step-name {
color: inherit !important;
}
/* 未到达项 hover:教育蓝边浅底 */
&:hover:not(.active):not(.complete) {
@ -1476,7 +1579,10 @@ tr.is-incorrect .classification-feedback {
color: #2563eb !important;
z-index: 1 !important;
.step-no { background: #2563eb !important; color: #ffffff !important; }
.step-no {
background: #2563eb !important;
color: #ffffff !important;
}
}
/* 已完成:绿色编号徽章 */
@ -1484,14 +1590,20 @@ tr.is-incorrect .classification-feedback {
border-color: #e5e7eb !important;
color: #374151 !important;
.step-no { background: rgba(43, 164, 113, 0.12) !important; color: #16915f !important; }
.step-no {
background: rgba(43, 164, 113, 0.12) !important;
color: #16915f !important;
}
&:hover {
border-color: #2ba471 !important;
background: #f0fdf6 !important;
z-index: 1 !important;
.step-no { background: #2ba471 !important; color: #ffffff !important; }
.step-no {
background: #2ba471 !important;
color: #ffffff !important;
}
}
}
@ -1503,7 +1615,10 @@ tr.is-incorrect .classification-feedback {
font-weight: 700 !important;
z-index: 2 !important;
.step-no { background: #ffffff !important; color: #2563eb !important; }
.step-no {
background: #ffffff !important;
color: #2563eb !important;
}
&:hover {
background: #1d4ed8 !important;
@ -1537,7 +1652,11 @@ tr.is-incorrect .classification-feedback {
background: #ffffff !important;
font-size: 13px !important;
font-weight: 600 !important;
transition: border-color 0.16s ease, background 0.16s ease, color 0.16s ease, box-shadow 0.16s ease !important;
transition:
border-color 0.16s ease,
background 0.16s ease,
color 0.16s ease,
box-shadow 0.16s ease !important;
&:hover {
border-color: #2563eb !important;
@ -1590,7 +1709,11 @@ tr.is-incorrect .classification-feedback {
font-size: 13px !important;
font-weight: 600 !important;
line-height: 1 !important;
transition: border-color 0.16s ease, background 0.16s ease, color 0.16s ease, box-shadow 0.16s ease !important;
transition:
border-color 0.16s ease,
background 0.16s ease,
color 0.16s ease,
box-shadow 0.16s ease !important;
}
#app .studentLayout section.app-main:has(.product-process) .workbench-actions > button.outline-action:hover {
@ -1660,7 +1783,11 @@ tr.is-incorrect .classification-feedback {
font-size: 13px !important;
font-weight: 700 !important;
line-height: 1 !important;
transition: transform 0.16s ease, border-color 0.16s ease, background 0.16s ease, box-shadow 0.16s ease !important;
transition:
transform 0.16s ease,
border-color 0.16s ease,
background 0.16s ease,
box-shadow 0.16s ease !important;
}
#app .studentLayout section.app-main:has(.product-process) .task-actions > button .el-icon {

Loading…
Cancel
Save