m2pool_web_frontend/mining-pool/src/views/simulation.vue

397 lines
9.2 KiB
Vue

<template>
<div>
<h1>{{ msg }}</h1>
<!-- 用户ID输入部分 -->
<div class="user-input-container">
<div class="input-group" :class="{ 'disabled': isConnected }">
<input
v-model="email"
placeholder="请输入您的用户邮箱"
:disabled="isConnected"
/>
<input
v-model="targetEmail"
placeholder="请输入目标用户邮箱"
:disabled="isConnected"
@keyup.enter="!isConnected && !isConnecting && connectWebSocket()"
/>
</div>
<div class="button-group">
<button
@click="connectWebSocket"
:disabled="isConnected || isConnecting || !email || !targetEmail"
:class="{ 'disabled': isConnected || isConnecting || !email || !targetEmail }"
>
{{ isConnecting ? '连接中...' : '连接' }}
</button>
<button
v-if="isConnected"
@click="disconnectWebSocket"
class="disconnect-btn"
>
断开连接
</button>
</div>
</div>
<div v-if="connectionError" class="error-message">
{{ connectionError }}
</div>
<div v-if="isConnected">
<!-- WebSocket 聊天部分 -->
<div class="chat-container">
<div class="message-list" ref="messageList">
<div v-for="(msg, index) in receivedMessages" :key="index" class="message"
:class="{ 'error-message': msg.error }">
<div v-if="typeof msg === 'string'">{{ msg }}</div>
<div v-else>
<div class="message-header">
<span class="message-sender">{{ msg.sender || 'Unknown' }}</span>
<span class="message-time">{{ formatTime(msg.timestamp) }}</span>
</div>
<div class="message-content">{{ msg.content }}</div>
</div>
</div>
</div>
<div class="input-container">
<textarea
v-model="message"
@keyup.enter="sendMessage"
placeholder="输入消息..."
rows="3"
></textarea>
<button @click="sendMessage" :disabled="!message.trim()">
发送
</button>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted, nextTick, watch } from 'vue'
import { Stomp } from '@stomp/stompjs'
const props = defineProps({
msg: {
type: String,
required: true
}
})
const message = ref('')
const receivedMessages = ref([])
const email = ref('')
const targetEmail = ref('')
const isConnected = ref(false)
const isConnecting = ref(false)
const connectionError = ref('')
const messageList = ref(null)
// 创建一个响应式的 stompClient
const stompClient = ref(null)
// 添加连接时间变量
const connectTime = ref(null)
// 自动滚动到底部
const scrollToBottom = async () => {
await nextTick()
if (messageList.value) {
messageList.value.scrollTop = messageList.value.scrollHeight
}
}
// 监听消息列表变化,自动滚动
watch(receivedMessages, () => {
scrollToBottom()
})
// 格式化时间
const formatTime = (timestamp) => {
if (!timestamp) return ''
try {
const date = new Date(timestamp)
return date.toLocaleTimeString()
} catch (e) {
return ''
}
}
// 连接WebSocket
const connectWebSocket = () => {
if (!email.value || !targetEmail.value) {
connectionError.value = '请输入用户邮箱和目标用户邮箱'
return
}
connectionError.value = ''
isConnecting.value = true
connectTime.value = new Date() // 记录连接时间
try {
stompClient.value = Stomp.client('ws://localhost:8101/chat/ws')
stompClient.value.heartbeat.outgoing = 20000
stompClient.value.heartbeat.incoming = 0
const connectHeaders = {
'email': email.value,
'type': 2 //0 游客 1 登录用户 2 客服
}
stompClient.value.connect(
connectHeaders,
function(frame) {
console.log('连接成功: ' + frame)
console.log('连接时间:', connectTime.value?.toLocaleString())
isConnected.value = true
isConnecting.value = false
// 添加系统消息
receivedMessages.value.push({
sender: 'System',
content: '已连接到聊天服务器',
timestamp: new Date().toISOString(),
system: true
})
// 订阅自己管道的消息
stompClient.value.subscribe(`/user/queue/${email.value}`, function(message) {
console.log('收到消息:', message.body)
try {
const parsedMessage = JSON.parse(message.body)
receivedMessages.value.push(parsedMessage)
} catch (error) {
console.error('消息解析失败:', error)
receivedMessages.value.push({
sender: 'System',
content: `消息格式错误: ${message.body}`,
timestamp: new Date().toISOString(),
error: true
})
}
})
},
function(error) {
console.error('连接失败:', error)
isConnected.value = false
isConnecting.value = false
connectionError.value = `连接失败: ${error.headers?.message || error.message || '未知错误'}`
}
)
} catch (error) {
console.error('初始化WebSocket客户端失败:', error)
isConnected.value = false
isConnecting.value = false
connectionError.value = `初始化失败: ${error.message || '未知错误'}`
}
}
// 添加断开连接方法
const disconnectWebSocket = () => {
if (stompClient.value?.connected) {
stompClient.value.disconnect(() => {
const disconnectTime = new Date()
const duration = connectTime.value ?
Math.floor((disconnectTime.getTime() - connectTime.value.getTime()) / 1000) : 0
console.log('断开连接时间:', disconnectTime.toLocaleString())
console.log(`连接持续时间: ${Math.floor(duration / 60)}${duration % 60}`)
isConnected.value = false
receivedMessages.value = []
connectTime.value = null
// 添加提示
connectionError.value = '已断开连接'
setTimeout(() => {
connectionError.value = ''
}, 3000)
})
}
}
// 发送消息方法
const sendMessage = () => {
if (!message.value.trim()) return
if (stompClient.value?.connected) {
try {
const messageObj = {
email: targetEmail.value,
content: message.value.trim()
}
stompClient.value.send(
`/point/send/message`,
{},
JSON.stringify(messageObj)
)
// 添加自己发送的消息到显示列表
receivedMessages.value.push({
sender: email.value,
content: message.value.trim(),
timestamp: new Date().toISOString(),
isSelf: true
})
message.value = ''
} catch (error) {
console.error('发送消息失败:', error)
receivedMessages.value.push({
sender: 'System',
content: `发送失败: ${error.message || '未知错误'}`,
timestamp: new Date().toISOString(),
error: true
})
}
} else {
connectionError.value = '连接已断开,请重新连接'
isConnected.value = false
}
}
// 组件卸载时断开连接
onUnmounted(() => {
if (stompClient.value?.connected) {
stompClient.value.disconnect()
}
})
</script>
<style scoped>
.read-the-docs {
color: #888;
}
.user-input-container {
max-width: 400px;
margin: 20px auto;
}
.input-group {
display: flex;
gap: 10px;
margin-bottom: 10px;
}
.chat-container {
max-width: 600px;
margin: 20px auto;
padding: 20px;
border: 1px solid #ddd;
border-radius: 8px;
box-shadow: 0 2px 6px rgba(0,0,0,0.1);
}
.message-list {
height: 300px;
overflow-y: auto;
border: 1px solid #eee;
padding: 10px;
margin-bottom: 10px;
border-radius: 4px;
}
.message {
margin: 8px 0;
padding: 10px;
background-color: #f5f5f5;
border-radius: 8px;
max-width: 80%;
}
.message-header {
display: flex;
justify-content: space-between;
font-size: 0.8em;
margin-bottom: 4px;
}
.message-sender {
font-weight: bold;
color: #333;
}
.message-time {
color: #999;
}
.message-content {
word-break: break-word;
}
.message[class*="isSelf"] {
background-color: #dcf8c6;
margin-left: auto;
}
.error-message {
background-color: #ffebee;
color: #d32f2f;
padding: 8px;
border-radius: 4px;
margin: 10px 0;
text-align: center;
}
.input-container {
display: flex;
gap: 10px;
}
input, textarea {
flex: 1;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-family: inherit;
resize: none;
}
button {
padding: 8px 16px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover:not(:disabled) {
background-color: #45a049;
}
button:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.button-group {
display: flex;
gap: 10px;
}
.disabled {
opacity: 0.6;
cursor: not-allowed;
}
.disconnect-btn {
background-color: #dc3545;
}
.disconnect-btn:hover {
background-color: #c82333;
}
input:disabled, textarea:disabled {
background-color: #f5f5f5;
cursor: not-allowed;
}
</style>