feat(admin): 运营后台(登录/查看/审核编辑/补全)+ 写入API + 审计留痕
CI / Go (api) (pull_request) Failing after 18s
CI / Python (ingestion) (pull_request) Successful in 7s
CI / Migrations (postgres) (pull_request) Failing after 18s

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
oyaegeli98668
2026-06-20 02:53:30 +00:00
parent c272b2c5d7
commit d90a539e6b
36 changed files with 5519 additions and 1 deletions
+82
View File
@@ -0,0 +1,82 @@
import { useEffect, useState } from "react";
import { api, clearToken, getToken } from "./api";
import Login from "./components/Login";
import ProductList from "./components/ProductList";
import ProductDetail from "./components/ProductDetail";
import { LogOut, Package } from "lucide-react";
type View = { name: "list" } | { name: "detail"; id: string };
export default function App() {
const [authed, setAuthed] = useState(false);
const [checking, setChecking] = useState(true);
const [username, setUsername] = useState("");
const [view, setView] = useState<View>({ name: "list" });
useEffect(() => {
if (!getToken()) {
setChecking(false);
return;
}
api
.me()
.then((r) => {
setUsername(r.username);
setAuthed(true);
})
.catch(() => clearToken())
.finally(() => setChecking(false));
}, []);
function onLoggedIn(name: string) {
setUsername(name);
setAuthed(true);
setView({ name: "list" });
}
function logout() {
clearToken();
setAuthed(false);
setUsername("");
}
if (checking) {
return (
<div className="flex h-full items-center justify-center text-gray-500">
</div>
);
}
if (!authed) return <Login onLoggedIn={onLoggedIn} />;
return (
<div className="flex h-full flex-col">
<header className="flex items-center justify-between bg-white px-6 py-3 shadow-sm">
<div className="flex items-center gap-2 text-lg font-semibold text-gray-800">
<Package className="h-5 w-5 text-emerald-600" />
OpenGoods
</div>
<div className="flex items-center gap-4 text-sm text-gray-600">
<span>{username}</span>
<button
onClick={logout}
className="flex items-center gap-1 rounded px-2 py-1 text-gray-500 hover:bg-gray-100 hover:text-gray-800"
>
<LogOut className="h-4 w-4" /> 退
</button>
</div>
</header>
<main className="flex-1 overflow-auto p-6">
{view.name === "list" ? (
<ProductList onOpen={(id) => setView({ name: "detail", id })} />
) : (
<ProductDetail
id={view.id}
onBack={() => setView({ name: "list" })}
/>
)}
</main>
</div>
);
}