// 聊天室房间在线状态渲染工具,抽离右侧主面板与手机抽屉可共用的纯前端逻辑。 import { escapeHtml } from "./html.js"; const EMPTY_ROOMS_HTML = '
暂无房间
'; /** * 转换接口房间数据,过滤异常房间编号。 * * @param {Record} room * @returns {{id:number,name:string,online:number,doorOpen:boolean}|null} */ export function normalizeRoomStatus(room) { const roomId = Number.parseInt(room?.id, 10); if (!Number.isInteger(roomId)) { return null; } return { id: roomId, name: String(room?.name ?? ""), online: Math.max(Number.parseInt(room?.online, 10) || 0, 0), doorOpen: Boolean(room?.door_open), }; } /** * 生成房间跳转地址,默认保持现有 `/room/{id}` 路径。 * * @param {number} roomId * @param {(roomId:number) => string} [roomUrlResolver] * @returns {string} */ export function resolveRoomUrl(roomId, roomUrlResolver = undefined) { return typeof roomUrlResolver === "function" ? String(roomUrlResolver(roomId)) : `/room/${roomId}`; } /** * 生成可放入 onclick 属性的安全跳转语句。 * * @param {number} roomId * @param {(roomId:number) => string} [roomUrlResolver] * @returns {string} */ function buildRoomClickHandler(roomId, roomUrlResolver = undefined) { const safeUrlLiteral = escapeHtml(JSON.stringify(resolveRoomUrl(roomId, roomUrlResolver))); return `onclick="location.href=${safeUrlLiteral}"`; } /** * 渲染单个房间在线状态行。 * * @param {{id:number,name:string,online:number,doorOpen:boolean}} room * @param {{currentRoomId?:number|null, variant?:'desktop'|'mobile', roomUrlResolver?:(roomId:number)=>string}} options * @returns {string} */ export function renderRoomStatusRow(room, options = {}) { const currentRoomId = Number.parseInt(options.currentRoomId, 10); const isCurrent = Number.isInteger(currentRoomId) && room.id === currentRoomId; const variant = options.variant === "mobile" ? "mobile" : "desktop"; const safeRoomName = escapeHtml(room.name); const bg = isCurrent ? "#ecf4ff" : "#fff"; const nameColor = isCurrent ? "#336699" : (room.doorOpen ? "#444" : "#bbb"); const currentTag = isCurrent ? (variant === "mobile" ? '当前' : '当前') : ""; const clickHandler = isCurrent ? "" : buildRoomClickHandler(room.id, options.roomUrlResolver); const badge = room.online > 0 ? `${room.online}${variant === "mobile" ? "" : " "}人` : ``; if (variant === "mobile") { return `
${safeRoomName}${currentTag} ${badge}
`; } const border = isCurrent ? "#aac5f0" : "#e0eaf5"; return `
${safeRoomName}${currentTag} ${badge}
`; } /** * 渲染房间在线状态列表 HTML。 * * @param {{rooms?: Array>}} data * @param {{currentRoomId?:number|null, variant?:'desktop'|'mobile', emptyHtml?:string, roomUrlResolver?:(roomId:number)=>string}} options * @returns {string} */ export function renderRoomsOnlineStatus(data, options = {}) { const rooms = Array.isArray(data?.rooms) ? data.rooms : []; const rows = rooms .map((room) => normalizeRoomStatus(room)) .filter(Boolean) .map((room) => renderRoomStatusRow(room, options)) .join(""); return rows || options.emptyHtml || EMPTY_ROOMS_HTML; } /** * 将房间在线状态列表渲染到指定容器。 * * @param {{rooms?: Array>}} data * @param {HTMLElement} container * @param {{currentRoomId?:number|null, variant?:'desktop'|'mobile', emptyHtml?:string, roomUrlResolver?:(roomId:number)=>string}} options * @returns {void} */ export function renderRoomsOnlineStatusToContainer(data, container, options = {}) { container.innerHTML = renderRoomsOnlineStatus(data, options); }