Przeglądaj źródła

AI问答界面调整

丸子 9 miesięcy temu
rodzic
commit
0696308945

+ 0 - 6
src/views/AIDevelop.vue

@@ -290,10 +290,6 @@ const videoMap = {
 // 定义视频源
 const videoSrc = ref(video1)
 
-// 课程列表
-// ClassType().then(res=>{
-//   console.log(res);
-// })
 
 // 返回上一页
 const goBack = () => {
@@ -366,8 +362,6 @@ const handleSelect = index => {
     videoSrc.value = videoMap[index]
     currentIndex.value = index
   }
-  // 选中标题后关闭抽屉
-  drawerVisible.value = false
 }
 
 // 展平所有菜单项索引

+ 4 - 4
src/views/AILaboratory.vue

@@ -8,7 +8,6 @@
         :style="{ color: drawerVisible ? 'white' : '#B6B0D8' }"
       >
         <component :is="drawerVisible ? Fold : Expand" />
-        <Tickets />
       </el-icon>
     </div>
 
@@ -76,7 +75,8 @@ import {
   Expand,
   ChatLineRound,
   Picture,
-  MagicStick
+  MagicStick,
+  Avatar
 } from '@element-plus/icons-vue'
 const router = useRouter()
 
@@ -143,7 +143,7 @@ const groupList = ref([
     title: '智能绘画'
   },
   {
-    icon: Setting,
+    icon: Avatar,
     title: '数字人老师'
   }
 ])
