2026-05-25: 等级体系升级(20级)+论坛二级分类+会员空间
This commit is contained in:
@@ -32,6 +32,7 @@ interface Category {
|
||||
export default function PendingSkillsPage() {
|
||||
const [skills, setSkills] = useState<PendingSkill[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState("");
|
||||
const [categories, setCategories] = useState<Category[]>([]);
|
||||
const [page, setPage] = useState(1);
|
||||
const [total, setTotal] = useState(0);
|
||||
@@ -55,13 +56,14 @@ export default function PendingSkillsPage() {
|
||||
const [saving, setSaving] = useState(false);
|
||||
|
||||
const fetchSkills = useCallback(async () => {
|
||||
setError("");
|
||||
try {
|
||||
const res = await fetch(`/api/admin/pending-skills?page=${page}&pageSize=${pageSize}`);
|
||||
const data = await res.json();
|
||||
setSkills(data.skills || []);
|
||||
setTotal(data.total || 0);
|
||||
} catch {
|
||||
console.error("Failed to fetch skills");
|
||||
setError("加载待审核技能失败,请稍后重试");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -421,11 +423,54 @@ export default function PendingSkillsPage() {
|
||||
</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-200/60">
|
||||
<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={() => fetchSkills()}
|
||||
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">
|
||||
<th className="px-4 py-3.5 w-10">
|
||||
<div className="h-4 w-4 animate-pulse rounded bg-muted/30 mx-auto" />
|
||||
</th>
|
||||
{Array.from({ length: 7 }).map((_, i) => (
|
||||
<th key={i} className="px-4 py-3.5">
|
||||
<div className="h-3 w-12 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-4 py-3">
|
||||
<div className="h-4 w-4 animate-pulse rounded bg-muted/20 mx-auto" />
|
||||
</td>
|
||||
{Array.from({ length: 7 }).map((_, j) => (
|
||||
<td key={j} className="px-4 py-3">
|
||||
<div className="h-3 animate-pulse rounded bg-muted/20" style={{ width: `${60 + Math.floor(Math.random() * 60)}%` }} />
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
) : skills.length === 0 ? (
|
||||
<div className="flex flex-col items-center justify-center py-24">
|
||||
@@ -435,6 +480,7 @@ export default function PendingSkillsPage() {
|
||||
</svg>
|
||||
</div>
|
||||
<p className="mt-5 text-sm font-medium text-muted-foreground">暂无待审核技能</p>
|
||||
<p className="mt-1 text-xs text-muted-foreground/60">在探索技能页面发现新技能后即可在此审核</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="overflow-x-auto rounded-2xl border border-border/50 bg-white">
|
||||
|
||||
Reference in New Issue
Block a user