Kaynağa Gözat

1、添加新的编程课注册页,并设置租户信息隐藏
2、AI自主学习添加新的通用课类型并绑定value
3、解决对话中上下按钮Bug

丸子 4 hafta önce
ebeveyn
işleme
e4c66d0b85

+ 11 - 3
src/components/study/SelfDirectedLearning.vue

@@ -185,10 +185,13 @@ const lessonList = ref([
   {id: '2', name: '知识讲解'},
   {id: '3', name: '课程总结'}
 ])
-//主讲人、助讲人
+// 主讲人、助讲人
 const selectedMainTeacher = ref('')
 const selectedAssistants = ref([''])
 
+// 选中的主题value
+const selectedThemeValue = ref('')
+
 //脚本数据
 const scriptData = reactive(
     {
@@ -317,7 +320,7 @@ const generateScript = async () => {
     //2、生成课程小节
     progressSteps.value[1].visible = true;
     progressSteps.value[1].active = true;
-    await createAiRoleIdConversation(256)
+    await createAiRoleIdConversation(selectedThemeValue.value)
     const role = localScriptRoles.value.find((r) => r.name === selectedMainTeacher.value)
     const assistants = selectedAssistants.value.map(rName => {
       const assistantRole = localScriptRoles.value.find(r => r.name === rName)
@@ -932,6 +935,11 @@ const themeOptions = computed(() => {
       value: 256, // 诗词课生成脚本id
       sections: [1, 2, 3] // 绑定课程小节id
     },
+    {
+      name: '通用课程',
+      value: 13,
+      sections: [1, 2, 3] 
+    }
   ]
   
   // 为每个主题添加label getter
@@ -1006,6 +1014,7 @@ const assistantDropdown = ref({
 const handleSelect = (key, command) => {
   if (key === 'course' && courseDropdown.value) {
     courseDropdown.value.value = command;
+    console.log( courseDropdown.value);
   }
   else if (key === 'teacher' && teacherDropdown.value) {
     teacherDropdown.value.value = command;
@@ -1014,7 +1023,6 @@ const handleSelect = (key, command) => {
   else if (key === 'assistant' && assistantDropdown.value) {
     assistantDropdown.value.value = command;
     selectedAssistants.value = [command];
-    console.log('【助教下拉框选择】', command);
   }
 };
 

+ 6 - 2
src/router/index.js

@@ -20,7 +20,11 @@ const routes = [
     path: '/register-login',
     component: () => import('../views/RegisterLogin.vue')
   },
-
+  // 编程课注册页
+  {
+    path: '/blockly-register',
+    component: () => import('../views/BlocklyRegister.vue')
+  },
   // 管理界面
   {
     path: '/management-interface',
@@ -243,7 +247,7 @@ const router = createRouter({
 // 导航守卫
 router.beforeEach(async (to, from, next) => {
   // 注册页面始终允许访问,不需要登录状态
-  if (to.path === '/register-login') {
+  if ( ['/register-login', '/blockly-register'].includes(to.path)) {
     next()
     return
   }

+ 10 - 0
src/views/AIPage/aiGenerate/DialogContent.vue

@@ -415,6 +415,11 @@ const playSequence = () => {
 }
 
 const playPrevious = () => {
+  // 如果已经到达第一句,直接返回,不执行任何操作
+  if (currentDialogueIndex.value === 0 && currentSectionIndex.value === 0) {
+    return
+  }
+  
   // 停止当前音频
   stopAllAudio()
 
@@ -468,6 +473,11 @@ const playPrevious = () => {
 }
 
 const playNext = (isAutoPlay = false) => {
+  // 如果已经到达最后一句,直接返回,不执行任何操作
+  if (currentSection.value && currentDialogueIndex.value === currentSection.value.dialogues.length - 1 && currentSectionIndex.value === props.scriptData.sections.length - 1) {
+    return false
+  }
+  
   // 停止当前对话语音
   if (dialogueAudio.value) {
     dialogueAudio.value.pause()

+ 491 - 0
src/views/BlocklyRegister.vue

@@ -0,0 +1,491 @@
+<!-- 编程课注册页 -->
+<template>
+  <!-- 注册输入框 -->
+  <div class="login-content">
+    <!-- 注册输入框 -->
+    <div class="login-wrapper">
+      <!-- 左侧图片背景盒子 -->
+      <div class="login-left">
+        <img :src="registerBG" alt="背景图片" class="bg-image">
+      </div>
+      <!-- 右侧输入框盒子 -->
+      <div class="login-right">
+        <div class="login-input">
+          <span>{{ appTitle }}</span>
+          <el-form
+            ref="loginFormRef"
+            :model="loginForm"
+            :rules="rules"
+            label-width="0px"
+            class="input-item"
+          >
+            <!-- 学校输入框 -->
+            <el-form-item prop="tenantName" v-if="false">
+              <el-input
+                  v-model="loginForm.tenantName"
+                  :prefix-icon="HomeFilled"
+                  placeholder="请输入学校"
+              />
+            </el-form-item>
+            <el-form-item prop="phone">
+              <el-input
+                  v-model="loginForm.phone"
+                  :prefix-icon="Iphone"
+                  placeholder="请输入手机号"
+              />
+            </el-form-item>
+            <el-form-item prop="password">
+              <el-input
+                v-model="loginForm.password"
+                class="password-input"
+                type="password"
+                :prefix-icon="Lock"
+                placeholder="请输入密码"
+                show-password
+              />
+            </el-form-item>
+            <el-form-item prop="confirmPassword">
+              <el-input
+                v-model="loginForm.confirmPassword"
+                class="password-input"
+                type="password"
+                :prefix-icon="Lock"
+                placeholder="请再次确认密码"
+                show-password
+              />
+            </el-form-item>
+            <el-form-item prop="inviteCode">
+              <el-input v-model="loginForm.inviteCode" :prefix-icon="Key" placeholder="请输入邀请码" />
+            </el-form-item>
+
+            <!-- 注册按钮 -->
+            <el-form-item>
+              <div class="login-btn-container">
+                <router-link to="" @click.prevent="goBackToLogin" class="register-link">已有账号,去登录</router-link>
+                <el-button @click="handleRegister" class="login-btn" type="primary">注册</el-button>
+              </div>
+            </el-form-item>
+
+          </el-form>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed, watch } from "vue";
+import { Lock, Iphone, Key, HomeFilled } from '@element-plus/icons-vue';
+import { ElMessage } from 'element-plus';
+import { useRouter } from 'vue-router';
+import homeBG from "@/assets/images/homeBG.png";
+import blocklyBG from "@/assets/images/blocklyBG.png";
+import aiCourseBG from "@/assets/images/aiCourseBG.png";
+import { registerSignUp } from '@/api/login/login.js';
+import { getTenantId } from '@/utils/loginUtils.js';
+
+// 获取环境变量
+const appTitle = ref("注册账号");
+
+const router = useRouter();
+
+const loginFormRef = ref(null);
+
+// 获取查询参数
+const bgImageType = computed(() => router.currentRoute.value.query.bgImage || 'homeBG');
+const loginTypePage = computed(() => router.currentRoute.value.query.loginType || 'login');
+
+// 注册表单数据
+const loginForm = ref({
+  tenantName: "编程课租户", // 学校
+  phone: "", // 手机号
+  password: "", // 密码
+  confirmPassword: "", // 确认密码
+  inviteCode: "", // 邀请码
+  rememberMe: false,
+});
+
+// 判断是否为blocklyLogin类型
+const isBlocklyLogin = computed(() => loginTypePage.value === 'blocklyLogin');
+
+// 设置默认值并监听变化
+watch(isBlocklyLogin, (newVal) => {
+  if (newVal) {
+    loginForm.value.tenantName = '编程课租户';
+  }
+}, { immediate: true });
+
+// 动态选择背景图
+const registerBG = computed(() => {
+  switch(bgImageType.value) {
+    case 'blocklyBG':
+      return blocklyBG;
+    case 'aiCourseBG':
+      return aiCourseBG;
+    default:
+      return homeBG;
+  }
+});
+
+// 返回登录页
+const goBackToLogin = () => {
+  let loginPath = '/';
+  switch(loginTypePage.value) {
+    case 'blocklyLogin':
+      loginPath = '/blockly-login';
+      break;
+    case 'aiCourseLogin':
+      loginPath = '/ai-login';
+      break;
+    default:
+      loginPath = '/';
+  }
+  router.replace(loginPath);
+};
+
+// 表单验证规则
+const rules = ref({
+  tenantName: [
+    { required: true, message: "请输入学校", trigger: "blur" }
+  ],
+  phone: [
+    { required: true, message: "请输入手机号", trigger: "blur" },
+    { pattern: /^1[3-9]\d{9}$/, message: "请输入正确的手机号格式", trigger: "blur" }
+  ],
+  password: [
+    { required: true, message: "请输入密码", trigger: "blur" },
+    { min: 6, max: 20, message: "密码长度应在6-20位之间", trigger: "blur" }
+  ],
+  confirmPassword: [
+    { required: true, message: "请确认密码", trigger: "blur" },
+    {
+      validator: (rule, value, callback) => {
+        if (value !== loginForm.value.password) {
+          callback(new Error("两次输入的密码不一致"));
+        } else {
+          callback();
+        }
+      },
+      trigger: "blur"
+    }
+  ],
+  inviteCode: [{ required: true, message: "请输入邀请码", trigger: "blur" }],
+});
+
+// 处理注册
+const handleRegister = async () => {
+
+  if (!loginFormRef.value) return
+  await loginFormRef.value.validate(async valid => {
+    if (valid) {
+// 表单验证
+      try {
+        // 获取租户名称
+        const tenantName = loginForm.value.tenantName;
+
+        if (tenantName === import.meta.env.VITE_APP_TITLE) {
+          ElMessage.error('此租户不支持注册!');
+          return;
+        }
+
+        // 获取租户ID
+        const tenantId = await getTenantId(tenantName);
+        if (!tenantId) {
+          // 租户验证失败
+          return;
+        }
+        // 准备请求数据
+        const registerData = {
+          username: loginForm.value.phone,
+          nickname: loginForm.value.phone,
+          password: loginForm.value.password,
+          inviteCode: loginForm.value.inviteCode,
+          rememberMe: loginForm.value.rememberMe,
+          tenantId: tenantId
+        };
+        // 调用注册接口
+        const res = await registerSignUp({ 'Tenant-Id': tenantId },registerData);
+        // 注册成功处理
+        if (res && res.code === 0) {
+          ElMessage.success('注册成功,请登录');
+          goBackToLogin();
+        } else {
+          ElMessage.error(res && res.msg);
+        }
+      } catch (error) {
+        console.error('注册失败:', error);
+        ElMessage.error('注册失败,请检查输入信息或网络连接');
+      }
+    }
+  })
+};
+
+</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;
+  justify-content: center; // 水平居中
+  align-items: center; // 垂直居中
+  background: linear-gradient(to bottom, #001169, #8a78d0);
+}
+
+.login-wrapper {
+  width: rpx(500); // 固定宽度
+  height: rpx(300); // 固定高度
+  max-width: 90%; // 响应式最大宽度
+  padding: 0;
+  border-radius: rpx(15);
+  box-shadow: 0 rpx(5) rpx(15) rgba(0, 0, 0, 0.3);
+  display: flex;
+  overflow: hidden;
+  background-color: white;
+}
+
+/* 左侧图片背景盒子 */
+.login-left {
+  width: 50%;
+  position: relative;
+  overflow: hidden;
+}
+
+/* 背景图片样式 */
+.bg-image {
+  width: 100%;
+  height: 100%;
+  object-fit: cover;
+}
+
+/* 右侧输入框盒子 */
+.login-right {
+  width: 50%;
+  padding: rpx(20);
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  background-color: white;
+}
+.login-input {
+  width: 80%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  flex-direction: column;
+  text-align: center;
+}
+
+
+.login-input span {
+  color: black;
+  font-size: rpx(12);
+  padding-bottom: rpx(10);
+  letter-spacing: rpx(1);
+}
+.input-item {
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+  width: 100%;
+}
+.el-input ::v-deep(.el-input__wrapper) {
+  border-radius: rpx(5);
+}
+.input-item .el-form-item {
+  margin-bottom: 0;
+  width: 100%;
+}
+.input-item .el-input {
+  width: 100%;
+  height: rpx(22);
+  margin-bottom: rpx(15);
+  font-size: rpx(8);
+}
+.el-form-item ::v-deep(.el-form-item__error) {
+  top: rpx(25);
+  font-size: rpx(7);
+}
+
+/* 按钮容器,用于控制按钮位置 */
+.login-btn-container {
+  width: 100%;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+.register-link {
+  color: black;
+  font-size: rpx(7);
+  text-decoration: none;
+}
+.register-link:hover {
+  text-decoration: underline;
+}
+.login-btn {
+  width: rpx(65); /* 缩小宽度 */
+  height: rpx(20);
+  color: black;
+  font-size: rpx(9);
+  letter-spacing: rpx(4); /* 调整字间距以适应较小宽度 */
+  border-radius: rpx(5);
+  margin: 0;
+  border: none;
+  outline: none;
+  background: linear-gradient(to bottom, #fee78a, #ffce1b);
+  box-shadow: 0 rpx(4) rpx(4) rgb(0, 0, 0, 0.2);
+}
+.check-box {
+  width: 100%;
+  height: rpx(18);
+  margin: rpx(10) 0;
+  display: flex;
+  justify-content: flex-end;
+  align-items: center;
+}
+.check-box .el-checkbox {
+  color: white;
+  padding-right: rpx(10);
+  font-size: rpx(7);
+}
+.el-checkbox ::v-deep(.el-checkbox__label) {
+  font-size: rpx(7);
+}
+.el-checkbox ::v-deep(.el-checkbox__label) {
+  font-size: 14px;
+}
+
+// 移动端响应式设计
+@media screen and (max-width: 768px) {
+  .login-content {
+    flex-direction: column;
+    background: url('@/assets/images/homeBG.png') no-repeat center center fixed;
+    background-size: cover;
+  }
+  
+  .bg-image-container {
+    position: fixed;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    z-index: -1;
+    background-size: cover;
+    background-position: center;
+  }
+  
+  .login-wrapper {
+    flex: none;
+    width: 70%;
+    margin: 0 auto;
+    padding: 30px;
+    border-radius: 15px;
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    transform: translate(-50%, -50%);
+    min-height: 40%;
+    overflow-y: auto;
+  }
+
+  .login-left{
+    display: none;
+  }
+  .login-right{
+    width: 100%;
+  }
+  
+  .login-input {
+    width: 100%;
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+  }
+  
+  .login-input span {
+    font-size: rpx(18);
+    padding-bottom: rpx(20);
+  }
+  
+  .input-item {
+    width: 100%;
+    align-items: stretch;
+  }
+  
+  .input-item .el-input {
+    width: 100% !important;
+    max-width: none;
+    height: rpx(45) !important;
+    font-size: rpx(14) !important;
+    margin-bottom: rpx(25) !important;
+  }
+  
+  .el-input ::v-deep(.el-input__wrapper) {
+    height: rpx(45) !important;
+    border-radius: rpx(15);
+  }
+  
+  .el-input ::v-deep(.el-input__inner) {
+    height: rpx(45) !important;
+    font-size: rpx(14) !important;
+  }
+  
+  .login-btn {
+    width: 40% !important;
+    max-width: none;
+    height: rpx(45) !important;
+    font-size: rpx(16) !important;
+    margin: rpx(25) 0 !important;
+    letter-spacing: rpx(15);
+    border-radius: rpx(10);
+  }
+  
+  .check-box {
+    width: 100% !important;
+    max-width: none;
+    height: auto;
+    margin: rpx(15) 0 !important;
+  }
+  
+  .check-box .el-checkbox {
+    font-size: rpx(12) !important;
+  }
+  
+  .el-checkbox ::v-deep(.el-checkbox__label) {
+    font-size: rpx(12) !important;
+  }
+  
+  .sms-code-container {
+    width: 100% !important;
+    max-width: none;
+    height: rpx(45) !important;
+  }
+  
+  .sms-input {
+    width: calc(100% - rpx(75)) !important;
+    height: rpx(45) !important;
+  }
+  
+  .get-code-btn {
+    width: rpx(75) !important;
+    height: rpx(45) !important;
+    font-size: rpx(10) !important;
+  }
+  
+  .el-form-item ::v-deep(.el-form-item__error) {
+    top: rpx(50) !important;
+    font-size: rpx(10) !important;
+  }
+}
+</style>