优化后端和前端逻辑

This commit is contained in:
minghanchen
2026-09-03 10:21:01 +08:00
parent ccd64adcfe
commit 009d8ca73e
19 changed files with 3083 additions and 763 deletions
+47 -65
View File
@@ -806,6 +806,34 @@
const selectControl = (key, label, value, options) => ({ key, label, type: "select", value, options });
const hasControlValue = (values, key) => Object.hasOwn(values, key);
function normalizeDeviceReadout(value) {
const text = String(value ?? "").trim();
if (!text || /^-+$/.test(text) || /^--\s*/.test(text)) return "-";
if (/未知|等待反馈|同步中|状态未知/.test(text)) return "-";
return text;
}
function selectedTransportHasConnection() {
const selected = $('.vehicle-transport-options [data-control-transport][aria-pressed="true"]')
|| $('.vehicle-transport-options [data-control-transport].active');
return selected?.dataset.connectionState === "online";
}
function deviceFeedbackAvailable(deviceId) {
if (!selectedTransportHasConnection()) return false;
return deviceId === "passenger"
? document.body.dataset.passengerFeedbackState === "online"
: document.body.dataset.vehicleFeedbackState === "online";
}
function deviceReadoutRows(device) {
const rows = device.rows();
if (!deviceFeedbackAvailable(device.id)) {
return rows.map(([label]) => [label, "-"]);
}
return rows.map(([label, value]) => [label, normalizeDeviceReadout(value)]);
}
function deviceControlValue(deviceId, control) {
const draft = displaySync.deviceDrafts[deviceId];
return draft && Object.hasOwn(draft, control.key) ? draft[control.key] : control.value;
@@ -894,7 +922,9 @@
rows: () => {
const [frontBrightness, rearBrightness] = splitReadout("#ambientBrightness");
const [frontColor, rearColor] = splitReadout("#ambientColorIndex");
return [["开关状态", /OFF|关闭/.test(readText("#ambientPower")) ? "关闭" : "开启"], ["前排亮度", frontBrightness], ["前排颜色", frontColor], ["后排亮度", rearBrightness], ["后排颜色", rearColor]];
const rawPower = normalizeDeviceReadout(readText("#ambientPower"));
const power = rawPower === "-" ? "-" : /OFF|关闭/.test(rawPower) ? "关闭" : "开启";
return [["开关状态", power], ["前排亮度", frontBrightness], ["前排颜色", frontColor], ["后排亮度", rearBrightness], ["后排颜色", rearColor]];
},
controls: () => {
const [frontBrightness, rearBrightness] = splitReadout("#ambientBrightness");
@@ -954,8 +984,8 @@
const grid = $("#v2DeviceSyncGrid");
if (!grid) return;
grid.innerHTML = syncedDevices.map((device) => {
const rows = device.rows();
const waiting = rows.some(([, value]) => /--|等待|同步中|未知/.test(value));
const rows = deviceReadoutRows(device);
const waiting = rows.some(([, value]) => value === "-");
const controllable = device.controllable !== false;
const controlOpen = controllable && displaySync.controlDevice === device.id;
const hasDraft = Boolean(displaySync.deviceDrafts[device.id]);
@@ -979,7 +1009,7 @@
if (displaySync.activeDevice) {
$(`.v2-synced-device[data-device="${displaySync.activeDevice}"]`)?.classList.add("active");
}
displaySync.deviceFingerprint = JSON.stringify(syncedDevices.map((device) => [device.id, device.rows()]));
displaySync.deviceFingerprint = JSON.stringify(syncedDevices.map((device) => [device.id, deviceReadoutRows(device)]));
window.lucide?.createIcons({ attrs: { "aria-hidden": "true" } });
}
@@ -1071,7 +1101,7 @@
if (displaySync.deviceFrame) return;
displaySync.deviceFrame = requestAnimationFrame(() => {
displaySync.deviceFrame = null;
const fingerprint = JSON.stringify(syncedDevices.map((device) => [device.id, device.rows()]));
const fingerprint = JSON.stringify(syncedDevices.map((device) => [device.id, deviceReadoutRows(device)]));
if (fingerprint === displaySync.deviceFingerprint && $("#v2DeviceSyncGrid")?.childElementCount) return;
renderSyncedDevices();
if (displaySync.activeDevice) presentTwinDevice(displaySync.activeDevice, {
@@ -1157,7 +1187,7 @@
}
displaySync.activeDevice = deviceId;
if (pinned) displaySync.pinnedDevice = deviceId;
const rows = device.rows();
const rows = deviceReadoutRows(device);
popover.hidden = false;
popover.dataset.device = deviceId;
popover.dataset.pinned = String(Boolean(displaySync.pinnedDevice));
@@ -1341,30 +1371,9 @@
}
function applyDisplaySnapshot(snapshot) {
const health = snapshot?.extension_health || {};
const physicallyConnected = health.transport !== "simulation"
&& Boolean(health.connected || health.peer_connected);
const synchronizedCatalog = Array.isArray(snapshot?.extension?.catalog)
? snapshot.extension.catalog
: [];
const connected = physicallyConnected || synchronizedCatalog.length > 0;
const listening = Boolean(health.listening);
const usbState = $("#v2UsbState");
if (usbState) {
const usbLabel = $("span", usbState);
if (usbLabel) usbLabel.textContent = physicallyConnected
? "USB 外设已接入"
: synchronizedCatalog.length
? `${new Set(synchronizedCatalog.map((item) => item.device_id)).size} 个外设已同步`
: listening ? "等待 USB 外设" : "扩展链路检查中";
usbState.dataset.state = connected ? "online" : listening ? "listening" : "offline";
}
const vehicleOnline = health.vehicle?.connected !== false;
const vehicleBadge = $("#v2HeadUnitState");
if (vehicleBadge) {
vehicleBadge.dataset.state = vehicleOnline ? "online" : "offline";
$("span", vehicleBadge).textContent = vehicleOnline ? "车机链路在线" : "车机链路离线";
}
const revision = Number(snapshot?.revision ?? -1);
if (revision !== displaySync.revision) {
displaySync.revision = revision;
@@ -1417,13 +1426,9 @@
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
applyDisplaySnapshot(await response.json());
$("#v2MirrorState")?.setAttribute("data-state", "online");
if ($("#v2MirrorState span")) $("#v2MirrorState span").textContent = "同步正常";
appendSyncLog({ key: "sync:online", category: "sync", title: "网页同步链路已建立", detail: "车机任务与设备状态每 2 秒同步", status: "success" });
} catch (_error) {
console.warn("[vehicle-display-sync]", _error);
$("#v2MirrorState")?.setAttribute("data-state", "offline");
if ($("#v2MirrorState span")) $("#v2MirrorState span").textContent = "等待同步服务";
}
}
@@ -1488,11 +1493,6 @@
const syncSidebar = make("section", "v2-sync-sidebar", `
<div class="v2-sync-sidebar-overview">
<div class="v2-sync-sidebar-heading"><h2>511工程-智能座舱</h2></div>
<div class="v2-sync-link-stack">
<div class="v2-sync-link" id="v2HeadUnitState" data-state="checking"><i data-lucide="monitor-smartphone"></i><div><strong>车机链路</strong><span>连接检查中</span></div><b></b></div>
<div class="v2-sync-link" id="v2MirrorState" data-state="checking"><i data-lucide="refresh-cw"></i><div><strong>网页同步</strong><span>建立同步中</span></div><b></b></div>
<div class="v2-sync-link" id="v2UsbState" data-state="checking"><i data-lucide="usb"></i><div><strong>USB 扩展</strong><span>检查外设中</span></div><b></b></div>
</div>
</div>
<section class="v2-sync-log" aria-labelledby="v2SyncLogTitle">
<header><div><h3 id="v2SyncLogTitle">运行日志 / 同步记录</h3></div><select id="v2LogFilter" aria-label="筛选同步日志"><option value="all">全部</option><option value="task">任务</option><option value="sync">同步</option><option value="extension">外设</option><option value="device">设备</option></select></header>
@@ -1505,12 +1505,10 @@
const executionField = $(".field-group:has(.execution-mode-options)");
const simulationFaultField = $("#simulationFaultField");
if (syncOverview) {
const linkStack = $(".v2-sync-link-stack", syncOverview);
const insertVisibleControl = (field, className) => {
if (!field) return;
field.classList.add(className);
if (linkStack) syncOverview.insertBefore(field, linkStack);
else syncOverview.append(field);
syncOverview.append(field);
};
insertVisibleControl(transportField, "v2-sync-transport-field");
if (executionField) executionField.hidden = false;
@@ -1526,8 +1524,7 @@
<p>等待自然语言解析结果</p>
</div>`);
canCommandPanel.id = "v2CanCommandPanel";
if (linkStack) syncOverview.insertBefore(canCommandPanel, linkStack);
else syncOverview.append(canCommandPanel);
syncOverview.append(canCommandPanel);
canCommandPanel.addEventListener("toggle", () => {
requestAnimationFrame(syncSidebarLogBaseline);
});
@@ -1790,7 +1787,6 @@
if (requestStatus && statusTarget && requestStatus.parentElement !== statusTarget) {
statusTarget.append(requestStatus);
}
requestAnimationFrame(updateModeEdgeHints);
const drawer = $("#gestureDrawer");
if (!triggerGesture) {
const gestureVisible = ui.controlMode === "gesture";
@@ -1902,32 +1898,19 @@
}
});
window.addEventListener("resize", updateScrollCues);
window.addEventListener("resize", updateModeEdgeHints);
window.addEventListener("cognitive-agent:result", () => requestAnimationFrame(updateScrollCues));
const modeChips = $(".v2-mode-rail .mode-chips");
modeChips?.addEventListener(
"scroll",
() => requestAnimationFrame(updateModeEdgeHints),
{ passive: true },
);
modeChips?.addEventListener("wheel", (event) => {
if (modeChips.scrollWidth <= modeChips.clientWidth + 2) return;
const distance = Math.abs(event.deltaX) > Math.abs(event.deltaY)
? event.deltaX
: event.deltaY;
if (!distance) return;
event.preventDefault();
modeChips.scrollBy({ left: distance, behavior: "smooth" });
window.requestAnimationFrame(updateModeEdgeHints);
}, { passive: false });
if (modeChips) {
new MutationObserver(() => requestAnimationFrame(updateModeEdgeHints))
.observe(modeChips, { childList: true });
}
window.addEventListener("cognitive-agent:connection", (event) => {
syncRuntimePresentation(event.detail);
});
window.addEventListener("cognitive-agent:vehicle-feedback-state", () => {
renderSyncedDevices();
if (displaySync.activeDevice) {
presentTwinDevice(displaySync.activeDevice, {
pinned: Boolean(displaySync.pinnedDevice),
selectSeat: false,
});
}
});
const observer = new MutationObserver(() => keepRunButtonAvailable());
[$("#hardwareMode"), $("#simulationMode")]
.filter(Boolean)
@@ -1982,7 +1965,6 @@
syncRuntimePresentation();
window.lucide?.createIcons({ attrs: { "aria-hidden": "true" } });
requestAnimationFrame(() => {
updateModeEdgeHints();
document.body.classList.add("v2-ready");
window.setTimeout(() => $("#v2BootScreen")?.remove(), 220);
});