yuanbao-site
/ index.html
5672 字节
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>元宝聊天室</title>
<style>
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,-apple-system,sans-serif;background:#0d1117;color:#c9d1d9;height:100vh;display:flex;flex-direction:column}
#header{background:#161b22;padding:12px 16px;border-bottom:1px solid #30363d;display:flex;align-items:center;gap:10px}
#header h1{font-size:16px;color:#58a6ff}
#status{font-size:12px;color:#8b949e;margin-left:auto}
#messages{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:8px}
.msg{margin-bottom:4px}
.msg .name{font-weight:600;font-size:13px;margin-right:6px}
.msg .name.own{color:#58a6ff}
.msg .text{font-size:14px;line-height:1.5;word-break:break-all}
.msg .time{font-size:11px;color:#8b949e;margin-left:8px}
.system{text-align:center;color:#8b949e;font-size:12px;padding:4px 0}
#input-area{background:#161b22;padding:12px 16px;border-top:1px solid #30363d;display:flex;gap:8px}
#username-in{background:#0d1117;border:1px solid #30363d;color:#c9d1d9;padding:8px 12px;border-radius:6px;font-size:14px;width:100px}
#message{background:#0d1117;border:1px solid #30363d;color:#c9d1d9;padding:8px 12px;border-radius:6px;font-size:14px;flex:1}
#message:focus,#username-in:focus{outline:none;border-color:#58a6ff}
#send{background:#238636;border:none;color:#fff;padding:8px 16px;border-radius:6px;font-size:14px;cursor:pointer}
#send:hover{background:#2ea043}
#login-overlay{position:fixed;inset:0;background:#0d1117;display:flex;align-items:center;justify-content:center;z-index:100}
#login-overlay.hidden{display:none}
.login-box{background:#161b22;border:1px solid #30363d;border-radius:8px;padding:32px;width:320px}
.login-box h2{font-size:20px;margin-bottom:20px;color:#58a6ff}
.login-box input{width:100%;background:#0d1117;border:1px solid #30363d;color:#c9d1d9;padding:10px 12px;border-radius:6px;font-size:14px;margin-bottom:12px}
.login-box input:focus{outline:none;border-color:#58a6ff}
.login-box button{width:100%;background:#238636;border:none;color:#fff;padding:10px;border-radius:6px;font-size:14px;cursor:pointer}
.login-box button:hover{background:#2ea043}
</style>
</head>
<body>
<div id="login-overlay">
<div class="login-box">
<h2>🦆 加入聊天室</h2>
<input type="text" id="login-name" placeholder="输入你的昵称" maxlength="20">
<button onclick="enterChat()">进入聊天</button>
</div>
</div>
<div id="header">
<h1>🦆 元宝聊天室</h1>
<span id="status">🟢 在线</span>
</div>
<div id="messages"></div>
<div id="input-area">
<input type="text" id="username-in" placeholder="昵称" maxlength="20">
<input type="text" id="message" placeholder="说点什么…" maxlength="500" onkeydown="if(event.key==='Enter'&&!event.shiftKey){sendMsg();event.preventDefault()}">
<button id="send" onclick="sendMsg()">发送</button>
</div>
<script>
const ROOM = 'yuanbao-chat-v1';
const CHANNEL = new BroadcastChannel('yuanbao-chat-v1');
let myName = localStorage.getItem('chat_name') || '';
let myId = localStorage.getItem('chat_id') || Math.random().toString(36).substr(2,9);
localStorage.setItem('chat_id', myId);
function enterChat(){
const name = document.getElementById('login-name').value.trim();
if(!name) return;
myName = name;
localStorage.setItem('chat_name', name);
document.getElementById('username-in').value = name;
document.getElementById('login-overlay').classList.add('hidden');
loadHistory();
CHANNEL.postMessage({type:'join', name:myName, id:myId});
}
function loadHistory(){
const msgs = JSON.parse(localStorage.getItem(ROOM+'-msgs') || '[]');
msgs.forEach(renderMsg);
scrollBottom();
}
function renderMsg(m){
if(m.type==='system'){
const el = document.createElement('div');
el.className='system'; el.textContent=m.text;
document.getElementById('messages').appendChild(el);
return;
}
const div = document.createElement('div');
div.className='msg';
const isOwn = m.id===myId;
const time = new Date(m.time).toLocaleTimeString('zh-CN',{hour:'2-digit',minute:'2-digit'});
div.innerHTML='<span class="name '+(isOwn?'own':'')+'">'+(isOwn?'我':escHtml(m.name))+'</span><span class="text">'+escHtml(m.text)+'</span><span class="time">'+time+'</span>';
document.getElementById('messages').appendChild(div);
}
function escHtml(s){return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"')}
function scrollBottom(){
const m=document.getElementById('messages');
m.scrollTop=m.scrollHeight;
}
function saveMsg(m){
const msgs = JSON.parse(localStorage.getItem(ROOM+'-msgs') || '[]');
msgs.push(m);
if(msgs.length>200) msgs.splice(0,msgs.length-200);
localStorage.setItem(ROOM+'-msgs', JSON.stringify(msgs));
}
function sendMsg(){
const text = document.getElementById('message').value.trim();
const name = document.getElementById('username-in').value.trim() || myName;
if(!text) return;
const msg = {type:'msg', name, text, id:myId, time:Date.now()};
CHANNEL.postMessage(msg);
renderMsg(msg);
saveMsg(msg);
scrollBottom();
document.getElementById('message').value='';
}
CHANNEL.onmessage = e=>{
const m=e.data;
if(m.id===myId) return;
if(m.type==='join'){
renderMsg({type:'system', text:m.name+' 加入了聊天室'});
saveMsg({type:'system', text:m.name+' 加入了聊天室'});
} else if(m.type==='msg'){
renderMsg(m);
saveMsg(m);
scrollBottom();
}
};
if(myName){
document.getElementById('login-name').value=myName;
enterChat();
}
</script>
</body>
</html>