|
|
<!DOCTYPE html>
|
|
|
<head>
|
|
|
<title>无痕聊天室</title>
|
|
|
<meta charset="utf-8">
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
|
|
|
|
|
<!-- ================= 原有样式,未做任何删改 ================= -->
|
|
|
<style>
|
|
|
html, body {
|
|
|
margin: 0;
|
|
|
padding: 0;
|
|
|
height: 100%;
|
|
|
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
|
|
background: linear-gradient(135deg, #eceff4, #f5f7fa);
|
|
|
}
|
|
|
body.dark { background: #1e1e1e; color: #ccc; }
|
|
|
|
|
|
#chat-container {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
height: 100vh;
|
|
|
max-width: 800px;
|
|
|
margin: 0 auto;
|
|
|
background: #ffffffcc;
|
|
|
backdrop-filter: blur(8px);
|
|
|
border-radius: 12px;
|
|
|
overflow: hidden;
|
|
|
position: relative;
|
|
|
}
|
|
|
body.dark #chat-container { background: #2c2c2ccc; }
|
|
|
|
|
|
#chatLog {
|
|
|
flex: 1;
|
|
|
padding: 16px;
|
|
|
overflow-y: auto;
|
|
|
background: #f9f9fb;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
gap: 8px;
|
|
|
}
|
|
|
body.dark #chatLog { background: #2a2a2a; }
|
|
|
|
|
|
.message {
|
|
|
max-width: 75%;
|
|
|
padding: 10px 14px;
|
|
|
border-radius: 16px;
|
|
|
font-size: 14px;
|
|
|
line-height: 1.5;
|
|
|
word-wrap: break-word;
|
|
|
white-space: pre-wrap;
|
|
|
}
|
|
|
.self {
|
|
|
align-self: flex-end;
|
|
|
background: linear-gradient(135deg, #409eff, #66b1ff);
|
|
|
color: #fff;
|
|
|
border-bottom-right-radius: 4px;
|
|
|
}
|
|
|
.other {
|
|
|
align-self: flex-start;
|
|
|
background: #e5e5ea;
|
|
|
color: #333;
|
|
|
border-bottom-left-radius: 4px;
|
|
|
}
|
|
|
.system {
|
|
|
align-self: center;
|
|
|
background: transparent;
|
|
|
color: #999;
|
|
|
font-size: 13px;
|
|
|
}
|
|
|
|
|
|
#input-container {
|
|
|
padding: 12px;
|
|
|
background-color: #f4f6f8;
|
|
|
display: flex;
|
|
|
gap: 8px;
|
|
|
border-top: 1px solid #e0e0e0;
|
|
|
align-items: flex-end;
|
|
|
}
|
|
|
|
|
|
#messageInput {
|
|
|
flex: 1;
|
|
|
padding: 10px 14px;
|
|
|
border: 1px solid #ccc;
|
|
|
border-radius: 10px;
|
|
|
resize: none;
|
|
|
min-height: 42px;
|
|
|
}
|
|
|
|
|
|
#sendButton, #uploadButton {
|
|
|
padding: 10px 14px;
|
|
|
border: none;
|
|
|
border-radius: 8px;
|
|
|
cursor: pointer;
|
|
|
background: #409eff;
|
|
|
color: #fff;
|
|
|
}
|
|
|
|
|
|
#onlineCount, #toggleDark, #status {
|
|
|
position: absolute;
|
|
|
font-size: 12px;
|
|
|
padding: 4px 8px;
|
|
|
background: #eef;
|
|
|
border-radius: 12px;
|
|
|
}
|
|
|
#onlineCount { top: 8px; right: 12px; }
|
|
|
#toggleDark { top: 8px; left: 12px; cursor: pointer; }
|
|
|
#status { top: 40px; left: 12px; }
|
|
|
</style>
|
|
|
</head>
|
|
|
|
|
|
<body>
|
|
|
<div id="chat-container">
|
|
|
<div id="toggleDark" onclick="toggleDarkMode()">🌙 夜间</div>
|
|
|
<div id="onlineCount">在线人数:0</div>
|
|
|
<div id="status">🟢 已连接</div>
|
|
|
|
|
|
<div id="chatLog"></div>
|
|
|
|
|
|
<div id="input-container">
|
|
|
<textarea id="messageInput" placeholder="来说点什么吧..."></textarea>
|
|
|
<input type="file" id="fileInput" style="display:none">
|
|
|
<button id="uploadButton">📎</button>
|
|
|
<button id="sendButton">发送</button>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<script>
|
|
|
/* ================= 用户名 ================= */
|
|
|
let username = localStorage.getItem("chat_username");
|
|
|
if (!username) {
|
|
|
username = prompt("请输入你的昵称:") || "匿名用户";
|
|
|
localStorage.setItem("chat_username", username);
|
|
|
}
|
|
|
|
|
|
/* ================= WebSocket ================= */
|
|
|
let ws;
|
|
|
function connectWS() {
|
|
|
const scheme = location.protocol === "https:" ? "wss" : "ws";
|
|
|
ws = new WebSocket(`${scheme}://${location.host}/ws`);
|
|
|
|
|
|
ws.onopen = () => {
|
|
|
ws.send(username);
|
|
|
setStatus("🟢 已连接");
|
|
|
};
|
|
|
|
|
|
ws.onmessage = e => renderMessage(e.data);
|
|
|
|
|
|
ws.onclose = () => {
|
|
|
setStatus("🔴 断开,重连中...");
|
|
|
setTimeout(connectWS, 2000);
|
|
|
};
|
|
|
}
|
|
|
connectWS();
|
|
|
|
|
|
/* ================= 渲染消息 ================= */
|
|
|
function renderMessage(msg) {
|
|
|
const log = document.getElementById("chatLog");
|
|
|
const div = document.createElement("div");
|
|
|
|
|
|
if (msg.startsWith("COUNT::")) {
|
|
|
document.getElementById("onlineCount").textContent =
|
|
|
"在线人数:" + msg.replace("COUNT::", "");
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
if (msg.startsWith("SYSTEM::")) {
|
|
|
div.className = "message system";
|
|
|
div.textContent = msg.replace("SYSTEM::", "");
|
|
|
}
|
|
|
else if (msg.startsWith("IMG::")) {
|
|
|
div.className = "message other";
|
|
|
const img = document.createElement("img");
|
|
|
img.src = msg.replace("IMG::", "");
|
|
|
img.style.maxWidth = "240px";
|
|
|
img.style.borderRadius = "8px";
|
|
|
div.appendChild(img);
|
|
|
}
|
|
|
else if (msg.startsWith("VIDEO::")) {
|
|
|
div.className = "message other";
|
|
|
const v = document.createElement("video");
|
|
|
v.src = msg.replace("VIDEO::", "");
|
|
|
v.controls = true;
|
|
|
v.style.maxWidth = "260px";
|
|
|
div.appendChild(v);
|
|
|
}
|
|
|
else if (msg.startsWith("TEXT::")) {
|
|
|
const [, sender, text] = msg.split("::", 3);
|
|
|
div.className = "message " + (sender === username ? "self" : "other");
|
|
|
div.textContent = `${sender}:${text}`;
|
|
|
}
|
|
|
|
|
|
log.appendChild(div);
|
|
|
log.scrollTop = log.scrollHeight;
|
|
|
}
|
|
|
|
|
|
/* ================= 发送文本 ================= */
|
|
|
document.getElementById("sendButton").onclick = () => {
|
|
|
const input = document.getElementById("messageInput");
|
|
|
if (input.value.trim()) {
|
|
|
ws.send(input.value.trim());
|
|
|
input.value = "";
|
|
|
}
|
|
|
}
|
|
|
|
|
|
document.getElementById("messageInput").addEventListener("keydown", (event) => {
|
|
|
if (event.key === "Enter" && !event.shiftKey) {
|
|
|
event.preventDefault(); // 防止换行
|
|
|
const input = event.target;
|
|
|
|
|
|
if (input.value.trim()) {
|
|
|
ws.send(input.value.trim());
|
|
|
input.value = "";
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
|
|
|
/* ================= 上传附件 ================= */
|
|
|
document.getElementById("uploadButton").onclick = () =>
|
|
|
document.getElementById("fileInput").click();
|
|
|
|
|
|
document.getElementById("fileInput").onchange = async () => {
|
|
|
const file = fileInput.files[0];
|
|
|
if (!file) return;
|
|
|
|
|
|
setStatus("📤 上传中...");
|
|
|
const form = new FormData();
|
|
|
form.append("file", file);
|
|
|
|
|
|
const res = await fetch("/upload", { method: "POST", body: form });
|
|
|
const data = await res.json();
|
|
|
if (data.error) alert(data.error);
|
|
|
|
|
|
setStatus("🟢 已连接");
|
|
|
fileInput.value = "";
|
|
|
};
|
|
|
|
|
|
/* ================= UI ================= */
|
|
|
function toggleDarkMode() {
|
|
|
document.body.classList.toggle("dark");
|
|
|
}
|
|
|
function setStatus(t) {
|
|
|
document.getElementById("status").textContent = t;
|
|
|
}
|
|
|
</script>
|
|
|
</body>
|
|
|
</html> |