From 7f99fc5a5bc25fe2d2b9449dec385f247724f03a Mon Sep 17 00:00:00 2001
From: shuaiplus <2327005759@qq.com>
Date: Mon, 5 May 2025 18:36:01 +0800
Subject: [PATCH] =?UTF-8?q?xss,=E5=88=A0=E6=8E=89=E5=A4=B4=E5=83=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
web/main.js | 88 ++++++++++++-----------------------
web/style.css | 124 ++++++++++++--------------------------------------
2 files changed, 57 insertions(+), 155 deletions(-)
diff --git a/web/main.js b/web/main.js
index 3c48461..17f2d80 100644
--- a/web/main.js
+++ b/web/main.js
@@ -1,15 +1,5 @@
import config from './config.js';
-// 头像尺寸常量
-const AVATAR_SIZE_SIDEBAR = 44;
-const AVATAR_SIZE_MEMBER = 38;
-const AVATAR_SIZE_MSG = 32;
-
-// 统一头像渲染工具函数
-function renderAvatar(name, size) {
- return getSvgAvatar(name, size);
-}
-
// 多房间状态管理
let roomsData = [];
let activeRoomIndex = -1;
@@ -24,8 +14,6 @@ function switchRoom(index) {
// 同步更新sidebar用户名和头像
const sidebarUsername = document.getElementById('sidebar-username');
if (sidebarUsername) sidebarUsername.textContent = rd.myName;
- const avatarEl = document.getElementById('sidebar-user-avatar');
- if (avatarEl) avatarEl.innerHTML = renderAvatar(rd.myName, AVATAR_SIZE_SIDEBAR);
renderRooms(index);
renderMainHeader();
renderUserList();
@@ -58,13 +46,6 @@ function stringToColor(str) {
return `hsl(${h}, 70%, 75%)`;
}
-// 生成 Dicebear Micah 风格头像,背景色与用户名相关
-function getSvgAvatar(text, size = 42) {
- const seed = encodeURIComponent(text || 'User');
- const bg = stringToColor(text || 'User');
- return `
`;
-}
-
// 获取用户名首字母(前2位大写)
function getInitials(name) {
if (!name) return '?';
@@ -92,13 +73,13 @@ function renderUserList() {
function createUserItem(user, isMe) {
let div = document.createElement('div');
div.className = 'member' + (isMe ? ' me' : '');
+ const safeUsername = escapeHTML(user.username);
div.innerHTML = `
- ${renderAvatar(user.username, AVATAR_SIZE_MEMBER)}
+
-
${escapeHTML(user.username)}${isMe ? ' (我)' : ''}
+
${safeUsername}${isMe ? ' (我)' : ''}
`;
- // 不可点击
return div;
}
@@ -108,7 +89,9 @@ function handleClientList(idx, list, selfId) {
if (!rd) return;
rd.userList = list;
rd.userMap = {};
- list.forEach(u => { rd.userMap[u.clientId] = u; });
+ list.forEach(u => {
+ rd.userMap[u.clientId] = u;
+ });
rd.myId = selfId;
if (activeRoomIndex === idx) renderUserList();
}
@@ -154,7 +137,9 @@ function addMsg(text, isHistory = false) {
div.className = 'bubble me';
const now = new Date();
const time = now.getHours().toString().padStart(2, '0') + ':' + now.getMinutes().toString().padStart(2, '0');
- div.innerHTML = `${text}${time}`;
+ // 先escape再处理换行
+ const safeText = escapeHTML(text).replace(/\n/g, '
');
+ div.innerHTML = `${safeText}${time}`;
chatArea.appendChild(div);
chatArea.scrollTop = chatArea.scrollHeight;
}
@@ -166,12 +151,15 @@ function addOtherMsg(msg, name = 'Anonymous', avatar = '', isHistory = false) {
if (!chatArea) return;
const bubbleWrap = document.createElement('div');
bubbleWrap.className = 'bubble-other-wrap';
+ // 先escape再处理换行
+ const safeMsg = escapeHTML(msg).replace(/\n/g, '
');
+ const safeName = escapeHTML(name);
bubbleWrap.innerHTML = `
- ${renderAvatar(avatar || name, AVATAR_SIZE_MSG)}
+
-
${name}
-
${msg.replace(/\n/g, '
')}
+
${safeName}
+
${safeMsg}
${(new Date()).getHours().toString().padStart(2, '0')}:${(new Date()).getMinutes().toString().padStart(2, '0')}
@@ -187,9 +175,10 @@ function renderRooms(activeId = 0) {
roomsData.forEach((rd, i) => {
const div = document.createElement('div');
div.className = 'room' + (i === activeId ? ' active' : '');
+ const safeRoom = escapeHTML(rd.room);
div.innerHTML = `
-
#${escapeHTML(rd.room)}
+
#${safeRoom}
`;
div.onclick = () => switchRoom(i);
@@ -219,8 +208,6 @@ function joinRoom(username, room, password, modal = null) {
// 更新侧边栏
const sidebarUsername = document.getElementById('sidebar-username');
if (sidebarUsername) sidebarUsername.textContent = username;
- const avatarEl = document.getElementById('sidebar-user-avatar');
- if (avatarEl) avatarEl.innerHTML = renderAvatar(username, AVATAR_SIZE_SIDEBAR);
// 隐藏登录界面或关闭modal
if (modal) modal.remove();
else document.getElementById('login-container').style.display = 'none';
@@ -297,9 +284,10 @@ function renderMainHeader() {
if (rd && !rd.userList.some(u => u.clientId === rd.myId)) {
onlineCount += 1;
}
+ const safeRoomName = escapeHTML(roomName);
document.getElementById("main-header").innerHTML = `
-
#${escapeHTML(roomName)}
+
#${safeRoomName}
${onlineCount} members
@@ -395,7 +383,7 @@ function setupMoreBtnMenu() {
}
}
function unbindMouseMove() {
- if (isMouseMoveBound) {
+ if (!isMouseMoveBound) {
window.removeEventListener('mousemove', onMouseMove);
isMouseMoveBound = false;
}
@@ -435,26 +423,7 @@ function setupMoreBtnMenu() {
if (e.target.dataset.action === 'share') {
alert('分享功能待实现');
} else if (e.target.dataset.action === 'exit') {
- // 退出当前房间
- if (activeRoomIndex >= 0 && roomsData[activeRoomIndex]) {
- // 优先彻底销毁连接
- const chatInst = roomsData[activeRoomIndex].chat;
- if (chatInst && typeof chatInst.destruct === 'function') {
- chatInst.destruct();
- } else if (chatInst && typeof chatInst.disconnect === 'function') {
- chatInst.disconnect();
- }
- roomsData[activeRoomIndex].chat = null;
- // 移除房间
- roomsData.splice(activeRoomIndex, 1);
- if (roomsData.length > 0) {
- // 切换到第一个房间
- switchRoom(0);
- } else {
- // 没有房间了,回到登录
- location.reload();
- }
- }
+ // 退出功能待实现
}
closeMenu();
}
@@ -475,6 +444,7 @@ function setupMoreBtnMenu() {
});
}
+// 页面初始化
window.addEventListener('DOMContentLoaded', () => {
const loginContainer = document.getElementById('login-container');
const chatContainer = document.getElementById('chat-container');
@@ -507,11 +477,11 @@ window.addEventListener('DOMContentLoaded', () => {
}
});
}
-});
-// 初始化
-renderRooms(activeRoomIndex);
-renderMainHeader();
-renderUserList();
-setupTabs();
-renderChatArea();
\ No newline at end of file
+ // 初始化
+ renderRooms(activeRoomIndex);
+ renderMainHeader();
+ renderUserList();
+ setupTabs();
+ renderChatArea();
+});
\ No newline at end of file
diff --git a/web/style.css b/web/style.css
index 0298974..a0ed68b 100644
--- a/web/style.css
+++ b/web/style.css
@@ -20,21 +20,6 @@ body {
background: #fff;
border-bottom: 1px solid #e0e0e0;
}
-.sidebar-user .avatar {
- width: 44px;
- height: 44px;
- border-radius: 50%;
- margin-right: 12px;
- background: #30a8f7;
- display: flex;
- align-items: center;
- justify-content: center;
- color: #fff;
- font-weight: bold;
- font-size: 22px;
- user-select: none;
- overflow: hidden;
-}
.sidebar-username {
font-weight: 600;
font-size: 17px;
@@ -103,23 +88,6 @@ body {
box-shadow: 0 2px 8px #b3d8f7a0;
}
-.room .avatar {
- width: 42px;
- height: 42px;
- border-radius: 50%;
- margin-right: 12px;
- display: flex;
- align-items: center;
- justify-content: center;
- background: #30a8f7;
- color: #fff;
- font-weight: bold;
- font-size: 18px;
- user-select: none;
- overflow: hidden;
- flex-shrink: 0;
-}
-
.room .info {
flex: 1;
}
@@ -173,22 +141,6 @@ body {
box-shadow: 0 2px 8px #e0e0e040;
}
-.main-header .avatar {
- width: 40px;
- height: 40px;
- border-radius: 50%;
- background: #30a8f7;
- margin-right: 12px;
- display: flex;
- align-items: center;
- justify-content: center;
- color: #fff;
- font-weight: bold;
- font-size: 20px;
- user-select: none;
- overflow: hidden;
-}
-
.main-header .group-title {
font-weight: bold;
font-size: 18px;
@@ -308,21 +260,6 @@ body {
align-items: flex-end;
margin-bottom: 12px;
}
-.bubble-other-avatar {
- width: 32px;
- height: 32px;
- border-radius: 50%;
- background: #30a8f7;
- color: #fff;
- font-weight: bold;
- font-size: 16px;
- display: flex;
- align-items: center;
- justify-content: center;
- margin-right: 8px;
- flex-shrink: 0;
- user-select: none;
-}
.bubble-other-main {
display: flex;
flex-direction: column;
@@ -657,22 +594,6 @@ body {
justify-content: space-around;
}
-.rightbar-group .avatar {
- width: 44px;
- height: 44px;
- border-radius: 50%;
- background: #30a8f7;
- margin-right: 12px;
- display: flex;
- align-items: center;
- justify-content: center;
- color: #fff;
- font-weight: bold;
- font-size: 22px;
- user-select: none;
- overflow: hidden;
-}
-
.rightbar .group-info .title {
font-size: 18px;
font-weight: 600;
@@ -735,23 +656,6 @@ body {
box-shadow: 0 2px 8px #b3d8f7a0;
}
-.member .avatar {
- width: 38px;
- height: 38px;
- border-radius: 50%;
- margin-right: 14px;
- background: #30a8f7;
- display: flex;
- align-items: center;
- justify-content: center;
- color: #fff;
- font-weight: bold;
- font-size: 18px;
- user-select: none;
- overflow: hidden;
- flex-shrink: 0;
-}
-
.member .member-info {
flex: 1;
}
@@ -900,4 +804,32 @@ body {
}
.login-modal-close:hover {
color: #30a8f7;
+}
+
+/* 统一所有头像样式为.avatar */
+.avatar {
+ width: 42px !important;
+ height: 42px !important;
+ border-radius: 50% !important;
+ background: #30a8f7 !important;
+ display: flex !important;
+ align-items: center !important;
+ justify-content: center !important;
+ color: #fff !important;
+ font-weight: bold !important;
+ font-size: 20px !important;
+ user-select: none !important;
+ overflow: hidden !important;
+ flex-shrink: 0 !important;
+ object-fit: cover !important;
+ margin-right: 12px !important;
+ box-sizing: border-box !important;
+}
+
+.avatar img {
+ width: 100% !important;
+ height: 100% !important;
+ border-radius: 50% !important;
+ object-fit: cover !important;
+ display: block !important;
}
\ No newline at end of file