feat: 按来源分组展示 Skill

This commit is contained in:
Wenjie Zhang 2026-06-03 22:12:32 +08:00
parent a60c5db7ee
commit fafbc5c9ee

View File

@ -76,49 +76,54 @@
</div> </div>
<template v-else> <template v-else>
<ExtensionCardGrid :min-width="360"> <template v-for="group in skillGroups" :key="group.key">
<div <template v-if="group.skills.length">
v-for="skill in filteredInstalledSkills" <div class="extension-section-header">{{ group.title }}</div>
:key="skill.slug" <ExtensionCardGrid :min-width="360">
class="card-wrapper" <div
:class="{ v-for="skill in group.skills"
selected: selectedCardSlugs.includes(skill.slug), :key="skill.slug"
'batch-mode': isBatchDeleteMode class="card-wrapper"
}" :class="{
> selected: selectedCardSlugs.includes(skill.slug),
<a-checkbox 'batch-mode': isBatchDeleteMode
v-if="isBatchDeleteMode && canManageSkill(skill) && skill.sourceType !== 'builtin'" }"
:checked="selectedCardSlugs.includes(skill.slug)" >
@change="handleToggleCardSelect(skill.slug)" <a-checkbox
class="card-select-checkbox" v-if="isBatchDeleteMode && canManageSkill(skill) && skill.sourceType !== 'builtin'"
/> :checked="selectedCardSlugs.includes(skill.slug)"
<InfoCard @change="handleToggleCardSelect(skill.slug)"
variant="mini" class="card-select-checkbox"
:title="formatExtensionCardTitle(skill.name)" />
:description="skill.description || '暂无描述'" <InfoCard
:default-icon="BookMarkedIcon" variant="mini"
@click="handleCardClick(skill)" :title="formatExtensionCardTitle(skill.name)"
:class="{ 'card-clickable-select': isBatchDeleteMode }" :description="skill.description || '暂无描述'"
> :default-icon="BookMarkedIcon"
<template #action> @click="handleCardClick(skill)"
<button :class="{ 'card-clickable-select': isBatchDeleteMode }"
type="button"
class="skill-enabled-action"
:class="{ enabled: skill.enabled !== false }"
:disabled="!canManageSkill(skill) || isSkillToggling(skill.slug)"
:aria-label="skill.enabled === false ? '启用 Skill' : '禁用 Skill'"
@click.stop="handleToggleSkillEnabled(skill)"
> >
<Plus v-if="skill.enabled === false" :size="15" class="action-icon" /> <template #action>
<template v-else> <button
<Check :size="15" class="action-icon action-icon-check" /> type="button"
<Minus :size="15" class="action-icon action-icon-minus" /> class="skill-enabled-action"
:class="{ enabled: skill.enabled !== false }"
:disabled="!canManageSkill(skill) || isSkillToggling(skill.slug)"
:aria-label="skill.enabled === false ? '启用 Skill' : '禁用 Skill'"
@click.stop="handleToggleSkillEnabled(skill)"
>
<Plus v-if="skill.enabled === false" :size="15" class="action-icon" />
<template v-else>
<Check :size="15" class="action-icon action-icon-check" />
<Minus :size="15" class="action-icon action-icon-minus" />
</template>
</button>
</template> </template>
</button> </InfoCard>
</template> </div>
</InfoCard> </ExtensionCardGrid>
</div> </template>
</ExtensionCardGrid> </template>
</template> </template>
<a-modal <a-modal
@ -610,6 +615,18 @@ const installedSkillCards = computed(() =>
) )
const filteredInstalledSkills = computed(() => installedSkillCards.value.filter(matchesSearch)) const filteredInstalledSkills = computed(() => installedSkillCards.value.filter(matchesSearch))
const skillGroups = computed(() => [
{
key: 'builtin',
title: '内置',
skills: filteredInstalledSkills.value.filter((skill) => skill.sourceType === 'builtin')
},
{
key: 'uploaded',
title: '上传的',
skills: filteredInstalledSkills.value.filter((skill) => skill.sourceType !== 'builtin')
}
])
const filteredDeletableSkills = computed(() => const filteredDeletableSkills = computed(() =>
filteredInstalledSkills.value.filter( filteredInstalledSkills.value.filter(
(skill) => canManageSkill(skill) && skill.sourceType !== 'builtin' (skill) => canManageSkill(skill) && skill.sourceType !== 'builtin'