|
@@ -13,36 +13,36 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="main-content">
|
|
|
- <div class="chat-container">
|
|
|
+ <div class="chat-container" ref="chatContainerRef">
|
|
|
<div class="message-list">
|
|
|
<div v-for="(item, index) in chatRecordList" :key="item.id">
|
|
|
<!-- 会话图标 -->
|
|
|
<!--<div :class="item.isUser == 1? 'user-image' : 'system-image' "></div>-->
|
|
|
<div v-if="item.isUser == 1" class="user-message">
|
|
|
- <img src="@/assets/images/common/ai/user.png" alt="icon"/>
|
|
|
+ <img src="@/assets/images/common/ai/user.png" alt="icon" style="margin-left: 4px"/>
|
|
|
<span class="user-message-content">{{item.content}}</span>
|
|
|
</div>
|
|
|
- <div v-else class="message bot-message">
|
|
|
- <img src="@/assets/images/common/ai/system.png" alt="icon"/>
|
|
|
- <div v-show="!expandIndexList.includes(index)" class="bot-message-content" v-html="item.content.substring(0, 80)"></div>
|
|
|
- <div v-show="expandIndexList.includes(index)" class="bot-message-content" v-html="item.content"></div>
|
|
|
- <el-button v-if="item.content.length>80" @click="foldOrExpandMessage(index, expandIndexList.includes(index))">{{expandIndexList.includes(index)?'折叠':'展开'}}</el-button>
|
|
|
+ <div v-else class="bot-message">
|
|
|
+ <img src="@/assets/images/common/ai/system.png" alt="icon" style="margin-left: 4px"/>
|
|
|
+ <div v-show="!expandIndexList.includes(index)" class="bot-message-content" style="margin-left: 4px" v-html="item.content.substring(0, 80)"></div>
|
|
|
+ <div v-show="expandIndexList.includes(index)" class="bot-message-content" style="margin-left: 4px" v-html="item.content"></div>
|
|
|
+ <el-button v-if="item.content.length>80" style="margin-left: 4px" @click="foldOrExpandMessage(index, expandIndexList.includes(index))">{{expandIndexList.includes(index)?'折叠':'展开'}}</el-button>
|
|
|
<el-button>播放</el-button>
|
|
|
</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- <div class="message-box">
|
|
|
- <div class="typing-message">
|
|
|
- <span>
|
|
|
- <span v-html="streamHtmlData"></span>
|
|
|
- <span class="loading-dots">
|
|
|
- <span class="dot"></span>
|
|
|
- <span class="dot"></span>
|
|
|
+ <div class="bot-message" v-if="streamHtmlData">
|
|
|
+ <img src="@/assets/images/common/ai/system.png" alt="icon"/>
|
|
|
+ <div class="bot-message-content" v-html="streamHtmlData"></div>
|
|
|
+ <div class="typing">
|
|
|
+ <span>
|
|
|
+ <span class="loading-dots">
|
|
|
+ <span class="dot"></span>
|
|
|
+ <span class="dot"></span>
|
|
|
+ </span>
|
|
|
</span>
|
|
|
- </span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- <el-button @click="stopMessage">停止回答</el-button>
|
|
|
</div>
|
|
|
<div class="input-container">
|
|
|
<el-input
|
|
@@ -65,6 +65,9 @@
|
|
|
<div class="control-button">
|
|
|
<el-button style="width:140px" @click="closeVoiceOpen">结束</el-button>
|
|
|
</div>
|
|
|
+ <div class="control-button">
|
|
|
+ <el-button style="width:140px" @click="stopMessage">停止回答</el-button>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
<div class="session-list-box">
|
|
|
|
|
@@ -74,14 +77,17 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
- import {nextTick, onMounted} from 'vue'
|
|
|
- import {aiChatRecordList} from '@/api/xjc-integratedmachine/common/aiChat.js'
|
|
|
+ import {nextTick, onMounted, useTemplateRef} from 'vue'
|
|
|
+ import {aiChatRecordList, aiChatRecordAdd} from '@/api/xjc-integratedmachine/common/aiChat.js'
|
|
|
import { getToken } from '@/utils/auth'
|
|
|
+ import { setScroll } from '@/utils/scroll.js'
|
|
|
// md转换为html
|
|
|
import { marked } from 'marked'
|
|
|
// 语音识别
|
|
|
import CryptoJS from 'crypto-js';
|
|
|
import * as RecorderManager from "/public/ai/iat/dist/index.umd.js"
|
|
|
+ import * as AudioPlayer from "/public/ai/tts/dist/index.umd.js"
|
|
|
+
|
|
|
|
|
|
// 聊天记录
|
|
|
let chatRecordList = ref([])
|
|
@@ -116,6 +122,16 @@
|
|
|
})
|
|
|
}
|
|
|
|
|
|
+ function addRecord(content) {
|
|
|
+ let queryForm = {
|
|
|
+ content: content,
|
|
|
+ isUser: 0
|
|
|
+ }
|
|
|
+ aiChatRecordAdd(queryForm).then(resp =>{
|
|
|
+ console.log(resp)
|
|
|
+
|
|
|
+ })
|
|
|
+ }
|
|
|
const sendMessage = () => {
|
|
|
if (inputMessage.value.trim()) {
|
|
|
sendRequest(inputMessage.value.trim())
|
|
@@ -131,7 +147,7 @@
|
|
|
isTyping: false
|
|
|
}
|
|
|
// 消息加入聊天记录
|
|
|
- // chatRecordList.value.push(userMessage)
|
|
|
+ chatRecordList.value.push(userMessage)
|
|
|
try{
|
|
|
isLoading.value = true;
|
|
|
streamMarkdownData.value = ''; // 清空之前的数据
|
|
@@ -177,6 +193,7 @@
|
|
|
} finally {
|
|
|
streamHtmlData.value = marked(streamMarkdownData.value)
|
|
|
isLoading.value = false;
|
|
|
+ addRecord(streamHtmlData.value);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -229,7 +246,7 @@
|
|
|
APIKey: '8b1a53486bec887eb817b4410aa743ed',
|
|
|
}
|
|
|
// 初始化语音识别
|
|
|
- function initRecognize(){
|
|
|
+ function initSpeechRecognition(){
|
|
|
// 初始化录音
|
|
|
recorder = new window.RecorderManager('/ai/iat/dist');
|
|
|
// 开始录音
|
|
@@ -464,10 +481,31 @@
|
|
|
}
|
|
|
/* 语音识别 ↑*/
|
|
|
|
|
|
+ /*语音合成 ↓*/
|
|
|
+
|
|
|
+
|
|
|
+ function initSpeechsynthesis(){
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /*语音合成 ↑*/
|
|
|
+
|
|
|
+ let chatContainerRef = ref(null)
|
|
|
+ let chatContainerRefObj = useTemplateRef(chatContainerRef)
|
|
|
+
|
|
|
+
|
|
|
+ const scrollToBottom = () => {
|
|
|
+ nextTick(() => {
|
|
|
+ chatContainerRef.value.scrollTop = chatContainerRef.value.scrollHeight;
|
|
|
+ });
|
|
|
+ };
|
|
|
|
|
|
onMounted(()=>{
|
|
|
nextTick(()=>{
|
|
|
- initRecognize();
|
|
|
+ // 组件挂载后自动滚动到底部
|
|
|
+ scrollToBottom();
|
|
|
+ initSpeechRecognition();
|
|
|
+ initSpeechsynthesis();
|
|
|
sendMessage()
|
|
|
})
|
|
|
|
|
@@ -647,6 +685,7 @@
|
|
|
/*输入框*/
|
|
|
.input-container {
|
|
|
display: flex;
|
|
|
+ align-items: center;
|
|
|
}
|
|
|
|
|
|
.input-container .el-input {
|