2026-05-25: 等级体系升级(20级)+论坛二级分类+会员空间
This commit is contained in:
@@ -128,6 +128,7 @@ export default function ToolsAdminPage() {
|
||||
const [tools, setTools] = useState<Tool[]>([]);
|
||||
const [categories, setCategories] = useState<Category[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState("");
|
||||
const [page, setPage] = useState(1);
|
||||
const [total, setTotal] = useState(0);
|
||||
const pageSize = 20;
|
||||
@@ -138,13 +139,14 @@ export default function ToolsAdminPage() {
|
||||
const [saving, setSaving] = useState(false);
|
||||
|
||||
const fetchTools = useCallback(async () => {
|
||||
setError("");
|
||||
try {
|
||||
const res = await fetch(`/api/admin/tools?page=${page}&pageSize=${pageSize}`);
|
||||
const data = await res.json();
|
||||
setTools(data.tools || []);
|
||||
setTotal(data.total || 0);
|
||||
} catch {
|
||||
console.error("Fetch tools failed");
|
||||
setError("加载工具列表失败,请稍后重试");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -314,11 +316,57 @@ export default function ToolsAdminPage() {
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{loading ? (
|
||||
<div className="space-y-3">
|
||||
{Array.from({ length: 3 }).map((_, i) => (
|
||||
<div key={i} className="h-16 animate-pulse rounded-2xl bg-muted/20" />
|
||||
))}
|
||||
{error ? (
|
||||
<div className="flex flex-col items-center justify-center py-24">
|
||||
<div className="flex h-20 w-20 items-center justify-center rounded-2xl bg-red-50 border border-red-100">
|
||||
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" className="text-red-500">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<p className="mt-5 text-sm font-medium text-foreground">加载失败</p>
|
||||
<p className="mt-1 text-xs text-muted-foreground">{error}</p>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => fetchTools()}
|
||||
className="mt-4 rounded-xl"
|
||||
>
|
||||
重新加载
|
||||
</Button>
|
||||
</div>
|
||||
) : loading ? (
|
||||
<div className="overflow-x-auto rounded-2xl border border-border/50 bg-white">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-border/50">
|
||||
{Array.from({ length: 6 }).map((_, i) => (
|
||||
<th key={i} className="px-5 py-3.5">
|
||||
<div className="h-3 w-14 animate-pulse rounded bg-muted/30" />
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{Array.from({ length: 5 }).map((_, i) => (
|
||||
<tr key={i} className="border-b border-border/40">
|
||||
<td className="px-5 py-3.5">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="h-9 w-9 animate-pulse rounded-lg bg-muted/30" />
|
||||
<div className="space-y-1.5">
|
||||
<div className="h-3 w-24 animate-pulse rounded bg-muted/20" />
|
||||
<div className="h-2.5 w-40 animate-pulse rounded bg-muted/20" />
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
{Array.from({ length: 5 }).map((_, j) => (
|
||||
<td key={j} className="px-5 py-3.5">
|
||||
<div className="h-3 w-12 animate-pulse rounded bg-muted/20" />
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
) : tools.length === 0 ? (
|
||||
<div className="flex flex-col items-center justify-center py-24">
|
||||
|
||||
Reference in New Issue
Block a user