Przeglądaj źródła

提交AI实验室界面

丸子 10 miesięcy temu
rodzic
commit
ffa22fe56f

+ 13 - 0
src/App.vue

@@ -19,4 +19,17 @@
     padding: 20px;
   }
 }
+* {
+  margin: 0;
+  padding: 0;
+}
+html,
+body,
+.common-layout,
+.el-container,
+#app {
+  width: 100vw;
+  height: 100vh;
+  overflow: hidden;
+}
 </style>

BIN
src/assets/images/number-people01.png


BIN
src/assets/images/number-people02.png


BIN
src/assets/images/number-people03.png


BIN
src/assets/images/number-people04.png


BIN
src/assets/images/number-people05.png


+ 20 - 13
src/components/HomePage.vue

@@ -55,6 +55,7 @@
       </div>
       <div
         class="center-box-in-box2"
+        @click="goToAILab()"
         :style="{ backgroundImage: `url(${indexImages[1]})` }"
       >
         <span>AI实验室</span>
@@ -84,9 +85,9 @@ import { useRouter } from 'vue-router'
 import { ArrowDown, ArrowRightBold } from '@element-plus/icons-vue'
 import { Search, ArrowLeftBold } from '@element-plus/icons-vue'
 // 默认选中 AI 通识课
-const selectedButton = ref('AI通识课') 
+const selectedButton = ref('AI通识课')
 // 获取当前路由对象
