Files
gitea-pages/examples/js_ws_event/index.html
2025-11-20 01:19:47 +08:00

459 lines
13 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebSocket聊天客户端</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}
.chat-container {
width: 100%;
max-width: 900px;
height: 85vh;
background-color: white;
border-radius: 15px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
display: flex;
flex-direction: column;
overflow: hidden;
}
.header {
background: linear-gradient(to right, #4a00e0, #8e2de2);
color: white;
padding: 20px;
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 15px;
}
.header-left {
display: flex;
align-items: center;
gap: 15px;
flex: 1;
}
.header-right {
display: flex;
align-items: center;
gap: 15px;
}
.status {
display: flex;
align-items: center;
gap: 8px;
}
.status-indicator {
width: 10px;
height: 10px;
border-radius: 50%;
}
.status-connecting {
background-color: #ffc107;
}
.status-connected {
background-color: #28a745;
}
.status-disconnected {
background-color: #dc3545;
}
.connection-btn {
padding: 8px 16px;
border: none;
border-radius: 20px;
cursor: pointer;
font-weight: 600;
background: rgba(255, 255, 255, 0.2);
color: white;
transition: all 0.3s;
}
.connection-btn:hover {
background: rgba(255, 255, 255, 0.3);
transform: translateY(-2px);
}
.name-input-container {
display: flex;
align-items: center;
gap: 8px;
background: rgba(255, 255, 255, 0.2);
padding: 6px 12px;
border-radius: 20px;
}
.name-input-container label {
font-size: 14px;
}
.name-input-container input {
padding: 4px 8px;
border: none;
border-radius: 15px;
background: rgba(255, 255, 255, 0.9);
color: #333;
width: 80px;
outline: none;
font-size: 14px;
}
.chat-area {
flex: 1;
padding: 20px;
overflow-y: auto;
background-color: #f8f9fa;
display: flex;
flex-direction: column;
gap: 15px;
}
.message {
max-width: 70%;
padding: 12px 18px;
border-radius: 18px;
position: relative;
line-height: 1.4;
word-wrap: break-word;
}
.left-message {
align-self: flex-start;
background-color: white;
color: #333;
border: 1px solid #e0e0e0;
border-bottom-left-radius: 5px;
}
.right-message {
align-self: flex-end;
background: linear-gradient(to right, #4a00e0, #8e2de2);
color: white;
border-bottom-right-radius: 5px;
}
.message-time {
font-size: 11px;
margin-top: 5px;
opacity: 0.7;
text-align: right;
}
.message-name {
font-size: 12px;
margin-bottom: 5px;
font-weight: bold;
}
.input-area {
padding: 20px;
background-color: white;
border-top: 1px solid #e0e0e0;
display: flex;
gap: 10px;
}
.input-area input {
flex: 1;
padding: 12px 15px;
border: 1px solid #ddd;
border-radius: 25px;
outline: none;
font-size: 16px;
transition: all 0.3s;
}
.input-area input:focus {
border-color: #8e2de2;
box-shadow: 0 0 0 2px rgba(142, 45, 226, 0.2);
}
.send-btn {
padding: 12px 25px;
border: none;
border-radius: 25px;
cursor: pointer;
font-weight: 600;
background: linear-gradient(to right, #4a00e0, #8e2de2);
color: white;
transition: all 0.3s;
}
.send-btn:hover {
background: linear-gradient(to right, #3a00b0, #7a1dc2);
transform: translateY(-2px);
}
.send-btn:disabled {
background: #cccccc;
cursor: not-allowed;
transform: none;
}
.empty-state {
text-align: center;
color: #6c757d;
margin-top: 50px;
}
.empty-state i {
font-size: 48px;
margin-bottom: 15px;
opacity: 0.5;
}
@media (max-width: 768px) {
.chat-container {
height: 95vh;
}
.message {
max-width: 85%;
}
.header {
flex-direction: column;
gap: 10px;
text-align: center;
}
.header-left, .header-right {
width: 100%;
justify-content: center;
}
}
</style>
</head>
<body>
<div class="chat-container">
<div class="header">
<div class="header-left">
<h1>WebSocket聊天客户端</h1>
<div class="status">
<div class="status-indicator status-disconnected"></div>
<span id="status-text">未连接</span>
</div>
</div>
<div class="header-right">
<div class="name-input-container">
<label for="nameInput">姓名:</label>
<input type="text" id="nameInput" placeholder="输入姓名">
</div>
<button class="connection-btn" id="connectionButton" onclick="toggleConnection()">连接</button>
</div>
</div>
<div class="chat-area" id="chatArea">
<div class="empty-state">
<i>💬</i>
<p>连接服务器开始聊天</p>
</div>
</div>
<div class="input-area">
<input type="text" id="messageInput" placeholder="输入消息..." disabled>
<button class="send-btn" id="sendButton" onclick="sendMessage()" disabled>发送</button>
</div>
</div>
<script>
let ws = null;
let isConnected = false;
let currentUserName = "";
const statusText = document.getElementById('status-text');
const statusIndicator = document.querySelector('.status-indicator');
const chatArea = document.getElementById('chatArea');
const messageInput = document.getElementById('messageInput');
const sendButton = document.getElementById('sendButton');
const connectionButton = document.getElementById('connectionButton');
const nameInput = document.getElementById('nameInput');
// 生成4位随机数字作为默认用户名
function generateRandomUsername() {
return Math.floor(1000 + Math.random() * 9000).toString();
}
// 初始化
function init() {
const randomUsername = generateRandomUsername();
nameInput.value = randomUsername;
currentUserName = randomUsername;
}
function toggleConnection() {
if (isConnected) {
disconnect();
} else {
connect();
}
}
function connect() {
// 获取用户名,如果没有输入则使用默认值
const username = nameInput.value.trim() || currentUserName;
currentUserName = username;
updateStatus('正在连接...', 'status-connecting');
connectionButton.textContent = '连接中...';
try {
// 使用用户名作为查询参数
ws = new WebSocket(`/event?name=${encodeURIComponent(username)}`);
ws.onopen = () => {
isConnected = true;
updateStatus('已连接', 'status-connected');
connectionButton.textContent = '断开连接';
enableControls(true);
addSystemMessage('连接成功,可以开始聊天了');
};
ws.onmessage = event => {
try {
// 解析JSON数据
const data = JSON.parse(event.data);
// 根据name字段判断消息显示位置
if (data.name === currentUserName) {
// 如果是当前用户,显示在右侧
addMessage(data.data, 'right', data.name);
} else {
// 如果是其他用户,显示在左侧
addMessage(data.data, 'left', data.name);
}
} catch (e) {
// 如果不是JSON按原方式处理
addMessage(event.data, 'left', '系统');
}
};
ws.onclose = () => {
isConnected = false;
updateStatus('未连接', 'status-disconnected');
connectionButton.textContent = '连接';
enableControls(false);
addSystemMessage('连接已断开');
};
ws.onerror = error => {
isConnected = false;
updateStatus('连接错误', 'status-disconnected');
connectionButton.textContent = '连接';
enableControls(false);
addSystemMessage('连接错误,请检查服务器');
};
} catch (error) {
isConnected = false;
updateStatus('连接失败', 'status-disconnected');
connectionButton.textContent = '连接';
enableControls(false);
}
}
function disconnect() {
if (ws) {
ws.close();
ws = null;
}
isConnected = false;
connectionButton.textContent = '连接';
enableControls(false);
addSystemMessage('已断开连接');
}
function sendMessage() {
const message = messageInput.value.trim();
if (message) {
// 发送消息到服务器
ws.send(message);
// 清空输入框
messageInput.value = '';
}
}
function addMessage(message, type, senderName) {
const emptyState = document.querySelector('.empty-state');
if (emptyState) {
emptyState.remove();
}
const messageElement = document.createElement('div');
const time = new Date().toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'});
if (type === 'right') {
messageElement.className = 'message right-message';
messageElement.innerHTML = `
${message}
<div class="message-time">${time}</div>
`;
} else if (type === 'left') {
messageElement.className = 'message left-message';
messageElement.innerHTML = `
<div class="message-name">${senderName}</div>
${message}
<div class="message-time">${time}</div>
`;
} else {
messageElement.className = 'message system-message';
messageElement.style.alignSelf = 'center';
messageElement.style.maxWidth = '100%';
messageElement.style.backgroundColor = 'rgba(0,0,0,0.05)';
messageElement.style.color = '#6c757d';
messageElement.style.fontSize = '14px';
messageElement.style.textAlign = 'center';
messageElement.style.borderRadius = '10px';
messageElement.textContent = message;
}
chatArea.appendChild(messageElement);
chatArea.scrollTop = chatArea.scrollHeight;
}
function addSystemMessage(message) {
addMessage(message, 'system');
}
function updateStatus(message, statusClass) {
statusText.textContent = message;
statusIndicator.className = 'status-indicator ' + statusClass;
}
function enableControls(connected) {
messageInput.disabled = !connected;
sendButton.disabled = !connected;
nameInput.disabled = connected; // 连接时禁用姓名修改
}
messageInput.addEventListener('keypress', function(event) {
if (event.key === 'Enter') {
sendMessage();
}
});
// 初始化生成随机用户名
init();
</script>
</body>
</html>