204 lines
8.4 KiB
JavaScript
204 lines
8.4 KiB
JavaScript
(function () {
|
|
"use strict";
|
|
|
|
const qs = (selector, root = document) => root.querySelector(selector);
|
|
const qsa = (selector, root = document) => [...root.querySelectorAll(selector)];
|
|
const toastRegion = qs("[data-toast-region]");
|
|
let lastFocused = null;
|
|
|
|
function toast(message) {
|
|
if (!toastRegion) return;
|
|
const item = document.createElement("div");
|
|
item.className = "toast";
|
|
item.setAttribute("role", "status");
|
|
item.textContent = message;
|
|
toastRegion.appendChild(item);
|
|
window.setTimeout(() => item.remove(), 3600);
|
|
}
|
|
|
|
function setView(name) {
|
|
qsa("[data-view]").forEach((view) => {
|
|
const active = view.dataset.view === name;
|
|
view.classList.toggle("active", active);
|
|
view.hidden = !active;
|
|
});
|
|
qsa("[data-view-target]").forEach((button) => {
|
|
const active = button.dataset.viewTarget === name;
|
|
button.classList.toggle("active", active);
|
|
if (active) button.setAttribute("aria-current", "page");
|
|
else button.removeAttribute("aria-current");
|
|
});
|
|
const selected = qs(`[data-view-target="${name}"]`);
|
|
const title = selected?.dataset.title || selected?.textContent.trim();
|
|
const pageName = qs("[data-page-name]");
|
|
if (pageName && title) pageName.textContent = title;
|
|
qs("#main-content")?.focus({ preventScroll: true });
|
|
qs("[data-sidebar]")?.classList.remove("open");
|
|
qs("[data-mobile-menu]")?.setAttribute("aria-expanded", "false");
|
|
}
|
|
|
|
qsa("[data-view-target]").forEach((button) => {
|
|
button.addEventListener("click", () => setView(button.dataset.viewTarget));
|
|
});
|
|
|
|
qs("[data-mobile-menu]")?.addEventListener("click", () => {
|
|
const sidebar = qs("[data-sidebar]");
|
|
const open = sidebar.classList.toggle("open");
|
|
qs("[data-mobile-menu]").setAttribute("aria-expanded", String(open));
|
|
});
|
|
|
|
function openModal(id, trigger) {
|
|
const backdrop = qs(`#${id}`);
|
|
if (!backdrop) return;
|
|
lastFocused = trigger || document.activeElement;
|
|
backdrop.classList.add("open");
|
|
backdrop.setAttribute("aria-hidden", "false");
|
|
document.body.style.overflow = "hidden";
|
|
qs("input, select, textarea, button", qs(".modal", backdrop))?.focus();
|
|
}
|
|
|
|
function closeModal(backdrop) {
|
|
if (!backdrop) return;
|
|
backdrop.classList.remove("open");
|
|
backdrop.setAttribute("aria-hidden", "true");
|
|
document.body.style.overflow = "";
|
|
lastFocused?.focus();
|
|
}
|
|
|
|
qsa("[data-open-modal]").forEach((trigger) => {
|
|
trigger.addEventListener("click", () => openModal(trigger.dataset.openModal, trigger));
|
|
});
|
|
qsa("[data-close-modal]").forEach((trigger) => {
|
|
trigger.addEventListener("click", () => closeModal(trigger.closest(".modal-backdrop")));
|
|
});
|
|
qsa(".modal-backdrop").forEach((backdrop) => {
|
|
backdrop.addEventListener("mousedown", (event) => {
|
|
if (event.target === backdrop) closeModal(backdrop);
|
|
});
|
|
});
|
|
document.addEventListener("keydown", (event) => {
|
|
if (event.key === "Escape") closeModal(qs(".modal-backdrop.open"));
|
|
});
|
|
|
|
qsa("[role='tab'][data-tab-target]").forEach((tab) => {
|
|
tab.addEventListener("click", () => {
|
|
const group = tab.closest("[data-tabs]");
|
|
qsa("[role='tab']", group).forEach((item) => {
|
|
item.setAttribute("aria-selected", String(item === tab));
|
|
item.tabIndex = item === tab ? 0 : -1;
|
|
});
|
|
qsa(".tab-panel", group.parentElement).forEach((panel) => {
|
|
const active = panel.id === tab.dataset.tabTarget;
|
|
panel.classList.toggle("active", active);
|
|
panel.hidden = !active;
|
|
});
|
|
});
|
|
});
|
|
|
|
qsa("[data-filter-input]").forEach((input) => {
|
|
input.addEventListener("input", () => {
|
|
const table = qs(input.dataset.filterInput);
|
|
const query = input.value.trim().toLocaleLowerCase("zh-CN");
|
|
qsa("tbody tr", table).forEach((row) => {
|
|
row.hidden = query && !row.textContent.toLocaleLowerCase("zh-CN").includes(query);
|
|
});
|
|
});
|
|
});
|
|
|
|
qsa("[data-toast]").forEach((button) => {
|
|
button.addEventListener("click", () => toast(button.dataset.toast));
|
|
});
|
|
|
|
const addDevice = qs("[data-action='add-device']");
|
|
addDevice?.addEventListener("click", () => {
|
|
const name = qs("#device-name")?.value.trim() || "新摄像头";
|
|
const address = qs("#device-address")?.value.trim() || "192.168.10.88";
|
|
const tbody = qs("#device-table tbody");
|
|
if (tbody) {
|
|
const row = document.createElement("tr");
|
|
row.innerHTML = `<td><span class="table-title"></span><span class="table-subtitle">待探测 · video</span></td><td></td><td><span class="status status-warning">待激活</span></td><td>尚未选择</td><td><button class="btn btn-quiet" data-toast="已打开设备探测流程">查看</button></td>`;
|
|
qs(".table-title", row).textContent = name;
|
|
row.children[1].textContent = address;
|
|
qs("button", row).addEventListener("click", () => toast("已打开设备探测流程"));
|
|
tbody.prepend(row);
|
|
}
|
|
closeModal(addDevice.closest(".modal-backdrop"));
|
|
toast(`${name} 已保存为待激活设备`);
|
|
});
|
|
|
|
let frameCount = 12842;
|
|
qs("[data-action='run-frame']")?.addEventListener("click", (event) => {
|
|
event.currentTarget.disabled = true;
|
|
event.currentTarget.textContent = "处理中…";
|
|
window.setTimeout(() => {
|
|
frameCount += 1;
|
|
const counter = qs("[data-frame-count]");
|
|
if (counter) counter.textContent = frameCount.toLocaleString("zh-CN");
|
|
const events = qs("[data-brain-events]");
|
|
if (events) {
|
|
const item = document.createElement("div");
|
|
item.className = "activity-item";
|
|
item.innerHTML = `<div class="activity-icon"><svg aria-hidden="true"><use href="#icon-activity"></use></svg></div><div><div class="activity-title">方向越线候选</div><div class="activity-meta">synthetic-camera-01 · 置信度 0.91 · 项目内事件</div></div><div class="activity-time">刚刚</div>`;
|
|
events.prepend(item);
|
|
}
|
|
event.currentTarget.disabled = false;
|
|
event.currentTarget.textContent = "处理下一帧";
|
|
toast("已完成一帧推理并生成项目内候选事件");
|
|
}, 520);
|
|
});
|
|
|
|
qs("[data-action='inject-event']")?.addEventListener("click", (event) => {
|
|
const button = event.currentTarget;
|
|
const idleText = button.textContent;
|
|
button.disabled = true;
|
|
button.textContent = "注入中…";
|
|
window.setTimeout(() => {
|
|
const count = qs("[data-alert-count]");
|
|
if (count) count.textContent = String(Number(count.textContent) + 1);
|
|
button.disabled = false;
|
|
button.textContent = idleText;
|
|
closeModal(button.closest(".modal-backdrop"));
|
|
toast("合成事件已校验并生成新的 Alert");
|
|
}, 520);
|
|
});
|
|
|
|
const alertState = qs("[data-alert-state]");
|
|
const ackButton = qs("[data-action='ack-alert']");
|
|
const closeButton = qs("[data-action='close-alert']");
|
|
ackButton?.addEventListener("click", () => {
|
|
alertState.textContent = "已确认";
|
|
alertState.className = "status status-warning el-tag";
|
|
ackButton.disabled = true;
|
|
closeButton.disabled = false;
|
|
const timeline = qs("[data-alert-timeline]");
|
|
timeline?.insertAdjacentHTML("beforeend", `<div class="timeline-item"><div class="timeline-title">王值班员确认预警</div><div class="timeline-meta">刚刚 · 首个成功 ack · 已写入审计</div></div>`);
|
|
toast("预警已由王值班员确认");
|
|
});
|
|
closeButton?.addEventListener("click", () => {
|
|
alertState.textContent = "已关闭";
|
|
alertState.className = "status status-success el-tag";
|
|
closeButton.disabled = true;
|
|
qs("[data-alert-timeline]")?.insertAdjacentHTML("beforeend", `<div class="timeline-item"><div class="timeline-title">处置完成并关闭</div><div class="timeline-meta">刚刚 · 原始 Event 保持不变</div></div>`);
|
|
toast("Alert 已关闭,原始 Event 未修改");
|
|
});
|
|
|
|
qsa("[data-select-alert]").forEach((row) => {
|
|
const select = () => {
|
|
qsa("[data-select-alert]").forEach((item) => item.classList.toggle("selected", item === row));
|
|
toast(`已选择:${row.dataset.selectAlert}`);
|
|
};
|
|
row.addEventListener("click", select);
|
|
if (row.tagName !== "BUTTON") {
|
|
row.addEventListener("keydown", (event) => {
|
|
if (event.key === "Enter" || event.key === " ") {
|
|
event.preventDefault();
|
|
select();
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
const initial = qs("[data-view-target].active")?.dataset.viewTarget || qs("[data-view]")?.dataset.view;
|
|
if (initial) setView(initial);
|
|
})();
|