first commit

This commit is contained in:
npc0-hue
2026-07-11 14:56:10 +08:00
commit 7e05d0a4e7
660 changed files with 78119 additions and 0 deletions
+14
View File
@@ -0,0 +1,14 @@
import { renderToStaticMarkup } from "react-dom/server";
import { describe, expect, it } from "vitest";
import { App } from "./App";
describe("App", () => {
it("renders a visible authentication loading state before the session resolves", () => {
const html = renderToStaticMarkup(<App />);
expect(html).toContain("正在加载身份状态");
expect(html).toContain("auth-shell");
expect(html).not.toContain("blank");
});
});
+101
View File
@@ -0,0 +1,101 @@
import { useEffect, useState } from "react";
import { AuthView } from "../components/AuthView";
import { AppShell } from "../components/AppShell";
import { EmptyState, LoadingState } from "../components/StateViews";
import type { PageId, PageParams } from "../contracts/page";
import { pageRegistry } from "../pages/pageRegistry";
import { canAccessRoute, defaultPageForUser, hashForPage, navigationRoutesForUser } from "../routes/routes";
import { initialNavigationState, type NavigationState } from "../stores/navigation";
import { useOperationTracker } from "../stores/operations";
import { useSession } from "../stores/session";
export function App() {
const session = useSession();
const user = session.user;
const [navigation, setNavigation] = useState<NavigationState | undefined>();
const operations = useOperationTracker();
useEffect(() => {
if (session.loaded && user) {
const nextNavigation = initialNavigationState(user);
setNavigation(nextNavigation);
if (typeof window !== "undefined") {
const nextHash = hashForPage(nextNavigation.pageId, nextNavigation.params);
if (window.location.hash !== nextHash) {
window.history.replaceState(null, "", nextHash);
}
}
}
}, [session.loaded, user]);
useEffect(() => {
function syncPageFromHash() {
if (user) {
setNavigation(initialNavigationState(user));
}
}
window.addEventListener("hashchange", syncPageFromHash);
window.addEventListener("popstate", syncPageFromHash);
return () => {
window.removeEventListener("hashchange", syncPageFromHash);
window.removeEventListener("popstate", syncPageFromHash);
};
}, [user]);
function handleNavigate(pageId: PageId, params: PageParams = {}) {
if (!user) {
return;
}
const target: NavigationState = canAccessRoute(user, pageId) ? { pageId, params } : { pageId: defaultPageForUser(user), params: {} };
setNavigation(target);
if (typeof window !== "undefined") {
window.history.replaceState(null, "", hashForPage(target.pageId, target.params));
}
}
if (!session.loaded) {
return (
<main className="auth-shell">
<LoadingState label="正在加载身份状态…" />
</main>
);
}
if (!user || !navigation) {
return <AuthView session={session} />;
}
const navRoutes = navigationRoutesForUser(user);
const ActivePage = pageRegistry[navigation.pageId];
const allowed = canAccessRoute(user, navigation.pageId);
return (
<AppShell
routes={navRoutes}
currentPage={navigation.pageId}
session={user}
onNavigate={handleNavigate}
>
{allowed ? (
<ActivePage
session={user}
params={navigation.params}
operations={operations}
onNavigate={handleNavigate}
onLogout={session.logout}
onProfileSave={session.updateProfile}
onThemePreferenceSave={session.updateThemePreference}
/>
) : (
<EmptyState
title="没有访问权限"
description="当前账号无权访问该区域。请返回你的默认工作台,或联系平台管理员调整权限。"
actionLabel="返回工作台"
onAction={() => handleNavigate(defaultPageForUser(user))}
/>
)}
</AppShell>
);
}
+11
View File
@@ -0,0 +1,11 @@
import React from "react";
import { createRoot } from "react-dom/client";
import { App } from "./App";
import "../theme/base.css";
createRoot(document.getElementById("root") as HTMLElement).render(
<React.StrictMode>
<App />
</React.StrictMode>
);