283 lines
13 KiB
HTML
283 lines
13 KiB
HTML
<!doctype html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
<title>在线聊天室</title>
|
||
<script>
|
||
(() => {
|
||
try {
|
||
const saved = localStorage.getItem("theme");
|
||
const theme =
|
||
saved === "dark" || saved === "light"
|
||
? saved
|
||
: window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches
|
||
? "dark"
|
||
: "light";
|
||
document.documentElement.dataset.theme = theme;
|
||
} catch {}
|
||
})();
|
||
</script>
|
||
<link rel="stylesheet" href="/app.css" />
|
||
</head>
|
||
<body>
|
||
<div class="scene-container">
|
||
<main class="chat-room" id="chatRoom">
|
||
<div class="chat-interface">
|
||
<header class="topbar">
|
||
<div class="brand">聊天室</div>
|
||
<div class="topbarRight">
|
||
<div class="me" id="meBox"></div>
|
||
<button class="btn iconBtn themeBtn" id="themeBtn" type="button" aria-label="切换黑夜模式" aria-pressed="false">
|
||
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M21 12.8A9 9 0 1 1 11.2 3 7 7 0 0 0 21 12.8Z" />
|
||
</svg>
|
||
</button>
|
||
<button class="btn iconBtn menuBtn" id="menuBtn" type="button" aria-label="账户与管理">
|
||
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M20 21a8 8 0 0 0-16 0" />
|
||
<circle cx="12" cy="8" r="4" />
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</header>
|
||
|
||
<div class="menuDropdown" id="menuDropdown" hidden>
|
||
<div class="menuDropdownCard card" id="menuDropdownCard">
|
||
<div class="menuDropdownHeader">
|
||
<div class="menuDropdownTitle">菜单</div>
|
||
<button class="btn iconBtn" id="menuCloseBtn" type="button" aria-label="关闭菜单">
|
||
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M18 6 6 18" />
|
||
<path d="M6 6l12 12" />
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div id="menuHost">
|
||
<details class="card fold" id="accountFold">
|
||
<summary class="foldSummary">账户</summary>
|
||
<div class="foldBody">
|
||
<div id="authBox"></div>
|
||
</div>
|
||
</details>
|
||
|
||
<details class="card fold" id="adminCard" style="display:none">
|
||
<summary class="foldSummary">管理</summary>
|
||
<div class="foldBody">
|
||
<div class="row">
|
||
<button id="loadUsersBtn" class="btn">刷新用户</button>
|
||
<button id="lobbyPwdBtn" class="btn">大厅密码</button>
|
||
</div>
|
||
<div id="adminToolsBox" class="stack" style="margin-top:10px" hidden></div>
|
||
<div class="hint" style="margin-top:10px">用户列表</div>
|
||
<div id="adminUsersBox" class="admin"></div>
|
||
</div>
|
||
</details>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<main class="layout">
|
||
<aside class="sidebar">
|
||
<div class="card" id="onlineCard">
|
||
<div class="row" style="justify-content:space-between; align-items:center">
|
||
<div style="font-weight:900; color:var(--text-main); font-size:13px">在线用户</div>
|
||
<div class="hint" id="onlineCount"></div>
|
||
</div>
|
||
<div class="onlineList" id="onlineList"></div>
|
||
</div>
|
||
</aside>
|
||
|
||
<section class="chat">
|
||
<div class="chatHeader">
|
||
<div>
|
||
<div class="chatTitle" id="roomTitle">未进入聊天室</div>
|
||
<div class="chatSubtitle" id="roomSubtitle"></div>
|
||
</div>
|
||
<div class="row">
|
||
<button id="leaveRoomBtn" class="btn" disabled>离开</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="messages" id="messages"></div>
|
||
|
||
<div class="composer">
|
||
<div class="composerRow">
|
||
<button class="btn iconBtn toolBtn" id="emojiBtn" type="button" aria-label="表情" disabled>
|
||
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
|
||
<path d="M8 14s1.5 2 4 2 4-2 4-2" />
|
||
<path d="M9 9h.01" />
|
||
<path d="M15 9h.01" />
|
||
</svg>
|
||
</button>
|
||
|
||
<button class="btn iconBtn toolBtn" id="imageBtn" type="button" aria-label="图片" disabled hidden>
|
||
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M21 19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v14Z" />
|
||
<path d="M8.5 10.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z" />
|
||
<path d="m21 15-5-5L5 21" />
|
||
</svg>
|
||
</button>
|
||
|
||
<div class="msgInputWrap" id="msgInputWrap">
|
||
<img id="inlineThumb" class="inlineThumb" alt="待发送图片" hidden />
|
||
<input id="msgInput" class="input" placeholder="输入消息…" disabled />
|
||
</div>
|
||
<button id="sendBtn" class="btn btnPrimary" disabled>发送</button>
|
||
</div>
|
||
|
||
<input id="fileInput" type="file" accept="image/*" hidden />
|
||
<div class="composerAttach" id="composerAttach" hidden></div>
|
||
<div class="emojiPanel" id="emojiPanel" hidden></div>
|
||
</div>
|
||
</section>
|
||
</main>
|
||
</div>
|
||
</main>
|
||
|
||
<div class="doors-container" id="doorsContainer" aria-hidden="true">
|
||
<div class="door left">
|
||
<div class="door-handle"></div>
|
||
</div>
|
||
<div class="door right">
|
||
<div class="door-handle"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="login-wrapper" id="loginWrapper">
|
||
<div class="login-card">
|
||
<h1>进入聊天室</h1>
|
||
<p>门已锁好,请验证身份进入</p>
|
||
<div class="gateTabs">
|
||
<button class="btn btnPrimary" id="gateTabLogin" type="button">登录</button>
|
||
<button class="btn" id="gateTabRegister" type="button">注册</button>
|
||
<button class="btn" id="gateTabGuest" type="button">游客登录</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 登录/注册/游客 模态框 -->
|
||
<div class="confirm" id="gateModal" hidden>
|
||
<div class="confirmCard gateModalCard">
|
||
<div class="gateModalHeader">
|
||
<div class="confirmTitle" id="gateModalTitle"></div>
|
||
<button class="btn iconBtn" id="gateModalClose" type="button" aria-label="关闭">
|
||
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M18 6 6 18" /><path d="M6 6l12 12" />
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div class="error-msg" id="gateError"></div>
|
||
|
||
<form id="gateLoginForm" class="gatePanel" hidden>
|
||
<div class="input-group">
|
||
<label for="gateLoginUser">用户名</label>
|
||
<input id="gateLoginUser" autocomplete="username" />
|
||
</div>
|
||
<div class="input-group">
|
||
<label for="gateLoginPass">密码</label>
|
||
<input id="gateLoginPass" type="password" autocomplete="current-password" />
|
||
</div>
|
||
<button class="login-btn" type="submit">登录并进入</button>
|
||
<button class="link" type="button" id="forgotPasswordBtn">找回密码</button>
|
||
</form>
|
||
|
||
<form id="gateRegisterForm" class="gatePanel" hidden>
|
||
<div class="input-group">
|
||
<label for="gateRegUser">用户名</label>
|
||
<input id="gateRegUser" autocomplete="username" />
|
||
</div>
|
||
<div class="input-group">
|
||
<label for="gateRegPass">密码</label>
|
||
<input id="gateRegPass" type="password" autocomplete="new-password" />
|
||
</div>
|
||
<div class="input-group">
|
||
<label for="gateRegEmail">邮箱(选填)</label>
|
||
<input id="gateRegEmail" autocomplete="email" />
|
||
</div>
|
||
<div class="input-group">
|
||
<label for="gateRegQq">QQ(选填)</label>
|
||
<input id="gateRegQq" autocomplete="off" />
|
||
</div>
|
||
<div class="input-group">
|
||
<label for="gateRegPhone">联系电话(选填)</label>
|
||
<input id="gateRegPhone" autocomplete="tel" />
|
||
</div>
|
||
<button class="login-btn" type="submit">注册并进入</button>
|
||
</form>
|
||
|
||
<form id="gateGuestForm" class="gatePanel" hidden>
|
||
<input id="gateGuestUser" class="srOnly" name="username" autocomplete="username" tabindex="-1" />
|
||
<div class="hint" id="gateGuestNameHint"></div>
|
||
<div class="input-group">
|
||
<label for="gateGuestPass">聊天室密码(若已设置)</label>
|
||
<input id="gateGuestPass" type="password" autocomplete="current-password" />
|
||
<div class="hint" id="gateGuestHint"></div>
|
||
</div>
|
||
<button class="login-btn" type="submit">游客进入</button>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 找回密码模态框 -->
|
||
<div class="confirm" id="forgotModal" hidden>
|
||
<div class="confirmCard" style="width:min(480px,92vw)">
|
||
<div class="confirmTitle" id="forgotModalTitle">找回密码</div>
|
||
<!-- 步骤1 -->
|
||
<div id="forgotStep1" class="stack" style="margin-top:12px">
|
||
<div class="hint">请输入你的用户名,我们将向注册邮箱发送 6 位数字验证码。</div>
|
||
<div class="input-group">
|
||
<label for="forgotUsername">用户名</label>
|
||
<input id="forgotUsername" autocomplete="username" />
|
||
</div>
|
||
<div class="hint" id="forgotStep1Msg"></div>
|
||
<div class="row" style="justify-content:flex-end;margin-top:4px">
|
||
<button class="btn" id="forgotCancelBtn" type="button">取消</button>
|
||
<button class="btn btnPrimary" id="forgotSendBtn" type="button">发送验证码</button>
|
||
</div>
|
||
</div>
|
||
<!-- 步骤2 -->
|
||
<div id="forgotStep2" class="stack" style="margin-top:12px;display:none">
|
||
<div class="hint">请输入邮件中收到的 6 位数字验证码和新密码。验证码有效期 30 分钟。</div>
|
||
<div class="input-group">
|
||
<label for="forgotToken">验证码</label>
|
||
<input id="forgotToken" maxlength="6" pattern="[0-9]*" inputmode="numeric" autocomplete="one-time-code" />
|
||
</div>
|
||
<div class="input-group">
|
||
<label for="forgotNewPass">新密码</label>
|
||
<input id="forgotNewPass" type="password" autocomplete="new-password" />
|
||
</div>
|
||
<div class="hint" id="forgotStep2Msg"></div>
|
||
<div class="row" style="justify-content:flex-end;margin-top:4px">
|
||
<button class="btn" id="forgotBackBtn" type="button">返回</button>
|
||
<button class="btn btnPrimary" id="forgotResetBtn" type="button">重置密码</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="imgViewer" id="imgViewer" hidden>
|
||
<div class="imgViewerBackdrop" id="imgViewerBackdrop"></div>
|
||
<img class="imgViewerImg" id="imgViewerImg" alt="图片预览" />
|
||
<button class="imgViewerClose" id="imgViewerClose" type="button">关闭</button>
|
||
</div>
|
||
|
||
<div class="toast" id="toast" hidden></div>
|
||
|
||
<div class="confirm" id="confirm" hidden>
|
||
<div class="confirmCard">
|
||
<div class="confirmTitle" id="confirmTitle"></div>
|
||
<div class="confirmMsg" id="confirmMsg"></div>
|
||
<div class="row" style="justify-content:flex-end">
|
||
<button class="btn" id="confirmCancel" type="button">取消</button>
|
||
<button class="btn btnPrimary" id="confirmOk" type="button">确定</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="/app.js"></script>
|
||
</body>
|
||
</html>
|