diff --git a/.trae/skills/ink-training-theme/SKILL.md b/.trae/skills/ink-training-theme/SKILL.md
index c23edc5..9e74b83 100644
--- a/.trae/skills/ink-training-theme/SKILL.md
+++ b/.trae/skills/ink-training-theme/SKILL.md
@@ -15,18 +15,176 @@ description: Apply ink-minimal style to student training pages (Vue 3 + Element
## 边界
- 只动 template 结构与样式;script 业务逻辑(判题/保存/草稿/上传/接口)一律不碰。
-- 仅作用于当前页根类,不得影响其他页面;不修改全局 `student-training.scss`。
+- 仅作用于当前页根类,不得影响其他页面;**但必须同步修改全局
+ `src/assets/styles/student-training.scss`**(见下一节「全局 !important 覆盖
+ 机制」),否则页面样式会被带 `!important` 的旧蓝色规则碾压。
- 表格类内容区不铺任何底色,对错只靠文字反馈,不靠行背景。
+## 选择器边界(防污染教师端/超管端)
+
+### 必须遵守的规则
+
+**改全局 `student-training.scss` 时,所有视觉样式选择器必须以以下锚点开头
+之一——否则会漏到教师端或超管端的普通页面:**
+
+| 安全锚点 | 覆盖范围 |
+|----------|----------|
+| `.student-training-shell` | **实训页面专用容器类**(学生端 + 教师端预览共用,是实训风格) |
+| `#app .studentLayout .app-main:has(.xxx-page)` | 精确限定在学生端某个路由页面 |
+| `#app .teacherLayout .app-main:has(.student-training-shell)` | 限定在教师端预览实训页面(如需差异化) |
+
+**危险锚点(绝对不能用)**:
+
+| 危险选择器 | 后果 |
+|------------|------|
+| `.el-button { ... }` | 污染全站所有 Element Plus 按钮(教师端/超管端/学生端都会被改) |
+| `.btn-primary { ... }` | 同上 |
+| `.task-actions { ... }` (裸写,不带前置锚) | 教师端/超管端如果也有 `.task-actions` 类就会被改 |
+| `button.outline-action { ... }` (裸写) | 全站所有 outline-action 按钮被改 |
+| `:root { --xxx: ... }` (视觉令牌) | CSS 自定义属性会全局继承,但字体/字号/间距这类无害 |
+
+**项目实际验证**:
+- 教师端 `views/teacher/**/*.vue` 和超管端 `views/admin/**/*.vue` 均不直接使用
+ `.student-training-shell` 类
+- 教师端预览学生实训页面时,才会通过布局嵌套间接使用
+- **超管端完全不涉及实训模块**,所以以 `.student-training-shell` 为锚点最安全
+
+### 页面非 scoped 块的边界
+
+每个页面自己写的非 scoped `
+
+
diff --git a/src/views/demand/industry-demand-analysis.vue b/src/views/demand/industry-demand-analysis.vue
index 0b0d992..91a1513 100644
--- a/src/views/demand/industry-demand-analysis.vue
+++ b/src/views/demand/industry-demand-analysis.vue
@@ -2,19 +2,26 @@
-
-
-
-
MARKET INSIGHT · INDUSTRY DEMAND
-
{{ trainingSteps[currentStep - 1] || taskTitle }}
-
-
-
@@ -50,9 +55,7 @@
请先完成并保存第 1 步的 PEST 信息归类,再填写本步骤。
| 序号 | PEST类型 | 补充信息资讯 |
- | {{ index + 1 }} | {{ row.label }} | |
-
-
+ | {{ index + 1 }} | {{ row.label }} | |
@@ -61,9 +64,7 @@
请先完成并保存第 2 步的 PEST 补充资讯,再填写本步骤。
| PEST维度 | 核心机遇(至少1条) | 潜在挑战(至少1条) | 应对思路建议 |
- | {{ row.label }} | | | |
-
-
+ | {{ row.label }} | | | |
@@ -71,10 +72,10 @@
基于前述 PEST 分析,完成行业环境评估、市场进入判断与风险总结。
请先完成并保存第 3 步的机会与挑战分析,再填写本步骤。
@@ -98,7 +99,6 @@ import { getTrainingTaskByKey } from "@/api/trainingTask";
import { parseTrainingArray } from "@/views/training/taskKeyMap";
import useUserStore from "@/store/modules/user";
import TrainingAiSidebar from "@/views/components/TrainingAiSidebar.vue";
-import TrainingTaskBrief from "@/views/components/TrainingTaskBrief.vue";
import TrainingTaskToolbar from "@/views/components/TrainingTaskToolbar.vue";
import TrainingStepOneChoiceQuestions from "@/views/components/TrainingStepOneChoiceQuestions.vue";
@@ -127,6 +127,7 @@ const defaultTask = {
requirement: "请阅读案例资料,将相关信息归类到政治、经济、社会和技术环境中,并补充自行收集的行业资讯。",
};
const taskConfig = ref(null);
+const briefOpen = ref(false);
const currentStep = ref(1);
const saving = ref(false);
const draftReady = ref(false);
@@ -214,12 +215,652 @@ function exportOutcome() { const blob = new Blob([JSON.stringify(buildDraft(), n
+
+
diff --git a/src/views/product/market-opportunity-selection.vue b/src/views/product/market-opportunity-selection.vue
index 980b184..f286b42 100644
--- a/src/views/product/market-opportunity-selection.vue
+++ b/src/views/product/market-opportunity-selection.vue
@@ -1,16 +1,36 @@
-
-
+
+
-
Step 01
{{ trainingSteps[0] }}
指标分类练习
请对实训数据表中的各项指标,按照市场规模、市场潜力、竞争强度、运营难度 4 个类别进行分类。
- | 序号 | 指标 | 分类 |
|---|
| {{ index + 1 }} | {{ item.name }} | {{ stepOneCheckResults[item.id].message }} |
+ | 序号 | 指标 | 分类 |
|---|
| {{ index + 1 }} | {{ item.name }} | {{ stepOneCheckResults[item.id].message }} |
@@ -18,14 +38,14 @@
第 1 步分类结果已自动带入。请填写四类一级指标权重和各维度内二级指标权重,系统将自动计算对应总权重。
请先完成并保存第 1 步的指标分类,再填写本步骤。
一级指标权重合计{{ primaryWeightTotal }}%对应总权重合计{{ correspondingWeightTotal }}%一级权重合计、每类维度内权重合计、对应总权重均须为 100%
-
+ | 一级指标 | 权重占比(%) | 二级指标 | 维度内权重(%) | 对应总权重 |
|---|
| {{ group.name }} | %建议 {{ group.suggestion }}维度内权重合计 {{ dimensionWeightTotal(group.name) }}% | {{ item.name }} | %建议 {{ item.suggestion }} | {{ formatWeight(correspondingWeight(item)) }}% |
Step 03
{{ trainingSteps[2] }}
加权评分计算
通过 Min-Max 归一法计算各二级指标得分,采用加权评分法计算产品品类综合得分。
请先完成并保存第 2 步的指标权重设定,再填写本步骤。
-
+ | 序号 | 产品品类 | 综合得分 |
|---|
| {{ index + 1 }} | {{ product.name }} | 分 |
推荐工具及方法如下,自行选择:
1. Excel/WPS 表格,自行操作计算,可参考《实训操作说明》完成。
2. 下载自动化模板,填写案例数据及第 2 步权重,完成指标计算。
3. 导出实训数据表,上传到通用 AI 大模型,参考提示词完成计算。
先按 Min-Max 归一法和加权评分法计算品类综合得分;如案例资料提供三段式时间权重,则分别按第 1–20 天、第 21–40 天、第 41–60 天的权重计算最终得分。
@@ -46,7 +66,7 @@
Step 05
{{ trainingSteps[4] }}
评论数据洞察
结合本实训提供的智能健身镜、智能卫浴镜和智能美妆镜三类产品评论数据,分析用户的痛点及未被满足的需求。
请先完成并保存第 4 步的数据可视化结果,再填写本步骤。
-
+ | 品类 | 好评率(%) | 用户痛点 | 未被满足的需求 |
|---|
| {{ product.name }} | % | | |
可参考以下方法:
1. 自行根据评价数据计算好评率,阅读评论后分析用户痛点及未被满足的需求。
2. 导出评论数据表,上传到通用 AI 大模型,通过文本分析完成计算与提炼。
提示词:请计算 Excel 表中产品评论数据的好评率,提炼用户痛点和未被满足的需求。
@@ -54,7 +74,7 @@
Step 06
{{ trainingSteps[5] }}
选品结论
结合前面的数据分析,请说明应该进行开发什么产品,具体原因是什么。
请先完成并保存第 5 步的用户需求洞察,再填写本步骤。
-
+
第 {{ currentStep }} 步内容待配置
已保存的步骤可从上方步骤栏返回查看。
@@ -74,7 +94,6 @@ import { parseTrainingArray } from "@/views/training/taskKeyMap";
import { isStudentDemo } from "@/utils/studentDemo";
import TrainingAiSidebar from "@/views/components/TrainingAiSidebar.vue";
import TrainingMaterialButton from "@/views/components/TrainingMaterialButton.vue";
-import TrainingTaskBrief from "@/views/components/TrainingTaskBrief.vue";
const TASK_KEY = "market-opportunity-selection";
const SCORE_CALCULATION_GUIDE_URL = "/file/training-materials/market-opportunity-selection/6.综合得分计算操作说明.docx";
@@ -90,7 +109,7 @@ const groupSuggestions = { "市场规模": "25–35", "市场潜力": "25–35",
const products = [{ id: "smart-fitness-mirror", name: "智能健身镜" }, { id: "smart-bathroom-mirror", name: "智能卫浴镜" }, { id: "smart-beauty-mirror", name: "智能美妆镜" }];
const defaultTask = { title: "市场机会识别与选品", background: "基于实训数据中的市场、供给与流量指标,完成市场机会识别和选品判断。", goals: ["理解选品数据指标的业务含义", "掌握市场规模、潜力、竞争和运营难度的分类方法", "为后续选品分析建立指标基础"], requirement: "阅读案例资料,自行完成 9 项指标的分类,并依据系统判题反馈修改。" };
const { proxy } = getCurrentInstance();
-const taskConfig = ref(null); const currentStep = ref(1); const saving = ref(false); const uploading = ref(false); const visualizationInput = ref(null); const visualization = ref({ url: "", name: "" }); const stepOnePassed = ref(false); const stepTwoPassed = ref(false); const stepThreePassed = ref(false); const stepFourPassed = ref(false); const stepFivePassed = ref(false); const answers = ref(createAnswers()); const primaryWeights = ref(createPrimaryWeights()); const weights = ref(createWeights()); const scores = ref(createScores()); const reviewInsights = ref(createReviewInsights()); const developmentConclusion = ref(""); const dynamicStepAnswers = ref({}); const stepOneCheckResults = ref({});
+const taskConfig = ref(null); const briefOpen = ref(false); const currentStep = ref(1); const saving = ref(false); const uploading = ref(false); const visualizationInput = ref(null); const visualization = ref({ url: "", name: "" }); const stepOnePassed = ref(false); const stepTwoPassed = ref(false); const stepThreePassed = ref(false); const stepFourPassed = ref(false); const stepFivePassed = ref(false); const answers = ref(createAnswers()); const primaryWeights = ref(createPrimaryWeights()); const weights = ref(createWeights()); const scores = ref(createScores()); const reviewInsights = ref(createReviewInsights()); const developmentConclusion = ref(""); const dynamicStepAnswers = ref({}); const stepOneCheckResults = ref({});
const taskTitle = computed(() => taskConfig.value?.taskName || defaultTask.title); const taskBackground = computed(() => taskConfig.value?.background || defaultTask.background); const taskGoals = computed(() => parseArray(taskConfig.value?.objectives, defaultTask.goals)); const taskRequirement = computed(() => taskConfig.value?.requirements || defaultTask.requirement); const trainingSteps = computed(() => { const configured = parseTrainingArray(taskConfig.value?.steps, DEFAULT_STEPS); return DEFAULT_STEPS.map((fallback, index) => configured[index] || fallback); });
const progressItems = computed(() => indicators.map((item) => ({ text: `${answers.value[item.id] ? "已分类" : "待分类"}:${item.name}`, status: answers.value[item.id] ? "done" : "" })));
const indicatorGroups = computed(() => categories.map((name) => ({ name, suggestion: groupSuggestions[name], items: indicators.filter((item) => item.group === name) })));
@@ -182,6 +201,785 @@ function exportOutcome() { const blob = new Blob([JSON.stringify({ taskName: tas
+
+