feat(admin): 运营后台(登录/查看/审核编辑/补全)+ 写入API + 审计留痕
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
@@ -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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user