HomePage.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  1. <template>
  2. <div class="home-container">
  3. <div class="box-1">
  4. <div class="inner-box left-box">
  5. <span>{{ platformTitle }}</span>
  6. <div class="dropdown-box">
  7. <!-- 下拉菜单 -->
  8. <el-dropdown v-model="selectedGrade" @command="handleGradeSelect" popper-class="no-arrow-dropdown">
  9. <el-button type="primary">
  10. {{ selectedGrade }}
  11. <el-icon class="el-icon--right"><ArrowDown /></el-icon>
  12. </el-button>
  13. <template #dropdown>
  14. <el-dropdown-menu class="dropdown-menu">
  15. <el-dropdown-item
  16. v-for="item in classData"
  17. :key="item.id"
  18. :command="item.ctType"
  19. >{{ item.ctType }}</el-dropdown-item
  20. >
  21. </el-dropdown-menu>
  22. </template>
  23. </el-dropdown>
  24. </div>
  25. </div>
  26. <div class="inner-box right-box">
  27. <div class="top-right-box">
  28. <el-button
  29. round
  30. class="top-right-btn"
  31. :class="{ 'is-active': selectedButton === 'AI编程课' }"
  32. @click="router.push(blocklyRoutes.home)"
  33. >AI编程课</el-button
  34. >
  35. <el-button
  36. round
  37. class="top-right-btn"
  38. :class="{ 'is-active': selectedButton === 'AI实验课' }"
  39. @click="router.push(aiCourseRoutes.home)"
  40. >AI实验课</el-button
  41. >
  42. <el-button
  43. round
  44. class="top-right-btn"
  45. :class="{ 'is-active': selectedButton === 'AI艺术课' }"
  46. @click="
  47. ElMessage.warning('此版本未开放,敬请期待!')
  48. "
  49. >AI艺术课</el-button
  50. >
  51. <!-- 用户名显示 -->
  52. <div class="user-name-box">
  53. {{ userName }}
  54. </div>
  55. <!-- 退出登录 -->
  56. <el-button round class="logout-box-btn" @click="LogoutClick()">
  57. <img :src="logoutIcon" alt="Logout" />
  58. 退出登录
  59. </el-button>
  60. </div>
  61. </div>
  62. </div>
  63. <div class="box-2">
  64. <div
  65. class="left-box-in-box2"
  66. @click="goToAIGeneralCourse('AI智能课')"
  67. :style="{ backgroundImage: `url(${indexImages[0]})` }"
  68. >
  69. <span>智能课</span>
  70. </div>
  71. <div
  72. class="center-box-in-box2"
  73. @click="goToAILab()"
  74. :style="{ backgroundImage: `url(${indexImages[1]})` }"
  75. >
  76. <span>AI实验室</span>
  77. </div>
  78. <div class="right-box-in-box2">
  79. <div
  80. class="top-sub-box"
  81. :style="{ backgroundImage: `url(${indexImages[2]})` }"
  82. @click="goToEvaluation"
  83. >
  84. <span>能力测评</span>
  85. </div>
  86. <div
  87. class="bottom-sub-box"
  88. :style="{ backgroundImage: `url(${indexImages[3]})` }"
  89. @click="goToPersonalized"
  90. >
  91. <span>评估报告</span>
  92. </div>
  93. </div>
  94. </div>
  95. </div>
  96. </template>
  97. <script setup>
  98. import { ref, onMounted, watch } from 'vue'
  99. import { useRouter } from 'vue-router'
  100. import { ClassList } from '@/api/class.js'
  101. import {ArrowDown} from '@element-plus/icons-vue'
  102. import { ElMessage } from 'element-plus'
  103. // 导入图片
  104. import intelligenceImg from '@/assets/images/intelligence.png'
  105. import roomImg from '@/assets/images/room.png'
  106. import testImg from '@/assets/images/test.png'
  107. import studyImg from '@/assets/images/study.png'
  108. // 退出登录图标
  109. import logoutIcon from '@/assets/icon/logout.png'
  110. // 退出登录
  111. import {logoutLogic, removeLocalStorageKey} from '@/utils/loginUtils.js'
  112. import {aiCourseRoutes, blocklyRoutes, homeRoutes} from "@/router/index.js";
  113. // 平台标题响应式变量
  114. const platformTitle = ref(import.meta.env.VITE_APP_TITLE)
  115. // 用户名响应式变量
  116. const userName = ref(import.meta.env.VITE_APP_DEFAULT_LOGIN_USERNAME)
  117. // 更新平台标题
  118. const updatePlatformTitle = () => {
  119. platformTitle.value = localStorage.getItem('tenantName') || import.meta.env.VITE_APP_DEFAULT_LOGIN_TENANT
  120. }
  121. // 更新用户名
  122. const updateUserName = () => {
  123. userName.value = localStorage.getItem('userName') || import.meta.env.VITE_APP_DEFAULT_LOGIN_USERNAME
  124. }
  125. // 获取当前路由对象
  126. const router = useRouter()
  127. // 退出登录
  128. const LogoutClick = async () => {
  129. await logoutLogic(router, homeRoutes.login)
  130. }
  131. // 默认选中 AI 通识课
  132. const selectedButton = ref('AI通识课')
  133. // 图片路径
  134. const indexImages = ref([intelligenceImg, roomImg, testImg, studyImg])
  135. // AI初体验
  136. const goToAIGeneralCourse = title => {
  137. router.push({ path: '/ai-general-course', state: { title } })
  138. }
  139. //AI实验室
  140. const goToAILab = () => {
  141. router.push({
  142. path: '/ai-laboratory',
  143. // 跳转页面携带下拉菜单选中项的值
  144. state: { grade: selectedGrade.value }
  145. })
  146. }
  147. // 能力测评
  148. const goToEvaluation = () =>{
  149. router.push({
  150. path:'/evaluation'
  151. })
  152. }
  153. // 评估报告
  154. const goToPersonalized = () =>{
  155. router.push({
  156. path:'/personalized'
  157. })
  158. }
  159. // 下拉菜单选中项
  160. const selectedGrade = ref(localStorage.getItem('selectedGrade') || '')
  161. // 年级ID存储变量
  162. const selectedGradeId = ref(localStorage.getItem('selectedGradeId') || '')
  163. // 获取年级
  164. const classData = ref([])
  165. const fetchCtTypes = async () => {
  166. try {
  167. const response = await ClassList()
  168. if (response.code === 0) {
  169. classData.value = response.data
  170. // 获取到数据,将第一个选项的值作为默认选中值
  171. if (classData.value.length > 0 && !selectedGrade.value) {
  172. selectedGrade.value = classData.value[0].ctType
  173. selectedGradeId.value = classData.value[0].id
  174. localStorage.setItem('selectedGrade', selectedGrade.value)
  175. localStorage.setItem('selectedGradeId', selectedGradeId.value)
  176. }
  177. }
  178. } catch (error) {
  179. console.error('获取 ctType 数据失败:', error)
  180. }
  181. }
  182. // 监听 selectedGrade 变化,保存到 localStorage
  183. watch(selectedGrade, (newValue) => {
  184. if (newValue) {
  185. localStorage.setItem('selectedGrade', newValue)
  186. // 当年级名称变化时,查找对应的ID
  187. const selectedItem = classData.value.find(item => item.ctType === newValue)
  188. if (selectedItem) {
  189. selectedGradeId.value = selectedItem.id
  190. localStorage.setItem('selectedGradeId', selectedGradeId.value)
  191. }
  192. }
  193. })
  194. // 监听 selectedGradeId 变化,保存到 localStorage
  195. watch(selectedGradeId, (newValue) => {
  196. if (newValue) {
  197. localStorage.setItem('selectedGradeId', newValue)
  198. }
  199. })
  200. // 处理下拉菜单选择
  201. const handleGradeSelect = (command) => {
  202. selectedGrade.value = command
  203. // 查找对应的ID
  204. const selectedItem = classData.value.find(item => item.ctType === command)
  205. if (selectedItem) {
  206. selectedGradeId.value = selectedItem.id
  207. }
  208. }
  209. onMounted(() => {
  210. fetchCtTypes()
  211. // 初始化平台标题
  212. updatePlatformTitle()
  213. // 初始化用户名
  214. updateUserName()
  215. // storage事件监听器,监听其他标签页对localStorage的修改
  216. window.addEventListener('storage', (e) => {
  217. if (e.key === 'tenantName') {
  218. updatePlatformTitle()
  219. }
  220. if (e.key === 'userName') {
  221. updateUserName()
  222. }
  223. })
  224. //删除所有以token开头的键值对
  225. removeLocalStorageKey(localStorage.getItem("token") + "_ai_")//AI实验室
  226. removeLocalStorageKey(localStorage.getItem('token') + "_course_")//通识课、实操课
  227. removeLocalStorageKey(localStorage.getItem("token") + "_blockly_")//编程课
  228. removeLocalStorageKey(localStorage.getItem("token") + "_aiCourse_")//AI实验课
  229. })
  230. // 全局:更新租户名称
  231. window.updateTenantName = (newName) => {
  232. localStorage.setItem('tenantName', newName)
  233. updatePlatformTitle()
  234. }
  235. </script>
  236. <style scoped lang="scss">
  237. @use 'sass:math';
  238. // 定义rpx转换函数
  239. @function rpx($px) {
  240. @return math.div($px, 750) * 100vw;
  241. }
  242. .logout-box {
  243. width: rpx(100);
  244. position: fixed;
  245. }
  246. .logout-box-btn {
  247. width: rpx(65); // 使用 rpx 函数设置按钮宽度
  248. height: rpx(15); // 使用 rpx 函数设置按钮高度
  249. margin: rpx(10) rpx(10) 0 0; // 使用 rpx 函数设置外边距
  250. background-color: transparent;
  251. color: white;
  252. border: none; // 移除默认边框
  253. font-size: rpx(7); // 使用 rpx 函数设置字体大小
  254. outline: none; // 移除默认的外边框
  255. }
  256. .logout-box-btn img {
  257. width: rpx(10);
  258. }
  259. // 用户名显示
  260. .user-name-box {
  261. width: auto;
  262. height: rpx(15);
  263. margin: rpx(10) rpx(-10) 0 0;
  264. color: white;
  265. font-size: rpx(8);
  266. display: flex;
  267. justify-content: center;
  268. align-items: center;
  269. background-color: rgba(255, 255, 255, 0.2);
  270. border-radius: rpx(15);
  271. padding: rpx(0) rpx(10);
  272. min-width: rpx(20);
  273. }
  274. .home-container {
  275. position: fixed;
  276. top: 0;
  277. left: 0;
  278. right: 0;
  279. bottom: 0;
  280. background: linear-gradient(to bottom, #001169, #8a78d0);
  281. display: flex;
  282. flex-direction: column;
  283. gap: rpx(0);
  284. }
  285. .box-1 {
  286. width: 100%;
  287. height: rpx(50);
  288. display: flex;
  289. justify-content: center;
  290. align-items: center;
  291. box-sizing: border-box;
  292. font-size: rpx(16); // 默认字体大小
  293. }
  294. .box-2 {
  295. width: 90%;
  296. margin: auto;
  297. flex: 1;
  298. display: flex;
  299. justify-content: space-between;
  300. align-items: center;
  301. box-shadow: 0 4px 8px rgba(202, 52, 52, 0.1);
  302. box-sizing: border-box;
  303. // padding: 0 rpx(20); // 添加左右内边距
  304. cursor: pointer; // 添加鼠标指针样式
  305. }
  306. .box-2 span {
  307. // 添加 padding,使文字距上边和左边留有间距
  308. padding: rpx(10) 0 0 rpx(10);
  309. font-size: rpx(12); // 默认字体大小
  310. color: white;
  311. }
  312. .left-box-in-box2,
  313. .center-box-in-box2 {
  314. background-color: rgba(133, 135, 176, 0.5);
  315. border-radius: rpx(20);
  316. flex: 1; // 让三个盒子平均分配空间
  317. margin: 0 rpx(10); // 添加左右间距
  318. height: 85%; // 高度占满容器
  319. display: flex;
  320. justify-content: flex-start;
  321. align-items: flex-start;
  322. background-origin: border-box; // 确保背景图从边框开始显示
  323. background-clip: padding-box; // 确保背景图不会延伸到边框外
  324. }
  325. .left-box-in-box2:hover,
  326. .left-box-in-box2:active,
  327. .center-box-in-box2:hover,
  328. .center-box-in-box2:active {
  329. box-shadow: 0 4px 8px rgba(0, 0, 0, 0.5);
  330. }
  331. .left-box-in-box2,
  332. .center-box-in-box2,
  333. .top-sub-box,
  334. .bottom-sub-box {
  335. background-repeat: no-repeat;
  336. background-size: cover;
  337. background-position: center;
  338. }
  339. .right-box-in-box2 {
  340. flex: 1; // 让三个盒子平均分配空间
  341. margin: 0 rpx(10); // 添加左右间距
  342. height: 85%; // 高度占满容器
  343. display: flex;
  344. // 确保两个子盒子上下排列
  345. flex-direction: column;
  346. justify-content: flex-start;
  347. align-items: flex-start;
  348. gap: rpx(10);
  349. }
  350. .top-sub-box,
  351. .bottom-sub-box {
  352. background-color: rgba(133, 135, 176, 0.5);
  353. flex: 1; // 让两个子盒子平均分配空间;
  354. display: flex;
  355. border-radius: rpx(20);
  356. width: 100%; // 宽度占满容器;
  357. }
  358. .top-sub-box:hover,
  359. .top-sub-box:active,
  360. .bottom-sub-box:hover,
  361. .bottom-sub-box:active {
  362. box-shadow: 0 4px 8px rgba(0, 0, 0, 0.5);
  363. }
  364. .inner-box {
  365. height: 100%;
  366. display: flex;
  367. justify-content: center;
  368. align-items: center;
  369. font-size: rpx(16); // 默认字体大小
  370. }
  371. .left-box {
  372. position: relative;
  373. justify-content: flex-start;
  374. align-items: center;
  375. display: flex;
  376. flex: 1;
  377. padding-left: rpx(30);
  378. cursor: pointer;
  379. }
  380. .left-box span {
  381. position: static;
  382. margin-top: 0;
  383. margin-left: 0;
  384. margin-right: rpx(10); // 与下拉菜单之间的间距
  385. font-size: rpx(11);
  386. color: white;
  387. max-width: rpx(200); // 最大宽度限制
  388. white-space: normal; // 允许换行
  389. line-height: rpx(16); // 行高
  390. text-align: left;
  391. }
  392. .right-box {
  393. flex: 1;
  394. position: relative; // 添加相对定位;
  395. margin-right: rpx(25);
  396. }
  397. .top-right-box {
  398. position: absolute; // 添加绝对定位
  399. width: 100%; // 设置盒子宽度,可按需调整
  400. height: 60px; // 设置盒子高度,可按需调整
  401. display: flex;
  402. justify-content: flex-end;
  403. cursor: pointer; // 添加鼠标指针样式
  404. }
  405. .top-right-btn {
  406. width: rpx(50); // 使用 rpx 函数设置按钮宽度
  407. height: rpx(15); // 使用 rpx 函数设置按钮高度
  408. margin: rpx(10) rpx(5) 0 0; // 使用 rpx 函数设置外边距
  409. background-color: transparent;
  410. color: white;
  411. border: none; // 移除默认边框
  412. font-size: rpx(8); // 使用 rpx 函数设置字体大小
  413. outline: none; // 移除默认的外边框
  414. }
  415. .top-right-btn:hover {
  416. background-color: rgb(255, 255, 255, 0.7);
  417. border: 1px white solid;
  418. color: black;
  419. outline: none;
  420. box-shadow: 0 4px 8px rgba(0, 0, 0, 0.5);
  421. }
  422. .dropdown-box {
  423. align-items: center; // 垂直居中;
  424. margin-top: 0;
  425. display: flex;
  426. }
  427. .dropdown-box .el-button {
  428. width: rpx(60); // 设置按钮宽度;
  429. height: rpx(15); // 设置按钮高度;
  430. background-color: rgb(255, 255, 255, 0.7);
  431. border: 1px white solid;
  432. box-shadow: 0 4px 8px rgb(0, 0, 0, 0.3);
  433. color: black;
  434. border-radius: rpx(12);
  435. font-size: rpx(8); // 设置字体大小;
  436. }
  437. .dropdown-box .el-button:hover,
  438. .dropdown-box .el-button:focus,
  439. .dropdown-box .el-button:active {
  440. border: none; /* 移除悬停、聚焦、点击状态下的边框 */
  441. outline: none; /* 移除悬停、聚焦、点击状态下的轮廓线 el-scrollbar__view el-dropdown__list */
  442. }
  443. .dropdown-menu {
  444. width: rpx(100);
  445. // height: rpx(60);
  446. border-radius: rpx(5);
  447. border: 1px white solid;
  448. background-color: rgb(255, 255, 255,0.5);
  449. backdrop-filter: blur(rpx(5));
  450. box-shadow: 0 4px 8px rgba(202, 52, 52, 0.1);
  451. margin-left: rpx(40);
  452. }
  453. .el-scrollbar__view .el-dropdown__list{
  454. background-color: transparent;
  455. }
  456. .dropdown-menu ::v-deep(.el-dropdown-menu__item) {
  457. font-size: rpx(8);
  458. color: black;
  459. border-radius: rpx(5);
  460. width: rpx(78);
  461. height: rpx(20);
  462. margin-left: rpx(4);
  463. margin-bottom: rpx(8);
  464. }
  465. .dropdown-menu ::v-deep(.el-dropdown-menu__item:hover),
  466. .dropdown-menu ::v-deep(.el-dropdown-menu__item:focus),
  467. .dropdown-menu ::v-deep(.el-dropdown-menu__item:active) {
  468. background: linear-gradient(
  469. to bottom,
  470. #fee78a,
  471. #ffce1b
  472. );
  473. box-shadow: 0 4px 8px rgba(202, 52, 52, 0.1);
  474. }
  475. </style>
  476. <style lang="scss">
  477. /* 消除小三角 */
  478. .el-popper__arrow{
  479. display: none;
  480. }
  481. .el-popper.is-light,
  482. .el-dropdown__popper.el-popper{
  483. background: transparent;
  484. border: none;
  485. box-shadow: none;
  486. }
  487. .el-dropdown__popper{
  488. --el-dropdown-menuItem-hover-color: none;
  489. }
  490. </style>