ws聊天记录
This commit is contained in:
parent
a411e94890
commit
859d6f2e79
353
pages/chat/chatPage - 副本 (2).vue
Normal file
353
pages/chat/chatPage - 副本 (2).vue
Normal file
@ -0,0 +1,353 @@
|
||||
<template>
|
||||
<view class="chat-container">
|
||||
<!-- 聊天消息区域 -->
|
||||
<scroll-view scroll-y :scroll-top="scrollTop" class="chat-messages" :scroll-with-animation="true"
|
||||
@scroll="onScroll">
|
||||
<view v-for="(msg, index) in messages" :key="index"
|
||||
:class="['message', msg.sender === 'me' ? 'sent' : 'received']">
|
||||
<image v-if="msg.sender !== 'me'" :src="botAvatar" class="avatar"></image>
|
||||
|
||||
<view :class="['message-bubble', msg.sender === 'me' ? 'me' : '']">
|
||||
<text class="message-text">{{ msg.content }}</text>
|
||||
<view class="typing-indicator" v-if="msg.isTyping">
|
||||
<view class="dot"></view>
|
||||
<view class="dot"></view>
|
||||
<view class="dot"></view>
|
||||
<text class="typing-text">对方正在输入...</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<image v-if="msg.sender === 'me'" :src="userAvatar" class="avatar"></image>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 输入区域 -->
|
||||
<view class="chat-input">
|
||||
<input class="input-field" placeholder="输入消息..." v-model="inputMsg" @confirm="sendMessage"
|
||||
confirm-type="send" />
|
||||
<button class="send-btn" :disabled="!inputMsg.trim()" @click="sendMessage">
|
||||
发送
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
BASE_URL
|
||||
} from '@/utils/config';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
inputMsg: '',
|
||||
scrollTop: 0,
|
||||
userAvatar: require('../../static/imgs/index/nav.png'),
|
||||
botAvatar: require('../../static/imgs/ai/chuandaguwen.png'),
|
||||
messages: [],
|
||||
socketTask: null, // 微信小程序的 WebSocket 任务对象
|
||||
isConnected: false,
|
||||
reconnectAttempts: 0,
|
||||
maxReconnectAttempts: 5,
|
||||
reconnectDelay: 3000,
|
||||
userId: null,
|
||||
userName: '默认用户',
|
||||
apiKey: '',
|
||||
serviceUrl: ''
|
||||
}
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
this.serviceUrl = options.serviceUrl || '';
|
||||
this.apiKey = options.apiKey || '';
|
||||
this.botAvatar = options.icon || this.botAvatar;
|
||||
|
||||
const userInfo = wx.getStorageSync('userInfo') || {};
|
||||
this.userId = userInfo.id || Date.now().toString();
|
||||
this.userName = userInfo.name || '默认用户';
|
||||
this.userAvatar = userInfo.avatar || this.userAvatar;
|
||||
|
||||
uni.setNavigationBarTitle({
|
||||
title: options.name || 'AI助手'
|
||||
});
|
||||
|
||||
this.initWebSocket();
|
||||
},
|
||||
|
||||
onUnload() {
|
||||
this.closeWebSocket();
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 初始化WebSocket连接
|
||||
initWebSocket() {
|
||||
if (this.isConnected) return;
|
||||
|
||||
// 微信小程序中使用 wx.connectSocket
|
||||
this.socketTask = wx.connectSocket({
|
||||
url: `ws://10.10.1.6:8071/api/v1/ws/ai?apiUrl=${encodeURIComponent(this.serviceUrl)}&apiToken=${this.apiKey}&userName=${this.userName}`,
|
||||
success: () => {
|
||||
console.log('WebSocket连接创建成功');
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('WebSocket连接创建失败', err);
|
||||
this.handleReconnect();
|
||||
}
|
||||
});
|
||||
|
||||
// 监听 WebSocket 事件
|
||||
this.socketTask.onOpen(() => {
|
||||
console.log('WebSocket连接已打开');
|
||||
this.isConnected = true;
|
||||
this.reconnectAttempts = 0;
|
||||
this.addMessage('bot', '连接已建立,请问有什么可以帮您?');
|
||||
});
|
||||
|
||||
this.socketTask.onMessage((res) => {
|
||||
console.log('收到WebSocket消息:', res.data);
|
||||
this.processBotMessage(res.data);
|
||||
});
|
||||
|
||||
this.socketTask.onError((err) => {
|
||||
console.error('WebSocket发生错误:', err);
|
||||
this.isConnected = false;
|
||||
this.addMessage('bot', '连接出错,请稍后再试');
|
||||
this.handleReconnect();
|
||||
});
|
||||
|
||||
this.socketTask.onClose(() => {
|
||||
console.log('WebSocket连接已关闭');
|
||||
this.isConnected = false;
|
||||
this.handleReconnect();
|
||||
});
|
||||
},
|
||||
|
||||
// 处理机器人消息
|
||||
processBotMessage(data) {
|
||||
// 移除思考状态的消息
|
||||
this.messages = this.messages.filter(msg => !msg.isTyping);
|
||||
|
||||
// 处理不同类型的消息
|
||||
if (data.startsWith('<think>')) {
|
||||
// 机器人正在思考
|
||||
this.addMessage('bot', '', true);
|
||||
} else if (data.startsWith('<answer>')) {
|
||||
// 正式回答
|
||||
const content = data.replace(/<answer>/g, '').replace(/<\/answer>/g, '');
|
||||
this.addMessage('bot', content);
|
||||
} else if (data.includes('</think>')) {
|
||||
// 思考结束,不做特殊处理
|
||||
} else {
|
||||
// 普通消息
|
||||
this.addMessage('bot', data);
|
||||
}
|
||||
},
|
||||
|
||||
// 添加消息到聊天列表
|
||||
addMessage(sender, content, isTyping = false) {
|
||||
const message = {
|
||||
sender: sender === 'bot' ? 'other' : 'me',
|
||||
content,
|
||||
isTyping,
|
||||
timestamp: Date.now()
|
||||
};
|
||||
|
||||
this.messages.push(message);
|
||||
this.scrollToBottom();
|
||||
},
|
||||
|
||||
// 发送消息
|
||||
sendMessage() {
|
||||
const content = this.inputMsg.trim();
|
||||
if (!content || !this.isConnected) return;
|
||||
|
||||
this.addMessage('me', content);
|
||||
|
||||
// 微信小程序中使用 socketTask.send
|
||||
this.socketTask.send({
|
||||
data: content,
|
||||
success: () => {
|
||||
console.log('消息发送成功');
|
||||
// 添加机器人正在输入的提示
|
||||
this.addMessage('bot', '', true);
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('消息发送失败', err);
|
||||
this.addMessage('bot', '消息发送失败,请重试');
|
||||
}
|
||||
});
|
||||
|
||||
this.inputMsg = '';
|
||||
},
|
||||
|
||||
// 处理重连逻辑
|
||||
handleReconnect() {
|
||||
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
||||
console.log('已达到最大重连次数');
|
||||
this.addMessage('bot', '连接已断开,请刷新页面重试');
|
||||
return;
|
||||
}
|
||||
|
||||
this.reconnectAttempts++;
|
||||
console.log(`尝试重新连接,第${this.reconnectAttempts}次`);
|
||||
|
||||
setTimeout(() => {
|
||||
this.initWebSocket();
|
||||
}, this.reconnectDelay);
|
||||
},
|
||||
|
||||
// 关闭WebSocket连接
|
||||
closeWebSocket() {
|
||||
if (this.socketTask) {
|
||||
this.socketTask.close({
|
||||
success: () => {
|
||||
console.log('WebSocket已主动关闭');
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('关闭WebSocket失败', err);
|
||||
}
|
||||
});
|
||||
this.socketTask = null;
|
||||
}
|
||||
this.isConnected = false;
|
||||
},
|
||||
|
||||
// 滚动到底部
|
||||
scrollToBottom() {
|
||||
this.$nextTick(() => {
|
||||
this.scrollTop = 99999; // 足够大的值确保滚动到底部
|
||||
});
|
||||
},
|
||||
|
||||
onScroll(e) {
|
||||
// 可以在这里实现加载历史消息
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.chat-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
.chat-messages {
|
||||
flex: 1;
|
||||
padding: 15px;
|
||||
background-color: #e5ddd5;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.message {
|
||||
display: flex;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.sent {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.received {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 50%;
|
||||
margin: 0 8px;
|
||||
}
|
||||
|
||||
.message-bubble {
|
||||
max-width: 70%;
|
||||
padding: 10px 15px;
|
||||
border-radius: 18px;
|
||||
background-color: white;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.me {
|
||||
background-color: #dcf8c6;
|
||||
}
|
||||
|
||||
.message-text {
|
||||
font-size: 16px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.chat-input {
|
||||
display: flex;
|
||||
padding: 10px;
|
||||
background-color: white;
|
||||
border-top: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.input-field {
|
||||
flex: 1;
|
||||
padding: 8px 15px;
|
||||
border-radius: 20px;
|
||||
background-color: #f0f0f0;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.send-btn {
|
||||
padding: 0 20px;
|
||||
border-radius: 20px;
|
||||
background-color: #07C160;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.send-btn:disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* 对方正在输入提示 */
|
||||
.typing-indicator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background-color: #7f8c8d;
|
||||
margin: 0 3px;
|
||||
animation: typing 1.4s infinite ease-in-out;
|
||||
}
|
||||
|
||||
.dot:nth-child(1) {
|
||||
animation-delay: 0s;
|
||||
}
|
||||
|
||||
.dot:nth-child(2) {
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
|
||||
.dot:nth-child(3) {
|
||||
animation-delay: 0.4s;
|
||||
}
|
||||
|
||||
.typing-text {
|
||||
font-size: 12px;
|
||||
margin-left: 8px;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
@keyframes typing {
|
||||
|
||||
0%,
|
||||
60%,
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
30% {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1,370 +1,330 @@
|
||||
<template>
|
||||
<view class="chat-container">
|
||||
|
||||
<!-- 聊天消息区域 -->
|
||||
<scroll-view scroll-y :scroll-top="scrollTop" class="chat-messages" :scroll-with-animation="true"
|
||||
@scroll="onScroll">
|
||||
<view class="message-time" v-if="showDate">今天 {{ getTime() }}</view>
|
||||
|
||||
<!-- 消息列表 -->
|
||||
<view v-for="(msg, index) in messages" :key="index"
|
||||
:class="['message', msg.sender === 'me' ? 'sent' : 'received']">
|
||||
<image v-if="msg.sender !== 'me'" :src="currentChat.avatar" class="avatar"></image>
|
||||
<image v-if="msg.sender !== 'me'" :src="botAvatar" class="avatar"></image>
|
||||
|
||||
<view :class="['message-bubble', msg.sender === 'me' ? 'me' : '']">
|
||||
<text class="message-text" v-if="msg.content">{{ msg.content }}</text>
|
||||
<!-- 对方正在输入提示 -->
|
||||
<view :class="['message-bubble', msg.sender === 'me' ? 'me' : '', msg.type]">
|
||||
<!-- <text class="message-type-tag" v-if="msg.type !== 'normal'">
|
||||
{{ msg.type === 'thinking' ? '思考中' : msg.type === 'answer' ? '回答' : '' }}
|
||||
</text> -->
|
||||
<text class="message-text">{{ msg.content }}</text>
|
||||
<view class="typing-indicator" v-if="msg.isTyping">
|
||||
<view class="dot"></view>
|
||||
<view class="dot"></view>
|
||||
<view class="dot"></view>
|
||||
<text class="typing-text">对方正在输入...</text>
|
||||
</view>
|
||||
<!-- <text class="message-status">{{ msg.time }}</text> -->
|
||||
</view>
|
||||
<image v-if="msg.sender === 'me'" :src="myAvatar" class="avatar"></image>
|
||||
|
||||
<image v-if="msg.sender === 'me'" :src="userAvatar" class="avatar"></image>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 输入区域 -->
|
||||
<view class="chat-input">
|
||||
<view class="input-container">
|
||||
<!-- <uni-icons type="plus" size="28" color="#666" class="action-icon"></uni-icons> -->
|
||||
<input class="input-field" placeholder="输入消息..." v-model="inputMsg" @confirm="sendMessage"
|
||||
@input="onInput" :focus="isFocus" />
|
||||
<!-- <uni-icons type="mic" size="28" color="#666" class="action-icon"></uni-icons>
|
||||
<uni-icons type="image" size="28" color="#666" class="action-icon"></uni-icons> -->
|
||||
</view>
|
||||
<button class="send-btn" :class="{active: inputMsg.trim() !== ''}" @click="sendMessage">
|
||||
<uni-icons type="paperplane" size="24" color="#fff"></uni-icons>
|
||||
confirm-type="send" />
|
||||
<button class="send-btn" :disabled="!inputMsg.trim()" @click="sendMessage">
|
||||
发送
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import uniIcons from '@dcloudio/uni-ui/lib/uni-icons/uni-icons.vue'
|
||||
import {IMAGE_BASE_URL,BASE_URL} from '@/utils/config';
|
||||
import {
|
||||
BASE_URL
|
||||
} from '@/utils/config';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
uniIcons
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
inputMsg: '',
|
||||
scrollTop: 0,
|
||||
isTyping: false,
|
||||
isFocus: false,
|
||||
showDate: true,
|
||||
myAvatar: require('../../static/imgs/index/nav.png'),
|
||||
currentChat: {
|
||||
name: '张婷婷',
|
||||
avatar: require('../../static/imgs/ai/chuandaguwen.png'),
|
||||
status: '在线'
|
||||
},
|
||||
userAvatar: require('../../static/imgs/index/nav.png'),
|
||||
botAvatar: require('../../static/imgs/ai/chuandaguwen.png'),
|
||||
messages: [],
|
||||
currentResponse: null, // 用于存储当前正在接收的响应
|
||||
currentResponseId: null, // 用于标识当前响应
|
||||
abortController: null, // 用于取消请求
|
||||
isStreaming: false, // 标记是否正在接收流
|
||||
socketTask: null, // 微信小程序的 WebSocket 任务对象
|
||||
isConnected: false,
|
||||
reconnectAttempts: 0,
|
||||
maxReconnectAttempts: 5,
|
||||
reconnectDelay: 3000,
|
||||
userId: null,
|
||||
serviceUrl:'',
|
||||
apiKey:'',
|
||||
aiId:null,
|
||||
aiName:''
|
||||
|
||||
userName: '默认用户',
|
||||
apiKey: '',
|
||||
serviceUrl: ''
|
||||
}
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
this.serviceUrl=options.serviceUrl
|
||||
this.apiKey=options.apiKey
|
||||
this.aiId=options.id
|
||||
this.currentChat.avatar=options.icon
|
||||
this.serviceUrl = options.serviceUrl || '';
|
||||
this.apiKey = options.apiKey || '';
|
||||
this.botAvatar = options.icon || this.botAvatar;
|
||||
|
||||
const userInfo = wx.getStorageSync('userInfo') || {};
|
||||
this.userId = userInfo.id || Date.now().toString();
|
||||
this.userName = userInfo.name || '默认用户';
|
||||
this.userAvatar = userInfo.avatar || this.userAvatar;
|
||||
|
||||
uni.setNavigationBarTitle({
|
||||
title: options.name
|
||||
})
|
||||
title: options.name || 'AI助手'
|
||||
});
|
||||
|
||||
this.initWebSocket();
|
||||
},
|
||||
mounted() {
|
||||
try {
|
||||
const userInfo = wx.getStorageSync('userInfo')
|
||||
if (userInfo === undefined || userInfo === null||userInfo=='') {
|
||||
const defaultValue=this.generateMiniProgramUUID()
|
||||
wx.setStorageSync('userId', defaultValue)
|
||||
this.userId=defaultValue
|
||||
// this.myAvatar=require('../../static/imgs/index/nav.png')
|
||||
}else{
|
||||
this.userId=userInfo.id
|
||||
this.myAvatar=userInfo.avatar||require('../../static/imgs/index/nav.png')
|
||||
}
|
||||
} catch (e) {
|
||||
const defaultValue=this.generateMiniProgramUUID()
|
||||
wx.setStorageSync('userId', defaultValue)
|
||||
this.userId=defaultValue
|
||||
}
|
||||
|
||||
onUnload() {
|
||||
this.closeWebSocket();
|
||||
},
|
||||
|
||||
methods: {
|
||||
generateMiniProgramUUID() {
|
||||
const buffer = new Uint8Array(16);
|
||||
wx.getRandomValues(buffer);
|
||||
buffer[6] = (buffer[6] & 0x0f) | 0x40; // Version 4
|
||||
buffer[8] = (buffer[8] & 0x3f) | 0x80; // Variant 10
|
||||
return Array.from(buffer)
|
||||
.map(b => b.toString(16).padStart(2, '0'))
|
||||
.join('')
|
||||
.replace(/(\w{8})(\w{4})(\w{4})(\w{4})(\w{12})/, '$1-$2-$3-$4-$5');
|
||||
},
|
||||
async sendMessage() {
|
||||
if (!this.inputMsg.trim() || this.isStreaming) return;
|
||||
// 初始化WebSocket连接
|
||||
initWebSocket() {
|
||||
if (this.isConnected) return;
|
||||
|
||||
// 添加用户消息
|
||||
const userMsg = {
|
||||
sender: 'me',
|
||||
content: this.inputMsg,
|
||||
time: this.getTime()
|
||||
};
|
||||
this.messages.push(userMsg);
|
||||
const userInput = this.inputMsg;
|
||||
this.inputMsg = '';
|
||||
this.scrollToBottom();
|
||||
|
||||
// 创建AI回复的消息对象
|
||||
const aiMsg = {
|
||||
sender: 'other',
|
||||
content: '',
|
||||
time: this.getTime(),
|
||||
isTyping: true,
|
||||
id: Date.now() // 唯一标识符
|
||||
};
|
||||
this.messages.push(aiMsg);
|
||||
this.isTyping = true;
|
||||
this.isStreaming = true;
|
||||
|
||||
try {
|
||||
// 创建AbortController以便取消请求
|
||||
// this.abortController = new AbortController();
|
||||
const response = await this.streamResponse(userInput, aiMsg.id);
|
||||
console.log('Stream completed:', response);
|
||||
} catch (error) {
|
||||
console.error('Stream error:', error);
|
||||
if (error.name !== 'AbortError') {
|
||||
this.updateAiMessage(aiMsg.id, '\n\n抱歉,请求过程中出现错误: ' + error.message);
|
||||
}
|
||||
} finally {
|
||||
this.isTyping = false;
|
||||
this.isStreaming = false;
|
||||
// this.abortController = null;
|
||||
}
|
||||
},
|
||||
|
||||
async streamResponse(inputParams, messageId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 使用uni.request进行流式请求
|
||||
uni.request({
|
||||
url: this.serviceUrl,
|
||||
method: 'POST',
|
||||
data: {
|
||||
'query':inputParams,
|
||||
"inputs": {
|
||||
"content": inputParams
|
||||
},
|
||||
"response_mode": "blocking", //blocking,streaming
|
||||
"user":this.userId
|
||||
},
|
||||
header: {
|
||||
'Authorization': `Bearer ${this.apiKey}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
// signal: this.abortController?.signal, // 支持取消请求
|
||||
// enableChunked: true, // 启用分块传输
|
||||
success: (res) => {
|
||||
// console.log(res)
|
||||
// 处理流式数据
|
||||
if (res.data) {
|
||||
console.log(res.data)
|
||||
console.log(res.data.data)
|
||||
try {
|
||||
let result=null
|
||||
if(this.aiId==3){
|
||||
const result = res.data.data.outputs.text;
|
||||
// 提取实际内容(移除<think>标签)
|
||||
const content = result.replace(/<think>[\s\S]*?<\/think>\n?/g, '');
|
||||
this.updateAiMessage(messageId, content);
|
||||
this.isStreaming = false
|
||||
this.isTyping = false;
|
||||
}else{
|
||||
const result = res.data.answer;
|
||||
// 提取实际内容(移除<think>标签)
|
||||
const content = result.replace(/<think>[\s\S]*?<\/think>\n?/g, '');
|
||||
this.updateAiMessage(messageId, content);
|
||||
this.isStreaming = false
|
||||
this.isTyping = false;
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.error('Parse error:', e);
|
||||
}
|
||||
}
|
||||
// 微信小程序中使用 wx.connectSocket
|
||||
this.socketTask = wx.connectSocket({
|
||||
url: `ws://10.10.1.6:8071/api/v1/ws/ai?apiUrl=${encodeURIComponent(this.serviceUrl)}&apiToken=${this.apiKey}&userName=${this.userName}`,
|
||||
success: () => {
|
||||
console.log('WebSocket连接创建成功');
|
||||
},
|
||||
fail: (err) => {
|
||||
reject(new Error(err.errMsg));
|
||||
},
|
||||
complete: () => {
|
||||
// 请求完成
|
||||
console.error('WebSocket连接创建失败', err);
|
||||
this.handleReconnect();
|
||||
}
|
||||
});
|
||||
|
||||
// 监听 WebSocket 事件
|
||||
this.socketTask.onOpen(() => {
|
||||
console.log('WebSocket连接已打开');
|
||||
this.isConnected = true;
|
||||
this.reconnectAttempts = 0;
|
||||
this.addMessage('bot', '连接已建立,请问有什么可以帮您?');
|
||||
});
|
||||
|
||||
this.socketTask.onMessage((res) => {
|
||||
// console.log('收到WebSocket消息:', res.data);
|
||||
this.processBotMessage(res.data);
|
||||
});
|
||||
|
||||
this.socketTask.onError((err) => {
|
||||
console.error('WebSocket发生错误:', err);
|
||||
this.isConnected = false;
|
||||
this.addMessage('bot', '连接出错,请稍后再试');
|
||||
this.handleReconnect();
|
||||
});
|
||||
|
||||
this.socketTask.onClose(() => {
|
||||
console.log('WebSocket连接已关闭');
|
||||
this.isConnected = false;
|
||||
this.handleReconnect();
|
||||
});
|
||||
},
|
||||
|
||||
// 更新消息内容
|
||||
async updateAiMessage(id, newContent) {
|
||||
const msg = this.messages.find(m => m.id === id);
|
||||
if (msg) {
|
||||
// 格式化JSON内容为更易读的形式
|
||||
if (newContent.includes('{') && newContent.includes('}')) {
|
||||
processBotMessage(data) {
|
||||
try {
|
||||
const jsonObj = JSON.parse(newContent);
|
||||
msg.content = await this.formatJsonToReadableText(jsonObj);
|
||||
msg.isTyping = false
|
||||
// 解析数据
|
||||
const messageData = typeof data === 'string' ? JSON.parse(data) : data;
|
||||
|
||||
} catch (e) {
|
||||
// 如果不是完整JSON,直接显示
|
||||
msg.content = newContent;
|
||||
if (messageData.event === 'text_chunk') {
|
||||
// 处理分块文本
|
||||
this.handleTextChunk(messageData);
|
||||
} else if (messageData.event === 'workflow_finished') {
|
||||
// 处理完整节点完成
|
||||
this.handleNodeFinished(messageData);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('处理机器人消息出错:', error);
|
||||
this.addMessage('bot', '处理消息时出错: ' + error.message, 'error');
|
||||
}
|
||||
},
|
||||
|
||||
// 处理文本分块
|
||||
handleTextChunk(chunkData) {
|
||||
const text = chunkData.data.text;
|
||||
if (!text || text.trim() === '') return;
|
||||
|
||||
// 判断是否是思考内容
|
||||
const isThinking = text.includes('<think>') ||
|
||||
(this.messages[this.messages.length - 1]?.type === 'thinking');
|
||||
|
||||
if (isThinking) {
|
||||
this.handleThinkingContent(text);
|
||||
} else {
|
||||
msg.content = newContent;
|
||||
msg.isTyping = false
|
||||
this.handleAnswerContent(text);
|
||||
}
|
||||
|
||||
this.scrollToBottom();
|
||||
},
|
||||
|
||||
// 处理思考内容
|
||||
handleThinkingContent(text) {
|
||||
// 提取思考内容,去除标签
|
||||
const thinkContent = text.replace(/<\/?think>/g, '').trim();
|
||||
if (!thinkContent) return;
|
||||
|
||||
// 查找最后一条思考消息
|
||||
const lastThinkMsgIndex = this.messages.findLastIndex(
|
||||
msg => msg.type === 'thinking'
|
||||
);
|
||||
|
||||
if (lastThinkMsgIndex >= 0) {
|
||||
// 追加到现有思考消息
|
||||
this.messages[lastThinkMsgIndex].content += thinkContent;
|
||||
this.$forceUpdate();
|
||||
} else {
|
||||
// 创建新的思考消息
|
||||
this.addMessage('bot', thinkContent, 'thinking');
|
||||
}
|
||||
},
|
||||
|
||||
// 将JSON格式化为易读的文本
|
||||
formatJsonToReadableText(jsonObj) {
|
||||
let result = '';
|
||||
// 处理回答内容
|
||||
handleAnswerContent(text) {
|
||||
// 提取回答内容,去除标签
|
||||
const answerContent = text.replace(/<\/?answer>/g, '').trim();
|
||||
if (!answerContent) return;
|
||||
|
||||
// 处理穿搭部分
|
||||
if (jsonObj.outfit) {
|
||||
result += '👕 穿搭建议:\n';
|
||||
result += `上衣: ${jsonObj.outfit.top.style} (${jsonObj.outfit.top.color})\n`;
|
||||
result += `下装: ${jsonObj.outfit.bottom.style} (${jsonObj.outfit.bottom.color})\n`;
|
||||
result += `鞋子: ${jsonObj.outfit.shoes.style} (${jsonObj.outfit.shoes.color})\n`;
|
||||
// 查找最后一条回答消息
|
||||
const lastAnswerMsgIndex = this.messages.findLastIndex(
|
||||
msg => msg.type === 'answer' || msg.type === 'normal'
|
||||
);
|
||||
|
||||
if (jsonObj.outfit.accessories && jsonObj.outfit.accessories.length > 0) {
|
||||
result += '配饰:\n';
|
||||
jsonObj.outfit.accessories.forEach(acc => {
|
||||
result += `- ${acc.type}: ${acc.style} (${acc.color})\n`;
|
||||
});
|
||||
if (lastAnswerMsgIndex >= 0 && this.messages[lastAnswerMsgIndex].type === 'answer') {
|
||||
// 追加到现有回答消息
|
||||
this.messages[lastAnswerMsgIndex].content += answerContent;
|
||||
this.$forceUpdate();
|
||||
} else {
|
||||
// 创建新的回答消息
|
||||
this.addMessage('bot', answerContent, 'answer');
|
||||
}
|
||||
}
|
||||
|
||||
// 处理化妆建议
|
||||
if (jsonObj.makeup_suggestion) {
|
||||
result += '\n💄 化妆建议:\n';
|
||||
result += `风格: ${jsonObj.makeup_suggestion.style}\n`;
|
||||
result += `描述: ${jsonObj.makeup_suggestion.description}\n`;
|
||||
|
||||
if (jsonObj.makeup_suggestion.products && jsonObj.makeup_suggestion.products.length > 0) {
|
||||
result += '推荐产品:\n';
|
||||
jsonObj.makeup_suggestion.products.forEach(product => {
|
||||
result += `- ${product.name}: ${product.detail_description}\n`;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 处理造型建议
|
||||
if (jsonObj.styling_tips) {
|
||||
result += '\n💡 造型小贴士:\n';
|
||||
result += `色彩搭配: ${jsonObj.styling_tips.color_coordination}\n`;
|
||||
|
||||
if (jsonObj.styling_tips.detail_enhancements && jsonObj.styling_tips.detail_enhancements.length > 0) {
|
||||
result += '细节提升:\n';
|
||||
jsonObj.styling_tips.detail_enhancements.forEach(tip => {
|
||||
result += `- ${tip}\n`;
|
||||
});
|
||||
}
|
||||
|
||||
if (jsonObj.styling_tips.occasion_adaptation) {
|
||||
result += `场合适配: ${jsonObj.styling_tips.occasion_adaptation}\n`;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
// 取消请求
|
||||
cancelStream() {
|
||||
if (this.requestTask) {
|
||||
this.requestTask.abort();
|
||||
this.isTyping = false;
|
||||
this.isStreaming = false;
|
||||
this.requestTask = null;
|
||||
// 处理节点完成
|
||||
handleNodeFinished(nodeData) {
|
||||
// 标记最后一条消息为完成
|
||||
if (this.messages.length > 0) {
|
||||
const lastMsg = this.messages[this.messages.length - 1];
|
||||
lastMsg.isComplete = true;
|
||||
|
||||
// 如果最后一条是思考消息,我们不需要额外处理
|
||||
if (lastMsg.type === 'thinking') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 只有当没有找到回答内容时才添加完整输出
|
||||
if (nodeData.data.outputs?.text) {
|
||||
const fullText = nodeData.data.outputs.text;
|
||||
const answerContent = fullText.replace(/<think>[\s\S]*?<\/think>/g, '').trim();
|
||||
|
||||
// 检查是否已经有相同内容的消息
|
||||
const hasSameContent = this.messages.some(
|
||||
msg => msg.content.includes(answerContent)
|
||||
);
|
||||
|
||||
if (answerContent && !hasSameContent) {
|
||||
this.addMessage('bot', answerContent, 'answer');
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
receiveMessage() {
|
||||
this.isTyping = true;
|
||||
this.scrollToBottom();
|
||||
},
|
||||
|
||||
setTimeout(() => {
|
||||
const replies = [
|
||||
"明白了,谢谢您的解释明白了,谢谢您的解释明白了,谢谢您的解释明白了,谢谢您的解释明白了,谢谢您的解释明白了,谢谢您的解释明白了,谢谢您的解释明白了,谢谢您的解释明白了,谢谢您的解释明白了,谢谢您的解释明白了,谢谢您的解释",
|
||||
"这个功能听起来很实用这个功能听起来很实用这个功能听起来很实用这个功能听起来很实用这个功能听起来很实用这个功能听起来很实用",
|
||||
"请问价格方面是怎样的?这个功能听起来很实用这个功能听起来很实用这个功能听起来很实用这个功能听起来很实用这个功能听起来很实用这个功能听起来很实用",
|
||||
"我稍后会仔细查看这些信息这个功能听起来很实用这个功能听起来很实用这个功能听起来很实用",
|
||||
"好的,我会考虑购买专业版这个功能听起来很实用这个功能听起来很实用这个功能听起来很实用这个功能听起来很实用"
|
||||
];
|
||||
|
||||
const newMsg = {
|
||||
sender: 'other',
|
||||
content: replies[Math.floor(Math.random() * replies.length)],
|
||||
time: this.getTime()
|
||||
// 修改后的 addMessage 方法
|
||||
addMessage(sender, content, type = 'normal', isTyping = false) {
|
||||
const message = {
|
||||
sender: sender === 'bot' ? 'other' : 'me',
|
||||
content,
|
||||
type, // 'thinking', 'answer', 或 'normal'
|
||||
isTyping,
|
||||
isComplete: true, // 默认完整,流式消息会设为false
|
||||
timestamp: Date.now()
|
||||
};
|
||||
|
||||
this.messages.push(newMsg);
|
||||
this.isTyping = false;
|
||||
// 如果是流式消息,标记为未完成
|
||||
if (type === 'thinking' || type === 'answer') {
|
||||
message.isComplete = false;
|
||||
}
|
||||
|
||||
this.messages.push(message);
|
||||
this.scrollToBottom();
|
||||
}, 2000);
|
||||
},
|
||||
// 发送消息
|
||||
sendMessage() {
|
||||
const content = this.inputMsg.trim();
|
||||
if (!content || !this.isConnected) return;
|
||||
|
||||
this.addMessage('me', content);
|
||||
|
||||
// 微信小程序中使用 socketTask.send
|
||||
this.socketTask.send({
|
||||
data: content,
|
||||
success: () => {
|
||||
console.log('消息发送成功');
|
||||
// 添加机器人正在输入的提示
|
||||
this.addMessage('bot', '思考中...', true);
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('消息发送失败', err);
|
||||
this.addMessage('bot', '消息发送失败,请重试');
|
||||
}
|
||||
});
|
||||
|
||||
this.inputMsg = '';
|
||||
},
|
||||
|
||||
scrollToBottom() {
|
||||
// 处理重连逻辑
|
||||
handleReconnect() {
|
||||
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
||||
console.log('已达到最大重连次数');
|
||||
this.addMessage('bot', '连接已断开,请刷新页面重试');
|
||||
return;
|
||||
}
|
||||
|
||||
this.reconnectAttempts++;
|
||||
console.log(`尝试重新连接,第${this.reconnectAttempts}次`);
|
||||
|
||||
setTimeout(() => {
|
||||
this.scrollTop = Math.floor(Math.random() * 1000) + 10000;
|
||||
}, 100);
|
||||
this.initWebSocket();
|
||||
}, this.reconnectDelay);
|
||||
},
|
||||
|
||||
// 关闭WebSocket连接
|
||||
closeWebSocket() {
|
||||
if (this.socketTask) {
|
||||
this.socketTask.close({
|
||||
success: () => {
|
||||
console.log('WebSocket已主动关闭');
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('关闭WebSocket失败', err);
|
||||
}
|
||||
});
|
||||
this.socketTask = null;
|
||||
}
|
||||
this.isConnected = false;
|
||||
},
|
||||
|
||||
// 滚动到底部
|
||||
scrollToBottom() {
|
||||
this.$nextTick(() => {
|
||||
this.scrollTop = 99999; // 足够大的值确保滚动到底部
|
||||
});
|
||||
},
|
||||
|
||||
onScroll(e) {
|
||||
// 可以在这里实现加载历史消息
|
||||
},
|
||||
|
||||
onInput() {
|
||||
// 模拟对方正在输入
|
||||
if (this.inputMsg.trim() && !this.isTyping) {
|
||||
this.isTyping = true;
|
||||
setTimeout(() => {
|
||||
this.isTyping = false;
|
||||
}, 2000);
|
||||
}
|
||||
},
|
||||
|
||||
getTime() {
|
||||
const now = new Date();
|
||||
return `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}`;
|
||||
},
|
||||
|
||||
goBack() {
|
||||
uni.navigateBack();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* 通用样式 */
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.chat-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@ -372,67 +332,16 @@
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
/* 顶部导航栏 */
|
||||
.chat-header {
|
||||
background: linear-gradient(135deg, #3498db, #1a5276);
|
||||
padding: 10px 15px;
|
||||
color: white;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.header-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.user-details {
|
||||
margin-left: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.user-status {
|
||||
font-size: 12px;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
/* 消息区域 */
|
||||
.chat-messages {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
/* padding: 15px; */
|
||||
background-color: rgb(229, 221, 213);
|
||||
/* background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" opacity="0.05"><path d="M20,20 C30,15 40,25 50,20 C60,15 70,25 80,20" stroke="black" fill="none"/></svg>'); */
|
||||
}
|
||||
|
||||
.message-time {
|
||||
text-align: center;
|
||||
color: #7f8c8d;
|
||||
font-size: 12px;
|
||||
margin: 10px 0;
|
||||
background-color: #e5ddd5;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.message {
|
||||
display: flex;
|
||||
margin-bottom: 15px;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.sent {
|
||||
@ -448,90 +357,56 @@
|
||||
height: 36px;
|
||||
border-radius: 50%;
|
||||
margin: 0 8px;
|
||||
/* align-self: flex-end; */
|
||||
}
|
||||
|
||||
.message-bubble {
|
||||
max-width: 70%;
|
||||
background: white;
|
||||
border-radius: 18px;
|
||||
padding: 10px 15px;
|
||||
position: relative;
|
||||
border-radius: 18px;
|
||||
background-color: white;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.me {
|
||||
background: #dcf8c6;
|
||||
border-bottom-right-radius: 4px;
|
||||
background-color: #dcf8c6;
|
||||
}
|
||||
|
||||
.message-text {
|
||||
font-size: 16px;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.message-status {
|
||||
font-size: 11px;
|
||||
color: #7f8c8d;
|
||||
text-align: right;
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
/* 输入区域 */
|
||||
.chat-input {
|
||||
background: #f0f0f0;
|
||||
padding: 10px 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
background-color: white;
|
||||
border-top: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.input-container {
|
||||
flex: 1;
|
||||
background: white;
|
||||
border-radius: 25px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 5px 15px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.input-field {
|
||||
flex: 1;
|
||||
border: none;
|
||||
outline: none;
|
||||
padding: 8px 0;
|
||||
font-size: 16px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.action-icon {
|
||||
margin: 0 5px;
|
||||
padding: 8px 15px;
|
||||
border-radius: 20px;
|
||||
background-color: #f0f0f0;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.send-btn {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
background-color: #2196f3;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: none;
|
||||
transition: all 0.2s;
|
||||
padding: 0 20px;
|
||||
border-radius: 20px;
|
||||
background-color: #07C160;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.send-btn.active {
|
||||
background-color: #3498db;
|
||||
transform: scale(1.05);
|
||||
.send-btn:disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* 对方正在输入提示 */
|
||||
.typing-indicator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 10px 0 20px 10px;
|
||||
opacity: 0.7;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.dot {
|
||||
@ -556,7 +431,7 @@
|
||||
}
|
||||
|
||||
.typing-text {
|
||||
font-size: 13px;
|
||||
font-size: 12px;
|
||||
margin-left: 8px;
|
||||
color: #555;
|
||||
}
|
||||
@ -573,4 +448,33 @@
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
}
|
||||
|
||||
/* 思考消息样式 */
|
||||
.message-bubble.thinking {
|
||||
background-color: #f0f0f0;
|
||||
border-left: 4px solid #999;
|
||||
font-style: italic;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* 回答消息样式 */
|
||||
.message-bubble.answer {
|
||||
background-color: #f8f8f8;
|
||||
border-left: 4px solid #07C160;
|
||||
}
|
||||
|
||||
/* 错误消息样式 */
|
||||
.message-bubble.error {
|
||||
background-color: #ffeeee;
|
||||
border-left: 4px solid #ff4d4f;
|
||||
color: #ff4d4f;
|
||||
}
|
||||
|
||||
/* 消息类型标签 */
|
||||
.message-type-tag {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
margin-bottom: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
@ -30,6 +30,7 @@
|
||||
import Footer from '@/components/footer_common.vue';
|
||||
import { get, post } from '@/utils/request';
|
||||
import { IMAGE_BASE_URL, BASE_URL } from '@/utils/config';
|
||||
import {navigateTo} from '@/utils/router.js'
|
||||
export default {
|
||||
name: 'aiHelper',
|
||||
components: { Footer },
|
||||
@ -45,7 +46,7 @@
|
||||
},
|
||||
methods: {
|
||||
goAiType(item) {
|
||||
uni.navigateTo({
|
||||
navigateTo({
|
||||
url: `/pages/chat/chatPage?serviceUrl=${item.serviceUrl}&apiKey=${item.apiKey}&id=${item.id}&name=${item.name}&icon=${item.icon}`
|
||||
});
|
||||
},
|
||||
|
||||
@ -89,7 +89,7 @@
|
||||
<view class="date-section">
|
||||
<view class="section-header">
|
||||
<u-icon name="pushpin" size="38" color="#2979ff"></u-icon>
|
||||
<text class="section-title">申请场次及人数</text>
|
||||
<text class="section-title">申请场次及人数(选填)</text>
|
||||
</view>
|
||||
|
||||
<view>
|
||||
@ -217,16 +217,14 @@
|
||||
concatPhone: '负责人电话',
|
||||
companyName: '公司名称',
|
||||
startTime: '开始时间',
|
||||
endTime: '结束时间',
|
||||
num: "人数",
|
||||
counter: '场次'
|
||||
endTime: '结束时间'
|
||||
},
|
||||
userName: '丁',
|
||||
userPhone: "13667879876",
|
||||
userCardId: '1234',
|
||||
userAddress: '北京二环',
|
||||
counter: '10',
|
||||
num: '100',
|
||||
userName: '',
|
||||
userPhone: "",
|
||||
userCardId: '',
|
||||
userAddress: '',
|
||||
counter: '',
|
||||
num: '',
|
||||
// 会议室图片数组
|
||||
applyTheme: 1,
|
||||
applyArea:1,
|
||||
@ -267,8 +265,8 @@
|
||||
maxDate: `${year + 1}-12-31`,
|
||||
|
||||
// 时间相关
|
||||
startTime: `2025-08`,
|
||||
endTime: `2025-10`,
|
||||
startTime: ``,
|
||||
endTime: ``,
|
||||
startTimeValue: `${startHours.toString().padStart(2, '0')}:${startMinutes.toString().padStart(2, '0')}`,
|
||||
endTimeValue: `${endHours.toString().padStart(2, '0')}:${endMinutes.toString().padStart(2, '0')}`,
|
||||
|
||||
|
||||
@ -600,14 +600,14 @@ import pickerColor from "./pickerColor.vue"
|
||||
display: flex;
|
||||
align-content: center;
|
||||
flex-direction:column;
|
||||
justify-content: center;
|
||||
/* justify-content: center; */
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.handWriting {
|
||||
background: #fff;
|
||||
width: 95vw;
|
||||
height: 90vh;
|
||||
height: 240px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@ -618,7 +618,7 @@ import pickerColor from "./pickerColor.vue"
|
||||
|
||||
.handCenter {
|
||||
border: 4rpx dashed #e9e9e9;
|
||||
flex: 5;
|
||||
height: 240px;
|
||||
overflow: hidden;
|
||||
width: 95vw;
|
||||
box-sizing: border-box;
|
||||
|
||||
61
utils/ws.js
Normal file
61
utils/ws.js
Normal file
@ -0,0 +1,61 @@
|
||||
// 连接WebSocket
|
||||
const connectWebSocket = () => {
|
||||
if (isConnected.value) return;
|
||||
|
||||
socket.value = new WebSocket(WS_URL);
|
||||
|
||||
socket.value.onopen = () => {
|
||||
isConnected.value = true;
|
||||
addMessage('ai', '连接已建立,请问有什么可以帮您?', MESSAGE_TYPE.NORMAL);
|
||||
};
|
||||
|
||||
socket.value.onmessage = (event) => {
|
||||
console.log('收到消息:', event);
|
||||
const data = event.data.trim();
|
||||
if (data.startsWith('<think>')) {
|
||||
aiStatus.value = 2; // 切换到思考状态
|
||||
isReplying.value = true;
|
||||
}
|
||||
if (data.includes('</think>')) {
|
||||
aiStatus.value = 1; // 切换到思考状态
|
||||
}
|
||||
if (data.startsWith('<answer>')) {
|
||||
addingText.value = '';
|
||||
isAnswer.value = true;
|
||||
}
|
||||
if (isAnswer.value) {
|
||||
addingText.value += data.replace(/<answer>/g, '').replace(/<\/answer>/g, '');
|
||||
console.log('添加的文本:', addingText.value);
|
||||
}
|
||||
if (!data.includes('你好,我是 AI 小助手,请输入你的问题。') && !data.includes('<videoList>')) {
|
||||
processThinkingMessage(data);
|
||||
}
|
||||
if (data.includes('<videoList>')) {
|
||||
const strList = data.split('<videoList>')[1].split('</videoList>')[0];
|
||||
// videoList.value = [{ src: '/videos/1.mp4' }, { src: '/videos/2.mp4' }, { src: '/videos/3.mp4' }];
|
||||
videoList.value = strList.replace(/\[/g, '').replace(/\]/g, '').split(',').map(src => ({ src: `/videos/${src.trim()}.mp4` }))
|
||||
}
|
||||
if (data.includes('<audio>')) {
|
||||
audioPath.value = data.split('<audio>')[1].split('</audio>')[0];
|
||||
}
|
||||
if (data.includes('</answer>')) {
|
||||
currentThinking.value = null
|
||||
isReplying.value = false;
|
||||
isAnswer.value = false;
|
||||
audioText.value = addingText.value;
|
||||
messages.value[messages.value.length - 1].loading = false;
|
||||
// audioText.value='先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以塞忠谏之路也'
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
socket.value.onerror = (error) => {
|
||||
console.error('WebSocket错误:', error);
|
||||
addMessage('ai', '连接出错,请稍后再试', MESSAGE_TYPE.NORMAL);
|
||||
};
|
||||
|
||||
socket.value.onclose = () => {
|
||||
isConnected.value = false;
|
||||
addMessage('ai', '连接已断开', MESSAGE_TYPE.NORMAL);
|
||||
};
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user