Просмотр исходного кода

通识课菜单栏动态加载

liyanbo 3 недель назад
Родитель
Сommit
218a4f5344
1 измененных файлов с 161 добавлено и 143 удалено
  1. 161 143
      src/views/AIPage/AIGeneralCourse.vue

+ 161 - 143
src/views/AIPage/AIGeneralCourse.vue

@@ -5,19 +5,19 @@
     <div class="sidebar-container">
       <div class="sidebar-layout">
         <div
-          class="icon-expand"
-          :style="{
+            class="icon-expand"
+            :style="{
             backgroundColor: drawerVisible ? '#44449c' : '#7F70C840',
             left: drawerVisible ? '20%' : '0'
           }"
-          @click="toggleDrawer"
+            @click="toggleDrawer"
         >
           <span
-            class="vertical-lines"
-            :style="{
+              class="vertical-lines"
+              :style="{
               color: drawerVisible ? '#8a78d0' : 'white'
             }"
-            >||</span
+          >||</span
           >
         </div>
       </div>
@@ -29,27 +29,27 @@
             <el-row class="tac">
               <el-col :span="12">
                 <el-menu
-                  v-if="menuInitialized"
-                  :default-active="currentActiveIndex"
-                  :default-openeds="[currentOpenedMenu]"
-                  :class="{ 'el-menu-vertical-demo': true }"
-                  unique-opened
+                    v-if="menuInitialized"
+                    :default-active="currentActiveIndex"
+                    :default-openeds="[currentOpenedMenu]"
+                    :class="{ 'el-menu-vertical-demo': true }"
+                    unique-opened
                 >
                   <!-- 课程菜单 -->
-                  <el-sub-menu 
-                    v-for="menu in menuList" 
-                    :key="menu.id"
-                    :index="menu.id"
-                    @click="handleMenuClick(menu)"
+                  <el-sub-menu
+                      v-for="menu in menuList"
+                      :key="menu.id"
+                      :index="menu.id"
+                      @click="handleMenuClick(menu)"
                   >
                     <template #title>
                       <span>{{ menu.title }}</span>
                     </template>
                     <el-menu-item
-                      v-for="(item, index) in menu.data"
-                      :key="menu.id + '-' + index"
-                      :index="menu.id + '-' + (index + 1).toString()"
-                      @click="goToAIExperience(item)"
+                        v-for="(item, index) in menu.data"
+                        :key="menu.id + '-' + index"
+                        :index="menu.id + '-' + (index + 1).toString()"
+                        @click="goToAIExperience(item)"
                     >
                       {{ item.ctTypeSort }} {{ item.ctType }}
                     </el-menu-item>
@@ -81,10 +81,10 @@
               <template #dropdown>
                 <el-dropdown-menu class="dropdown-menu">
                   <el-dropdown-item
-                    v-for="item in classData"
-                    :key="item.id"
-                    :command="item.ctType"
-                    >{{ item.ctType }}</el-dropdown-item
+                      v-for="item in classData"
+                      :key="item.id"
+                      :command="item.ctType"
+                  >{{ item.ctType }}</el-dropdown-item
                   >
                 </el-dropdown-menu>
               </template>
@@ -94,14 +94,14 @@
         <div class="inner-box right-box">
           <div class="top-right-box">
             <el-autocomplete
-              v-model="SearchInput"
-              :fetch-suggestions="querySearch"
-              placeholder="搜索"
-              @select="handleSearchSelect"
-              class="search-input"
-              value-key="ctType"
-              :trigger-on-focus="false"
-              :key="searchKey"
+                v-model="SearchInput"
+                :fetch-suggestions="querySearch"
+                placeholder="搜索"
+                @select="handleSearchSelect"
+                class="search-input"
+                value-key="ctType"
+                :trigger-on-focus="false"
+                :key="searchKey"
             >
               <template #prefix>
                 <el-icon class="el-input__icon"><search /></el-icon>
@@ -126,19 +126,19 @@
         <!-- 自主学习组件,只在AI自主学习时显示 -->
         <SelfDirectedLearning v-if="currentOpenedMenu === 'selfstudy'"
                               @refreshData="refreshData" />
-        
+
         <div
