bbe8fcd776
- 将前端项目目录从 web-flowith 重命名为 web,使目录结构更简洁 - 保持所有前端代码和配置文件不变 - 统一项目目录命名规范
334 lines
7.9 KiB
Vue
334 lines
7.9 KiB
Vue
<template>
|
|
<div class="websocket-test">
|
|
<div class="test-container">
|
|
<h1>WebSocket连接测试</h1>
|
|
|
|
<!-- 连接状态 -->
|
|
<div class="status-section">
|
|
<h3>连接状态</h3>
|
|
<div class="status-info">
|
|
<span class="status-label">状态:</span>
|
|
<span
|
|
class="status-value"
|
|
:class="{
|
|
'connected': chatStore.wsConnected,
|
|
'connecting': chatStore.connectionStatus === 'CONNECTING',
|
|
'disconnected': !chatStore.wsConnected
|
|
}"
|
|
>
|
|
{{ getConnectionStatusText() }}
|
|
</span>
|
|
</div>
|
|
<div class="status-actions">
|
|
<a-button
|
|
type="primary"
|
|
@click="chatStore.connectWebSocket()"
|
|
:loading="chatStore.connectionStatus === 'CONNECTING'"
|
|
:disabled="chatStore.wsConnected"
|
|
>
|
|
连接
|
|
</a-button>
|
|
<a-button
|
|
@click="chatStore.disconnectWebSocket()"
|
|
:disabled="!chatStore.wsConnected"
|
|
>
|
|
断开
|
|
</a-button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 消息测试 -->
|
|
<div class="message-section">
|
|
<h3>消息测试</h3>
|
|
<div class="message-input">
|
|
<a-input
|
|
v-model:value="testMessage"
|
|
placeholder="输入测试消息..."
|
|
@press-enter="sendTestMessage"
|
|
:disabled="!chatStore.wsConnected"
|
|
/>
|
|
<a-button
|
|
type="primary"
|
|
@click="sendTestMessage"
|
|
:disabled="!chatStore.wsConnected || !testMessage.trim()"
|
|
>
|
|
发送
|
|
</a-button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 消息历史 -->
|
|
<div class="messages-section">
|
|
<h3>消息历史</h3>
|
|
<div class="messages-list">
|
|
<div
|
|
v-for="message in messages"
|
|
:key="message.id"
|
|
class="message-item"
|
|
:class="{ 'user': message.type === 'user', 'ai': message.type === 'ai' }"
|
|
>
|
|
<div class="message-header">
|
|
<span class="message-sender">{{ message.type === 'user' ? '用户' : 'AI' }}</span>
|
|
<span class="message-time">{{ formatTime(message.timestamp) }}</span>
|
|
</div>
|
|
<div class="message-content">{{ message.content }}</div>
|
|
</div>
|
|
</div>
|
|
<div class="messages-actions">
|
|
<a-button @click="clearMessages">清空消息</a-button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 配置信息 -->
|
|
<div class="config-section">
|
|
<h3>配置信息</h3>
|
|
<div class="config-info">
|
|
<div class="config-item">
|
|
<span class="config-label">WebSocket URL:</span>
|
|
<span class="config-value">{{ wsUrl }}</span>
|
|
</div>
|
|
<div class="config-item">
|
|
<span class="config-label">用户ID:</span>
|
|
<span class="config-value">{{ userId }}</span>
|
|
</div>
|
|
<div class="config-item">
|
|
<span class="config-label">会话ID:</span>
|
|
<span class="config-value">{{ chatStore.currentSession?.id || '未设置' }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
|
import { useChatStore } from '@/stores/chat'
|
|
import { useUserStore } from '@/stores/user'
|
|
import dayjs from 'dayjs'
|
|
|
|
const chatStore = useChatStore()
|
|
const userStore = useUserStore()
|
|
|
|
const testMessage = ref('')
|
|
const messages = ref<Array<{
|
|
id: string
|
|
type: 'user' | 'ai'
|
|
content: string
|
|
timestamp: number
|
|
}>>([])
|
|
|
|
const wsUrl = computed(() => import.meta.env.VITE_WS_URL)
|
|
const userId = computed(() => userStore.user?.id || `guest_${Date.now()}`)
|
|
|
|
// 获取连接状态文本
|
|
const getConnectionStatusText = () => {
|
|
switch (chatStore.connectionStatus) {
|
|
case 'CONNECTED':
|
|
return '已连接'
|
|
case 'CONNECTING':
|
|
return '连接中...'
|
|
case 'DISCONNECTED':
|
|
return '已断开'
|
|
case 'ERROR':
|
|
return '连接错误'
|
|
default:
|
|
return '未知状态'
|
|
}
|
|
}
|
|
|
|
// 发送测试消息
|
|
const sendTestMessage = () => {
|
|
if (!testMessage.value.trim() || !chatStore.wsConnected) return
|
|
|
|
const message = {
|
|
id: Date.now().toString(),
|
|
type: 'user' as const,
|
|
content: testMessage.value.trim(),
|
|
timestamp: Date.now()
|
|
}
|
|
|
|
messages.value.push(message)
|
|
chatStore.sendMessage(testMessage.value.trim())
|
|
testMessage.value = ''
|
|
}
|
|
|
|
// 清空消息
|
|
const clearMessages = () => {
|
|
messages.value = []
|
|
chatStore.clearMessages()
|
|
}
|
|
|
|
// 格式化时间
|
|
const formatTime = (timestamp: number) => {
|
|
return dayjs(timestamp).format('HH:mm:ss')
|
|
}
|
|
|
|
// 监听AI回复
|
|
const handleAiMessage = (content: string) => {
|
|
const message = {
|
|
id: Date.now().toString(),
|
|
type: 'ai' as const,
|
|
content,
|
|
timestamp: Date.now()
|
|
}
|
|
messages.value.push(message)
|
|
}
|
|
|
|
onMounted(() => {
|
|
// 监听聊天store中的消息变化
|
|
chatStore.$subscribe((mutation, state) => {
|
|
if (mutation.events && Array.isArray(mutation.events)) {
|
|
mutation.events.forEach((event: any) => {
|
|
if (event.key === 'messages' && event.type === 'add') {
|
|
const newMessage = event.newValue
|
|
if (newMessage && newMessage.type === 'ai') {
|
|
handleAiMessage(newMessage.content)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
})
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
chatStore.disconnectWebSocket()
|
|
})
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.websocket-test {
|
|
padding: 20px;
|
|
max-width: 800px;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
.test-container {
|
|
background: white;
|
|
border-radius: 8px;
|
|
padding: 24px;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.status-section,
|
|
.message-section,
|
|
.messages-section,
|
|
.config-section {
|
|
margin-bottom: 32px;
|
|
|
|
h3 {
|
|
margin-bottom: 16px;
|
|
color: #1890ff;
|
|
border-bottom: 2px solid #f0f0f0;
|
|
padding-bottom: 8px;
|
|
}
|
|
}
|
|
|
|
.status-info {
|
|
margin-bottom: 16px;
|
|
|
|
.status-label {
|
|
font-weight: 500;
|
|
margin-right: 8px;
|
|
}
|
|
|
|
.status-value {
|
|
padding: 4px 8px;
|
|
border-radius: 4px;
|
|
font-weight: 500;
|
|
|
|
&.connected {
|
|
background: #f6ffed;
|
|
color: #52c41a;
|
|
border: 1px solid #b7eb8f;
|
|
}
|
|
|
|
&.connecting {
|
|
background: #fffbe6;
|
|
color: #faad14;
|
|
border: 1px solid #ffe58f;
|
|
}
|
|
|
|
&.disconnected {
|
|
background: #fff2f0;
|
|
color: #ff4d4f;
|
|
border: 1px solid #ffccc7;
|
|
}
|
|
}
|
|
}
|
|
|
|
.status-actions {
|
|
display: flex;
|
|
gap: 12px;
|
|
}
|
|
|
|
.message-input {
|
|
display: flex;
|
|
gap: 12px;
|
|
|
|
.ant-input {
|
|
flex: 1;
|
|
}
|
|
}
|
|
|
|
.messages-list {
|
|
max-height: 300px;
|
|
overflow-y: auto;
|
|
border: 1px solid #f0f0f0;
|
|
border-radius: 4px;
|
|
padding: 12px;
|
|
margin-bottom: 16px;
|
|
|
|
.message-item {
|
|
margin-bottom: 12px;
|
|
padding: 8px;
|
|
border-radius: 4px;
|
|
|
|
&.user {
|
|
background: #e6f7ff;
|
|
border-left: 3px solid #1890ff;
|
|
}
|
|
|
|
&.ai {
|
|
background: #f6ffed;
|
|
border-left: 3px solid #52c41a;
|
|
}
|
|
|
|
.message-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
margin-bottom: 4px;
|
|
font-size: 12px;
|
|
color: #666;
|
|
|
|
.message-sender {
|
|
font-weight: 500;
|
|
}
|
|
}
|
|
|
|
.message-content {
|
|
color: #333;
|
|
line-height: 1.5;
|
|
}
|
|
}
|
|
}
|
|
|
|
.config-info {
|
|
.config-item {
|
|
display: flex;
|
|
margin-bottom: 8px;
|
|
|
|
.config-label {
|
|
font-weight: 500;
|
|
min-width: 120px;
|
|
color: #666;
|
|
}
|
|
|
|
.config-value {
|
|
color: #333;
|
|
word-break: break-all;
|
|
}
|
|
}
|
|
}
|
|
</style>
|