feat(skills): 技能管理 - 支持后台维护一级分类 (#694)

Co-authored-by: ddcat666 <26564566+ddcat666@users.noreply.github.com>
This commit is contained in:
Catlaxy AI
2026-10-06 09:28:03 +08:00
committed by GitHub
parent fb4af9bd34
commit fe2b892f1f
29 changed files with 579 additions and 37 deletions

View File

@@ -5,7 +5,7 @@ import { Check, LoaderCircle, Plus, Search, Sparkles, Users } from "lucide-react
import { AppModal } from "@/components/ui/product/app-modal";
import type { CanvasTheme } from "@/lib/canvas-theme";
import type { Skill, SkillCategory, SkillLibraryCategory } from "@/services/api/skills";
import { SkillCurationBrowser, matchesCuration, type useSkillCuration } from "@/components/skills/skill-curation-browser";
import { SkillCurationBrowser, matchesCuration, effectiveCurationRoot, type useSkillCuration } from "@/components/skills/skill-curation-browser";
type SkillLibraryTab = "enabled" | "installed" | "market";
@@ -135,6 +135,7 @@ export function CanvasAgentSkillLibraryModal({
setTab(value);
setLibraryCategoryId("all");
onCategoryChange("all");
onPlatformCategoryChange("");
};
const selectMarketplaceCategory = (value: string) => {
@@ -206,7 +207,7 @@ export function CanvasAgentSkillLibraryModal({
<div className="canvas-agent-skill-library-category-group">
<div className="canvas-agent-skill-library-category-heading">技能广场分类</div>
<SkillTab active={tab === "market" && category === "all"} label="全部技能" count={marketplaceCategoryTotal} onClick={() => changeTab("market")} />
{categoryItems.map((item) => (
{!curationState.curation?.enabled && categoryItems.map((item) => (
<SkillTab
key={item.value}
active={tab === "market" && category === item.value}
@@ -232,6 +233,7 @@ export function CanvasAgentSkillLibraryModal({
<SkillLibraryCard
key={skill.skillId}
skill={skill}
rootLabel={curationState.curation?.enabled ? curationState.curation.roots?.find((root) => root.id === effectiveCurationRoot(skill, curationState.curation!))?.name : undefined}
theme={theme}
categories={categories}
selected={selectedSkillIds.includes(skill.skillId)}
@@ -277,8 +279,9 @@ function SkillTab({ active, label, count, onClick }: { active: boolean; label: s
);
}
function SkillLibraryCard({ skill, theme, categories, selected, canSelect, onToggle, onInstall }: {
function SkillLibraryCard({ skill, rootLabel, theme, categories, selected, canSelect, onToggle, onInstall }: {
skill: Skill;
rootLabel?: string;
theme: CanvasTheme;
categories: SkillCategory[];
selected: boolean;
@@ -292,7 +295,7 @@ function SkillLibraryCard({ skill, theme, categories, selected, canSelect, onTog
|| skill.showcaseMedia?.find((item) => item.showcaseUrl);
const coverUrl = cover?.showcaseUrl;
const hasCover = Boolean(coverUrl && !coverFailed);
const categoryLabel = categories.find((item) => item.value === skill.tag)?.label || "其他";
const categoryLabel = rootLabel || categories.find((item) => item.value === skill.tag)?.label || "其他";
const author = skill.effectiveUser?.name || "影策创作者";
const addedCount = formatSkillCount(skill.addedCount || 0);

View File

@@ -115,6 +115,7 @@ export function CanvasCloudAgentPanel({ canvasId, domainProjectId, nodeCount, se
const skillBrowseKey = JSON.stringify([userId, skillTag, debouncedSkillSearch, platformCategory, curationState.curation?.revision, curationState.curation?.enabled]);
const skillBrowseKeyRef = useRef(skillBrowseKey);
skillBrowseKeyRef.current = skillBrowseKey;
useEffect(() => { if (curationState.curation?.enabled) setSkillTag("all"); }, [curationState.curation?.enabled]);
const [busy, setBusy] = useState(false);
const [approvalSubmitting, setApprovalSubmitting] = useState(false);
const [exporting, setExporting] = useState(false);