fix: 完成系统向智能体数据链路切换
- 切换工作台、聊天历史、资源候选与公共调用到 Agent - 加固资源绑定、删除保护及发布运行并发控制 - 隔离旧 Bot 专属服务和组件并保留兼容入口
This commit is contained in:
@@ -21,6 +21,7 @@ import { hasPermission } from '#/api/common/hasPermission';
|
||||
import { api } from '#/api/request';
|
||||
import { $t } from '#/locales';
|
||||
import { router } from '#/router';
|
||||
import AgentApprovalSnapshotPreview from '#/views/system/approval/components/AgentApprovalSnapshotPreview.vue';
|
||||
import BotApprovalSnapshotPreview from '#/views/system/approval/components/BotApprovalSnapshotPreview.vue';
|
||||
import KnowledgeApprovalSnapshotPreview from '#/views/system/approval/components/KnowledgeApprovalSnapshotPreview.vue';
|
||||
import WorkflowApprovalSnapshotPreview from '#/views/system/approval/components/WorkflowApprovalSnapshotPreview.vue';
|
||||
@@ -33,6 +34,7 @@ const detail = ref<any>(null);
|
||||
const approvalActionLoading = ref<'approve' | 'reject' | 'revoke' | null>(null);
|
||||
|
||||
const resourceLabelMap: Record<string, string> = {
|
||||
AGENT: $t('approval.resource.agent'),
|
||||
BOT: $t('approval.resource.bot'),
|
||||
KNOWLEDGE: $t('approval.resource.knowledge'),
|
||||
WORKFLOW: $t('approval.resource.workflow'),
|
||||
@@ -91,6 +93,13 @@ const botSnapshot = computed(() => {
|
||||
return detail.value?.snapshotJson?.resourceSnapshot || null;
|
||||
});
|
||||
|
||||
const agentSnapshot = computed(() => {
|
||||
if (detail.value?.resourceType !== 'AGENT') {
|
||||
return null;
|
||||
}
|
||||
return detail.value?.snapshotJson?.resourceSnapshot || null;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
void loadDetail();
|
||||
});
|
||||
@@ -498,8 +507,12 @@ function formatEventInfo(row: Record<string, any>) {
|
||||
<div class="approval-detail__panel-header">
|
||||
<h3>{{ $t('approval.section.snapshot') }}</h3>
|
||||
</div>
|
||||
<AgentApprovalSnapshotPreview
|
||||
v-if="agentSnapshot"
|
||||
:snapshot="agentSnapshot"
|
||||
/>
|
||||
<WorkflowApprovalSnapshotPreview
|
||||
v-if="workflowSnapshot"
|
||||
v-else-if="workflowSnapshot"
|
||||
:title="workflowSnapshot.title"
|
||||
:description="workflowSnapshot.description"
|
||||
:content="workflowSnapshot.content"
|
||||
|
||||
@@ -41,7 +41,7 @@ defineExpose({
|
||||
openDialog,
|
||||
});
|
||||
|
||||
type ResourceType = '' | 'BOT' | 'KNOWLEDGE' | 'WORKFLOW';
|
||||
type ResourceType = '' | 'AGENT' | 'BOT' | 'KNOWLEDGE' | 'WORKFLOW';
|
||||
type ActionType = '' | 'DELETE' | 'OFFLINE' | 'PUBLISH';
|
||||
type ScopeType = 'CATEGORY' | 'DEPT';
|
||||
type FlowStatus = 'DISABLED' | 'ENABLED';
|
||||
@@ -88,7 +88,7 @@ interface FlowFormModel {
|
||||
}
|
||||
|
||||
const RESOURCE_OPTIONS = [
|
||||
{ label: $t('approval.resource.bot'), value: 'BOT' },
|
||||
{ label: $t('approval.resource.agent'), value: 'AGENT' },
|
||||
{ label: $t('approval.resource.workflow'), value: 'WORKFLOW' },
|
||||
{ label: $t('approval.resource.knowledge'), value: 'KNOWLEDGE' },
|
||||
];
|
||||
@@ -128,6 +128,7 @@ const deptTreeOptions = ref<any[]>([]);
|
||||
const roleOptions = ref<SelectOption[]>([]);
|
||||
const accountOptions = ref<SelectOption[]>([]);
|
||||
const categoryOptions = ref<Record<Exclude<ResourceType, ''>, SelectOption[]>>({
|
||||
AGENT: [],
|
||||
BOT: [],
|
||||
KNOWLEDGE: [],
|
||||
WORKFLOW: [],
|
||||
@@ -188,7 +189,7 @@ function buildDefaultForm(): FlowFormModel {
|
||||
name: '',
|
||||
priority: 100,
|
||||
remark: '',
|
||||
resourceType: 'BOT',
|
||||
resourceType: 'AGENT',
|
||||
scopes: [],
|
||||
status: 'ENABLED',
|
||||
steps: [buildDefaultStep()],
|
||||
@@ -236,7 +237,7 @@ async function openDialog(row: any = {}) {
|
||||
name: res.data?.name || '',
|
||||
priority: Number(res.data?.priority || 100),
|
||||
remark: res.data?.remark || '',
|
||||
resourceType: res.data?.resourceType || 'BOT',
|
||||
resourceType: res.data?.resourceType || 'AGENT',
|
||||
scopes: (res.data?.scopes || []).map((item: any) => ({
|
||||
id: item.id,
|
||||
includeChildren: item.includeChildren === 1,
|
||||
@@ -279,8 +280,8 @@ async function ensureCategoryOptions() {
|
||||
if (categoryLoaded.value) {
|
||||
return;
|
||||
}
|
||||
const [botRes, workflowRes, knowledgeRes] = await Promise.all([
|
||||
api.get('/api/v1/botCategory/list', {
|
||||
const [agentRes, workflowRes, knowledgeRes] = await Promise.all([
|
||||
api.get('/api/v1/agentCategory/list', {
|
||||
params: { sortKey: 'sortNo', sortType: 'asc' },
|
||||
}),
|
||||
api.get('/api/v1/workflowCategory/list', {
|
||||
@@ -291,7 +292,8 @@ async function ensureCategoryOptions() {
|
||||
}),
|
||||
]);
|
||||
categoryOptions.value = {
|
||||
BOT: normalizeCategoryOptions(botRes.data, 'categoryName'),
|
||||
AGENT: normalizeCategoryOptions(agentRes.data, 'categoryName'),
|
||||
BOT: [],
|
||||
KNOWLEDGE: normalizeCategoryOptions(knowledgeRes.data, 'categoryName'),
|
||||
WORKFLOW: normalizeCategoryOptions(workflowRes.data, 'categoryName'),
|
||||
};
|
||||
|
||||
@@ -35,6 +35,7 @@ import ApprovalFlowModal from './ApprovalFlowModal.vue';
|
||||
type ApprovalTabName = 'flow' | 'initiated' | 'pending' | 'processed';
|
||||
|
||||
const RESOURCE_OPTIONS = [
|
||||
{ label: $t('approval.resource.agent'), value: 'AGENT' },
|
||||
{ label: $t('approval.resource.bot'), value: 'BOT' },
|
||||
{ label: $t('approval.resource.workflow'), value: 'WORKFLOW' },
|
||||
{ label: $t('approval.resource.knowledge'), value: 'KNOWLEDGE' },
|
||||
|
||||
@@ -0,0 +1,297 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { ElButton, ElTag } from 'element-plus';
|
||||
|
||||
import { $t } from '#/locales';
|
||||
|
||||
const props = defineProps<{
|
||||
snapshot?: null | Record<string, any>;
|
||||
}>();
|
||||
|
||||
const agent = computed(() => props.snapshot || {});
|
||||
const expandedPrompt = ref(false);
|
||||
|
||||
const modelItems = computed(() => {
|
||||
const model = agent.value.modelSummary || {};
|
||||
return [
|
||||
{
|
||||
label: $t('approval.snapshot.modelName'),
|
||||
value: model.title || model.modelName,
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
const promptPreview = computed(
|
||||
() =>
|
||||
agent.value.promptSummary?.systemPrompt ||
|
||||
agent.value.promptConfigJson?.systemPrompt ||
|
||||
'',
|
||||
);
|
||||
|
||||
const bindingGroups = computed(() => [
|
||||
{
|
||||
items: Array.isArray(agent.value.knowledgeSummaries)
|
||||
? agent.value.knowledgeSummaries
|
||||
: [],
|
||||
key: 'knowledge',
|
||||
label: $t('approval.snapshot.knowledgeBindings'),
|
||||
},
|
||||
{
|
||||
items: Array.isArray(agent.value.toolSummaries)
|
||||
? agent.value.toolSummaries
|
||||
: [],
|
||||
key: 'tool',
|
||||
label: $t('approval.snapshot.agentBindings'),
|
||||
},
|
||||
]);
|
||||
|
||||
function formatValue(value?: null | number | string) {
|
||||
if (value === null || value === undefined || value === '') {
|
||||
return $t('approval.snapshot.notConfigured');
|
||||
}
|
||||
return String(value);
|
||||
}
|
||||
|
||||
function getBindingName(item: Record<string, any>) {
|
||||
return (
|
||||
item.title ||
|
||||
item.toolName ||
|
||||
item.name ||
|
||||
$t('approval.snapshot.notConfigured')
|
||||
);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="agent-snapshot">
|
||||
<div class="agent-snapshot__hero">
|
||||
<div class="agent-snapshot__avatar">
|
||||
<img
|
||||
v-if="agent.avatar"
|
||||
:src="agent.avatar"
|
||||
:alt="agent.name || $t('approval.resource.agent')"
|
||||
/>
|
||||
<span v-else>{{ $t('approval.resource.agent').slice(0, 1) }}</span>
|
||||
</div>
|
||||
<div class="agent-snapshot__hero-copy">
|
||||
<div class="agent-snapshot__title">
|
||||
{{ agent.name || $t('approval.snapshot.untitledAgent') }}
|
||||
</div>
|
||||
<div v-if="agent.description" class="agent-snapshot__description">
|
||||
{{ agent.description }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="agent-snapshot__section">
|
||||
<div class="agent-snapshot__section-title">
|
||||
{{ $t('approval.snapshot.agentModelConfig') }}
|
||||
</div>
|
||||
<div class="agent-snapshot__info-grid">
|
||||
<div
|
||||
v-for="item in modelItems"
|
||||
:key="item.label"
|
||||
class="agent-snapshot__info-item"
|
||||
>
|
||||
<span>{{ item.label }}</span>
|
||||
<strong>{{ formatValue(item.value) }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="agent-snapshot__section">
|
||||
<div class="agent-snapshot__section-head">
|
||||
<div class="agent-snapshot__section-title">
|
||||
{{ $t('approval.snapshot.systemPrompt') }}
|
||||
</div>
|
||||
<ElButton
|
||||
v-if="promptPreview"
|
||||
link
|
||||
type="primary"
|
||||
@click="expandedPrompt = !expandedPrompt"
|
||||
>
|
||||
{{
|
||||
expandedPrompt
|
||||
? $t('approval.snapshot.collapsePrompt')
|
||||
: $t('approval.snapshot.expandPrompt')
|
||||
}}
|
||||
</ElButton>
|
||||
</div>
|
||||
<div
|
||||
v-if="promptPreview"
|
||||
class="agent-snapshot__prompt"
|
||||
:class="{ 'is-expanded': expandedPrompt }"
|
||||
>
|
||||
{{ promptPreview }}
|
||||
</div>
|
||||
<div v-else class="agent-snapshot__empty">
|
||||
{{ $t('approval.snapshot.notConfigured') }}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="agent-snapshot__section">
|
||||
<div class="agent-snapshot__section-title">
|
||||
{{ $t('approval.snapshot.agentBindings') }}
|
||||
</div>
|
||||
<div class="agent-snapshot__binding-grid">
|
||||
<div
|
||||
v-for="group in bindingGroups"
|
||||
:key="group.key"
|
||||
class="agent-snapshot__binding-group"
|
||||
>
|
||||
<div class="agent-snapshot__binding-head">
|
||||
<span>{{ group.label }}</span>
|
||||
<ElTag round effect="plain" type="info">
|
||||
{{ group.items.length }}
|
||||
</ElTag>
|
||||
</div>
|
||||
<div v-if="group.items.length" class="agent-snapshot__binding-list">
|
||||
<div
|
||||
v-for="item in group.items"
|
||||
:key="`${group.key}-${item.bindingId || item.id || item.targetId || item.knowledgeId}`"
|
||||
class="agent-snapshot__binding-item"
|
||||
>
|
||||
<span>{{ getBindingName(item) }}</span>
|
||||
<ElTag
|
||||
v-if="item.toolType || item.retrievalMode"
|
||||
round
|
||||
effect="plain"
|
||||
type="primary"
|
||||
>
|
||||
{{ item.toolType || item.retrievalMode }}
|
||||
</ElTag>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="agent-snapshot__empty">
|
||||
{{ $t('approval.snapshot.noBindings') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.agent-snapshot {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-4);
|
||||
}
|
||||
|
||||
.agent-snapshot__hero,
|
||||
.agent-snapshot__section {
|
||||
padding: var(--space-4);
|
||||
border: 1px solid hsl(var(--divider-faint) / 0.64);
|
||||
border-radius: var(--radius-panel);
|
||||
background: hsl(var(--surface-panel) / 0.88);
|
||||
}
|
||||
|
||||
.agent-snapshot__hero,
|
||||
.agent-snapshot__section-head,
|
||||
.agent-snapshot__binding-head,
|
||||
.agent-snapshot__binding-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.agent-snapshot__hero {
|
||||
gap: var(--space-3);
|
||||
}
|
||||
|
||||
.agent-snapshot__avatar {
|
||||
display: grid;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
flex: none;
|
||||
place-items: center;
|
||||
overflow: hidden;
|
||||
border-radius: var(--radius-control);
|
||||
background: hsl(var(--primary) / 0.1);
|
||||
color: hsl(var(--primary));
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.agent-snapshot__avatar img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.agent-snapshot__hero-copy,
|
||||
.agent-snapshot__section,
|
||||
.agent-snapshot__binding-group,
|
||||
.agent-snapshot__binding-list {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
flex-direction: column;
|
||||
gap: var(--space-2);
|
||||
}
|
||||
|
||||
.agent-snapshot__title,
|
||||
.agent-snapshot__section-title {
|
||||
color: hsl(var(--foreground));
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.agent-snapshot__description,
|
||||
.agent-snapshot__empty {
|
||||
color: hsl(var(--text-muted));
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.agent-snapshot__section-head,
|
||||
.agent-snapshot__binding-head,
|
||||
.agent-snapshot__binding-item {
|
||||
justify-content: space-between;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
|
||||
.agent-snapshot__info-grid,
|
||||
.agent-snapshot__binding-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||
gap: var(--space-3);
|
||||
}
|
||||
|
||||
.agent-snapshot__info-item,
|
||||
.agent-snapshot__binding-group {
|
||||
padding: var(--space-3);
|
||||
border-radius: var(--radius-control);
|
||||
background: hsl(var(--surface-contrast-soft));
|
||||
}
|
||||
|
||||
.agent-snapshot__info-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-1);
|
||||
color: hsl(var(--text-muted));
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.agent-snapshot__info-item strong {
|
||||
color: hsl(var(--foreground));
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.agent-snapshot__prompt {
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
color: hsl(var(--foreground));
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 4;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.agent-snapshot__prompt.is-expanded {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.agent-snapshot__binding-item {
|
||||
min-height: 32px;
|
||||
color: hsl(var(--foreground));
|
||||
font-size: 13px;
|
||||
}
|
||||
</style>
|
||||
@@ -17,7 +17,13 @@ defineExpose({
|
||||
openDialog,
|
||||
});
|
||||
|
||||
type ResourceType = 'BOT' | 'KNOWLEDGE' | 'PLUGIN' | 'RESOURCE' | 'WORKFLOW';
|
||||
type ResourceType =
|
||||
| 'AGENT'
|
||||
| 'BOT'
|
||||
| 'KNOWLEDGE'
|
||||
| 'PLUGIN'
|
||||
| 'RESOURCE'
|
||||
| 'WORKFLOW';
|
||||
|
||||
interface CategoryScopeItem {
|
||||
categoryIds: Array<number | string>;
|
||||
@@ -40,6 +46,7 @@ const RESOURCE_SCOPE_GROUPS: Array<{
|
||||
label: string;
|
||||
resourceType: ResourceType;
|
||||
}> = [
|
||||
{ resourceType: 'AGENT', label: $t('menus.ai.agents') },
|
||||
{ resourceType: 'BOT', label: $t('bot.chatAssistant') },
|
||||
{ resourceType: 'PLUGIN', label: $t('menus.ai.plugin') },
|
||||
{ resourceType: 'WORKFLOW', label: $t('menus.ai.workflow') },
|
||||
@@ -54,6 +61,7 @@ const entity = ref<any>(buildDefaultEntity());
|
||||
const categoryScopeLoaded = ref(false);
|
||||
const categoryScopeEditable = ref(false);
|
||||
const categoryOptions = ref<Record<ResourceType, CategoryOption[]>>({
|
||||
AGENT: [],
|
||||
BOT: [],
|
||||
KNOWLEDGE: [],
|
||||
PLUGIN: [],
|
||||
@@ -186,6 +194,9 @@ async function ensureCategoryOptions() {
|
||||
return;
|
||||
}
|
||||
const requests = [
|
||||
api.get('/api/v1/agentCategory/list', {
|
||||
params: { sortKey: 'sortNo', sortType: 'asc' },
|
||||
}),
|
||||
api.get('/api/v1/botCategory/list', {
|
||||
params: { sortKey: 'sortNo', sortType: 'asc' },
|
||||
}),
|
||||
@@ -201,9 +212,10 @@ async function ensureCategoryOptions() {
|
||||
}),
|
||||
];
|
||||
|
||||
const [botRes, pluginRes, workflowRes, knowledgeRes, resourceRes] =
|
||||
const [agentRes, botRes, pluginRes, workflowRes, knowledgeRes, resourceRes] =
|
||||
await Promise.all(requests);
|
||||
categoryOptions.value = {
|
||||
AGENT: normalizeCategoryOptions(agentRes.data, 'categoryName'),
|
||||
BOT: normalizeCategoryOptions(botRes.data, 'categoryName'),
|
||||
KNOWLEDGE: normalizeCategoryOptions(knowledgeRes.data, 'categoryName'),
|
||||
PLUGIN: normalizeCategoryOptions(pluginRes.data, 'name'),
|
||||
|
||||
Reference in New Issue
Block a user