|
|
|
@ -2,8 +2,8 @@
|
|
|
|
<view class="ai-page">
|
|
|
|
<view class="ai-page">
|
|
|
|
<page-meta :page-style="'overflow:' + (show ? 'hidden' : 'visible')"></page-meta>
|
|
|
|
<page-meta :page-style="'overflow:' + (show ? 'hidden' : 'visible')"></page-meta>
|
|
|
|
<top @clickLeft="openDrawer" @resetMessage="resetMessage"></top>
|
|
|
|
<top @clickLeft="openDrawer" @resetMessage="resetMessage"></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>
|
|
|
|
<front @onSuggestionTap="onQuickAsk" />
|
|
|
|
<front @onSuggestionTap="onQuickAsk" ref="front" />
|
|
|
|
<chat :messages="messages" @continueCreate="continueCreate" :isReplying="isReplying" @refresh="refresh"
|
|
|
|
<chat :messages="messages" @continueCreate="continueCreate" :isReplying="isReplying" @refresh="refresh"
|
|
|
|
@changeShow="changeShow" @changeInputText="changeInputText" @handleVoice="handleVoice"
|
|
|
|
@changeShow="changeShow" @changeInputText="changeInputText" @handleVoice="handleVoice"
|
|
|
|
:isPlayingVoice="isPlayingVoice" :playSrc="playSrc" />
|
|
|
|
:isPlayingVoice="isPlayingVoice" :playSrc="playSrc" />
|
|
|
|
@ -15,8 +15,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
<search ref="searchRef" :inputText="inputText" @onSend="onSend" @onQuickAsk="onQuickAsk"
|
|
|
|
<search ref="searchRef" :inputText="inputText" @onSend="onSend" @onQuickAsk="onQuickAsk"
|
|
|
|
@changeInputText="changeInputText" :isReplying="isReplying" @handleBreak="handleBreak"
|
|
|
|
@changeInputText="changeInputText" :isReplying="isReplying" @handleBreak="handleBreak"
|
|
|
|
@changeShow="changeShow" @startRecord="startRecord"/>
|
|
|
|
@changeShow="changeShow" @startRecord="startRecord" />
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- <view class="ai-logo" v-if="isShowRightLogo">
|
|
|
|
|
|
|
|
<image src="../../static/ai.webp" mode="widthFix" style="width: 100%;"></image>
|
|
|
|
|
|
|
|
</view> -->
|
|
|
|
</view>
|
|
|
|
</view>
|
|
|
|
</template>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
@ -61,9 +64,24 @@
|
|
|
|
playSrc: '',
|
|
|
|
playSrc: '',
|
|
|
|
breakRequestList: [],
|
|
|
|
breakRequestList: [],
|
|
|
|
speechIdList: [],
|
|
|
|
speechIdList: [],
|
|
|
|
textToVoiceLoading: false
|
|
|
|
textToVoiceLoading: false,
|
|
|
|
|
|
|
|
isGetAiLogoMsg: false,
|
|
|
|
|
|
|
|
aiLogoRect: null,
|
|
|
|
|
|
|
|
isShowRightLogo: false
|
|
|
|
};
|
|
|
|
};
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
// onPageScroll(e) {
|
|
|
|
|
|
|
|
// if (!this.getAiLogoMsg) {
|
|
|
|
|
|
|
|
// this.aiLogoRect = this.$refs.front.getAiLogoMsg();
|
|
|
|
|
|
|
|
// this.getAiLogoMsg = true
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// if (!this.aiLogoRect) return;
|
|
|
|
|
|
|
|
// if (e.scrollTop > (this.aiLogoRect.top + 12)) {
|
|
|
|
|
|
|
|
// this.isShowRightLogo = true;
|
|
|
|
|
|
|
|
// } else {
|
|
|
|
|
|
|
|
// this.isShowRightLogo = false;
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// },
|
|
|
|
async mounted() {
|
|
|
|
async mounted() {
|
|
|
|
this.loadChatHistory();
|
|
|
|
this.loadChatHistory();
|
|
|
|
uni.onKeyboardHeightChange((res) => {
|
|
|
|
uni.onKeyboardHeightChange((res) => {
|
|
|
|
@ -75,10 +93,10 @@
|
|
|
|
});
|
|
|
|
});
|
|
|
|
// #ifdef APP-PLUS
|
|
|
|
// #ifdef APP-PLUS
|
|
|
|
this.$nextTick(() => {
|
|
|
|
this.$nextTick(() => {
|
|
|
|
this.marginBottom = this.$refs.searchRef.getHeight() || 103;
|
|
|
|
this.marginBottom = this.$refs.searchRef.getHeight() || 112;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
// #endif
|
|
|
|
// #endif
|
|
|
|
this.marginBottom = 103;
|
|
|
|
this.marginBottom = 112;
|
|
|
|
|
|
|
|
|
|
|
|
this.initAudio();
|
|
|
|
this.initAudio();
|
|
|
|
},
|
|
|
|
},
|
|
|
|
@ -87,14 +105,15 @@
|
|
|
|
},
|
|
|
|
},
|
|
|
|
methods: {
|
|
|
|
methods: {
|
|
|
|
// 开始录音后暂停播放语音
|
|
|
|
// 开始录音后暂停播放语音
|
|
|
|
startRecord(){
|
|
|
|
startRecord() {
|
|
|
|
if(this.isPlayingVoice && this.audioContext.src){
|
|
|
|
if (this.isPlayingVoice && this.audioContext.src) {
|
|
|
|
this.audioContext.stop();
|
|
|
|
this.audioContext.stop();
|
|
|
|
this.isPlayingVoice = false;
|
|
|
|
this.isPlayingVoice = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// 点击语音播放暂停
|
|
|
|
// 点击语音播放暂停
|
|
|
|
handleVoice(src) {
|
|
|
|
handleVoice(src) {
|
|
|
|
|
|
|
|
if (!src) return;
|
|
|
|
if (this.audioContext.src === src && this.isPlayingVoice) {
|
|
|
|
if (this.audioContext.src === src && this.isPlayingVoice) {
|
|
|
|
this.isPlayingVoice = false;
|
|
|
|
this.isPlayingVoice = false;
|
|
|
|
this.audioContext.stop();
|
|
|
|
this.audioContext.stop();
|
|
|
|
@ -125,9 +144,9 @@
|
|
|
|
});
|
|
|
|
});
|
|
|
|
this.audioContext.onEnded(res => {
|
|
|
|
this.audioContext.onEnded(res => {
|
|
|
|
this.isPlayingVoice = false
|
|
|
|
this.isPlayingVoice = false
|
|
|
|
// const platform = uni.getSystemInfoSync().uniPlatform;
|
|
|
|
const platform = uni.getSystemInfoSync().uniPlatform;
|
|
|
|
// if(platform === 'web') return;
|
|
|
|
if(platform === 'web') return;
|
|
|
|
// removeFile(this.audioContext.src)
|
|
|
|
removeFile(this.audioContext.src)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// 处理百度返回arraybuff格式的语音
|
|
|
|
// 处理百度返回arraybuff格式的语音
|
|
|
|
@ -157,7 +176,7 @@
|
|
|
|
const base64WithPrefix = `data:audio/mp3;base64,${base64Audio}`;
|
|
|
|
const base64WithPrefix = `data:audio/mp3;base64,${base64Audio}`;
|
|
|
|
const fileName = `_doc/${Date.now()}_numberPerson.mp3`;
|
|
|
|
const fileName = `_doc/${Date.now()}_numberPerson.mp3`;
|
|
|
|
base64ToFile(base64WithPrefix, fileName, (path) => {
|
|
|
|
base64ToFile(base64WithPrefix, fileName, (path) => {
|
|
|
|
this.playSrc = path;
|
|
|
|
self.playSrc = path;
|
|
|
|
self.textToVoiceLoading = false;
|
|
|
|
self.textToVoiceLoading = false;
|
|
|
|
self.audioContext.src = path;
|
|
|
|
self.audioContext.src = path;
|
|
|
|
let endTime2 = Date.now();
|
|
|
|
let endTime2 = Date.now();
|
|
|
|
@ -196,6 +215,7 @@
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// 中断回复
|
|
|
|
// 中断回复
|
|
|
|
handleBreak() {
|
|
|
|
handleBreak() {
|
|
|
|
|
|
|
|
console.log('handleBreak');
|
|
|
|
if (this.isLoading) {
|
|
|
|
if (this.isLoading) {
|
|
|
|
const loadingIdx = this.messages.findIndex((m) => m.id === this.loadingId);
|
|
|
|
const loadingIdx = this.messages.findIndex((m) => m.id === this.loadingId);
|
|
|
|
if (loadingIdx > -1) this.messages.splice(loadingIdx, 1);
|
|
|
|
if (loadingIdx > -1) this.messages.splice(loadingIdx, 1);
|
|
|
|
@ -398,11 +418,22 @@
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.replyAction(reply)
|
|
|
|
this.replyAction(reply)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
async replyAction(reply, isCreate) {
|
|
|
|
async replyAction(reply, isContinue) {
|
|
|
|
if (!this.messages[this.messages.length - 1].src) {
|
|
|
|
let content = ''
|
|
|
|
|
|
|
|
// 判断请求是否出错
|
|
|
|
|
|
|
|
if (reply.errMsg) {
|
|
|
|
|
|
|
|
content = `请求出错: ${reply.errMsg}`
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
content = reply;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
let result
|
|
|
|
|
|
|
|
if (this.$store.state.set.replyPattern === '1' && !this.messages[this.messages.length - 1].src && !
|
|
|
|
|
|
|
|
reply.errMsg) {
|
|
|
|
let speechId = Date.now();
|
|
|
|
let speechId = Date.now();
|
|
|
|
this.speechId = speechId;
|
|
|
|
this.speechId = speechId;
|
|
|
|
const result = await this.getSpeech(reply);
|
|
|
|
result = await this.getSpeech(reply);
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
if (result) {
|
|
|
|
let speechIndex = this.speechIdList.findIndex(item => item.speechId === speechId)
|
|
|
|
let speechIndex = this.speechIdList.findIndex(item => item.speechId === speechId)
|
|
|
|
if (speechIndex > -1) {
|
|
|
|
if (speechIndex > -1) {
|
|
|
|
this.speechIdList = this.speechIdList.splice(speechIndex, -1)
|
|
|
|
this.speechIdList = this.speechIdList.splice(speechIndex, -1)
|
|
|
|
@ -412,28 +443,26 @@
|
|
|
|
this.isPlayingVoice = true;
|
|
|
|
this.isPlayingVoice = true;
|
|
|
|
this.audioContext.play();
|
|
|
|
this.audioContext.play();
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}
|
|
|
|
let content = ''
|
|
|
|
|
|
|
|
if (reply.errMsg) {
|
|
|
|
|
|
|
|
content = `请求出错: ${reply.errMsg}`
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
content = reply
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
// 4. 移除 loading
|
|
|
|
// 4. 移除 loading
|
|
|
|
const loadingIdx = this.messages.findIndex((m) => m.id === this.loadingId);
|
|
|
|
const loadingIdx = this.messages.findIndex((m) => m.id === this.loadingId);
|
|
|
|
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) {
|
|
|
|
if (!isContinue) {
|
|
|
|
this.messages.push({
|
|
|
|
this.messages.push({
|
|
|
|
id: replyId,
|
|
|
|
id: replyId,
|
|
|
|
role: "assistant",
|
|
|
|
role: "assistant",
|
|
|
|
type: "text",
|
|
|
|
type: "text",
|
|
|
|
content,
|
|
|
|
content,
|
|
|
|
displayText: "",
|
|
|
|
displayText: "",
|
|
|
|
src: JSON.parse(JSON.stringify(this.audioContext.src)),
|
|
|
|
src: (this.audioContext.src && this.$store.state.set.replyPattern === '1') ? JSON
|
|
|
|
|
|
|
|
.parse(JSON.stringify(this.audioContext.src)) : null,
|
|
|
|
duration: null
|
|
|
|
duration: null
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
this.audioContext.play();
|
|
|
|
|
|
|
|
this.isPlayingVoice = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.$nextTick(() => this.scrollToBottom());
|
|
|
|
this.$nextTick(() => this.scrollToBottom());
|
|
|
|
this.typewriter(replyId, content);
|
|
|
|
this.typewriter(replyId, content);
|
|
|
|
@ -517,14 +546,22 @@
|
|
|
|
.ai-page {
|
|
|
|
.ai-page {
|
|
|
|
display: flex;
|
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
flex-direction: column;
|
|
|
|
background: #f7f8fc;
|
|
|
|
background: #fff;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.content {
|
|
|
|
.content {
|
|
|
|
flex: 1;
|
|
|
|
flex: 1;
|
|
|
|
padding: 16px 12px 0px 12px;
|
|
|
|
padding: 16px 12px 0px 12px;
|
|
|
|
background-color: #f7f8fc;
|
|
|
|
background-color: #fff;
|
|
|
|
width: 100%;
|
|
|
|
width: 100%;
|
|
|
|
box-sizing: border-box;
|
|
|
|
box-sizing: border-box;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.ai-logo {
|
|
|
|
|
|
|
|
width: 60px;
|
|
|
|
|
|
|
|
position: fixed;
|
|
|
|
|
|
|
|
top: 50%;
|
|
|
|
|
|
|
|
right: 10px;
|
|
|
|
|
|
|
|
z-index: 999999999;
|
|
|
|
|
|
|
|
}
|
|
|
|
</style>
|
|
|
|
</style>
|