This commit is contained in:
aaron 2025-05-10 22:55:20 +08:00
parent 75286acc9a
commit 318f7eb08f
2 changed files with 100 additions and 97 deletions

View File

@ -5,7 +5,7 @@ services:
build: build:
context: . context: .
dockerfile: Dockerfile dockerfile: Dockerfile
image: tradus-web:1.0.27 image: tradus-web:1.1
container_name: tradus-web container_name: tradus-web
ports: ports:
- '6000:80' - '6000:80'

View File

@ -6,11 +6,13 @@ import { marked } from 'marked'
// marked // marked
marked.setOptions({ marked.setOptions({
async: false, async: false,
gfm: true,
breaks: true,
}) })
// Markdown // Markdown
const renderMarkdown = (content: string) => { const renderMarkdown = (content: string) => {
return marked.parse(content) return `<div class="markdown-body">${marked.parse(content)}</div>`
} }
// Agent // Agent
@ -111,6 +113,7 @@ const userInput = ref('')
const chatHistory = ref<ChatMessage[]>([]) const chatHistory = ref<ChatMessage[]>([])
const isLoading = ref(false) const isLoading = ref(false)
const messagesContainer = ref<HTMLElement | null>(null) const messagesContainer = ref<HTMLElement | null>(null)
const currentResponseText = ref('')
// VIP // VIP
onMounted(() => { onMounted(() => {
@ -154,6 +157,9 @@ const sendMessage = async () => {
if (!userInput.value.trim() || isLoading.value || !isAuthenticated.value || !selectedAgent.value) if (!userInput.value.trim() || isLoading.value || !isAuthenticated.value || !selectedAgent.value)
return return
//
currentResponseText.value = ''
// //
chatHistory.value.push({ chatHistory.value.push({
role: 'user', role: 'user',
@ -210,7 +216,6 @@ const sendMessage = async () => {
} }
const decoder = new TextDecoder() const decoder = new TextDecoder()
let responseText = ''
// //
while (true) { while (true) {
@ -227,43 +232,20 @@ const sendMessage = async () => {
const data = JSON.parse(line.slice(6)) const data = JSON.parse(line.slice(6))
switch (data.event) { switch (data.event) {
case 'agent_thought': case 'agent_message':
// 使 if (data.answer) {
if (data.tool) { currentResponseText.value += data.answer
if (!chatHistory.value[tempMessageIndex].tools) { chatHistory.value[tempMessageIndex].content = currentResponseText.value
chatHistory.value[tempMessageIndex].tools = []
}
chatHistory.value[tempMessageIndex].tools?.push(data.tool)
//
let toolsText = ''
chatHistory.value[tempMessageIndex].tools?.forEach((tool, index) => {
toolsText += `${index + 1}.调用 ${tool} 工具\n`
})
if (responseText) {
chatHistory.value[tempMessageIndex].content = toolsText + '\n' + responseText
} else {
chatHistory.value[tempMessageIndex].content = toolsText
}
await scrollToBottom() await scrollToBottom()
} }
break break
case 'agent_message': case 'agent_thought':
if (data.answer) { if (data.tool) {
responseText += data.answer const thoughtText = `AI正在调用 ${data.tool} 工具来回答问题`
chatHistory.value[tempMessageIndex].content = currentResponseText.value
// ? `${thoughtText}\n\n${currentResponseText.value}`
let toolsText = '' : thoughtText
if (chatHistory.value[tempMessageIndex].tools?.length) {
chatHistory.value[tempMessageIndex].tools?.forEach((tool, index) => {
toolsText += `${index + 1}.调用 ${tool} 工具\n`
})
chatHistory.value[tempMessageIndex].content = toolsText + '\n' + responseText
} else {
chatHistory.value[tempMessageIndex].content = responseText
}
await scrollToBottom() await scrollToBottom()
} }
break break
@ -357,9 +339,18 @@ const getIconPath = (agent: Agent) => {
<div class="chat-window"> <div class="chat-window">
<div class="messages-container" ref="messagesContainer"> <div class="messages-container" ref="messagesContainer">
<div v-for="(message, index) in chatHistory" :key="index" class="message-wrapper"> <div v-for="(message, index) in chatHistory" :key="index" class="message-wrapper">
<div :class="['message', message.role === 'user' ? 'user-message' : 'ai-message']"> <div
:class="[
'message',
message.role === 'user'
? 'user-message'
: message.role === 'thought'
? 'thought-message'
: 'ai-message',
]"
>
<div class="message-content"> <div class="message-content">
<div class="message-text" v-html="renderMarkdown(message.content)"></div> <div v-html="renderMarkdown(message.content)"></div>
</div> </div>
</div> </div>
</div> </div>
@ -586,7 +577,7 @@ const getIconPath = (agent: Agent) => {
} }
.message-wrapper { .message-wrapper {
margin: 0.5rem 0; margin: 0.75rem 0;
display: flex; display: flex;
width: 100%; width: 100%;
} }
@ -597,7 +588,8 @@ const getIconPath = (agent: Agent) => {
display: flex; display: flex;
} }
.ai-message { .ai-message,
.thought-message {
justify-content: flex-start; justify-content: flex-start;
width: 100%; width: 100%;
display: flex; display: flex;
@ -612,8 +604,6 @@ const getIconPath = (agent: Agent) => {
display: inline-block; display: inline-block;
max-width: 800px; max-width: 800px;
min-width: 4em; min-width: 4em;
padding: 1.5rem;
border-radius: 0.75rem;
word-break: break-word; word-break: break-word;
overflow-wrap: break-word; overflow-wrap: break-word;
white-space: pre-wrap; white-space: pre-wrap;
@ -627,49 +617,50 @@ const getIconPath = (agent: Agent) => {
background-color: var(--color-accent); background-color: var(--color-accent);
color: white; color: white;
border-radius: 1rem 1rem 0 1rem; border-radius: 1rem 1rem 0 1rem;
padding: 1.25rem;
} }
.ai-message .message-content { .ai-message .message-content {
background-color: var(--color-bg-secondary); background-color: var(--color-bg-secondary);
color: var(--color-text-primary); color: var(--color-text-primary);
border-radius: 1rem 1rem 1rem 0; border-radius: 1rem 1rem 1rem 0;
border: 1px solid var(--color-border);
padding: 1.25rem;
margin-top: 0.5rem;
} }
.message-text { .thought-message .message-content {
font-size: 1rem; background-color: rgba(var(--color-accent-rgb), 0.08);
line-height: 1.4; color: var(--color-text-secondary);
white-space: pre-wrap; border-radius: 0.75rem;
word-break: break-word; font-style: italic;
padding: 0.75rem 1rem;
border: 1px solid rgba(var(--color-accent-rgb), 0.15);
font-size: 0.95rem;
max-width: 600px;
} }
.message-text :deep(p) { /* 调整思考消息的间距 */
margin: 0.2em 0; .thought-message + .ai-message {
line-height: 1.4; margin-top: 0.25rem;
display: inline-block;
} }
.message-text :deep(p:last-child) { /* 确保思考消息和AI回复之间的视觉关联 */
margin-bottom: 0; .thought-message {
margin-bottom: 0.25rem;
} }
.message-text :deep(p:first-child) { /* 移动端适配 */
margin-top: 0; @media (max-width: 768px) {
.message-content {
max-width: 90%;
} }
.message-text :deep(ul), .thought-message .message-content {
.message-text :deep(ol) { max-width: 85%;
margin: 0.2em 0; font-size: 0.9rem;
padding-left: 1.2em; padding: 0.6rem 0.8rem;
} }
.message-text :deep(li) {
margin: 0;
line-height: 1.4;
}
.message-text :deep(li > p) {
display: inline;
margin: 0;
} }
.input-container { .input-container {
@ -761,7 +752,7 @@ const getIconPath = (agent: Agent) => {
} }
:deep(.markdown-body p) { :deep(.markdown-body p) {
margin: 0.2em 0; margin: 0;
line-height: 1.4; line-height: 1.4;
display: inline-block; display: inline-block;
} }
@ -799,36 +790,20 @@ const getIconPath = (agent: Agent) => {
border-left: 4px solid var(--color-accent); border-left: 4px solid var(--color-accent);
} }
:deep(.markdown-body table) { :deep(.markdown-body ul),
border-collapse: collapse; :deep(.markdown-body ol) {
width: 100%; margin: 0.2em 0;
margin: 0.5rem 0; padding-left: 1.2em;
} }
:deep(.markdown-body th), :deep(.markdown-body li) {
:deep(.markdown-body td) { margin: 0;
border: 1px solid var(--color-border); line-height: 1.4;
padding: 0.5rem;
text-align: left;
} }
:deep(.markdown-body th) { :deep(.markdown-body li > p) {
background-color: var(--color-bg-secondary); display: inline;
} margin: 0;
:deep(.markdown-body img) {
max-width: 100%;
height: auto;
border-radius: 0.5rem;
}
:deep(.markdown-body a) {
color: var(--color-accent);
text-decoration: none;
}
:deep(.markdown-body a:hover) {
text-decoration: underline;
} }
/* 修改 Agent 选择器样式 */ /* 修改 Agent 选择器样式 */
@ -1020,4 +995,32 @@ const getIconPath = (agent: Agent) => {
word-break: break-word; word-break: break-word;
overflow-wrap: break-word; overflow-wrap: break-word;
} }
.user-message .markdown-body {
color: white;
}
.user-message :deep(.markdown-body) {
color: white;
}
.user-message :deep(.markdown-body pre) {
background-color: rgba(255, 255, 255, 0.1);
border-color: rgba(255, 255, 255, 0.2);
}
.user-message :deep(.markdown-body blockquote) {
background-color: rgba(255, 255, 255, 0.1);
border-left-color: rgba(255, 255, 255, 0.3);
}
.user-message :deep(.markdown-body a) {
color: white;
text-decoration: underline;
}
.user-message :deep(.markdown-body code) {
color: white;
background-color: rgba(255, 255, 255, 0.1);
}
</style> </style>