论坛,聊天室前后端对接

Change-Id: I90740329ab40dc050e8a791a382ab187900d673a
diff --git a/src/views/ChatRoom.vue b/src/views/ChatRoom.vue
new file mode 100644
index 0000000..ad63f9d
--- /dev/null
+++ b/src/views/ChatRoom.vue
@@ -0,0 +1,187 @@
+<template>
+  <div class="chat-room">
+    <h2>聊天室</h2>
+    <div class="chat-messages" ref="messagesRef">
+      <div v-for="(msg, idx) in messages" :key="idx" class="chat-message">
+        <span class="chat-user">{{ msg.username || '匿名' }}:</span>
+        <span class="chat-content">{{ msg.content }}</span>
+        <span class="chat-time" v-if="msg.timestamp">({{ formatTime(msg.timestamp) }})</span>
+      </div>
+    </div>
+    <div class="chat-input">
+      <el-input v-model="input" placeholder="输入消息..." @keyup.enter="sendMessage" />
+      <el-button type="primary" @click="sendMessage">发送</el-button>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue'
+import { useStore } from 'vuex'
+import axios from 'axios'
+
+const store = useStore()
+const wsUrl = 'ws://localhost:8081/api/ws/chat'
+const ws = ref(null)
+const messages = ref([])
+const input = ref('')
+const messagesRef = ref(null)
+
+function getToken() {
+  return localStorage.getItem('token')
+}
+
+function setCookie(name, value, days = 7) {
+  const expires = new Date()
+  expires.setTime(expires.getTime() + (days * 24 * 60 * 60 * 1000))
+  document.cookie = `${name}=${value};expires=${expires.toUTCString()};path=/`
+}
+
+// 获取聊天历史记录
+async function loadChatHistory() {
+  try {
+    const token = getToken()
+    if (!token) return
+
+    console.log('🔄 获取聊天历史记录...')
+    const response = await axios.get('http://localhost:8081/api/chat/history', {
+      params: { roomId: 1 },
+      headers: { 'sapling-token': token }
+    })
+    
+    if (response.data && Array.isArray(response.data)) {
+      messages.value = response.data
+      console.log('📚 获取到', response.data.length, '条历史消息')
+      
+      // 滚动到底部
+      nextTick(() => {
+        if (messagesRef.value) {
+          messagesRef.value.scrollTop = messagesRef.value.scrollHeight
+        }
+      })
+    }
+  } catch (error) {
+    console.error('❌ 获取聊天历史失败:', error)
+  }
+}
+
+function connect() {
+  const token = getToken()
+  if (!token) {
+    console.error('无法连接WebSocket:未找到token')
+    return
+  }
+
+  // 设置cookie,这样WebSocket握手时后端可以从cookie获取token
+  setCookie('sapling-token', token)
+  
+  console.log('尝试连接WebSocket:', wsUrl)
+  console.log('已设置cookie: sapling-token =', token)
+  
+  ws.value = new WebSocket(wsUrl)
+  
+  ws.value.onopen = async () => {
+    console.log('✅ WebSocket连接成功')
+    // 连接成功后立即获取聊天历史
+    await loadChatHistory()
+  }
+  
+  ws.value.onmessage = (event) => {
+    console.log('📥 收到消息:', event.data)
+    try {
+      const msg = JSON.parse(event.data)
+      console.log('📥 解析后的消息:', msg)
+      messages.value.push(msg)
+      nextTick(() => {
+        if (messagesRef.value) {
+          messagesRef.value.scrollTop = messagesRef.value.scrollHeight
+        }
+      })
+    } catch (e) {
+      console.error('❌ 消息解析失败:', e, event.data)
+    }
+  }
+  
+  ws.value.onerror = (err) => {
+    console.error('❌ WebSocket错误:', err)
+  }
+  
+  ws.value.onclose = (event) => {
+    console.log('❌ WebSocket已断开, 代码:', event.code, '原因:', event.reason)
+    setTimeout(() => {
+      console.log('🔄 尝试重连...')
+      connect()
+    }, 2000)
+  }
+}
+
+function sendMessage() {
+  if (!input.value.trim()) return
+  
+  if (!ws.value || ws.value.readyState !== WebSocket.OPEN) {
+    console.error('❌ WebSocket未连接')
+    return
+  }
+
+  const message = {
+    roomId: 1, // 默认房间ID为1
+    content: input.value.trim()
+  }
+  
+  console.log('📤 发送消息:', message)
+  ws.value.send(JSON.stringify(message))
+  input.value = ''
+}
+
+function formatTime(timestamp) {
+  return new Date(timestamp).toLocaleTimeString()
+}
+
+onMounted(() => {
+  console.log('🚀 聊天室组件已挂载')
+  connect()
+})
+
+onBeforeUnmount(() => {
+  console.log('🔌 组件卸载,关闭WebSocket连接')
+  if (ws.value) {
+    ws.value.close()
+  }
+})
+</script>
+
+<style scoped>
+.chat-room {
+  max-width: 600px;
+  margin: 40px auto;
+  border: 1px solid #eee;
+  border-radius: 8px;
+  padding: 24px;
+  background: #fff;
+}
+.chat-messages {
+  height: 320px;
+  overflow-y: auto;
+  border: 1px solid #f0f0f0;
+  border-radius: 4px;
+  padding: 12px;
+  margin-bottom: 16px;
+  background: #fafbfc;
+}
+.chat-message {
+  margin-bottom: 8px;
+}
+.chat-user {
+  font-weight: bold;
+  color: #409eff;
+}
+.chat-time {
+  color: #999;
+  font-size: 12px;
+  margin-left: 8px;
+}
+.chat-input {
+  display: flex;
+  gap: 8px;
+}
+</style> 
\ No newline at end of file