-          class="small-box"
-          v-for="(outlineData, index) in currentCourseData"
-          :key="index"
+            class="small-box"
+            v-for="(outlineData, index) in currentCourseData"
+            :key="index"
         >
           <div
-            class="nested-box"
-            :style="{
+              class="nested-box"
+              :style="{
               backgroundImage: `url(${outlineData.ctTypeImage})`,
               backgroundSize: 'cover'
             }"
-            @click="goToAIExperience(outlineData)"
+              @click="goToAIExperience(outlineData)"
           ></div>
           <div class="additional-text">
             {{ outlineData.ctTypeSort }} {{ outlineData.ctType }}
@@ -172,6 +172,8 @@ import teachingImg from '@/assets/icon/teaching.png'
 import SelfDirectedLearning from '@/components/study/SelfDirectedLearning.vue'
 import DialogContent from "@/views/AIPage/aiGenerate/DialogContent.vue";
 import {teacherList} from "@/api/teachers.js";
+import { CONFIG } from '@/utils/roleUtils.js'
+
 
 const router = useRouter() // 获取当前路由对象
 // 下拉菜单选中项
@@ -186,39 +188,55 @@ const handleVisibleChange = (visible) => {
 }
 
 
-// 课程数据管理
-const courseData = ref({
-  general: [], // 通识课
-  practical: [], // 实操课
-  ai: [], // AI场景教学
-  selfstudy: [] // AI自主学习
-})
+// 课程数据管理 - 拆分成独立的数组
+const generalCourseData = ref([])
+const practicalCourseData = ref([])
+const aiCourseData = ref([])
+const selfStudyCourseData = ref([])
 
 // 菜单配置
