Files
SystemSimulationApp/app/static/index.html
T

266 lines
7.0 KiB
HTML

<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>System Simulation App</title>
<style>
* {
box-sizing: border-box;
}
body {
margin: 0;
height: 100vh;
color: #18202a;
font-family:
system-ui,
-apple-system,
BlinkMacSystemFont,
"Segoe UI",
sans-serif;
background: #f6f8fb;
}
.app {
display: grid;
grid-template-rows: 48px minmax(0, 1fr);
height: 100vh;
}
.toolbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
padding: 0 14px;
border-bottom: 1px solid #d8dee8;
background: #ffffff;
}
.title {
min-width: 0;
overflow: hidden;
font-size: 15px;
font-weight: 650;
line-height: 1.2;
text-overflow: ellipsis;
white-space: nowrap;
}
.actions {
display: flex;
align-items: center;
gap: 10px;
}
.status {
max-width: 220px;
overflow: hidden;
color: #5d6877;
font-size: 13px;
text-overflow: ellipsis;
white-space: nowrap;
}
button {
height: 32px;
padding: 0 12px;
border: 1px solid #1b6fb8;
border-radius: 6px;
color: #ffffff;
font: inherit;
font-size: 14px;
background: #1b6fb8;
cursor: pointer;
}
button:disabled {
border-color: #98a2af;
background: #98a2af;
cursor: wait;
}
iframe {
display: block;
width: 100%;
height: 100%;
border: 0;
}
</style>
</head>
<body>
<main class="app">
<header class="toolbar">
<div class="title">System Simulation App</div>
<div class="actions">
<span class="status" id="status">加载中</span>
<button id="export-button" type="button" disabled>生成 XML</button>
</div>
</header>
<iframe id="drawio-frame" title="draw.io"></iframe>
</main>
<script>
const DRAWIO_BASE_URL = __DRAWIO_BASE_URL__;
const frame = document.getElementById("drawio-frame");
const exportButton = document.getElementById("export-button");
const statusNode = document.getElementById("status");
const drawioUrl = new URL(DRAWIO_BASE_URL, window.location.href);
const componentLibraryUrl = new URL(
"/api/component-library.drawiolib",
window.location.href,
);
const componentLibraryKey = `U${componentLibraryUrl.href}`;
drawioUrl.searchParams.set("embed", "1");
drawioUrl.searchParams.set("proto", "json");
drawioUrl.searchParams.set("libraries", "1");
drawioUrl.searchParams.set("clibs", componentLibraryKey);
drawioUrl.searchParams.set(
"cors",
`^${escapeRegExp(window.location.origin)}/`,
);
drawioUrl.searchParams.set("spin", "1");
drawioUrl.searchParams.set("noSaveBtn", "1");
drawioUrl.searchParams.set("noExitBtn", "1");
drawioUrl.searchParams.set("saveAndExit", "0");
frame.src = drawioUrl.toString();
let pendingExport = false;
let exportTimeoutId = null;
const initialXml =
'<mxGraphModel dx="1200" dy="800" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="827" math="0" shadow="0"><root><mxCell id="0"/><mxCell id="1" parent="0"/></root></mxGraphModel>';
function setStatus(text) {
statusNode.textContent = text;
}
function escapeRegExp(value) {
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function sendToDrawio(message) {
frame.contentWindow.postMessage(JSON.stringify(message), drawioUrl.origin);
}
function parseMessage(data) {
if (typeof data !== "string") {
return null;
}
try {
return JSON.parse(data);
} catch {
return null;
}
}
function clearExportTimeout() {
if (exportTimeoutId !== null) {
window.clearTimeout(exportTimeoutId);
exportTimeoutId = null;
}
}
async function finishExport(diagramXml) {
clearExportTimeout();
pendingExport = false;
await exportSystemXml(diagramXml || initialXml);
}
function scheduleExportTimeout() {
clearExportTimeout();
exportTimeoutId = window.setTimeout(() => {
if (!pendingExport) {
return;
}
pendingExport = false;
exportButton.disabled = false;
setStatus("生成超时");
}, 5000);
}
window.addEventListener("message", async (event) => {
if (event.origin !== drawioUrl.origin) {
return;
}
const message = parseMessage(event.data);
if (!message) {
return;
}
if (message.event === "init") {
sendToDrawio({
action: "load",
xml: initialXml,
title: "system.drawio",
autosave: 1,
});
exportButton.disabled = false;
setStatus("已就绪");
return;
}
if (message.event === "autosave") {
if (!pendingExport) {
setStatus("已更新");
}
return;
}
if (message.event === "export" && pendingExport) {
await finishExport(message.xml);
}
});
exportButton.addEventListener("click", () => {
if (pendingExport) {
return;
}
pendingExport = true;
exportButton.disabled = true;
setStatus("生成中");
sendToDrawio({ action: "export", format: "xml" });
scheduleExportTimeout();
});
async function exportSystemXml(diagramXml) {
try {
const response = await fetch("/api/system-xml", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ xml: diagramXml }),
});
if (!response.ok) {
throw new Error(await response.text());
}
const systemXml = await response.text();
downloadText(systemXml, "system.xml", "application/xml");
setStatus("已生成");
} catch (error) {
console.error(error);
setStatus("生成失败");
} finally {
exportButton.disabled = false;
}
}
function downloadText(text, filename, mimeType) {
const blob = new Blob([text], { type: mimeType });
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = filename;
link.click();
URL.revokeObjectURL(url);
}
</script>
</body>
</html>