mirror of
https://github.com/bin456789/reinstall.git
synced 2025-12-10 15:37:43 +08:00
160 lines
4.5 KiB
HTML
160 lines
4.5 KiB
HTML
<!DOCTYPE html>
|
||
<html>
|
||
|
||
<head>
|
||
<title>Reinstall Logs</title>
|
||
<style>
|
||
body {
|
||
margin: 0;
|
||
padding: 0;
|
||
overflow: hidden;
|
||
}
|
||
|
||
#log-container {
|
||
height: calc(100vh);
|
||
margin: 0;
|
||
padding: 8px;
|
||
overflow-y: scroll;
|
||
}
|
||
|
||
#scroll-to-bottom {
|
||
position: fixed;
|
||
bottom: 24px;
|
||
right: 24px;
|
||
background-color: #0099FF;
|
||
color: #fff;
|
||
border: none;
|
||
cursor: pointer;
|
||
display: none;
|
||
width: 48px;
|
||
height: 48px;
|
||
border-radius: 50%;
|
||
}
|
||
|
||
#scroll-to-bottom:hover {
|
||
background-color: #00CCFF;
|
||
}
|
||
|
||
#scroll-to-bottom svg {
|
||
fill: #fff;
|
||
}
|
||
|
||
.done {
|
||
background-color: #cfc;
|
||
}
|
||
|
||
.error {
|
||
background-color: #fcc;
|
||
}
|
||
</style>
|
||
</head>
|
||
|
||
<body>
|
||
<pre id="log-container"></pre>
|
||
<button id="scroll-to-bottom">
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||
<path d="M5 10l7 7 7-7H5z" />
|
||
</svg>
|
||
</button>
|
||
|
||
<script
|
||
src="https://mirrors.sustech.edu.cn/cdnjs/ajax/libs/reconnecting-websocket/1.0.0/reconnecting-websocket.min.js"
|
||
type="application/javascript"></script>
|
||
|
||
<script>
|
||
const logContainer = document.getElementById('log-container');
|
||
const scrollToBottomButton = document.getElementById('scroll-to-bottom');
|
||
let shouldScrollToBottom = true;
|
||
|
||
// 缓冲区相关
|
||
let messageBuffer = [];
|
||
let flushScheduled = false;
|
||
const BUFFER_FLUSH_INTERVAL = 100; // 毫秒
|
||
const BUFFER_MAX_SIZE = 50; // 最大缓冲消息数
|
||
|
||
scrollToBottomButton.addEventListener('click', () => {
|
||
logContainer.scrollTop = logContainer.scrollHeight;
|
||
});
|
||
|
||
logContainer.addEventListener('scroll', () => {
|
||
const isAtBottom = Math.ceil(logContainer.scrollTop + logContainer.clientHeight) >= logContainer.scrollHeight;
|
||
if (isAtBottom) {
|
||
scrollToBottomButton.style.display = 'none';
|
||
} else {
|
||
scrollToBottomButton.style.display = 'block';
|
||
}
|
||
|
||
shouldScrollToBottom = isAtBottom;
|
||
});
|
||
|
||
// 刷新缓冲区到 DOM
|
||
function flushBuffer() {
|
||
if (messageBuffer.length === 0) {
|
||
flushScheduled = false;
|
||
return;
|
||
}
|
||
|
||
// 批量更新文本内容
|
||
const batchText = messageBuffer.join('\n');
|
||
logContainer.textContent += '\n' + batchText;
|
||
|
||
// 检查状态变化(优先级:error > done > start)
|
||
if (batchText.includes('***** ERROR *****')) {
|
||
document.body.className = 'error';
|
||
} else if (batchText.includes('***** DONE *****')) {
|
||
document.body.className = 'done';
|
||
} else if (batchText.includes('***** START TRANS *****')) {
|
||
document.body.className = '';
|
||
}
|
||
|
||
// 自动滚动
|
||
if (shouldScrollToBottom) {
|
||
logContainer.scrollTop = logContainer.scrollHeight;
|
||
}
|
||
|
||
// 清空缓冲区
|
||
messageBuffer = [];
|
||
flushScheduled = false;
|
||
}
|
||
|
||
// 调度刷新
|
||
function scheduleFlush() {
|
||
if (!flushScheduled) {
|
||
flushScheduled = true;
|
||
requestAnimationFrame(() => {
|
||
setTimeout(flushBuffer, BUFFER_FLUSH_INTERVAL);
|
||
});
|
||
}
|
||
}
|
||
|
||
// 添加消息到缓冲区
|
||
function bufferMessage(message) {
|
||
messageBuffer.push(message);
|
||
|
||
// 如果缓冲区满了,立即刷新
|
||
if (messageBuffer.length >= BUFFER_MAX_SIZE) {
|
||
if (flushScheduled) {
|
||
// 取消之前的调度,立即刷新
|
||
flushScheduled = false;
|
||
}
|
||
flushBuffer();
|
||
} else {
|
||
scheduleFlush();
|
||
}
|
||
}
|
||
|
||
var ws = new ReconnectingWebSocket('ws://' + location.host + '/');
|
||
ws.onopen = function () {
|
||
bufferMessage('WebSocket Connected.');
|
||
};
|
||
ws.onclose = function () {
|
||
bufferMessage('WebSocket Disconnected.');
|
||
};
|
||
ws.onmessage = function (event) {
|
||
bufferMessage(event.data);
|
||
};
|
||
</script>
|
||
</body>
|
||
|
||
</html>
|