HomePage.vue 13 KB

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