2026-05-25: 等级体系升级(20级)+论坛二级分类+会员空间

This commit is contained in:
ZhuiGuangAI Dev
2026-05-24 23:05:38 +08:00
parent 6c78c32157
commit 9bf3e7574a
118 changed files with 11842 additions and 3262 deletions
+52 -6
View File
@@ -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">