Browse Source

feat: 更新WebSocket客户端,添加消息类型支持和自动重连功能

refactor/repository-structure
hyh 8 months ago
parent
commit
bb9cd356c9
  1. 234
      src/CellularManagement.WebAPI/wwwroot/websocket.html

234
src/CellularManagement.WebAPI/wwwroot/websocket.html

@ -34,6 +34,13 @@
position: relative; position: relative;
z-index: 1; z-index: 1;
} }
.stats-card {
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(5px);
}
.dark .stats-card {
background: rgba(31, 41, 55, 0.9);
}
</style> </style>
</head> </head>
<body class="bg-gray-50 dark:bg-gray-900 h-screen flex flex-col"> <body class="bg-gray-50 dark:bg-gray-900 h-screen flex flex-col">
@ -61,6 +68,14 @@
<div class="w-2 h-2 rounded-full bg-red-500 status-dot"></div> <div class="w-2 h-2 rounded-full bg-red-500 status-dot"></div>
<span class="text-sm text-gray-600 dark:text-gray-300">未连接</span> <span class="text-sm text-gray-600 dark:text-gray-300">未连接</span>
</div> </div>
<div id="stats" class="flex items-center space-x-4 ml-4">
<span class="text-sm text-gray-600 dark:text-gray-300">
消息: <span id="messageCount">0</span>
</span>
<span class="text-sm text-gray-600 dark:text-gray-300">
连接: <span id="connectionCount">0</span>
</span>
</div>
</div> </div>
<div class="flex space-x-2"> <div class="flex space-x-2">
<button id="connectBtn" <button id="connectBtn"
@ -83,11 +98,20 @@
<div class="max-w-4xl mx-auto h-full px-4 py-4"> <div class="max-w-4xl mx-auto h-full px-4 py-4">
<div class="flex justify-between items-center mb-2"> <div class="flex justify-between items-center mb-2">
<h2 class="text-lg font-semibold text-gray-700 dark:text-gray-300">消息记录</h2> <h2 class="text-lg font-semibold text-gray-700 dark:text-gray-300">消息记录</h2>
<button id="clearBtn" <div class="flex space-x-2">
class="px-3 py-1 bg-gray-500 hover:bg-gray-600 text-white rounded-lg <select id="messageType"
shadow-sm transition-colors duration-200"> class="px-3 py-1 border border-gray-300 dark:border-gray-600 rounded-lg
清空消息 dark:bg-gray-700 dark:text-white">
</button> <option value="message">普通消息</option>
<option value="system">系统消息</option>
<option value="error">错误消息</option>
</select>
<button id="clearBtn"
class="px-3 py-1 bg-gray-500 hover:bg-gray-600 text-white rounded-lg
shadow-sm transition-colors duration-200">
清空消息
</button>
</div>
</div> </div>
<div id="messages" class="h-full overflow-y-auto space-y-4"> <div id="messages" class="h-full overflow-y-auto space-y-4">
<!-- 消息将在这里动态添加 --> <!-- 消息将在这里动态添加 -->
@ -205,6 +229,12 @@
// WebSocket 相关代码 // WebSocket 相关代码
let socket; let socket;
let reconnectAttempts = 0;
const maxReconnectAttempts = 5;
const reconnectDelay = 3000;
let messageCount = 0;
let connectionCount = 0;
const statusElement = document.getElementById('status'); const statusElement = document.getElementById('status');
const statusDot = statusElement.querySelector('.status-dot'); const statusDot = statusElement.querySelector('.status-dot');
const statusText = statusElement.querySelector('span:last-child'); const statusText = statusElement.querySelector('span:last-child');
@ -215,6 +245,9 @@
const sendBtn = document.getElementById('sendBtn'); const sendBtn = document.getElementById('sendBtn');
const wsAddressInput = document.getElementById('wsAddress'); const wsAddressInput = document.getElementById('wsAddress');
const clearBtn = document.getElementById('clearBtn'); const clearBtn = document.getElementById('clearBtn');
const messageTypeSelect = document.getElementById('messageType');
const messageCountElement = document.getElementById('messageCount');
const connectionCountElement = document.getElementById('connectionCount');
// 更新状态显示 // 更新状态显示
function updateStatus(connected) { function updateStatus(connected) {
@ -224,6 +257,8 @@
statusText.textContent = '已连接'; statusText.textContent = '已连接';
statusText.classList.remove('text-gray-600'); statusText.classList.remove('text-gray-600');
statusText.classList.add('text-green-600'); statusText.classList.add('text-green-600');
connectionCount++;
connectionCountElement.textContent = connectionCount;
} else { } else {
statusDot.classList.remove('bg-green-500'); statusDot.classList.remove('bg-green-500');
statusDot.classList.add('bg-red-500'); statusDot.classList.add('bg-red-500');
@ -234,7 +269,7 @@
} }
// 添加消息到显示区域 // 添加消息到显示区域
function addMessage(message, isReceived = false) { function addMessage(message, isReceived = false, type = 'message') {
const messageElement = document.createElement('div'); const messageElement = document.createElement('div');
messageElement.className = `message flex ${isReceived ? 'justify-start' : 'justify-end'}`; messageElement.className = `message flex ${isReceived ? 'justify-start' : 'justify-end'}`;
@ -242,140 +277,145 @@
messageContent.className = `flex items-start space-x-2 max-w-[80%]`; messageContent.className = `flex items-start space-x-2 max-w-[80%]`;
const avatar = document.createElement('div'); const avatar = document.createElement('div');
avatar.className = `flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center let avatarClass = 'flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center text-white font-bold';
text-white font-bold ${isReceived ? 'bg-gray-500' : 'bg-indigo-500'}`;
avatar.textContent = isReceived ? 'S' : '我'; switch(type) {
case 'system':
const bubble = document.createElement('div'); avatarClass += ' bg-blue-500';
bubble.className = `p-3 rounded-lg shadow-sm ${ avatar.textContent = '系';
isReceived break;
? 'bg-white dark:bg-gray-700 text-gray-800 dark:text-gray-200' case 'error':
: 'bg-indigo-500 text-white' avatarClass += ' bg-red-500';
}`; avatar.textContent = '错';
bubble.textContent = message; break;
default:
if (isReceived) { avatarClass += isReceived ? ' bg-gray-500' : ' bg-indigo-500';
messageContent.appendChild(avatar); avatar.textContent = isReceived ? 'S' : '我';
messageContent.appendChild(bubble);
} else {
messageContent.appendChild(bubble);
messageContent.appendChild(avatar);
} }
messageElement.appendChild(messageContent); avatar.className = avatarClass;
messagesContainer.appendChild(messageElement);
messagesContainer.scrollTop = messagesContainer.scrollHeight;
// 收到消息时更新3D场景 const bubble = document.createElement('div');
if (isReceived) { let bubbleClass = 'p-3 rounded-lg shadow-sm';
updateParticles();
switch(type) {
case 'system':
bubbleClass += ' bg-blue-100 dark:bg-blue-900';
break;
case 'error':
bubbleClass += ' bg-red-100 dark:bg-red-900';
break;
default:
bubbleClass += isReceived ? ' bg-gray-100 dark:bg-gray-700' : ' bg-indigo-100 dark:bg-indigo-900';
} }
}
// 更新粒子效果 bubble.className = bubbleClass;
function updateParticles() { bubble.textContent = message;
const positions = particleGeometry.attributes.position.array;
const colors = particleGeometry.attributes.color.array;
// 随机更新一些粒子的位置和颜色 messageContent.appendChild(avatar);
for (let i = 0; i < particleCount; i += 10) { messageContent.appendChild(bubble);
positions[i * 3] = (Math.random() - 0.5) * 20; messageElement.appendChild(messageContent);
positions[i * 3 + 1] = (Math.random() - 0.5) * 20; messagesContainer.appendChild(messageElement);
positions[i * 3 + 2] = (Math.random() - 0.5) * 20;
colors[i * 3] = Math.random(); // 滚动到底部
colors[i * 3 + 1] = Math.random(); messagesContainer.scrollTop = messagesContainer.scrollHeight;
colors[i * 3 + 2] = Math.random();
}
particleGeometry.attributes.position.needsUpdate = true; // 更新消息计数
particleGeometry.attributes.color.needsUpdate = true; messageCount++;
messageCountElement.textContent = messageCount;
} }
// 初始化 WebSocket 连接 // 连接WebSocket
function initWebSocket() { function connect() {
const wsAddress = wsAddressInput.value.trim();
if (!wsAddress) {
addMessage('请输入WebSocket地址', true);
return;
}
try { try {
// 确保地址以 ws:// 开头 socket = new WebSocket(wsAddressInput.value);
const normalizedAddress = wsAddress.startsWith('ws://')
? wsAddress
: `ws://${wsAddress}`;
socket = new WebSocket(normalizedAddress);
socket.onopen = function() { socket.onopen = function() {
updateStatus(true); updateStatus(true);
addMessage('WebSocket 连接已建立', true); addMessage('连接成功', false, 'system');
reconnectAttempts = 0;
}; };
socket.onclose = function(event) { socket.onclose = function() {
updateStatus(false); updateStatus(false);
addMessage(`WebSocket 连接已关闭 (${event.code})`, true); addMessage('连接已关闭', false, 'system');
attemptReconnect();
}; };
socket.onerror = function(error) { socket.onerror = function(error) {
addMessage(`连接错误: ${error.message || '未知错误'}`, true); addMessage(`连接错误: ${error.message}`, false, 'error');
}; };
socket.onmessage = function(event) { socket.onmessage = function(event) {
addMessage(event.data, true); try {
const data = JSON.parse(event.data);
addMessage(data.content, true, data.type || 'message');
} catch (e) {
addMessage(event.data, true);
}
}; };
} catch (error) { } catch (error) {
addMessage(`连接失败: ${error.message}`, true); addMessage(`连接失败: ${error.message}`, false, 'error');
} }
} }
// 按钮事件处理 // 尝试重新连接
connectBtn.addEventListener('click', function() { function attemptReconnect() {
if (!socket || socket.readyState === WebSocket.CLOSED) { if (reconnectAttempts < maxReconnectAttempts) {
initWebSocket(); reconnectAttempts++;
addMessage(`尝试重新连接 (${reconnectAttempts}/${maxReconnectAttempts})...`, false, 'system');
setTimeout(connect, reconnectDelay);
} else {
addMessage('达到最大重连次数,请手动重连', false, 'error');
} }
}); }
disconnectBtn.addEventListener('click', function() { // 发送消息
if (socket && socket.readyState === WebSocket.OPEN) { function sendMessage() {
socket.close(); if (!socket || socket.readyState !== WebSocket.OPEN) {
addMessage('未连接到服务器', false, 'error');
return;
} }
});
sendBtn.addEventListener('click', function() { const message = messageInput.value.trim();
if (socket && socket.readyState === WebSocket.OPEN) { if (!message) return;
const message = messageInput.value.trim();
if (message) { const messageType = messageTypeSelect.value;
socket.send(message); const data = {
addMessage(message); type: messageType,
messageInput.value = ''; content: message,
} timestamp: new Date().toISOString()
} else { };
addMessage('WebSocket 未连接', true);
try {
socket.send(JSON.stringify(data));
addMessage(message, false, messageType);
messageInput.value = '';
} catch (error) {
addMessage(`发送失败: ${error.message}`, false, 'error');
} }
}); }
// 按回车发送消息 // 事件监听
messageInput.addEventListener('keypress', function(e) { connectBtn.addEventListener('click', connect);
if (e.key === 'Enter') { disconnectBtn.addEventListener('click', function() {
sendBtn.click(); if (socket) {
socket.close();
} }
}); });
sendBtn.addEventListener('click', sendMessage);
// 按回车连接WebSocket messageInput.addEventListener('keypress', function(e) {
wsAddressInput.addEventListener('keypress', function(e) {
if (e.key === 'Enter') { if (e.key === 'Enter') {
connectBtn.click(); sendMessage();
} }
}); });
// 清空消息
clearBtn.addEventListener('click', function() { clearBtn.addEventListener('click', function() {
messagesContainer.innerHTML = ''; messagesContainer.innerHTML = '';
messageCount = 0;
messageCountElement.textContent = '0';
}); });
// 初始化Three.js场景 // 初始化Three.js
initThreeJS(); initThreeJS();
</script> </script>
</body> </body>

Loading…
Cancel
Save