126 lines
4.6 KiB
HTML
126 lines
4.6 KiB
HTML
{{define "layout"}}
|
|
<!doctype html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<title>{{.Title}}</title>
|
|
<link rel="stylesheet" href="/ui/assets/vendor/tabler.min.css" />
|
|
<link rel="stylesheet" href="/ui/assets/style.css" />
|
|
</head>
|
|
<body class="theme-light">
|
|
<div class="app-shell">
|
|
<aside class="sidebar">
|
|
<div class="brand-block">
|
|
<div class="brand-mark">AI</div>
|
|
<div>
|
|
<div class="brand-title">视觉识别运维平台</div>
|
|
<div class="brand-subtitle">Fleet Operations Console</div>
|
|
</div>
|
|
</div>
|
|
<nav class="side-nav" aria-label="主导航">
|
|
<div class="nav-section">主模块</div>
|
|
<a href="/ui/dashboard"><span class="nav-icon">{{icon "overview"}}</span><span>总览</span></a>
|
|
<a href="/ui/devices"><span class="nav-icon">{{icon "devices"}}</span><span>设备</span></a>
|
|
<a href="/ui/assets"><span class="nav-icon">{{icon "assets"}}</span><span>识别配置</span></a>
|
|
<a href="/ui/tasks"><span class="nav-icon">{{icon "task"}}</span><span>任务</span></a>
|
|
<a href="/ui/diagnostics"><span class="nav-icon">{{icon "logs"}}</span><span>诊断</span></a>
|
|
</nav>
|
|
<div class="sidebar-footer">
|
|
<div class="sidebar-note">总览 fleet 状态,进入设备工作台,统一查看任务与诊断</div>
|
|
</div>
|
|
</aside>
|
|
<div class="workspace">
|
|
<header class="topbar">
|
|
<div>
|
|
<div class="crumb">多设备视觉识别运维平台</div>
|
|
<h1>{{.Title}}</h1>
|
|
</div>
|
|
</header>
|
|
<main>
|
|
{{if .Error}}<div class="error">{{.Error}}</div>{{end}}
|
|
{{if .Message}}<div class="msg">{{.Message}}</div>{{end}}
|
|
{{.ContentHTML}}
|
|
</main>
|
|
</div>
|
|
</div>
|
|
<script src="/ui/assets/vendor/tabler.min.js"></script>
|
|
<script>
|
|
(function () {
|
|
function suggestedFilenameFromHeader(headerValue, fallback) {
|
|
if (!headerValue) return fallback;
|
|
const match = /filename="?([^"]+)"?/i.exec(headerValue);
|
|
if (!match || !match[1]) return fallback;
|
|
return match[1];
|
|
}
|
|
|
|
async function saveBlobFromURL(url, defaultFilename, types, errorMessage) {
|
|
if (window.showSaveFilePicker) {
|
|
const response = await fetch(url, { credentials: "same-origin" });
|
|
if (!response.ok) {
|
|
throw new Error(errorMessage);
|
|
}
|
|
const blob = await response.blob();
|
|
const filename = suggestedFilenameFromHeader(response.headers.get("Content-Disposition"), defaultFilename);
|
|
const handle = await window.showSaveFilePicker({
|
|
suggestedName: filename,
|
|
types: types
|
|
});
|
|
const writable = await handle.createWritable();
|
|
await writable.write(blob);
|
|
await writable.close();
|
|
return;
|
|
}
|
|
throw new Error("当前浏览器不支持选择保存目录和文件名,请使用支持文件保存对话框的浏览器。");
|
|
}
|
|
|
|
async function saveJSONFromURL(url, defaultFilename) {
|
|
return saveBlobFromURL(
|
|
url,
|
|
defaultFilename || "config.json",
|
|
[{
|
|
description: "JSON 文件",
|
|
accept: { "application/json": [".json"] }
|
|
}],
|
|
"导出失败"
|
|
);
|
|
}
|
|
|
|
async function saveDBFromURL(url, defaultFilename) {
|
|
return saveBlobFromURL(
|
|
url,
|
|
defaultFilename || "app.db",
|
|
[{
|
|
description: "SQLite 数据库",
|
|
accept: { "application/octet-stream": [".db"], "application/x-sqlite3": [".db"] }
|
|
}],
|
|
"备份失败"
|
|
);
|
|
}
|
|
|
|
document.addEventListener("click", function (event) {
|
|
const btn = event.target.closest(".js-export-json");
|
|
if (btn) {
|
|
event.preventDefault();
|
|
const url = btn.getAttribute("data-export-url");
|
|
const filename = btn.getAttribute("data-default-filename");
|
|
saveJSONFromURL(url, filename).catch(function (err) {
|
|
window.alert(err && err.message ? err.message : "导出失败");
|
|
});
|
|
return;
|
|
}
|
|
const dbBtn = event.target.closest(".js-export-db");
|
|
if (!dbBtn) return;
|
|
event.preventDefault();
|
|
const url = dbBtn.getAttribute("data-export-url");
|
|
const filename = dbBtn.getAttribute("data-default-filename");
|
|
saveDBFromURL(url, filename).catch(function (err) {
|
|
window.alert(err && err.message ? err.message : "备份失败");
|
|
});
|
|
});
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|
|
{{end}}
|