fix(skills): P3 frontend polish for skill/agent category split
Deploy to Production / deploy (push) Waiting to run
Details
Deploy to Production / deploy (push) Waiting to run
Details
- skills.ts: make category/agent_type/execution_mode/task_mode optional in ISkillInfo and ISkillDetail for backward compat during rollout - SkillCard.vue: remove dead size="small" on a-tag, add title attr for a11y, add isEngine computed, CSS fallback cleanup, category fallback in class binding - SkillsView.vue: fix a-empty condition to use grouped counts so orphan skills (category mismatch) don't render a blank page - SkillsTab.vue: add type tag (引擎/技能) and category-based icon (thunderbolt for engine, appstore for business), remove size="small", add engine icon color variant
This commit is contained in:
parent
a672dddc9a
commit
3d108dd08e
|
|
@ -13,11 +13,11 @@ export interface ISkillInfo {
|
||||||
capabilities: string[]
|
capabilities: string[]
|
||||||
dependencies: string[]
|
dependencies: string[]
|
||||||
status: string
|
status: string
|
||||||
/** "agent_template" = 通用执行引擎 (react/direct/rewoo/...); "business_skill" = 业务领域技能 */
|
/** "agent_template" = 通用执行引擎 (react/direct/rewoo/...); "business_skill" = 业务领域技能. Optional for backward compat during rollout. */
|
||||||
category: SkillCategory
|
category?: SkillCategory
|
||||||
agent_type: string
|
agent_type?: string
|
||||||
execution_mode: string
|
execution_mode?: string
|
||||||
task_mode: string
|
task_mode?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ISkillDetail {
|
export interface ISkillDetail {
|
||||||
|
|
@ -28,10 +28,10 @@ export interface ISkillDetail {
|
||||||
dependencies: string[]
|
dependencies: string[]
|
||||||
config: Record<string, unknown>
|
config: Record<string, unknown>
|
||||||
health_status: string
|
health_status: string
|
||||||
category: SkillCategory
|
category?: SkillCategory
|
||||||
agent_type: string
|
agent_type?: string
|
||||||
execution_mode: string
|
execution_mode?: string
|
||||||
task_mode: string
|
task_mode?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ICapabilityInfo {
|
export interface ICapabilityInfo {
|
||||||
|
|
|
||||||
|
|
@ -20,11 +20,19 @@
|
||||||
v-for="skill in skillsStore.skills"
|
v-for="skill in skillsStore.skills"
|
||||||
:key="skill.name"
|
:key="skill.name"
|
||||||
class="skills-tab__item"
|
class="skills-tab__item"
|
||||||
|
:class="`skills-tab__item--${skill.category || 'business_skill'}`"
|
||||||
@click="openSkill(skill.name)"
|
@click="openSkill(skill.name)"
|
||||||
>
|
>
|
||||||
<div class="skills-tab__item-header">
|
<div class="skills-tab__item-header">
|
||||||
<AppstoreOutlined class="skills-tab__item-icon" />
|
<component :is="iconFor(skill)" class="skills-tab__item-icon" />
|
||||||
<span class="skills-tab__item-name">{{ skill.name }}</span>
|
<span class="skills-tab__item-name">{{ skill.name }}</span>
|
||||||
|
<a-tag
|
||||||
|
class="skills-tab__item-type"
|
||||||
|
:color="isEngine(skill) ? 'purple' : 'blue'"
|
||||||
|
:title="isEngine(skill) ? '执行引擎模板' : '业务领域技能'"
|
||||||
|
>
|
||||||
|
{{ isEngine(skill) ? '引擎' : '技能' }}
|
||||||
|
</a-tag>
|
||||||
<a-badge
|
<a-badge
|
||||||
class="skills-tab__item-status"
|
class="skills-tab__item-status"
|
||||||
:status="skill.status === 'active' ? 'success' : 'default'"
|
:status="skill.status === 'active' ? 'success' : 'default'"
|
||||||
|
|
@ -33,7 +41,7 @@
|
||||||
</div>
|
</div>
|
||||||
<p class="skills-tab__item-desc">{{ skill.description || '暂无描述' }}</p>
|
<p class="skills-tab__item-desc">{{ skill.description || '暂无描述' }}</p>
|
||||||
<div class="skills-tab__item-tags">
|
<div class="skills-tab__item-tags">
|
||||||
<a-tag v-for="cap in skill.capabilities.slice(0, 3)" :key="cap" size="small">
|
<a-tag v-for="cap in skill.capabilities.slice(0, 3)" :key="cap">
|
||||||
{{ cap }}
|
{{ cap }}
|
||||||
</a-tag>
|
</a-tag>
|
||||||
<span v-if="skill.capabilities.length > 3" class="skills-tab__item-more">
|
<span v-if="skill.capabilities.length > 3" class="skills-tab__item-more">
|
||||||
|
|
@ -53,13 +61,22 @@
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { AppstoreOutlined, WarningOutlined } from '@ant-design/icons-vue'
|
import { AppstoreOutlined, ThunderboltOutlined, WarningOutlined } from '@ant-design/icons-vue'
|
||||||
import { useSkillsStore } from '@/stores/skills'
|
import { useSkillsStore } from '@/stores/skills'
|
||||||
|
import type { ISkillInfo } from '@/api/skills'
|
||||||
import SkillDetail from '@/components/skills/SkillDetail.vue'
|
import SkillDetail from '@/components/skills/SkillDetail.vue'
|
||||||
|
|
||||||
const skillsStore = useSkillsStore()
|
const skillsStore = useSkillsStore()
|
||||||
const detailVisible = ref(false)
|
const detailVisible = ref(false)
|
||||||
|
|
||||||
|
function isEngine(skill: ISkillInfo): boolean {
|
||||||
|
return skill.category === 'agent_template'
|
||||||
|
}
|
||||||
|
|
||||||
|
function iconFor(skill: ISkillInfo) {
|
||||||
|
return isEngine(skill) ? ThunderboltOutlined : AppstoreOutlined
|
||||||
|
}
|
||||||
|
|
||||||
async function openSkill(name: string): Promise<void> {
|
async function openSkill(name: string): Promise<void> {
|
||||||
await skillsStore.fetchSkillDetail(name)
|
await skillsStore.fetchSkillDetail(name)
|
||||||
detailVisible.value = true
|
detailVisible.value = true
|
||||||
|
|
@ -130,6 +147,14 @@ onMounted(() => {
|
||||||
font-size: var(--font-sm);
|
font-size: var(--font-sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.skills-tab__item--agent_template .skills-tab__item-icon {
|
||||||
|
color: var(--accent-board);
|
||||||
|
}
|
||||||
|
|
||||||
|
.skills-tab__item-type {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.skills-tab__item-name {
|
.skills-tab__item-name {
|
||||||
font-weight: var(--font-weight-medium);
|
font-weight: var(--font-weight-medium);
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="skill-card" :class="`skill-card--${skill.category}`" @click="$emit('click')">
|
<div class="skill-card" :class="`skill-card--${skill.category || 'business_skill'}`" @click="$emit('click')">
|
||||||
<a-card hoverable size="small">
|
<a-card hoverable size="small">
|
||||||
<template #title>
|
<template #title>
|
||||||
<div class="skill-card__title">
|
<div class="skill-card__title">
|
||||||
|
|
@ -10,11 +10,11 @@
|
||||||
<template #extra>
|
<template #extra>
|
||||||
<div class="skill-card__badges">
|
<div class="skill-card__badges">
|
||||||
<a-tag
|
<a-tag
|
||||||
:color="skill.category === 'agent_template' ? 'purple' : 'blue'"
|
:color="isEngine ? 'purple' : 'blue'"
|
||||||
size="small"
|
|
||||||
class="skill-card__type-tag"
|
class="skill-card__type-tag"
|
||||||
|
:title="isEngine ? '执行引擎模板' : '业务领域技能'"
|
||||||
>
|
>
|
||||||
{{ skill.category === 'agent_template' ? '引擎' : '技能' }}
|
{{ isEngine ? '引擎' : '技能' }}
|
||||||
</a-tag>
|
</a-tag>
|
||||||
<a-badge
|
<a-badge
|
||||||
:status="skill.status === 'active' ? 'success' : 'default'"
|
:status="skill.status === 'active' ? 'success' : 'default'"
|
||||||
|
|
@ -28,13 +28,13 @@
|
||||||
<code class="skill-card__mode-value">{{ modeText }}</code>
|
<code class="skill-card__mode-value">{{ modeText }}</code>
|
||||||
</div>
|
</div>
|
||||||
<div class="skill-card__tags">
|
<div class="skill-card__tags">
|
||||||
<a-tag v-for="cap in skill.capabilities" :key="cap" size="small" color="blue">
|
<a-tag v-for="cap in skill.capabilities" :key="cap" color="blue">
|
||||||
{{ cap }}
|
{{ cap }}
|
||||||
</a-tag>
|
</a-tag>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="skill.dependencies.length > 0" class="skill-card__deps">
|
<div v-if="skill.dependencies.length > 0" class="skill-card__deps">
|
||||||
<span class="skill-card__deps-label">依赖:</span>
|
<span class="skill-card__deps-label">依赖:</span>
|
||||||
<a-tag v-for="dep in skill.dependencies.slice(0, 3)" :key="dep" size="small">
|
<a-tag v-for="dep in skill.dependencies.slice(0, 3)" :key="dep">
|
||||||
{{ dep }}
|
{{ dep }}
|
||||||
</a-tag>
|
</a-tag>
|
||||||
<span v-if="skill.dependencies.length > 3" class="skill-card__more">
|
<span v-if="skill.dependencies.length > 3" class="skill-card__more">
|
||||||
|
|
@ -61,22 +61,18 @@ defineEmits<{
|
||||||
click: []
|
click: []
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
const isEngine = computed(() => props.skill.category === 'agent_template')
|
||||||
|
|
||||||
// Differentiate icon by category: engine = thunderbolt, business = appstore
|
// Differentiate icon by category: engine = thunderbolt, business = appstore
|
||||||
const titleIcon = computed(() =>
|
const titleIcon = computed(() => (isEngine.value ? ThunderboltOutlined : AppstoreOutlined))
|
||||||
props.skill.category === 'agent_template' ? ThunderboltOutlined : AppstoreOutlined
|
|
||||||
)
|
|
||||||
|
|
||||||
// Show the most relevant mode field.
|
// Show the most relevant mode field.
|
||||||
// Agent templates: execution_mode (react/direct/rewoo/...).
|
// Agent templates: execution_mode (react/direct/rewoo/...).
|
||||||
// Business skills: agent_type (the domain identifier).
|
// Business skills: agent_type (the domain identifier).
|
||||||
const modeText = computed(() =>
|
const modeText = computed(() =>
|
||||||
props.skill.category === 'agent_template'
|
isEngine.value ? props.skill.execution_mode : props.skill.agent_type
|
||||||
? props.skill.execution_mode
|
|
||||||
: props.skill.agent_type
|
|
||||||
)
|
|
||||||
const modeLabel = computed(() =>
|
|
||||||
props.skill.category === 'agent_template' ? '执行模式' : '领域类型'
|
|
||||||
)
|
)
|
||||||
|
const modeLabel = computed(() => (isEngine.value ? '执行模式' : '领域类型'))
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
@ -118,7 +114,7 @@ const modeLabel = computed(() =>
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
margin-bottom: var(--space-2);
|
margin-bottom: var(--space-2);
|
||||||
font-size: var(--font-xs, 12px);
|
font-size: var(--font-xs);
|
||||||
}
|
}
|
||||||
|
|
||||||
.skill-card__mode-label {
|
.skill-card__mode-label {
|
||||||
|
|
@ -128,7 +124,7 @@ const modeLabel = computed(() =>
|
||||||
.skill-card__mode-value {
|
.skill-card__mode-value {
|
||||||
font-family: 'SF Mono', 'Fira Code', Menlo, Consolas, monospace;
|
font-family: 'SF Mono', 'Fira Code', Menlo, Consolas, monospace;
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
background: var(--bg-secondary, rgba(0, 0, 0, 0.04));
|
background: var(--bg-secondary);
|
||||||
padding: 1px 4px;
|
padding: 1px 4px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<a-empty
|
<a-empty
|
||||||
v-if="!skillsStore.isLoading && skillsStore.skills.length === 0"
|
v-if="!skillsStore.isLoading && skillsStore.agentTemplates.length + skillsStore.businessSkills.length === 0"
|
||||||
description="暂无已注册技能"
|
description="暂无已注册技能"
|
||||||
/>
|
/>
|
||||||
</a-spin>
|
</a-spin>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue