Bläddra i källkod

优化各主页路由

liyanbo 3 månader sedan
förälder
incheckning
577e843500

+ 2 - 2
src/components/HomePage.vue

@@ -29,14 +29,14 @@
             round
             class="top-right-btn"
             :class="{ 'is-active': selectedButton === 'AI编程课' }"
-            @click="router.push('/programming')"
+            @click="router.push('/blocklyHome')"
             >AI编程课</el-button
           >
           <el-button
             round
             class="top-right-btn"
             :class="{ 'is-active': selectedButton === 'AI实验课' }"
-            @click="router.push('/experimental-theme')"
+            @click="router.push('/aiCourseHome')"
             >AI实验课</el-button
           >
           <el-button

+ 1 - 1
src/components/sidebar/ProgrammingSidebar.vue

@@ -101,7 +101,7 @@ watch(() => route, () => {
 // 跳转智能问答
 const navigateToAI = async (group) => {
   if (group.title === 'blockly编程') {
-    router.push('/programming')
+    router.push('/blocklyHome')
   }
 }
 

+ 26 - 18
src/router/index.js

@@ -5,12 +5,16 @@ const routes = [
 
   { path: '/', component: () => import('../views/Login.vue') },
   { path: '/login', component: () => import('../views/Login.vue') },
-    //免租户
-  { path: '/blockly-login', meta: {TENANT: '内部测试租户'}, component: () => import('../views/BlocklyLogin.vue') },
   // 免登录
   { path: '/quick-login', component: () => import('../views/QuickLogin.vue') },
   { path: '/promotion-login', component: () => import('../views/PromotionLogin.vue') },
-  // 首页
+  //【AI实验课】登录
+  { path: '/ai-login', component: () => import('../views/AiCourseLogin.vue') },
+  //【blockly编程课】免租户登录
+  { path: '/blockly-login', meta: {TENANT: '内部测试租户'}, component: () => import('../views/BlocklyLogin.vue') },
+
+
+  // 【通识课】首页
   {
     path: '/home',
     component: () => import('../components/HomePage.vue')
@@ -36,7 +40,7 @@ const routes = [
     component: () => import('../views/evaluation/testList.vue')
   },
   // 在线测试
-   {
+  {
     path: '/testTopic',
     component: () => import('../views/evaluation/testTopic.vue')
   },
@@ -85,6 +89,13 @@ const routes = [
     path: '/virtual-laboratory',
     component: () => import('../views/virtuallaboratory/index.vue')
   },
+
+
+  // 【blockly编程课】首页
+  {
+    path: '/blocklyHome',
+    component: () => import('../views/programming/ProgrammingGame.vue')
+  },
   // Blockly
   { path: '/blockly', component: () => import('../views/blockly/Blockly.vue') },
   // Blockly2
@@ -92,45 +103,42 @@ const routes = [
   { path: '/mapGame', component: () => import('../views/blockly/MapGame.vue') },
   // 编程游戏列表
   { path: '/programming02', component: () => import('../views/gamepage/GameIndex.vue') },
-  // 编程课
-  {
-    path: '/programming', 
-    component: () => import('../views/programming/ProgrammingGame.vue')
-  },
   // 编程课列表
-    {
-    path: '/programminglist', 
+  {
+    path: '/programminglist',
     component: () => import('../views/programming/ProgrammingList.vue')
   },
   // 编程课列表内容
   {
-    path: '/programmingcourset', 
+    path: '/programmingcourset',
     component: () => import('../views/programming/ProgrammingCourset.vue')
   },
   // 编程课视频
   {
-    path: '/interface', 
+    path: '/interface',
     component: () => import('../views/programming/Interface.vue')
   },
-  // AI实验室
+
+
+  // 【AI实验课】首页
   // 实验室主题
   {
-    path: '/experimental-theme', 
+    path: '/aiCourseHome',
     component: () => import('../views/laboratory/ExperimentalTheme.vue')
   },
   // 实验室类型
   {
-    path: '/experiment-type', 
+    path: '/experiment-type',
     component: () => import('../views/laboratory/ExperimentType.vue')
   },
   // 实验课程
   {
-    path: '/experimental-course', 
+    path: '/experimental-course',
     component: () => import('../views/laboratory/ExperimentalCourses.vue')
   },
   // 实验界面
   {
-    path: '/experimental-interface', 
+    path: '/experimental-interface',
     component: () => import('../views/laboratory/ExperimentalInterface.vue')
   }
 ]

+ 344 - 0
src/views/AiCourseLogin.vue

@@ -0,0 +1,344 @@
+<template>
+  <!-- 登录页面 -->
+  <div class="login-content">
+    <!-- 背景图容器 -->
+    <div
+        class="bg-image-container"
+        :style="{ backgroundImage: `url(${BGImages})`, backgroundSize: 'cover' }"
+    ></div>
+    <!-- 登录输入框 -->
+    <div class="login-wrapper">
+      <div class="login-input">
+        <span>{{ appTitle }}</span>
+        <el-form
+            ref="loginFormRef"
+            :model="loginData.loginForm"
+            :rules="rules"
+            label-width="0px"
+            class="input-item"
+        >
+          <el-form-item prop="tenantName">
+            <el-input v-model="loginData.loginForm.tenantName"
+                      :prefix-icon="HomeFilled"
+                      placeholder="学校"
+            />
+          </el-form-item>
+
+          <!-- 条件显示手机号和短信验证码或账号和密码 -->
+          <template v-if="isAuthorized">
+            <el-form-item prop="username">
+              <el-input
+                  v-model="loginData.loginForm.username"
+                  :prefix-icon="Avatar"
+                  placeholder="账号"
+              />
+            </el-form-item>
+            <el-form-item prop="password">
+              <el-input
+                  v-model="loginData.loginForm.password"
+                  class="password-input"
+                  type="password"
+                  :prefix-icon="Lock"
+                  placeholder="密码"
+                  show-password
+              />
+            </el-form-item>
+          </template>
+
+          <template v-else>
+            <el-form-item prop="phoneNumber">
+              <el-input
+                  v-model="loginData.loginForm.phoneNumber"
+                  :prefix-icon="Iphone"
+                  placeholder="手机号"
+              />
+            </el-form-item>
+            <!-- 短信验证码输入框和获取验证码按钮 -->
+            <el-form-item prop="smsCode">
+              <div class="sms-code-container">
+                <el-input
+                    v-model="loginData.loginForm.smsCode"
+                    placeholder="短信验证码"
+                    class="sms-input"
+                />
+                <el-button
+                    type="primary"
+                    @click="handleGetSmsCode"
+                    :disabled="countingDown"
+                    class="get-code-btn"
+                    :loading="sendingCode"
+                >
+                  {{ countingDown ? `${countDown}秒后重新获取` : '获取验证码' }}
+                </el-button>
+              </div>
+            </el-form-item>
+          </template>
+
+          <!-- 登录按钮 -->
+          <el-form-item>
+            <el-button class="login-btn" type="primary" @click="handleLogin">登录</el-button>
+          </el-form-item>
+        </el-form>
+        <!-- 多选框 -->
+        <div class="check-box">
+          <el-checkbox
+              v-model="loginData.loginForm.rememberMe"
+              label="记住我"
+              size="large"
+          />
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted } from 'vue'
+import { useRouter } from 'vue-router'
+import { HomeFilled, Avatar, Lock, Iphone } from '@element-plus/icons-vue'
+import { ElMessage } from 'element-plus'
+
+import BGImages from '@/assets/images/homeBG.png'
+import {
+  createLoginData,
+  createVerificationCodeLogic,
+  getTenantId,
+  loginLogic,
+  loadLoginData,
+  checkLoginStatus,
+  generateRules
+} from '@/utils/loginUtils.js'
+
+const router = useRouter()
+
+// 获取环境变量
+const appTitle = import.meta.env.VITE_APP_TITLE
+
+const loginFormRef = ref(null)
+
+// 初始化登录数据
+const loginData = createLoginData()
+
+// 初始化验证码逻辑
+const { countingDown, countDown, sendingCode, getSmsCode, clearCountDownTimer } = createVerificationCodeLogic()
+
+// 登录状态标识
+const isLoggedIn = ref(false)
+
+// 授权状态 默认授权
+const isAuthorized = ref(true)
+
+// 生成表单验证规则
+const rules = generateRules(isAuthorized)
+
+// 获取短信验证码
+const handleGetSmsCode = async () => {
+  // 先验证租户和手机号是否填写
+  if (!loginData.value.loginForm.tenantName) {
+    ElMessage.warning('请先输入学校名称')
+    return
+  }
+  if (!loginData.value.loginForm.phoneNumber) {
+    ElMessage.warning('请先输入手机号')
+    return
+  }
+  // 验证租户是否存在
+  const tenantId = await getTenantId(loginData.value.loginForm.tenantName)
+  if (!tenantId) {
+    return
+  }
+
+  // 调用验证码逻辑
+  getSmsCode(tenantId, loginData.value.loginForm.tenantName, loginData.value.loginForm.phoneNumber)
+}
+
+// 登录
+const handleLogin = async () => {
+  if (!loginFormRef.value) return
+  await loginFormRef.value.validate(async valid => {
+    if (valid) {
+      // 先验证租户是否存在
+      const tenantId = await getTenantId(loginData.value.loginForm.tenantName)
+      if (!tenantId) {
+        // 租户验证失败,不执行登录
+        return
+      }
+
+      // 调用登录逻辑
+      await loginLogic(loginData.value.loginForm, tenantId, isAuthorized, router, '/aiCourseHome')
+    }
+  })
+}
+
+// 在组件挂载时检查登录状态和恢复登录信息
+onMounted(() => {
+  // 加载本地存储的登录数据
+  loadLoginData(loginData)
+
+  // 检查地址栏是否有tenantName参数
+  if (Object.keys(router.currentRoute.value.query).length > 0) {
+    // 其他参数,重定向到登录页
+    router.replace('/ai-login')
+  }
+
+  // 检查登录状态,如果已登录则直接跳转到首页
+  checkLoginStatus(router, '/aiCourseHome')
+
+  const handleKeyPress = (event) => {
+    // 检查是否按下回车键(keyCode 13)
+    if (event.key === 'Enter' || event.keyCode === 13) {
+      handleLogin()
+    }
+  }
+
+  document.addEventListener('keydown', handleKeyPress)
+
+  // 在组件卸载时移除事件监听
+  onUnmounted(() => {
+    document.removeEventListener('keydown', handleKeyPress)
+    clearCountDownTimer()
+  })
+})
+</script>
+
+<style scoped lang="scss">
+@use 'sass:math';
+// 定义rpx转换函数
+@function rpx($px) {
+  @return math.div($px, 750) * 100vw;
+}
+.login-content {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  display: flex;
+  flex-direction: row; // 修改为水平布局
+}
+.bg-image-container {
+  flex: 3; // 背景图占比为 3
+  background-size: cover;
+  background-position: center;
+}
+
+.login-wrapper {
+  flex: 1; // 登录框占比为 1
+  background: linear-gradient(to bottom, #001169, #8a78d0);
+  padding: 20px;
+  position: static;
+  transform: none;
+  display: flex; // 添加 Flexbox 布局
+  justify-content: center; // 水平居中
+  align-items: center; // 垂直居中
+}
+.login-input {
+  width: rpx(190);
+  height: rpx(240);
+  display: flex;
+  justify-content: center; // 水平居中
+  align-items: center; // 垂直居中
+  flex-direction: column; // 子元素垂直排列
+  text-align: center; // 文本居中
+}
+.login-input span{
+  color: white;
+  font-size: rpx(11);
+  padding-bottom: rpx(5);
+  letter-spacing: rpx(1);
+}
+.input-item {
+  display: flex;
+  flex-direction: column; // 子元素垂直排列
+  justify-content: center; // 内容垂直居中
+  align-items: center; // 内容水平居中
+}
+.el-input ::v-deep(.el-input__wrapper){
+  border-radius: rpx(5);
+}
+.input-item .el-form-item {
+  margin-bottom: 0;
+}
+.input-item .el-input {
+  width: rpx(150);
+  height: rpx(22);
+  margin-bottom: rpx(15);
+  font-size: rpx(7);
+}
+.el-form-item ::v-deep(.el-form-item__error) {
+  top: rpx(25);
+}
+.login-btn {
+  width: rpx(150);
+  height: rpx(22);
+  color: black;
+  font-size: rpx(8);
+  letter-spacing: rpx(10);
+  border-radius: rpx(5);
+  margin: rpx(15) 0 auto;
+  border: none;
+  background: linear-gradient(to bottom, #fee78a, #ffce1b);
+  box-shadow: 0 8px 8px rgb(0, 0, 0, 0.2);
+}
+.password-input {
+  margin-bottom: rpx(0) !important;
+}
+.check-box {
+  width: rpx(150);
+  height: rpx(18);
+  margin: rpx(5) auto;
+  display: flex;
+  justify-content: flex-end;
+  align-items: center;
+}
+.check-box .el-checkbox {
+  color: white;
+  padding-right: rpx(10);
+  font-size: rpx(6);
+}
+.el-checkbox ::v-deep(.el-checkbox__label){
+  font-size: rpx(6);
+}
+.check-box .forgot-password {
+  color: white;
+  font-size: rpx(6);
+  text-decoration: none;
+}
+
+// 短信验证码容器样式
+.sms-code-container {
+  display: flex;
+  align-items: center;
+  width: rpx(150);
+  height: rpx(22);
+}
+
+.sms-input {
+  flex: 1;
+  height: rpx(22);
+  margin-bottom: 0 !important;
+}
+.sms-input ::v-deep(.el-input__wrapper) {
+  border-top-right-radius: 0;
+  border-bottom-right-radius: 0;
+}
+
+.get-code-btn {
+  width: rpx(40);
+  height: rpx(22);
+  margin: 0 !important;
+  padding: 0;
+  font-size: rpx(5);
+  border-radius: rpx(5);
+  border-top-left-radius: 0;
+  border-bottom-left-radius: 0;
+  letter-spacing: normal;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background-color: #fff;
+  color: #919191;
+  // background: linear-gradient(to bottom, #78c0ff, #0070f3);
+  border: none;
+}
+</style>

+ 2 - 2
src/views/BlocklyLogin.vue

@@ -169,7 +169,7 @@ const handleLogin = async () => {
       }
 
       // 调用登录逻辑
-      await loginLogic(loginData.value.loginForm, tenantId, isAuthorized, router, '/programming')
+      await loginLogic(loginData.value.loginForm, tenantId, isAuthorized, router, '/blocklyHome')
     }
   })
 }
@@ -189,7 +189,7 @@ onMounted(() => {
   }
 
   // 检查登录状态,如果已登录则直接跳转到首页
-  checkLoginStatus(router, '/programming')
+  checkLoginStatus(router, '/blocklyHome')
 
   const handleKeyPress = (event) => {
     // 检查是否按下回车键(keyCode 13)

+ 1 - 1
src/views/laboratory/ExperimentType.vue

@@ -132,7 +132,7 @@ const scrollLeft = ref(0)
 
 // 返回上一页
 const goBackToTopic = () => {
-  router.push('/experimental-theme')
+  router.push('/aiCourseHome')
 }
 
 // 定义实验类型数据

+ 1 - 1
src/views/programming/ProgrammingList.vue

@@ -123,7 +123,7 @@ const categoryId = ref('')
 
 // 返回上一页
 const goBackIndex = () => {
-  router.push('/programming')
+  router.push('/blocklyHome')
 }
 
 // 定义课程数据