- 基于 agent-runtime 打造,默认 ReAct agent - 支持 agent 能力对接,已对接工作流、插件、知识库等 tool 能力 - 全新 agent 编排界面,支持可视化便捷配置 agent - 全新 agent 聊天界面,支持快捷操作、额外知识库选择等
353 lines
8.1 KiB
Vue
353 lines
8.1 KiB
Vue
<script setup lang="ts">
|
|
import type {AgentCapabilityKind} from '../types';
|
|
|
|
import {computed, onBeforeUnmount, onMounted, ref} from 'vue';
|
|
|
|
import {Connection, Files, Loading, Plus, Share, VideoPlay,} from '@element-plus/icons-vue';
|
|
|
|
const props = defineProps<{
|
|
offlineDisabled?: boolean;
|
|
offlineLoading?: boolean;
|
|
offlineVisible?: boolean;
|
|
publishDisabled?: boolean;
|
|
publishLoading?: boolean;
|
|
publishText: string;
|
|
saveLoading?: boolean;
|
|
tryoutDisabled?: boolean;
|
|
}>();
|
|
|
|
const isRepublish = computed(() => props.publishText === '重新发布');
|
|
|
|
const emit = defineEmits<{
|
|
add: [kind: AgentCapabilityKind];
|
|
offline: [];
|
|
publish: [];
|
|
save: [];
|
|
tryout: [];
|
|
}>();
|
|
|
|
const capabilityOpen = ref(false);
|
|
const capabilityRef = ref<HTMLElement>();
|
|
const capabilityItems = [
|
|
{
|
|
kind: 'knowledge' as const,
|
|
title: '知识库',
|
|
desc: '检索企业知识',
|
|
icon: Files,
|
|
},
|
|
{
|
|
kind: 'workflow' as const,
|
|
title: '工作流',
|
|
desc: '调用已发布流程',
|
|
icon: Share,
|
|
},
|
|
{
|
|
kind: 'plugin' as const,
|
|
title: '插件',
|
|
desc: '执行工具能力',
|
|
icon: Connection,
|
|
},
|
|
];
|
|
|
|
function handleAdd(kind: AgentCapabilityKind) {
|
|
capabilityOpen.value = false;
|
|
emit('add', kind);
|
|
}
|
|
|
|
function handlePointerDown(event: PointerEvent) {
|
|
if (!capabilityOpen.value || !capabilityRef.value) return;
|
|
const target = event.target as Node | null;
|
|
if (target && !capabilityRef.value.contains(target)) {
|
|
capabilityOpen.value = false;
|
|
}
|
|
}
|
|
|
|
onMounted(() => {
|
|
window.addEventListener('pointerdown', handlePointerDown);
|
|
});
|
|
|
|
onBeforeUnmount(() => {
|
|
window.removeEventListener('pointerdown', handlePointerDown);
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<div class="agent-command-bar">
|
|
<div class="agent-command-bar__group">
|
|
<div ref="capabilityRef" class="agent-command-bar__capability">
|
|
<div v-if="capabilityOpen" class="agent-command-bar__capability-panel">
|
|
<button
|
|
v-for="item in capabilityItems"
|
|
:key="item.kind"
|
|
class="agent-command-bar__capability-item"
|
|
type="button"
|
|
@click="handleAdd(item.kind)"
|
|
>
|
|
<span class="agent-command-bar__capability-icon">
|
|
<component :is="item.icon" />
|
|
</span>
|
|
<span class="agent-command-bar__capability-meta">
|
|
<span class="agent-command-bar__capability-title">
|
|
{{ item.title }}
|
|
</span>
|
|
<span class="agent-command-bar__capability-desc">
|
|
{{ item.desc }}
|
|
</span>
|
|
</span>
|
|
</button>
|
|
</div>
|
|
<button
|
|
class="agent-command-bar__button agent-command-bar__button--add"
|
|
type="button"
|
|
:aria-expanded="capabilityOpen"
|
|
@click="capabilityOpen = !capabilityOpen"
|
|
>
|
|
<Plus />
|
|
<span>{{ capabilityOpen ? '收起能力' : '增加能力' }}</span>
|
|
</button>
|
|
</div>
|
|
<div class="agent-command-bar__divider"></div>
|
|
<button
|
|
class="agent-command-bar__button agent-command-bar__button--primary"
|
|
type="button"
|
|
:disabled="saveLoading"
|
|
@click="emit('save')"
|
|
>
|
|
<Loading v-if="saveLoading" class="is-loading" />
|
|
<span>保存</span>
|
|
</button>
|
|
<div class="agent-command-bar__divider"></div>
|
|
<button
|
|
v-if="offlineVisible"
|
|
class="agent-command-bar__button agent-command-bar__button--ghost"
|
|
type="button"
|
|
:disabled="offlineDisabled || offlineLoading || publishLoading"
|
|
@click="emit('offline')"
|
|
>
|
|
<Loading v-if="offlineLoading" class="is-loading" />
|
|
<span>下线</span>
|
|
</button>
|
|
<button
|
|
class="agent-command-bar__button agent-command-bar__button--ghost"
|
|
:class="{ 'agent-command-bar__button--republish': isRepublish }"
|
|
type="button"
|
|
:disabled="publishDisabled || publishLoading"
|
|
@click="emit('publish')"
|
|
>
|
|
<Loading v-if="publishLoading" class="is-loading" />
|
|
<span>{{ publishText }}</span>
|
|
</button>
|
|
</div>
|
|
<button
|
|
class="agent-command-bar__run"
|
|
type="button"
|
|
:disabled="tryoutDisabled"
|
|
@click="emit('tryout')"
|
|
>
|
|
<VideoPlay />
|
|
<span>试运行</span>
|
|
</button>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.agent-command-bar {
|
|
position: absolute;
|
|
bottom: 16px;
|
|
left: 50%;
|
|
z-index: 30;
|
|
display: flex;
|
|
gap: 8px;
|
|
align-items: center;
|
|
transform: translateX(-50%);
|
|
}
|
|
|
|
.agent-command-bar__group {
|
|
display: inline-flex;
|
|
gap: 2px;
|
|
align-items: center;
|
|
height: 40px;
|
|
padding: 0 4px;
|
|
background: var(--el-bg-color);
|
|
border: 1px solid var(--el-border-color);
|
|
border-radius: 10px;
|
|
box-shadow: 0 2px 8px rgb(0 0 0 / 8%);
|
|
}
|
|
|
|
.agent-command-bar__button,
|
|
.agent-command-bar__run {
|
|
all: unset;
|
|
box-sizing: border-box;
|
|
display: inline-flex;
|
|
flex-shrink: 0;
|
|
gap: 6px;
|
|
align-items: center;
|
|
justify-content: center;
|
|
height: 32px;
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
line-height: 1;
|
|
cursor: pointer;
|
|
border-radius: 8px;
|
|
transition:
|
|
background 0.15s,
|
|
color 0.15s,
|
|
filter 0.15s,
|
|
opacity 0.15s;
|
|
}
|
|
|
|
.agent-command-bar__button {
|
|
padding: 0 12px;
|
|
}
|
|
|
|
.agent-command-bar__capability {
|
|
position: relative;
|
|
display: inline-flex;
|
|
}
|
|
|
|
.agent-command-bar__capability-panel {
|
|
position: absolute;
|
|
bottom: calc(100% + 10px);
|
|
left: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 4px;
|
|
width: 224px;
|
|
padding: 8px;
|
|
background: var(--el-bg-color-overlay);
|
|
border: 1px solid var(--el-border-color);
|
|
border-radius: 10px;
|
|
box-shadow: var(--el-box-shadow-light);
|
|
}
|
|
|
|
.agent-command-bar__capability-item {
|
|
all: unset;
|
|
box-sizing: border-box;
|
|
display: flex;
|
|
gap: 10px;
|
|
align-items: center;
|
|
padding: 8px;
|
|
cursor: pointer;
|
|
border-radius: 8px;
|
|
transition: background 0.15s;
|
|
}
|
|
|
|
.agent-command-bar__capability-item:hover {
|
|
background: var(--el-fill-color-light);
|
|
}
|
|
|
|
.agent-command-bar__capability-icon {
|
|
display: inline-flex;
|
|
flex-shrink: 0;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 30px;
|
|
height: 30px;
|
|
color: var(--el-color-primary);
|
|
background: var(--el-color-primary-light-9);
|
|
border-radius: 8px;
|
|
}
|
|
|
|
.agent-command-bar__capability-meta {
|
|
display: flex;
|
|
flex: 1;
|
|
flex-direction: column;
|
|
min-width: 0;
|
|
}
|
|
|
|
.agent-command-bar__capability-title {
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
color: var(--el-text-color-primary);
|
|
}
|
|
|
|
.agent-command-bar__capability-desc {
|
|
margin-top: 2px;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
font-size: 12px;
|
|
color: var(--el-text-color-secondary);
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.agent-command-bar__button--add {
|
|
color: var(--el-color-primary);
|
|
background: var(--el-color-primary-light-9);
|
|
}
|
|
|
|
.agent-command-bar__button--add:hover {
|
|
background: var(--el-color-primary-light-8);
|
|
}
|
|
|
|
.agent-command-bar__button--primary {
|
|
color: var(--el-color-primary);
|
|
background: var(--el-color-primary-light-9);
|
|
}
|
|
|
|
.agent-command-bar__button--ghost {
|
|
color: var(--el-text-color-secondary);
|
|
}
|
|
|
|
.agent-command-bar__button--republish {
|
|
color: var(--el-color-warning);
|
|
background: var(--el-color-warning-light-9);
|
|
}
|
|
|
|
.agent-command-bar__button:hover:not(:disabled) {
|
|
color: var(--el-text-color-primary);
|
|
background: var(--el-fill-color-light);
|
|
}
|
|
|
|
.agent-command-bar__button--republish:hover:not(:disabled) {
|
|
color: var(--el-color-warning);
|
|
background: var(--el-color-warning-light-8);
|
|
}
|
|
|
|
.agent-command-bar__button--primary:hover:not(:disabled) {
|
|
color: var(--el-color-primary);
|
|
background: var(--el-color-primary-light-8);
|
|
}
|
|
|
|
.agent-command-bar__divider {
|
|
width: 1px;
|
|
height: 20px;
|
|
margin: 0 4px;
|
|
background: var(--el-border-color);
|
|
}
|
|
|
|
.agent-command-bar__run {
|
|
padding: 0 14px;
|
|
color: #fff;
|
|
background: #13b33f;
|
|
}
|
|
|
|
.agent-command-bar__run:hover:not(:disabled) {
|
|
filter: brightness(0.95);
|
|
}
|
|
|
|
.agent-command-bar__button:disabled,
|
|
.agent-command-bar__run:disabled {
|
|
cursor: not-allowed;
|
|
opacity: 0.55;
|
|
}
|
|
|
|
.agent-command-bar svg {
|
|
width: 14px;
|
|
height: 14px;
|
|
}
|
|
|
|
.agent-command-bar .is-loading {
|
|
animation: agent-command-spin 1s linear infinite;
|
|
}
|
|
|
|
@keyframes agent-command-spin {
|
|
from {
|
|
transform: rotate(0deg);
|
|
}
|
|
|
|
to {
|
|
transform: rotate(360deg);
|
|
}
|
|
}
|
|
</style>
|