Backend:
- Persist alert events and robot delivery counters in SQLite (data/app.db,
YOLO_DB_PATH override); EventStore keeps its interface, DeliveryStatsStore
uses read/write-through UPSERT; in-memory fallback remains for standalone
AlertManager use
- Serve /api/detect from a sync endpoint and serialize YOLO inference with a
lock, so concurrent requests no longer block the event loop
- Unify single-image and session detection: requests without session_id
share the fixed single-image session and run full frame confirmation
- Store UTC Z-suffixed timestamps for sortable string comparison; close
executor and database on shutdown via FastAPI lifespan
Frontend:
- Split app.js into ES modules (main.js + modules/{dom,api,ui,charts,store}
+ views/{dashboard,inspection,events,robots,robotCards}), no build step
- Escape all server data interpolated into innerHTML; guard missing
event.classes; replace lazy element ID list with memoized qs()
- Remove hardcoded fake stats (trend badge, device donut segment)
Docs:
- Rewrite README: accurate weight policy (only trained best.pt committed,
no Git LFS), SQLite persistence, single-worker note, data asset inventory
- Align models/pretrained/README.md with actual files; add YOLO_DB_PATH to
.env.example; add persistence unit tests; ruff clean
37 lines
1.2 KiB
JavaScript
37 lines
1.2 KiB
JavaScript
import { qs } from "./dom.js";
|
|
|
|
export function showToast(message, type = "info") {
|
|
const toast = document.createElement("div");
|
|
toast.className = `toast ${type}`;
|
|
toast.textContent = message;
|
|
qs("#toastRegion").appendChild(toast);
|
|
setTimeout(() => toast.remove(), 3600);
|
|
}
|
|
|
|
export function setServiceStatus(online) {
|
|
const statusChip = qs("#globalStatus");
|
|
statusChip.textContent = online ? "服务运行正常" : "服务连接异常";
|
|
statusChip.className = `status-chip ${online ? "status-online" : "status-offline"}`;
|
|
qs("#sidebarStatus").textContent = online ? "服务在线" : "服务离线";
|
|
qs("#sidebarStatusDot").className = `status-dot ${online ? "is-online" : "is-offline"}`;
|
|
const readyApi = qs("#readyApi");
|
|
readyApi.textContent = online ? "正常" : "异常";
|
|
readyApi.className = online ? "ready" : "not-ready";
|
|
}
|
|
|
|
export function setChannelReady(element, enabled) {
|
|
element.textContent = enabled ? "已启用" : "未配置";
|
|
element.className = enabled ? "ready" : "optional";
|
|
}
|
|
|
|
export function updateClock() {
|
|
qs("#currentClock").textContent = new Date().toLocaleString("zh-CN", {
|
|
hour12: false,
|
|
month: "2-digit",
|
|
day: "2-digit",
|
|
hour: "2-digit",
|
|
minute: "2-digit",
|
|
second: "2-digit",
|
|
});
|
|
}
|