feat:教师端菜单栏替换为接口获取动态渲染

This commit is contained in:
yuk255 2025-09-29 21:26:56 +08:00
parent a7d8b00fe9
commit 4c43226c91
6 changed files with 171 additions and 200 deletions

View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 490 B

After

Width:  |  Height:  |  Size: 490 B

View File

Before

Width:  |  Height:  |  Size: 712 B

After

Width:  |  Height:  |  Size: 712 B

View File

@ -126,6 +126,23 @@ export interface QueryCourseSectionParams {
* API模块 * API模块
*/ */
export class TeachCourseApi { export class TeachCourseApi {
/**
*
*/
static async getTeacherMenuList(): Promise<ApiResponse<any>> {
try{
const response = await ApiRequest.get<any>('/aiol/aiolMenu/getTeacherMenus')
console.log('📝 获取教师菜单响应:', response);
return response
} catch(error) {
console.error('❌ 获取教师菜单失败:', error)
throw error
}
}
/** /**
* *

View File

@ -21,79 +21,44 @@
<!-- 导航栏 --> <!-- 导航栏 -->
<div class="nav-container"> <div class="nav-container">
<router-link to="/teacher/course-management" class="nav-item" :class="{ active: activeNavItem === 0 }" <!-- 动态菜单渲染 -->
@click="setActiveNavItem(0)"> <template v-for="menu in menuList" :key="menu.id">
<img :src="activeNavItem === 0 ? '/images/teacher/课程管理(选中).png' : '/images/teacher/课程管理.png'" alt=""> <!-- 有子菜单的父级菜单 -->
<span>课程管理</span> <div v-if="menu.children && menu.children.length > 0"
</router-link> class="nav-item"
:class="{ active: activeNavItem === menu.id }"
@click="toggleMenu(menu)">
<img :src="getMenuIcon(menu)" :alt="menu.name" v-if="menu.icon">
<span>{{ menu.name }}</span>
<n-icon class="expand-icon" :class="{ expanded: isMenuExpanded(menu.id) }">
<ChevronDownOutline />
</n-icon>
</div>
<!-- 考试管理 - 可展开菜单 --> <!-- 子菜单容器 -->
<div class="nav-item" :class="{ active: activeNavItem === 4 }" @click="toggleExamMenu"> <div v-if="menu.children && menu.children.length > 0"
<img class="submenu-container"
:src="activeNavItem === 4 ? '/images/teacher/examination-active.png' : '/images/teacher/examination.png'" :class="{ expanded: isMenuExpanded(menu.id) }">
alt=""> <router-link v-for="subMenu in menu.children"
<span>考试管理</span> :key="subMenu.id"
<n-icon class="expand-icon" :class="{ expanded: examMenuExpanded }"> :to="subMenu.path"
<ChevronDownOutline /> class="submenu-item"
</n-icon> :class="{ active: activeSubNavItem === subMenu.id }"
</div> @click="handleSubMenuClick(subMenu, menu)">
<span>{{ subMenu.name }}</span>
</router-link>
</div>
<!-- 考试管理子菜单 --> <!-- 没有子菜单的直接链接 -->
<div class="submenu-container" :class="{ expanded: examMenuExpanded }"> <router-link v-else
<router-link to="/teacher/exam-management/question-bank" class="submenu-item" :to="menu.path"
:class="{ active: activeSubNavItem === 'question-bank' }" @click="setActiveSubNavItem('question-bank')"> class="nav-item"
<span>题库管理</span> :class="{ active: activeNavItem === menu.id }"
@click="toggleMenu(menu)">
<img :src="getMenuIcon(menu)" :alt="menu.name" v-if="menu.icon">
<span>{{ menu.name }}</span>
</router-link> </router-link>
<router-link to="/teacher/exam-management/exam-library" class="submenu-item" </template>
:class="{ active: activeSubNavItem === 'exam-library' }" @click="setActiveSubNavItem('exam-library')">
<span>试卷管理</span>
</router-link>
<router-link to="/teacher/exam-management/marking-center/list" class="submenu-item"
:class="{ active: activeSubNavItem === 'marking-center' }" @click="setActiveSubNavItem('marking-center')">
<span>阅卷中心</span>
</router-link>
</div>
<!-- 学员中心 - 可展开菜单 -->
<div class="nav-item" :class="{ active: activeNavItem === 1 }"
@click="toggleStudentMenu('/teacher/student-management/student-library')">
<img :src="activeNavItem === 1 ? '/images/teacher/学院管理(选中).png' : '/images/teacher/学员管理.png'" alt="">
<span>学员中心</span>
<n-icon class="expand-icon" :class="{ expanded: studentMenuExpanded }">
<ChevronDownOutline />
</n-icon>
</div>
<!-- 学员中心子菜单 -->
<div class="submenu-container" :class="{ expanded: studentMenuExpanded }">
<router-link to="/teacher/student-management/student-library" class="submenu-item"
:class="{ active: activeSubNavItem === 'student-library' }"
@click="setActiveSubNavItem('student-library')">
<span>学员库</span>
</router-link>
<router-link to="/teacher/student-management/class-management" class="submenu-item"
:class="{ active: activeSubNavItem === 'class-management' }"
@click="setActiveSubNavItem('class-management')">
<span>班级管理</span>
</router-link>
</div>
<!-- <router-link to="/teacher/my-resources" class="nav-item" :class="{ active: activeNavItem === 2 }"
@click="setActiveNavItem(2)">
<img :src="activeNavItem === 2 ? '/images/teacher/我的资源(选中).png' : '/images/teacher/我的资源.png'" alt="">
<span>我的资源</span>
</router-link> -->
<router-link to="/teacher/message-center" class="nav-item" :class="{ active: activeNavItem === 5 }"
@click="setActiveNavItem(5)">
<img :src="activeNavItem === 5 ? '/images/profile/message-active.png' : '/images/profile/message.png'"
alt="">
<span>消息中心</span>
</router-link>
<router-link to="/teacher/personal-center" class="nav-item" :class="{ active: activeNavItem === 3 }"
@click="setActiveNavItem(3)">
<img :src="activeNavItem === 3 ? '/images/teacher/个人中心(选中).png' : '/images/teacher/个人中心.png'" alt="">
<span>个人中心</span>
</router-link>
</div> </div>
<!-- ai助教 - 已注释 --> <!-- ai助教 - 已注释 -->
@ -188,6 +153,7 @@ import { useRoute, useRouter } from 'vue-router'
import { ChevronDownOutline } from '@vicons/ionicons5' import { ChevronDownOutline } from '@vicons/ionicons5'
import { useUserStore } from '@/stores/user' import { useUserStore } from '@/stores/user'
import { ChevronBackSharp } from '@vicons/ionicons5' import { ChevronBackSharp } from '@vicons/ionicons5'
import { TeachCourseApi } from '@/api/modules/teachCourse'
const userStore = useUserStore() const userStore = useUserStore()
@ -197,11 +163,9 @@ const height = window.innerHeight;
console.log(`当前屏幕宽度: ${width}px, 高度: ${height}px`); console.log(`当前屏幕宽度: ${width}px, 高度: ${height}px`);
// //
const activeNavItem = ref(0); // 0: , 1: , 2: , 3: , 4: , 5: (6: - ) const activeNavItem = ref(''); // ID
const activeSubNavItem = ref(''); // const activeSubNavItem = ref(''); //
const examMenuExpanded = ref(false); // const expandedMenus = ref(new Set()); // ID
const studentMenuExpanded = ref(false); //
// const orchestrationMenuExpanded = ref(false); // -
const showTopImage = ref(false); // / const showTopImage = ref(false); // /
// //
@ -227,88 +191,77 @@ const breadcrumbDisplay = computed(() => {
const isCourseEditor = computed(() => route.path.includes('course-editor') || route.path.includes('chapter-editor-teacher')); const isCourseEditor = computed(() => route.path.includes('course-editor') || route.path.includes('chapter-editor-teacher'));
const setActiveNavItem = (index: number) => { //
activeNavItem.value = index; const isMenuExpanded = (menuId) => {
// return expandedMenus.value.has(menuId)
if (index !== 4) { }
examMenuExpanded.value = false;
//
const toggleMenu = (menu) => {
if (menu.children && menu.children.length > 0) {
if (expandedMenus.value.has(menu.id)) {
expandedMenus.value.delete(menu.id)
} else {
expandedMenus.value.add(menu.id)
}
} }
//
if (index !== 1) { //
studentMenuExpanded.value = false; activeNavItem.value = menu.id
}
// - //
// if (index !== 6) { if (menu.path && (!menu.children || menu.children.length === 0)) {
// orchestrationMenuExpanded.value = false; router.push(menu.path)
// }
//
if (index !== 4 && index !== 1) { // index !== 6
activeSubNavItem.value = '';
} }
} }
// //
const toggleExamMenu = () => { const handleSubMenuClick = (subMenu, parentMenu) => {
// activeSubNavItem.value = subMenu.id
if (!examMenuExpanded.value) { activeNavItem.value = parentMenu.id
studentMenuExpanded.value = false; if (subMenu.path) {
} router.push(subMenu.path)
examMenuExpanded.value = !examMenuExpanded.value;
activeNavItem.value = 4;
//
if (examMenuExpanded.value && !activeSubNavItem.value) {
activeSubNavItem.value = 'question-management';
} }
} }
// //
const toggleStudentMenu = (path: string) => { const getMenuIcon = (menu) => {
// if (!menu.icon) return null
if (!studentMenuExpanded.value) {
examMenuExpanded.value = false; //
if (activeNavItem.value === menu.id) {
return menu.icon.replace('.png', '-active.png')
} }
studentMenuExpanded.value = !studentMenuExpanded.value; return menu.icon
activeNavItem.value = 1;
//
if (studentMenuExpanded.value && !activeSubNavItem.value) {
activeSubNavItem.value = 'student-library';
router.push(path);
}
} }
// - //
// const toggleOrchestrationMenu = () => { const updateActiveNavItem = () => {
// orchestrationMenuExpanded.value = !orchestrationMenuExpanded.value; const path = route.path;
// activeNavItem.value = 6; console.log('当前路径:', path);
// // //
// if (orchestrationMenuExpanded.value && !activeSubNavItem.value) { const findActiveMenu = (menus, parentId = null) => {
// activeSubNavItem.value = 'app-management'; for (const menu of menus) {
// } if (menu.path && path.includes(menu.path.replace('/teacher/', ''))) {
// } activeNavItem.value = parentId || menu.id
if (parentId) {
// activeSubNavItem.value = menu.id
const setActiveSubNavItem = (subItem: string) => { expandedMenus.value.add(parentId)
activeSubNavItem.value = subItem; }
return true
// }
if (subItem === 'question-bank' || subItem === 'exam-library' || subItem === 'marking-center') { if (menu.children && menu.children.length > 0) {
// if (findActiveMenu(menu.children, menu.id)) {
activeNavItem.value = 4; return true
examMenuExpanded.value = true; }
} else if (subItem === 'student-library' || subItem === 'class-management') { }
// }
activeNavItem.value = 1; return false
studentMenuExpanded.value = true;
} else if (subItem === 'app-management' || subItem === 'knowledge-base' || subItem === 'process-design' || subItem === 'model-config' || subItem === 'ocr-recognition') {
//
activeNavItem.value = 6;
orchestrationMenuExpanded.value = true;
} }
findActiveMenu(menuList.value)
} }
// //
@ -335,6 +288,50 @@ const hideSidebar = computed(() => {
return currentPath.includes('course-editor') || currentPath.includes('chapter-editor-teacher') || currentPath.includes('certificate') return currentPath.includes('course-editor') || currentPath.includes('chapter-editor-teacher') || currentPath.includes('certificate')
}) })
//
const menuList = ref([])
//
const processMenuData = (menuData) => {
const menuMap = new Map()
const rootMenus = []
//
menuData.forEach(menu => {
menuMap.set(menu.id, {
...menu,
children: []
})
})
//
menuData.forEach(menu => {
if (menu.parentId === null) {
//
rootMenus.push(menuMap.get(menu.id))
} else {
//
const parent = menuMap.get(menu.parentId)
if (parent) {
parent.children.push(menuMap.get(menu.id))
}
}
})
// sortOrder
const sortMenus = (menus) => {
menus.sort((a, b) => b.sortOrder - a.sortOrder)
menus.forEach(menu => {
if (menu.children.length > 0) {
sortMenus(menu.children)
}
})
}
sortMenus(rootMenus)
return rootMenus
}
// //
const breadcrumbPathItems = computed(() => { const breadcrumbPathItems = computed(() => {
const currentPath = route.path; const currentPath = route.path;
@ -795,7 +792,20 @@ const breadcrumbPathItems = computed(() => {
}); });
// //
onMounted(() => { onMounted(async () => {
try {
//
const menuResponse = await TeachCourseApi.getTeacherMenuList()
const rawMenuData = menuResponse.data.result
console.log('原始菜单数据:', rawMenuData);
//
menuList.value = processMenuData(rawMenuData)
console.log('处理后的菜单数据:', menuList.value);
} catch (error) {
console.error('获取菜单数据失败:', error)
}
// //
updateActiveNavItem(); updateActiveNavItem();
@ -832,62 +842,6 @@ const updateTopImageVisibility = () => {
console.log('顶部图片显示状态:', showTopImage.value); console.log('顶部图片显示状态:', showTopImage.value);
}; };
//
const updateActiveNavItem = () => {
const path = route.path;
console.log('当前路径:', path); //
if (path.includes('course-management')) {
activeNavItem.value = 0; //
} else if (path.includes('student-management')) {
activeNavItem.value = 1; //
studentMenuExpanded.value = true;
//
if (path.includes('student-library')) {
activeSubNavItem.value = 'student-library';
} else if (path.includes('class-management')) {
activeSubNavItem.value = 'class-management';
}
} else if (path.includes('my-resources')) {
activeNavItem.value = 2; //
} else if (path.includes('personal-center')) {
activeNavItem.value = 3; //
} else if (path.includes('exam-management')) {
activeNavItem.value = 4; //
examMenuExpanded.value = true;
const arr = ['question-bank', 'exam-library', 'marking-center'];
const found = arr.find(item => path.includes(item));
activeSubNavItem.value = found || '';
} else if (path.includes('message-center')) {
activeNavItem.value = 5; //
// } else if (path.includes('ai-assistant')) {
// // AI -
// console.log('AI');
// activeNavItem.value = -1;
// activeSubNavItem.value = '';
// examMenuExpanded.value = false;
// studentMenuExpanded.value = false;
// } else if (path.includes('airag')) {
// // -
// activeNavItem.value = 6; //
// orchestrationMenuExpanded.value = true;
// //
// if (path.includes('aiapp')) {
// activeSubNavItem.value = 'app-management';
// } else if (path.includes('aiknowledge')) {
// activeSubNavItem.value = 'knowledge-base';
// } else if (path.includes('aiflow')) {
// activeSubNavItem.value = 'process-design';
// } else if (path.includes('aimodel')) {
// activeSubNavItem.value = 'model-config';
// } else if (path.includes('ocr')) {
// activeSubNavItem.value = 'ocr-recognition';
// }
}
}
</script> </script>
<style scoped> <style scoped>