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