debug
This commit is contained in:
parent
f948dfc45f
commit
b07e3411e3
|
|
@ -1,8 +1,13 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useAuth } from './utils/useAuth.js'
|
import { useAuth } from './utils/useAuth.js'
|
||||||
import AppHeader from './components/AppHeader.vue'
|
import AppHeader from './components/AppHeader.vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
const { isLoggedIn } = useAuth()
|
const { isLoggedIn } = useAuth()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
// 设置全局 router 引用,供 api.js 响应拦截器使用
|
||||||
|
window.__VUE_ROUTER__ = router
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
||||||
|
|
@ -157,12 +157,11 @@ const regenerateIcon = `<svg viewBox="0 0 24 24" width="14" height="14" fill="no
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
padding: 8px 0 0;
|
margin-top: 8px;
|
||||||
|
padding-top: 10px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: var(--text-tertiary);
|
color: var(--text-tertiary);
|
||||||
border-top: 1px solid transparent;
|
border-top: 1px solid transparent;
|
||||||
margin-top: 8px;
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -194,8 +194,9 @@ function handleBack() {
|
||||||
<div v-if="sidebarTab === 'roomAgents' && room" class="sidebar-tab-content">
|
<div v-if="sidebarTab === 'roomAgents' && room" class="sidebar-tab-content">
|
||||||
<div class="sidebar-header sidebar-header-row">
|
<div class="sidebar-header sidebar-header-row">
|
||||||
<button class="btn-back" @click="handleBack">
|
<button class="btn-back" @click="handleBack">
|
||||||
<svg width="18" height="18">
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
<use href="#arrow-left-icon"/>
|
<line x1="19" y1="12" x2="5" y2="12"></line>
|
||||||
|
<polyline points="12 19 5 12 12 5"></polyline>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<span class="sidebar-title">{{ room.title }}</span>
|
<span class="sidebar-title">{{ room.title }}</span>
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,12 @@ api.interceptors.response.use(
|
||||||
if (error.response?.status === 401) {
|
if (error.response?.status === 401) {
|
||||||
localStorage.removeItem('access_token')
|
localStorage.removeItem('access_token')
|
||||||
localStorage.removeItem('user')
|
localStorage.removeItem('user')
|
||||||
window.location.href = '/auth'
|
// 使用 Vue Router 跳转,避免 SPA 路由丢失
|
||||||
|
if (window.__VUE_ROUTER__) {
|
||||||
|
window.__VUE_ROUTER__.push('/auth')
|
||||||
|
} else {
|
||||||
|
window.location.href = '/auth'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return Promise.reject(error.response?.data || error.message)
|
return Promise.reject(error.response?.data || error.message)
|
||||||
}
|
}
|
||||||
|
|
@ -106,6 +111,7 @@ export const chatRoomsAPI = {
|
||||||
delete: (id) => api.delete(`/chat-rooms/${id}`),
|
delete: (id) => api.delete(`/chat-rooms/${id}`),
|
||||||
getMessages: (id) => api.get(`/chat-rooms/${id}/messages`),
|
getMessages: (id) => api.get(`/chat-rooms/${id}/messages`),
|
||||||
start: (id) => `/api/chat-rooms/${id}/start`,
|
start: (id) => `/api/chat-rooms/${id}/start`,
|
||||||
|
// 注意: start 返回路径字符串,由调用方使用 fetch 处理 SSE 流
|
||||||
stop: (id) => api.post(`/chat-rooms/${id}/stop`),
|
stop: (id) => api.post(`/chat-rooms/${id}/stop`),
|
||||||
reset: (id) => api.post(`/chat-rooms/${id}/reset`),
|
reset: (id) => api.post(`/chat-rooms/${id}/reset`),
|
||||||
addAgent: (roomId, data) => api.post(`/chat-rooms/${roomId}/agents`, data),
|
addAgent: (roomId, data) => api.post(`/chat-rooms/${roomId}/agents`, data),
|
||||||
|
|
|
||||||
|
|
@ -108,24 +108,24 @@ class ParallelStreamManager {
|
||||||
break
|
break
|
||||||
|
|
||||||
case 'message_start':
|
case 'message_start':
|
||||||
store.startAgentStream(roomId, data.agent_id || data.agentId, data)
|
store.startAgentStream(roomId, data.agent_id, data)
|
||||||
break
|
break
|
||||||
|
|
||||||
case 'message_chunk':
|
case 'message_chunk':
|
||||||
store.updateAgentContent(roomId, data.agent_id || data.agentId, {
|
store.updateAgentContent(roomId, data.agent_id, {
|
||||||
content: data.content || '',
|
content: data.content || '',
|
||||||
progress: data.progress || 0
|
progress: data.progress || 0
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
|
|
||||||
case 'message_end':
|
case 'message_end':
|
||||||
store.completeAgentStream(roomId, data.agent_id || data.agentId, data)
|
store.completeAgentStream(roomId, data.agent_id, data)
|
||||||
break
|
break
|
||||||
|
|
||||||
case 'agent_error':
|
case 'agent_error':
|
||||||
store.errorAgentStream(roomId, data.agent_id || data.agentId, {
|
store.errorAgentStream(roomId, data.agent_id, {
|
||||||
message: data.error,
|
message: data.error,
|
||||||
agentName: data.agent_name || data.agentName
|
agentName: data.agent_name
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -102,8 +102,8 @@ export function useConversations() {
|
||||||
convMessages.value = res.data?.messages || []
|
convMessages.value = res.data?.messages || []
|
||||||
// 加载完成后强制滚动到底部(初始加载总是显示最新消息)
|
// 加载完成后强制滚动到底部(初始加载总是显示最新消息)
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (typeof onInitialScroll === 'function') {
|
if (typeof initialScrollCallback === 'function') {
|
||||||
onInitialScroll()
|
initialScrollCallback()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -278,10 +278,10 @@ export function useConversations() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始滚动回调(由外部设置)
|
// 初始滚动回调(由外部设置)
|
||||||
let onInitialScroll = null
|
let initialScrollCallback = null
|
||||||
|
|
||||||
const setOnInitialScroll = (callback) => {
|
const setOnInitialScroll = (callback) => {
|
||||||
onInitialScroll = callback
|
initialScrollCallback = callback
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -94,21 +94,23 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="chat-input-area">
|
<div class="chat-input-area">
|
||||||
<input
|
<div class="input-wrapper">
|
||||||
v-model="newMessage"
|
<textarea
|
||||||
@keyup.enter="handleSend"
|
v-model="newMessage"
|
||||||
type="text"
|
@keydown.enter.exact.prevent="handleSend"
|
||||||
placeholder="输入消息..."
|
placeholder="输入消息... (Shift+Enter 换行)"
|
||||||
class="chat-input"
|
class="chat-input"
|
||||||
:disabled="sending"
|
:disabled="sending"
|
||||||
/>
|
rows="3"
|
||||||
<button @click="handleSend" class="btn-send" :disabled="sending || !newMessage.trim()" title="发送">
|
></textarea>
|
||||||
<svg v-if="!sending" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
<button @click="handleSend" class="btn-send" :disabled="sending || !newMessage.trim()" title="发送">
|
||||||
<line x1="22" y1="2" x2="11" y2="13"></line>
|
<svg v-if="!sending" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
<polygon points="22 2 15 22 11 13 2 9 22 2"></polygon>
|
<line x1="22" y1="2" x2="11" y2="13"></line>
|
||||||
</svg>
|
<polygon points="22 2 15 22 11 13 2 9 22 2"></polygon>
|
||||||
<span v-else>...</span>
|
</svg>
|
||||||
</button>
|
<span v-else>...</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
@ -424,10 +426,51 @@ onUnmounted(() => {
|
||||||
.loading-messages .spinner-small { margin-bottom: 0.5rem; }
|
.loading-messages .spinner-small { margin-bottom: 0.5rem; }
|
||||||
|
|
||||||
/* 聊天输入区 */
|
/* 聊天输入区 */
|
||||||
.chat-input-area { padding: 1rem; border-top: 1px solid var(--border-light); display: flex; gap: 0.75rem; }
|
.chat-input-area { padding: 1rem; border-top: 1px solid var(--border-light); }
|
||||||
.chat-input { flex: 1; padding: 0.65rem 0.9rem; border: 1px solid var(--border-input); border-radius: 8px; background: var(--bg-input); color: var(--text-primary); font-size: 0.9rem; }
|
.input-wrapper {
|
||||||
.chat-input:focus { outline: none; border-color: var(--accent-primary); }
|
position: relative;
|
||||||
.btn-send { width: 40px; height: 40px; background: var(--accent-primary); color: white; border: none; border-radius: 8px; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.15s ease; }
|
display: flex;
|
||||||
|
background: var(--bg-input);
|
||||||
|
border: 1px solid var(--border-input);
|
||||||
|
border-radius: 10px;
|
||||||
|
transition: border-color 0.2s ease;
|
||||||
|
}
|
||||||
|
.input-wrapper:focus-within { border-color: var(--accent-primary); }
|
||||||
|
.chat-input {
|
||||||
|
flex: 1;
|
||||||
|
padding: 0.75rem 3rem 0.75rem 1rem;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-family: inherit;
|
||||||
|
resize: none;
|
||||||
|
min-height: 72px;
|
||||||
|
max-height: 200px;
|
||||||
|
overflow-y: auto;
|
||||||
|
line-height: 1.5;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-word;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.chat-input:focus { outline: none; }
|
||||||
|
.chat-input:disabled { opacity: 0.6; }
|
||||||
|
.btn-send {
|
||||||
|
position: absolute;
|
||||||
|
right: 8px;
|
||||||
|
bottom: 8px;
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
background: var(--accent-primary);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: all 0.15s ease;
|
||||||
|
}
|
||||||
.btn-send:hover:not(:disabled) { background: var(--accent-primary-hover); transform: translateY(-1px); box-shadow: 0 2px 8px rgba(37, 99, 235, 0.3); }
|
.btn-send:hover:not(:disabled) { background: var(--accent-primary-hover); transform: translateY(-1px); box-shadow: 0 2px 8px rgba(37, 99, 235, 0.3); }
|
||||||
.btn-send:disabled { opacity: 0.5; cursor: not-allowed; }
|
.btn-send:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue