2026-02-03 23:45:27 +08:00
|
|
|
<!DOCTYPE html>
|
2026-02-04 13:06:11 +08:00
|
|
|
<html lang="zh-CN">
|
2026-02-03 23:45:27 +08:00
|
|
|
<head>
|
2026-02-04 13:06:11 +08:00
|
|
|
<meta charset="UTF-8">
|
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
|
|
<title>混沌聊天室 - 散修浅谈代码</title>
|
2026-02-03 23:45:27 +08:00
|
|
|
<style>
|
2026-02-04 13:06:11 +08:00
|
|
|
:root {
|
|
|
|
|
--bg-color: #f4f1de;
|
|
|
|
|
--ink-color: #2b2d42;
|
|
|
|
|
--accent-color: #8d99ae;
|
|
|
|
|
--gold-color: #d4af37;
|
|
|
|
|
--jade-color: #00a86b;
|
|
|
|
|
--scroll-bg: rgba(255, 255, 255, 0.6);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@font-face {
|
|
|
|
|
font-family: 'Kaiti';
|
|
|
|
|
src: local('STKaiti'), local('KaiTi'), local('楷体');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
* {
|
|
|
|
|
margin: 0;
|
|
|
|
|
padding: 0;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
body {
|
|
|
|
|
background-color: var(--bg-color);
|
|
|
|
|
background-image:
|
|
|
|
|
radial-gradient(circle at 10% 20%, rgba(0,0,0,0.02) 0%, transparent 20%),
|
|
|
|
|
radial-gradient(circle at 90% 80%, rgba(0,0,0,0.02) 0%, transparent 20%);
|
|
|
|
|
color: var(--ink-color);
|
|
|
|
|
font-family: 'Kaiti', serif;
|
|
|
|
|
min-height: 100vh;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
padding: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-container {
|
|
|
|
|
width: 100%;
|
|
|
|
|
max-width: 900px;
|
|
|
|
|
background: var(--scroll-bg);
|
|
|
|
|
border: 2px solid var(--ink-color);
|
|
|
|
|
box-shadow: 0 0 30px rgba(0,0,0,0.1), inset 0 0 50px rgba(0,0,0,0.05);
|
|
|
|
|
position: relative;
|
|
|
|
|
backdrop-filter: blur(5px);
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
height: 90vh;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 四角花纹 */
|
|
|
|
|
.chat-container::before, .chat-container::after {
|
|
|
|
|
content: '';
|
|
|
|
|
position: absolute;
|
|
|
|
|
width: 40px;
|
|
|
|
|
height: 40px;
|
|
|
|
|
border: 2px solid var(--ink-color);
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
}
|
|
|
|
|
.chat-container::before { top: 10px; left: 10px; border-right: none; border-bottom: none; }
|
|
|
|
|
.chat-container::after { bottom: 10px; right: 10px; border-left: none; border-top: none; }
|
|
|
|
|
|
|
|
|
|
header {
|
|
|
|
|
padding: 20px;
|
|
|
|
|
text-align: center;
|
|
|
|
|
border-bottom: 1px solid var(--ink-color);
|
|
|
|
|
position: relative;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#online-count {
|
|
|
|
|
position: absolute;
|
|
|
|
|
right: 20px;
|
|
|
|
|
top: 20px;
|
|
|
|
|
font-size: 0.9rem;
|
|
|
|
|
color: var(--jade-color);
|
|
|
|
|
border: 1px solid var(--jade-color);
|
|
|
|
|
padding: 5px 10px;
|
|
|
|
|
border-radius: 15px;
|
|
|
|
|
background: rgba(255, 255, 255, 0.5);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
h2 {
|
|
|
|
|
font-size: 2rem;
|
|
|
|
|
letter-spacing: 0.3rem;
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.auth-section {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 10px;
|
|
|
|
|
padding: 10px;
|
|
|
|
|
background: rgba(0,0,0,0.03);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#messages {
|
|
|
|
|
flex: 1;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
padding: 20px;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 10px;
|
|
|
|
|
scrollbar-width: thin;
|
|
|
|
|
scrollbar-color: var(--ink-color) transparent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#messages::-webkit-scrollbar {
|
|
|
|
|
width: 6px;
|
|
|
|
|
}
|
|
|
|
|
#messages::-webkit-scrollbar-thumb {
|
|
|
|
|
background-color: var(--ink-color);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.message-item {
|
|
|
|
|
padding: 8px 15px;
|
|
|
|
|
border-left: 3px solid var(--accent-color);
|
|
|
|
|
background: rgba(255,255,255,0.4);
|
|
|
|
|
animation: fadeIn 0.3s ease;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.message-time {
|
|
|
|
|
font-size: 0.8rem;
|
|
|
|
|
color: #888;
|
|
|
|
|
margin-right: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.message-tag {
|
|
|
|
|
font-size: 0.85rem;
|
|
|
|
|
padding: 2px 6px;
|
|
|
|
|
border-radius: 3px;
|
|
|
|
|
margin-right: 8px;
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tag-broadcast { background: #ffe8d6; color: #d62828; }
|
|
|
|
|
.tag-room { background: #e9f5db; color: #588157; }
|
|
|
|
|
.tag-private { background: #e0e1dd; color: #1b263b; }
|
|
|
|
|
.tag-system { border: 1px solid var(--ink-color); }
|
|
|
|
|
|
|
|
|
|
.control-panel {
|
|
|
|
|
padding: 20px;
|
|
|
|
|
border-top: 1px solid var(--ink-color);
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: 1fr;
|
|
|
|
|
gap: 15px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.input-group {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 10px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
input {
|
|
|
|
|
padding: 10px 15px;
|
|
|
|
|
border: 1px solid var(--accent-color);
|
|
|
|
|
background: rgba(255,255,255,0.8);
|
|
|
|
|
font-family: 'Kaiti', serif;
|
|
|
|
|
font-size: 1rem;
|
|
|
|
|
flex: 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
input:focus {
|
|
|
|
|
outline: none;
|
|
|
|
|
border-color: var(--ink-color);
|
|
|
|
|
box-shadow: 0 0 5px rgba(0,0,0,0.1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.small-input {
|
|
|
|
|
width: 120px;
|
|
|
|
|
flex: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
button {
|
|
|
|
|
padding: 10px 20px;
|
|
|
|
|
background: var(--ink-color);
|
|
|
|
|
color: white;
|
|
|
|
|
border: none;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-family: 'Kaiti', serif;
|
|
|
|
|
font-size: 1rem;
|
|
|
|
|
transition: all 0.3s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
button:hover {
|
|
|
|
|
background: #4a4e69;
|
|
|
|
|
transform: translateY(-2px);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
button:active {
|
|
|
|
|
transform: translateY(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes fadeIn {
|
|
|
|
|
from { opacity: 0; transform: translateX(-10px); }
|
|
|
|
|
to { opacity: 1; transform: translateX(0); }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.status-msg {
|
|
|
|
|
text-align: center;
|
|
|
|
|
font-style: italic;
|
|
|
|
|
color: var(--accent-color);
|
|
|
|
|
font-size: 0.9rem;
|
|
|
|
|
margin: 5px 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-03 23:45:27 +08:00
|
|
|
</style>
|
|
|
|
|
</head>
|
|
|
|
|
<body>
|
2026-02-04 13:06:11 +08:00
|
|
|
<div class="chat-container">
|
|
|
|
|
<header>
|
|
|
|
|
<h2>混沌聊天室</h2>
|
|
|
|
|
<div id="online-count">当前道友: 0</div>
|
|
|
|
|
<div class="auth-section">
|
|
|
|
|
<span>道号:</span>
|
|
|
|
|
<input id="username" value="散修道友" class="small-input">
|
|
|
|
|
<button onclick="connect()">开启神识</button>
|
|
|
|
|
</div>
|
|
|
|
|
</header>
|
2026-02-03 23:45:27 +08:00
|
|
|
|
2026-02-04 13:06:11 +08:00
|
|
|
<div id="messages">
|
|
|
|
|
<div class="status-msg">神识未开,请先开启神识以感应诸位道友...</div>
|
|
|
|
|
</div>
|
2026-02-03 23:45:27 +08:00
|
|
|
|
2026-02-04 13:06:11 +08:00
|
|
|
<div class="control-panel">
|
|
|
|
|
<!-- 全体消息 -->
|
|
|
|
|
<div class="input-group">
|
|
|
|
|
<input type="text" id="globalMsg" placeholder="传音诸天...">
|
|
|
|
|
<button onclick="sendGlobal()">诸天传音</button>
|
|
|
|
|
</div>
|
2026-02-03 23:45:27 +08:00
|
|
|
|
2026-02-04 13:06:11 +08:00
|
|
|
<!-- 房间消息 -->
|
|
|
|
|
<div class="input-group">
|
|
|
|
|
<input type="text" id="roomID" value="仙缘洞府" class="small-input" placeholder="秘境标识">
|
|
|
|
|
<input type="text" id="roomMsg" placeholder="传音秘境...">
|
|
|
|
|
<button onclick="sendRoom()">秘境传音</button>
|
|
|
|
|
</div>
|
2026-02-03 23:45:27 +08:00
|
|
|
|
2026-02-04 13:06:11 +08:00
|
|
|
<!-- 私聊消息 -->
|
|
|
|
|
<div class="input-group">
|
|
|
|
|
<input type="text" id="toUser" value="某位道友" class="small-input" placeholder="道号">
|
|
|
|
|
<input type="text" id="privateMsg" placeholder="神识密语...">
|
|
|
|
|
<button onclick="sendPrivate()">神识密语</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-02-03 23:45:27 +08:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
let ws;
|
|
|
|
|
|
|
|
|
|
function connect() {
|
|
|
|
|
const user = document.getElementById('username').value;
|
2026-02-04 13:06:11 +08:00
|
|
|
if (!user) { alert('请留下道号'); return; }
|
2026-02-03 23:45:27 +08:00
|
|
|
|
|
|
|
|
if (ws) ws.close();
|
|
|
|
|
|
2026-02-04 13:06:11 +08:00
|
|
|
// 保持原有的逻辑地址,生产环境可能需要动态获取
|
|
|
|
|
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
|
|
|
const host = window.location.host || 'localhost:8080';
|
|
|
|
|
ws = new WebSocket(`${protocol}//${host}/ws?user=${encodeURIComponent(user)}`);
|
2026-02-03 23:45:27 +08:00
|
|
|
|
2026-02-04 13:06:11 +08:00
|
|
|
ws.onopen = () => log('🟢 神识已开启,成功感应虚空', 'system');
|
|
|
|
|
ws.onclose = () => log('🔴 神识已断开,归于虚无', 'system');
|
|
|
|
|
ws.onerror = (err) => log('❌ 神识波动异常: ' + err.message, 'system');
|
2026-02-03 23:45:27 +08:00
|
|
|
|
|
|
|
|
ws.onmessage = (event) => {
|
|
|
|
|
try {
|
|
|
|
|
const msg = JSON.parse(event.data);
|
2026-02-04 13:06:11 +08:00
|
|
|
|
|
|
|
|
// 特殊处理在线人数
|
|
|
|
|
if (msg.type === 'user_count') {
|
|
|
|
|
document.getElementById('online-count').textContent = `当前道友: ${msg.count}`;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let typeLabel = '';
|
|
|
|
|
let tagClass = '';
|
|
|
|
|
|
|
|
|
|
switch(msg.type) {
|
|
|
|
|
case 'broadcast': typeLabel = '诸天'; tagClass = 'tag-broadcast'; break;
|
|
|
|
|
case 'room': typeLabel = `秘境:${msg.room}`; tagClass = 'tag-room'; break;
|
|
|
|
|
case 'private': typeLabel = '密语'; tagClass = 'tag-private'; break;
|
|
|
|
|
default: typeLabel = '感应'; tagClass = 'tag-system';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
displayMsg(msg.user || '无名氏', msg.content, typeLabel, tagClass);
|
2026-02-03 23:45:27 +08:00
|
|
|
} catch (e) {
|
2026-02-04 13:06:11 +08:00
|
|
|
log('⚠️ 感应到未知因果: ' + event.data, 'system');
|
2026-02-03 23:45:27 +08:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function sendGlobal() {
|
|
|
|
|
const content = document.getElementById('globalMsg').value;
|
2026-02-04 13:06:11 +08:00
|
|
|
if (!content || !ws) return;
|
2026-02-03 23:45:27 +08:00
|
|
|
ws.send(JSON.stringify({
|
|
|
|
|
type: 'broadcast',
|
|
|
|
|
user: '',
|
|
|
|
|
content: content
|
|
|
|
|
}));
|
|
|
|
|
document.getElementById('globalMsg').value = '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function sendRoom() {
|
|
|
|
|
const room = document.getElementById('roomID').value;
|
|
|
|
|
const content = document.getElementById('roomMsg').value;
|
2026-02-04 13:06:11 +08:00
|
|
|
if (!room || !content || !ws) return;
|
2026-02-03 23:45:27 +08:00
|
|
|
ws.send(JSON.stringify({
|
|
|
|
|
type: 'room',
|
|
|
|
|
room: room,
|
|
|
|
|
content: content
|
|
|
|
|
}));
|
|
|
|
|
document.getElementById('roomMsg').value = '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function sendPrivate() {
|
|
|
|
|
const to = document.getElementById('toUser').value;
|
|
|
|
|
const content = document.getElementById('privateMsg').value;
|
2026-02-04 13:06:11 +08:00
|
|
|
if (!to || !content || !ws) return;
|
2026-02-03 23:45:27 +08:00
|
|
|
ws.send(JSON.stringify({
|
|
|
|
|
type: 'private',
|
|
|
|
|
to: to,
|
|
|
|
|
content: content
|
|
|
|
|
}));
|
|
|
|
|
document.getElementById('privateMsg').value = '';
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-04 13:06:11 +08:00
|
|
|
function displayMsg(user, content, typeLabel, tagClass) {
|
|
|
|
|
const container = document.getElementById('messages');
|
|
|
|
|
const div = document.createElement('div');
|
|
|
|
|
div.className = 'message-item';
|
|
|
|
|
|
|
|
|
|
const time = new Date().toLocaleTimeString();
|
|
|
|
|
div.innerHTML = `
|
|
|
|
|
<span class="message-time">[${time}]</span>
|
|
|
|
|
<span class="message-tag ${tagClass}">${typeLabel}</span>
|
|
|
|
|
<span class="message-user"><strong>${user}</strong>:</span>
|
|
|
|
|
<span class="message-content">${content}</span>
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
container.appendChild(div);
|
|
|
|
|
div.scrollIntoView();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function log(msg, type) {
|
|
|
|
|
const container = document.getElementById('messages');
|
2026-02-03 23:45:27 +08:00
|
|
|
const div = document.createElement('div');
|
2026-02-04 13:06:11 +08:00
|
|
|
div.className = type === 'system' ? 'status-msg' : 'message-item';
|
|
|
|
|
div.textContent = type === 'system' ? msg : `[${new Date().toLocaleTimeString()}] ${msg}`;
|
|
|
|
|
container.appendChild(div);
|
2026-02-03 23:45:27 +08:00
|
|
|
div.scrollIntoView();
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
</body>
|
2026-02-04 13:06:11 +08:00
|
|
|
</html>
|