update pay ui
This commit is contained in:
289
szse_html.html
Normal file
289
szse_html.html
Normal file
@@ -0,0 +1,289 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>深交所行情 WebSocket 测试</title>
|
||||
<style>
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body { font-family: 'Microsoft YaHei', sans-serif; background: #1a1a2e; color: #eee; padding: 20px; }
|
||||
h1 { text-align: center; margin-bottom: 20px; color: #00d4ff; }
|
||||
.container { max-width: 1400px; margin: 0 auto; }
|
||||
.controls { background: #16213e; padding: 15px; border-radius: 8px; margin-bottom: 20px; display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
|
||||
input { padding: 8px 12px; border: 1px solid #0f3460; border-radius: 4px; background: #1a1a2e; color: #fff; width: 300px; }
|
||||
button { padding: 8px 20px; border: none; border-radius: 4px; cursor: pointer; font-weight: bold; }
|
||||
.btn-connect { background: #00d4ff; color: #000; }
|
||||
.btn-disconnect { background: #e94560; color: #fff; }
|
||||
.btn-ping { background: #ffc107; color: #000; }
|
||||
.btn-clear { background: #6c757d; color: #fff; }
|
||||
.status { padding: 5px 15px; border-radius: 20px; font-size: 14px; }
|
||||
.status.connected { background: #28a745; }
|
||||
.status.disconnected { background: #dc3545; }
|
||||
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; margin-bottom: 20px; }
|
||||
.stat-card { background: #16213e; padding: 15px; border-radius: 8px; text-align: center; }
|
||||
.stat-card .value { font-size: 24px; font-weight: bold; color: #00d4ff; }
|
||||
.stat-card .label { font-size: 12px; color: #888; margin-top: 5px; }
|
||||
.panels { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
|
||||
@media (max-width: 900px) { .panels { grid-template-columns: 1fr; } }
|
||||
.panel { background: #16213e; border-radius: 8px; overflow: hidden; }
|
||||
.panel-header { background: #0f3460; padding: 10px 15px; font-weight: bold; display: flex; justify-content: space-between; }
|
||||
.panel-body { height: 400px; overflow-y: auto; padding: 10px; font-family: 'Consolas', monospace; font-size: 12px; }
|
||||
.msg { padding: 5px; border-bottom: 1px solid #0f3460; word-break: break-all; }
|
||||
.msg.snapshot { color: #ffc107; }
|
||||
.msg.stock { color: #28a745; }
|
||||
.msg.index { color: #17a2b8; }
|
||||
.msg.bond { color: #6f42c1; }
|
||||
.msg.hk_stock { color: #fd7e14; }
|
||||
.msg.afterhours_block, .msg.afterhours_trading { color: #e83e8c; }
|
||||
.msg.volume_stats { color: #20c997; }
|
||||
.msg.fund_nav { color: #6610f2; }
|
||||
.msg.pong { color: #adb5bd; }
|
||||
.quote-table { width: 100%; border-collapse: collapse; font-size: 13px; }
|
||||
.quote-table th, .quote-table td { padding: 8px; text-align: right; border-bottom: 1px solid #0f3460; }
|
||||
.quote-table th { background: #0f3460; text-align: center; }
|
||||
.quote-table .code { text-align: left; font-weight: bold; }
|
||||
.quote-table .up { color: #f5222d; }
|
||||
.quote-table .down { color: #52c41a; }
|
||||
.quote-table .flat { color: #888; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>深交所行情 WebSocket 测试</h1>
|
||||
|
||||
<div class="controls">
|
||||
<input type="text" id="wsUrl" value="ws://222.128.1.157:8765" placeholder="WebSocket URL">
|
||||
<button class="btn-connect" onclick="connect()">连接</button>
|
||||
<button class="btn-disconnect" onclick="disconnect()">断开</button>
|
||||
<button class="btn-ping" onclick="sendPing()">Ping</button>
|
||||
<button class="btn-clear" onclick="clearLogs()">清空日志</button>
|
||||
<span id="status" class="status disconnected">未连接</span>
|
||||
</div>
|
||||
|
||||
<div class="stats">
|
||||
<div class="stat-card">
|
||||
<div class="value" id="msgCount">0</div>
|
||||
<div class="label">消息总数</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="value" id="stockCount">0</div>
|
||||
<div class="label">股票 (300111)</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="value" id="indexCount">0</div>
|
||||
<div class="label">指数 (309011)</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="value" id="bondCount">0</div>
|
||||
<div class="label">债券 (300211)</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="value" id="hkCount">0</div>
|
||||
<div class="label">港股 (306311)</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="value" id="otherCount">0</div>
|
||||
<div class="label">其他类型</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panels">
|
||||
<div class="panel">
|
||||
<div class="panel-header">
|
||||
<span>实时行情</span>
|
||||
<span id="quoteTime">--</span>
|
||||
</div>
|
||||
<div class="panel-body" style="padding: 0;">
|
||||
<table class="quote-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>代码</th>
|
||||
<th>类型</th>
|
||||
<th>最新价</th>
|
||||
<th>涨跌幅</th>
|
||||
<th>成交量</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="quoteTable"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<div class="panel-header">
|
||||
<span>消息日志</span>
|
||||
<span id="logCount">0 条</span>
|
||||
</div>
|
||||
<div class="panel-body" id="logs"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let ws = null;
|
||||
let msgCount = 0;
|
||||
let counts = { stock: 0, index: 0, bond: 0, hk_stock: 0, other: 0 };
|
||||
let quotes = {};
|
||||
const maxLogs = 200;
|
||||
|
||||
function connect() {
|
||||
const url = document.getElementById('wsUrl').value;
|
||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||
addLog('已经连接', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
ws = new WebSocket(url);
|
||||
|
||||
ws.onopen = () => {
|
||||
setStatus(true);
|
||||
addLog(`已连接到 ${url}`, 'info');
|
||||
};
|
||||
|
||||
ws.onmessage = (e) => {
|
||||
msgCount++;
|
||||
document.getElementById('msgCount').textContent = msgCount;
|
||||
|
||||
try {
|
||||
const msg = JSON.parse(e.data);
|
||||
handleMessage(msg);
|
||||
} catch (err) {
|
||||
addLog(`解析错误: ${err.message}`, 'error');
|
||||
}
|
||||
};
|
||||
|
||||
ws.onclose = () => {
|
||||
setStatus(false);
|
||||
addLog('连接已断开', 'warning');
|
||||
};
|
||||
|
||||
ws.onerror = (err) => {
|
||||
addLog(`连接错误`, 'error');
|
||||
};
|
||||
} catch (err) {
|
||||
addLog(`连接失败: ${err.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
function disconnect() {
|
||||
if (ws) {
|
||||
ws.close();
|
||||
ws = null;
|
||||
}
|
||||
}
|
||||
|
||||
function sendPing() {
|
||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||
ws.send(JSON.stringify({ type: 'ping' }));
|
||||
addLog('发送 Ping', 'info');
|
||||
} else {
|
||||
addLog('未连接', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
function handleMessage(msg) {
|
||||
if (msg.type === 'snapshot') {
|
||||
addLog(`收到快照: 股票${msg.data.stocks?.length || 0}只, 指数${msg.data.indexes?.length || 0}个, 债券${msg.data.bonds?.length || 0}只`, 'snapshot');
|
||||
// 初始化行情表
|
||||
msg.data.stocks?.slice(-20).forEach(s => updateQuote('stock', s));
|
||||
msg.data.indexes?.forEach(s => updateQuote('index', s));
|
||||
} else if (msg.type === 'realtime') {
|
||||
const cat = msg.category;
|
||||
if (cat === 'stock') { counts.stock++; updateQuote('stock', msg.data); }
|
||||
else if (cat === 'index') { counts.index++; updateQuote('index', msg.data); }
|
||||
else if (cat === 'bond') { counts.bond++; updateQuote('bond', msg.data); }
|
||||
else if (cat === 'hk_stock') { counts.hk_stock++; updateQuote('hk_stock', msg.data); }
|
||||
else { counts.other++; }
|
||||
|
||||
document.getElementById('stockCount').textContent = counts.stock;
|
||||
document.getElementById('indexCount').textContent = counts.index;
|
||||
document.getElementById('bondCount').textContent = counts.bond;
|
||||
document.getElementById('hkCount').textContent = counts.hk_stock;
|
||||
document.getElementById('otherCount').textContent = counts.other;
|
||||
|
||||
// 每50条记录一次日志
|
||||
if (msgCount % 50 === 0) {
|
||||
addLog(`${cat}: ${msg.data.security_id} = ${msg.data.last_px || msg.data.current_index || '--'}`, cat);
|
||||
}
|
||||
} else if (msg.type === 'pong') {
|
||||
addLog('收到 Pong', 'pong');
|
||||
}
|
||||
|
||||
document.getElementById('quoteTime').textContent = new Date().toLocaleTimeString();
|
||||
}
|
||||
|
||||
function updateQuote(type, data) {
|
||||
const id = data.security_id;
|
||||
quotes[id] = { type, ...data };
|
||||
renderQuotes();
|
||||
}
|
||||
|
||||
function renderQuotes() {
|
||||
const tbody = document.getElementById('quoteTable');
|
||||
const sorted = Object.values(quotes).sort((a, b) => {
|
||||
const order = { index: 0, stock: 1, hk_stock: 2, bond: 3 };
|
||||
return (order[a.type] || 9) - (order[b.type] || 9);
|
||||
}).slice(0, 30);
|
||||
|
||||
tbody.innerHTML = sorted.map(q => {
|
||||
const price = q.last_px || q.current_index || 0;
|
||||
const prev = q.prev_close || 0;
|
||||
const change = prev ? ((price - prev) / prev * 100).toFixed(2) : '0.00';
|
||||
const cls = change > 0 ? 'up' : change < 0 ? 'down' : 'flat';
|
||||
const typeMap = { stock: '股票', index: '指数', bond: '债券', hk_stock: '港股' };
|
||||
return `<tr>
|
||||
<td class="code">${q.security_id}</td>
|
||||
<td>${typeMap[q.type] || q.type}</td>
|
||||
<td class="${cls}">${price.toFixed(q.type === 'index' ? 2 : 2)}</td>
|
||||
<td class="${cls}">${change}%</td>
|
||||
<td>${formatVolume(q.volume)}</td>
|
||||
</tr>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function formatVolume(v) {
|
||||
if (!v) return '--';
|
||||
if (v >= 100000000) return (v / 100000000).toFixed(2) + '亿';
|
||||
if (v >= 10000) return (v / 10000).toFixed(2) + '万';
|
||||
return v.toString();
|
||||
}
|
||||
|
||||
function addLog(text, type = 'info') {
|
||||
const logs = document.getElementById('logs');
|
||||
const time = new Date().toLocaleTimeString();
|
||||
const div = document.createElement('div');
|
||||
div.className = `msg ${type}`;
|
||||
div.textContent = `[${time}] ${text}`;
|
||||
logs.insertBefore(div, logs.firstChild);
|
||||
|
||||
// 限制日志数量
|
||||
while (logs.children.length > maxLogs) {
|
||||
logs.removeChild(logs.lastChild);
|
||||
}
|
||||
document.getElementById('logCount').textContent = `${logs.children.length} 条`;
|
||||
}
|
||||
|
||||
function clearLogs() {
|
||||
document.getElementById('logs').innerHTML = '';
|
||||
document.getElementById('logCount').textContent = '0 条';
|
||||
msgCount = 0;
|
||||
counts = { stock: 0, index: 0, bond: 0, hk_stock: 0, other: 0 };
|
||||
document.getElementById('msgCount').textContent = '0';
|
||||
document.getElementById('stockCount').textContent = '0';
|
||||
document.getElementById('indexCount').textContent = '0';
|
||||
document.getElementById('bondCount').textContent = '0';
|
||||
document.getElementById('hkCount').textContent = '0';
|
||||
document.getElementById('otherCount').textContent = '0';
|
||||
}
|
||||
|
||||
function setStatus(connected) {
|
||||
const el = document.getElementById('status');
|
||||
el.textContent = connected ? '已连接' : '未连接';
|
||||
el.className = `status ${connected ? 'connected' : 'disconnected'}`;
|
||||
}
|
||||
|
||||
// 页面关闭时断开连接
|
||||
window.onbeforeunload = () => { if (ws) ws.close(); };
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user