feat: sync student navigation with task allocation
parent
6faa6b1dab
commit
1df7b1781a
@ -0,0 +1,67 @@
|
||||
const COMPREHENSIVE_TASK_KEY = "comprehensive-case-training";
|
||||
|
||||
const KNOWN_TASK_PATHS = Object.freeze({
|
||||
"new-product-survey": "/foundation/new-product-survey",
|
||||
"product-development-factors": "/foundation/product-development-factors",
|
||||
"product-development-process": "/foundation/product-development-process",
|
||||
"industry-demand-analysis": "/demand/index",
|
||||
"competitive-environment-analysis": "/demand/environment",
|
||||
"market-opportunity-selection": "/product/index",
|
||||
"business-feasibility": "/demand/business-feasibility",
|
||||
"target-user-profile": "/demand/people",
|
||||
"consumer-behavior": "/demand/consumer-behavior",
|
||||
"consumer-scenario": "/demand/consumer-scenario",
|
||||
"feature-conversion": "/demand/feature-conversion",
|
||||
"product-value-positioning": "/product/differentiation",
|
||||
"product-structure": "/product/structure",
|
||||
"after-sales-service": "/product/after-sales-service",
|
||||
"requirements-doc": "/product/requirements-doc",
|
||||
"hardware-cost": "/product/hardware-cost",
|
||||
"supplier-evaluation": "/channel/supplier",
|
||||
"purchase-demand-forecast": "/channel/index",
|
||||
"product-pricing": "/product/price",
|
||||
"category-optimization": "/assessment/optimization",
|
||||
"release-channel-evaluation": "/test/index",
|
||||
"promotion-effect-optimization": "/test/promotion",
|
||||
"product-diagnosis-iteration": "/assessment/diagnosis",
|
||||
});
|
||||
|
||||
function taskSort(task) {
|
||||
const sort = Number(task?.sort);
|
||||
return Number.isFinite(sort) ? sort : Number.MAX_SAFE_INTEGER;
|
||||
}
|
||||
|
||||
function taskPath(taskKey) {
|
||||
return KNOWN_TASK_PATHS[taskKey] || `/training/task/${encodeURIComponent(taskKey)}`;
|
||||
}
|
||||
|
||||
function buildStudentTaskSections(tasks) {
|
||||
const sections = new Map();
|
||||
[...(tasks || [])]
|
||||
.filter((task) => task?.taskKey && task.taskKey !== COMPREHENSIVE_TASK_KEY)
|
||||
.sort((left, right) => taskSort(left) - taskSort(right))
|
||||
.forEach((task) => {
|
||||
const title = task.projectName || "其他实训";
|
||||
if (!sections.has(title)) sections.set(title, { title, children: [] });
|
||||
sections.get(title).children.push({
|
||||
title: task.taskName || task.taskKey,
|
||||
path: taskPath(task.taskKey),
|
||||
});
|
||||
});
|
||||
return [...sections.values()];
|
||||
}
|
||||
|
||||
function getComprehensiveTopTask(tasks) {
|
||||
const task = (tasks || []).find((item) => item?.taskKey === COMPREHENSIVE_TASK_KEY);
|
||||
if (!task) return null;
|
||||
return {
|
||||
title: task.taskName || "综合实训",
|
||||
path: "/comprehensive/index",
|
||||
activePrefix: "/comprehensive",
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
buildStudentTaskSections,
|
||||
getComprehensiveTopTask,
|
||||
};
|
||||
@ -0,0 +1,48 @@
|
||||
const assert = require("assert");
|
||||
const {
|
||||
buildStudentTaskSections,
|
||||
getComprehensiveTopTask,
|
||||
} = require("../src/utils/studentTrainingNavigation.cjs");
|
||||
|
||||
const tasks = [
|
||||
{ taskKey: "comprehensive-case-training", taskName: "综合案例实训", projectName: "综合实训", sort: 1 },
|
||||
{ taskKey: "market-task", taskName: "需求访谈", projectName: "市场洞察与需求分析", sort: 3 },
|
||||
{ taskKey: "new-product-survey", taskName: "新产品调查与分析", projectName: "互联网产品开发基础认知", sort: 2 },
|
||||
{ taskKey: "teacher-added-task", taskName: "教师新增任务", projectName: "市场洞察与需求分析", sort: 4 },
|
||||
];
|
||||
|
||||
const sections = buildStudentTaskSections(tasks);
|
||||
|
||||
assert.deepStrictEqual(sections, [
|
||||
{
|
||||
title: "互联网产品开发基础认知",
|
||||
children: [{ title: "新产品调查与分析", path: "/foundation/new-product-survey" }],
|
||||
},
|
||||
{
|
||||
title: "市场洞察与需求分析",
|
||||
children: [
|
||||
{ title: "需求访谈", path: "/training/task/market-task" },
|
||||
{ title: "教师新增任务", path: "/training/task/teacher-added-task" },
|
||||
],
|
||||
},
|
||||
]);
|
||||
assert.deepStrictEqual(getComprehensiveTopTask(tasks), {
|
||||
title: "综合案例实训",
|
||||
path: "/comprehensive/index",
|
||||
activePrefix: "/comprehensive",
|
||||
});
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const frontendRoot = path.resolve(__dirname, "..");
|
||||
const sidebarSource = fs.readFileSync(path.join(frontendRoot, "src/layout/components/Sidebar/index.vue"), "utf8");
|
||||
const navbarSource = fs.readFileSync(path.join(frontendRoot, "src/layout/components/Navbar.vue"), "utf8");
|
||||
const routerSource = fs.readFileSync(path.join(frontendRoot, "src/router/index.js"), "utf8");
|
||||
|
||||
assert(sidebarSource.includes("loadStudentTaskNavigation"), "student sidebar must load its task navigation from the task API");
|
||||
assert(sidebarSource.includes("studentTaskSections"), "student sidebar must render dynamic task sections");
|
||||
assert(navbarSource.includes("getComprehensiveTopTask"), "top navigation must derive comprehensive training from the task API");
|
||||
assert(navbarSource.includes("studentTopTask"), "top navigation must hold the published comprehensive task");
|
||||
assert(routerSource.includes('path: "task/:pageKey"'), "teacher-added tasks must have a generic student route");
|
||||
|
||||
console.log("student training navigation contract passed");
|
||||
Loading…
Reference in New Issue