迁移手机抽屉脚本到Vite模块
This commit is contained in:
@@ -7,7 +7,16 @@ export { applyFontSize, bindChatFontSizeControl, CHAT_FONT_SIZE_STORAGE_KEY, res
|
||||
export { bindChatImageUploadControl } from "./chat-room/image-upload.js";
|
||||
export { bindFriendPanelControls, closeFriendPanel, friendSearch, loadFriends, openFriendPanel } from "./chat-room/friend-panel.js";
|
||||
export { closeChatImageLightbox, initChatImageLightboxEvents, openChatImageLightbox } from "./chat-room/lightbox.js";
|
||||
export { bindMobileDrawerControls } from "./chat-room/mobile-drawer.js";
|
||||
export {
|
||||
bindMobileDrawerControls,
|
||||
closeMobileDrawer,
|
||||
loadMobileRoomList,
|
||||
openMobileDrawer,
|
||||
renderMobileRoomList,
|
||||
renderMobileUserList,
|
||||
scheduleRenderMobileUserList,
|
||||
switchMobileTab,
|
||||
} from "./chat-room/mobile-drawer.js";
|
||||
export { bindMarriageStatusControls } from "./chat-room/marriage-status.js";
|
||||
export { bindToolbarControls } from "./chat-room/toolbar.js";
|
||||
export { bindWelcomeMenuControls } from "./chat-room/welcome-menu.js";
|
||||
@@ -66,7 +75,16 @@ import { applyFontSize, bindChatFontSizeControl, CHAT_FONT_SIZE_STORAGE_KEY, res
|
||||
import { bindChatImageUploadControl } from "./chat-room/image-upload.js";
|
||||
import { bindFriendPanelControls, closeFriendPanel, friendSearch, loadFriends, openFriendPanel } from "./chat-room/friend-panel.js";
|
||||
import { closeChatImageLightbox, initChatImageLightboxEvents, openChatImageLightbox } from "./chat-room/lightbox.js";
|
||||
import { bindMobileDrawerControls } from "./chat-room/mobile-drawer.js";
|
||||
import {
|
||||
bindMobileDrawerControls,
|
||||
closeMobileDrawer,
|
||||
loadMobileRoomList,
|
||||
openMobileDrawer,
|
||||
renderMobileRoomList,
|
||||
renderMobileUserList,
|
||||
scheduleRenderMobileUserList,
|
||||
switchMobileTab,
|
||||
} from "./chat-room/mobile-drawer.js";
|
||||
import { bindMarriageStatusControls } from "./chat-room/marriage-status.js";
|
||||
import { bindToolbarControls } from "./chat-room/toolbar.js";
|
||||
import { bindWelcomeMenuControls } from "./chat-room/welcome-menu.js";
|
||||
@@ -134,6 +152,13 @@ if (typeof window !== "undefined") {
|
||||
loadFriends,
|
||||
openFriendPanel,
|
||||
bindMobileDrawerControls,
|
||||
closeMobileDrawer,
|
||||
loadMobileRoomList,
|
||||
openMobileDrawer,
|
||||
renderMobileRoomList,
|
||||
renderMobileUserList,
|
||||
scheduleRenderMobileUserList,
|
||||
switchMobileTab,
|
||||
bindToolbarControls,
|
||||
bindWelcomeMenuControls,
|
||||
bindAdminMenuControls,
|
||||
@@ -190,6 +215,13 @@ if (typeof window !== "undefined") {
|
||||
window.closeFriendPanel = closeFriendPanel;
|
||||
window.friendSearch = friendSearch;
|
||||
window.openFriendPanel = openFriendPanel;
|
||||
window.closeMobileDrawer = closeMobileDrawer;
|
||||
window.loadMobileRoomList = loadMobileRoomList;
|
||||
window.openMobileDrawer = openMobileDrawer;
|
||||
window.renderMobileRoomList = renderMobileRoomList;
|
||||
window.renderMobileUserList = renderMobileUserList;
|
||||
window.scheduleRenderMobileUserList = scheduleRenderMobileUserList;
|
||||
window.switchMobileTab = switchMobileTab;
|
||||
window.closeAdminBaccaratLossCoverModal = closeAdminBaccaratLossCoverModal;
|
||||
window.closeCurrentBaccaratLossCoverEvent = closeCurrentBaccaratLossCoverEvent;
|
||||
window.openAdminBaccaratLossCoverModal = openAdminBaccaratLossCoverModal;
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
// 手机端抽屉基础控件事件绑定,替代浮动按钮、关闭按钮与名单 tab 的内联事件。
|
||||
// 手机端抽屉业务模块,承接工具菜单、在线名单和房间列表的 Blade 内联脚本。
|
||||
|
||||
import { escapeHtml } from "./html.js";
|
||||
import { renderRoomsOnlineStatusToContainer } from "./rooms.js";
|
||||
|
||||
let mobileDrawerEventsBound = false;
|
||||
let mobileDrawerOpen = null;
|
||||
let mobileUserListRenderTimer = null;
|
||||
let mobileRoomsOnlineStatusCache = null;
|
||||
let mobileRoomsOnlineStatusCacheAt = 0;
|
||||
|
||||
const MOBILE_ROOMS_ONLINE_STATUS_CACHE_TTL = 10000;
|
||||
|
||||
/**
|
||||
* 执行手机抽屉工具入口动作。
|
||||
@@ -26,6 +35,7 @@ function runMobileToolAction(action) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 兼容设置弹窗尚未迁移为全局函数的页面。
|
||||
const settingsModal = document.getElementById("settings-modal");
|
||||
if (settingsModal) {
|
||||
settingsModal.style.display = "flex";
|
||||
@@ -52,6 +62,208 @@ function confirmMobileLeaveRoom() {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开指定手机端抽屉。
|
||||
*
|
||||
* @param {"toolbar"|"users"|string} which 抽屉名称
|
||||
* @returns {void}
|
||||
*/
|
||||
export function openMobileDrawer(which) {
|
||||
if (!["toolbar", "users"].includes(which)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 切换抽屉时只关闭旧面板,不恢复 body overflow,避免中间态闪烁。
|
||||
if (mobileDrawerOpen && mobileDrawerOpen !== which) {
|
||||
document.getElementById(`mobile-drawer-${mobileDrawerOpen}`)?.classList.remove("open");
|
||||
}
|
||||
|
||||
const drawer = document.getElementById(`mobile-drawer-${which}`);
|
||||
const mask = document.getElementById("mobile-drawer-mask");
|
||||
if (!drawer || !mask) {
|
||||
return;
|
||||
}
|
||||
|
||||
drawer.classList.add("open");
|
||||
mask.classList.add("open");
|
||||
mobileDrawerOpen = which;
|
||||
document.body.style.overflow = "hidden";
|
||||
|
||||
if (which === "users") {
|
||||
renderMobileUserList();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭当前打开的手机端抽屉。
|
||||
*
|
||||
* @param {boolean} resetOverflow 是否恢复 body overflow
|
||||
* @returns {void}
|
||||
*/
|
||||
export function closeMobileDrawer(resetOverflow = true) {
|
||||
if (mobileDrawerOpen) {
|
||||
document.getElementById(`mobile-drawer-${mobileDrawerOpen}`)?.classList.remove("open");
|
||||
mobileDrawerOpen = null;
|
||||
}
|
||||
|
||||
document.getElementById("mobile-drawer-mask")?.classList.remove("open");
|
||||
if (resetOverflow) {
|
||||
document.body.style.overflow = "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换名单抽屉内的用户/房间 Tab。
|
||||
*
|
||||
* @param {"users"|"rooms"|string} tab Tab 名称
|
||||
* @returns {void}
|
||||
*/
|
||||
export function switchMobileTab(tab) {
|
||||
if (!["users", "rooms"].includes(tab)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const panelUsers = document.getElementById("mob-panel-users");
|
||||
const panelRooms = document.getElementById("mob-panel-rooms");
|
||||
const tabUsers = document.getElementById("mob-tab-users");
|
||||
const tabRooms = document.getElementById("mob-tab-rooms");
|
||||
|
||||
if (panelUsers) {
|
||||
panelUsers.style.display = tab === "users" ? "flex" : "none";
|
||||
}
|
||||
if (panelRooms) {
|
||||
panelRooms.style.display = tab === "rooms" ? "block" : "none";
|
||||
}
|
||||
|
||||
syncMobileTabButton(tabUsers, tab === "users");
|
||||
syncMobileTabButton(tabRooms, tab === "rooms");
|
||||
|
||||
if (tab === "rooms") {
|
||||
void loadMobileRoomList();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步手机抽屉 Tab 按钮选中样式。
|
||||
*
|
||||
* @param {HTMLElement|null} button 按钮元素
|
||||
* @param {boolean} active 是否选中
|
||||
* @returns {void}
|
||||
*/
|
||||
function syncMobileTabButton(button, active) {
|
||||
if (!button) {
|
||||
return;
|
||||
}
|
||||
|
||||
button.style.background = active ? "#336699" : "transparent";
|
||||
button.style.color = active ? "#fff" : "#336699";
|
||||
button.style.fontWeight = active ? "bold" : "normal";
|
||||
}
|
||||
|
||||
/**
|
||||
* 将在线用户渲染到手机端名单容器。
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
export function renderMobileUserList() {
|
||||
const onlineUsers = window.onlineUsers;
|
||||
if (!onlineUsers || typeof onlineUsers !== "object") {
|
||||
return;
|
||||
}
|
||||
|
||||
const sortValue = document.getElementById("mob-user-sort-select")?.value || "default";
|
||||
const keyword = (document.getElementById("mob-user-search-input")?.value || "").trim().toLowerCase();
|
||||
const container = document.getElementById("mob-online-users-list");
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof window._renderUserListToContainer === "function") {
|
||||
window._renderUserListToContainer(container, sortValue, keyword);
|
||||
} else {
|
||||
// 降级渲染只展示用户名,保障主渲染函数异常缺失时手机名单仍可用。
|
||||
const users = Object.keys(onlineUsers);
|
||||
container.innerHTML = users.length
|
||||
? users
|
||||
.filter((username) => !keyword || username.toLowerCase().includes(keyword))
|
||||
.map((username) => `<div class="user-item" style="padding:5px 8px;font-size:12px;border-bottom:1px solid #eee;">${escapeHtml(username)}</div>`)
|
||||
.join("")
|
||||
: '<div style="text-align:center;color:#aaa;padding:20px;font-size:12px;">暂无用户</div>';
|
||||
}
|
||||
|
||||
const footerElement = document.getElementById("mob-online-count-footer");
|
||||
if (footerElement) {
|
||||
footerElement.textContent = Object.keys(onlineUsers).length;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 调度手机端在线名单渲染,避免搜索输入时同步重建整份名单。
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
export function scheduleRenderMobileUserList() {
|
||||
if (mobileUserListRenderTimer !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const scheduleRender = window.requestAnimationFrame || ((callback) => window.setTimeout(callback, 16));
|
||||
mobileUserListRenderTimer = scheduleRender(() => {
|
||||
mobileUserListRenderTimer = null;
|
||||
renderMobileUserList();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 拉取房间列表并渲染到手机端房间容器。
|
||||
*
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function loadMobileRoomList() {
|
||||
const container = document.getElementById("mob-rooms-online-list");
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mobileRoomsOnlineStatusCache && Date.now() - mobileRoomsOnlineStatusCacheAt < MOBILE_ROOMS_ONLINE_STATUS_CACHE_TTL) {
|
||||
renderMobileRoomList(mobileRoomsOnlineStatusCache, container);
|
||||
return;
|
||||
}
|
||||
|
||||
const statusUrl = container.getAttribute("data-mobile-rooms-status-url") || "";
|
||||
if (!statusUrl) {
|
||||
container.innerHTML = '<div style="text-align:center;color:#f00;padding:10px;font-size:11px;">缺少房间接口</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
container.innerHTML = '<div style="text-align:center;color:#aaa;padding:16px;font-size:11px;">加载中...</div>';
|
||||
|
||||
try {
|
||||
const response = await fetch(statusUrl);
|
||||
const data = await response.json();
|
||||
mobileRoomsOnlineStatusCache = data;
|
||||
mobileRoomsOnlineStatusCacheAt = Date.now();
|
||||
renderMobileRoomList(data, container);
|
||||
} catch (error) {
|
||||
container.innerHTML = '<div style="text-align:center;color:#f00;padding:10px;font-size:11px;">加载失败</div>';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染手机端房间列表。
|
||||
*
|
||||
* @param {Record<string, unknown>} data 接口返回数据
|
||||
* @param {HTMLElement} container 目标容器
|
||||
* @returns {void}
|
||||
*/
|
||||
export function renderMobileRoomList(data, container) {
|
||||
renderRoomsOnlineStatusToContainer(data, container, {
|
||||
currentRoomId: window.chatContext?.roomId,
|
||||
variant: "mobile",
|
||||
emptyHtml: '<div style="text-align:center;color:#bbb;padding:16px;font-size:11px;">暂无房间</div>',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定手机端抽屉基础控件事件。
|
||||
*
|
||||
@@ -68,23 +280,24 @@ export function bindMobileDrawerControls() {
|
||||
return;
|
||||
}
|
||||
|
||||
// 手机抽屉内多数节点由 Blade 与后续 AJAX 动态生成,统一使用 document 事件代理。
|
||||
const drawerTrigger = event.target.closest("[data-mobile-drawer-open]");
|
||||
if (drawerTrigger) {
|
||||
event.preventDefault();
|
||||
window.openMobileDrawer?.(drawerTrigger.dataset.mobileDrawerOpen);
|
||||
openMobileDrawer(drawerTrigger.dataset.mobileDrawerOpen || "");
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.target.closest("[data-mobile-drawer-close]")) {
|
||||
event.preventDefault();
|
||||
window.closeMobileDrawer?.();
|
||||
closeMobileDrawer();
|
||||
return;
|
||||
}
|
||||
|
||||
const toolAction = event.target.closest("[data-mobile-tool-action]");
|
||||
if (toolAction) {
|
||||
event.preventDefault();
|
||||
window.closeMobileDrawer?.();
|
||||
closeMobileDrawer();
|
||||
|
||||
const action = toolAction.getAttribute("data-mobile-tool-action") || "";
|
||||
if (action === "leave") {
|
||||
@@ -99,7 +312,7 @@ export function bindMobileDrawerControls() {
|
||||
const toolUrl = event.target.closest("[data-mobile-tool-url]");
|
||||
if (toolUrl) {
|
||||
event.preventDefault();
|
||||
window.closeMobileDrawer?.();
|
||||
closeMobileDrawer();
|
||||
|
||||
const url = toolUrl.getAttribute("data-mobile-tool-url");
|
||||
if (url) {
|
||||
@@ -112,7 +325,7 @@ export function bindMobileDrawerControls() {
|
||||
const tabTrigger = event.target.closest("[data-mobile-drawer-tab]");
|
||||
if (tabTrigger) {
|
||||
event.preventDefault();
|
||||
window.switchMobileTab?.(tabTrigger.dataset.mobileDrawerTab);
|
||||
switchMobileTab(tabTrigger.dataset.mobileDrawerTab || "");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -121,7 +334,7 @@ export function bindMobileDrawerControls() {
|
||||
return;
|
||||
}
|
||||
|
||||
window.renderMobileUserList?.();
|
||||
renderMobileUserList();
|
||||
});
|
||||
|
||||
document.addEventListener("input", (event) => {
|
||||
@@ -129,6 +342,12 @@ export function bindMobileDrawerControls() {
|
||||
return;
|
||||
}
|
||||
|
||||
window.scheduleRenderMobileUserList?.();
|
||||
scheduleRenderMobileUserList();
|
||||
});
|
||||
|
||||
window.addEventListener("chatroom:users-updated", () => {
|
||||
if (mobileDrawerOpen === "users") {
|
||||
renderMobileUserList();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -98,235 +98,8 @@
|
||||
|
||||
{{-- 房间面板 --}}
|
||||
<div id="mob-panel-rooms" style="display:none;flex:1;overflow-y:auto;min-height:0;">
|
||||
<div id="mob-rooms-online-list" style="padding:4px 0;">
|
||||
<div id="mob-rooms-online-list" data-mobile-rooms-status-url="{{ route('chat.rooms-online-status') }}" style="padding:4px 0;">
|
||||
<div style="text-align:center;color:#aaa;padding:20px;font-size:12px;">加载中...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- ── 手机端抽屉控制脚本 ── --}}
|
||||
<script>
|
||||
/**
|
||||
* 对手机端抽屉中的动态文本做 HTML 转义,避免直接拼入 innerHTML。
|
||||
*
|
||||
* @param {string} text
|
||||
* @returns {string}
|
||||
*/
|
||||
function escapeMobileDrawerHtml(text) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前打开的抽屉名称:'toolbar' | 'users' | null
|
||||
*
|
||||
* @type {string|null}
|
||||
*/
|
||||
let _mobileDrawerOpen = null;
|
||||
let _mobileUserListRenderTimer = null;
|
||||
let _mobileRoomsOnlineStatusCache = null;
|
||||
let _mobileRoomsOnlineStatusCacheAt = 0;
|
||||
const MOBILE_ROOMS_ONLINE_STATUS_CACHE_TTL = 10000;
|
||||
|
||||
/**
|
||||
* 打开指定抽屉
|
||||
*
|
||||
* @param {'toolbar'|'users'} which
|
||||
*/
|
||||
function openMobileDrawer(which) {
|
||||
// 先关掉已有的抽屉(不重置状态,避免中间态闪烁)
|
||||
if (_mobileDrawerOpen && _mobileDrawerOpen !== which) {
|
||||
document.getElementById('mobile-drawer-' + _mobileDrawerOpen)?.classList.remove('open');
|
||||
}
|
||||
|
||||
const drawer = document.getElementById('mobile-drawer-' + which);
|
||||
const mask = document.getElementById('mobile-drawer-mask');
|
||||
if (!drawer || !mask) return;
|
||||
|
||||
drawer.classList.add('open');
|
||||
mask.classList.add('open');
|
||||
_mobileDrawerOpen = which;
|
||||
document.body.style.overflow = 'hidden';
|
||||
|
||||
// 名单抽屉打开时立即渲染用户列表
|
||||
if (which === 'users') {
|
||||
renderMobileUserList();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭当前打开的抽屉
|
||||
*
|
||||
* @param {boolean} [resetOverflow=true] 是否恢复 body overflow
|
||||
*/
|
||||
function closeMobileDrawer(resetOverflow = true) {
|
||||
if (_mobileDrawerOpen) {
|
||||
document.getElementById('mobile-drawer-' + _mobileDrawerOpen)?.classList.remove('open');
|
||||
_mobileDrawerOpen = null;
|
||||
}
|
||||
document.getElementById('mobile-drawer-mask')?.classList.remove('open');
|
||||
if (resetOverflow) {
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换名单抽屉内的 Tab
|
||||
*
|
||||
* @param {'users'|'rooms'} tab
|
||||
*/
|
||||
function switchMobileTab(tab) {
|
||||
const panelUsers = document.getElementById('mob-panel-users');
|
||||
const panelRooms = document.getElementById('mob-panel-rooms');
|
||||
const tabUsers = document.getElementById('mob-tab-users');
|
||||
const tabRooms = document.getElementById('mob-tab-rooms');
|
||||
|
||||
if (panelUsers) panelUsers.style.display = tab === 'users' ? 'flex' : 'none';
|
||||
if (panelRooms) panelRooms.style.display = tab === 'rooms' ? 'block' : 'none';
|
||||
|
||||
if (tabUsers) {
|
||||
tabUsers.style.background = tab === 'users' ? '#336699' : 'transparent';
|
||||
tabUsers.style.color = tab === 'users' ? '#fff' : '#336699';
|
||||
tabUsers.style.fontWeight = tab === 'users' ? 'bold' : 'normal';
|
||||
}
|
||||
if (tabRooms) {
|
||||
tabRooms.style.background = tab === 'rooms' ? '#336699' : 'transparent';
|
||||
tabRooms.style.color = tab === 'rooms' ? '#fff' : '#336699';
|
||||
tabRooms.style.fontWeight = tab === 'rooms' ? 'bold' : 'normal';
|
||||
}
|
||||
|
||||
if (tab === 'rooms') {
|
||||
_loadMobileRoomList();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将在线用户渲染到手机端名单容器
|
||||
*/
|
||||
function renderMobileUserList() {
|
||||
if (typeof onlineUsers === 'undefined') return;
|
||||
|
||||
const sortVal = document.getElementById('mob-user-sort-select')?.value || 'default';
|
||||
const keyword = (document.getElementById('mob-user-search-input')?.value || '').trim().toLowerCase();
|
||||
const container = document.getElementById('mob-online-users-list');
|
||||
if (!container) return;
|
||||
|
||||
if (typeof _renderUserListToContainer === 'function') {
|
||||
_renderUserListToContainer(container, sortVal, keyword);
|
||||
} else {
|
||||
// 降级:简单渲染用户名列表
|
||||
const users = Object.keys(onlineUsers);
|
||||
container.innerHTML = users.length
|
||||
? users.filter(u => !keyword || u.toLowerCase().includes(keyword))
|
||||
.map(u => `<div class="user-item" style="padding:5px 8px;font-size:12px;border-bottom:1px solid #eee;">${escapeMobileDrawerHtml(u)}</div>`).join('')
|
||||
: '<div style="text-align:center;color:#aaa;padding:20px;font-size:12px;">暂无用户</div>';
|
||||
}
|
||||
|
||||
// 更新在线人数
|
||||
const count = Object.keys(onlineUsers).length;
|
||||
const footerEl = document.getElementById('mob-online-count-footer');
|
||||
if (footerEl) footerEl.textContent = count;
|
||||
}
|
||||
|
||||
/**
|
||||
* 调度手机端在线名单渲染,避免搜索输入时同步重建整份名单。
|
||||
*/
|
||||
function scheduleRenderMobileUserList() {
|
||||
if (_mobileUserListRenderTimer !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const scheduleRender = window.requestAnimationFrame || ((callback) => window.setTimeout(callback, 16));
|
||||
_mobileUserListRenderTimer = scheduleRender(() => {
|
||||
_mobileUserListRenderTimer = null;
|
||||
renderMobileUserList();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 拉取房间列表并渲染到手机端房间容器
|
||||
*/
|
||||
function _loadMobileRoomList() {
|
||||
const container = document.getElementById('mob-rooms-online-list');
|
||||
if (!container) return;
|
||||
|
||||
if (_mobileRoomsOnlineStatusCache && Date.now() - _mobileRoomsOnlineStatusCacheAt < MOBILE_ROOMS_ONLINE_STATUS_CACHE_TTL) {
|
||||
renderMobileRoomList(_mobileRoomsOnlineStatusCache, container);
|
||||
return;
|
||||
}
|
||||
|
||||
container.innerHTML = '<div style="text-align:center;color:#aaa;padding:16px;font-size:11px;">加载中...</div>';
|
||||
|
||||
fetch('{{ route('chat.rooms-online-status') }}')
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
_mobileRoomsOnlineStatusCache = data;
|
||||
_mobileRoomsOnlineStatusCacheAt = Date.now();
|
||||
renderMobileRoomList(data, container);
|
||||
})
|
||||
.catch(() => {
|
||||
container.innerHTML = '<div style="text-align:center;color:#f00;padding:10px;font-size:11px;">加载失败</div>';
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染手机端房间列表。
|
||||
*
|
||||
* @param {Object} data 接口返回数据
|
||||
* @param {HTMLElement} container 目标容器
|
||||
*/
|
||||
function renderMobileRoomList(data, container) {
|
||||
if (window.ChatRoomTools?.renderRoomsOnlineStatusToContainer) {
|
||||
window.ChatRoomTools.renderRoomsOnlineStatusToContainer(data, container, {
|
||||
currentRoomId: window.chatContext?.roomId,
|
||||
variant: 'mobile',
|
||||
emptyHtml: '<div style="text-align:center;color:#bbb;padding:16px;font-size:11px;">暂无房间</div>',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data.rooms || !data.rooms.length) {
|
||||
container.innerHTML = '<div style="text-align:center;color:#bbb;padding:16px;font-size:11px;">暂无房间</div>';
|
||||
return;
|
||||
}
|
||||
const currentRoomId = window.chatContext?.roomId;
|
||||
const roomRows = data.rooms.map(room => {
|
||||
const roomId = Number.parseInt(room.id, 10);
|
||||
if (!Number.isInteger(roomId)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const isCurrent = roomId === currentRoomId;
|
||||
const bg = isCurrent ? '#ecf4ff' : '#fff';
|
||||
const nameColor = isCurrent ? '#336699' : (room.door_open ? '#444' : '#bbb');
|
||||
const safeRoomName = escapeMobileDrawerHtml(String(room.name ?? ''));
|
||||
const safeOnlineCount = Math.max(Number.parseInt(room.online, 10) || 0, 0);
|
||||
const badge = safeOnlineCount > 0
|
||||
? `<span style="background:#e8f5e9;color:#2e7d32;border-radius:8px;padding:0 6px;font-size:10px;font-weight:bold;">${safeOnlineCount}人</span>`
|
||||
: `<span style="background:#f5f5f5;color:#bbb;border-radius:8px;padding:0 6px;font-size:10px;">空</span>`;
|
||||
const currentTag = isCurrent ? `<span style="font-size:9px;color:#7090b0;margin-left:3px;">当前</span>` : '';
|
||||
const clickAttr = isCurrent ? '' : `onclick="location.href='/room/${roomId}'"`;
|
||||
|
||||
return `<div ${clickAttr}
|
||||
style="display:flex;align-items:center;justify-content:space-between;
|
||||
padding:6px 10px;border-bottom:1px solid #eef2f8;background:${bg};
|
||||
cursor:${isCurrent ? 'default' : 'pointer'};">
|
||||
<span style="color:${nameColor};font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;margin-right:6px;">
|
||||
${safeRoomName}${currentTag}
|
||||
</span>${badge}
|
||||
</div>`;
|
||||
}).filter(Boolean).join('');
|
||||
|
||||
container.innerHTML = roomRows || '<div style="text-align:center;color:#bbb;padding:16px;font-size:11px;">暂无房间</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听全局用户列表更新,若名单抽屉已打开则同步刷新
|
||||
*/
|
||||
window.addEventListener('chatroom:users-updated', () => {
|
||||
if (_mobileDrawerOpen === 'users') {
|
||||
renderMobileUserList();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -71,6 +71,8 @@
|
||||
});
|
||||
})();
|
||||
let onlineUsers = {};
|
||||
// 暂时暴露给已迁移的手机抽屉 Vite 模块读取,后续在线名单整体迁移后可移除。
|
||||
window.onlineUsers = onlineUsers;
|
||||
let autoScroll = true;
|
||||
let userBadgeRotationTick = 0;
|
||||
let userListRenderTimer = null;
|
||||
@@ -2805,6 +2807,8 @@
|
||||
window.addEventListener('chat:here', (e) => {
|
||||
const users = e.detail;
|
||||
onlineUsers = {};
|
||||
// onlineUsers 使用 let 重建对象时,需要同步 window 引用给 Vite 模块。
|
||||
window.onlineUsers = onlineUsers;
|
||||
users.forEach(u => {
|
||||
hydrateOnlineUserPayload(u.username, u);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user