-const router = useRouter() 
+const router = useRouter()
 // 添加图片路径
 const indexImages = ref([
   './src/assets/images/intelligence.png',
@@ -94,17 +95,20 @@ const indexImages = ref([
   './src/assets/images/test.png',
   './src/assets/images/study.png'
 ])
-const goToAIGeneralCourse = (title) => {
-  router.push({ path: '/ai-general-course', query: { title } });
-};
+// AI初体验
+const goToAIGeneralCourse = title => {
+  router.push({ path: '/ai-general-course', query: { title } })
+}
+//AI实验室
+const goToAILab = () => {
+  router.push({ path: '/ai-laboratory' })
+}
 // 添加下拉菜单选中项
 const selectedGrade = ref('小学低年级')
 // 获取年级
 // ClassList().then(res=>{
 //   console.log(res);
 // })
-
-
 </script>
 
 <style scoped lang="scss">
@@ -119,14 +123,14 @@ const selectedGrade = ref('小学低年级')
   left: 0;
   right: 0;
   bottom: 0;
-  background: linear-gradient(to bottom, #08105e, #8271c8);
+   background: linear-gradient(to bottom, #001169, #B4A8E1);
   display: flex;
   flex-direction: column;
   gap: rpx(0);
 }
 .box-1 {
   width: 100%;
-  flex: 0.3;
+  height: rpx(50);
   display: flex;
   justify-content: center;
   align-items: center;
@@ -135,13 +139,13 @@ const selectedGrade = ref('小学低年级')
 }
 .box-2 {
   width: 100%;
-  flex: 2;
+  flex: 1;
   display: flex;
   justify-content: space-between;
   align-items: center;
   box-shadow: 0 4px 8px rgba(202, 52, 52, 0.1);
   box-sizing: border-box;
-  padding: 0 rpx(20); // 添加左右内边距
+  // padding: 0 rpx(20); // 添加左右内边距
   cursor: pointer; // 添加鼠标指针样式
 }
 .box-2 span {
@@ -167,7 +171,10 @@ const selectedGrade = ref('小学低年级')
 .center-box-in-box2:active {
   box-shadow: 0 4px 8px rgba(0, 0, 0, 0.5);
 }
-.left-box-in-box2, .center-box-in-box2, .top-sub-box, .bottom-sub-box {
+.left-box-in-box2,
+.center-box-in-box2,
+.top-sub-box,
+.bottom-sub-box {
   background-repeat: no-repeat;
   background-size: cover;
   background-position: center;
@@ -222,7 +229,7 @@ const selectedGrade = ref('小学低年级')
 }
 
 .right-box {
-  flex: 2;
+  flex: 1;
   position: relative; // 添加相对定位;
 }
 .top-right-box {

+ 10 - 0
src/router/index.js

@@ -10,6 +10,16 @@ const routes = [
     path: '/ai-general-course',
     component: () => import('../views/AIGeneralCourse.vue')
   },
+  // AI实验室
+  {
+    path: '/ai-laboratory',
+    component: () => import('../views/AILaboratory.vue')
+  },
+  // 智能问答
+   {
+    path: '/ai-questions',
+    component: () => import('../views/AIQuestions.vue')
+  },
   // AI初体验
   {
     path: '/ai-initial-experience',

+ 58 - 23
src/views/AIDevelop.vue

@@ -22,6 +22,7 @@
               @open="handleOpen"
               @close="handleClose"
               @select="handleSelect"
+              :default-openeds="['1']"
             >
               <template v-for="item in menuItems" :key="item.index">
                 <el-menu-item v-if="!item.children" :index="item.index">{{
@@ -68,7 +69,7 @@
       </div>
     </div>
     <div class="box-2">
-      <!-- 课程标题 autoplay自动播放-->
+      <!-- 课程标题 autoplay自动播放   @ended="playNextVideo"-->
       <div class="small-title">
         <span>{{ smallTitle }}</span>
       </div>
@@ -76,14 +77,23 @@
         class="full-box-video"
         :src="videoSrc"
         controls
-        autoplay
-        @ended="playNextVideo"
         @timeupdate="handleTimeUpdate"
         ref="videoRef"
       >
         <source :src="videoSrc" type="video/mp4" />
         您的浏览器不支持视频播放。
       </video>
+      <!-- 添加切换视频 -->
+      <div class="video-switch">
+        <!-- 上一个视频 -->
+        <div class="caret-left" @click="playPreviousVideo">
+          <el-icon class="caret-left-icon"><CaretLeft /></el-icon>
+        </div>
+        <!-- 下一个视频 -->
+        <div class="caret-right" @click="playNextVideo">
+          <el-icon class="caret-right-icon"><CaretRight /></el-icon>
+        </div>
+      </div>
     </div>
   </div>
 </template>
@@ -91,7 +101,7 @@
 <script setup>
 import { ref, onMounted } from 'vue'
 import { useRouter } from 'vue-router'
-import { ArrowDown, ArrowRightBold } from '@element-plus/icons-vue'
+import { ArrowDown, ArrowRightBold, CaretRight,CaretLeft } from '@element-plus/icons-vue'
 import { Search, ArrowLeftBold } from '@element-plus/icons-vue'
 import { valueEquals } from 'element-plus'
 // 引入视频
@@ -244,9 +254,19 @@ const playNextVideo = () => {
   pausedIndices = ref([])
 }
 
-// 视频 ref
-const videoRef = ref(null)
+// 切换视频
+// 播放上一个视频
+const playPreviousVideo = () => {
+  const allIndices = flattenMenuItems()
+  const currentIndexInList = allIndices.indexOf(currentIndex.value)
+  if (currentIndexInList > 0) {
+    const previousIndex = allIndices[currentIndexInList - 1]
+    handleSelect(previousIndex)
+  }
+}
 
+// 视频 ref
+const videoRef = ref(null) 
 // 定义每个视频对应的暂停时间
 const videoPauseTimes = {
   [video4]: [1],
@@ -254,15 +274,11 @@ const videoPauseTimes = {
   [video9]: [2],
   [video12]: [1]
 }
-
-
 // 处理视频时间更新事件
 const handleTimeUpdate = () => {
   if (!videoRef.value) return
-
   const currentTime = videoRef.value.currentTime
   const currentPauseTimes = videoPauseTimes[videoSrc.value]
-
   if (currentPauseTimes) {
     currentPauseTimes.forEach((time, index) => {
       // 检查是否到达时间点且还未暂停过
@@ -273,6 +289,7 @@ const handleTimeUpdate = () => {
     })
   }
 }
+
 </script>
 
 <style scoped lang="scss">
@@ -287,7 +304,7 @@ const handleTimeUpdate = () => {
   left: 0;
   right: 0;
   bottom: 0;
-  background: linear-gradient(to bottom, #08105e, #8271c8);
+   background: linear-gradient(to bottom, #001169, #B4A8E1);
   display: flex;
   flex-direction: column;
   gap: rpx(0);
@@ -393,12 +410,13 @@ const handleTimeUpdate = () => {
 
 .box-1 {
   width: 100%;
-  flex: 0.3;
+  height: rpx(50);
+  // margin-top: rpx(10);
   display: flex;
   justify-content: center;
   align-items: center;
   box-sizing: border-box;
-  font-size: rpx(16); // 默认字体大小
+  font-size: rpx(15); // 默认字体大小
 }
 
 .inner-box {
@@ -415,7 +433,6 @@ const handleTimeUpdate = () => {
   align-items: flex-start;
   flex: 1; // 设置左侧盒子占比为 2
   cursor: pointer; // 添加鼠标指针样式
-  // margin-top: rpx(20);
 }
 .box-icon {
   width: 100%;
@@ -434,14 +451,12 @@ const handleTimeUpdate = () => {
 
 .left-box span {
   position: absolute;
-  // top: 20px;
-  // left: 20px;
   font-size: rpx(11); // 默认字体大小
   color: white;
 }
 
 .right-box {
-  flex: 2;
+  flex: 1;
   position: relative; // 添加相对定位;
 }
 .top-right-box {
@@ -479,22 +494,22 @@ const handleTimeUpdate = () => {
 }
 .box-2 {
   width: 100%;
-  flex: 2;
+  flex: 1;
   box-shadow: 0 4px 8px rgba(202, 52, 52, 0.1);
   box-sizing: border-box;
-  // padding: 0 rpx(30); // 添加左右内边距
+  // padding: rpx(-20) rpx(20); // 添加左右内边距
   display: flex; // 确保子元素水平排列
   flex-wrap: wrap; // 允许子元素换行;
   align-content: center; // 顶部对齐;
   cursor: pointer; // 添加鼠标指针样式
   video.full-box-video {
-    width: 85%;
-    height: 90%;
-    margin: auto;
+    width: rpx(550);
+    height: rpx(250);
+    margin: 0 auto;
     border-radius: rpx(12);
     box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3);
     object-fit: cover;
-    background-color: #000;
+    // object-fit: contain;
   }
 }
 .small-title {
@@ -505,4 +520,24 @@ const handleTimeUpdate = () => {
   font-size: rpx(10);
   justify-content: center; //使子元素水平居中
 }
+.video-switch {
+  width: 100%;
+  // height: rpx(50);
+  display: flex;
+  margin-top: rpx(10);
+  // background-color: saddlebrown;
+}
+.caret-right,
+.caret-left{
+  width: rpx(20);
+  height: rpx(20);
+  margin: auto;
+  border-radius: rpx(50);
+  border: 2px solid white;
+}
+.caret-right-icon,
+.caret-left-icon{
+  font-size: rpx(20);
+  color: white;
+}
 </style>

+ 4 - 4
src/views/AIGeneralCourse.vue

@@ -180,7 +180,7 @@ onMounted(()=>{
   left: 0;
   right: 0;
   bottom: 0;
-  background: linear-gradient(to bottom, #08105e, #8271c8);
+  background: linear-gradient(to bottom, #001169, #B4A8E1);
   display: flex;
   flex-direction: column;
   gap: rpx(0);
@@ -259,7 +259,7 @@ onMounted(()=>{
 }
 .box-1 {
   width: 100%;
-  flex: 0.3;
+  height: rpx(50);
   display: flex;
   justify-content: center;
   align-items: center;
@@ -387,10 +387,10 @@ onMounted(()=>{
 }
 .box-2 {
   width: 100%;
-  flex: 2;
+  flex: 1;
   box-shadow: 0 4px 8px rgba(202, 52, 52, 0.1);
   box-sizing: border-box;
-  padding: 0 rpx(30); // 添加左右内边距
+  // padding: 0 rpx(30); // 添加左右内边距
   display: flex; // 确保子元素水平排列
   flex-wrap: wrap; // 允许子元素换行;
   align-content: center; // 顶部对齐;

+ 1 - 1
src/views/AIInitialExperience.vue

@@ -124,7 +124,7 @@ const classTitles = [
   left: 0;
   right: 0;
   bottom: 0;
-  background: linear-gradient(to bottom, #08105e, #8271c8);
+  background: linear-gradient(to bottom, #001169, #B4A8E1);
   display: flex;
   flex-direction: column;
   gap: rpx(0);

+ 234 - 0
src/views/AILaboratory.vue

@@ -0,0 +1,234 @@
+<template>
+  <!-- AI实验室 -->
+  <div class="home-container">
+    <!-- 左侧折叠面板 -->
+    <div class="left-group">
+      <el-row class="tac">
+        <el-col :span="12">
+          <el-menu
+            default-active="2"
+            class="el-menu-vertical-demo"
+            @open="handleOpen"
+            @close="handleClose"
+          >
+            <el-menu-item v-for="(item, index) in groupList" :key="index">
+              <el-icon><component :is="item.icon" /></el-icon>
+              <span>{{ item.title }}</span>
+            </el-menu-item>
+          </el-menu>
+        </el-col>
+      </el-row>
+    </div>
+    <!-- 右侧数字人 -->
+    <div class="number-people">
+      <div class="title-box">
+        <div class="box-icon" @click="goBack">
+          <el-icon class="left-icon"><ArrowLeftBold /></el-icon>
+          AI实验室 | {{ groupList[2].title }}
+        </div>
+      </div>
+
+      <div class="content-box">
+        <!-- 动态渲染数字人 -->
+        <div
+          v-for="(person, index) in peopleList"
+          :key="index"
+          @click="navigateToAIQuestions(person)"
+          class="small-box"
+        >
+          <div class="people-box">
+            <img :src="person.image" alt="{{ person.name }}" />
+          </div>
+          <div class="people-title">{{ person.name }}</div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted } from 'vue'
+import { useRouter } from 'vue-router'
+import {
+  Document,
+  Menu as IconMenu,
+  Location,
+  Setting,
+  ArrowLeftBold
+} from '@element-plus/icons-vue'
+const router = useRouter()
+
+
+// 返回上一页
+const goBack = () => {
+  router.go(-1)
+}
+// 引入
+import NumberPeople01 from '@/assets/images/number-people01.png'
+import NumberPeople02 from '@/assets/images/number-people02.png'
+import NumberPeople03 from '@/assets/images/number-people03.png'
+import NumberPeople04 from '@/assets/images/number-people04.png'
+import NumberPeople05 from '@/assets/images/number-people05.png'
+// 渲染数字人老师及图片
+const peopleList = ref([
+  { name: '鲁迅', image: NumberPeople01 },
+  { name: '门捷列夫', image: NumberPeople02 },
+  { name: '牛顿', image: NumberPeople03 },
+  { name: '特斯拉', image: NumberPeople04 },
+  { name: '李白', image: NumberPeople05 }
+])
+
+// 跳转页面携带名字和人物形象
+const navigateToAIQuestions = (person) => { 
+  router.push({ 
+    // 跳转问答页面
+    path: '/ai-questions', 
+    query: {  name: person.name, image: person.image } 
+  }); 
+};
+
+// 渲染侧边栏
+const groupList = ref([
+  {
+    icon: IconMenu,
+    title: '智能问答',
+  },
+  {
+    icon: Document,
+    title: '智能绘图'
+  },
+  {
+    icon: Setting,
+    title: '数字人老师'
+  }
+])
+</script>
+
+<style scoped lang="scss">
+@use 'sass:math';
+// 定义rpx转换函数
+@function rpx($px) {
+  @return math.div($px, 750) * 100vw;
+}
+.home-container {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  display: flex;
+  flex-direction: row;
+  gap: rpx(0);
+}
+
+// 侧边栏
+.left-group {
+  width: rpx(150);
+  height: 100%;
+  background: linear-gradient(to bottom, #001169, #b4a8e1);
+}
+.mb-2 {
+  color: black;
+  margin-top: rpx(1);
+}
+.tac ::v-deep(.el-menu) {
+  background-color: transparent;
+  border: none;
+  width: 100%;
+  margin-top: rpx(30);
+  margin-left: rpx(10);
+}
+.el-menu-item {
+  width: rpx(130);
+  height: rpx(25);
+  margin-bottom: rpx(5);
+  border-radius: rpx(6);
+}
+.el-menu-item .el-icon svg {
+  font-size: rpx(15);
+  color: white;
+}
+.el-menu-item span {
+  color: white;
+  font-size: rpx(8);
+  padding-left: rpx(5);
+}
+.el-menu ::v-deep(.el-menu-item:hover),
+.el-menu ::v-deep(.el-menu-item:focus),
+.el-menu ::v-deep(.el-menu-item:active) {
+  background: linear-gradient(
+    to bottom,
+    #fee78a,
+    #ffce1b
+  ); /* 设置悬停、聚焦、点击状态下的背景色 */
+}
+
+// 右侧数字人内容
+.number-people {
+  flex: 1;
+  height: 100%;
+  display: flex;
+  flex-direction: column; /* 子元素上下排列 */
+  background-color: #e5e1fc;
+}
+// 标题样式
+.title-box {
+  height: rpx(50);
+}
+.box-icon {
+  width: 100%;
+  height: 100%;
+  flex: 1;
+  display: flex; // 添加 flex 布局
+  align-items: center; // 垂直居中
+  color: black; // 设置图标颜色为白色
+  padding-left: rpx(15);
+  font-size: rpx(10); // 设置图标大小,可按需调整
+  cursor: pointer; // 添加鼠标指针样式
+}
+.box-icon .left-icon {
+  margin-left: rpx(10);
+  margin-right: rpx(5); // 设置图标和文字之间的间距 ;
+}
+// 内容样式
+.content-box {
+  flex: 1;
+  display: flex;
+  flex-wrap: wrap;
+}
+.small-box {
+  width: rpx(180);
+  height: rpx(110);
+  margin-top: rpx(10);
+  margin-left: rpx(15);
+  border-radius: rpx(6);
+  background: rgba($color: #ffffff, $alpha: 0.5);
+  position: relative;
+  cursor: pointer; // 添加鼠标指针样式
+}
+.people-box {
+  position: absolute;
+  top: rpx(-30);
+  overflow: hidden;
+  width: rpx(180);
+  height: rpx(140);
+  border-radius: rpx(6);
+  margin-bottom: rpx(5);
+  // background-color: pink;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+.people-box img {
+  width: rpx(100);
+  margin-top: rpx(75);
+}
+.people-title {
+  font-size: rpx(8);
+  margin-top: rpx(110);
+}
+.small-box span {
+  color: black;
+  font-size: rpx(8);
+}
+</style>

+ 209 - 0
src/views/AIQuestions.vue

@@ -0,0 +1,209 @@
+<template>
+  <!-- AI实验室 -->
+  <div class="home-container">
+    <!-- 左侧折叠面板 -->
+    <div class="left-group">
+      <div class="title-box">
+        <div class="box-icon" @click="goBack">
+          <el-icon class="left-icon"><ArrowLeftBold /></el-icon>
+          {{ personName }}
+        </div>
+      </div>
+      <img :src="selectedImage" alt="" />
+    </div>
+    <!-- 右侧AI问答 -->
+    <div class="number-people">
+      <div class="content-box">
+        <!-- AI对话框 -->
+        <div class="chat-dialog">
+          <!-- 对话消息列表 -->
+          <div class="message-list">
+            <!-- AI消息 -->
+            <div class="ai-message">
+              你好小友,我是鲁迅。<br />
+              我是个写字的人,写下所见,写下所思,哪怕无人理解,哪怕只是逆风低语。
+              但只要还有一个人愿意思考,事情便还不算太坏。
+            </div>
+            <!-- 用户消息 -->
+            <div class="user-message">你好</div>
+          </div>
+          <!-- 输入框和发送按钮 -->
+          <div class="input-section">
+            <input
+              type="text"
+              v-model="inputMessage"
+              placeholder="问我任何问题..."
+            />
+            <button @click="sendMessage">发送</button>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted } from 'vue'
+import { useRouter, useRoute } from 'vue-router'
+import {
+  Document,
+  Menu as IconMenu,
+  Location,
+  Setting,
+  ArrowLeftBold
+} from '@element-plus/icons-vue'
+// 返回上一页
+const goBack = () => {
+  router.go(-1)
+}
+const router = useRouter()
+const route = useRoute()
+
+// 渲染实验室携带的人物名称
+const personName = ref(route.query.name)
+
+// 渲染实验室携带的人物形象图片
+const selectedImage = ref('')
+onMounted(() => {
+  const image = route.query.image
+  if (image) {
+    selectedImage.value = image
+  }
+})
+
+// 消息列表和输入内容的响应式变量
+const messages = ref([])
+const inputMessage = ref('')
+// 发送消息函数
+const sendMessage = () => {
+  if (inputMessage.value.trim()) {
+    messages.value.push(inputMessage.value.trim())
+    inputMessage.value = ''
+  }
+}
+</script>
+
+<style scoped lang="scss">
+@use 'sass:math';
+// 定义rpx转换函数
+@function rpx($px) {
+  @return math.div($px, 750) * 100vw;
+}
+.home-container {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  display: flex;
+  flex-direction: row;
+  gap: rpx(0);
+}
+
+// 侧边栏
+.left-group {
+  width: rpx(150);
+  height: 100%;
+  background-color: #ece9fd;
+}
+.left-group img {
+  width: rpx(110);
+  height: auto;
+  margin-top: rpx(20);
+}
+.title-box {
+  height: rpx(50);
+}
+.box-icon {
+  width: 100%;
+  height: 100%;
+  flex: 1;
+  display: flex; // 添加 flex 布局
+  align-items: center; // 垂直居中
+  color: black; // 设置图标颜色为白色
+  padding-left: rpx(15);
+  font-size: rpx(10); // 设置图标大小,可按需调整
+  cursor: pointer; // 添加鼠标指针样式
+}
+.box-icon .left-icon {
+  margin-left: rpx(10);
+  margin-right: rpx(5); // 设置图标和文字之间的间距 ;
+}
+
+.number-people {
+  flex: 1;
+  height: 100%;
+  display: flex;
+  background-color: #ece9fd;
+}
+.content-box {
+  flex: 1;
+  margin-top: rpx(10);
+  margin-bottom: rpx(10);
+  margin-right: rpx(10);
+  border-radius: rpx(15);
+  background: rgba($color: #ffffff, $alpha: 0.5);
+}
+
+// 对话框
+.chat-dialog {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+}
+.message-list {
+  flex: 1;
+  overflow-y: auto;
+  padding: rpx(15);
+}
+
+.message-list .user-message {
+  background-color: #ffffff;
+  margin-left: auto; // 消息靠右显示
+  margin-right: 0; // 重置右边距
+  max-width: rpx(400);
+  font-size: rpx(8);
+  width: fit-content;
+  border-radius: rpx(5);
+  padding: rpx(5);
+
+  text-align: left; // 文字左对齐
+}
+.message-list .ai-message {
+  background-color: #FFDD55;
+  margin-left: 0; // 消息靠左显示
+  margin-right: auto; // 重置右边距
+  margin-bottom: rpx(10);
+  width: fit-content;
+  max-width: rpx(400);
+  padding: rpx(5);
+  font-size: rpx(8);
+  border-radius: rpx(5);
+  text-align: left; // 文字左对齐 
+}
+.input-section {
+  display: flex;
+  padding: rpx(10);
+  gap: rpx(10);
+}
+.input-section input {
+  flex: 1;
+  padding: rpx(5);
+  font-size: rpx(7);
+  border: 1px solid #ccc;
+  border-radius: rpx(5);
+}
+.input-section button {
+  padding: rpx(5) rpx(15);
+  background: linear-gradient(
+    to bottom,
+    #fee78a,
+    #ffce1b
+  ); /* 设置悬停、聚焦、点击状态下的背景色 */
+  color: black;
+  border: none;
+  font-size: rpx(7);
+  border-radius: rpx(5);
+  cursor: pointer;
+}
+</style>