Build lightweight AI agent admin
This commit is contained in:
@@ -0,0 +1,926 @@
|
||||
<script setup lang="ts">
|
||||
import type {
|
||||
Agent,
|
||||
AgentChatEvent,
|
||||
AgentMessage,
|
||||
ReasoningStep,
|
||||
} from '#/api/ai-platform/ai-platform';
|
||||
import type { ChatMessage } from '#/components/ChatBox/index';
|
||||
|
||||
/**
|
||||
* Agent 对话面板组件
|
||||
* 可复用于 Agent 编辑页面和 Agent 对话页面
|
||||
*/
|
||||
import { computed, nextTick, onUnmounted, ref, watch } from 'vue';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
import {
|
||||
feedbackAgentMessageApi,
|
||||
getAgentMessagesApi,
|
||||
sendAgentMessageStream,
|
||||
} from '#/api/ai-platform/ai-platform';
|
||||
import { ChatBox } from '#/components/ChatBox/index';
|
||||
import { AppDesignPanel, DesignEditorPanel } from '#/components/form-editor';
|
||||
|
||||
import AiWorkingAnimation from '../../../../components/ai-loading/AiWorkingAnimation.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
/** Agent 详情(可选,用于显示名称和欢迎配置) */
|
||||
agent?: Agent | null;
|
||||
/** Agent ID */
|
||||
agentId: string;
|
||||
/** 当前对话 ID(用于加载历史消息) */
|
||||
currentConversationId?: null | string;
|
||||
/** 是否禁用输入 */
|
||||
disabled?: boolean;
|
||||
/** 是否启用附件 */
|
||||
enableAttachment?: boolean;
|
||||
/** 是否启用图片 */
|
||||
enableImage?: boolean;
|
||||
/** 是否启用语音 */
|
||||
enableVoice?: boolean;
|
||||
/** 占位符文本 */
|
||||
placeholder?: string;
|
||||
/** 是否显示清空按钮 */
|
||||
showClearButton?: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'conversation-created', conversationId: string): void;
|
||||
}>();
|
||||
|
||||
// 设计面板显示状态
|
||||
const showDesignPanel = ref(false);
|
||||
const showAppDesignPanel = ref(false);
|
||||
|
||||
// 对话状态
|
||||
const chatMessages = ref<AgentMessage[]>([]);
|
||||
const sending = ref(false);
|
||||
const streamingContent = ref('');
|
||||
const streamingSteps = ref<ReasoningStep[]>([]);
|
||||
const currentStep = ref('');
|
||||
const conversationId = ref<null | string>(null);
|
||||
const chatBoxRef = ref<InstanceType<typeof ChatBox> | null>(null);
|
||||
|
||||
// 对话流交互状态
|
||||
const waitingForInput = ref(false);
|
||||
const waitingConfig = ref<any>(null);
|
||||
|
||||
// 设计面板状态
|
||||
const currentDesign = ref<null | {
|
||||
data: any;
|
||||
form_fields?: any[];
|
||||
nodeId: string;
|
||||
table_configs?: any[];
|
||||
title: string;
|
||||
type: string;
|
||||
}>(null);
|
||||
|
||||
// 应用设计面板状态
|
||||
const currentAppDesign = ref<null | {
|
||||
data: any;
|
||||
nodeId: string;
|
||||
title: string;
|
||||
type: string;
|
||||
}>(null);
|
||||
|
||||
// 取消流式请求
|
||||
let cancelStream: (() => void) | null = null;
|
||||
|
||||
// 生成消息 ID
|
||||
const generateId = () =>
|
||||
`msg-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
|
||||
// 转换消息格式为 ChatMessage
|
||||
const formattedMessages = computed<ChatMessage[]>(() => {
|
||||
return chatMessages.value.map((msg) => ({
|
||||
id: msg.id,
|
||||
role: msg.role as 'assistant' | 'user',
|
||||
content: msg.content,
|
||||
status: msg.status as any,
|
||||
timestamp: msg.created_at,
|
||||
elapsed_time: msg.elapsed_time,
|
||||
tokens_used: msg.total_tokens,
|
||||
error_message: msg.error_message,
|
||||
feedback: (msg.feedback as any) || null,
|
||||
reasoning_steps: msg.reasoning_steps?.map((step) => ({
|
||||
type: step.type as any,
|
||||
content: step.content || '',
|
||||
tool: step.tool,
|
||||
params: step.params,
|
||||
})),
|
||||
interaction: msg.interaction,
|
||||
voice: msg.voice as any,
|
||||
}));
|
||||
});
|
||||
|
||||
// 欢迎配置
|
||||
const welcomeConfig = computed(() => {
|
||||
if (!props.agent) return undefined;
|
||||
return {
|
||||
title: props.agent.name,
|
||||
description:
|
||||
props.agent.status === 'published'
|
||||
? props.agent.welcome_message ||
|
||||
$t('ai-platform.agent.chatPanel.sendToStart')
|
||||
: $t('ai-platform.agent.chatPanel.publishFirst'),
|
||||
suggestions:
|
||||
props.agent.status === 'published' ? props.agent.suggested_questions : [],
|
||||
};
|
||||
});
|
||||
|
||||
// 监听 agentId 变化,清空对话
|
||||
watch(
|
||||
() => props.agentId,
|
||||
() => {
|
||||
clearChat();
|
||||
},
|
||||
);
|
||||
|
||||
// 监听 currentConversationId 变化,加载历史消息
|
||||
watch(
|
||||
() => props.currentConversationId,
|
||||
async (newId) => {
|
||||
if (newId) {
|
||||
await loadMessages(newId);
|
||||
} else {
|
||||
clearChat();
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
// 加载历史消息
|
||||
async function loadMessages(convId: string) {
|
||||
// 如果正在发送消息或等待用户输入,跳过加载以避免覆盖当前状态
|
||||
if (sending.value || waitingForInput.value) {
|
||||
conversationId.value = convId;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await getAgentMessagesApi(convId, { pageSize: 100 });
|
||||
chatMessages.value = res.items;
|
||||
conversationId.value = convId;
|
||||
await nextTick();
|
||||
scrollToBottom();
|
||||
} catch (error) {
|
||||
console.error('加载消息失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 发送消息 (由 ChatBox 触发)
|
||||
async function handleSendMessage(message: string) {
|
||||
if (!message.trim() || sending.value) return;
|
||||
|
||||
// 如果正在等待用户输入(对话流模式),处理恢复逻辑
|
||||
if (waitingForInput.value) {
|
||||
// 移除最后一条助手消息的交互配置
|
||||
const lastAssistantMsg = chatMessages.value.findLast(
|
||||
(m) => m.role === 'assistant',
|
||||
);
|
||||
if (lastAssistantMsg && lastAssistantMsg.interaction) {
|
||||
const msgIndex = chatMessages.value.findIndex(
|
||||
(m) => m.id === lastAssistantMsg.id,
|
||||
);
|
||||
if (msgIndex !== -1) {
|
||||
chatMessages.value[msgIndex] = {
|
||||
...lastAssistantMsg,
|
||||
interaction: undefined,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 添加用户消息
|
||||
const userMsg: AgentMessage = {
|
||||
id: generateId(),
|
||||
role: 'user',
|
||||
content: message,
|
||||
status: 'completed',
|
||||
reasoning_steps: [],
|
||||
tool_calls: [],
|
||||
prompt_tokens: 0,
|
||||
completion_tokens: 0,
|
||||
total_tokens: 0,
|
||||
elapsed_time: 0,
|
||||
error_message: '',
|
||||
feedback: '',
|
||||
created_at: new Date().toISOString(),
|
||||
};
|
||||
chatMessages.value.push(userMsg);
|
||||
|
||||
// 重置等待状态并继续工作流
|
||||
waitingForInput.value = false;
|
||||
waitingConfig.value = null;
|
||||
doSendMessage(message, false);
|
||||
return;
|
||||
}
|
||||
|
||||
doSendMessage(message, true);
|
||||
}
|
||||
|
||||
// 实际发送消息的函数
|
||||
async function doSendMessage(message: string, addUserMessage: boolean = true) {
|
||||
sending.value = true;
|
||||
streamingContent.value = '';
|
||||
streamingSteps.value = [];
|
||||
currentStep.value = '';
|
||||
|
||||
// 如果工作流类型是 "application",立即显示右侧面板(加载状态)
|
||||
if (props.agent?.workflow_type === 'application') {
|
||||
showDesignPanel.value = true;
|
||||
currentDesign.value = {
|
||||
type: 'loading',
|
||||
title: $t('ai-platform.agent.chatPanel.aiDeveloping'),
|
||||
data: null,
|
||||
nodeId: '',
|
||||
};
|
||||
}
|
||||
|
||||
// 添加用户消息
|
||||
if (addUserMessage) {
|
||||
const userMessage: AgentMessage = {
|
||||
id: generateId(),
|
||||
role: 'user',
|
||||
content: message,
|
||||
status: 'completed',
|
||||
reasoning_steps: [],
|
||||
tool_calls: [],
|
||||
prompt_tokens: 0,
|
||||
completion_tokens: 0,
|
||||
total_tokens: 0,
|
||||
elapsed_time: 0,
|
||||
error_message: '',
|
||||
feedback: '',
|
||||
created_at: new Date().toISOString(),
|
||||
};
|
||||
chatMessages.value.push(userMessage);
|
||||
}
|
||||
|
||||
// 添加助手消息占位
|
||||
const assistantMessage: AgentMessage = {
|
||||
id: generateId(),
|
||||
role: 'assistant',
|
||||
content: '',
|
||||
status: 'pending',
|
||||
reasoning_steps: [],
|
||||
tool_calls: [],
|
||||
prompt_tokens: 0,
|
||||
completion_tokens: 0,
|
||||
total_tokens: 0,
|
||||
elapsed_time: 0,
|
||||
error_message: '',
|
||||
feedback: '',
|
||||
created_at: new Date().toISOString(),
|
||||
};
|
||||
chatMessages.value.push(assistantMessage);
|
||||
|
||||
await nextTick();
|
||||
scrollToBottom();
|
||||
|
||||
// 发送流式请求
|
||||
// 系统变量(application_id 等)由 API 层自动注入
|
||||
cancelStream = sendAgentMessageStream(
|
||||
props.agentId,
|
||||
{
|
||||
message,
|
||||
conversation_id: conversationId.value || undefined,
|
||||
},
|
||||
(event: AgentChatEvent) => handleStreamEvent(event, assistantMessage),
|
||||
(error) => {
|
||||
console.error('Stream error:', error);
|
||||
assistantMessage.status = 'failed';
|
||||
assistantMessage.error_message = error.message;
|
||||
sending.value = false;
|
||||
},
|
||||
() => {
|
||||
sending.value = false;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// 处理流式事件
|
||||
function handleStreamEvent(
|
||||
event: AgentChatEvent,
|
||||
assistantMessage: AgentMessage,
|
||||
) {
|
||||
const msgIndex = chatMessages.value.findIndex(
|
||||
(m) => m.id === assistantMessage.id,
|
||||
);
|
||||
|
||||
switch (event.type) {
|
||||
case 'action': {
|
||||
currentStep.value = 'action';
|
||||
streamingSteps.value.push({
|
||||
type: 'action',
|
||||
content: '',
|
||||
tool: event.tool,
|
||||
params: event.params,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
assistantMessage.reasoning_steps = [...streamingSteps.value];
|
||||
break;
|
||||
}
|
||||
|
||||
case 'annotation_reply':
|
||||
|
||||
case 'knowledge_retrieval': {
|
||||
currentStep.value = event.type;
|
||||
streamingSteps.value.push({
|
||||
type: event.type as 'annotation_reply' | 'knowledge_retrieval',
|
||||
content: event.content || '',
|
||||
status: 'completed',
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
assistantMessage.reasoning_steps = [...streamingSteps.value];
|
||||
break;
|
||||
}
|
||||
|
||||
case 'answer': {
|
||||
currentStep.value = 'answering';
|
||||
streamingContent.value = event.content || '';
|
||||
assistantMessage.content = streamingContent.value;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'complete': {
|
||||
assistantMessage.status = 'completed';
|
||||
assistantMessage.total_tokens = event.tokens_used || 0;
|
||||
assistantMessage.elapsed_time = event.elapsed_time || 0;
|
||||
currentStep.value = '';
|
||||
break;
|
||||
}
|
||||
|
||||
case 'error': {
|
||||
assistantMessage.status = 'failed';
|
||||
assistantMessage.error_message =
|
||||
event.content || $t('ai-platform.agent.chatPanel.executionFailed');
|
||||
currentStep.value = '';
|
||||
break;
|
||||
}
|
||||
|
||||
case 'node_complete': {
|
||||
// 查找已存在的 node_start 步骤并更新为 node_complete
|
||||
const existingIndex = streamingSteps.value.findIndex(
|
||||
(s) => s.node_id === event.node_id && s.type === 'node_start',
|
||||
);
|
||||
if (existingIndex === -1) {
|
||||
// 如果没有找到对应的 start,添加新的 complete 步骤
|
||||
streamingSteps.value.push({
|
||||
type: 'node_complete',
|
||||
content: event.node_label || event.node_type || '',
|
||||
node_id: event.node_id,
|
||||
node_type: event.node_type,
|
||||
output: event.outputs,
|
||||
status: 'completed',
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
} else {
|
||||
const existingStep = streamingSteps.value[existingIndex]!;
|
||||
// 更新已存在的步骤
|
||||
streamingSteps.value[existingIndex] = {
|
||||
...existingStep,
|
||||
type: 'node_complete',
|
||||
content: event.node_label || event.node_type || '',
|
||||
output: event.outputs,
|
||||
status: 'completed',
|
||||
timestamp: existingStep.timestamp || new Date().toISOString(),
|
||||
};
|
||||
}
|
||||
assistantMessage.reasoning_steps = [...streamingSteps.value];
|
||||
break;
|
||||
}
|
||||
case 'node_event': {
|
||||
// 节点事件(如消息节点发送消息)
|
||||
const nodeEvent = event.event;
|
||||
if (nodeEvent?.type === 'message') {
|
||||
// 从消息列表中查找初始消息,检查其内容是否为空
|
||||
const initialMsg = chatMessages.value.find(
|
||||
(m) => m.id === assistantMessage.id,
|
||||
);
|
||||
if (initialMsg && !initialMsg.content?.trim()) {
|
||||
// 更新初始消息(typing indicator -> 实际内容)
|
||||
initialMsg.content = nodeEvent.content || '';
|
||||
initialMsg.status = 'completed';
|
||||
} else {
|
||||
// 已有内容,创建新消息
|
||||
const newMsg: AgentMessage = {
|
||||
id: generateId(),
|
||||
role: 'assistant',
|
||||
content: nodeEvent.content || '',
|
||||
status: 'completed',
|
||||
reasoning_steps: [],
|
||||
tool_calls: [],
|
||||
prompt_tokens: 0,
|
||||
completion_tokens: 0,
|
||||
total_tokens: 0,
|
||||
elapsed_time: 0,
|
||||
error_message: '',
|
||||
feedback: '',
|
||||
created_at: new Date().toISOString(),
|
||||
};
|
||||
chatMessages.value.push(newMsg);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'node_start': {
|
||||
// 检查是否已存在相同 node_id 的步骤(避免重复)
|
||||
const existingIndex = streamingSteps.value.findIndex(
|
||||
(s) => s.node_id === event.node_id,
|
||||
);
|
||||
if (existingIndex === -1) {
|
||||
// 添加新的执行步骤
|
||||
streamingSteps.value.push({
|
||||
type: 'node_start',
|
||||
content: `${event.node_label || event.node_type}`,
|
||||
node_id: event.node_id,
|
||||
node_type: event.node_type,
|
||||
status: 'running',
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
assistantMessage.reasoning_steps = [...streamingSteps.value];
|
||||
break;
|
||||
}
|
||||
|
||||
case 'observation': {
|
||||
currentStep.value = 'observation';
|
||||
streamingSteps.value.push({
|
||||
type: 'observation',
|
||||
content: event.content || '',
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
assistantMessage.reasoning_steps = [...streamingSteps.value];
|
||||
break;
|
||||
}
|
||||
|
||||
case 'start': {
|
||||
if (event.conversation_id && !conversationId.value) {
|
||||
conversationId.value = event.conversation_id;
|
||||
emit('conversation-created', event.conversation_id);
|
||||
}
|
||||
if (event.message_id) {
|
||||
assistantMessage.id = event.message_id;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'thought': {
|
||||
currentStep.value = 'thinking';
|
||||
streamingSteps.value.push({
|
||||
type: 'thought',
|
||||
content: event.content || '',
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
assistantMessage.reasoning_steps = [...streamingSteps.value];
|
||||
break;
|
||||
}
|
||||
|
||||
case 'waiting_input': {
|
||||
assistantMessage.status = 'completed';
|
||||
currentStep.value = '';
|
||||
waitingForInput.value = true;
|
||||
waitingConfig.value = event.config;
|
||||
|
||||
const configData = event.config as any;
|
||||
|
||||
// 检查是否是设计预览类型
|
||||
if (configData?.type === 'design_preview') {
|
||||
const previewType = configData.preview_type;
|
||||
|
||||
// 应用设计方案使用 AppDesignPanel(富文本编辑器)
|
||||
if (previewType === 'app_design') {
|
||||
currentAppDesign.value = {
|
||||
type: previewType,
|
||||
title:
|
||||
configData.title ||
|
||||
$t('ai-platform.agent.chatPanel.appDesignTitle'),
|
||||
data: configData.data || {},
|
||||
nodeId: event.node_id || '',
|
||||
};
|
||||
showAppDesignPanel.value = true;
|
||||
} else {
|
||||
// 其他设计类型使用 DesignEditorPanel(表单编辑器)
|
||||
currentDesign.value = {
|
||||
type: previewType,
|
||||
title:
|
||||
configData.title ||
|
||||
$t('ai-platform.agent.chatPanel.designPreview'),
|
||||
data: configData.data || {},
|
||||
nodeId: event.node_id || '',
|
||||
form_fields: configData.form_fields || [],
|
||||
table_configs: configData.table_configs || [],
|
||||
};
|
||||
showDesignPanel.value = true;
|
||||
}
|
||||
|
||||
// 显示提示消息
|
||||
const waitingContent = `**${configData.title}**\n\n${configData.message || $t('ai-platform.agent.chatPanel.designPanelHint')}`;
|
||||
const newMsg: AgentMessage = {
|
||||
id: generateId(),
|
||||
role: 'assistant',
|
||||
content: waitingContent,
|
||||
status: 'completed',
|
||||
reasoning_steps: [],
|
||||
tool_calls: [],
|
||||
prompt_tokens: 0,
|
||||
completion_tokens: 0,
|
||||
total_tokens: 0,
|
||||
elapsed_time: 0,
|
||||
error_message: '',
|
||||
feedback: '',
|
||||
created_at: new Date().toISOString(),
|
||||
};
|
||||
chatMessages.value.push(newMsg);
|
||||
} else {
|
||||
// 普通对话流交互
|
||||
const newMsg: AgentMessage = {
|
||||
id: generateId(),
|
||||
role: 'assistant',
|
||||
content: getWaitingPrompt(event.config),
|
||||
status: 'completed',
|
||||
reasoning_steps: [],
|
||||
tool_calls: [],
|
||||
prompt_tokens: 0,
|
||||
completion_tokens: 0,
|
||||
total_tokens: 0,
|
||||
elapsed_time: 0,
|
||||
error_message: '',
|
||||
feedback: '',
|
||||
created_at: new Date().toISOString(),
|
||||
interaction: event.config,
|
||||
};
|
||||
chatMessages.value.push(newMsg);
|
||||
}
|
||||
sending.value = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (msgIndex !== -1) {
|
||||
chatMessages.value[msgIndex] = { ...assistantMessage };
|
||||
}
|
||||
|
||||
scrollToBottom();
|
||||
}
|
||||
|
||||
// 获取等待提示文本
|
||||
function getWaitingPrompt(config: any) {
|
||||
if (!config) return $t('ai-platform.agent.chatPanel.waitingInput');
|
||||
switch (config.type) {
|
||||
case 'choice': {
|
||||
return config.question || $t('ai-platform.agent.chatPanel.selectPrompt');
|
||||
}
|
||||
case 'confirm': {
|
||||
return `**${config.title || $t('ai-platform.agent.chatPanel.confirm')}**\n\n${config.content || $t('ai-platform.agent.chatPanel.confirmContinue')}`;
|
||||
}
|
||||
case 'question': {
|
||||
return config.question || $t('ai-platform.agent.chatPanel.inputPrompt');
|
||||
}
|
||||
default: {
|
||||
return $t('ai-platform.agent.chatPanel.waitingInput');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理交互提交
|
||||
function handleInteractionSubmit(messageId: string, value: any) {
|
||||
const msgIndex = chatMessages.value.findIndex((m) => m.id === messageId);
|
||||
if (msgIndex !== -1) {
|
||||
const msg = chatMessages.value[msgIndex];
|
||||
if (msg) {
|
||||
chatMessages.value[msgIndex] = { ...msg, interaction: undefined };
|
||||
}
|
||||
}
|
||||
|
||||
const userMsg: AgentMessage = {
|
||||
id: generateId(),
|
||||
role: 'user',
|
||||
content: formatInteractionValue(value),
|
||||
status: 'completed',
|
||||
reasoning_steps: [],
|
||||
tool_calls: [],
|
||||
prompt_tokens: 0,
|
||||
completion_tokens: 0,
|
||||
total_tokens: 0,
|
||||
elapsed_time: 0,
|
||||
error_message: '',
|
||||
feedback: '',
|
||||
created_at: new Date().toISOString(),
|
||||
};
|
||||
chatMessages.value.push(userMsg);
|
||||
|
||||
waitingForInput.value = false;
|
||||
waitingConfig.value = null;
|
||||
|
||||
doSendMessage(String(value), false);
|
||||
}
|
||||
|
||||
// 处理交互取消
|
||||
function handleInteractionCancel(messageId: string) {
|
||||
const msgIndex = chatMessages.value.findIndex((m) => m.id === messageId);
|
||||
if (msgIndex !== -1) {
|
||||
const msg = chatMessages.value[msgIndex];
|
||||
if (msg) {
|
||||
chatMessages.value[msgIndex] = { ...msg, interaction: undefined };
|
||||
}
|
||||
}
|
||||
|
||||
const userMsg: AgentMessage = {
|
||||
id: generateId(),
|
||||
role: 'user',
|
||||
content:
|
||||
waitingConfig.value?.cancel_text ||
|
||||
$t('ai-platform.agent.chatPanel.cancelText'),
|
||||
status: 'completed',
|
||||
reasoning_steps: [],
|
||||
tool_calls: [],
|
||||
prompt_tokens: 0,
|
||||
completion_tokens: 0,
|
||||
total_tokens: 0,
|
||||
elapsed_time: 0,
|
||||
error_message: '',
|
||||
feedback: '',
|
||||
created_at: new Date().toISOString(),
|
||||
};
|
||||
chatMessages.value.push(userMsg);
|
||||
|
||||
waitingForInput.value = false;
|
||||
waitingConfig.value = null;
|
||||
|
||||
doSendMessage('false', false);
|
||||
}
|
||||
|
||||
// 格式化交互值显示
|
||||
function formatInteractionValue(value: any) {
|
||||
if (value === true) {
|
||||
return (
|
||||
waitingConfig.value?.confirm_text ||
|
||||
$t('ai-platform.agent.chatPanel.confirm')
|
||||
);
|
||||
}
|
||||
if (value === false) {
|
||||
return (
|
||||
waitingConfig.value?.cancel_text ||
|
||||
$t('ai-platform.agent.chatPanel.cancelText')
|
||||
);
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
const options = waitingConfig.value?.options || [];
|
||||
const labels = value.map((v: string) => {
|
||||
const opt = options.find((o: any) => o.value === v);
|
||||
return opt?.label || v;
|
||||
});
|
||||
return labels.join(', ');
|
||||
}
|
||||
if (waitingConfig.value?.type === 'choice') {
|
||||
const options = waitingConfig.value?.options || [];
|
||||
const opt = options.find((o: any) => o.value === value);
|
||||
return opt?.label || value;
|
||||
}
|
||||
return String(value);
|
||||
}
|
||||
|
||||
// 滚动到底部
|
||||
function scrollToBottom() {
|
||||
chatBoxRef.value?.scrollToBottom();
|
||||
}
|
||||
|
||||
// 清空对话
|
||||
function clearChat() {
|
||||
chatMessages.value = [];
|
||||
conversationId.value = null;
|
||||
streamingContent.value = '';
|
||||
streamingSteps.value = [];
|
||||
waitingForInput.value = false;
|
||||
waitingConfig.value = null;
|
||||
}
|
||||
|
||||
// 添加消息(语音消息用)
|
||||
function handleAddMessage(message: any) {
|
||||
const agentMsg: AgentMessage = {
|
||||
id: message.id || generateId(),
|
||||
role: message.role || 'user',
|
||||
content: message.content || '',
|
||||
status: 'completed',
|
||||
reasoning_steps: [],
|
||||
tool_calls: [],
|
||||
prompt_tokens: 0,
|
||||
completion_tokens: 0,
|
||||
total_tokens: 0,
|
||||
elapsed_time: 0,
|
||||
error_message: '',
|
||||
feedback: '',
|
||||
created_at: new Date().toISOString(),
|
||||
voice: message.voice,
|
||||
};
|
||||
chatMessages.value.push(agentMsg);
|
||||
scrollToBottom();
|
||||
}
|
||||
|
||||
// 更新消息(语音识别完成后用)
|
||||
function handleUpdateMessage(messageId: string, updates: any) {
|
||||
const msgIndex = chatMessages.value.findIndex((m) => m.id === messageId);
|
||||
if (msgIndex !== -1) {
|
||||
const msg = chatMessages.value[msgIndex]!;
|
||||
chatMessages.value[msgIndex] = { ...msg, ...updates };
|
||||
|
||||
// 如果语音识别成功,触发发送
|
||||
if (updates.content && !updates.content.startsWith('[')) {
|
||||
doSendMessage(updates.content, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 消息反馈
|
||||
async function handleChatFeedback(
|
||||
messageId: string,
|
||||
feedback: 'dislike' | 'like',
|
||||
) {
|
||||
if (messageId.startsWith('temp-') || messageId.startsWith('msg-')) return;
|
||||
|
||||
try {
|
||||
await feedbackAgentMessageApi(messageId, { feedback });
|
||||
const msg = chatMessages.value.find((m) => m.id === messageId);
|
||||
if (msg) {
|
||||
msg.feedback = feedback;
|
||||
}
|
||||
ElMessage.success($t('ai-platform.agent.chatPanel.feedbackThanks'));
|
||||
} catch (error) {
|
||||
console.error('反馈失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 设计面板确认
|
||||
function handleDesignConfirm(data: Record<string, any>) {
|
||||
showDesignPanel.value = false;
|
||||
|
||||
// 添加用户确认消息
|
||||
const userMsg: AgentMessage = {
|
||||
id: generateId(),
|
||||
role: 'user',
|
||||
content: $t('ai-platform.agent.chatPanel.confirmDesign'),
|
||||
status: 'completed',
|
||||
reasoning_steps: [],
|
||||
tool_calls: [],
|
||||
prompt_tokens: 0,
|
||||
completion_tokens: 0,
|
||||
total_tokens: 0,
|
||||
elapsed_time: 0,
|
||||
error_message: '',
|
||||
feedback: '',
|
||||
created_at: new Date().toISOString(),
|
||||
};
|
||||
chatMessages.value.push(userMsg);
|
||||
|
||||
// 重置等待状态
|
||||
waitingForInput.value = false;
|
||||
waitingConfig.value = null;
|
||||
currentDesign.value = null;
|
||||
|
||||
// 继续工作流,传递编辑后的数据
|
||||
doSendMessage(JSON.stringify(data), false);
|
||||
}
|
||||
|
||||
// 设计面板取消
|
||||
function handleDesignCancel() {
|
||||
showDesignPanel.value = false;
|
||||
waitingForInput.value = false;
|
||||
waitingConfig.value = null;
|
||||
currentDesign.value = null;
|
||||
}
|
||||
|
||||
// 应用设计面板确认
|
||||
function handleAppDesignConfirm(data: Record<string, any>) {
|
||||
showAppDesignPanel.value = false;
|
||||
|
||||
// 添加用户确认消息
|
||||
const userMsg: AgentMessage = {
|
||||
id: generateId(),
|
||||
role: 'user',
|
||||
content: $t('ai-platform.agent.chatPanel.confirmAppDesign'),
|
||||
status: 'completed',
|
||||
reasoning_steps: [],
|
||||
tool_calls: [],
|
||||
prompt_tokens: 0,
|
||||
completion_tokens: 0,
|
||||
total_tokens: 0,
|
||||
elapsed_time: 0,
|
||||
error_message: '',
|
||||
feedback: '',
|
||||
created_at: new Date().toISOString(),
|
||||
};
|
||||
chatMessages.value.push(userMsg);
|
||||
|
||||
// 重置等待状态
|
||||
waitingForInput.value = false;
|
||||
waitingConfig.value = null;
|
||||
currentAppDesign.value = null;
|
||||
|
||||
// 继续工作流,传递编辑后的数据
|
||||
doSendMessage(JSON.stringify(data), false);
|
||||
}
|
||||
|
||||
// 应用设计面板取消
|
||||
function handleAppDesignCancel() {
|
||||
showAppDesignPanel.value = false;
|
||||
waitingForInput.value = false;
|
||||
waitingConfig.value = null;
|
||||
currentAppDesign.value = null;
|
||||
}
|
||||
|
||||
// 暴露方法给父组件
|
||||
defineExpose({
|
||||
clearChat,
|
||||
scrollToBottom,
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
cancelStream?.();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex h-full overflow-hidden">
|
||||
<!-- 聊天区域 -->
|
||||
<div
|
||||
class="h-full overflow-hidden"
|
||||
:class="showDesignPanel ? 'w-[400px] flex-shrink-0' : 'flex-1'"
|
||||
>
|
||||
<ChatBox
|
||||
ref="chatBoxRef"
|
||||
:messages="formattedMessages"
|
||||
:loading="sending"
|
||||
:streaming-content="streamingContent"
|
||||
:assistant-name="agent?.name"
|
||||
:welcome-config="welcomeConfig"
|
||||
:disabled="disabled"
|
||||
:placeholder="
|
||||
placeholder || $t('ai-platform.agent.chatPanel.inputPlaceholder')
|
||||
"
|
||||
:enable-feedback="true"
|
||||
:enable-regenerate="false"
|
||||
:enable-attachment="enableAttachment ?? false"
|
||||
:enable-image="enableImage ?? false"
|
||||
:enable-voice="enableVoice ?? true"
|
||||
:show-clear-button="showClearButton ?? true"
|
||||
class="h-full"
|
||||
@send="handleSendMessage"
|
||||
@feedback="handleChatFeedback"
|
||||
@clear="clearChat"
|
||||
@interaction-submit="handleInteractionSubmit"
|
||||
@interaction-cancel="handleInteractionCancel"
|
||||
@add-message="handleAddMessage"
|
||||
@update-message="handleUpdateMessage"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 设计面板 -->
|
||||
<div
|
||||
v-if="showDesignPanel && currentDesign"
|
||||
class="flex flex-1 flex-col overflow-hidden"
|
||||
>
|
||||
<!-- 加载状态 - AI 工作中动画 -->
|
||||
<AiWorkingAnimation
|
||||
v-if="currentDesign.type === 'loading'"
|
||||
:title="currentDesign.title"
|
||||
:subtitle="$t('ai-platform.agent.chatPanel.analyzingAndGenerating')"
|
||||
class="ml-3 h-full min-h-[400px] flex-1 rounded-lg"
|
||||
/>
|
||||
<!-- 设计编辑器 -->
|
||||
<DesignEditorPanel
|
||||
v-else
|
||||
v-model:visible="showDesignPanel"
|
||||
:design="{
|
||||
type: currentDesign.type as any,
|
||||
title: currentDesign.title,
|
||||
data: currentDesign.data,
|
||||
nodeId: currentDesign.nodeId,
|
||||
form_fields: currentDesign.form_fields,
|
||||
table_configs: currentDesign.table_configs,
|
||||
}"
|
||||
@confirm="handleDesignConfirm"
|
||||
@close="handleDesignCancel"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 应用设计面板 -->
|
||||
<div
|
||||
v-if="showAppDesignPanel && currentAppDesign"
|
||||
class="flex flex-1 flex-col overflow-hidden"
|
||||
>
|
||||
<AppDesignPanel
|
||||
:visible="showAppDesignPanel"
|
||||
:design="currentAppDesign as any"
|
||||
@update:visible="showAppDesignPanel = $event"
|
||||
@confirm="handleAppDesignConfirm"
|
||||
@close="handleAppDesignCancel"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,193 @@
|
||||
<script lang="ts" setup>
|
||||
import type { MenuItem } from '#/components/zq-form/zq-menu-selector/types';
|
||||
|
||||
import { computed, ref, watch } from 'vue';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import {
|
||||
ElButton,
|
||||
ElDialog,
|
||||
ElForm,
|
||||
ElFormItem,
|
||||
ElInput,
|
||||
ElInputNumber,
|
||||
ElMessage,
|
||||
ElScrollbar,
|
||||
} from 'element-plus';
|
||||
|
||||
import { publishAgentToMenuApi } from '#/api/ai-platform/ai-platform';
|
||||
import { ZqIconPicker } from '#/components/zq-form/zq-icon-picker';
|
||||
import { ZqMenuSelector } from '#/components/zq-form/zq-menu-selector';
|
||||
import { useAppContextStore } from '#/store/app-context';
|
||||
|
||||
interface Props {
|
||||
modelValue: boolean;
|
||||
agentId: string;
|
||||
agentName: string;
|
||||
agentCode?: string;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
published: [];
|
||||
'update:modelValue': [value: boolean];
|
||||
}>();
|
||||
|
||||
const appContextStore = useAppContextStore();
|
||||
|
||||
const dialogVisible = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (val) => emit('update:modelValue', val),
|
||||
});
|
||||
|
||||
const loading = ref(false);
|
||||
|
||||
// 发布表单
|
||||
const publishForm = ref({
|
||||
menu_name: '',
|
||||
menu_parent_id: undefined as string | undefined,
|
||||
menu_icon: 'lucide:bot',
|
||||
menu_order: 0,
|
||||
});
|
||||
|
||||
// 监听弹窗打开
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
// 重置表单,菜单名称默认为智能体名称
|
||||
publishForm.value = {
|
||||
menu_name: props.agentName,
|
||||
menu_parent_id: undefined,
|
||||
menu_icon: 'lucide:bot',
|
||||
menu_order: 0,
|
||||
};
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// 菜单选择回调
|
||||
function handleMenuChange(menu: MenuItem | MenuItem[] | null) {
|
||||
publishForm.value.menu_parent_id =
|
||||
menu && !Array.isArray(menu) ? menu.id : undefined;
|
||||
}
|
||||
|
||||
async function handlePublish() {
|
||||
if (!publishForm.value.menu_name) {
|
||||
ElMessage.warning($t('ui.placeholder.input'));
|
||||
return;
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
try {
|
||||
await publishAgentToMenuApi(props.agentId, {
|
||||
menu_name: publishForm.value.menu_name,
|
||||
menu_parent_id: publishForm.value.menu_parent_id,
|
||||
menu_icon: publishForm.value.menu_icon,
|
||||
menu_order: publishForm.value.menu_order,
|
||||
});
|
||||
ElMessage.success($t('common.operationSuccess'));
|
||||
emit('published');
|
||||
dialogVisible.value = false;
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error?.message || $t('common.operationFailed'));
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElDialog
|
||||
v-model="dialogVisible"
|
||||
:title="$t('ai-platform.agent.publishToMenu')"
|
||||
width="500px"
|
||||
destroy-on-close
|
||||
align-center
|
||||
append-to-body
|
||||
>
|
||||
<ElScrollbar max-height="60vh">
|
||||
<div class="pr-4">
|
||||
<ElForm :model="publishForm" label-width="100px">
|
||||
<!-- 菜单配置 -->
|
||||
<div class="mb-4 font-medium">
|
||||
{{ $t('form-manager.publishDialog.menuConfig') }}
|
||||
</div>
|
||||
|
||||
<ElFormItem
|
||||
:label="$t('form-manager.publishDialog.menuName')"
|
||||
required
|
||||
>
|
||||
<ElInput
|
||||
v-model="publishForm.menu_name"
|
||||
:placeholder="$t('form-manager.placeholder.name')"
|
||||
/>
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem :label="$t('form-manager.publishDialog.parentMenu')">
|
||||
<ZqMenuSelector
|
||||
:model-value="publishForm.menu_parent_id || null"
|
||||
mode="dialog"
|
||||
:placeholder="
|
||||
$t('form-manager.publishDialog.parentMenuPlaceholder')
|
||||
"
|
||||
:application-id="appContextStore.currentApp?.id"
|
||||
@change="handleMenuChange"
|
||||
/>
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem :label="$t('form-manager.publishDialog.menuIcon')">
|
||||
<ZqIconPicker
|
||||
v-model="publishForm.menu_icon"
|
||||
prefix="lucide"
|
||||
:auto-fetch-api="false"
|
||||
class="w-full"
|
||||
/>
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem :label="$t('common.sort')">
|
||||
<ElInputNumber
|
||||
v-model="publishForm.menu_order"
|
||||
:min="0"
|
||||
:max="999"
|
||||
class="w-full"
|
||||
/>
|
||||
</ElFormItem>
|
||||
|
||||
<!-- 路由信息 -->
|
||||
<template v-if="agentCode">
|
||||
<div class="mb-4 mt-6 font-medium">
|
||||
{{ $t('form-manager.publishDialog.routeInfo') }}
|
||||
</div>
|
||||
|
||||
<ElFormItem :label="$t('form-manager.publishDialog.accessPath')">
|
||||
<ElInput
|
||||
:model-value="`/agent-chat/${agentCode}`"
|
||||
disabled
|
||||
class="text-muted-foreground"
|
||||
/>
|
||||
</ElFormItem>
|
||||
</template>
|
||||
</ElForm>
|
||||
</div>
|
||||
</ElScrollbar>
|
||||
|
||||
<template #footer>
|
||||
<ElButton @click="dialogVisible = false">
|
||||
{{ $t('common.cancel') }}
|
||||
</ElButton>
|
||||
<ElButton type="primary" :loading="loading" @click="handlePublish">
|
||||
{{ $t('common.confirm') }}
|
||||
</ElButton>
|
||||
</template>
|
||||
</ElDialog>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
/* 确保 IconPicker 的弹出层在 Dialog 之上 */
|
||||
.z-popup {
|
||||
z-index: 2100 !important;
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,642 @@
|
||||
<script setup lang="ts">
|
||||
import type {
|
||||
AgentCreateInput,
|
||||
AgentListItem,
|
||||
} from '#/api/ai-platform/ai-platform';
|
||||
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { Page } from '@vben/common-ui';
|
||||
import {
|
||||
Bot,
|
||||
CirclePlus,
|
||||
Download,
|
||||
Edit,
|
||||
Menu,
|
||||
MessageSquare,
|
||||
MoreVertical,
|
||||
Play,
|
||||
Square,
|
||||
Trash2,
|
||||
Upload,
|
||||
} from '@vben/icons';
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import {
|
||||
ElButton,
|
||||
ElCard,
|
||||
ElDialog,
|
||||
ElDropdown,
|
||||
ElDropdownItem,
|
||||
ElDropdownMenu,
|
||||
ElEmpty,
|
||||
ElForm,
|
||||
ElFormItem,
|
||||
ElInput,
|
||||
ElMessage,
|
||||
ElMessageBox,
|
||||
ElOption,
|
||||
ElPagination,
|
||||
ElSelect,
|
||||
ElSwitch,
|
||||
ElTag,
|
||||
ElTooltip,
|
||||
} from 'element-plus';
|
||||
|
||||
import {
|
||||
createAgentApi,
|
||||
deleteAgentApi,
|
||||
disableAgentApi,
|
||||
exportAgentConfigApi,
|
||||
getAgentListApi,
|
||||
publishAgentApi,
|
||||
unpublishAgentMenuApi,
|
||||
} from '#/api/ai-platform/ai-platform';
|
||||
import { useAppContextStore } from '#/store/app-context';
|
||||
|
||||
import PublishToMenuDialog from './components/PublishToMenuDialog.vue';
|
||||
import ImportDialog from './modules/import-dialog.vue';
|
||||
|
||||
defineOptions({ name: 'AgentList' });
|
||||
|
||||
const router = useRouter();
|
||||
const appContextStore = useAppContextStore();
|
||||
|
||||
// 是否在主应用下(只有主应用才显示“子应用可见”开关)
|
||||
const isMainApp = !appContextStore.currentApp?.id;
|
||||
|
||||
// 搜索关键词
|
||||
const searchKeyword = ref('');
|
||||
|
||||
// 列表数据
|
||||
const loading = ref(false);
|
||||
const agentList = ref<AgentListItem[]>([]);
|
||||
|
||||
// 分页
|
||||
const pagination = reactive({
|
||||
current: 1,
|
||||
pageSize: 12,
|
||||
total: 0,
|
||||
});
|
||||
|
||||
// 发布到菜单弹窗
|
||||
const showPublishToMenuDialog = ref(false);
|
||||
const publishingAgent = ref<AgentListItem | null>(null);
|
||||
|
||||
// 导入弹窗
|
||||
const showImportDialog = ref(false);
|
||||
|
||||
// 创建对话框
|
||||
const createDialogVisible = ref(false);
|
||||
const createForm = reactive<AgentCreateInput>({
|
||||
name: '',
|
||||
code: '',
|
||||
description: '',
|
||||
mode: 'autonomous',
|
||||
is_global: false,
|
||||
});
|
||||
const createLoading = ref(false);
|
||||
|
||||
// 获取列表
|
||||
async function fetchList() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await getAgentListApi({
|
||||
page: pagination.current,
|
||||
pageSize: pagination.pageSize,
|
||||
name: searchKeyword.value || undefined,
|
||||
applicationId: appContextStore.currentApp?.id,
|
||||
});
|
||||
agentList.value = res.items;
|
||||
pagination.total = res.total;
|
||||
} catch (error) {
|
||||
console.error('获取智能体列表失败:', error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索
|
||||
function handleSearch() {
|
||||
pagination.current = 1;
|
||||
fetchList();
|
||||
}
|
||||
|
||||
// 分页变化
|
||||
function handlePageChange(page: number) {
|
||||
pagination.current = page;
|
||||
fetchList();
|
||||
}
|
||||
|
||||
// 每页条数变化
|
||||
function handleSizeChange(size: number) {
|
||||
pagination.pageSize = size;
|
||||
pagination.current = 1;
|
||||
fetchList();
|
||||
}
|
||||
|
||||
// 打开创建对话框
|
||||
function openCreateDialog() {
|
||||
createForm.name = '';
|
||||
createForm.code = '';
|
||||
createForm.description = '';
|
||||
createForm.mode = 'autonomous';
|
||||
createForm.is_global = false;
|
||||
createDialogVisible.value = true;
|
||||
}
|
||||
|
||||
// 创建智能体
|
||||
async function handleCreate() {
|
||||
if (!createForm.name || !createForm.code) {
|
||||
ElMessage.warning($t('ai-platform.agent.fillNameAndCode'));
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证 code 格式
|
||||
if (!/^[a-z]\w*$/i.test(createForm.code)) {
|
||||
ElMessage.warning($t('ai-platform.agent.codeFormatError'));
|
||||
return;
|
||||
}
|
||||
|
||||
createLoading.value = true;
|
||||
try {
|
||||
const agent = await createAgentApi({
|
||||
...createForm,
|
||||
application_id: appContextStore.currentApp?.id,
|
||||
});
|
||||
ElMessage.success($t('ai-platform.agent.createSuccess'));
|
||||
createDialogVisible.value = false;
|
||||
// 跳转到编辑页(支持子应用模式)
|
||||
router.push(
|
||||
appContextStore.getContextPath(`/ai-platform/agent/editor/${agent.id}`),
|
||||
);
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message || $t('ai-platform.agent.createFailed'));
|
||||
} finally {
|
||||
createLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 编辑智能体
|
||||
function handleEdit(agent: AgentListItem) {
|
||||
router.push(
|
||||
appContextStore.getContextPath(`/ai-platform/agent/editor/${agent.id}`),
|
||||
);
|
||||
}
|
||||
|
||||
// 对话
|
||||
function handleChat(agent: AgentListItem) {
|
||||
router.push(
|
||||
appContextStore.getContextPath(`/ai-platform/agent/chat/${agent.id}`),
|
||||
);
|
||||
}
|
||||
|
||||
// 发布
|
||||
async function handlePublish(agent: AgentListItem) {
|
||||
try {
|
||||
await publishAgentApi(agent.id);
|
||||
ElMessage.success($t('ai-platform.agent.publishSuccess'));
|
||||
fetchList();
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message || $t('ai-platform.agent.publishFailed'));
|
||||
}
|
||||
}
|
||||
|
||||
// 停用
|
||||
async function handleDisable(agent: AgentListItem) {
|
||||
try {
|
||||
await disableAgentApi(agent.id);
|
||||
ElMessage.success($t('ai-platform.agent.disableSuccess'));
|
||||
fetchList();
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message || $t('ai-platform.agent.operationFailed'));
|
||||
}
|
||||
}
|
||||
|
||||
// 删除
|
||||
async function handleDelete(agent: AgentListItem) {
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
$t('ai-platform.agent.deleteConfirm', { name: agent.name }),
|
||||
$t('ai-platform.agent.deleteTitle'),
|
||||
{ type: 'warning' },
|
||||
);
|
||||
await deleteAgentApi(agent.id);
|
||||
ElMessage.success($t('ai-platform.agent.deleteSuccess'));
|
||||
fetchList();
|
||||
} catch (error: any) {
|
||||
if (error !== 'cancel') {
|
||||
ElMessage.error(error.message || $t('ai-platform.agent.deleteFailed'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取状态标签类型
|
||||
function getStatusType(status: string) {
|
||||
switch (status) {
|
||||
case 'disabled': {
|
||||
return 'danger';
|
||||
}
|
||||
case 'published': {
|
||||
return 'success';
|
||||
}
|
||||
default: {
|
||||
return 'info';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取状态文本
|
||||
function getStatusText(status: string) {
|
||||
switch (status) {
|
||||
case 'disabled': {
|
||||
return $t('ai-platform.agent.status.disabled');
|
||||
}
|
||||
case 'published': {
|
||||
return $t('ai-platform.agent.status.published');
|
||||
}
|
||||
default: {
|
||||
return $t('ai-platform.agent.status.draft');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取模式文本
|
||||
function getModeText(mode: string) {
|
||||
return mode === 'autonomous'
|
||||
? $t('ai-platform.agent.mode.autonomous')
|
||||
: $t('ai-platform.agent.mode.dialogFlow');
|
||||
}
|
||||
|
||||
// 发布到菜单
|
||||
function handlePublishToMenu(agent: AgentListItem) {
|
||||
publishingAgent.value = agent;
|
||||
showPublishToMenuDialog.value = true;
|
||||
}
|
||||
|
||||
// 发布到菜单成功回调
|
||||
function handlePublishedToMenu() {
|
||||
fetchList();
|
||||
}
|
||||
|
||||
// 取消发布菜单
|
||||
async function handleUnpublishMenu(agent: AgentListItem) {
|
||||
try {
|
||||
await unpublishAgentMenuApi(agent.id);
|
||||
ElMessage.success($t('ai-platform.agent.unpublishMenuSuccess'));
|
||||
fetchList();
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message || $t('ai-platform.agent.operationFailed'));
|
||||
}
|
||||
}
|
||||
|
||||
function downloadBlob(blob: Blob, filename: string) {
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = filename;
|
||||
document.body.append(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
async function handleExport(agent: AgentListItem) {
|
||||
try {
|
||||
const blob = await exportAgentConfigApi(agent.id);
|
||||
downloadBlob(blob, `${agent.code}.json`);
|
||||
ElMessage.success($t('ai-platform.agent.importExport.exportSuccess'));
|
||||
} catch {
|
||||
ElMessage.error($t('ai-platform.agent.importExport.exportFailed'));
|
||||
}
|
||||
}
|
||||
|
||||
function handleImported() {
|
||||
fetchList();
|
||||
}
|
||||
|
||||
// 处理更多菜单命令
|
||||
function handleCommand(command: string, agent: AgentListItem) {
|
||||
switch (command) {
|
||||
case 'export': {
|
||||
handleExport(agent);
|
||||
break;
|
||||
}
|
||||
case 'chat': {
|
||||
handleChat(agent);
|
||||
break;
|
||||
}
|
||||
case 'disable': {
|
||||
handleDisable(agent);
|
||||
break;
|
||||
}
|
||||
case 'publish': {
|
||||
handlePublish(agent);
|
||||
break;
|
||||
}
|
||||
case 'publishToMenu': {
|
||||
handlePublishToMenu(agent);
|
||||
break;
|
||||
}
|
||||
case 'unpublishMenu': {
|
||||
handleUnpublishMenu(agent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="agent-list-page">
|
||||
<Page auto-content-height v-loading="loading">
|
||||
<template #title>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-4">
|
||||
<ElInput
|
||||
v-model="searchKeyword"
|
||||
:placeholder="$t('ai-platform.agent.searchPlaceholder')"
|
||||
clearable
|
||||
class="w-64"
|
||||
@keyup.enter="handleSearch"
|
||||
/>
|
||||
<ElButton @click="handleSearch">
|
||||
{{ $t('ai-platform.agent.search') }}
|
||||
</ElButton>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<ElButton @click="showImportDialog = true">
|
||||
<Upload class="mr-1 h-4 w-4" />
|
||||
{{ $t('ai-platform.agent.importExport.import') }}
|
||||
</ElButton>
|
||||
<ElButton type="primary" @click="openCreateDialog">
|
||||
<CirclePlus class="mr-1 h-4 w-4" />
|
||||
{{ $t('ai-platform.agent.create') }}
|
||||
</ElButton>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 智能体卡片列表 -->
|
||||
<div
|
||||
v-if="agentList.length > 0"
|
||||
class="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5"
|
||||
>
|
||||
<ElCard
|
||||
v-for="agent in agentList"
|
||||
:key="agent.id"
|
||||
class="agent-card cursor-pointer transition-shadow"
|
||||
shadow="hover"
|
||||
:body-style="{ padding: '0' }"
|
||||
style="border: none"
|
||||
@click="handleEdit(agent)"
|
||||
>
|
||||
<div class="p-4">
|
||||
<!-- 头部:图标 + 右侧信息区 -->
|
||||
<div class="mb-4 flex gap-3">
|
||||
<div
|
||||
class="bg-primary/10 flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg"
|
||||
>
|
||||
<Bot class="text-primary h-5 w-5" />
|
||||
</div>
|
||||
<div class="min-w-0 flex-1">
|
||||
<!-- name + 操作 -->
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="min-w-0 flex-1 truncate text-sm font-medium">
|
||||
{{ agent.name }}
|
||||
</div>
|
||||
<div
|
||||
class="flex flex-shrink-0 items-center -space-x-1"
|
||||
@click.stop
|
||||
>
|
||||
<ElTooltip
|
||||
:content="$t('ai-platform.agent.edit')"
|
||||
placement="top"
|
||||
>
|
||||
<ElButton text size="small" @click="handleEdit(agent)">
|
||||
<Edit class="h-3.5 w-3.5" />
|
||||
</ElButton>
|
||||
</ElTooltip>
|
||||
<ElTooltip
|
||||
:content="$t('ai-platform.agent.delete')"
|
||||
placement="top"
|
||||
>
|
||||
<ElButton text size="small" @click="handleDelete(agent)">
|
||||
<Trash2 class="h-3.5 w-3.5" />
|
||||
</ElButton>
|
||||
</ElTooltip>
|
||||
<ElDropdown
|
||||
trigger="click"
|
||||
@command="(cmd: string) => handleCommand(cmd, agent)"
|
||||
>
|
||||
<ElButton text size="small">
|
||||
<MoreVertical class="h-3.5 w-3.5" />
|
||||
</ElButton>
|
||||
<template #dropdown>
|
||||
<ElDropdownMenu>
|
||||
<ElDropdownItem
|
||||
v-if="agent.status === 'published'"
|
||||
command="chat"
|
||||
>
|
||||
<MessageSquare class="mr-2 h-4 w-4" />
|
||||
{{ $t('ai-platform.agent.chat') }}
|
||||
</ElDropdownItem>
|
||||
<ElDropdownItem
|
||||
v-if="agent.status === 'draft'"
|
||||
command="publish"
|
||||
>
|
||||
<Play class="mr-2 h-4 w-4" />
|
||||
{{ $t('ai-platform.agent.publish') }}
|
||||
</ElDropdownItem>
|
||||
<ElDropdownItem
|
||||
v-if="
|
||||
agent.status === 'published' && !agent.has_menu
|
||||
"
|
||||
command="publishToMenu"
|
||||
>
|
||||
<Menu class="mr-2 h-4 w-4" />
|
||||
{{ $t('ai-platform.agent.publishToMenu') }}
|
||||
</ElDropdownItem>
|
||||
<ElDropdownItem
|
||||
v-if="
|
||||
agent.status === 'published' && agent.has_menu
|
||||
"
|
||||
command="unpublishMenu"
|
||||
>
|
||||
<Square class="mr-2 h-4 w-4" />
|
||||
{{ $t('ai-platform.agent.unpublishMenu') }}
|
||||
</ElDropdownItem>
|
||||
<ElDropdownItem command="export">
|
||||
<Download class="mr-2 h-4 w-4" />
|
||||
{{ $t('ai-platform.agent.importExport.export') }}
|
||||
</ElDropdownItem>
|
||||
<ElDropdownItem
|
||||
v-if="agent.status === 'published'"
|
||||
command="disable"
|
||||
>
|
||||
<Square class="mr-2 h-4 w-4" />
|
||||
{{ $t('ai-platform.agent.disable') }}
|
||||
</ElDropdownItem>
|
||||
</ElDropdownMenu>
|
||||
</template>
|
||||
</ElDropdown>
|
||||
</div>
|
||||
</div>
|
||||
<!-- code -->
|
||||
<div class="text-muted-foreground font-mono text-xs">
|
||||
{{ agent.code }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 描述 -->
|
||||
<div
|
||||
class="text-muted-foreground mb-4 line-clamp-1 min-h-[18px] text-xs"
|
||||
>
|
||||
{{ agent.description || $t('ai-platform.agent.noDescription') }}
|
||||
</div>
|
||||
<!-- 标签 -->
|
||||
<div class="mb-4 flex items-center justify-between">
|
||||
<div class="flex gap-1">
|
||||
<ElTag size="small" :type="getStatusType(agent.status) as any">
|
||||
{{ getStatusText(agent.status) }}
|
||||
</ElTag>
|
||||
<ElTag size="small" type="info">
|
||||
{{ getModeText(agent.mode) }}
|
||||
</ElTag>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 应用名称 + 创建时间 -->
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="text-muted-foreground text-xs">
|
||||
<span v-if="agent.application_name">{{
|
||||
agent.application_name
|
||||
}}</span>
|
||||
<span v-else>{{ $t('ai-platform.agent.mainApp') }}</span>
|
||||
</div>
|
||||
<span class="text-muted-foreground text-xs">
|
||||
{{ agent.sys_create_datetime }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</ElCard>
|
||||
</div>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<ElEmpty v-else :description="$t('ai-platform.agent.empty')" />
|
||||
|
||||
<!-- 分页 -->
|
||||
<template #footer>
|
||||
<div class="flex w-full items-center justify-end">
|
||||
<ElPagination
|
||||
v-model:current-page="pagination.current"
|
||||
v-model:page-size="pagination.pageSize"
|
||||
:total="pagination.total"
|
||||
:page-sizes="[12, 24, 36, 48]"
|
||||
:pager-count="7"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
background
|
||||
size="small"
|
||||
@current-change="handlePageChange"
|
||||
@size-change="handleSizeChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Page>
|
||||
|
||||
<ImportDialog v-model="showImportDialog" @imported="handleImported" />
|
||||
|
||||
<!-- 发布到菜单弹窗 -->
|
||||
<PublishToMenuDialog
|
||||
v-if="publishingAgent"
|
||||
v-model="showPublishToMenuDialog"
|
||||
:agent-id="publishingAgent.id"
|
||||
:agent-name="publishingAgent.name"
|
||||
:agent-code="publishingAgent.code"
|
||||
@published="handlePublishedToMenu"
|
||||
/>
|
||||
|
||||
<!-- 创建对话框 -->
|
||||
<ElDialog
|
||||
v-model="createDialogVisible"
|
||||
:title="$t('ai-platform.agent.create')"
|
||||
width="500px"
|
||||
>
|
||||
<ElForm label-width="80px">
|
||||
<ElFormItem :label="$t('ai-platform.agent.form.name')" required>
|
||||
<ElInput
|
||||
v-model="createForm.name"
|
||||
:placeholder="$t('ai-platform.agent.form.namePlaceholder')"
|
||||
/>
|
||||
</ElFormItem>
|
||||
<ElFormItem :label="$t('ai-platform.agent.form.code')" required>
|
||||
<ElInput
|
||||
v-model="createForm.code"
|
||||
:placeholder="$t('ai-platform.agent.form.codePlaceholder')"
|
||||
/>
|
||||
</ElFormItem>
|
||||
<ElFormItem :label="$t('ai-platform.agent.form.mode')">
|
||||
<ElSelect v-model="createForm.mode" style="width: 100%">
|
||||
<ElOption
|
||||
:label="$t('ai-platform.agent.mode.autonomousLabel')"
|
||||
value="autonomous"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<div>
|
||||
<div>{{ $t('ai-platform.agent.mode.autonomousLabel') }}</div>
|
||||
<div class="text-muted-foreground text-xs">
|
||||
{{ $t('ai-platform.agent.mode.autonomousDesc') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ElOption>
|
||||
<ElOption
|
||||
:label="$t('ai-platform.agent.mode.dialogFlowLabel')"
|
||||
value="dialog_flow"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<div>
|
||||
<div>{{ $t('ai-platform.agent.mode.dialogFlowLabel') }}</div>
|
||||
<div class="text-muted-foreground text-xs">
|
||||
{{ $t('ai-platform.agent.mode.dialogFlowDesc') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ElOption>
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem :label="$t('ai-platform.agent.form.description')">
|
||||
<ElInput
|
||||
v-model="createForm.description"
|
||||
:rows="3"
|
||||
:placeholder="$t('ai-platform.agent.form.descriptionPlaceholder')"
|
||||
type="textarea"
|
||||
/>
|
||||
</ElFormItem>
|
||||
<ElFormItem
|
||||
v-if="isMainApp"
|
||||
:label="$t('ai-platform.agent.form.globalVisible')"
|
||||
>
|
||||
<ElSwitch v-model="createForm.is_global" />
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
<template #footer>
|
||||
<ElButton @click="createDialogVisible = false">
|
||||
{{ $t('ai-platform.agent.cancel') }}
|
||||
</ElButton>
|
||||
<ElButton :loading="createLoading" type="primary" @click="handleCreate">
|
||||
{{ $t('ai-platform.agent.create') }}
|
||||
</ElButton>
|
||||
</template>
|
||||
</ElDialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.agent-card :deep(.el-card__body) {
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,278 @@
|
||||
<script lang="ts" setup>
|
||||
import type {
|
||||
AgentImportCheckResult,
|
||||
AgentImportInput,
|
||||
} from '#/api/ai-platform/ai-platform';
|
||||
|
||||
import { computed, ref, watch } from 'vue';
|
||||
|
||||
import { Upload } from '@vben/icons';
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import {
|
||||
ElAlert,
|
||||
ElButton,
|
||||
ElDescriptions,
|
||||
ElDescriptionsItem,
|
||||
ElInput,
|
||||
ElMessage,
|
||||
ElUpload,
|
||||
} from 'element-plus';
|
||||
|
||||
import {
|
||||
checkImportAgentApi,
|
||||
importAgentConfigApi,
|
||||
} from '#/api/ai-platform/ai-platform';
|
||||
import { ZqDialog } from '#/components/zq-dialog';
|
||||
import { useAppContextStore } from '#/store/app-context';
|
||||
|
||||
interface Props {
|
||||
modelValue: boolean;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
imported: [];
|
||||
'update:modelValue': [value: boolean];
|
||||
}>();
|
||||
|
||||
const appContextStore = useAppContextStore();
|
||||
|
||||
const dialogVisible = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (val) => emit('update:modelValue', val),
|
||||
});
|
||||
|
||||
type Step = 'check' | 'result' | 'upload';
|
||||
|
||||
const step = ref<Step>('upload');
|
||||
const loading = ref(false);
|
||||
const importData = ref<AgentImportInput | null>(null);
|
||||
const checkResult = ref<AgentImportCheckResult | null>(null);
|
||||
const newCode = ref('');
|
||||
|
||||
function resetState() {
|
||||
step.value = 'upload';
|
||||
loading.value = false;
|
||||
importData.value = null;
|
||||
checkResult.value = null;
|
||||
newCode.value = '';
|
||||
}
|
||||
|
||||
watch(dialogVisible, (val) => {
|
||||
if (!val) resetState();
|
||||
});
|
||||
|
||||
const canConfirmImport = computed(() => {
|
||||
if (!checkResult.value || !importData.value) return false;
|
||||
if (checkResult.value.code_exists && !newCode.value.trim()) return false;
|
||||
return true;
|
||||
});
|
||||
|
||||
const showFooter = computed(() => step.value === 'result');
|
||||
|
||||
function readFileAsJson(file: File): Promise<AgentImportInput> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
try {
|
||||
resolve(JSON.parse(reader.result as string));
|
||||
} catch {
|
||||
reject(new Error('parse'));
|
||||
}
|
||||
};
|
||||
reader.onerror = () => reject(new Error('read'));
|
||||
reader.readAsText(file);
|
||||
});
|
||||
}
|
||||
|
||||
async function handleFileChange(file: any) {
|
||||
const rawFile = (file.raw || file) as File;
|
||||
if (!rawFile) return;
|
||||
|
||||
loading.value = true;
|
||||
try {
|
||||
const data = await readFileAsJson(rawFile);
|
||||
if (!data.name || !data.code) {
|
||||
ElMessage.error($t('ai-platform.agent.importExport.fileParseError'));
|
||||
return;
|
||||
}
|
||||
importData.value = data;
|
||||
newCode.value = data.code;
|
||||
step.value = 'check';
|
||||
await runCheck();
|
||||
} catch {
|
||||
ElMessage.error($t('ai-platform.agent.importExport.fileParseError'));
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function runCheck() {
|
||||
if (!importData.value) return;
|
||||
loading.value = true;
|
||||
try {
|
||||
checkResult.value = await checkImportAgentApi({
|
||||
code: importData.value.code,
|
||||
});
|
||||
step.value = 'result';
|
||||
} catch (error: any) {
|
||||
ElMessage.error(
|
||||
error?.message || $t('ai-platform.agent.importExport.importFailed'),
|
||||
);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleConfirmImport() {
|
||||
if (!importData.value || !canConfirmImport.value) return;
|
||||
loading.value = true;
|
||||
try {
|
||||
const payload: AgentImportInput = {
|
||||
...importData.value,
|
||||
application_id: appContextStore.currentApp?.id,
|
||||
code: checkResult.value?.code_exists
|
||||
? newCode.value.trim()
|
||||
: importData.value.code,
|
||||
};
|
||||
await importAgentConfigApi(payload);
|
||||
ElMessage.success($t('ai-platform.agent.importExport.importSuccess'));
|
||||
dialogVisible.value = false;
|
||||
emit('imported');
|
||||
} catch (error: any) {
|
||||
ElMessage.error(
|
||||
error?.message || $t('ai-platform.agent.importExport.importFailed'),
|
||||
);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ZqDialog
|
||||
v-model="dialogVisible"
|
||||
:title="$t('ai-platform.agent.importExport.importTitle')"
|
||||
:confirm-loading="loading"
|
||||
width="520px"
|
||||
:show-footer="showFooter"
|
||||
@confirm="handleConfirmImport"
|
||||
>
|
||||
<template #footer>
|
||||
<ElButton @click="resetState">
|
||||
{{ $t('ai-platform.agent.importExport.reselect') }}
|
||||
</ElButton>
|
||||
<ElButton
|
||||
type="primary"
|
||||
:loading="loading"
|
||||
:disabled="!canConfirmImport"
|
||||
@click="handleConfirmImport"
|
||||
>
|
||||
{{ $t('ai-platform.agent.importExport.confirmImport') }}
|
||||
</ElButton>
|
||||
</template>
|
||||
|
||||
<div v-if="step === 'upload' || step === 'check'" class="py-4">
|
||||
<ElUpload
|
||||
drag
|
||||
:auto-upload="false"
|
||||
:show-file-list="false"
|
||||
accept=".json"
|
||||
:disabled="loading"
|
||||
@change="handleFileChange"
|
||||
>
|
||||
<div class="flex flex-col items-center py-6">
|
||||
<Upload class="text-muted-foreground mb-3 h-10 w-10" />
|
||||
<div class="text-sm">
|
||||
{{ $t('ai-platform.agent.importExport.dragOrClick') }}
|
||||
</div>
|
||||
<div class="text-muted-foreground mt-1 text-xs">
|
||||
{{ $t('ai-platform.agent.importExport.onlyJson') }}
|
||||
</div>
|
||||
</div>
|
||||
</ElUpload>
|
||||
<div
|
||||
v-if="loading && step === 'check'"
|
||||
class="text-muted-foreground mt-4 text-center text-sm"
|
||||
>
|
||||
{{ $t('ai-platform.agent.importExport.checking') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="step === 'result' && importData && checkResult" class="space-y-4">
|
||||
<ElAlert
|
||||
:title="$t('ai-platform.agent.importExport.appTip')"
|
||||
type="info"
|
||||
show-icon
|
||||
:closable="false"
|
||||
/>
|
||||
<ElAlert
|
||||
:title="$t('ai-platform.agent.importExport.refTip')"
|
||||
type="warning"
|
||||
show-icon
|
||||
:closable="false"
|
||||
/>
|
||||
|
||||
<div>
|
||||
<div class="mb-2 text-sm font-medium">
|
||||
{{ $t('ai-platform.agent.importExport.agentInfo') }}
|
||||
</div>
|
||||
<ElDescriptions :column="1" border size="small">
|
||||
<ElDescriptionsItem :label="$t('ai-platform.agent.form.name')">
|
||||
{{ importData.name }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem :label="$t('ai-platform.agent.form.code')">
|
||||
{{ importData.code }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem
|
||||
v-if="importData.mode"
|
||||
:label="$t('ai-platform.agent.form.mode')"
|
||||
>
|
||||
{{ importData.mode }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem
|
||||
v-if="importData.description"
|
||||
:label="$t('ai-platform.agent.form.description')"
|
||||
>
|
||||
{{ importData.description }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem
|
||||
v-if="importData.model_name"
|
||||
:label="$t('ai-platform.agent.importExport.modelName')"
|
||||
>
|
||||
{{ importData.model_name }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem
|
||||
v-if="importData.workflow_code"
|
||||
:label="$t('ai-platform.agent.importExport.workflowCode')"
|
||||
>
|
||||
{{ importData.workflow_code }}
|
||||
</ElDescriptionsItem>
|
||||
</ElDescriptions>
|
||||
</div>
|
||||
|
||||
<div v-if="checkResult.code_exists">
|
||||
<ElAlert
|
||||
:title="$t('ai-platform.agent.importExport.codeConflictTip')"
|
||||
type="warning"
|
||||
show-icon
|
||||
:closable="false"
|
||||
class="mb-3"
|
||||
/>
|
||||
<ElInput
|
||||
v-model="newCode"
|
||||
:placeholder="$t('ai-platform.agent.importExport.newCodePlaceholder')"
|
||||
/>
|
||||
</div>
|
||||
<ElAlert
|
||||
v-else
|
||||
:title="$t('ai-platform.agent.importExport.codeAvailable')"
|
||||
type="success"
|
||||
show-icon
|
||||
:closable="false"
|
||||
/>
|
||||
</div>
|
||||
</ZqDialog>
|
||||
</template>
|
||||
Reference in New Issue
Block a user