diff --git a/src/api/teacher.js b/src/api/teacher.js index 7822217..6aed8b0 100644 --- a/src/api/teacher.js +++ b/src/api/teacher.js @@ -548,6 +548,14 @@ export function getTaskList(query) { params: query, }); } +// 查询教学班实训任务发布状态;未保存过配置时 configured=false,学生端默认全部可见。 +export function getTaskPublication(query) { + return request({ + url: "/api/taskAllocation/selectTaskPublicationByClassId", + method: "POST", + params: query, + }); +} export function getStudentTaskList(query) { return request({ url: "/api/taskAllocation/selectTaskAllocationByStudentUserId", diff --git a/src/layout/components/Sidebar/index.vue b/src/layout/components/Sidebar/index.vue index 8acd820..777be09 100644 --- a/src/layout/components/Sidebar/index.vue +++ b/src/layout/components/Sidebar/index.vue @@ -376,7 +376,10 @@ const trainingTaskKeyByPath = { "/assessment/diagnosis": "product-diagnosis-iteration", }; const trainingTaskTitleByPath = ref({}); -const currentCourseNav = computed(() => (isTeacherRole.value ? filterTeacherManageNavigation(teacherManageNav) : studentCourseNav)); +const publishedTaskKeys = ref(null); +const currentCourseNav = computed(() => (isTeacherRole.value + ? filterTeacherManageNavigation(teacherManageNav) + : filterStudentCourseNavigation(studentCourseNav, publishedTaskKeys.value))); const studentIconByPath = { "/assistant/index": "MagicStick", "/foundation/new-product-survey": "Search", @@ -459,6 +462,7 @@ function getCourseNavTitle(item) { async function loadTrainingTaskTitles() { if (!isStudentRole.value) { trainingTaskTitleByPath.value = {}; + publishedTaskKeys.value = null; return; } try { @@ -469,6 +473,7 @@ async function loadTrainingTaskTitles() { titleByKey[task.taskKey] = task.taskName; } }); + publishedTaskKeys.value = new Set(Object.keys(titleByKey)); const titleByPath = {}; Object.entries(trainingTaskKeyByPath).forEach(([path, taskKey]) => { if (titleByKey[taskKey]) { @@ -478,8 +483,22 @@ async function loadTrainingTaskTitles() { trainingTaskTitleByPath.value = titleByPath; } catch (error) { trainingTaskTitleByPath.value = {}; + publishedTaskKeys.value = new Set(); } } +function filterStudentCourseNavigation(sections, allowedTaskKeys) { + if (allowedTaskKeys == null) return sections; + return sections + .map((section) => { + if (section.path) return section; + const children = (section.children || []).filter((child) => { + const taskKey = trainingTaskKeyByPath[child.path]; + return !taskKey || allowedTaskKeys.has(taskKey); + }); + return { ...section, children }; + }) + .filter((section) => section.path || section.children?.length); +} function groupAndSortModules(modules) { const grouped = modules.reduce((acc, item) => { const prefix = item.module.split("-")[0].trim(); @@ -570,13 +589,8 @@ const getTaskList = async () => { await userStore.ensureSchoolProductConfig().catch(() => null); sidebarRouters.value = [...constantRoutes, ...filterRoutesByOrganizationMode(schoolAdminRoutes)]; } else if (userStore.userInfo.roleId != 3) { - indexApi.getStudentTaskList({ userId: userStore.userInfo.userId }).then((res) => { - const disabledTitles = processModuleStatus(groupAndSortModules(res.data)).disabledParents; - console.log(processModuleStatus(groupAndSortModules(res.data))); - proxy.$cache.session.setJSON("disabledChildren", processModuleStatus(groupAndSortModules(res.data)).disabledChildren); - proxy.$cache.session.setJSON("disabledTitles", disabledTitles); - sidebarRouters.value = [...constantRoutes, ...dynamicRoutes]; - }); + await loadTrainingTaskTitles(); + sidebarRouters.value = [...constantRoutes, ...dynamicRoutes]; } else { await userStore.ensureSchoolProductConfig().catch(() => null); sidebarRouters.value = [...constantRoutes, ...filterRoutesByOrganizationMode(teacherRoutes)]; @@ -622,6 +636,5 @@ watch( ); onMounted(() => { getTaskList(); - loadTrainingTaskTitles(); }); diff --git a/src/views/teacherEnd/task/index.vue b/src/views/teacherEnd/task/index.vue index 3e67699..c6905eb 100644 --- a/src/views/teacherEnd/task/index.vue +++ b/src/views/teacherEnd/task/index.vue @@ -1,189 +1,132 @@ - diff --git a/tests/task-publication.static.test.cjs b/tests/task-publication.static.test.cjs new file mode 100644 index 0000000..f8fa35f --- /dev/null +++ b/tests/task-publication.static.test.cjs @@ -0,0 +1,19 @@ +const assert = require("assert"); +const fs = require("fs"); +const path = require("path"); + +const root = path.resolve(__dirname, ".."); +const read = (file) => fs.readFileSync(path.join(root, file), "utf8"); +const taskPage = read("src/views/teacherEnd/task/index.vue"); +const sidebar = read("src/layout/components/Sidebar/index.vue"); +const api = read("src/api/teacher.js"); + +assert(taskPage.includes("listTrainingTasks"), "task allocation must use training tasks as its data source"); +assert(taskPage.includes("getTaskPublication"), "task allocation must load the selected class publication state"); +assert(taskPage.includes("tasks.value.map((task) => task.taskKey)"), "an unconfigured class must default to all tasks selected"); +assert(taskPage.includes("保存发布配置"), "task allocation must require an explicit save"); +assert(api.includes("selectTaskPublicationByClassId"), "publication API missing"); +assert(sidebar.includes("filterStudentCourseNavigation"), "student menu must be filtered by published training tasks"); +assert(sidebar.includes("publishedTaskKeys"), "student menu must retain published task keys"); + +console.log("task publication front-end contract passed");