|
@@ -1,20 +1,54 @@
|
|
<template>
|
|
<template>
|
|
<div class="app-layout">
|
|
<div class="app-layout">
|
|
- <div class="sidebar">
|
|
|
|
|
|
+ <div class="side-bar">
|
|
<div class="logo-section">
|
|
<div class="logo-section">
|
|
<img src="@/assets/images/common/ai/logo.png" alt="新基础 小新" width="160" height="160" />
|
|
<img src="@/assets/images/common/ai/logo.png" alt="新基础 小新" width="160" height="160" />
|
|
<span class="logo-text">新基础 小新</span>
|
|
<span class="logo-text">新基础 小新</span>
|
|
</div>
|
|
</div>
|
|
|
|
+ <div>
|
|
|
|
+ <ul>
|
|
|
|
+ <li><el-text>我的学习压力大,每天睡不好怎么办?</el-text></li>
|
|
|
|
+ <li><el-text>帮我查一下2024年清华大学在四川省录取分数线</el-text></li>
|
|
|
|
+ </ul>
|
|
|
|
+ </div>
|
|
</div>
|
|
</div>
|
|
- <div>
|
|
|
|
- <div class="content-box">
|
|
|
|
- <div v-html="streamHtmlData"></div>
|
|
|
|
-<!-- <span>{{streamData}}</span>-->
|
|
|
|
- <span class="loading-dots"></span>
|
|
|
|
- <span class="dot"></span>
|
|
|
|
- <span class="dot"></span>
|
|
|
|
|
|
+ <div class="main-content">
|
|
|
|
+ <div class="chat-container">
|
|
|
|
+ <div class="message-list">
|
|
|
|
+
|
|
|
|
+ </div>
|
|
|
|
+ <div class="content-box">
|
|
|
|
+ <div v-html="streamHtmlData"></div>
|
|
|
|
+ <span class="loading-dots"></span>
|
|
|
|
+ <span class="dot"></span>
|
|
|
|
+ <span class="dot"></span>
|
|
|
|
+ </div>
|
|
|
|
+ <el-button @click="stopMessage">停止回答</el-button>
|
|
|
|
+ </div>
|
|
|
|
+ <div class="input-container">
|
|
|
|
+ <el-input
|
|
|
|
+ v-model="inputMessage"
|
|
|
|
+ placeholder="请输入消息"
|
|
|
|
+ @keyup.enter="sendMessage"
|
|
|
|
+ ></el-input>
|
|
|
|
+ <el-button @click="sendMessage" :disabled="isSending" type="primary">发送</el-button>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ <div class="control-bar">
|
|
|
|
+ <div class="control-button-box">
|
|
|
|
+ <div class="control-button">
|
|
|
|
+ <el-button style="width:140px">退出</el-button>
|
|
|
|
+ </div>
|
|
|
|
+ <div class="control-button">
|
|
|
|
+ <el-button style="width:140px">静音</el-button>
|
|
|
|
+ </div>
|
|
|
|
+ <div class="control-button">
|
|
|
|
+ <el-button style="width:140px">新会话</el-button>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ <div class="session-list-box">
|
|
|
|
+
|
|
</div>
|
|
</div>
|
|
- <el-button @click="stopMessage">停止回答</el-button>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</template>
|
|
@@ -24,24 +58,46 @@
|
|
import { getToken } from '@/utils/auth'
|
|
import { getToken } from '@/utils/auth'
|
|
import { marked } from 'marked'
|
|
import { marked } from 'marked'
|
|
|
|
|
|
- let data = ref()
|
|
|
|
- const streamData = ref(''); // 存储流式数据
|
|
|
|
- let streamHtmlData = ref(''); // 存储流式数据
|
|
|
|
- const isLoading = ref(false); // 加载状态
|
|
|
|
- let reader = null; // 读取器实例
|
|
|
|
- let controller = null; // AbortController用于中止请求
|
|
|
|
-
|
|
|
|
|
|
+ // 聊天记录
|
|
|
|
+ let chatRecordList = ref([])
|
|
|
|
+ // md流式数据
|
|
|
|
+ const streamData = ref('');
|
|
|
|
+ // html流式数据
|
|
|
|
+ let streamHtmlData = ref('');
|
|
|
|
+ // 流式加载状态
|
|
|
|
+ const isLoading = ref(false);
|
|
|
|
+ // 读取器实例
|
|
|
|
+ let reader = null;
|
|
|
|
+ // AbortController用于中止请求
|
|
|
|
+ let controller = null;
|
|
|
|
+ // 输入的问题
|
|
|
|
+ const inputMessage = ref('')
|
|
|
|
|
|
// 查看所有聊天记录
|
|
// 查看所有聊天记录
|
|
list();
|
|
list();
|
|
function list() {
|
|
function list() {
|
|
getAiChatRecordList().then(resp =>{
|
|
getAiChatRecordList().then(resp =>{
|
|
console.log(resp)
|
|
console.log(resp)
|
|
- data.value = resp;
|
|
|
|
|
|
+ chatRecordList.value = resp;
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
|
|
- const sendMessage = async() => {
|
|
|
|
|
|
+ const sendMessage = () => {
|
|
|
|
+ if (inputMessage.value.trim()) {
|
|
|
|
+ sendRequest(inputMessage.value.trim())
|
|
|
|
+ inputMessage.value = ''
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const sendRequest = async(message) => {
|
|
|
|
+ // 用户信息
|
|
|
|
+ const userMessage = {
|
|
|
|
+ isUser: true,
|
|
|
|
+ content: message,
|
|
|
|
+ isTyping: false
|
|
|
|
+ }
|
|
|
|
+ // 消息加入聊天记录
|
|
|
|
+ // chatRecordList.value.push(userMessage)
|
|
try{
|
|
try{
|
|
isLoading.value = true;
|
|
isLoading.value = true;
|
|
streamData.value = ''; // 清空之前的数据
|
|
streamData.value = ''; // 清空之前的数据
|
|
@@ -51,7 +107,7 @@
|
|
|
|
|
|
// 请求体
|
|
// 请求体
|
|
let form = {
|
|
let form = {
|
|
- "content": "生成一个高中学习计划表格"
|
|
|
|
|
|
+ "content": message? message: "你是谁?"
|
|
}
|
|
}
|
|
// 发送fetch请求
|
|
// 发送fetch请求
|
|
const response = await fetch('/dev-api/ai/chat/stream', {
|
|
const response = await fetch('/dev-api/ai/chat/stream', {
|
|
@@ -74,7 +130,7 @@
|
|
|
|
|
|
// 解码并追加数据
|
|
// 解码并追加数据
|
|
let chunk = decoder.decode(value, { stream: true });
|
|
let chunk = decoder.decode(value, { stream: true });
|
|
- chunk = chunk.replace(/\n\n/g, ' ').replace(/data:/g, '')
|
|
|
|
|
|
+ chunk = chunk.replace(/\n\n/g, '').replace(/data:/g, '')
|
|
streamData.value += chunk;
|
|
streamData.value += chunk;
|
|
streamHtmlData.value = marked(streamData.value)
|
|
streamHtmlData.value = marked(streamData.value)
|
|
}
|
|
}
|
|
@@ -87,6 +143,7 @@
|
|
} finally {
|
|
} finally {
|
|
streamHtmlData.value = marked(streamData.value)
|
|
streamHtmlData.value = marked(streamData.value)
|
|
isLoading.value = false;
|
|
isLoading.value = false;
|
|
|
|
+ console.log("==============");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -120,10 +177,11 @@
|
|
.app-layout {
|
|
.app-layout {
|
|
display: flex;
|
|
display: flex;
|
|
height: 100vh;
|
|
height: 100vh;
|
|
|
|
+ width: 100vw;
|
|
}
|
|
}
|
|
|
|
|
|
- .sidebar {
|
|
|
|
- width: 200px;
|
|
|
|
|
|
+ .side-bar {
|
|
|
|
+ width: 20vw;
|
|
background-color: #f4f4f9;
|
|
background-color: #f4f4f9;
|
|
padding: 20px;
|
|
padding: 20px;
|
|
display: flex;
|
|
display: flex;
|
|
@@ -131,21 +189,86 @@
|
|
align-items: center;
|
|
align-items: center;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ .main-content {
|
|
|
|
+ width: 70vw;
|
|
|
|
+ height: 100vh;
|
|
|
|
+ flex: 1;
|
|
|
|
+ padding: 20px;
|
|
|
|
+ overflow-y: auto;
|
|
|
|
+ }
|
|
|
|
+ .chat-container {
|
|
|
|
+ display: flex;
|
|
|
|
+ flex-direction: column;
|
|
|
|
+ height: 90vh;
|
|
|
|
+ /*overflow-y: auto;*/
|
|
|
|
+ }
|
|
|
|
+
|
|
.logo-section {
|
|
.logo-section {
|
|
display: flex;
|
|
display: flex;
|
|
flex-direction: column;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
align-items: center;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /*控制区*/
|
|
|
|
+ .control-bar{
|
|
|
|
+ width: 10vw;
|
|
|
|
+ background-color: #f4f4f9;
|
|
|
|
+ padding: 20px;
|
|
|
|
+/* display: flex;
|
|
|
|
+ flex-direction: column;
|
|
|
|
+ align-items: center;*/
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ .control-button-box {
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ .control-button {
|
|
|
|
+ display: flex;
|
|
|
|
+ justify-content: center;
|
|
|
|
+ margin: 4px 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ .session-list-box{
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /*聊天列表*/
|
|
|
|
+ .message-list{
|
|
|
|
+ flex: 1;
|
|
|
|
+ overflow-y: auto;
|
|
|
|
+ padding: 10px;
|
|
|
|
+ border: 1px solid #e0e0e0;
|
|
|
|
+ border-radius: 4px;
|
|
|
|
+ background-color: #fff;
|
|
|
|
+ margin-bottom: 10px;
|
|
|
|
+ display: flex;
|
|
|
|
+ flex-direction: column;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ .user-message {
|
|
|
|
+ max-width: 70%;
|
|
|
|
+ background-color: #e1f5fe;
|
|
|
|
+ align-self: flex-end;
|
|
|
|
+ flex-direction: row-reverse;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ .bot-message {
|
|
|
|
+ max-width: 100%;
|
|
|
|
+ background-color: #f1f8e9;
|
|
|
|
+ align-self: flex-start;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* 内容区 */
|
|
.content-box {
|
|
.content-box {
|
|
/*max-height: 300px;*/
|
|
/*max-height: 300px;*/
|
|
- width: 80vw;
|
|
|
|
|
|
+ width: 60vw;
|
|
overflow-y: auto;
|
|
overflow-y: auto;
|
|
border: 1px solid #eee;
|
|
border: 1px solid #eee;
|
|
padding: 10px;
|
|
padding: 10px;
|
|
margin-top: 10px;
|
|
margin-top: 10px;
|
|
background: #f9f9f9;
|
|
background: #f9f9f9;
|
|
}
|
|
}
|
|
|
|
+
|
|
pre {
|
|
pre {
|
|
white-space: pre-wrap;
|
|
white-space: pre-wrap;
|
|
word-wrap: break-word;
|
|
word-wrap: break-word;
|
|
@@ -178,4 +301,14 @@
|
|
opacity: 1;
|
|
opacity: 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ /*输入框*/
|
|
|
|
+ .input-container {
|
|
|
|
+ display: flex;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ .input-container .el-input {
|
|
|
|
+ flex: 1;
|
|
|
|
+ margin-right: 10px;
|
|
|
|
+ }
|
|
</style>
|
|
</style>
|