mirror of
https://github.com/yangjian102621/geekai.git
synced 2026-02-19 21:04:27 +08:00
SSE 消息重构已完成
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
</div>
|
||||
|
||||
<div class="chat-item">
|
||||
<div v-if="files.length > 0" class="file-list-box">
|
||||
<div v-if="files && files.length > 0" class="file-list-box">
|
||||
<div v-for="file in files" :key="file.url">
|
||||
<div class="image" v-if="isImage(file.ext)">
|
||||
<el-image :src="file.url" fit="cover" />
|
||||
@@ -49,7 +49,7 @@
|
||||
</div>
|
||||
|
||||
<div class="chat-item">
|
||||
<div v-if="files.length > 0" class="file-list-box">
|
||||
<div v-if="files && files.length > 0" class="file-list-box">
|
||||
<div v-for="file in files" :key="file.url">
|
||||
<div class="image" v-if="isImage(file.ext)">
|
||||
<el-image :src="file.url" fit="cover" />
|
||||
@@ -90,9 +90,8 @@
|
||||
|
||||
<script setup>
|
||||
import { FormatFileSize, GetFileIcon, GetFileType } from '@/store/system'
|
||||
import { httpPost } from '@/utils/http'
|
||||
import { dateFormat, isImage, processPrompt } from '@/utils/libs'
|
||||
import { Clock, Edit } from '@element-plus/icons-vue'
|
||||
import { Clock } from '@element-plus/icons-vue'
|
||||
import hl from 'highlight.js'
|
||||
import MarkdownIt from 'markdown-it'
|
||||
import emoji from 'markdown-it-emoji'
|
||||
@@ -115,7 +114,7 @@ const md = new MarkdownIt({
|
||||
if (lang && hl.getLanguage(lang)) {
|
||||
const langHtml = `<span class="lang-name">${lang}</span>`
|
||||
// 处理代码高亮
|
||||
const preCode = hl.highlight(lang, str, true).value
|
||||
const preCode = hl.highlight(str, { language: lang, ignoreIllegals: true }).value
|
||||
// 将代码包裹在 pre 中
|
||||
return `<pre class="code-container"><code class="language-${lang} hljs">${preCode}</code>${copyBtn} ${langHtml}</pre>`
|
||||
}
|
||||
@@ -128,16 +127,19 @@ const md = new MarkdownIt({
|
||||
})
|
||||
md.use(mathjaxPlugin)
|
||||
md.use(emoji)
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: {
|
||||
content: '',
|
||||
content: {
|
||||
text: '',
|
||||
files: [],
|
||||
},
|
||||
created_at: '',
|
||||
tokens: 0,
|
||||
model: '',
|
||||
icon: '',
|
||||
files: [],
|
||||
},
|
||||
},
|
||||
listStyle: {
|
||||
@@ -146,8 +148,8 @@ const props = defineProps({
|
||||
},
|
||||
})
|
||||
const finalTokens = ref(props.data.tokens)
|
||||
const content = ref(processPrompt(props.data.content))
|
||||
const files = ref(props.data.files)
|
||||
const content = ref(processPrompt(props.data.content.text))
|
||||
const files = ref(props.data.content.files)
|
||||
|
||||
// 定义emit事件
|
||||
const emit = defineEmits(['edit'])
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
<div class="chat-item">
|
||||
<div
|
||||
class="content-wrapper"
|
||||
v-html="md.render(processContent(data.content))"
|
||||
v-if="data.content"
|
||||
v-html="md.render(processContent(data.content.text))"
|
||||
v-if="data.content.text"
|
||||
></div>
|
||||
<div class="content-wrapper flex justify-start items-center" v-else>
|
||||
<span class="mr-2">AI 思考中</span> <Thinking :duration="1.5" />
|
||||
@@ -48,18 +48,6 @@
|
||||
</el-tooltip>
|
||||
</span>
|
||||
</span>
|
||||
<!-- <span class="bar-item">-->
|
||||
<!-- <el-dropdown trigger="click">-->
|
||||
<!-- <span class="el-dropdown-link">-->
|
||||
<!-- <el-icon><More/></el-icon>-->
|
||||
<!-- </span>-->
|
||||
<!-- <template #dropdown>-->
|
||||
<!-- <el-dropdown-menu>-->
|
||||
<!-- <el-dropdown-item :icon="Headset" @click="synthesis(orgContent)">生成语音</el-dropdown-item>-->
|
||||
<!-- </el-dropdown-menu>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-dropdown>-->
|
||||
<!-- </span>-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -74,8 +62,8 @@
|
||||
<div class="content-wrapper">
|
||||
<div
|
||||
class="content"
|
||||
v-html="md.render(processContent(data.content))"
|
||||
v-if="data.content"
|
||||
v-html="md.render(processContent(data.content.text))"
|
||||
v-if="data.content.text"
|
||||
></div>
|
||||
<div class="content flex justify-start items-center" v-else>
|
||||
<span class="mr-2">AI 思考中</span> <Thinking :duration="1.5" />
|
||||
@@ -83,10 +71,9 @@
|
||||
</div>
|
||||
<div class="bar text-gray-500" v-if="data.created_at">
|
||||
<span class="bar-item text-sm"> {{ dateFormat(data.created_at) }}</span>
|
||||
<!-- <span class="bar-item">tokens: {{ data.tokens }}</span>-->
|
||||
<span class="bar-item bg">
|
||||
<el-tooltip class="box-item" effect="dark" content="复制回答" placement="bottom">
|
||||
<el-icon class="copy-reply" :data-clipboard-text="data.content">
|
||||
<el-icon class="copy-reply" :data-clipboard-text="data.content.text">
|
||||
<DocumentCopy />
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
@@ -106,7 +93,7 @@
|
||||
placement="bottom"
|
||||
v-if="!isPlaying"
|
||||
>
|
||||
<i class="iconfont icon-speaker" @click="synthesis(data.content)"></i>
|
||||
<i class="iconfont icon-speaker" @click="synthesis(data.content.text)"></i>
|
||||
</el-tooltip>
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
@@ -145,7 +132,10 @@ const props = defineProps({
|
||||
type: Object,
|
||||
default: {
|
||||
icon: '',
|
||||
content: '',
|
||||
content: {
|
||||
text: '',
|
||||
files: [],
|
||||
},
|
||||
created_at: '',
|
||||
tokens: 0,
|
||||
},
|
||||
|
||||
@@ -744,71 +744,15 @@ const initData = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 发送消息
|
||||
const sendMessage = async function () {
|
||||
if (!isLogin.value) {
|
||||
console.log('未登录')
|
||||
store.setShowLoginDialog(true)
|
||||
return
|
||||
}
|
||||
|
||||
if (canSend.value === false) {
|
||||
ElMessage.warning('AI 正在作答中,请稍后...')
|
||||
return
|
||||
}
|
||||
|
||||
if (prompt.value.trim().length === 0 || canSend.value === false) {
|
||||
showMessageError('请输入要发送的消息!')
|
||||
return false
|
||||
}
|
||||
|
||||
// 追加消息
|
||||
chatData.value.push({
|
||||
type: 'prompt',
|
||||
id: randString(32),
|
||||
icon: loginUser.value.avatar,
|
||||
content: prompt.value,
|
||||
model: getModelValue(modelID.value),
|
||||
created_at: new Date().getTime() / 1000,
|
||||
files: files.value,
|
||||
})
|
||||
|
||||
// 添加空回复消息
|
||||
const _role = getRoleById(roleId.value)
|
||||
chatData.value.push({
|
||||
chat_id: chatId,
|
||||
role_id: roleId.value,
|
||||
type: 'reply',
|
||||
id: randString(32),
|
||||
icon: _role['icon'],
|
||||
content: '',
|
||||
})
|
||||
|
||||
nextTick(() => {
|
||||
document
|
||||
.getElementById('chat-box')
|
||||
.scrollTo(0, document.getElementById('chat-box').scrollHeight)
|
||||
})
|
||||
|
||||
showHello.value = false
|
||||
disableInput(false)
|
||||
|
||||
// 发送 SSE 请求
|
||||
const sendSSERequest = async (message) => {
|
||||
try {
|
||||
await fetchEventSource('/api/chat/message', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: getUserToken(),
|
||||
},
|
||||
body: JSON.stringify({
|
||||
user_id: loginUser.value.id,
|
||||
role_id: roleId.value,
|
||||
model_id: modelID.value,
|
||||
chat_id: chatId.value,
|
||||
content: prompt.value,
|
||||
tools: toolSelected.value,
|
||||
stream: stream.value,
|
||||
files: files.value,
|
||||
}),
|
||||
body: JSON.stringify(message),
|
||||
openWhenHidden: true,
|
||||
onopen(response) {
|
||||
if (response.ok && response.status === 200) {
|
||||
@@ -849,6 +793,7 @@ const sendMessage = async function () {
|
||||
})
|
||||
.catch(() => {})
|
||||
isNewMsg.value = true
|
||||
tmpChatTitle.value = message.prompt
|
||||
return
|
||||
}
|
||||
|
||||
@@ -858,13 +803,13 @@ const sendMessage = async function () {
|
||||
lineBuffer.value = data.body
|
||||
const reply = chatData.value[chatData.value.length - 1]
|
||||
if (reply) {
|
||||
reply['content'] = lineBuffer.value
|
||||
reply['content'].text = lineBuffer.value
|
||||
}
|
||||
} else {
|
||||
lineBuffer.value += data.body
|
||||
const reply = chatData.value[chatData.value.length - 1]
|
||||
if (reply) {
|
||||
reply['content'] = lineBuffer.value
|
||||
reply['content'].text = lineBuffer.value
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -897,12 +842,77 @@ const sendMessage = async function () {
|
||||
enableInput()
|
||||
ElMessage.error('发送消息失败,请重试')
|
||||
}
|
||||
}
|
||||
|
||||
// 发送消息
|
||||
const sendMessage = () => {
|
||||
if (!isLogin.value) {
|
||||
console.log('未登录')
|
||||
store.setShowLoginDialog(true)
|
||||
return
|
||||
}
|
||||
|
||||
if (canSend.value === false) {
|
||||
ElMessage.warning('AI 正在作答中,请稍后...')
|
||||
return
|
||||
}
|
||||
|
||||
if (prompt.value.trim().length === 0 || canSend.value === false) {
|
||||
showMessageError('请输入要发送的消息!')
|
||||
return false
|
||||
}
|
||||
|
||||
// 追加消息
|
||||
chatData.value.push({
|
||||
type: 'prompt',
|
||||
id: randString(32),
|
||||
icon: loginUser.value.avatar,
|
||||
content: {
|
||||
text: prompt.value,
|
||||
files: files.value,
|
||||
},
|
||||
model: getModelValue(modelID.value),
|
||||
created_at: new Date().getTime() / 1000,
|
||||
})
|
||||
|
||||
// 添加空回复消息
|
||||
const _role = getRoleById(roleId.value)
|
||||
chatData.value.push({
|
||||
chat_id: chatId,
|
||||
role_id: roleId.value,
|
||||
type: 'reply',
|
||||
id: randString(32),
|
||||
icon: _role['icon'],
|
||||
content: {
|
||||
text: '',
|
||||
files: [],
|
||||
},
|
||||
})
|
||||
|
||||
nextTick(() => {
|
||||
document
|
||||
.getElementById('chat-box')
|
||||
.scrollTo(0, document.getElementById('chat-box').scrollHeight)
|
||||
})
|
||||
|
||||
showHello.value = false
|
||||
disableInput(false)
|
||||
|
||||
// 异步发送 SSE 请求
|
||||
sendSSERequest({
|
||||
user_id: loginUser.value.id,
|
||||
role_id: roleId.value,
|
||||
model_id: modelID.value,
|
||||
chat_id: chatId.value,
|
||||
prompt: prompt.value,
|
||||
tools: toolSelected.value,
|
||||
stream: stream.value,
|
||||
files: files.value,
|
||||
})
|
||||
|
||||
tmpChatTitle.value = prompt.value
|
||||
prompt.value = ''
|
||||
files.value = []
|
||||
row.value = 1
|
||||
return true
|
||||
}
|
||||
|
||||
const getRoleById = function (rid) {
|
||||
@@ -1139,7 +1149,10 @@ const loadChatHistory = function (chatId) {
|
||||
type: 'reply',
|
||||
id: randString(32),
|
||||
icon: _role['icon'],
|
||||
content: _role['hello_msg'],
|
||||
content: {
|
||||
text: _role['hello_msg'],
|
||||
files: [],
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user