Files
fastweb/www/index.html
2026-08-04 20:07:32 +08:00

346 lines
9.0 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>WebSocket 测试</title>
<style>
:root {
--bg: #0f1419;
--surface: #1a2332;
--border: #2d3a4d;
--text: #e7ecf3;
--muted: #8b9bb4;
--accent: #3d9cf0;
--accent-hover: #5aadf5;
--ok: #3ecf8e;
--err: #f07178;
--warn: #e6c07b;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: "IBM Plex Sans", "Segoe UI", sans-serif;
background:
radial-gradient(ellipse 80% 50% at 20% -10%, #1a3050 0%, transparent 55%),
radial-gradient(ellipse 60% 40% at 90% 100%, #152030 0%, transparent 50%),
var(--bg);
color: var(--text);
min-height: 100vh;
padding: 2rem 1.25rem 3rem;
line-height: 1.5;
}
main {
max-width: 720px;
margin: 0 auto;
}
h1 {
font-family: "IBM Plex Mono", "Cascadia Code", monospace;
font-size: 1.35rem;
font-weight: 600;
letter-spacing: -0.02em;
margin-bottom: 0.35rem;
}
.sub {
color: var(--muted);
font-size: 0.9rem;
margin-bottom: 1.75rem;
}
.sub code {
font-family: "IBM Plex Mono", monospace;
font-size: 0.85em;
color: var(--accent);
}
.row {
display: flex;
flex-wrap: wrap;
gap: 0.6rem;
align-items: center;
margin-bottom: 1rem;
}
.status {
display: inline-flex;
align-items: center;
gap: 0.45rem;
font-size: 0.85rem;
color: var(--muted);
margin-left: auto;
}
.dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--muted);
}
.dot.open { background: var(--ok); box-shadow: 0 0 8px var(--ok); }
.dot.connecting { background: var(--warn); }
.dot.closed, .dot.error { background: var(--err); }
button, input {
font: inherit;
}
button {
background: var(--accent);
color: #0a1220;
border: none;
padding: 0.55rem 1.1rem;
font-weight: 600;
font-size: 0.875rem;
cursor: pointer;
transition: background 0.15s;
}
button:hover:not(:disabled) { background: var(--accent-hover); }
button:disabled {
opacity: 0.4;
cursor: not-allowed;
}
button.ghost {
background: transparent;
color: var(--text);
border: 1px solid var(--border);
}
button.ghost:hover:not(:disabled) {
border-color: var(--muted);
background: var(--surface);
}
.url {
width: 100%;
background: var(--surface);
border: 1px solid var(--border);
color: var(--text);
padding: 0.65rem 0.85rem;
font-family: "IBM Plex Mono", monospace;
font-size: 0.85rem;
margin-bottom: 1rem;
}
.url:focus {
outline: none;
border-color: var(--accent);
}
.send-row {
display: flex;
gap: 0.6rem;
margin-bottom: 1rem;
}
.send-row input {
flex: 1;
background: var(--surface);
border: 1px solid var(--border);
color: var(--text);
padding: 0.65rem 0.85rem;
font-size: 0.9rem;
}
.send-row input:focus {
outline: none;
border-color: var(--accent);
}
.log {
background: var(--surface);
border: 1px solid var(--border);
height: min(52vh, 420px);
overflow-y: auto;
padding: 0.75rem;
font-family: "IBM Plex Mono", monospace;
font-size: 0.8rem;
}
.log .line {
padding: 0.2rem 0;
border-bottom: 1px solid transparent;
word-break: break-all;
white-space: pre-wrap;
}
.log .line .t { color: var(--muted); margin-right: 0.5rem; }
.log .line.sys { color: var(--muted); }
.log .line.out { color: var(--accent); }
.log .line.in { color: var(--ok); }
.log .line.err { color: var(--err); }
</style>
</head>
<body>
<main>
<h1>WebSocket 测试</h1>
<p class="sub">连接到当前服务器 <code id="pathHint">/api/ws.lua</code></p>
<input class="url" id="url" type="text" spellcheck="false" />
<div class="row">
<button id="btnConnect" type="button">连接</button>
<button id="btnDisconnect" class="ghost" type="button" disabled>断开</button>
<button id="btnClear" class="ghost" type="button">清空日志</button>
<span class="status">
<span class="dot" id="dot"></span>
<span id="statusText">未连接</span>
</span>
</div>
<div class="send-row">
<input id="msg" type="text" placeholder="输入要发送的消息…" disabled />
<button id="btnSend" type="button" disabled>发送</button>
</div>
<div class="log" id="log" aria-live="polite"></div>
</main>
<script>
(function () {
var path = "/api/ws.lua";
var proto = location.protocol === "https:" ? "wss:" : "ws:";
var defaultUrl = proto + "//" + location.host + path;
var urlEl = document.getElementById("url");
var msgEl = document.getElementById("msg");
var logEl = document.getElementById("log");
var dotEl = document.getElementById("dot");
var statusEl = document.getElementById("statusText");
var btnConnect = document.getElementById("btnConnect");
var btnDisconnect = document.getElementById("btnDisconnect");
var btnSend = document.getElementById("btnSend");
var btnClear = document.getElementById("btnClear");
urlEl.value = defaultUrl;
var ws = null;
function now() {
var d = new Date();
return (
String(d.getHours()).padStart(2, "0") + ":" +
String(d.getMinutes()).padStart(2, "0") + ":" +
String(d.getSeconds()).padStart(2, "0") + "." +
String(d.getMilliseconds()).padStart(3, "0")
);
}
function log(kind, text) {
var line = document.createElement("div");
line.className = "line " + kind;
var t = document.createElement("span");
t.className = "t";
t.textContent = now();
line.appendChild(t);
line.appendChild(document.createTextNode(text));
logEl.appendChild(line);
logEl.scrollTop = logEl.scrollHeight;
}
function setState(state, label) {
dotEl.className = "dot " + state;
statusEl.textContent = label;
var open = state === "open";
var connecting = state === "connecting";
btnConnect.disabled = open || connecting;
btnDisconnect.disabled = !open && !connecting;
btnSend.disabled = !open;
msgEl.disabled = !open;
urlEl.disabled = open || connecting;
}
function connect() {
var url = urlEl.value.trim();
if (!url) return;
if (ws) {
try { ws.close(); } catch (e) {}
ws = null;
}
setState("connecting", "连接中…");
log("sys", "→ 连接 " + url);
try {
ws = new WebSocket(url);
} catch (e) {
setState("error", "错误");
log("err", "创建失败: " + e.message);
return;
}
ws.onopen = function () {
setState("open", "已连接");
log("sys", "✓ 连接已建立 (readyState=" + ws.readyState + ")");
msgEl.focus();
};
ws.onmessage = function (ev) {
var data = ev.data;
if (data instanceof Blob) {
data.arrayBuffer().then(function (buf) {
log("in", "← binary " + buf.byteLength + " bytes");
});
} else if (data instanceof ArrayBuffer) {
log("in", "← binary " + data.byteLength + " bytes");
} else {
log("in", "← " + data);
}
};
ws.onerror = function () {
log("err", "WebSocket error");
setState("error", "错误");
};
ws.onclose = function (ev) {
var reason = ev.reason ? " — " + ev.reason : "";
log("sys", "× 已关闭 (code=" + ev.code + reason + ", clean=" + ev.wasClean + ")");
setState("closed", "已断开");
ws = null;
};
}
function disconnect() {
if (!ws) return;
log("sys", "→ 主动关闭");
ws.close(1000, "client close");
}
function send() {
if (!ws || ws.readyState !== WebSocket.OPEN) return;
var text = msgEl.value;
ws.send(text);
log("out", "→ " + text);
msgEl.value = "";
msgEl.focus();
}
btnConnect.addEventListener("click", connect);
btnDisconnect.addEventListener("click", disconnect);
btnSend.addEventListener("click", send);
btnClear.addEventListener("click", function () {
logEl.innerHTML = "";
});
msgEl.addEventListener("keydown", function (e) {
if (e.key === "Enter") send();
});
urlEl.addEventListener("keydown", function (e) {
if (e.key === "Enter") connect();
});
setState("closed", "未连接");
})();
</script>
</body>
</html>