fix(aibox/frontend): Batch M 波次3 前端功能修复(F1/F2/F3/F4/F6)

- F1: entry/frontend 清空按钮先 cloneNode 缓存 welcome-message 再清空,修 TypeError 中断
- F2: 三页面手动断开清除/作废重连定时器,杜绝连接复活(entry 新增 reconnectTimer 句柄)
- F3: 三页面连接前关闭 CONNECTING 中旧连接,防孤儿连接/双后端会话
- F4: 6 个 CDN CSS link 补 SRI integrity+crossorigin(sha384 实测计算)
- F6: frontend 重连文案与 3000*N 实际延迟对齐

三页面内联脚本 node --check 通过;测试 493 passed/4 failed(既有平台项)/1 skipped 零回归
This commit is contained in:
qing committed 2026-08-20 13:25:12 +08:00
1 parent 9064750f33
commit 9db2bc112d
4 files changed
+53 -18

No files matched your search

+14 -7
View File
@@ -4,8 +4,8 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI智能助手 - Socket.IO客户端</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/github-dark.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" integrity="sha384-3B6NwesSXE7YJlcLI9RpRqGf2p/EgVH8BgoKTaUrmKNDkHPStTQ3EyoYjCGXaOTS" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/github-dark.min.css" integrity="sha384-wH75j6z1lH97ZOpMOInqhgKzFkAInZPPSPlZpYKYTOqsaizPvhQZmAtLcPKXpLyH" crossorigin="anonymous">
<!-- J13(审计 2026-08-19):CDN 脚本补 SRI integrity,防供应链劫持 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/9.1.6/marked.min.js" integrity="sha384-odPBjvtXVM/5hOYIr3A1dB+flh0c3wAT3bSesIOqEGmyUA4JoKf/YTWy0XKOYAY7" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/highlight.min.js" integrity="sha384-g4mRvs7AO0/Ol5LxcGyz4Doe21pVhGNnC3EQw5shw+z+aXDN86HqUdwXWO+Gz2zI" crossorigin="anonymous"></script>
@@ -1449,6 +1449,7 @@
let reconnectAttempts = 0;
const MAX_RECONNECT_ATTEMPTS = 5;
let targetWsUrl = null;
let reconnectTimer = null; // F2(审计 2026-08-20):重连定时器句柄,手动断开时清除
function createSocketConnection() {
targetWsUrl = document.getElementById('ws-url').value.trim();
@@ -1514,7 +1515,8 @@
if (reason !== 'io client disconnect' && reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
reconnectAttempts++;
showSystemMessage(`⚠️ 连接断开,${reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS} 次重试...`);
setTimeout(() => {
reconnectTimer = setTimeout(() => {
reconnectTimer = null;
if (!socket || !socket.connected) {
console.log('尝试重连...');
socket = createSocketConnection();
@@ -1687,7 +1689,8 @@
// 事件绑定
document.getElementById('connect-btn').addEventListener('click', function() {
if (socket && socket.connected) {
// F3(审计 2026-08-20):CONNECTING 中的旧连接也要断开,否则重复点击产生孤儿连接/双后端会话
if (socket) {
socket.disconnect();
}
reconnectAttempts = 0;
@@ -1695,6 +1698,8 @@
});
document.getElementById('disconnect-btn').addEventListener('click', function() {
// F2:清除已排程的重连定时器,防止倒计时到期把连接"复活"
if (reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null; }
if (socket) {
socket.disconnect();
socket = null;
@@ -1728,10 +1733,12 @@
document.getElementById('clear-chat-btn').addEventListener('click', function() {
const container = document.getElementById('messages-container');
// F1(审计 2026-08-20):welcome-message 在 container 内,
// 必须先 cloneNode 缓存再清空,否则 querySelector 返回 null 抛 TypeError
const welcomeEl = document.querySelector('.welcome-message');
const welcomeHtml = welcomeEl ? welcomeEl.cloneNode(true) : null;
container.innerHTML = '';
const welcomeHtml = document.querySelector('.welcome-message').cloneNode(true);
container.appendChild(welcomeHtml);
if (welcomeHtml) container.appendChild(welcomeHtml);
clearAllStreamingMessages();
});
+6 -3
View File
@@ -5,9 +5,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>小猫AI · 智能喵语伙伴</title>
<!-- Font Awesome 6 (免费图标库) -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" integrity="sha384-3B6NwesSXE7YJlcLI9RpRqGf2p/EgVH8BgoKTaUrmKNDkHPStTQ3EyoYjCGXaOTS" crossorigin="anonymous">
<!-- highlight.js 代码高亮 + marked markdown解析 -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/github-dark.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/github-dark.min.css" integrity="sha384-wH75j6z1lH97ZOpMOInqhgKzFkAInZPPSPlZpYKYTOqsaizPvhQZmAtLcPKXpLyH" crossorigin="anonymous">
<!-- J13(审计 2026-08-19):CDN 脚本补 SRI integrity,防供应链劫持 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/9.1.6/marked.min.js" integrity="sha384-odPBjvtXVM/5hOYIr3A1dB+flh0c3wAT3bSesIOqEGmyUA4JoKf/YTWy0XKOYAY7" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/highlight.min.js" integrity="sha384-g4mRvs7AO0/Ol5LxcGyz4Doe21pVhGNnC3EQw5shw+z+aXDN86HqUdwXWO+Gz2zI" crossorigin="anonymous"></script>
@@ -673,7 +673,8 @@
}
function connectWebSocket() {
if(socket && socket.connected) {
// F3(审计 2026-08-20):CONNECTING 中的旧连接也要断开,否则重复点击产生孤儿连接/双后端会话
if(socket) {
socket.disconnect();
}
@@ -752,6 +753,8 @@
function disconnectWebSocket() {
manualDisconnect = true;
// F2(审计 2026-08-20):清除已排程的重连定时器,防止倒计时到期把连接"复活"
if(reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null; }
if(socket) {
socket.disconnect();
socket = null;
+15 -8
View File
@@ -4,8 +4,8 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI智能助手 - WebSocket直连</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/github-dark.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" integrity="sha384-3B6NwesSXE7YJlcLI9RpRqGf2p/EgVH8BgoKTaUrmKNDkHPStTQ3EyoYjCGXaOTS" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/github-dark.min.css" integrity="sha384-wH75j6z1lH97ZOpMOInqhgKzFkAInZPPSPlZpYKYTOqsaizPvhQZmAtLcPKXpLyH" crossorigin="anonymous">
<!-- J13(审计 2026-08-19):CDN 脚本补 SRI integrity,防供应链劫持 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/9.1.6/marked.min.js" integrity="sha384-odPBjvtXVM/5hOYIr3A1dB+flh0c3wAT3bSesIOqEGmyUA4JoKf/YTWy0XKOYAY7" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/highlight.min.js" integrity="sha384-g4mRvs7AO0/Ol5LxcGyz4Doe21pVhGNnC3EQw5shw+z+aXDN86HqUdwXWO+Gz2zI" crossorigin="anonymous"></script>
@@ -1424,9 +1424,12 @@
if (!manualDisconnect && reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
reconnectAttempts++;
showSystemMessage(`⚠️ 连接断开,${reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS} 秒后重试...`);
// F6(审计 2026-08-20):实际延迟为 3000*N ms,文案与此对齐
showSystemMessage(`⚠️ 连接断开,${3 * reconnectAttempts} 秒后重试(${reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})...`);
setTimeout(() => {
if (!ws || ws.readyState !== WebSocket.OPEN) {
// F2(审计 2026-08-20):闭包到期时再判 manualDisconnect,
// 防止手动断开后已排程定时器把连接"复活"
if (!manualDisconnect && (!ws || ws.readyState !== WebSocket.OPEN)) {
console.log('尝试重连...');
ws = connectWebSocket();
}
@@ -1596,7 +1599,8 @@
// 事件绑定
document.getElementById('connect-btn').addEventListener('click', function() {
if (ws && ws.readyState === WebSocket.OPEN) {
// F3(审计 2026-08-20):CONNECTING 中的旧连接也要关闭,否则重复点击产生孤儿连接/双后端会话
if (ws && ws.readyState !== WebSocket.CLOSED) {
ws.close();
}
reconnectAttempts = 0;
@@ -1639,10 +1643,13 @@
document.getElementById('clear-chat-btn').addEventListener('click', function() {
const container = document.getElementById('messages-container');
// F1(审计 2026-08-20):welcome-message 在 container 内,
// 必须先 cloneNode 缓存再清空,否则 querySelector 返回 null 抛 TypeError,
// 且后续 clearAllStreamingMessages/计数清零全部中断不执行
const welcomeEl = document.querySelector('.welcome-message');
const welcomeHtml = welcomeEl ? welcomeEl.cloneNode(true) : null;
container.innerHTML = '';
const welcomeHtml = document.querySelector('.welcome-message').cloneNode(true);
container.appendChild(welcomeHtml);
if (welcomeHtml) container.appendChild(welcomeHtml);
clearAllStreamingMessages();
messageCount = 0;
+18
View File
@@ -912,3 +912,21 @@ Linux 基线(`aibox/venv`)维持 **490 passed** 不受影响。
- 全量测试 **493 passed / 4 failed(既有平台环境项:symlink 权限、`python3` 缺 PATH、Windows 可执行位语义)/ 1 skipped**,与波次 1 基线完全一致,零回归
**波次 3~5 待落地**:前端功能(F1/F2 等)、命令面规整(W 系列)、客户端收尾(M1 等)。
## 二十、Batch M 修复记录(波次 3:前端功能,2026-08-20)
范围:§17.6 波次 3——前端三页面功能缺陷(F1/F2 + F3/F4/F6)。F5(消息无限追加内存增长)按审计口径"可不做"跳过。
| # | 修复内容 | 关键变更 |
|---|---|---|
| F1 | 清空聊天按钮 TypeError(entry/frontend 同源) | `.welcome-message` 在 `#messages-container` 内,`innerHTML=''` 后 querySelector 必为 null——两页面统一改为"先 cloneNode 缓存 → 清空 → 回插",frontend 后续的计数清零不再被中断 |
| F2 | 手动断开后重连定时器"复活"连接(三页面) | `entry.html` 新增 `reconnectTimer` 句柄,disconnect-btn 处 `clearTimeout`;`frontend.html` 重连闭包到期时再判 `manualDisconnect`;`entry1.html` `disconnectWebSocket` 补 `clearTimeout(reconnectTimer)` |
| F3 | CONNECTING 中重复点击产生孤儿连接(三页面) | `entry.html`/`entry1.html` 连接前无条件 `disconnect()` 旧 socket(原仅 `connected` 才断开);`frontend.html` 关闭条件放宽为 `readyState !== CLOSED` |
| F4 | 三页面 6 个 CSS `<link>` 无 SRI | font-awesome 6.0.0 与 highlight.js github-dark 11.8.0 补 `integrity`+`crossorigin`(sha384 哈希经 curl 拉取实测计算,与 J13 脚本 SRI 口径对齐) |
| F6 | frontend 重连文案与实际行为不符 | "N/5 秒后重试" → "`3*N` 秒后重试(N/5)",与 `3000*reconnectAttempts` 实际延迟对齐 |
**验证**:
- 三页面全部内联脚本 `node --check` 通过
- 全量测试 **493 passed / 4 failed(既有平台环境项)/ 1 skipped**,与波次 1/2 基线完全一致,零回归(前端无 pytest 覆盖,语法级验证兜底)
**波次 4~5 待落地**:命令面规整(W1~W8/W11/W12 + W5 择优)、客户端收尾(M1~M5/M7/M8)。