From d731eb4d9f6a99bae55c75fa1042ee0ecc3fd49a Mon Sep 17 00:00:00 2001 From: shuaiplus <2327005759@qq.com> Date: Sun, 4 May 2025 23:49:09 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=87=E6=8D=A2=E6=88=BF=E9=97=B4=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/server.js | 2 +- web/config.js | 4 +- web/main.js | 284 ++++++++++++++++++++++++++++++++++------------- 3 files changed, 211 insertions(+), 79 deletions(-) diff --git a/server/server.js b/server/server.js index d75b597..4a74db3 100644 --- a/server/server.js +++ b/server/server.js @@ -7,7 +7,7 @@ const ws = require('ws'); const config = { rsaPrivate: crypto.createPrivateKey({ - key: Buffer.from('...generate it with key-generate.js...', 'base64'), + key: Buffer.from('MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDMqxjzmpko9umpyWFoRjP0JcdSpPBd7IJifI5VB+sNjaFqjmQCqmVTU0qn3ChyDzV7UQIVMybvlAUmEMv9n8vqDvprWxoUZ3R+Wlnarnke10ht4eaFVl3YL4IN+x++hS0uIVK8q2VvFoTSE6fely6tUU7pF7xAIHs9TtF/iN2/KXB4WNC9/vyg8b2sjW7Ok/Blh92Hbcdtgin9T882KUiMT102KvtrjN8a+gWxfOlLd4vozdmkcEbtOpRkh7EgkMf4KaelROzXePjKct8K/YGphy+s6W8aVLR8Px7EmOaiDt0JaByJY+1wjYb5LrQQ3qi4qzSWpSb2QNiW2cjBZIZpAgMBAAECggEACHpe7HpfNU/zEm4t24aq8MZe2RjNfyqEpSVBNRsAnN1czh/rWyPpUhgNa+ImB8GDBuJuamflyT9Rrke8V3t21DF+JJPPwwAdFn3SIomfKUykKq8UoDnZZdLj+YXJRRhZrINDXN7hswYVBum7BrCvBY1sbiHmUaa2zC2XZofKHbi78iAldZW/N7HxCmWv8NlxU76X6EBza6uQPwiz3aWr3lbZAE9BQVbU1T9fyW9s11auEunciP6eTrv7IYb1318SAJ5oPj2bDqMS2qr4BpdwMHw0ny6OfNJJfPlbV66J6FfM69sgxNKIgPlBW4tcYsDOXZf2+9uOLclKZJeEg9u8oQKBgQD4KDberLO+WdHs1ctgBEvQ1cdbTxcvXmlwuPo4gMOW7y6xPaC6hQil+ngnLTOTbIbWkRfYDJ0MmkUdCLmaoh6d3VecWOFny3OjpdZmlu/2DWdwLDBbBm4QrjNIEWVL682+Yx3CeiEooakD01NEVfAXWVGqvjkQRXcC1vkTcOh1UQKBgQDTIwZs9yFAW56UVlj54DGdvYq+Wcfm//yPP0tLwShZs7EnZO/a9CJ3Hx7T5wWUIYOKi5r+hLD9/HV3l/Wn8YU/nF+HvIZ6D83Wi+jbytXn15cA4v1uAQPd/JkFxoVIclbIoi8KT3NNZoyYAcpf8qUY4/mCsgPWQG7pxpTbkImZmQKBgAUhd7j04yZ9XuzBotnSXOrtcJi/suAE2LM+79V5PBID0fhSKN+SXpJn8lIFCjrzFHqdN1v9LsYS7njiNEwxK+bhq60M3WQy/aoVhLBMUJvKvSfBkiJfqKjpoR/NVEXUk0VrcTMTKVpYtVDh7qfMCxHAhAK+uhlPBl3lqfKv8+5xAoGAJBreM24DQqUoNgeaCYjcEzRhcHGb7JWj078oGhk61DQVPD5XgeKzrhklRWlBG/arLtr2s0Vvms88KypSONjlR15vO5/ygxcGxQSB2mwSLiZAzUcDvXbw+VoDtOggzKW4z3BkiOfTOM4rhqbvu36b2ujJNx9H5rB4BwQNWoqZ7SkCgYA0W1tXV92Uh+h6DEBciPChfiCjJctNpnvDUx/QZGYm5sIp6I95dF0Qu2ufQNle2zzcPz/VRnei3zkF5ZQyBcBNvd65wqGoS0bub84Qi6BXJUmIXt72HvRsRH+ztzD8gJKWBES6kS9HucdGWCmq8WOPrWBbLRmKqrLZphSqu7CgCg==', 'base64'), format: 'der', type: 'pkcs8' }), diff --git a/web/config.js b/web/config.js index cb16125..f295722 100644 --- a/web/config.js +++ b/web/config.js @@ -1,6 +1,6 @@ const config = { - rsaPublic: 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsB3/iPQiU6nvQhTMfTctJb4uCazd4Vbw0pSqPa/R2wRo5i3m8qqCQa1roTJ/n3aR1wmrR4KY7ntnMVlTWza75PB2OWAY0aKWZbAj5dsrfoT6GrCo72IXQpVGCn2RerddOQs/1rART3Y0nsL3K1SnVf0yQYh3QbWqd8if0Gpa8bBaL0Of1WnXoJyUpS3p9agbXIimdP6UayNtKh/VmkztvXr+36/f4s4zZN2hhMNeRJePpQ1MtAf+oSqlyaeYiFr5w6vphkGwNRn13oqmiXzcH4t4jSxFmEPLpZozz7JyEncOU0yBTj6iyCsrIegJkabLTqJkOXn25c2QbsddPfWvcwIDAQAB', - wsAddress: 'ws://46.232.60.69:8088', + rsaPublic: 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzKsY85qZKPbpqclhaEYz9CXHUqTwXeyCYnyOVQfrDY2hao5kAqplU1NKp9wocg81e1ECFTMm75QFJhDL/Z/L6g76a1saFGd0flpZ2q55HtdIbeHmhVZd2C+CDfsfvoUtLiFSvKtlbxaE0hOn3pcurVFO6Re8QCB7PU7Rf4jdvylweFjQvf78oPG9rI1uzpPwZYfdh23HbYIp/U/PNilIjE9dNir7a4zfGvoFsXzpS3eL6M3ZpHBG7TqUZIexIJDH+CmnpUTs13j4ynLfCv2BqYcvrOlvGlS0fD8exJjmog7dCWgciWPtcI2G+S60EN6ouKs0lqUm9kDYltnIwWSGaQIDAQAB', + wsAddress: 'ws://127.0.0.1:8088', debug: true }; diff --git a/web/main.js b/web/main.js index 7106b24..b57372d 100644 --- a/web/main.js +++ b/web/main.js @@ -1,5 +1,39 @@ import config from './config.js'; +// 多房间状态管理 +let roomsData = []; +let activeRoomIndex = -1; +function getNewRoomData() { + return { room: '', userList: [], userMap: {}, myId: null, myName: '', chat: null, messages: [] }; +} +// 切换房间并恢复上下文,更新 UI +function switchRoom(index) { + if (index < 0 || index >= roomsData.length) return; + activeRoomIndex = index; + const rd = roomsData[index]; + currentRoom = rd.room; + userList = rd.userList; + userMap = rd.userMap; + myId = rd.myId; + myName = rd.myName; + chat = rd.chat; + renderRooms(index); + renderMainHeader(); + renderUserList(); + renderChatArea(); +} + +// 渲染当前房间消息区 +function renderChatArea() { + const chatArea = document.getElementById('chat-area'); + if (!chatArea) return; + chatArea.innerHTML = ''; + roomsData[activeRoomIndex].messages.forEach(m => { + if (m.type === 'me') addMsg(m.text, true); + else addOtherMsg(m.text, m.name, m.avatar, true); + }); +} + // 生成简易SVG头像 function getSvgAvatar(text, size = 42) { const color = "#30a8f7"; @@ -35,9 +69,10 @@ function escapeHTML(str) { function renderUserList() { const userListEl = document.getElementById('member-list'); userListEl.innerHTML = ''; - // 不再提前 return,让 main-header 始终刷新 - let me = userList.find(u => u.clientId === myId); - let others = userList.filter(u => u.clientId !== myId); + const rd = roomsData[activeRoomIndex]; + if (!rd) return; + let me = rd.userList.find(u => u.clientId === rd.myId); + let others = rd.userList.filter(u => u.clientId !== rd.myId); if (me) userListEl.appendChild(createUserItem(me, true)); others.forEach(u => userListEl.appendChild(createUserItem(u, false))); renderMainHeader(); // 在线用户变化时刷新 @@ -57,27 +92,33 @@ function createUserItem(user, isMe) { // 处理服务器推送的在线用户列表 function handleClientList(list, selfId) { - userList = list; - userMap = {}; - userList.forEach(u => { userMap[u.clientId] = u; }); - myId = selfId; + const rd = roomsData[activeRoomIndex]; + if (!rd) return; + rd.userList = list; + rd.userMap = {}; + list.forEach(u => { rd.userMap[u.clientId] = u; }); + rd.myId = selfId; renderUserList(); } // 新用户上线或资料变更 function handleClientSecured(user) { - let idx = userList.findIndex(u => u.clientId === user.clientId); + const rd = roomsData[activeRoomIndex]; + if (!rd) return; + let idx = rd.userList.findIndex(u => u.clientId === user.clientId); if (idx === -1) { - userList.push(user); + rd.userList.push(user); } else { - userList[idx] = user; + rd.userList[idx] = user; } - userMap[user.clientId] = user; + rd.userMap[user.clientId] = user; renderUserList(); } // 用户下线 function handleClientLeft(clientId) { - userList = userList.filter(u => u.clientId !== clientId); - delete userMap[clientId]; + const rd = roomsData[activeRoomIndex]; + if (!rd) return; + rd.userList = rd.userList.filter(u => u.clientId !== clientId); + delete rd.userMap[clientId]; renderUserList(); } @@ -95,7 +136,9 @@ function setStatus(text) { statusBar.innerText = text; } -function addMsg(text) { +function addMsg(text, isHistory = false) { + // 只有新消息才记录 + if (!isHistory && activeRoomIndex >= 0) roomsData[activeRoomIndex].messages.push({ type: 'me', text }); const chatArea = document.getElementById('chat-area'); if (!chatArea) return; const div = document.createElement('div'); @@ -107,7 +150,9 @@ function addMsg(text) { chatArea.scrollTop = chatArea.scrollHeight; } -function addOtherMsg(msg, name = '匿名', avatar = '') { +function addOtherMsg(msg, name = '匿名', avatar = '', isHistory = false) { + // 只有新消息才记录 + if (!isHistory && activeRoomIndex >= 0) roomsData[activeRoomIndex].messages.push({ type: 'other', text: msg, name, avatar }); const chatArea = document.getElementById('chat-area'); if (!chatArea) return; const bubbleWrap = document.createElement('div'); @@ -126,40 +171,158 @@ function addOtherMsg(msg, name = '匿名', avatar = '') { chatArea.scrollTop = chatArea.scrollHeight; } -// 占位房间和成员数量,实际项目请用你的数据替换 -const ROOM_COUNT = 4; -const MEMBER_COUNT = 8; - -// 渲染房间列表(UI占位) +// 渲染房间列表(动态多房间) function renderRooms(activeId = 0) { - const roomList = document.getElementById("room-list"); - roomList.innerHTML = ""; - for (let i = 0; i < ROOM_COUNT; i++) { - const div = document.createElement("div"); - div.className = "room" + (i === activeId ? " active" : ""); + const roomList = document.getElementById('room-list'); + roomList.innerHTML = ''; + roomsData.forEach((rd, i) => { + const div = document.createElement('div'); + div.className = 'room' + (i === activeId ? ' active' : ''); div.innerHTML = `
-
#Room ${i+1}
-
+
#${escapeHTML(rd.room)}
-
`; - div.onclick = () => { - renderRooms(i); - renderMainHeader(i); - clearChat(); - }; + div.onclick = () => switchRoom(i); roomList.appendChild(div); - } + }); +} + +// 登录表单提交处理 +function loginFormHandler(modal) { + return function(e) { + e.preventDefault(); + const username = document.getElementById('username').value.trim(); + const room = document.getElementById('room').value.trim(); + const password = document.getElementById('password').value.trim(); + if (!username || !room) { + alert('请输入用户名和房间名'); + return; + } + // 生成房间数据并切换 + const newRd = getNewRoomData(); + newRd.room = room; + newRd.myName = username; + roomsData.push(newRd); + const idx = roomsData.length - 1; + switchRoom(idx); + // 更新侧边栏 + const sidebarUsername = document.getElementById('sidebar-username'); + if (sidebarUsername) sidebarUsername.textContent = username; + const avatarEl = document.getElementById('sidebar-user-avatar'); + if (avatarEl) avatarEl.innerHTML = getSvgAvatar(username, 44); + // 隐藏登录界面 + if (modal) modal.remove(); + else document.getElementById('login-container').style.display = 'none'; + document.getElementById('chat-container').style.display = ''; + setStatus('正在连接...'); + // 初始化 ChatCrypt + const callbacks = { + onServerClosed: () => setStatus('服务器连接关闭'), + onServerSecured: () => setStatus('与服务器安全连接已建立'), + onClientSecured: (user) => handleClientSecured(user), + onClientList: (list, selfId) => handleClientList(list, selfId), + onClientLeft: (clientId) => handleClientLeft(clientId), + onClientMessage: (msg) => { + if (msg.username === newRd.myName) return; + roomsData[idx].messages.push({ type: 'other', text: msg.data, name: msg.username, avatar: msg.username }); + if (activeRoomIndex === idx) renderChatArea(); + } + }; + chat = new window.ChatCrypt(config, callbacks); + chat.setCredentials(username, room, password); + chat.connect(); + roomsData[idx].chat = chat; + }; +} + +// 打开新房间登录模态 +function openLoginModal() { + const loginContainer = document.getElementById('login-container'); + // 创建一个新的modal,内容copy(不是复用)初始登录页面 + const modal = document.createElement('div'); + modal.className = 'login-modal'; + // 复制登录表单内容 + modal.innerHTML = ` +
+
+ +

Sign in to Chatroom

+

请输入用户名、房间名,房间密码可选

+
+
+ + +
+
+ + +
+
+ + +
+ +
+
+ `; + document.body.appendChild(modal); + // 关闭按钮 + modal.querySelector('.login-modal-close').onclick = () => modal.remove(); + // 表单提交逻辑,复用loginFormHandler但传入modal + const form = modal.querySelector('#login-form-modal'); + form.addEventListener('submit', function(e) { + e.preventDefault(); + // 获取输入 + const username = document.getElementById('username-modal').value.trim(); + const room = document.getElementById('room-modal').value.trim(); + const password = document.getElementById('password-modal').value.trim(); + if (!username || !room) { + alert('请输入用户名和房间名'); + return; + } + // 生成房间数据并切换 + const newRd = getNewRoomData(); + newRd.room = room; + newRd.myName = username; + roomsData.push(newRd); + const idx = roomsData.length - 1; + switchRoom(idx); + // 更新侧边栏 + const sidebarUsername = document.getElementById('sidebar-username'); + if (sidebarUsername) sidebarUsername.textContent = username; + const avatarEl = document.getElementById('sidebar-user-avatar'); + if (avatarEl) avatarEl.innerHTML = getSvgAvatar(username, 44); + // 关闭modal + modal.remove(); + document.getElementById('chat-container').style.display = ''; + setStatus('正在连接...'); + // 初始化 ChatCrypt + const callbacks = { + onServerClosed: () => setStatus('服务器连接关闭'), + onServerSecured: () => setStatus('与服务器安全连接已建立'), + onClientSecured: (user) => handleClientSecured(user), + onClientList: (list, selfId) => handleClientList(list, selfId), + onClientLeft: (clientId) => handleClientLeft(clientId), + onClientMessage: (msg) => { + if (msg.username === newRd.myName) return; + roomsData[idx].messages.push({ type: 'other', text: msg.data, name: msg.username, avatar: msg.username }); + if (activeRoomIndex === idx) renderChatArea(); + } + }; + chat = new window.ChatCrypt(config, callbacks); + chat.setCredentials(username, room, password); + chat.connect(); + roomsData[idx].chat = chat; + }); } // 渲染主面板房间头部(UI占位) function renderMainHeader() { - // 取当前房间名和在线人数 - let roomName = currentRoom || 'Room'; - // 如果用户列表不包含自己,则显示人数+1 - let onlineCount = userList && userList.length ? userList.length : 0; - if (!userList.some(u => u.clientId === myId)) { + const rd = roomsData[activeRoomIndex]; + let roomName = rd ? rd.room : 'Room'; + let onlineCount = rd && rd.userList ? rd.userList.length : 0; + if (rd && !rd.userList.some(u => u.clientId === rd.myId)) { onlineCount += 1; } document.getElementById("main-header").innerHTML = ` @@ -328,42 +491,11 @@ window.addEventListener('DOMContentLoaded', () => { const chatContainer = document.getElementById('chat-container'); const loginForm = document.getElementById('login-form'); if (loginForm) { - loginForm.addEventListener('submit', function(e) { - e.preventDefault(); - const username = document.getElementById('username').value.trim(); - const room = document.getElementById('room').value.trim(); - const password = document.getElementById('password').value.trim(); - if (!username || !room) { - alert('请输入用户名和房间名'); - return; - } - myName = username; - currentRoom = room; - loginContainer.style.display = 'none'; - chatContainer.style.display = ''; - const sidebarUsername = document.getElementById('sidebar-username'); - if (sidebarUsername) sidebarUsername.textContent = username; - const avatar = document.getElementById('sidebar-user-avatar'); - if (avatar) avatar.innerHTML = getSvgAvatar(username, 44); - setStatus('正在连接...'); - renderMainHeader(); // 登录后立即渲染一次 - // 初始化 ChatCrypt - const callbacks = { - onServerClosed: () => setStatus('服务器连接关闭'), - onServerSecured: () => setStatus('与服务器安全连接已建立'), - onClientSecured: (user) => handleClientSecured(user), - onClientList: (list, selfId) => handleClientList(list, selfId), - onClientLeft: (clientId) => handleClientLeft(clientId), - onClientMessage: (msg) => { - if (msg.username === myName) return; - addOtherMsg(msg.data, msg.username, msg.username); - } - }; - chat = new window.ChatCrypt(config, callbacks); - chat.setCredentials(username, room, password); - chat.connect(); - }); + loginForm.addEventListener('submit', loginFormHandler(null)); } + // 绑定“进入新房间”按钮 + const joinBtn = document.querySelector('.join-room'); + if (joinBtn) joinBtn.onclick = openLoginModal; setupInputPlaceholder(); setupMoreBtnMenu(); @@ -388,8 +520,8 @@ window.addEventListener('DOMContentLoaded', () => { }); // 初始化 -renderRooms(); +renderRooms(activeRoomIndex); renderMainHeader(); renderUserList(); setupTabs(); -clearChat(); \ No newline at end of file +renderChatArea(); \ No newline at end of file