|
|
@ -30,7 +30,7 @@ |
|
|
|
></i> |
|
|
|
<!-- 会话内容 --> |
|
|
|
<span> |
|
|
|
<span v-html="message.content"></span> |
|
|
|
<span v-html="convertStreamOutput(message.content)"></span> |
|
|
|
<!-- loading --> |
|
|
|
<span |
|
|
|
class="loading-dots" |
|
|
@ -61,6 +61,7 @@ |
|
|
|
import { onMounted, ref, watch } from 'vue' |
|
|
|
import axios from 'axios' |
|
|
|
import { v4 as uuidv4 } from 'uuid' |
|
|
|
import { marked } from 'marked' |
|
|
|
|
|
|
|
const messaggListRef = ref() |
|
|
|
const isSending = ref(false) |
|
|
@ -100,12 +101,11 @@ const sendRequest = (message) => { |
|
|
|
isTyping: false, |
|
|
|
isThinking: false, |
|
|
|
} |
|
|
|
//第一条默认发送的用户消息”你好“不放入会话列表 |
|
|
|
//第一条默认发送的用户消息"你好"不放入会话列表 |
|
|
|
if(messages.value.length > 0){ |
|
|
|
messages.value.push(userMsg) |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 添加机器人加载消息 |
|
|
|
const botMsg = { |
|
|
|
isUser: false, |
|
|
@ -127,7 +127,6 @@ const sendRequest = (message) => { |
|
|
|
const fullText = e.event.target.responseText // 累积的完整文本 |
|
|
|
let newText = fullText.substring(lastMsg.content.length) |
|
|
|
lastMsg.content += newText //增量更新 |
|
|
|
console.log(lastMsg) |
|
|
|
scrollToBottom() // 实时滚动 |
|
|
|
}, |
|
|
|
} |
|
|
@ -166,12 +165,12 @@ const uuidToNumber = (uuid) => { |
|
|
|
|
|
|
|
// 转换特殊字符 |
|
|
|
const convertStreamOutput = (output) => { |
|
|
|
return output |
|
|
|
.replace(/\n/g, '<br>') |
|
|
|
.replace(/\t/g, ' ') |
|
|
|
.replace(/&/g, '&') // 新增转义,避免 HTML 注入 |
|
|
|
.replace(/</g, '<') |
|
|
|
.replace(/>/g, '>') |
|
|
|
// 使用 marked 解析 Markdown |
|
|
|
return marked(output, { |
|
|
|
breaks: true, // 支持换行 |
|
|
|
gfm: true, // 支持 GitHub 风格的 Markdown |
|
|
|
sanitize: true // 防止 XSS 攻击 |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
const newChat = () => { |
|
|
@ -378,4 +377,70 @@ const newChat = () => { |
|
|
|
margin-top: 20px; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* Markdown 样式 */ |
|
|
|
:deep(p) { |
|
|
|
margin: 0.5em 0; |
|
|
|
} |
|
|
|
|
|
|
|
:deep(pre) { |
|
|
|
background-color: #f6f8fa; |
|
|
|
border-radius: 6px; |
|
|
|
padding: 16px; |
|
|
|
overflow: auto; |
|
|
|
margin: 0.5em 0; |
|
|
|
} |
|
|
|
|
|
|
|
:deep(code) { |
|
|
|
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace; |
|
|
|
background-color: rgba(175, 184, 193, 0.2); |
|
|
|
padding: 0.2em 0.4em; |
|
|
|
border-radius: 6px; |
|
|
|
font-size: 85%; |
|
|
|
} |
|
|
|
|
|
|
|
:deep(ul), :deep(ol) { |
|
|
|
padding-left: 2em; |
|
|
|
margin: 0.5em 0; |
|
|
|
} |
|
|
|
|
|
|
|
:deep(li) { |
|
|
|
margin: 0.25em 0; |
|
|
|
} |
|
|
|
|
|
|
|
:deep(blockquote) { |
|
|
|
margin: 0.5em 0; |
|
|
|
padding: 0 1em; |
|
|
|
color: #57606a; |
|
|
|
border-left: 0.25em solid #d0d7de; |
|
|
|
} |
|
|
|
|
|
|
|
:deep(table) { |
|
|
|
border-collapse: collapse; |
|
|
|
width: 100%; |
|
|
|
margin: 0.5em 0; |
|
|
|
} |
|
|
|
|
|
|
|
:deep(th), :deep(td) { |
|
|
|
border: 1px solid #d0d7de; |
|
|
|
padding: 6px 13px; |
|
|
|
} |
|
|
|
|
|
|
|
:deep(th) { |
|
|
|
background-color: #f6f8fa; |
|
|
|
} |
|
|
|
|
|
|
|
:deep(img) { |
|
|
|
max-width: 100%; |
|
|
|
height: auto; |
|
|
|
} |
|
|
|
|
|
|
|
:deep(a) { |
|
|
|
color: #0969da; |
|
|
|
text-decoration: none; |
|
|
|
} |
|
|
|
|
|
|
|
:deep(a:hover) { |
|
|
|
text-decoration: underline; |
|
|
|
} |
|
|
|
</style> |
|
|
|