|
|
|
@ -4,7 +4,7 @@
|
|
|
|
<top @clickLeft="openDrawer"></top>
|
|
|
|
<top @clickLeft="openDrawer"></top>
|
|
|
|
<scroll-view class="content" :scroll-y="true" show-scrollbar="false" scroll-with-animation ref="scrollView">
|
|
|
|
<scroll-view class="content" :scroll-y="true" show-scrollbar="false" scroll-with-animation ref="scrollView">
|
|
|
|
<front @onSuggestionTap="onQuickAsk" />
|
|
|
|
<front @onSuggestionTap="onQuickAsk" />
|
|
|
|
<chat :messages="messages" />
|
|
|
|
<chat :messages="messages" @continueCreate="continueCreate" :isReplying="isReplying" @refresh="refresh" @changeShow="changeShow"/>
|
|
|
|
</scroll-view>
|
|
|
|
</scroll-view>
|
|
|
|
<view :style="{height: marginBottom + 'px',backgroundColor : '#fff'}" />
|
|
|
|
<view :style="{height: marginBottom + 'px',backgroundColor : '#fff'}" />
|
|
|
|
<leftDrawer :historyGroups="historyGroups" ref="popup" @changeShow="changeShow"
|
|
|
|
<leftDrawer :historyGroups="historyGroups" ref="popup" @changeShow="changeShow"
|
|
|
|
@ -26,9 +26,6 @@
|
|
|
|
import chat from "./chat/index.vue";
|
|
|
|
import chat from "./chat/index.vue";
|
|
|
|
import leftDrawer from "./leftDrawer/index.vue";
|
|
|
|
import leftDrawer from "./leftDrawer/index.vue";
|
|
|
|
import search from "./search/index.vue";
|
|
|
|
import search from "./search/index.vue";
|
|
|
|
import {
|
|
|
|
|
|
|
|
recognizeAudio
|
|
|
|
|
|
|
|
} from "@/utils/uploadVoice.js";
|
|
|
|
|
|
|
|
export default {
|
|
|
|
export default {
|
|
|
|
components: {
|
|
|
|
components: {
|
|
|
|
top,
|
|
|
|
top,
|
|
|
|
@ -47,12 +44,14 @@
|
|
|
|
show: false,
|
|
|
|
show: false,
|
|
|
|
marginBottom: 0,
|
|
|
|
marginBottom: 0,
|
|
|
|
isReplying: false,
|
|
|
|
isReplying: false,
|
|
|
|
breakReplying: false
|
|
|
|
breakReplying: false,
|
|
|
|
|
|
|
|
replyData: {},
|
|
|
|
|
|
|
|
isRefresh: false
|
|
|
|
};
|
|
|
|
};
|
|
|
|
},
|
|
|
|
},
|
|
|
|
mounted() {
|
|
|
|
mounted() {
|
|
|
|
this.loadChatHistory();
|
|
|
|
this.loadChatHistory();
|
|
|
|
this.scrollToBottom();
|
|
|
|
// this.scrollToBottom();
|
|
|
|
let self = this;
|
|
|
|
let self = this;
|
|
|
|
uni.onKeyboardHeightChange((res) => {
|
|
|
|
uni.onKeyboardHeightChange((res) => {
|
|
|
|
uni.pageScrollTo({
|
|
|
|
uni.pageScrollTo({
|
|
|
|
@ -73,6 +72,29 @@
|
|
|
|
this.typewriterTimers = {};
|
|
|
|
this.typewriterTimers = {};
|
|
|
|
},
|
|
|
|
},
|
|
|
|
methods: {
|
|
|
|
methods: {
|
|
|
|
|
|
|
|
// 重新回复
|
|
|
|
|
|
|
|
refresh() {
|
|
|
|
|
|
|
|
this.messages.splice(this.messages.length - 1, 1);
|
|
|
|
|
|
|
|
this.inputText = this.messages[this.messages.length - 1].content;
|
|
|
|
|
|
|
|
this.isRefresh = true;
|
|
|
|
|
|
|
|
this.onSend();
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
// 继续生成
|
|
|
|
|
|
|
|
continueCreate() {
|
|
|
|
|
|
|
|
this.breakReplying = false;
|
|
|
|
|
|
|
|
this.isReplying = true;
|
|
|
|
|
|
|
|
if (!this.isLoading) {
|
|
|
|
|
|
|
|
this.messages[this.messages.length - 1].isBreak = false;
|
|
|
|
|
|
|
|
this.replyAction(this.replyData, true)
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
this.messages.splice(this.messages.length - 1, 1);
|
|
|
|
|
|
|
|
this.messages.push({
|
|
|
|
|
|
|
|
id: this.loadingId,
|
|
|
|
|
|
|
|
role: "assistant",
|
|
|
|
|
|
|
|
loading: true,
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
// 中断回复
|
|
|
|
// 中断回复
|
|
|
|
handleBreak() {
|
|
|
|
handleBreak() {
|
|
|
|
if (this.isLoading) {
|
|
|
|
if (this.isLoading) {
|
|
|
|
@ -84,11 +106,12 @@
|
|
|
|
id: replyId,
|
|
|
|
id: replyId,
|
|
|
|
role: "assistant",
|
|
|
|
role: "assistant",
|
|
|
|
type: "text",
|
|
|
|
type: "text",
|
|
|
|
content: '已中断回复 ',
|
|
|
|
content: '',
|
|
|
|
displayText: "",
|
|
|
|
displayText: "已停止",
|
|
|
|
|
|
|
|
isBreak: true
|
|
|
|
});
|
|
|
|
});
|
|
|
|
this.isReplying = false;
|
|
|
|
this.isReplying = false;
|
|
|
|
this.isLoading = false;
|
|
|
|
this.scrollToBottom();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.breakReplying = true;
|
|
|
|
this.breakReplying = true;
|
|
|
|
},
|
|
|
|
},
|
|
|
|
@ -196,10 +219,11 @@
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// 发送消息
|
|
|
|
// 发送消息
|
|
|
|
async onSend(inputType = "text", inputContent = "", duration = undefined) {
|
|
|
|
async onSend(inputType = "text", inputContent = "", duration = undefined) {
|
|
|
|
if(this.isReplying) return;
|
|
|
|
if (this.isReplying) return;
|
|
|
|
const text = (this.inputText || "").trim();
|
|
|
|
const text = (this.inputText || "").trim();
|
|
|
|
if (!text || this.isLoading) return;
|
|
|
|
if (!text || this.isLoading) return;
|
|
|
|
this.baseId = Date.now();
|
|
|
|
this.baseId = Date.now();
|
|
|
|
|
|
|
|
if (!this.isRefresh) {
|
|
|
|
// 1. 用户消息
|
|
|
|
// 1. 用户消息
|
|
|
|
this.messages.push({
|
|
|
|
this.messages.push({
|
|
|
|
id: this.baseId,
|
|
|
|
id: this.baseId,
|
|
|
|
@ -210,7 +234,7 @@
|
|
|
|
inputContent,
|
|
|
|
inputContent,
|
|
|
|
duration,
|
|
|
|
duration,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
// 2. loading 消息
|
|
|
|
// 2. loading 消息
|
|
|
|
this.loadingId = this.baseId + 0.5;
|
|
|
|
this.loadingId = this.baseId + 0.5;
|
|
|
|
this.messages.push({
|
|
|
|
this.messages.push({
|
|
|
|
@ -219,19 +243,24 @@
|
|
|
|
loading: true,
|
|
|
|
loading: true,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
this.scrollToBottom();
|
|
|
|
this.scrollToBottom();
|
|
|
|
this.isReplying = true;
|
|
|
|
|
|
|
|
this.inputText = "";
|
|
|
|
this.inputText = "";
|
|
|
|
|
|
|
|
this.isReplying = true;
|
|
|
|
this.isLoading = true;
|
|
|
|
this.isLoading = true;
|
|
|
|
|
|
|
|
this.isRefresh = false;
|
|
|
|
this.addToHistory(text);
|
|
|
|
this.addToHistory(text);
|
|
|
|
// 3. 真正等待 AI 回复
|
|
|
|
// 3. 真正等待 AI 回复
|
|
|
|
const reply = await getAIResponse({
|
|
|
|
const reply = await getAIResponse({
|
|
|
|
message: text,
|
|
|
|
message: text,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
if(this.breakReplying) {
|
|
|
|
this.replyData = reply;
|
|
|
|
|
|
|
|
this.isLoading = false;
|
|
|
|
|
|
|
|
if (this.breakReplying) {
|
|
|
|
this.breakReplying = false;
|
|
|
|
this.breakReplying = false;
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.isLoading = false;
|
|
|
|
this.replyAction(reply)
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
replyAction(reply, isCreate) {
|
|
|
|
let content = ''
|
|
|
|
let content = ''
|
|
|
|
if (reply.errMsg) {
|
|
|
|
if (reply.errMsg) {
|
|
|
|
content = `请求出错: ${reply.errMsg}`
|
|
|
|
content = `请求出错: ${reply.errMsg}`
|
|
|
|
@ -243,6 +272,7 @@
|
|
|
|
if (loadingIdx > -1) this.messages.splice(loadingIdx, 1);
|
|
|
|
if (loadingIdx > -1) this.messages.splice(loadingIdx, 1);
|
|
|
|
// 5. 添加回复 + 打字机
|
|
|
|
// 5. 添加回复 + 打字机
|
|
|
|
const replyId = this.baseId + 1;
|
|
|
|
const replyId = this.baseId + 1;
|
|
|
|
|
|
|
|
if (!isCreate) {
|
|
|
|
this.messages.push({
|
|
|
|
this.messages.push({
|
|
|
|
id: replyId,
|
|
|
|
id: replyId,
|
|
|
|
role: "assistant",
|
|
|
|
role: "assistant",
|
|
|
|
@ -250,9 +280,10 @@
|
|
|
|
content,
|
|
|
|
content,
|
|
|
|
displayText: "",
|
|
|
|
displayText: "",
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.$nextTick(() => this.scrollToBottom());
|
|
|
|
this.$nextTick(() => this.scrollToBottom());
|
|
|
|
this.typewriter(replyId, content);
|
|
|
|
this.typewriter(replyId, content);
|
|
|
|
|
|
|
|
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// 打印机效果,并清除加载动画
|
|
|
|
// 打印机效果,并清除加载动画
|
|
|
|
typewriter(messageId, fullText) {
|
|
|
|
typewriter(messageId, fullText) {
|
|
|
|
@ -262,7 +293,7 @@
|
|
|
|
if (this.typewriterTimers[messageId]) {
|
|
|
|
if (this.typewriterTimers[messageId]) {
|
|
|
|
clearInterval(this.typewriterTimers[messageId]);
|
|
|
|
clearInterval(this.typewriterTimers[messageId]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let index = 0;
|
|
|
|
let index = msg.displayText.length;
|
|
|
|
msg.displayText = fullText.substring(0, index + 1);
|
|
|
|
msg.displayText = fullText.substring(0, index + 1);
|
|
|
|
index += 1;
|
|
|
|
index += 1;
|
|
|
|
const speed = 50; // 每个字符间隔50ms
|
|
|
|
const speed = 50; // 每个字符间隔50ms
|
|
|
|
@ -271,6 +302,7 @@
|
|
|
|
if (this.breakReplying) {
|
|
|
|
if (this.breakReplying) {
|
|
|
|
clearInterval(timer);
|
|
|
|
clearInterval(timer);
|
|
|
|
delete this.typewriterTimers[messageId];
|
|
|
|
delete this.typewriterTimers[messageId];
|
|
|
|
|
|
|
|
msg.isBreak = true;
|
|
|
|
this.isReplying = false;
|
|
|
|
this.isReplying = false;
|
|
|
|
this.breakReplying = false
|
|
|
|
this.breakReplying = false
|
|
|
|
this.isLoading = false;
|
|
|
|
this.isLoading = false;
|
|
|
|
@ -285,7 +317,10 @@
|
|
|
|
// 完成后使用完整文本
|
|
|
|
// 完成后使用完整文本
|
|
|
|
msg.displayText = fullText;
|
|
|
|
msg.displayText = fullText;
|
|
|
|
this.isReplying = false;
|
|
|
|
this.isReplying = false;
|
|
|
|
this.breakReplying = false
|
|
|
|
this.breakReplying = false;
|
|
|
|
|
|
|
|
this.$nextTick(() => {
|
|
|
|
|
|
|
|
this.scrollToBottom();
|
|
|
|
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, speed);
|
|
|
|
}, speed);
|
|
|
|
this.typewriterTimers[messageId] = timer;
|
|
|
|
this.typewriterTimers[messageId] = timer;
|
|
|
|
@ -337,8 +372,4 @@
|
|
|
|
box-sizing: border-box;
|
|
|
|
box-sizing: border-box;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
::v-deep .drawer-scroll {
|
|
|
|
|
|
|
|
padding-top: 44px;
|
|
|
|
|
|
|
|
} */
|
|
|
|
|
|
|
|
</style>
|
|
|
|
</style>
|