@@ -232,7 +232,7 @@ const groupList = ref([
 
 .el-menu ::v-deep(.el-menu-item:hover),
 .el-menu ::v-deep(.el-menu-item:focus),
-.el-menu ::v-deep(.el-menu-item:active) {
+.el-menu ::v-deep(.el-menu-item:active){
   background: linear-gradient(
     to bottom,
     #ffefb0,

+ 125 - 2
src/views/AIPainting.vue

@@ -1,6 +1,42 @@
 <template>
   <!-- 智能绘画 -->
   <div class="home-container">
+
+     <!-- 展开收起侧边栏 -->
+    <div class="icon-expand">
+      <el-icon
+        @click="toggleDrawer"
+        :style="{ color: drawerVisible ? 'white' : '#B6B0D8' }"
+      >
+        <component :is="drawerVisible ? Fold : Expand" />
+      </el-icon>
+    </div>
+
+    <!-- 左侧折叠面板 -->
+    <transition name="drawer-slide">
+      <div class="left-group1" v-if="drawerVisible">
+        <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"
+                @click="navigateToAI(item)"
+              >
+                <el-icon><component :is="item.icon" /></el-icon>
+                {{ item.title }}
+              </el-menu-item>
+            </el-menu>
+          </el-col>
+        </el-row>
+      </div>
+    </transition>
+
     <div class="left-group2">
       <div class="title-box">
         <div class="box-icon" @click="goBack">
@@ -145,9 +181,15 @@ import {
   Menu as IconMenu,
   Location,
   Setting,
-  ArrowLeftBold
+  ArrowLeftBold,
+  Fold,
+  Expand,
+  ChatLineRound,
+  Picture,
+  MagicStick,
+  Tickets,
+  Avatar
 } from '@element-plus/icons-vue'
-
 // 返回上一页
 const goBack = () => {
   router.go(-1)
@@ -155,6 +197,49 @@ const goBack = () => {
 const router = useRouter()
 const route = useRoute()
 
+// 添加抽屉显示状态
+const drawerVisible = ref(true)
+// 添加切换抽屉显示状态的函数
+const toggleDrawer = () => {
+  drawerVisible.value = !drawerVisible.value
+}
+
+// 跳转智能问答
+const navigateToAI = (group) => {
+  if (group.title === "智能问答") {
+    let person = { id: 10, name: '小智', image: NumberPeople00, message: '您好,我是您的AI智能助手小智,我会尽力回答您的问题或提供有用的建议!!!!'  };
+    router.push({
+      // 跳转问答页面
+      path: '/ai-questions',
+      query: {  id: person.id, name: person.name, image: person.image, message: person.message }
+    });
+  }
+  if (group.title === "智能绘画") {
+    router.push('/ai-painting')
+  }
+}
+
+// 渲染侧边栏
+const groupList = ref([
+  {
+    icon: ChatLineRound,
+    title: '智能问答'
+  },
+  {
+    icon: MagicStick,
+    title: '智能绘画'
+  },
+  {
+    icon: Avatar,
+    title: '数字人老师'
+  }
+])
+
+// 处理菜单展开和关闭事件
+const handleOpen = () => {}
+const handleClose = () => {}
+
+
 const demoImageList = [demo1, demo2, demo3, demo4]
 
 // 消息列表和输入内容的响应式变量
@@ -272,6 +357,39 @@ const inProgressTimerFun = () => {
 @function rpx($px) {
   @return math.div($px, 750) * 100vw;
 }
+/* 添加过渡样式 */
+.drawer-slide-enter-active,
+.drawer-slide-leave-active {
+  transition: all 0.3s ease;
+}
+.drawer-slide-enter-from,
+.drawer-slide-leave-to {
+  transform: translateX(-100%);
+  opacity: 0;
+}
+.icon-expand {
+  width: rpx(30);
+  height: rpx(30);
+  z-index: 9999;
+  position: absolute;
+  cursor: pointer; // 添加鼠标指针样式
+}
+.icon-expand .el-icon {
+  font-size: rpx(15);
+  position: absolute;
+  color: white;
+  left: rpx(9);
+  margin-top: rpx(17);
+}
+.icon-expand .el-icon:active {
+  color: black;
+}
+// 侧边栏
+.left-group1 {
+  width: rpx(135);
+  height: 100%;
+  background: linear-gradient(to bottom, #001169, #b4a8e1);
+}
 .home-container {
   position: fixed;
   top: 0;
@@ -281,6 +399,11 @@ const inProgressTimerFun = () => {
   display: flex;
   flex-direction: row;
   gap: rpx(0);
+   background: linear-gradient(
+    to bottom,
+    #e2ddfc,
+    #f1effd
+  ); /* 设置悬停、聚焦、点击状态下的背景色 */
 }
 
 // 侧边栏

+ 255 - 95
src/views/AIQuestions.vue

@@ -1,48 +1,84 @@
 <template>
   <!-- 数字人智能问答 -->
   <div class="home-container">
+    <!-- 展开收起侧边栏 -->
+    <div class="icon-expand">
+      <el-icon
+        @click="toggleDrawer"
+        :style="{ color: drawerVisible ? 'white' : '#B6B0D8' }"
+      >
+        <component :is="drawerVisible ? Fold : Expand" />
+      </el-icon>
+    </div>
+
     <!-- 左侧折叠面板 -->
-    <div class="left-group">
-      <div class="title-box">
-        <div class="box-icon" @click="goBack">
-          <el-icon class="left-icon"><ArrowLeftBold /></el-icon>
-          {{ personName }}
-        </div>
+    <transition name="drawer-slide">
+      <div class="left-group" v-if="drawerVisible">
+        <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"
+                @click="navigateToAI(item)"
+              >
+                <el-icon><component :is="item.icon" /></el-icon>
+                {{ item.title }}
+              </el-menu-item>
+            </el-menu>
+          </el-col>
+        </el-row>
       </div>
-      <div class="selected-image">
-        <img :src="selectedImage" alt="" />
+    </transition>
+
+    <!-- 原左侧折叠面板和右侧AI问答 -->
+    <div class="content-wrapper">
+      <div class="left-group2">
+        <div class="title-box">
+          <div class="box-icon" @click="goBack">
+            <el-icon class="left-icon"><ArrowLeftBold /></el-icon>
+            {{ personName }}
+          </div>
+        </div>
+        <div class="selected-image">
+          <img :src="selectedImage" alt="" />
+        </div>
       </div>
-    </div>
-    <!-- 右侧AI问答 -->
-    <div class="number-people">
-      <div class="content-box">
-        <!-- AI对话框 -->
-        <div class="chat-dialog">
-          <!-- 对话消息列表 -->
-          <div class="message-list">
-            <div v-for="(item, index) in messageList" :key="index">
-              <!-- AI消息 -->
-              <div class="ai-message" v-if="item.type !== 'user'">
-                 <MarkdownView class="left-text" :content="item.content" />
-               <!-- {{item.content}} -->
-              </div>
-
-              <!-- 用户消息 -->
-              <div class="user-message" v-if="item.type === 'user'">
-                {{ item.content }}
+      <!-- 右侧AI问答 -->
+      <div class="number-people">
+        <div class="content-box">
+          <!-- AI对话框 -->
+          <div class="chat-dialog">
+            <!-- 对话消息列表 -->
+            <div class="message-list">
+              <div v-for="(item, index) in messageList" :key="index">
+                <!-- AI消息 -->
+                <div class="ai-message" v-if="item.type !== 'user'">
+                  <MarkdownView class="left-text" :content="item.content" />
+                  <!-- {{item.content}} -->
+                </div>
+
+                <!-- 用户消息 -->
+                <div class="user-message" v-if="item.type === 'user'">
+                  {{ item.content }}
+                </div>
               </div>
-
             </div>
-          </div>
-          <!-- 输入框和发送按钮 -->
-          <div class="input-section">
-            <input
-              type="text"
-              v-model="prompt"
-              placeholder="问我任何问题..."
-              @keyup.enter="handleSendByKeydown"
-            />
-            <button @click="handleSendByButton">发送</button>
+            <!-- 输入框和发送按钮 -->
+            <div class="input-section">
+              <input
+                type="text"
+                v-model="prompt"
+                placeholder="问我任何问题..."
+                @keyup.enter="handleSendByKeydown"
+              />
+              <button @click="handleSendByButton">发送</button>
+            </div>
           </div>
         </div>
       </div>
@@ -63,7 +99,54 @@ import {
   ArrowLeftBold,
   MagicStick,
   ChatLineRound,
+  Fold,
+  Expand,
+  Picture,
+  Tickets,
+  Avatar
 } from '@element-plus/icons-vue'
+
+// 添加抽屉显示状态
+const drawerVisible = ref(true)
+// 添加切换抽屉显示状态的函数
+const toggleDrawer = () => {
+  drawerVisible.value = !drawerVisible.value
+}
+// 渲染侧边栏
+const groupList = ref([
+  { icon: ChatLineRound, title: '智能问答' },
+  { icon: MagicStick, title: '智能绘画' },
+  { icon: Avatar, title: '数字人老师' }
+])
+// 跳转智能问答
+const navigateToAI = group => {
+  if (group.title === '智能问答') {
+    let person = {
+      id: 10,
+      name: '小智',
+      image: '@/assets/images/xiaozhi.png',
+      message:
+        '您好,我是您的AI智能助手小智,我会尽力回答您的问题或提供有用的建议!!!!'
+    }
+    router.push({
+      // 跳转问答页面
+      path: '/ai-questions',
+      query: {
+        id: person.id,
+        name: person.name,
+        image: person.image,
+        message: person.message
+      }
+    })
+  }
+  if (group.title === '智能绘画') {
+    router.push('/ai-painting')
+  }
+}
+// 处理菜单展开和关闭
+const handleOpen = () => {}
+const handleClose = () => {}
+
 // 返回上一页
 const goBack = () => {
   router.go(-1)
@@ -86,7 +169,7 @@ onMounted(() => {
 })
 
 // 聊天对话
-const activeConversationModelPath= ref(null) // 选中的对话编号
+const activeConversationModelPath = ref(null) // 选中的对话编号
 const activeConversationId = ref(null) // 选中的对话编号
 const activeConversation = ref(null) // 选中的 Conversation
 const conversationInProgress = ref(false) // 对话是否正在进行中。目前只有【发送】消息时,会更新为 true,避免切换对话、删除对话等操作,导致 stream 中断
@@ -110,11 +193,10 @@ const enableContext = ref(true) // 是否开启上下文
 const receiveMessageFullText = ref('')
 const receiveMessageDisplayedText = ref('')
 
-
 // =========== 【聊天对话】相关 ===========
 
 /** 获取对话信息 */
-const getConversation = async (id) => {
+const getConversation = async id => {
   if (!id) {
     return
   }
@@ -128,11 +210,10 @@ const getConversation = async (id) => {
   activeConversationModelPath.value = personImage.value
 }
 
-
 // =========== 【发送消息】相关 ===========
 
 /** 处理来自 keydown 的发送消息 */
-const handleSendByKeydown = async (event) => {
+const handleSendByKeydown = async event => {
   // 判断用户是否在输入
   if (isComposing.value) {
     return
@@ -161,7 +242,7 @@ const handleSendByButton = () => {
 }
 
 /** 处理 prompt 输入变化 */
-const handlePromptInput = (event) => {
+const handlePromptInput = event => {
   // 非输入法 输入设置为 true
   if (!isComposing.value) {
     // 回车 event data 是 null
@@ -190,7 +271,7 @@ const onCompositionend = () => {
 }
 
 /** 真正执行【发送】消息操作 */
-const doSendMessage = async (content) => {
+const doSendMessage = async content => {
   // 校验
   if (content.length < 1) {
     console.error('发送失败,原因:内容为空!')
@@ -211,8 +292,7 @@ const doSendMessage = async (content) => {
 }
 
 /** 真正执行【发送】消息操作 */
-const doSendMessageStream = async (userMessage) => {
-
+const doSendMessageStream = async userMessage => {
   // 创建 AbortController 实例,以便中止请求
   conversationInAbortController.value = new AbortController()
   // 标记对话进行中
@@ -244,41 +324,42 @@ const doSendMessageStream = async (userMessage) => {
     let isFirstChunk = true // 是否是第一个 chunk 消息段
 
     await sendChatMessageStream(
-        userMessage.conversationId,
-        userMessage.content,
-        conversationInAbortController.value,
-        enableContext.value,
-        async (res) => {
-          const { code, data, msg } = JSON.parse(res.data)
-          if (code !== 0) {
-            console.log(`对话异常! ${msg}`)
-            return
-          }
-          // 如果内容为空,就不处理。
-          // if (data.receive.content === '') {
-          //   return
-          // }
-          receiveMessageFullText.value = receiveMessageFullText.value + data.receive.content
-          // 首次返回需要添加一个 message 到页面,后面的都是更新
-          if (isFirstChunk) {
-            isFirstChunk = false
-            // 弹出两个假数据
-            activeMessageList.value.pop()
-            activeMessageList.value.pop()
-            // 更新返回的数据
-            activeMessageList.value.push(data.send)
-            activeMessageList.value.push(data.receive)
-          }
-        },
-        (error) => {
-          console.log(`对话异常! ${error}`)
-          stopStream()
-          // 需要抛出异常,禁止重试
-          throw error
-        },
-        () => {
-          stopStream()
+      userMessage.conversationId,
+      userMessage.content,
+      conversationInAbortController.value,
+      enableContext.value,
+      async res => {
+        const { code, data, msg } = JSON.parse(res.data)
+        if (code !== 0) {
+          console.log(`对话异常! ${msg}`)
+          return
         }
+        // 如果内容为空,就不处理。
+        // if (data.receive.content === '') {
+        //   return
+        // }
+        receiveMessageFullText.value =
+          receiveMessageFullText.value + data.receive.content
+        // 首次返回需要添加一个 message 到页面,后面的都是更新
+        if (isFirstChunk) {
+          isFirstChunk = false
+          // 弹出两个假数据
+          activeMessageList.value.pop()
+          activeMessageList.value.pop()
+          // 更新返回的数据
+          activeMessageList.value.push(data.send)
+          activeMessageList.value.push(data.receive)
+        }
+      },
+      error => {
+        console.log(`对话异常! ${error}`)
+        stopStream()
+        // 需要抛出异常,禁止重试
+        throw error
+      },
+      () => {
+        stopStream()
+      }
     )
   } catch {}
 }
@@ -319,9 +400,9 @@ const messageList = computed(() => {
 // ============== 【消息滚动】相关 =============
 
 /** 滚动到 message 底部 */
-const scrollToBottom = async (isIgnore) => {
+const scrollToBottom = async isIgnore => {
   // if (messageRef.value) {
-    // messageRef.value.scrollToBottom(isIgnore)
+  // messageRef.value.scrollToBottom(isIgnore)
   // }
 }
 
@@ -339,7 +420,9 @@ const textRoll = async () => {
     const task = async () => {
       // 调整速度
       const diff =
-          (receiveMessageFullText.value.length - receiveMessageDisplayedText.value.length) / 10
+        (receiveMessageFullText.value.length -
+          receiveMessageDisplayedText.value.length) /
+        10
       if (diff > 5) {
         textSpeed.value = 10
       } else if (diff > 2) {
@@ -359,7 +442,8 @@ const textRoll = async () => {
         index++
 
         // 更新 message
-        const lastMessage = activeMessageList.value[activeMessageList.value.length - 1]
+        const lastMessage =
+          activeMessageList.value[activeMessageList.value.length - 1]
         lastMessage.content = receiveMessageDisplayedText.value
         // 滚动到住下面
         await scrollToBottom()
@@ -380,24 +464,24 @@ const textRoll = async () => {
   } catch {}
 }
 
-
 /** 初始化 **/
 onMounted(async () => {
-  if(personId.value) {
+  if (personId.value) {
     // 智能问答
-    CreateDialogue({roleId: personId.value}).then(res => {
-      console.log("创建会话:",res);
-      activeConversationId.value = res.data;
-    }).catch(error => {
-      console.error('请求出错:', error);
-    });
+    CreateDialogue({ roleId: personId.value })
+      .then(res => {
+        console.log('创建会话:', res)
+        activeConversationId.value = res.data
+      })
+      .catch(error => {
+        console.error('请求出错:', error)
+      })
 
     await getConversation(personId.value)
   }
 
   // 获取列表数据
   // activeMessageListLoading.value = true
-
 })
 </script>
 
@@ -407,6 +491,17 @@ onMounted(async () => {
 @function rpx($px) {
   @return math.div($px, 750) * 100vw;
 }
+/* 添加过渡样式 */
+.drawer-slide-enter-active,
+.drawer-slide-leave-active {
+  transition: all 0.3s ease;
+}
+
+.drawer-slide-enter-from,
+.drawer-slide-leave-to {
+  transform: translateX(-100%);
+  opacity: 0;
+}
 .home-container {
   position: fixed;
   top: 0;
@@ -416,16 +511,81 @@ onMounted(async () => {
   display: flex;
   flex-direction: row;
   gap: rpx(0);
+  background: linear-gradient(to bottom, #e2ddfc, #f1effd);
+}
+.icon-expand {
+  width: rpx(30);
+  height: rpx(30);
+  z-index: 9999;
+  position: absolute;
+  cursor: pointer;
 }
 
-// 侧边栏
+.icon-expand .el-icon {
+  font-size: rpx(15);
+  position: absolute;
+  color: white;
+  left: rpx(9);
+  margin-top: rpx(17);
+}
+
+.icon-expand .el-icon:active {
+  color: black;
+}
+
+.content-wrapper {
+  display: flex;
+  flex: 1;
+}
 .left-group {
+  width: rpx(135);
+  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(55);
+  margin-left: rpx(10);
+}
+.el-menu-item {
+  width: rpx(115);
+  height: rpx(25);
+  margin-bottom: rpx(5);
+  border-radius: rpx(6);
+  color: white;
+  font-size: rpx(8);
+}
+.el-menu-item .el-icon svg {
+  font-size: rpx(15);
+  color: white;
+}
+
+.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,
+    #ffefb0,
+    #ffcc00
+  ); /* 设置悬停、聚焦、点击状态下的背景色 */
+  box-shadow: 0 8px 8px rgb(0, 0, 0, 0.3);
+  color: black;
+  font-size: rpx(8);
+}
+// 侧边栏
+.left-group2 {
   width: rpx(150);
   height: 100%;
   display: flex;
   background-color: #ece9fd;
 }
-.left-group img {
+.left-group2 img {
   width: rpx(120);
   // height: auto;
 }