LeftPanel.vue 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. <template>
  2. <!-- 左侧折叠面板 -->
  3. <transition name="drawer-slide">
  4. <div class="left-group">
  5. <el-row class="tac">
  6. <el-col :span="12">
  7. <el-menu
  8. :default-active="currentActiveIndex"
  9. class="el-menu-vertical-demo"
  10. @open="handleOpen"
  11. @close="handleClose"
  12. >
  13. <el-menu-item
  14. v-for="(item, index) in groupList"
  15. :key="index"
  16. :index="index.toString()"
  17. @click="navigateToAI(item)"
  18. @mouseover="item.isHover = true"
  19. @mouseout="item.isHover = false"
  20. >
  21. <!-- 根据状态切换图片-->
  22. <img
  23. :src="currentActiveIndex === index.toString() || item.isHover ? item.hoverIcon : item.icon"
  24. alt=""
  25. class="menu-icon"
  26. />
  27. {{ item.title }}
  28. </el-menu-item>
  29. </el-menu>
  30. </el-col>
  31. </el-row>
  32. </div>
  33. </transition>
  34. </template>
  35. <script setup>
  36. import { ref, onMounted, watch } from 'vue'
  37. import { useRouter, useRoute } from 'vue-router'
  38. import { teacherList } from '@/api/teachers.js'
  39. // 导入图片
  40. // 白色图标
  41. import question from '@/assets/icon/question.png'
  42. import painting from '@/assets/icon/painting.png'
  43. import Human from '@/assets/icon/human.png'
  44. import image2image from '@/assets/icon/image2image.png'
  45. import video from '@/assets/icon/video.png'
  46. import labImage from '@/assets/icon/labImage.png'
  47. import en from '@/assets/icon/en.png'
  48. import canal from '@/assets/icon/canal.png'
  49. import plant from '@/assets/icon/plant.png'
  50. import poetry from '@/assets/icon/poetry.png'
  51. // 黑色图标
  52. import question02 from '@/assets/icon/question02.png'
  53. import painting02 from '@/assets/icon/painting02.png'
  54. import Human02 from '@/assets/icon/Human02.png'
  55. import image2image02 from '@/assets/icon/image2image02.png'
  56. import video02 from '@/assets/icon/video02.png'
  57. import labImage02 from '@/assets/icon/labImage02.png'
  58. import en02 from '@/assets/icon/en02.png'
  59. import canal02 from '@/assets/icon/canal02.png'
  60. import plant02 from '@/assets/icon/plant02.png'
  61. import poetry02 from '@/assets/icon/poetry02.png'
  62. const router = useRouter()
  63. const route = useRoute()
  64. // 添加抽屉显示状态
  65. const drawerVisible = ref(true)
  66. // 当前选中索引状态
  67. const currentActiveIndex = ref('2')
  68. // 路由路径到索引的映射
  69. const pathIndexMap = {
  70. 'ai-questions': '0', // 智能问答
  71. 'ai-painting': '1', // 智能绘画
  72. 'ai-laboratory': '2', // 数字人老师
  73. 'ai-poetry': '3' , // AI 古诗
  74. 'ai-ennumerals': '4', // 英文数字人老师
  75. 'ai-image': '5', // 图生图
  76. 'ai-video': '6', // 图生视频
  77. 'ai-grandcanal': '7', // 大运河
  78. 'ai-plantexperts': '8', // 植物专家
  79. 'virtual-laboratory': '9', // 虚拟实验室
  80. }
  81. // 菜单项到路由的映射
  82. const menuRouteMap = {
  83. '智能问答': '/ai-questions',
  84. '智能绘画': '/ai-painting',
  85. '数字人老师': '/ai-laboratory',
  86. 'AI+古诗人': '/ai-poetry',
  87. '英文数字人老师': '/ai-ennumerals',
  88. '图生图': '/ai-image',
  89. '图生视频': '/ai-video',
  90. '大运河': '/ai-grandcanal',
  91. '植物专家': '/ai-plantexperts',
  92. '虚拟实验室': '/virtual-laboratory',
  93. }
  94. // 渲染侧边栏
  95. const groupList = ref([
  96. {
  97. icon: question, // 默认图片
  98. hoverIcon: question02, // 交互图片
  99. title: '智能问答'
  100. },
  101. {
  102. icon: painting,
  103. hoverIcon: painting02,
  104. title: '智能绘画'
  105. },
  106. {
  107. icon: Human,
  108. hoverIcon: Human02,
  109. title: '数字人老师'
  110. },
  111. {
  112. icon: poetry,
  113. hoverIcon: poetry02,
  114. title: 'AI+古诗人'
  115. },
  116. {
  117. icon: en,
  118. hoverIcon: en02,
  119. title: '英文数字人老师'
  120. },
  121. {
  122. icon: image2image,
  123. hoverIcon: image2image02,
  124. title: '图生图'
  125. },
  126. {
  127. icon: video,
  128. hoverIcon: video02,
  129. title: '图生视频'
  130. },
  131. {
  132. icon: canal,
  133. hoverIcon: canal02,
  134. title: '大运河'
  135. },
  136. {
  137. icon: plant,
  138. hoverIcon: plant02,
  139. title: '植物专家'
  140. },
  141. {
  142. icon: labImage,
  143. hoverIcon: labImage02,
  144. title: '虚拟实验室'
  145. }
  146. ])
  147. // 更新选中状态的方法
  148. const updateActiveIndex = () => {
  149. const path = route.path
  150. const from = window.history.state?.from // 从history.state获取来源信息
  151. // 从数字人老师页面进入智能问答页面
  152. if (path.includes('ai-questions') && from === 'ai-laboratory') {
  153. currentActiveIndex.value = '2' // 保持选中数字人老师
  154. return
  155. }
  156. // 从英文数字人老师页面进入智能问答页面
  157. if (path.includes('ai-questions') && from === 'ai-ennumerals') {
  158. currentActiveIndex.value = '4' // 保持选中英文数字人老师
  159. return
  160. }
  161. // 从大运河页面进入智能问答页面
  162. if (path.includes('ai-questions') && from === 'ai-grandcanal') {
  163. currentActiveIndex.value = '6' // 保持选中大运河
  164. return
  165. }
  166. // 从AI+古诗页面进入智能问答页面
  167. if (path.includes('ai-questions') && from === 'ai-poetry') {
  168. currentActiveIndex.value = '3' // 保持选中AI+古诗
  169. return
  170. }
  171. // 查找路径对应的索引
  172. for (const [key, index] of Object.entries(pathIndexMap)) {
  173. if (path.includes(key)) {
  174. currentActiveIndex.value = index
  175. return
  176. }
  177. }
  178. }
  179. // 组件挂载时确保默认选中状态
  180. onMounted(() => {
  181. updateActiveIndex()
  182. })
  183. // 添加路由变化监听,更新选中状态
  184. watch(() => route.path, () => {
  185. updateActiveIndex()
  186. }, { immediate: true })
  187. // 存储小智数据
  188. const personData = ref([])
  189. // 跳转智能问答
  190. const navigateToAI = async (group) => {
  191. const routePath = menuRouteMap[group.title]
  192. if (group.title === '智能问答') {
  193. try {
  194. const grade = route.query.grade || localStorage.getItem('selectedGrade')
  195. // 获取小学低年级AI数据
  196. const juniorAIRes = await teacherList({ category: grade + 'AI' })
  197. const aiPerson = juniorAIRes.data.list.find(
  198. person => person.name === '小智'
  199. )
  200. if (aiPerson) {
  201. personData.value = {
  202. id: aiPerson.id,
  203. name: aiPerson.name,
  204. image: aiPerson.model2dPath,
  205. message: aiPerson.systemMessage,
  206. default: aiPerson.questTip
  207. }
  208. const newState = {
  209. ...personData.value,
  210. category: grade + 'AI'
  211. }
  212. // 先 push 再手动更新 history.state(确保原生 state 同步)
  213. router
  214. .push({
  215. path: routePath,
  216. state: newState
  217. })
  218. .then(() => {
  219. // 手动替换当前历史记录的 state 为最新值
  220. window.history.replaceState(newState, '', window.location.href)
  221. window.location.reload()
  222. })
  223. } else {
  224. console.warn('未找到名为小智的数据')
  225. }
  226. } catch (error) {
  227. console.error('获取小学低年级AI数据失败:', error)
  228. }
  229. } else {
  230. // 其他菜单项直接跳转
  231. router.push(routePath)
  232. }
  233. }
  234. // 处理菜单展开和关闭
  235. const handleOpen = () => {}
  236. const handleClose = () => {}
  237. // 导出状态和方法供父组件使用
  238. defineExpose({
  239. drawerVisible,
  240. toggleDrawer: () => {
  241. drawerVisible.value = !drawerVisible.value
  242. }
  243. })
  244. </script>
  245. <style scoped lang="scss">
  246. @use 'sass:math';
  247. // 定义rpx转换函数
  248. @function rpx($px) {
  249. @return math.div($px, 750) * 100vw;
  250. }
  251. /* 添加过渡样式 */
  252. ::v-deep .drawer-slide-enter-active,
  253. ::v-deep .drawer-slide-leave-active {
  254. transition: all 0.3s ease;
  255. }
  256. ::v-deep .drawer-slide-enter-from,
  257. ::v-deep .drawer-slide-leave-to {
  258. transform: translateX(-100%);
  259. opacity: 0;
  260. }
  261. // 侧边栏
  262. .left-group {
  263. width: rpx(135);
  264. height: 100%;
  265. background: linear-gradient(to bottom, #001169, #8a78d0);
  266. overflow-y: auto;
  267. // 自定义滚动条样式
  268. &::-webkit-scrollbar {
  269. width: rpx(0); // 滚动条宽度
  270. }
  271. &::-webkit-scrollbar-track {
  272. background-color: rgba(255, 255, 255, 0.1); // 滚动条轨道背景色
  273. border-radius: rpx(2); // 滚动条轨道圆角
  274. }
  275. &::-webkit-scrollbar-thumb {
  276. background-color: rgba(255, 255, 255, 0.3); // 滚动条滑块颜色
  277. border-radius: rpx(2); // 滚动条滑块圆角
  278. transition: background-color 0.3s ease; // 滑块颜色过渡效果
  279. }
  280. &::-webkit-scrollbar-thumb:hover {
  281. background-color: rgba(255, 255, 255, 0.5); // 鼠标悬停时的滑块颜色
  282. }
  283. }
  284. .mb-2 {
  285. color: black;
  286. margin-top: rpx(1);
  287. }
  288. .tac ::v-deep(.el-menu) {
  289. background-color: transparent;
  290. border: none;
  291. width: 100%;
  292. margin-top: rpx(25);
  293. margin-left: rpx(10);
  294. }
  295. .el-menu-item {
  296. width: rpx(115);
  297. height: rpx(25);
  298. margin-bottom: rpx(5);
  299. border-radius: rpx(6);
  300. color: white;
  301. font-size: rpx(8);
  302. }
  303. .el-menu-item .el-icon svg {
  304. font-size: rpx(15);
  305. color: white;
  306. }
  307. .el-menu .el-menu-item:hover {
  308. background: linear-gradient(to bottom, #ffefb0, #ffcc00);
  309. box-shadow: 0 8px 8px rgb(0, 0, 0, 0.3);
  310. color: black;
  311. font-size: rpx(8);
  312. }
  313. :deep(.el-menu .el-menu-item.is-active) {
  314. background: linear-gradient(to bottom, #fee78a, #ffce1b);
  315. color: black;
  316. font-size: rpx(8);
  317. box-shadow: 0 4px 8px rgba(3, 3, 3, 0.3);
  318. }
  319. .menu-icon {
  320. width: rpx(11);
  321. height: rpx(11);
  322. margin-right: rpx(2);
  323. }
  324. </style>