Files
happy-life-star/web/src/views/WebSocketTest.vue
T
peanut bbe8fcd776 重命名前端项目目录:web-flowith -> web
- 将前端项目目录从 web-flowith 重命名为 web,使目录结构更简洁
- 保持所有前端代码和配置文件不变
- 统一项目目录命名规范
2025-07-24 22:20:19 +08:00

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>