优化后端和前端逻辑
This commit is contained in:
@@ -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);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user