import { useEffect, useState } from "react"; import { Alert, App, Button, Input, InputNumber, Select } from "antd"; import { Check, Edit3, Plus, RefreshCw, Shapes, SlidersHorizontal } from "lucide-react"; import { AdminPageFrame } from "./components/admin-shell"; import { AdminDataTable, AdminStatusBadge, AdminTableEmpty, SettingsSectionCard } from "./components/admin-ui"; import { AdminSwitch } from "./ui/controls"; import { getSkillCuration, updateSkillCuration, type CurationCategory, type CurationChange, type CurationRoot, type SkillCuration } from "@/services/api/skill-curation"; import { curationIcon, curationIconKeys, curationIconLabels } from "@/components/skills/skill-curation-browser"; import { listSkills, type Skill } from "@/services/api/skills"; import "./skill-curation-page.css"; const emptyRoot = (): CurationRoot => ({ id: "", name: "", iconKey: "shapes", sortOrder: 0, enabled: true }); const emptyCategory = (rootTag = "drama"): CurationCategory => ({ id: "", rootTag, name: "", sortOrder: 0, enabled: true }); export default function SkillCurationPage() { const { message } = App.useApp(); const [data, setData] = useState(null); const [error, setError] = useState(""); const [busy, setBusy] = useState(false); const [root, setRoot] = useState(emptyRoot); const [category, setCategory] = useState(emptyCategory); const [search, setSearch] = useState(""); const [skills, setSkills] = useState([]); const [skillId, setSkillId] = useState(""); const [assignedRoot, setAssignedRoot] = useState(""); const [categoryIds, setCategoryIds] = useState([]); const selectedSkill = skills.find((skill) => skill.skillId === skillId); const selectedRoot = data?.roots?.find((item) => item.id === assignedRoot); const availableCategories = data?.categories.filter((item) => item.enabled && item.rootTag === (assignedRoot || selectedSkill?.tag)) ?? []; const reload = async () => { try { setData(await getSkillCuration(true)); setError(""); } catch (e) { setError(String(e)); } }; useEffect(() => { void reload(); }, []); useEffect(() => { let cancelled = false; const timer = setTimeout(() => { listSkills({ scope: "public", search, pageSize: 50 }) .then((result) => { if (!cancelled) setSkills(result.skills); }) .catch((e) => { if (!cancelled) setError(String(e)); }); }, 250); return () => { cancelled = true; clearTimeout(timer); }; }, [search]); const save = async (change: CurationChange) => { if (!data) return; setBusy(true); try { setData(await updateSkillCuration(data.revision, change)); setError(""); message.success("已保存"); window.dispatchEvent(new Event("canvas-skills-changed")); } catch (e) { setError(`${String(e)};请重新加载后核对修改`); } finally { setBusy(false); } }; const selectSkill = (id: string) => { const nextRoot = data?.rootAssignments?.find((item) => item.skillId === id)?.rootId || ""; setSkillId(id); setAssignedRoot(nextRoot); setCategoryIds(data?.assignments.filter((item) => item.skillId === id).map((item) => item.categoryId) || []); }; return ( } onClick={() => void reload()} disabled={busy}> 重新加载 } >
{error ? : null}
平台策展模式

开启后,技能库会优先使用这里维护的分类和归属;关闭时不影响现有技能展示。

void save({ enabled })} />