|
@@ -1,21 +1,32 @@
|
|
|
<template>
|
|
|
- <div>
|
|
|
- <div class="content-box">
|
|
|
- <pre>{{ streamData }}</pre>
|
|
|
- <span class="dot"></span>
|
|
|
- <span class="dot"></span>
|
|
|
+ <div class="app-layout">
|
|
|
+ <div class="sidebar">
|
|
|
+ <div class="logo-section">
|
|
|
+ <img src="@/assets/images/common/ai/logo.png" alt="新基础 小新" width="160" height="160" />
|
|
|
+ <span class="logo-text">新基础 小新</span>
|
|
|
+ </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>
|
|
|
+ <el-button @click="stopMessage">停止回答</el-button>
|
|
|
</div>
|
|
|
-
|
|
|
- <el-button @click="stopMessage">停止回答</el-button>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
import {getAiChatRecordList} from '@/api/xjc-integratedmachine/common/aiChat.js'
|
|
|
import { getToken } from '@/utils/auth'
|
|
|
+ import { marked } from 'marked'
|
|
|
|
|
|
let data = ref()
|
|
|
const streamData = ref(''); // 存储流式数据
|
|
|
+ let streamHtmlData = ref(''); // 存储流式数据
|
|
|
const isLoading = ref(false); // 加载状态
|
|
|
let reader = null; // 读取器实例
|
|
|
let controller = null; // AbortController用于中止请求
|
|
@@ -40,7 +51,7 @@
|
|
|
|
|
|
// 请求体
|
|
|
let form = {
|
|
|
- "content": "你是谁?"
|
|
|
+ "content": "生成一个高中学习计划表格"
|
|
|
}
|
|
|
// 发送fetch请求
|
|
|
const response = await fetch('/dev-api/ai/chat/stream', {
|
|
@@ -63,8 +74,9 @@
|
|
|
|
|
|
// 解码并追加数据
|
|
|
let chunk = decoder.decode(value, { stream: true });
|
|
|
- chunk = chunk.replace(/\n/g, '').replace(/data:/g, '')
|
|
|
+ chunk = chunk.replace(/\n\n/g, ' ').replace(/data:/g, '')
|
|
|
streamData.value += chunk;
|
|
|
+ streamHtmlData.value = marked(streamData.value)
|
|
|
}
|
|
|
}catch (error) {
|
|
|
// 如果是手动中止,不显示错误
|
|
@@ -73,6 +85,7 @@
|
|
|
streamData.value = 'Error: ' + error.message;
|
|
|
}
|
|
|
} finally {
|
|
|
+ streamHtmlData.value = marked(streamData.value)
|
|
|
isLoading.value = false;
|
|
|
}
|
|
|
}
|
|
@@ -104,8 +117,29 @@
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
+ .app-layout {
|
|
|
+ display: flex;
|
|
|
+ height: 100vh;
|
|
|
+ }
|
|
|
+
|
|
|
+ .sidebar {
|
|
|
+ width: 200px;
|
|
|
+ background-color: #f4f4f9;
|
|
|
+ padding: 20px;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ .logo-section {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ }
|
|
|
+
|
|
|
.content-box {
|
|
|
- max-height: 300px;
|
|
|
+ /*max-height: 300px;*/
|
|
|
+ width: 80vw;
|
|
|
overflow-y: auto;
|
|
|
border: 1px solid #eee;
|
|
|
padding: 10px;
|
|
@@ -116,6 +150,9 @@
|
|
|
white-space: pre-wrap;
|
|
|
word-wrap: break-word;
|
|
|
}
|
|
|
+ .loading-dots {
|
|
|
+ padding-left: 5px;
|
|
|
+ }
|
|
|
.dot {
|
|
|
display: inline-block;
|
|
|
margin-left: 5px;
|
|
@@ -125,4 +162,20 @@
|
|
|
border-radius: 50%;
|
|
|
animation: pulse 1.2s infinite ease-in-out both;
|
|
|
}
|
|
|
+ .dot:nth-child(2) {
|
|
|
+ animation-delay: -0.6s;
|
|
|
+ }
|
|
|
+
|
|
|
+ @keyframes pulse {
|
|
|
+ 0%,
|
|
|
+ 100% {
|
|
|
+ transform: scale(0.6);
|
|
|
+ opacity: 0.4;
|
|
|
+ }
|
|
|
+
|
|
|
+ 50% {
|
|
|
+ transform: scale(1);
|
|
|
+ opacity: 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
</style>
|