refactor(ops): add ops web scaffold and update router for split dirs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
caoqianming 2026-04-17 08:33:30 +08:00
parent f9f9915012
commit 797e96cbb5
6 changed files with 71 additions and 17 deletions

View File

@ -3,14 +3,30 @@ use tower_http::services::ServeDir;
use crate::app::AppState;
const WEB_ROOT: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/web");
async fn no_cache(
req: axum::extract::Request,
next: axum::middleware::Next,
) -> axum::response::Response {
let mut response = next.run(req).await;
response.headers_mut().insert(
axum::http::header::CACHE_CONTROL,
axum::http::HeaderValue::from_static("no-store"),
);
response
}
pub fn build_router(state: AppState) -> Router {
Router::new()
.route("/api/health", get(health_check))
.nest_service(
.nest(
"/ui",
ServeDir::new(WEB_ROOT).append_index_html_on_directories(true),
Router::new()
.fallback_service(
ServeDir::new("web/ops")
.append_index_html_on_directories(true)
.fallback(ServeDir::new("web/core")),
)
.layer(axum::middleware::from_fn(no_cache)),
)
.with_state(state)
}

View File

@ -1,14 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>PLC Control Operation System</title>
</head>
<body>
<main>
<h1>Operation System</h1>
<p>This web root is a placeholder for the operation-system app skeleton.</p>
</main>
</body>
</html>

9
web/ops/html/topbar.html Normal file
View File

@ -0,0 +1,9 @@
<header class="topbar">
<div class="title">运转系统</div>
<div class="topbar-actions">
<div class="status" id="statusText">
<span class="ws-dot" id="wsDot"></span>
<span id="wsLabel">连接中…</span>
</div>
</div>
</header>

18
web/ops/index.html Normal file
View File

@ -0,0 +1,18 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>运转系统</title>
<link rel="stylesheet" href="/ui/styles.css" />
</head>
<body>
<div data-partial="/ui/html/topbar.html"></div>
<main>
<div class="muted" style="padding:2rem;text-align:center">运转系统页面开发中</div>
</main>
<script type="module" src="/ui/js/index.js"></script>
</body>
</html>

5
web/ops/js/app.js Normal file
View File

@ -0,0 +1,5 @@
function bootstrap() {
console.log("Operation system app initialized");
}
bootstrap();

20
web/ops/js/index.js Normal file
View File

@ -0,0 +1,20 @@
async function loadPartial(slot) {
const response = await fetch(slot.dataset.partial);
if (!response.ok) {
throw new Error(`Failed to load partial: ${slot.dataset.partial}`);
}
const html = await response.text();
slot.insertAdjacentHTML("beforebegin", html);
slot.remove();
}
async function bootstrapPage() {
const slots = Array.from(document.querySelectorAll("[data-partial]"));
await Promise.all(slots.map((slot) => loadPartial(slot)));
await import("./app.js");
}
bootstrapPage().catch((error) => {
document.body.innerHTML = `<pre>${error.message || String(error)}</pre>`;
});