安装
npm install --save @microsoft/fetch-event-source
使用
import {fetchEventSource} from '@microsoft/fetch-event-source';
data() {
return {
question: '',
isLoading: false,
currentIndex: '',
thinkFlag: false,
ctrlAbout: null,
conversationId: null,
answering: false
}
},
methods: {
sendQuestion(question) {
if (this.isLoading || this.answering) return;
if (window.EventSource) {
this.question = '';
this.currentIndex = this.dataList.length;
this.isLoading = true;
this.dataList.push({
index: this.currentIndex,
type: 'q',
code: 200,
info: question
});
this.scrollTop();
let _that = this;
let ix = 0;
const params = {question: question, conversationId: this.conversationId};
this.ctrlAbout = new AbortController();
fetchEventSource(process.env.VUE_APP_BASE_API + '/ai/aiQuestion', {
method: 'POST',
headers: {
[tokenName]: 'Bearer ' + getToken(),
accept: 'application/json;charset=UTF-8,text/plain,*/*',
'Content-Type': 'application/json;charset=UTF-8', // 文本返回格式
},
body: JSON.stringify(params),
signal: _that.ctrlAbout.signal,
openWhenHidden: true, // 在浏览器标签页隐藏时保持与服务器的EventSource连接
onopen(resp) {
console.log('SSE连接成功...', resp)
_that.isLoading = false;
_that.thinkFlag = false;
},
onmessage(esm) {
let esmData = esm.data;
if (esmData && esmData !== '' && esmData !== 'event: ping') {
// console.log('esmData', esmData)
let respData = JSON.parse(esmData.substring(esm.data.indexOf(":") + 1, esmData.length));
if (respData.event && respData.event === 'message') {
if (ix < 1) {
_that.answering = true;
}
if (!_that.answering) return;
let respAnswer = respData.answer;
console.log('respAnswer', respAnswer)
if (respAnswer === '<think>') {
_that.thinkFlag = true;
respAnswer = '';
} else if (respAnswer === '</think>') {
_that.thinkFlag = false;
respAnswer = '';
}
let isExist = _that.dataList.some(d => d.type === 'a' && d.index === _that.currentIndex);
if (isExist) {
_that.dataList.forEach(item => {
if (item.type === 'a' && item.index === _that.currentIndex) {
if (item.info.state === 0) {
if (_that.thinkFlag) {
item.info.text.think += respAnswer;
} else {
item.info.text.answer += respAnswer;
}
} else {
if (_that.thinkFlag) {
item.info = {
state: 0,
text: {
think: respAnswer,
answer: ''
}
}
} else {
item.info = {
state: 0,
text: {
think: '',
answer: respAnswer
}
}
}
}
}
})
} else {
if (_that.thinkFlag) {
_that.dataList.push({
index: _that.currentIndex,
type: 'a',
code: 200,
info: {
state: 0,
showInfo: true,
text: {
think: respAnswer,
answer: ''
}
}
});
} else {
_that.dataList.push({
index: _that.currentIndex,
type: 'a',
code: 200,
info: {
state: 0,
showInfo: true,
text: {
think: '',
answer: respAnswer
}
}
});
}
}
} else {
_that.dataList.forEach(item => {
if (item.type === 'a' && item.index === _that.currentIndex && item.info.state === 0) {
item.info.state = 1;
}
})
_that.answering = false;
_that.ctrlAbout = null;
_that.getHistorySession();
}
}
_that.scrollTop();
},
onclose() {
this.ctrlAbout = null;
console.log('SSE已关闭...')
},
onerror(e) {
console.log('SSE出现错误...', e)
this.stopAnswer();
this.$message.error('系统错误,请联系管理员进行处理!');
throw e;
}
});
} else {
this.$message.warning('您的浏览器不支持SSE!');
}
},
eventSourceClose() {
if (this.ctrlAbout) {
this.ctrlAbout.abort();
this.ctrlAbout = null;
aiQuestionByClose(this.taskId).then(resp => {
console.info('SSE已中断...')
})
}
},
stopAnswer() {
this.answering = false;
this.eventSourceClose();
this.dataList.forEach(item => {
if (item.type === 'a' && item.index === this.currentIndex && item.info.state === 0) {
item.info.state = 2;
}
});
this.scrollTop();
this.getHistorySession();
},
scrollTop() {
this.$nextTick(() => {
if (this.$refs.kqaWindow.scrollHeight) {
this.$refs.kqaWindow.scrollTo({
top: this.$refs.kqaWindow.scrollHeight,
behavior: 'smooth'
});
}
});
},
eventSourceClose() {
if (this.ctrlAbout) {
this.ctrlAbout.abort();
this.ctrlAbout = null;
aiQuestionByClose(this.taskId).then(resp => {
console.info('SSE已中断...')
})
}
},
}
参考文档
https://juejin.cn/post/7518783423273664547