-const menuConfig = [
-  { id: 'general', title: 'AI通识课', type: '1', isPractical: false, isAICourse: false },
-  { id: 'practical', title: 'AI实操课', type: '2', isPractical: true, isAICourse: false },
-  { id: 'ai', title: 'AI古诗词', type: '3', isPractical: false, isAICourse: true },
-  { id: 'selfstudy', title: 'AI自主学习', type: '4', isPractical: false, isAICourse: true }
+const menuConfigTemp = [
+  { id: '/general', title: 'AI通识课', type: '1', data: generalCourseData },
+  { id: '/practical', title: 'AI实操课', type: '2', data: practicalCourseData },
+  { id: '/poetry', title: 'AI古诗词', type: '3', data: aiCourseData },
+  { id: '/selfstudy', title: 'AI自主学习', type: '4', data: selfStudyCourseData }
 ]
+
+
+const menuConfig = computed(() => {
+  return menuConfigTemp.filter(item=> {
+    return roleRouteMenuSet.value.includes(item.id)
+  })
+
+})
+// 菜单列表计算属性
+const menuList = computed(() => {
+  return menuConfig.value.map(menu => ({
+    ...menu,
+    data: menu.data.value
+  }))
+})
+
+// 获取角色路由菜单集合
+const roleRouteMenuSet = computed(() => {
+  try {
+    const roleRouteMenuStr = localStorage.getItem(CONFIG.USER_ROLE_ROUTE_MENU_KEY)
+    return roleRouteMenuStr ? JSON.parse(roleRouteMenuStr) : []
+  } catch (error) {
+    console.error('Error parsing roleRouteMenuSet:', error)
+    return []
+  }
+})
 // 当前选中的菜单索引
-const currentActiveIndex = ref('general-1')
+const currentActiveIndex = ref('/general-1')
 // 当前打开的菜单
-const currentOpenedMenu = ref('general')
+const currentOpenedMenu = ref('/general')
 // 菜单初始化状态
 const menuInitialized = ref(false)
 // 存储选中状态的键名
 const activeMenuKey = 'aiGeneralCourseActiveMenu'
 const activeIndexKey = 'aiGeneralCourseActiveIndex'
 
-// 菜单列表计算属性
-const menuList = computed(() => {
-  return menuConfig.map(menu => ({
-    ...menu,
-    data: courseData.value[menu.id]
-  }))
-})
-
 // 处理课程数据,添加序号
 const processCourseData = (data) => {
   return data.map((item, index) => {
@@ -234,12 +252,12 @@ const saveActiveState = (menuId, index) => {
   localStorage.setItem(activeIndexKey, index)
 }
 
-//刷新AI生成课数据
+// 刷新AI生成课数据
 const refreshData = async () => {
 
   const res = await ClassOutline(localStorage.getItem('selectedGradeId'), menuConfig[3].type)
   if (res.code === 0) {
-    courseData.value[menuConfig[3].id] = processCourseData(res.data)
+    selfStudyCourseData.value = processCourseData(res.data)
   }
 }
 
@@ -247,10 +265,10 @@ const refreshData = async () => {
 const fetchClassOutline = async (classId) => {
   try {
     // 并行获取所有课程类型数据
-    await Promise.all(menuConfig.map(async (menu) => {
+    await Promise.all(menuConfig.value.map(async (menu) => {
       const res = await ClassOutline(classId, menu.type)
       if (res.code === 0) {
-        courseData.value[menu.id] = processCourseData(res.data)
+        menu.data.value = processCourseData(res.data)
       }
     }))
   } catch (error) {
@@ -262,12 +280,12 @@ const fetchClassOutline = async (classId) => {
 const handleMenuClick = (menu) => {
   currentOpenedMenu.value = menu.id
   saveActiveState(menu.id, menu.id)
-  
+
   // 清空搜索框内容
   SearchInput.value = ''
-  
+
   // 如果该菜单下有课程数据,设置默认选中第一项
-  if (menu.data && menu.data.length > 0) {
+  if (menu.data.value && menu.data.value.length > 0) {
     currentActiveIndex.value = menu.id + '-1'
   }
 }
@@ -309,15 +327,12 @@ const fetchCtTypes = async () => {
     const response = await ClassList()
     if (response.code === 0) {
       classData.value = response.data
+
       // 获取到数据,优先从localStorage读取选中值
       const savedGrade = localStorage.getItem('selectedGrade')
-      selectedGrade.value =
-        savedGrade ||
-        (classData.value.length > 0 ? classData.value[0].ctType : '')
+      selectedGrade.value = savedGrade || (classData.value.length > 0 ? classData.value[0].ctType : '')
       // 初始化时获取课程大纲数据
-      const selectedItem =
-        classData.value.find(item => item.ctType === selectedGrade.value) ||
-        classData.value[0]
+      const selectedItem = classData.value.find(item => item.ctType === selectedGrade.value) || classData.value[0]
       if (selectedItem) {
         // 使用新函数获取课程大纲
         fetchClassOutline(selectedItem.id)
@@ -331,9 +346,9 @@ const fetchCtTypes = async () => {
 // 获取课程标题
 const getCourseTitle = index => {
   if (
-    classOutlineData.value.length > 0 &&
-    index > 0 &&
-    index <= classOutlineData.value.length
+      classOutlineData.value.length > 0 &&
+      index > 0 &&
+      index <= classOutlineData.value.length
   ) {
     return classOutlineData.value[index - 1].ctType
   }
@@ -343,17 +358,18 @@ const getCourseTitle = index => {
 // 首页点击渲染后的页面title
 const pageTitle = ref('返回首页')
 onMounted(() => {
+  //加载所有数据
   fetchCtTypes()
-  
+
   // 读取之前保存的选中状态
   const savedMenu = localStorage.getItem(activeMenuKey)
   const savedIndex = localStorage.getItem(activeIndexKey)
-  
+
   if (savedMenu && savedIndex) {
     currentOpenedMenu.value = savedMenu
     currentActiveIndex.value = savedIndex
   }
-  
+
   // 标记菜单已初始化,触发el-menu重新渲染
   menuInitialized.value = true
 
@@ -372,20 +388,21 @@ const SearchInput = ref('')
 const searchKey = ref(Date.now())
 // 当前显示的课程数据
 const currentCourseData = computed(() => {
-  return courseData.value[currentOpenedMenu.value] || courseData.value.general
+  const menu = menuConfig.value.find(m => m.id == currentOpenedMenu.value)
+  return (menu && menu.data.value) || generalCourseData.value
 })
 
 // 搜索建议查询方法
 const querySearch = (queryString, cb) => {
   const data = currentCourseData.value
   const results = queryString
-    ? data.filter(item => {
+      ? data.filter(item => {
         // 课程标题和序号查询
         return item.ctType.toLowerCase().includes(queryString.toLowerCase()) ||
-        // 类型检查,确保ctTypeSort是字符串类型
-               (item.ctTypeSort && item.ctTypeSort.toString().includes(queryString))
+            // 类型检查,确保ctTypeSort是字符串类型
+            (item.ctTypeSort && item.ctTypeSort.toString().includes(queryString))
       })
-    : data
+      : data
   cb(results)
 }
 
@@ -404,9 +421,9 @@ const filteredTitles = computed(() => {
     return data
   }
   return data.filter(title =>
-    title.ctType.toLowerCase().includes(SearchInput.value.toLowerCase()) ||
-    // 类型检查,确保ctTypeSort是字符串类型
-    (title.ctTypeSort && title.ctTypeSort.toString().includes(SearchInput.value))
+      title.ctType.toLowerCase().includes(SearchInput.value.toLowerCase()) ||
+      // 类型检查,确保ctTypeSort是字符串类型
+      (title.ctTypeSort && title.ctTypeSort.toString().includes(SearchInput.value))
   )
 })
 
@@ -422,17 +439,18 @@ const goBack = () => {
 const goToAIExperience = outlineData => {
   // 确定当前课程所属的菜单类型
   const menuId = currentOpenedMenu.value
-  
+
   // 找到当前课程在对应菜单中的索引
-  const currentData = courseData.value[menuId]
+  const menu = menuConfig.value.find(m => m.id === menuId)
+  const currentData = menu ? menu.data.value : []
   const index = currentData.findIndex(item => item.id === outlineData.id)
-  
+
   if (index !== -1) {
     const menuIndex = menuId + '-' + (index + 1)
     // 保存选中状态
     saveActiveState(menuId, menuIndex)
   }
-  
+
   // 课程跳转逻辑
   router.push({
     path: '/ai-develop', // 跳转视频页面
@@ -468,9 +486,9 @@ const goToAIExperience = outlineData => {
   display: flex;
   flex-direction: row;
   background: linear-gradient(
-    to bottom,
-    #e2ddfc,
-    #f1effd
+          to bottom,
+          #e2ddfc,
+          #f1effd
   ); /* 设置悬停、聚焦、点击状态下的背景色 */
   gap: rpx(0);
 }
@@ -485,31 +503,31 @@ const goToAIExperience = outlineData => {
   background-color: saddlebrown;
 }
 .main-content {
-    width: rpx(150);
-    height: 100%;
-    flex-grow: 1;
-    background: linear-gradient(to bottom, hsl(230, 100%, 21%), #8a78d0);  
-    position: relative;
-    overflow-y: auto; /* 添加垂直滚动条 */
-    max-height: 100%; /* 设置最大高度 */
-    transition: all 0.3s ease;
-    // 自定义滚动条样式
-    &::-webkit-scrollbar {
-      width: rpx(0); // 滚动条宽度
-    }
-    &::-webkit-scrollbar-track {
-      background-color: rgba(255, 255, 255, 0.1); // 滚动条轨道背景色
-      border-radius: rpx(2); // 滚动条轨道圆角
-    }
-    &::-webkit-scrollbar-thumb {
-      background-color: rgba(255, 255, 255, 0.3); // 滚动条滑块颜色
-      border-radius: rpx(2); // 滚动条滑块圆角
-      transition: background-color 0.3s ease; // 滑块颜色过渡效果
-    }
-    &::-webkit-scrollbar-thumb:hover {
-      background-color: rgba(255, 255, 255, 0.5); // 鼠标悬停时的滑块颜色
-    }
+  width: rpx(150);
+  height: 100%;
+  flex-grow: 1;
+  background: linear-gradient(to bottom, hsl(230, 100%, 21%), #8a78d0);
+  position: relative;
+  overflow-y: auto; /* 添加垂直滚动条 */
+  max-height: 100%; /* 设置最大高度 */
+  transition: all 0.3s ease;
+  // 自定义滚动条样式
+  &::-webkit-scrollbar {
+    width: rpx(0); // 滚动条宽度
+  }
+  &::-webkit-scrollbar-track {
+    background-color: rgba(255, 255, 255, 0.1); // 滚动条轨道背景色
+    border-radius: rpx(2); // 滚动条轨道圆角
+  }
+  &::-webkit-scrollbar-thumb {
+    background-color: rgba(255, 255, 255, 0.3); // 滚动条滑块颜色
+    border-radius: rpx(2); // 滚动条滑块圆角
+    transition: background-color 0.3s ease; // 滑块颜色过渡效果
   }
+  &::-webkit-scrollbar-thumb:hover {
+    background-color: rgba(255, 255, 255, 0.5); // 鼠标悬停时的滑块颜色
+  }
+}
 .icon-expand {
   width: rpx(8);
   height: rpx(35);
@@ -538,9 +556,9 @@ const goToAIExperience = outlineData => {
   display: flex;
   flex-direction: column; /* 子元素上下排列 */
   background: linear-gradient(
-    to bottom,
-    #e2ddfc,
-    #f1effd
+          to bottom,
+          #e2ddfc,
+          #f1effd
   ); /* 设置悬停、聚焦、点击状态下的背景色 */
 }
 .tac .el-menu {
@@ -582,21 +600,21 @@ const goToAIExperience = outlineData => {
 
 .el-sub-menu ::v-deep(.el-sub-menu__title:hover) {
   background: linear-gradient(
-    to bottom,
-    #ffefb0,
-    #ffcc00
+          to bottom,
+          #ffefb0,
+          #ffcc00
   );
   color: black;
   box-shadow: 0 4px 8px rgb(0, 0, 0, 0.2);
 }
 
 .el-sub-menu.is-opened ::v-deep(.el-sub-menu__title) {
-    background: linear-gradient(
-    to bottom,
-    #ffefb0,
-    #ffcc00
+  background: linear-gradient(
+          to bottom,
+          #ffefb0,
+          #ffcc00
   );
-    color: black;
+  color: black;
   box-shadow: 0 4px 8px rgb(0, 0, 0, 0.2);
 }
 
@@ -627,9 +645,9 @@ const goToAIExperience = outlineData => {
 .el-menu ::v-deep(.el-menu-item:focus),
 .el-menu ::v-deep(.el-menu-item:active) {
   background: linear-gradient(
-    to bottom,
-    #ffefb0,
-    #ffcc00
+          to bottom,
+          #ffefb0,
+          #ffcc00
   ); /* 设置悬停、聚焦、点击状态下的背景色 */
   box-shadow: 0 4px 8px rgb(0, 0, 0, 0.2);
   color: black;
@@ -775,9 +793,9 @@ const goToAIExperience = outlineData => {
 .dropdown-menu ::v-deep(.el-dropdown-menu__item:focus),
 .dropdown-menu ::v-deep(.el-dropdown-menu__item:active) {
   background: linear-gradient(
-    to bottom,
-    #fee78a,
-    #ffce1b
+          to bottom,
+          #fee78a,
+          #ffce1b
   ); /* 设置悬停、聚焦、点击状态下的背景色 */
 }
 .right-box {
@@ -789,7 +807,7 @@ const goToAIExperience = outlineData => {
   align-items: center;
 }
 .top-right-box {
-   width: rpx(130);
+  width: rpx(130);
   display: flex;
   justify-content: flex;
 }
@@ -910,23 +928,23 @@ const goToAIExperience = outlineData => {
 @function rpx($px) {
   @return math.div($px, 750) * 100vw;
 }
- .el-autocomplete-suggestion .el-scrollbar__wrap{
+.el-autocomplete-suggestion .el-scrollbar__wrap{
   margin: 0 auto;
   background-color: rgba(255, 255, 255, 0.7);
   border: 2px solid white;
   border-radius: rpx(5);
   backdrop-filter: blur(rpx(5));
 }
- .el-autocomplete-suggestion li{
+.el-autocomplete-suggestion li{
   color: black;
   font-size: rpx(7);
   padding: rpx(5) rpx(8); // 调整下拉项内边距
 }
- .el-autocomplete-suggestion li:hover{
+.el-autocomplete-suggestion li:hover{
   background: linear-gradient(
-    to bottom,
-    #ffefb0,
-    #ffcc00
+          to bottom,
+          #ffefb0,
+          #ffcc00
   );
 }
 </style>