2023-10-25 14:19:25 +08:00
|
|
|
|
<!DOCTYPE html>
|
|
|
|
|
|
<html>
|
|
|
|
|
|
|
|
|
|
|
|
<head>
|
|
|
|
|
|
<title>Reinstall Logs</title>
|
|
|
|
|
|
<style>
|
|
|
|
|
|
body {
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#log-container {
|
2024-10-18 23:34:54 +08:00
|
|
|
|
height: calc(100vh);
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
padding: 8px;
|
2023-10-25 14:19:25 +08:00
|
|
|
|
overflow-y: scroll;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#scroll-to-bottom {
|
|
|
|
|
|
position: fixed;
|
|
|
|
|
|
bottom: 24px;
|
|
|
|
|
|
right: 24px;
|
2024-10-18 23:34:54 +08:00
|
|
|
|
background-color: #0099FF;
|
2023-10-25 14:19:25 +08:00
|
|
|
|
color: #fff;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
display: none;
|
|
|
|
|
|
width: 48px;
|
|
|
|
|
|
height: 48px;
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-10-18 23:34:54 +08:00
|
|
|
|
#scroll-to-bottom:hover {
|
|
|
|
|
|
background-color: #00CCFF;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-10-25 14:19:25 +08:00
|
|
|
|
#scroll-to-bottom svg {
|
|
|
|
|
|
fill: #fff;
|
|
|
|
|
|
}
|
2024-10-18 23:34:54 +08:00
|
|
|
|
|
|
|
|
|
|
.done {
|
|
|
|
|
|
background-color: #cfc;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.error {
|
|
|
|
|
|
background-color: #fcc;
|
|
|
|
|
|
}
|
2023-10-25 14:19:25 +08:00
|
|
|
|
</style>
|
|
|
|
|
|
</head>
|
|
|
|
|
|
|
|
|
|
|
|
<body>
|
2023-11-06 19:08:23 +08:00
|
|
|
|
<pre id="log-container"></pre>
|
2023-10-25 14:19:25 +08:00
|
|
|
|
<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>
|
|
|
|
|
|
|
2024-10-18 23:34:54 +08:00
|
|
|
|
<script
|
2025-07-21 20:47:22 +08:00
|
|
|
|
src="https://mirrors.sustech.edu.cn/cdnjs/ajax/libs/reconnecting-websocket/1.0.0/reconnecting-websocket.min.js"
|
2024-10-18 23:34:54 +08:00
|
|
|
|
type="application/javascript"></script>
|
|
|
|
|
|
|
2023-10-25 14:19:25 +08:00
|
|
|
|
<script>
|
|
|
|
|
|
const logContainer = document.getElementById('log-container');
|
|
|
|
|
|
const scrollToBottomButton = document.getElementById('scroll-to-bottom');
|
|
|
|
|
|
let shouldScrollToBottom = true;
|
|
|
|
|
|
|
2025-10-31 07:50:45 +08:00
|
|
|
|
// 缓冲区相关
|
|
|
|
|
|
let messageBuffer = [];
|
|
|
|
|
|
let flushScheduled = false;
|
|
|
|
|
|
const BUFFER_FLUSH_INTERVAL = 100; // 毫秒
|
|
|
|
|
|
const BUFFER_MAX_SIZE = 50; // 最大缓冲消息数
|
|
|
|
|
|
|
2023-10-25 14:19:25 +08:00
|
|
|
|
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;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2025-10-31 07:50:45 +08:00
|
|
|
|
// 刷新缓冲区到 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 = '';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 自动滚动
|
2024-10-18 23:34:54 +08:00
|
|
|
|
if (shouldScrollToBottom) {
|
|
|
|
|
|
logContainer.scrollTop = logContainer.scrollHeight;
|
|
|
|
|
|
}
|
2025-10-31 07:50:45 +08:00
|
|
|
|
|
|
|
|
|
|
// 清空缓冲区
|
|
|
|
|
|
messageBuffer = [];
|
|
|
|
|
|
flushScheduled = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 调度刷新
|
|
|
|
|
|
function scheduleFlush() {
|
|
|
|
|
|
if (!flushScheduled) {
|
|
|
|
|
|
flushScheduled = true;
|
|
|
|
|
|
requestAnimationFrame(() => {
|
|
|
|
|
|
setTimeout(flushBuffer, BUFFER_FLUSH_INTERVAL);
|
|
|
|
|
|
});
|
2024-10-18 23:34:54 +08:00
|
|
|
|
}
|
2025-10-31 07:50:45 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 添加消息到缓冲区
|
|
|
|
|
|
function bufferMessage(message) {
|
|
|
|
|
|
messageBuffer.push(message);
|
|
|
|
|
|
|
|
|
|
|
|
// 如果缓冲区满了,立即刷新
|
|
|
|
|
|
if (messageBuffer.length >= BUFFER_MAX_SIZE) {
|
|
|
|
|
|
if (flushScheduled) {
|
|
|
|
|
|
// 取消之前的调度,立即刷新
|
|
|
|
|
|
flushScheduled = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
flushBuffer();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
scheduleFlush();
|
2024-10-18 23:34:54 +08:00
|
|
|
|
}
|
2025-10-31 07:50:45 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-06 11:31:38 +08:00
|
|
|
|
var ws = new ReconnectingWebSocket(window.location.href.replace(/^http/, 'ws'));
|
2025-10-31 07:50:45 +08:00
|
|
|
|
ws.onopen = function () {
|
|
|
|
|
|
bufferMessage('WebSocket Connected.');
|
|
|
|
|
|
};
|
|
|
|
|
|
ws.onclose = function () {
|
|
|
|
|
|
bufferMessage('WebSocket Disconnected.');
|
|
|
|
|
|
};
|
|
|
|
|
|
ws.onmessage = function (event) {
|
|
|
|
|
|
bufferMessage(event.data);
|
2024-10-18 23:34:54 +08:00
|
|
|
|
};
|
2023-10-25 14:19:25 +08:00
|
|
|
|
</script>
|
|
|
|
|
|
</body>
|
|
|
|
|
|
|
2024-10-18 23:34:54 +08:00
|
|
|
|
</html>
|