style(web): translate the comments in the src/views directory into English
This commit is contained in:
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:26:44
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 16:26:44
|
||||
*/
|
||||
/**
|
||||
* AI Prompt Assistant Modal
|
||||
* Provides an interactive chat interface to help users optimize their prompts using AI
|
||||
* Features model selection, chat history, and variable insertion
|
||||
*/
|
||||
|
||||
import { forwardRef, useImperativeHandle, useState, useRef } from 'react';
|
||||
import { Button, Form, Input, App, Row, Col } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -19,11 +31,20 @@ import AiPromptVariableModal from './AiPromptVariableModal'
|
||||
import { type SSEMessage } from '@/utils/stream'
|
||||
import Editor from './Editor'
|
||||
|
||||
/**
|
||||
* Component props
|
||||
*/
|
||||
interface AiPromptModalProps {
|
||||
/** Callback to refresh prompt with optimized value */
|
||||
refresh: (value: string) => void;
|
||||
/** Default model to pre-select */
|
||||
defaultModel: ModelListItem | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* AI Prompt Assistant Modal Component
|
||||
* Helps users create and optimize prompts through AI-powered conversation
|
||||
*/
|
||||
const AiPromptModal = forwardRef<AiPromptModalRef, AiPromptModalProps>(({
|
||||
refresh,
|
||||
defaultModel,
|
||||
@@ -42,7 +63,7 @@ const AiPromptModal = forwardRef<AiPromptModalRef, AiPromptModalProps>(({
|
||||
|
||||
const values = Form.useWatch([], form)
|
||||
|
||||
// 封装取消方法,添加关闭弹窗逻辑
|
||||
/** Close modal and reset state */
|
||||
const handleClose = () => {
|
||||
setVisible(false);
|
||||
setLoading(false)
|
||||
@@ -54,6 +75,7 @@ const AiPromptModal = forwardRef<AiPromptModalRef, AiPromptModalProps>(({
|
||||
})
|
||||
};
|
||||
|
||||
/** Open modal and create new prompt session */
|
||||
const handleOpen = () => {
|
||||
createPromptSessions()
|
||||
.then(res => {
|
||||
@@ -66,6 +88,7 @@ const AiPromptModal = forwardRef<AiPromptModalRef, AiPromptModalProps>(({
|
||||
setVisible(true);
|
||||
})
|
||||
};
|
||||
/** Send user message and get AI response */
|
||||
const handleSend = () => {
|
||||
if (!promptSession) return
|
||||
if (!values.model_id) {
|
||||
@@ -115,7 +138,7 @@ const AiPromptModal = forwardRef<AiPromptModalRef, AiPromptModalProps>(({
|
||||
break;
|
||||
case 'end':
|
||||
setLoading(false)
|
||||
// 流结束时同步表单值
|
||||
// Sync form value when stream ends
|
||||
form.setFieldsValue({ current_prompt: currentPromptValueRef.current })
|
||||
break
|
||||
}
|
||||
@@ -134,14 +157,17 @@ const AiPromptModal = forwardRef<AiPromptModalRef, AiPromptModalProps>(({
|
||||
setLoading(false)
|
||||
})
|
||||
}
|
||||
/** Copy current prompt to clipboard */
|
||||
const handleCopy = () => {
|
||||
if (!values.current_prompt || values?.current_prompt?.trim() === '') return
|
||||
copy(values.current_prompt)
|
||||
message.success(t('common.copySuccess'))
|
||||
}
|
||||
/** Open variable selection modal */
|
||||
const handleAdd = () => {
|
||||
aiPromptVariableModalRef.current?.handleOpen()
|
||||
}
|
||||
/** Insert variable into prompt editor */
|
||||
const handleVariableApply = (value: string) => {
|
||||
if (editorRef.current?.insertText) {
|
||||
editorRef.current.insertText(value)
|
||||
@@ -149,6 +175,7 @@ const AiPromptModal = forwardRef<AiPromptModalRef, AiPromptModalProps>(({
|
||||
form.setFieldValue('current_prompt', (values.current_prompt || '') + value)
|
||||
}
|
||||
}
|
||||
/** Apply optimized prompt and close modal */
|
||||
const handleApply = () => {
|
||||
if (!values.current_prompt) {
|
||||
return
|
||||
@@ -157,7 +184,7 @@ const AiPromptModal = forwardRef<AiPromptModalRef, AiPromptModalProps>(({
|
||||
handleClose()
|
||||
}
|
||||
|
||||
// 暴露给父组件的方法
|
||||
/** Expose methods to parent component */
|
||||
useImperativeHandle(ref, () => ({
|
||||
handleOpen,
|
||||
}));
|
||||
|
||||
@@ -1,25 +1,42 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:27:14
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 16:27:14
|
||||
*/
|
||||
/**
|
||||
* AI Prompt Variable Modal
|
||||
* Allows users to insert variables into AI-generated prompts
|
||||
* Supports autocomplete with existing variables
|
||||
*/
|
||||
|
||||
import { forwardRef, useEffect, useImperativeHandle, useState } from 'react';
|
||||
import { Form, Input, App, Select, AutoComplete, type AutoCompleteProps } from 'antd';
|
||||
import { Form, AutoComplete, type AutoCompleteProps } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import type { Application } from '@/views/ApplicationManagement/types'
|
||||
import type { AiPromptVariableModalRef } from '../types'
|
||||
import { createApiKey } from '@/api/apiKey';
|
||||
import RbModal from '@/components/RbModal'
|
||||
|
||||
const FormItem = Form.Item;
|
||||
|
||||
/**
|
||||
* Component props
|
||||
*/
|
||||
interface AiPromptVariableModalProps {
|
||||
/** Callback to insert variable into prompt */
|
||||
refresh: (value: string) => void;
|
||||
/** List of available variables */
|
||||
variables: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Variable selection modal for AI prompt assistant
|
||||
*/
|
||||
const AiPromptVariableModal = forwardRef<AiPromptVariableModalRef, AiPromptVariableModalProps>(({
|
||||
refresh,
|
||||
variables
|
||||
}, ref) => {
|
||||
const { t } = useTranslation();
|
||||
const { message } = App.useApp();
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [form] = Form.useForm();
|
||||
const [loading, setLoading] = useState(false)
|
||||
@@ -31,6 +48,7 @@ const AiPromptVariableModal = forwardRef<AiPromptVariableModalRef, AiPromptVaria
|
||||
label: `{{${key}}}`
|
||||
})))
|
||||
}, [variables])
|
||||
/** Search and filter variables */
|
||||
const handleSearch = (value: string) => {
|
||||
const filterKeys = variables?.filter(key => key.includes(value))
|
||||
|
||||
@@ -47,18 +65,19 @@ const AiPromptVariableModal = forwardRef<AiPromptVariableModalRef, AiPromptVaria
|
||||
}
|
||||
}
|
||||
|
||||
// 封装取消方法,添加关闭弹窗逻辑
|
||||
/** Close modal and reset form */
|
||||
const handleClose = () => {
|
||||
setVisible(false);
|
||||
form.resetFields();
|
||||
setLoading(false)
|
||||
};
|
||||
|
||||
/** Open modal */
|
||||
const handleOpen = () => {
|
||||
setVisible(true);
|
||||
form.resetFields();
|
||||
};
|
||||
// 封装保存方法,添加提交逻辑
|
||||
/** Apply selected variable */
|
||||
const handleSave = () => {
|
||||
const variableName = form.getFieldValue('variableName')
|
||||
|
||||
@@ -68,7 +87,7 @@ const AiPromptVariableModal = forwardRef<AiPromptVariableModalRef, AiPromptVaria
|
||||
handleClose()
|
||||
}
|
||||
|
||||
// 暴露给父组件的方法
|
||||
/** Expose methods to parent component */
|
||||
useImperativeHandle(ref, () => ({
|
||||
handleOpen,
|
||||
handleClose
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:27:22
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 16:27:22
|
||||
*/
|
||||
/**
|
||||
* API Key Configuration Modal
|
||||
* Allows configuring rate limits and daily usage limits for API keys
|
||||
*/
|
||||
|
||||
import { forwardRef, useImperativeHandle, useState } from 'react';
|
||||
import { Form, Slider } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -7,10 +18,17 @@ import RbModal from '@/components/RbModal'
|
||||
import { updateApiKey } from '@/api/apiKey';
|
||||
import type { ApiKey } from '@/views/ApiKeyManagement/types'
|
||||
|
||||
/**
|
||||
* Component props
|
||||
*/
|
||||
interface ApiKeyConfigModalProps {
|
||||
/** Callback to refresh API key list */
|
||||
refresh: () => void;
|
||||
}
|
||||
const ApiKeyConfigModal = forwardRef<ApiKeyConfigModalRef, ApiKeyConfigModalProps>(({
|
||||
|
||||
/**
|
||||
* Modal for configuring API key limits
|
||||
*/const ApiKeyConfigModal = forwardRef<ApiKeyConfigModalRef, ApiKeyConfigModalProps>(({
|
||||
refresh
|
||||
}, ref) => {
|
||||
const { t } = useTranslation();
|
||||
@@ -20,7 +38,7 @@ const ApiKeyConfigModal = forwardRef<ApiKeyConfigModalRef, ApiKeyConfigModalProp
|
||||
const values = Form.useWatch<ApiKey>([], form)
|
||||
const [editVo, setEditVo] = useState<ApiKey | null>(null)
|
||||
|
||||
// 封装取消方法,添加关闭弹窗逻辑
|
||||
/** Close modal and reset state */
|
||||
const handleClose = () => {
|
||||
form.resetFields();
|
||||
setLoading(false)
|
||||
@@ -28,6 +46,7 @@ const ApiKeyConfigModal = forwardRef<ApiKeyConfigModalRef, ApiKeyConfigModalProp
|
||||
setVisible(false);
|
||||
};
|
||||
|
||||
/** Open modal with API key data */
|
||||
const handleOpen = (apiKey: ApiKey) => {
|
||||
setVisible(true);
|
||||
setEditVo(apiKey)
|
||||
@@ -36,7 +55,7 @@ const ApiKeyConfigModal = forwardRef<ApiKeyConfigModalRef, ApiKeyConfigModalProp
|
||||
rate_limit: apiKey.rate_limit
|
||||
});
|
||||
};
|
||||
// 封装保存方法,添加提交逻辑
|
||||
/** Save API key configuration */
|
||||
const handleSave = () => {
|
||||
if (!editVo?.id) return
|
||||
form.validateFields()
|
||||
@@ -52,7 +71,7 @@ const ApiKeyConfigModal = forwardRef<ApiKeyConfigModalRef, ApiKeyConfigModalProp
|
||||
})
|
||||
}
|
||||
|
||||
// 暴露给父组件的方法
|
||||
/** Expose methods to parent component */
|
||||
useImperativeHandle(ref, () => ({
|
||||
handleOpen,
|
||||
handleClose
|
||||
@@ -73,7 +92,7 @@ const ApiKeyConfigModal = forwardRef<ApiKeyConfigModalRef, ApiKeyConfigModalProp
|
||||
className="rb:px-2.5!"
|
||||
scrollToFirstError={{ behavior: 'instant', block: 'end', focus: true }}
|
||||
>
|
||||
{/* QPS 限制(每秒请求数) */}
|
||||
{/* QPS limit (requests per second) */}
|
||||
<>
|
||||
<div className="rb:text-[14px] rb:font-medium rb:leading-5 rb:mb-2">
|
||||
{t(`application.qpsLimit`)}({t('application.qpsLimitTip')})
|
||||
@@ -98,7 +117,7 @@ const ApiKeyConfigModal = forwardRef<ApiKeyConfigModalRef, ApiKeyConfigModalProp
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
{/* 日调用量限制 */}
|
||||
{/* Daily usage limit */}
|
||||
<>
|
||||
<div className="rb:text-[14px] rb:font-medium rb:leading-5 rb:mt-6 rb:mb-2">
|
||||
{t(`application.dailyUsageLimit`)}
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:27:25
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 16:27:25
|
||||
*/
|
||||
/**
|
||||
* API Key Creation Modal
|
||||
* Allows creating new API keys for application access
|
||||
*/
|
||||
|
||||
import { forwardRef, useImperativeHandle, useState } from 'react';
|
||||
import { Form, Input, App } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -9,11 +20,19 @@ import RbModal from '@/components/RbModal'
|
||||
|
||||
const FormItem = Form.Item;
|
||||
|
||||
/**
|
||||
* Component props
|
||||
*/
|
||||
interface ApiKeyModalProps {
|
||||
/** Callback to refresh API key list */
|
||||
refresh: () => void;
|
||||
/** Application data */
|
||||
application?: Application | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modal for creating new API keys
|
||||
*/
|
||||
const ApiKeyModal = forwardRef<ApiKeyModalRef, ApiKeyModalProps>(({
|
||||
refresh,
|
||||
application
|
||||
@@ -24,18 +43,19 @@ const ApiKeyModal = forwardRef<ApiKeyModalRef, ApiKeyModalProps>(({
|
||||
const [form] = Form.useForm();
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
// 封装取消方法,添加关闭弹窗逻辑
|
||||
/** Close modal and reset form */
|
||||
const handleClose = () => {
|
||||
setVisible(false);
|
||||
form.resetFields();
|
||||
setLoading(false)
|
||||
};
|
||||
|
||||
/** Open modal */
|
||||
const handleOpen = () => {
|
||||
setVisible(true);
|
||||
form.resetFields();
|
||||
};
|
||||
// 封装保存方法,添加提交逻辑
|
||||
/** Create new API key */
|
||||
const handleSave = () => {
|
||||
if (!application) return
|
||||
form.validateFields()
|
||||
@@ -58,7 +78,7 @@ const ApiKeyModal = forwardRef<ApiKeyModalRef, ApiKeyModalProps>(({
|
||||
})
|
||||
}
|
||||
|
||||
// 暴露给父组件的方法
|
||||
/** Expose methods to parent component */
|
||||
useImperativeHandle(ref, () => ({
|
||||
handleOpen,
|
||||
handleClose
|
||||
@@ -78,7 +98,7 @@ const ApiKeyModal = forwardRef<ApiKeyModalRef, ApiKeyModalProps>(({
|
||||
layout="vertical"
|
||||
scrollToFirstError={{ behavior: 'instant', block: 'end', focus: true }}
|
||||
>
|
||||
{/* Key 名称 */}
|
||||
{/* Key name */}
|
||||
<FormItem
|
||||
name="name"
|
||||
label={t('application.apiKeyName')}
|
||||
@@ -89,7 +109,7 @@ const ApiKeyModal = forwardRef<ApiKeyModalRef, ApiKeyModalProps>(({
|
||||
>
|
||||
<Input placeholder={t('application.apiKeyNamePlaceholder')} />
|
||||
</FormItem>
|
||||
{/* 描述 */}
|
||||
{/* Description */}
|
||||
<FormItem
|
||||
name="description"
|
||||
label={t('application.description')}
|
||||
|
||||
@@ -1,13 +1,31 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:27:31
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 16:27:31
|
||||
*/
|
||||
import { type FC, type ReactNode } from 'react'
|
||||
|
||||
import RbCard from '@/components/RbCard/Card'
|
||||
|
||||
/**
|
||||
* Props for Card component
|
||||
*/
|
||||
interface CardProps {
|
||||
/** Card title */
|
||||
title?: string | ReactNode;
|
||||
/** Card subtitle */
|
||||
subTitle?: string | ReactNode;
|
||||
/** Card content */
|
||||
children: ReactNode;
|
||||
/** Extra content in header */
|
||||
extra?: ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Card component wrapper
|
||||
* Styled card with left border accent for application configuration sections
|
||||
*/
|
||||
const Card: FC<CardProps> = ({
|
||||
title,
|
||||
subTitle,
|
||||
|
||||
@@ -1,7 +1,20 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:27:39
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 16:27:39
|
||||
*/
|
||||
/**
|
||||
* Chat debugging component for application testing
|
||||
* Supports both single agent and multi-agent cluster modes
|
||||
* Provides real-time streaming responses and conversation history
|
||||
*/
|
||||
|
||||
import { type FC, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import clsx from 'clsx'
|
||||
import { Input, Form } from 'antd'
|
||||
|
||||
import ChatIcon from '@/assets/images/application/chat.png'
|
||||
import ChatSendIcon from '@/assets/images/application/chatSend.svg'
|
||||
import DebuggingEmpty from '@/assets/images/application/debuggingEmpty.png'
|
||||
@@ -12,13 +25,26 @@ import ChatContent from '@/components/Chat/ChatContent'
|
||||
import type { ChatItem } from '@/components/Chat/types'
|
||||
import { type SSEMessage } from '@/utils/stream'
|
||||
|
||||
/**
|
||||
* Component props
|
||||
*/
|
||||
interface ChatProps {
|
||||
/** List of chat configurations for comparison */
|
||||
chatList: ChatData[];
|
||||
/** Application configuration data */
|
||||
data: Config;
|
||||
/** Update chat list state */
|
||||
updateChatList: React.Dispatch<React.SetStateAction<ChatData[]>>;
|
||||
/** Save configuration before running */
|
||||
handleSave: (flag?: boolean) => Promise<unknown>;
|
||||
/** Source type: multi-agent cluster or single agent */
|
||||
source?: 'multi_agent' | 'agent';
|
||||
}
|
||||
|
||||
/**
|
||||
* Chat debugging component
|
||||
* Allows testing application with different model configurations side-by-side
|
||||
*/
|
||||
const Chat: FC<ChatProps> = ({ chatList, data, updateChatList, handleSave, source = 'agent' }) => {
|
||||
const { t } = useTranslation();
|
||||
const [form] = Form.useForm<{ message: string }>()
|
||||
@@ -31,6 +57,7 @@ const Chat: FC<ChatProps> = ({ chatList, data, updateChatList, handleSave, sourc
|
||||
setIsCluster(source === 'multi_agent')
|
||||
}, [source])
|
||||
|
||||
/** Add user message to all chat lists */
|
||||
const addUserMessage = (message: string) => {
|
||||
const newUserMessage: ChatItem = {
|
||||
role: 'user',
|
||||
@@ -42,6 +69,7 @@ const Chat: FC<ChatProps> = ({ chatList, data, updateChatList, handleSave, sourc
|
||||
list: [...(item.list || []), newUserMessage]
|
||||
})))
|
||||
}
|
||||
/** Add empty assistant message placeholder */
|
||||
const addAssistantMessage = () => {
|
||||
const assistantMessage: ChatItem = {
|
||||
role: 'assistant',
|
||||
@@ -65,6 +93,7 @@ const Chat: FC<ChatProps> = ({ chatList, data, updateChatList, handleSave, sourc
|
||||
})))
|
||||
}
|
||||
}
|
||||
/** Update assistant message with streaming content */
|
||||
const updateAssistantMessage = (content?: string, model_config_id?: string, conversation_id?: string) => {
|
||||
if (!content || !model_config_id) return
|
||||
updateChatList(prev => {
|
||||
@@ -92,6 +121,7 @@ const Chat: FC<ChatProps> = ({ chatList, data, updateChatList, handleSave, sourc
|
||||
return prev;
|
||||
})
|
||||
}
|
||||
/** Update assistant message when error occurs */
|
||||
const updateErrorAssistantMessage = (message_length: number, model_config_id?: string) => {
|
||||
if (message_length > 0 || !model_config_id) return
|
||||
|
||||
@@ -120,6 +150,7 @@ const Chat: FC<ChatProps> = ({ chatList, data, updateChatList, handleSave, sourc
|
||||
return prev
|
||||
})
|
||||
}
|
||||
/** Send message for agent comparison mode */
|
||||
const handleSend = () => {
|
||||
if (loading) return
|
||||
setLoading(true)
|
||||
@@ -176,6 +207,7 @@ const Chat: FC<ChatProps> = ({ chatList, data, updateChatList, handleSave, sourc
|
||||
})
|
||||
}
|
||||
|
||||
/** Add assistant message for cluster mode */
|
||||
const addClusterAssistantMessage = () => {
|
||||
const assistantMessage: ChatItem = {
|
||||
role: 'assistant',
|
||||
@@ -187,6 +219,7 @@ const Chat: FC<ChatProps> = ({ chatList, data, updateChatList, handleSave, sourc
|
||||
list: [...(item.list || []), assistantMessage]
|
||||
})))
|
||||
}
|
||||
/** Update cluster assistant message with content */
|
||||
const updateClusterAssistantMessage = (content?: string) => {
|
||||
if (!content) return
|
||||
updateChatList(prev => {
|
||||
@@ -209,6 +242,7 @@ const Chat: FC<ChatProps> = ({ chatList, data, updateChatList, handleSave, sourc
|
||||
return [...modelChatList]
|
||||
})
|
||||
}
|
||||
/** Update cluster message when error occurs */
|
||||
const updateClusterErrorAssistantMessage = (message_length: number) => {
|
||||
if (message_length > 0) return
|
||||
|
||||
@@ -232,6 +266,7 @@ const Chat: FC<ChatProps> = ({ chatList, data, updateChatList, handleSave, sourc
|
||||
return [...modelChatList]
|
||||
})
|
||||
}
|
||||
/** Send message for cluster mode */
|
||||
const handleClusterSend = () => {
|
||||
if (loading) return
|
||||
setLoading(true)
|
||||
@@ -291,6 +326,7 @@ const Chat: FC<ChatProps> = ({ chatList, data, updateChatList, handleSave, sourc
|
||||
})
|
||||
}
|
||||
|
||||
/** Delete chat configuration from list */
|
||||
const handleDelete = (index: number) => {
|
||||
updateChatList(chatList.filter((_, voIndex) => voIndex !== index))
|
||||
}
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:27:44
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 16:27:44
|
||||
*/
|
||||
/**
|
||||
* Chat Variable Configuration Modal
|
||||
* Allows users to configure variable values before starting a chat session
|
||||
*/
|
||||
|
||||
import { forwardRef, useImperativeHandle, useState } from 'react';
|
||||
import { Form, Input, InputNumber } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -6,10 +17,17 @@ import type { ChatVariableConfigModalRef } from '../types'
|
||||
import type { Variable } from './VariableList/types'
|
||||
import RbModal from '@/components/RbModal'
|
||||
|
||||
/**
|
||||
* Component props
|
||||
*/
|
||||
interface VariableEditModalProps {
|
||||
/** Callback to update variables */
|
||||
refresh: (values: Variable[]) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modal for configuring chat variables
|
||||
*/
|
||||
const ChatVariableConfigModal = forwardRef<ChatVariableConfigModalRef, VariableEditModalProps>(({
|
||||
refresh,
|
||||
}, ref) => {
|
||||
@@ -19,20 +37,21 @@ const ChatVariableConfigModal = forwardRef<ChatVariableConfigModalRef, VariableE
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [initialValues, setInitialValues] = useState<Variable[]>([])
|
||||
|
||||
// 封装取消方法,添加关闭弹窗逻辑
|
||||
/** Close modal and reset form */
|
||||
const handleClose = () => {
|
||||
setVisible(false);
|
||||
form.resetFields();
|
||||
setLoading(false)
|
||||
};
|
||||
|
||||
/** Open modal with variable list */
|
||||
const handleOpen = (values: Variable[]) => {
|
||||
console.log('values', values)
|
||||
setVisible(true);
|
||||
form.setFieldsValue({variables: values})
|
||||
setInitialValues([...values])
|
||||
};
|
||||
// 封装保存方法,添加提交逻辑
|
||||
/** Save variable configuration */
|
||||
const handleSave = () => {
|
||||
form.validateFields().then((values) => {
|
||||
refresh([
|
||||
@@ -42,7 +61,7 @@ const ChatVariableConfigModal = forwardRef<ChatVariableConfigModalRef, VariableE
|
||||
})
|
||||
}
|
||||
|
||||
// 暴露给父组件的方法
|
||||
/** Expose methods to parent component */
|
||||
useImperativeHandle(ref, () => ({
|
||||
handleOpen,
|
||||
handleClose
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:27:52
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 16:27:52
|
||||
*/
|
||||
import { type FC, useRef } from 'react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { Layout, Tabs, Dropdown, Button, Flex } from 'antd';
|
||||
import type { MenuProps } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import styles from '../index.module.css'
|
||||
import logoutIcon from '@/assets/images/logout.svg'
|
||||
import editIcon from '@/assets/images/edit_hover.svg'
|
||||
@@ -17,21 +24,43 @@ import CopyModal from './CopyModal'
|
||||
|
||||
const { Header } = Layout;
|
||||
|
||||
/**
|
||||
* Tab keys for application configuration
|
||||
*/
|
||||
const tabKeys = ['arrangement', 'api', 'release', 'statistics']
|
||||
|
||||
/**
|
||||
* Menu icon mapping
|
||||
*/
|
||||
const menuIcons: Record<string, string> = {
|
||||
edit: editIcon,
|
||||
copy: copyIcon,
|
||||
export: exportIcon,
|
||||
delete: deleteIcon
|
||||
}
|
||||
|
||||
/**
|
||||
* Props for ConfigHeader component
|
||||
*/
|
||||
interface ConfigHeaderProps {
|
||||
/** Application data */
|
||||
application?: Application;
|
||||
/** Active tab key */
|
||||
activeTab: string;
|
||||
/** Tab change handler */
|
||||
handleChangeTab: (key: string) => void;
|
||||
/** Refresh application data */
|
||||
refresh: () => void;
|
||||
/** Workflow component ref */
|
||||
workflowRef: React.RefObject<WorkflowRef>
|
||||
/** App component ref (Agent/Cluster/Workflow) */
|
||||
appRef?: React.RefObject<AgentRef | ClusterRef | WorkflowRef>
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration header component
|
||||
* Displays application name, tabs, and action buttons
|
||||
*/
|
||||
const ConfigHeader: FC<ConfigHeaderProps> = ({
|
||||
application, activeTab, handleChangeTab, refresh,
|
||||
workflowRef,
|
||||
@@ -42,12 +71,18 @@ const ConfigHeader: FC<ConfigHeaderProps> = ({
|
||||
const applicationModalRef = useRef<ApplicationModalRef>(null);
|
||||
const copyModalRef = useRef<CopyModalRef>(null);
|
||||
|
||||
/**
|
||||
* Format tab items for display
|
||||
*/
|
||||
const formatTabItems = () => {
|
||||
return tabKeys.map(key => ({
|
||||
key,
|
||||
label: t(`application.${key}`),
|
||||
}))
|
||||
}
|
||||
/**
|
||||
* Format dropdown menu items
|
||||
*/
|
||||
const formatMenuItems = () => {
|
||||
const items = ['edit', 'copy', 'export', 'delete'].map(key => ({
|
||||
key,
|
||||
@@ -59,6 +94,9 @@ const ConfigHeader: FC<ConfigHeaderProps> = ({
|
||||
onClick: handleClick
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Handle menu item click
|
||||
*/
|
||||
const handleClick: MenuProps['onClick'] = ({ key }) => {
|
||||
switch (key) {
|
||||
case 'edit':
|
||||
@@ -74,6 +112,9 @@ const ConfigHeader: FC<ConfigHeaderProps> = ({
|
||||
break;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Delete application with confirmation
|
||||
*/
|
||||
const handleDelete = () => {
|
||||
if (!id) {
|
||||
return
|
||||
@@ -86,21 +127,36 @@ const ConfigHeader: FC<ConfigHeaderProps> = ({
|
||||
console.error('Failed to delete application');
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Navigate to application list
|
||||
*/
|
||||
const goToApplication = () => {
|
||||
navigate('/application', { replace: true })
|
||||
}
|
||||
/**
|
||||
* Save workflow configuration
|
||||
*/
|
||||
const save = () => {
|
||||
workflowRef.current?.handleSave()
|
||||
}
|
||||
/**
|
||||
* Run workflow
|
||||
*/
|
||||
const run = () => {
|
||||
workflowRef.current?.handleSave(false)
|
||||
.then(() => {
|
||||
workflowRef.current?.handleRun()
|
||||
})
|
||||
}
|
||||
/**
|
||||
* Clear workflow canvas
|
||||
*/
|
||||
const clear = () => {
|
||||
workflowRef?.current?.graphRef?.current?.clearCells()
|
||||
}
|
||||
/**
|
||||
* Add variable to workflow
|
||||
*/
|
||||
const addvariable = () => {
|
||||
workflowRef?.current?.addVariable()
|
||||
}
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:27:56
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 16:27:56
|
||||
*/
|
||||
/**
|
||||
* Copy Application Modal
|
||||
* Allows users to duplicate an existing application with a new name
|
||||
*/
|
||||
|
||||
import { forwardRef, useImperativeHandle, useState } from 'react';
|
||||
import { Form, Input } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -10,10 +21,17 @@ import type { Application } from '@/views/ApplicationManagement/types'
|
||||
|
||||
const FormItem = Form.Item;
|
||||
|
||||
/**
|
||||
* Component props
|
||||
*/
|
||||
interface CopyModalProps {
|
||||
/** Application data to copy */
|
||||
data: Application
|
||||
}
|
||||
|
||||
/**
|
||||
* Modal for copying applications
|
||||
*/
|
||||
const CopyModal = forwardRef<CopyModalRef, CopyModalProps>(({
|
||||
data
|
||||
}, ref) => {
|
||||
@@ -23,17 +41,18 @@ const CopyModal = forwardRef<CopyModalRef, CopyModalProps>(({
|
||||
const [form] = Form.useForm();
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
// 封装取消方法,添加关闭弹窗逻辑
|
||||
/** Close modal and reset form */
|
||||
const handleClose = () => {
|
||||
setVisible(false);
|
||||
form.resetFields();
|
||||
setLoading(false)
|
||||
};
|
||||
|
||||
/** Open modal */
|
||||
const handleOpen = () => {
|
||||
setVisible(true);
|
||||
};
|
||||
// 封装保存方法,添加提交逻辑
|
||||
/** Copy application with new name */
|
||||
const handleSave = () => {
|
||||
setVisible(false);
|
||||
setLoading(true)
|
||||
@@ -48,7 +67,7 @@ const CopyModal = forwardRef<CopyModalRef, CopyModalProps>(({
|
||||
})
|
||||
}
|
||||
|
||||
// 暴露给父组件的方法
|
||||
/** Expose methods to parent component */
|
||||
useImperativeHandle(ref, () => ({
|
||||
handleOpen,
|
||||
handleClose
|
||||
@@ -68,7 +87,7 @@ const CopyModal = forwardRef<CopyModalRef, CopyModalProps>(({
|
||||
form={form}
|
||||
layout="vertical"
|
||||
>
|
||||
{/* 应用名 */}
|
||||
{/* Application name */}
|
||||
<FormItem
|
||||
name="new_name"
|
||||
label={t('application.applicationName')}
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:25:17
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 16:25:17
|
||||
*/
|
||||
/**
|
||||
* Rich text editor component using Lexical framework
|
||||
* Provides text editing with insert, append, clear, and scroll capabilities
|
||||
*/
|
||||
|
||||
import {forwardRef, useImperativeHandle } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import { LexicalComposer } from '@lexical/react/LexicalComposer';
|
||||
@@ -6,25 +17,44 @@ import { ContentEditable } from '@lexical/react/LexicalContentEditable';
|
||||
import { LexicalErrorBoundary } from '@lexical/react/LexicalErrorBoundary';
|
||||
import { $getSelection, $getRoot, $createParagraphNode, $createTextNode, $isParagraphNode, $isTextNode } from 'lexical';
|
||||
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
||||
|
||||
import InitialValuePlugin from './plugin/InitialValuePlugin'
|
||||
import LineBreakPlugin from './plugin/LineBreakPlugin';
|
||||
import InsertTextPlugin from './plugin/InsertTextPlugin';
|
||||
|
||||
/**
|
||||
* Editor ref methods exposed to parent components
|
||||
*/
|
||||
export interface EditorRef {
|
||||
/** Insert text at current cursor position */
|
||||
insertText: (text: string) => void;
|
||||
/** Append text to the end of content */
|
||||
appendText: (text: string) => void;
|
||||
/** Clear all editor content */
|
||||
clear: () => void;
|
||||
/** Scroll editor to bottom */
|
||||
scrollToBottom: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Editor component props
|
||||
*/
|
||||
interface LexicalEditorProps {
|
||||
/** Additional CSS class names */
|
||||
className?: string;
|
||||
/** Placeholder text when editor is empty */
|
||||
placeholder?: string;
|
||||
/** Initial editor value */
|
||||
value?: string;
|
||||
/** Callback when content changes */
|
||||
onChange?: (value: string) => void;
|
||||
/** Editor height in pixels */
|
||||
height?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lexical editor theme configuration
|
||||
*/
|
||||
const theme = {
|
||||
paragraph: 'editor-paragraph',
|
||||
text: {
|
||||
@@ -33,6 +63,9 @@ const theme = {
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Editor content component with Lexical context
|
||||
*/
|
||||
const EditorContent = forwardRef<EditorRef, LexicalEditorProps>(({
|
||||
className = '',
|
||||
value,
|
||||
@@ -41,6 +74,13 @@ const EditorContent = forwardRef<EditorRef, LexicalEditorProps>(({
|
||||
}, ref) => {
|
||||
const [editor] = useLexicalComposerContext();
|
||||
|
||||
/**
|
||||
* Expose editor methods to parent component
|
||||
* - insertText: Insert at cursor position
|
||||
* - appendText: Append to end of content
|
||||
* - clear: Clear all content
|
||||
* - scrollToBottom: Scroll to bottom
|
||||
*/
|
||||
useImperativeHandle(ref, () => ({
|
||||
insertText: (text: string) => {
|
||||
editor.update(() => {
|
||||
@@ -109,6 +149,10 @@ const EditorContent = forwardRef<EditorRef, LexicalEditorProps>(({
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* Main editor wrapper component
|
||||
* Initializes Lexical composer with configuration
|
||||
*/
|
||||
const Editor = forwardRef<EditorRef, LexicalEditorProps>((props, ref) => {
|
||||
const initialConfig = {
|
||||
namespace: 'Editor',
|
||||
|
||||
@@ -1,20 +1,34 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:24:59
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 16:24:59
|
||||
*/
|
||||
/**
|
||||
* Initial Value Plugin
|
||||
* Sets the initial content of the Lexical editor
|
||||
* Only updates when the value prop changes
|
||||
*/
|
||||
|
||||
import { type FC, useEffect, useRef } from 'react';
|
||||
import { $getRoot, $createParagraphNode, $createTextNode } from 'lexical';
|
||||
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
||||
|
||||
// 设置初始值的插件
|
||||
/**
|
||||
* Plugin to set initial editor value
|
||||
*/
|
||||
const InitialValuePlugin: FC<{ value?: string }> = ({ value }) => {
|
||||
const [editor] = useLexicalComposerContext();
|
||||
const lastValueRef = useRef<string | undefined>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
// 只有当value真正发生变化时才更新
|
||||
// Only update when value actually changes
|
||||
if (lastValueRef.current !== value) {
|
||||
editor.update(() => {
|
||||
const root = $getRoot();
|
||||
const currentText = root.getTextContent();
|
||||
|
||||
// 如果当前内容和新值相同,则不更新
|
||||
// If current content matches new value, don't update
|
||||
if (currentText === (value || '')) {
|
||||
return;
|
||||
}
|
||||
@@ -26,7 +40,7 @@ const InitialValuePlugin: FC<{ value?: string }> = ({ value }) => {
|
||||
paragraph.append(textNode);
|
||||
root.append(paragraph);
|
||||
} else {
|
||||
// 当value为undefined或空时,创建一个空段落
|
||||
// When value is undefined or empty, create an empty paragraph
|
||||
const paragraph = $createParagraphNode();
|
||||
root.append(paragraph);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,22 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:25:05
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 16:25:05
|
||||
*/
|
||||
/**
|
||||
* Insert Text Plugin
|
||||
* Provides functionality to insert text at the current cursor position
|
||||
*/
|
||||
|
||||
import { forwardRef, useImperativeHandle } from 'react';
|
||||
import { $getSelection } from 'lexical';
|
||||
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
||||
import type { EditorRef } from '../index'
|
||||
|
||||
// 插入文本的插件
|
||||
const InsertTextPlugin = forwardRef<EditorRef>((_, ref) => {
|
||||
/**
|
||||
* Plugin to insert text at cursor position
|
||||
*/
|
||||
const InsertTextPlugin = forwardRef<{ insertText: (text: string) => void; }>((_, ref) => {
|
||||
const [editor] = useLexicalComposerContext();
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
|
||||
@@ -1,8 +1,22 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:25:09
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 16:25:09
|
||||
*/
|
||||
/**
|
||||
* Line Break Plugin
|
||||
* Handles line breaks and triggers onChange callback when editor content changes
|
||||
* Converts \n escape sequences to actual line breaks
|
||||
*/
|
||||
|
||||
import { type FC, useEffect } from 'react';
|
||||
import { $getRoot } from 'lexical';
|
||||
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
||||
|
||||
// 处理换行的插件
|
||||
/**
|
||||
* Plugin to handle line breaks and content changes
|
||||
*/
|
||||
const LineBreakPlugin: FC<{ onChange?: (value: string) => void }> = ({ onChange }) => {
|
||||
const [editor] = useLexicalComposerContext();
|
||||
|
||||
@@ -11,7 +25,7 @@ const LineBreakPlugin: FC<{ onChange?: (value: string) => void }> = ({ onChange
|
||||
editorState.read(() => {
|
||||
const root = $getRoot();
|
||||
const textContent = root.getTextContent();
|
||||
// 将\n转换为实际换行
|
||||
// Convert \n to actual line breaks
|
||||
const processedContent = textContent.replace(/\\n/g, '\n');
|
||||
onChange?.(processedContent);
|
||||
});
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:25:32
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 16:25:32
|
||||
*/
|
||||
/**
|
||||
* Knowledge Base Component
|
||||
* Manages knowledge base associations for the application
|
||||
* Allows adding, configuring, and removing knowledge bases
|
||||
*/
|
||||
|
||||
import { type FC, useRef, useState, useEffect } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Space, Button, List } from 'antd'
|
||||
|
||||
import knowledgeEmpty from '@/assets/images/application/knowledgeEmpty.svg'
|
||||
import type {
|
||||
KnowledgeConfigForm,
|
||||
@@ -19,6 +32,11 @@ import Tag from '@/components/Tag'
|
||||
import { getKnowledgeBaseList } from '@/api/knowledgeBase'
|
||||
import Card from '../Card'
|
||||
|
||||
/**
|
||||
* Knowledge base management component
|
||||
* @param value - Current knowledge configuration
|
||||
* @param onChange - Callback when configuration changes
|
||||
*/
|
||||
const Knowledge: FC<{value?: KnowledgeConfig; onChange?: (config: KnowledgeConfig) => void}> = ({value = {knowledge_bases: []}, onChange}) => {
|
||||
const { t } = useTranslation()
|
||||
const knowledgeModalRef = useRef<KnowledgeModalRef>(null)
|
||||
@@ -32,10 +50,10 @@ const Knowledge: FC<{value?: KnowledgeConfig; onChange?: (config: KnowledgeConfi
|
||||
setEditConfig({ ...(value || {}) })
|
||||
const knowledge_bases = [...(value.knowledge_bases || [])]
|
||||
|
||||
// 检查是否有knowledge_bases缺少name字段
|
||||
// Check if knowledge_bases are missing name field
|
||||
const basesWithoutName = knowledge_bases.filter(base => !base.name)
|
||||
if (basesWithoutName.length > 0) {
|
||||
// 调用接口获取完整的知识库信息
|
||||
// Call API to get complete knowledge base information
|
||||
getKnowledgeBaseList().then(res => {
|
||||
const fullBases = knowledge_bases.map(base => {
|
||||
if (!base.name) {
|
||||
@@ -54,12 +72,15 @@ const Knowledge: FC<{value?: KnowledgeConfig; onChange?: (config: KnowledgeConfi
|
||||
}
|
||||
}, [value])
|
||||
|
||||
/** Open global knowledge configuration modal */
|
||||
const handleKnowledgeConfig = () => {
|
||||
knowledgeGlobalConfigModalRef.current?.handleOpen()
|
||||
}
|
||||
/** Open knowledge base selection modal */
|
||||
const handleAddKnowledge = () => {
|
||||
knowledgeModalRef.current?.handleOpen()
|
||||
}
|
||||
/** Remove knowledge base from list */
|
||||
const handleDeleteKnowledge = (id: string) => {
|
||||
const list = knowledgeList.filter(item => item.id !== id)
|
||||
setKnowledgeList([...list])
|
||||
@@ -68,9 +89,11 @@ const Knowledge: FC<{value?: KnowledgeConfig; onChange?: (config: KnowledgeConfi
|
||||
knowledge_bases: [...list],
|
||||
})
|
||||
}
|
||||
/** Open knowledge base configuration modal */
|
||||
const handleEditKnowledge = (item: KnowledgeBase) => {
|
||||
knowledgeConfigModalRef.current?.handleOpen(item)
|
||||
}
|
||||
/** Update knowledge configuration */
|
||||
const refresh = (values: KnowledgeBase[] | KnowledgeConfigForm | RerankerConfig, type: 'knowledge' | 'knowledgeConfig' | 'rerankerConfig') => {
|
||||
if (type === 'knowledge') {
|
||||
let list = [...knowledgeList]
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:25:37
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 16:25:37
|
||||
*/
|
||||
/**
|
||||
* Knowledge Configuration Modal
|
||||
* Configures retrieval settings for individual knowledge bases
|
||||
* Supports different retrieval modes: participle, semantic, and hybrid
|
||||
*/
|
||||
|
||||
import { forwardRef, useEffect, useImperativeHandle, useState } from 'react';
|
||||
import { Form, Select, InputNumber } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -9,11 +21,22 @@ import { formatDateTime } from '@/utils/format';
|
||||
|
||||
const FormItem = Form.Item;
|
||||
|
||||
/**
|
||||
* Component props
|
||||
*/
|
||||
interface KnowledgeConfigModalProps {
|
||||
/** Callback to update knowledge configuration */
|
||||
refresh: (values: KnowledgeConfigForm, type: 'knowledgeConfig') => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Available retrieval types
|
||||
*/
|
||||
const retrieveTypes: RetrieveType[] = ['participle', 'semantic', 'hybrid']
|
||||
|
||||
/**
|
||||
* Modal for configuring knowledge base retrieval settings
|
||||
*/
|
||||
const KnowledgeConfigModal = forwardRef<KnowledgeConfigModalRef, KnowledgeConfigModalProps>(({
|
||||
refresh,
|
||||
}, ref) => {
|
||||
@@ -24,13 +47,14 @@ const KnowledgeConfigModal = forwardRef<KnowledgeConfigModalRef, KnowledgeConfig
|
||||
|
||||
const values = Form.useWatch<KnowledgeConfigForm>([], form);
|
||||
|
||||
// 封装取消方法,添加关闭弹窗逻辑
|
||||
/** Close modal and reset form */
|
||||
const handleClose = () => {
|
||||
setVisible(false);
|
||||
form.resetFields();
|
||||
setData(null)
|
||||
};
|
||||
|
||||
/** Open modal with knowledge base data */
|
||||
const handleOpen = (data: KnowledgeBase) => {
|
||||
form.setFieldsValue({
|
||||
retrieve_type: data?.config?.retrieve_type || retrieveTypes[0],
|
||||
@@ -44,7 +68,7 @@ const KnowledgeConfigModal = forwardRef<KnowledgeConfigModalRef, KnowledgeConfig
|
||||
setData({...data})
|
||||
setVisible(true);
|
||||
};
|
||||
// 封装保存方法,添加提交逻辑
|
||||
/** Save knowledge configuration */
|
||||
const handleSave = () => {
|
||||
form
|
||||
.validateFields()
|
||||
@@ -57,7 +81,7 @@ const KnowledgeConfigModal = forwardRef<KnowledgeConfigModalRef, KnowledgeConfig
|
||||
});
|
||||
}
|
||||
|
||||
// 暴露给父组件的方法
|
||||
/** Expose methods to parent component */
|
||||
useImperativeHandle(ref, () => ({
|
||||
handleOpen,
|
||||
handleClose
|
||||
@@ -94,7 +118,7 @@ const KnowledgeConfigModal = forwardRef<KnowledgeConfigModalRef, KnowledgeConfig
|
||||
</div>
|
||||
)}
|
||||
<FormItem name="kb_id" hidden />
|
||||
{/* 检索模式 */}
|
||||
{/* Retrieval mode */}
|
||||
<FormItem
|
||||
name="retrieve_type"
|
||||
label={t('application.retrieve_type')}
|
||||
@@ -124,7 +148,7 @@ const KnowledgeConfigModal = forwardRef<KnowledgeConfigModalRef, KnowledgeConfig
|
||||
onChange={(value) => form.setFieldValue('top_k', value)}
|
||||
/>
|
||||
</FormItem>
|
||||
{/* 语义相似度阈值 similarity_threshold */}
|
||||
{/* Semantic similarity threshold */}
|
||||
{values?.retrieve_type === 'semantic' && (
|
||||
<FormItem
|
||||
name="similarity_threshold"
|
||||
@@ -139,7 +163,7 @@ const KnowledgeConfigModal = forwardRef<KnowledgeConfigModalRef, KnowledgeConfig
|
||||
/>
|
||||
</FormItem>
|
||||
)}
|
||||
{/* 分词匹配度阈值 vector_similarity_weight */}
|
||||
{/* Word segmentation matching threshold */}
|
||||
{values?.retrieve_type === 'participle' && (
|
||||
<FormItem
|
||||
name="vector_similarity_weight"
|
||||
@@ -154,7 +178,7 @@ const KnowledgeConfigModal = forwardRef<KnowledgeConfigModalRef, KnowledgeConfig
|
||||
/>
|
||||
</FormItem>
|
||||
)}
|
||||
{/* 混合检索权重 */}
|
||||
{/* Hybrid retrieval weight */}
|
||||
{values?.retrieve_type === 'hybrid' && (
|
||||
<>
|
||||
<FormItem
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:25:42
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 16:25:42
|
||||
*/
|
||||
/**
|
||||
* Knowledge Global Configuration Modal
|
||||
* Configures global reranker settings for all knowledge bases
|
||||
*/
|
||||
|
||||
import { forwardRef, useImperativeHandle, useState, useEffect } from 'react';
|
||||
import { Form, InputNumber, Switch } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -9,11 +20,19 @@ import { getModelListUrl } from '@/api/models'
|
||||
|
||||
const FormItem = Form.Item;
|
||||
|
||||
/**
|
||||
* Component props
|
||||
*/
|
||||
interface KnowledgeGlobalConfigModalProps {
|
||||
/** Current reranker configuration */
|
||||
data: RerankerConfig;
|
||||
/** Callback to update reranker configuration */
|
||||
refresh: (values: RerankerConfig, type: 'rerankerConfig') => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modal for configuring global reranker settings
|
||||
*/
|
||||
const KnowledgeGlobalConfigModal = forwardRef<KnowledgeGlobalConfigModalRef, KnowledgeGlobalConfigModalProps>(({
|
||||
refresh,
|
||||
data,
|
||||
@@ -23,17 +42,18 @@ const KnowledgeGlobalConfigModal = forwardRef<KnowledgeGlobalConfigModalRef, Kno
|
||||
const [form] = Form.useForm<RerankerConfig>();
|
||||
const values = Form.useWatch<RerankerConfig>([], form);
|
||||
|
||||
// 封装取消方法,添加关闭弹窗逻辑
|
||||
/** Close modal and reset form */
|
||||
const handleClose = () => {
|
||||
setVisible(false);
|
||||
form.resetFields();
|
||||
};
|
||||
|
||||
/** Open modal with current configuration */
|
||||
const handleOpen = () => {
|
||||
form.setFieldsValue({ ...data, rerank_model: !!data?.reranker_id })
|
||||
setVisible(true);
|
||||
};
|
||||
// 封装保存方法,添加提交逻辑
|
||||
/** Save reranker configuration */
|
||||
const handleSave = () => {
|
||||
form
|
||||
.validateFields()
|
||||
@@ -54,7 +74,7 @@ const KnowledgeGlobalConfigModal = forwardRef<KnowledgeGlobalConfigModalRef, Kno
|
||||
}
|
||||
}, [values?.rerank_model])
|
||||
|
||||
// 暴露给父组件的方法
|
||||
/** Expose methods to parent component */
|
||||
useImperativeHandle(ref, () => ({
|
||||
handleOpen,
|
||||
}));
|
||||
@@ -73,7 +93,7 @@ const KnowledgeGlobalConfigModal = forwardRef<KnowledgeGlobalConfigModalRef, Kno
|
||||
>
|
||||
<div className="rb:text-[#5B6167] rb:mb-6">{t('application.globalConfigDesc')}</div>
|
||||
|
||||
{/* 结果重排 */}
|
||||
{/* Result reranking */}
|
||||
<div className="rb:flex rb:items-center rb:justify-between rb:my-6">
|
||||
<div className="rb:text-[14px] rb:font-medium rb:leading-5">
|
||||
{t('application.rerankModel')}
|
||||
|
||||
@@ -1,7 +1,19 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:25:49
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 16:25:49
|
||||
*/
|
||||
/**
|
||||
* Knowledge List Modal
|
||||
* Displays and allows selection of knowledge bases to associate with the application
|
||||
*/
|
||||
|
||||
import { forwardRef, useEffect, useImperativeHandle, useState } from 'react';
|
||||
import { Space, List } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import clsx from 'clsx'
|
||||
|
||||
import type { KnowledgeModalRef, KnowledgeBase } from './types'
|
||||
import type { KnowledgeBaseListItem } from '@/views/KnowledgeBase/types'
|
||||
import RbModal from '@/components/RbModal'
|
||||
@@ -9,11 +21,19 @@ import { getKnowledgeBaseList } from '@/api/knowledgeBase'
|
||||
import SearchInput from '@/components/SearchInput'
|
||||
import Empty from '@/components/Empty'
|
||||
import { formatDateTime } from '@/utils/format';
|
||||
/**
|
||||
* Component props
|
||||
*/
|
||||
interface KnowledgeModalProps {
|
||||
/** Callback to add selected knowledge bases */
|
||||
refresh: (rows: KnowledgeBase[], type: 'knowledge') => void;
|
||||
/** Currently selected knowledge bases */
|
||||
selectedList: KnowledgeBase[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Modal for selecting knowledge bases
|
||||
*/
|
||||
const KnowledgeListModal = forwardRef<KnowledgeModalRef, KnowledgeModalProps>(({
|
||||
refresh,
|
||||
selectedList
|
||||
@@ -26,7 +46,7 @@ const KnowledgeListModal = forwardRef<KnowledgeModalRef, KnowledgeModalProps>(({
|
||||
const [selectedIds, setSelectedIds] = useState<string[]>([])
|
||||
const [selectedRows, setSelectedRows] = useState<KnowledgeBase[]>([])
|
||||
|
||||
// 封装取消方法,添加关闭弹窗逻辑
|
||||
/** Close modal and reset state */
|
||||
const handleClose = () => {
|
||||
setVisible(false);
|
||||
setQuery({})
|
||||
@@ -34,6 +54,7 @@ const KnowledgeListModal = forwardRef<KnowledgeModalRef, KnowledgeModalProps>(({
|
||||
setSelectedRows([])
|
||||
};
|
||||
|
||||
/** Open modal */
|
||||
const handleOpen = () => {
|
||||
setVisible(true);
|
||||
setQuery({})
|
||||
@@ -46,6 +67,7 @@ const KnowledgeListModal = forwardRef<KnowledgeModalRef, KnowledgeModalProps>(({
|
||||
getList()
|
||||
}
|
||||
}, [query.keywords, visible])
|
||||
/** Fetch knowledge base list */
|
||||
const getList = () => {
|
||||
getKnowledgeBaseList(undefined, {
|
||||
...query,
|
||||
@@ -58,7 +80,7 @@ const KnowledgeListModal = forwardRef<KnowledgeModalRef, KnowledgeModalProps>(({
|
||||
setList(response.items || [])
|
||||
})
|
||||
}
|
||||
// 封装保存方法,添加提交逻辑
|
||||
/** Save selected knowledge bases */
|
||||
const handleSave = () => {
|
||||
refresh(selectedRows.map(item => ({
|
||||
...item,
|
||||
@@ -72,16 +94,18 @@ const KnowledgeListModal = forwardRef<KnowledgeModalRef, KnowledgeModalProps>(({
|
||||
setVisible(false);
|
||||
}
|
||||
|
||||
// 暴露给父组件的方法
|
||||
/** Expose methods to parent component */
|
||||
useImperativeHandle(ref, () => ({
|
||||
handleOpen,
|
||||
handleClose
|
||||
}));
|
||||
/** Search knowledge bases */
|
||||
const handleSearch = (value?: string) => {
|
||||
setQuery({keywords: value})
|
||||
setSelectedIds([])
|
||||
setSelectedRows([])
|
||||
}
|
||||
/** Toggle knowledge base selection */
|
||||
const handleSelect = (item: KnowledgeBase) => {
|
||||
const index = selectedIds.indexOf(item.id)
|
||||
if (index === -1) {
|
||||
|
||||
@@ -1,30 +1,87 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:25:53
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 16:25:53
|
||||
*/
|
||||
/**
|
||||
* Type definitions for knowledge base configuration in application settings
|
||||
*/
|
||||
|
||||
import type { KnowledgeBaseListItem } from '@/views/KnowledgeBase/types'
|
||||
|
||||
/**
|
||||
* Reranker configuration for knowledge retrieval
|
||||
*/
|
||||
export interface RerankerConfig {
|
||||
/** Whether to enable rerank model */
|
||||
rerank_model?: boolean | undefined;
|
||||
/** Reranker model ID */
|
||||
reranker_id?: string | undefined;
|
||||
/** Top K results for reranking */
|
||||
reranker_top_k?: number | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Knowledge retrieval type
|
||||
* - participle: Word segmentation based retrieval
|
||||
* - semantic: Semantic similarity based retrieval
|
||||
* - hybrid: Combination of both methods
|
||||
*/
|
||||
export type RetrieveType = 'participle' | 'semantic' | 'hybrid'
|
||||
|
||||
/**
|
||||
* Knowledge base configuration form data
|
||||
*/
|
||||
export interface KnowledgeConfigForm {
|
||||
/** Knowledge base ID */
|
||||
kb_id?: string;
|
||||
/** Similarity threshold for retrieval (0-1) */
|
||||
similarity_threshold?: number;
|
||||
/** Weight for vector similarity in hybrid mode (0-1) */
|
||||
vector_similarity_weight?: number;
|
||||
/** Number of top results to retrieve */
|
||||
top_k?: number;
|
||||
/** Retrieval strategy type */
|
||||
retrieve_type?: RetrieveType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Knowledge base with configuration
|
||||
*/
|
||||
export interface KnowledgeBase extends KnowledgeBaseListItem, KnowledgeConfigForm {
|
||||
/** Additional configuration object */
|
||||
config?: KnowledgeConfigForm
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete knowledge configuration including reranker settings
|
||||
*/
|
||||
export interface KnowledgeConfig extends RerankerConfig {
|
||||
/** List of configured knowledge bases */
|
||||
knowledge_bases: KnowledgeBase[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Modal ref for individual knowledge base configuration
|
||||
*/
|
||||
export interface KnowledgeConfigModalRef {
|
||||
/** Open modal with knowledge base data */
|
||||
handleOpen: (data: KnowledgeBase) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modal ref for global knowledge configuration
|
||||
*/
|
||||
export interface KnowledgeGlobalConfigModalRef {
|
||||
/** Open global configuration modal */
|
||||
handleOpen: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modal ref for knowledge base selection
|
||||
*/
|
||||
export interface KnowledgeModalRef {
|
||||
/** Open modal with optional existing configuration */
|
||||
handleOpen: (config?: KnowledgeConfig[]) => void;
|
||||
}
|
||||
@@ -1,18 +1,38 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:28:03
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 16:28:03
|
||||
*/
|
||||
/**
|
||||
* Line chart card component for displaying statistics
|
||||
* Uses ECharts to render time-series data with gradient area fill
|
||||
*/
|
||||
|
||||
import { type FC, useEffect, useRef } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import ReactEcharts from 'echarts-for-react';
|
||||
import * as echarts from 'echarts';
|
||||
import Empty from '@/components/Empty'
|
||||
|
||||
import Empty from '@/components/Empty'
|
||||
import Card from './Card'
|
||||
import type { StatisticsItem } from '../types'
|
||||
|
||||
/**
|
||||
* Component props
|
||||
*/
|
||||
interface LineCardProps {
|
||||
/** Chart data points */
|
||||
chartData: StatisticsItem[];
|
||||
/** Statistics type key */
|
||||
type: string;
|
||||
/** Total count to display */
|
||||
total: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* ECharts series configuration
|
||||
*/
|
||||
const SeriesConfig = {
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
@@ -30,6 +50,9 @@ const SeriesConfig = {
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Color mapping for different statistic types
|
||||
*/
|
||||
const ColorObj: Record<string, string> = {
|
||||
daily_conversations: '#FFB048',
|
||||
daily_new_users: '#4DA8FF',
|
||||
@@ -37,6 +60,10 @@ const ColorObj: Record<string, string> = {
|
||||
daily_tokens: '#AD88FF'
|
||||
}
|
||||
|
||||
/**
|
||||
* Line chart card component
|
||||
* Displays time-series statistics with gradient area chart
|
||||
*/
|
||||
const LineCard: FC<LineCardProps> = ({ chartData, type, total }) => {
|
||||
const { t } = useTranslation()
|
||||
const chartRef = useRef<ReactEcharts>(null);
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:28:07
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 16:28:07
|
||||
*/
|
||||
/**
|
||||
* Model Configuration Modal
|
||||
* Allows configuring model parameters like temperature, max_tokens, top_p, etc.
|
||||
* Supports different sources: model, chat, and multi_agent
|
||||
*/
|
||||
|
||||
import { forwardRef, useImperativeHandle, useState, useEffect } from 'react';
|
||||
import { Form, Select } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -9,12 +21,24 @@ import RbSlider from '@/components/RbSlider'
|
||||
|
||||
const FormItem = Form.Item;
|
||||
|
||||
/**
|
||||
* Component props
|
||||
*/
|
||||
interface ModelConfigModalProps {
|
||||
/** List of available models */
|
||||
modelList?: ModelListItem[];
|
||||
/** Callback to update model configuration */
|
||||
refresh: (values: ModelConfig, type: Source) => void;
|
||||
/** Application configuration data */
|
||||
data: Config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modal for configuring model parameters
|
||||
*/
|
||||
/**
|
||||
* Model parameter configuration fields
|
||||
*/
|
||||
const configFields = [
|
||||
{ key: 'temperature', max: 2, min: 0, step: 0.1, defaultValue: 0.7 },
|
||||
{ key: 'max_tokens', max: 32000, min: 256, step: 1, defaultValue: 2000 },
|
||||
@@ -36,12 +60,13 @@ const ModelConfigModal = forwardRef<ModelConfigModalRef, ModelConfigModalProps>(
|
||||
|
||||
const values = Form.useWatch([], form);
|
||||
|
||||
// 封装取消方法,添加关闭弹窗逻辑
|
||||
/** Close modal and reset form */
|
||||
const handleClose = () => {
|
||||
setVisible(false);
|
||||
form.resetFields();
|
||||
};
|
||||
|
||||
/** Open modal with configuration source */
|
||||
const handleOpen = (source: Source, model?: any) => {
|
||||
setSource(source)
|
||||
if (source === 'model') {
|
||||
@@ -64,7 +89,7 @@ const ModelConfigModal = forwardRef<ModelConfigModalRef, ModelConfigModalProps>(
|
||||
}
|
||||
setVisible(true);
|
||||
};
|
||||
// 封装保存方法,添加提交逻辑
|
||||
/** Save model configuration */
|
||||
const handleSave = () => {
|
||||
form
|
||||
.validateFields()
|
||||
@@ -76,13 +101,14 @@ const ModelConfigModal = forwardRef<ModelConfigModalRef, ModelConfigModalProps>(
|
||||
console.log('err', err)
|
||||
});
|
||||
}
|
||||
/** Handle model selection change */
|
||||
const handleChange = (_value: string, option: ModelListItem | ModelListItem[] | undefined) => {
|
||||
if (source === 'chat') {
|
||||
form.setFieldValue('label', (option as ModelListItem).name)
|
||||
}
|
||||
}
|
||||
|
||||
// 暴露给父组件的方法
|
||||
/** Expose methods to parent component */
|
||||
useImperativeHandle(ref, () => ({
|
||||
handleOpen,
|
||||
handleClose
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:28:11
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 16:28:11
|
||||
*/
|
||||
/**
|
||||
* Release Modal
|
||||
* Allows publishing a new version of the application
|
||||
*/
|
||||
|
||||
import { forwardRef, useImperativeHandle, useState } from 'react';
|
||||
import { Form, Input } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -9,11 +20,19 @@ import type { Application } from '@/views/ApplicationManagement/types'
|
||||
|
||||
const FormItem = Form.Item;
|
||||
|
||||
/**
|
||||
* Component props
|
||||
*/
|
||||
interface ReleaseModalProps {
|
||||
/** Callback to refresh release list */
|
||||
refreshTable: () => void;
|
||||
/** Application data */
|
||||
data: Application
|
||||
}
|
||||
|
||||
/**
|
||||
* Modal for publishing new application versions
|
||||
*/
|
||||
const ReleaseModal = forwardRef<ReleaseModalRef, ReleaseModalProps>(({
|
||||
refreshTable,
|
||||
data
|
||||
@@ -23,17 +42,18 @@ const ReleaseModal = forwardRef<ReleaseModalRef, ReleaseModalProps>(({
|
||||
const [form] = Form.useForm();
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
// 封装取消方法,添加关闭弹窗逻辑
|
||||
/** Close modal and reset form */
|
||||
const handleClose = () => {
|
||||
setVisible(false);
|
||||
form.resetFields();
|
||||
setLoading(false)
|
||||
};
|
||||
|
||||
/** Open modal */
|
||||
const handleOpen = () => {
|
||||
setVisible(true);
|
||||
};
|
||||
// 封装保存方法,添加提交逻辑
|
||||
/** Publish new release */
|
||||
const handleSave = () => {
|
||||
form.validateFields().then(() => {
|
||||
setLoading(true)
|
||||
@@ -49,7 +69,7 @@ const ReleaseModal = forwardRef<ReleaseModalRef, ReleaseModalProps>(({
|
||||
})
|
||||
}
|
||||
|
||||
// 暴露给父组件的方法
|
||||
/** Expose methods to parent component */
|
||||
useImperativeHandle(ref, () => ({
|
||||
handleOpen,
|
||||
handleClose
|
||||
@@ -69,7 +89,7 @@ const ReleaseModal = forwardRef<ReleaseModalRef, ReleaseModalProps>(({
|
||||
form={form}
|
||||
layout="vertical"
|
||||
>
|
||||
{/* 版本名 */}
|
||||
{/* Version name */}
|
||||
<FormItem
|
||||
name="version_name"
|
||||
label={t('application.versionName')}
|
||||
@@ -80,7 +100,7 @@ const ReleaseModal = forwardRef<ReleaseModalRef, ReleaseModalProps>(({
|
||||
>
|
||||
<Input placeholder={t('common.enter')} />
|
||||
</FormItem>
|
||||
{/* 版本描述 */}
|
||||
{/* Version description */}
|
||||
<FormItem
|
||||
name="release_notes"
|
||||
label={t('application.versionDescription')}
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:28:46
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 16:28:46
|
||||
*/
|
||||
/**
|
||||
* Release Share Modal
|
||||
* Generates and displays a shareable link for a specific application version
|
||||
*/
|
||||
|
||||
import { forwardRef, useImperativeHandle, useState } from 'react';
|
||||
import { Button, App } from 'antd';
|
||||
import { ExclamationCircleFilled } from '@ant-design/icons';
|
||||
@@ -9,10 +20,17 @@ import RbModal from '@/components/RbModal'
|
||||
import { shareRelease } from '@/api/application'
|
||||
import RbAlert from '@/components/RbAlert'
|
||||
|
||||
/**
|
||||
* Component props
|
||||
*/
|
||||
interface ReleaseShareModalProps {
|
||||
/** Version to share */
|
||||
version: Release | null
|
||||
}
|
||||
|
||||
/**
|
||||
* Modal for sharing application versions
|
||||
*/
|
||||
const ReleaseShareModal = forwardRef<ReleaseShareModalRef, ReleaseShareModalProps>(({
|
||||
version
|
||||
}, ref) => {
|
||||
@@ -22,12 +40,13 @@ const ReleaseShareModal = forwardRef<ReleaseShareModalRef, ReleaseShareModalProp
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [shareLink, setShareLink] = useState<string | null>(null)
|
||||
|
||||
// 封装取消方法,添加关闭弹窗逻辑
|
||||
/** Close modal */
|
||||
const handleClose = () => {
|
||||
setVisible(false);
|
||||
setLoading(false)
|
||||
};
|
||||
|
||||
/** Open modal and generate share link */
|
||||
const handleOpen = () => {
|
||||
if (!version) {
|
||||
return
|
||||
@@ -45,13 +64,14 @@ const ReleaseShareModal = forwardRef<ReleaseShareModalRef, ReleaseShareModalProp
|
||||
setLoading(false)
|
||||
})
|
||||
};
|
||||
/** Copy share link to clipboard */
|
||||
const handleCopy = () => {
|
||||
if (!shareLink) return
|
||||
copy(shareLink)
|
||||
message.success(t('common.copySuccess'))
|
||||
}
|
||||
|
||||
// 暴露给父组件的方法
|
||||
/** Expose methods to parent component */
|
||||
useImperativeHandle(ref, () => ({
|
||||
handleOpen,
|
||||
handleClose
|
||||
@@ -65,9 +85,9 @@ const ReleaseShareModal = forwardRef<ReleaseShareModalRef, ReleaseShareModalProp
|
||||
footer={false}
|
||||
>
|
||||
<>
|
||||
<div className="rb:leading-[20px] rb:mb-[8px]">{t('application.shareLink')}</div>
|
||||
<div className="rb:mb-[12px] rb:flex rb:items-center rb:gap-[10px] rb:justify-between">
|
||||
<div className="rb:overflow-hidden rb:whitespace-nowrap rb:text-ellipsis rb:cursor-pointer rb:h-[32px] rb:p-[6px_10px] rb:bg-[#FFFFFF] rb:border rb:border-[#EBEBEB] rb:rounded-[6px] rb:leading-[20px]">{shareLink}</div>
|
||||
<div className="rb:leading-5 rb:mb-2">{t('application.shareLink')}</div>
|
||||
<div className="rb:mb-3 rb:flex rb:items-center rb:gap-2.5 rb:justify-between">
|
||||
<div className="rb:overflow-hidden rb:whitespace-nowrap rb:text-ellipsis rb:cursor-pointer rb:h-8 rb:p-[6px_10px] rb:bg-[#FFFFFF] rb:border rb:border-[#EBEBEB] rb:rounded-md rb:leading-5">{shareLink}</div>
|
||||
|
||||
<Button type="primary" loading={loading} disabled={!shareLink} onClick={handleCopy}>
|
||||
{t('common.copy')}
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:28:51
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 16:28:51
|
||||
*/
|
||||
/**
|
||||
* Sub-Agent Modal
|
||||
* Allows adding or editing sub-agents in multi-agent cluster configuration
|
||||
*/
|
||||
|
||||
import { forwardRef, useImperativeHandle, useState, type Key } from 'react';
|
||||
import { Form, Select, Input } from 'antd';
|
||||
import type { DefaultOptionType } from 'antd/es/select'
|
||||
@@ -10,10 +21,17 @@ import { getApplicationListUrl } from '@/api/application';
|
||||
|
||||
const FormItem = Form.Item;
|
||||
|
||||
/**
|
||||
* Component props
|
||||
*/
|
||||
interface SubAgentModalProps {
|
||||
/** Callback to update sub-agent */
|
||||
refresh: (agent: SubAgentItem) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modal for managing sub-agents
|
||||
*/
|
||||
const SubAgentModal = forwardRef<SubAgentModalRef, SubAgentModalProps>(({
|
||||
refresh,
|
||||
}, ref) => {
|
||||
@@ -24,19 +42,20 @@ const SubAgentModal = forwardRef<SubAgentModalRef, SubAgentModalProps>(({
|
||||
const [editVo, setEditVo] = useState<SubAgentItem>()
|
||||
const values = Form.useWatch([], form)
|
||||
|
||||
// 封装取消方法,添加关闭弹窗逻辑
|
||||
/** Close modal and reset form */
|
||||
const handleClose = () => {
|
||||
setVisible(false);
|
||||
form.resetFields();
|
||||
setLoading(false)
|
||||
};
|
||||
|
||||
/** Open modal with optional agent data */
|
||||
const handleOpen = (agent?: SubAgentItem) => {
|
||||
setVisible(true);
|
||||
form.setFieldsValue(agent)
|
||||
setEditVo(agent)
|
||||
};
|
||||
// 封装保存方法,添加提交逻辑
|
||||
/** Save sub-agent configuration */
|
||||
const handleSave = () => {
|
||||
form.validateFields().then(() => {
|
||||
setLoading(false)
|
||||
@@ -47,6 +66,7 @@ const SubAgentModal = forwardRef<SubAgentModalRef, SubAgentModalProps>(({
|
||||
handleClose()
|
||||
})
|
||||
}
|
||||
/** Handle agent selection change */
|
||||
const handleChange = (value: Key, option?: DefaultOptionType | DefaultOptionType[] | undefined) => {
|
||||
console.log(value, option)
|
||||
if (option && !Array.isArray(option)) {
|
||||
@@ -54,7 +74,7 @@ const SubAgentModal = forwardRef<SubAgentModalRef, SubAgentModalProps>(({
|
||||
}
|
||||
}
|
||||
|
||||
// 暴露给父组件的方法
|
||||
/** Expose methods to parent component */
|
||||
useImperativeHandle(ref, () => ({
|
||||
handleOpen,
|
||||
handleClose
|
||||
@@ -73,7 +93,7 @@ const SubAgentModal = forwardRef<SubAgentModalRef, SubAgentModalProps>(({
|
||||
form={form}
|
||||
layout="vertical"
|
||||
>
|
||||
{/* Agent名称 */}
|
||||
{/* Agent name */}
|
||||
<FormItem
|
||||
name="agent_id"
|
||||
label={t('application.agentName')}
|
||||
@@ -93,14 +113,14 @@ const SubAgentModal = forwardRef<SubAgentModalRef, SubAgentModalProps>(({
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem name="name" hidden />
|
||||
{/* 描述 */}
|
||||
{/* Description */}
|
||||
<FormItem
|
||||
name="role"
|
||||
label={t('application.description')}
|
||||
>
|
||||
<Input.TextArea placeholder={t('common.pleaseEnter')} />
|
||||
</FormItem>
|
||||
{/* 关键词 */}
|
||||
{/* Keywords */}
|
||||
<FormItem
|
||||
name="capabilities"
|
||||
label={t('application.capabilities')}
|
||||
|
||||
@@ -1,11 +1,31 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:29:17
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 16:29:17
|
||||
*/
|
||||
/**
|
||||
* Tag Component
|
||||
* Displays colored status tags with different color schemes
|
||||
*/
|
||||
|
||||
import { type FC, type ReactNode } from 'react'
|
||||
|
||||
/**
|
||||
* Tag component props
|
||||
*/
|
||||
export interface TagProps {
|
||||
/** Tag color scheme */
|
||||
color?: 'processing' | 'warning' | 'default' | 'success';
|
||||
/** Tag content */
|
||||
children: ReactNode;
|
||||
/** Additional CSS classes */
|
||||
className?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Color scheme mapping
|
||||
*/
|
||||
const colors = {
|
||||
processing: 'rb:text-[#155EEF] rb:border-[rgba(21,94,239,0.25)] rb:bg-[rgba(21,94,239,0.06)]',
|
||||
warning: 'rb:text-[#FF5D34] rb:border-[rgba(255,93,52,0.08)] rb:bg-[rgba(255,93,52,0.08)]',
|
||||
@@ -13,9 +33,12 @@ const colors = {
|
||||
success: 'rb:text-[#369F21] rb:border-[rgba(54,159,33,0.30)] rb:bg-[rgba(54,159,33,0.08)]',
|
||||
}
|
||||
|
||||
/**
|
||||
* Tag component for displaying status labels
|
||||
*/
|
||||
const Tag: FC<TagProps> = ({ color = 'processing', children, className }) => {
|
||||
return (
|
||||
<span className={`rb:inline-block rb:px-[8px] rb:py-[2px] rb:rounded-[11px] rb:text-[12px] rb:font-regular rb:leading-[16px] rb:border-[1px] ${colors[color]} ${className || ''}`}>
|
||||
<span className={`rb:inline-block rb:px-2 rb:py-0.5 rb:rounded-[11px] rb:text-[12px] rb:font-regular rb:leading-4 rb:border ${colors[color]} ${className || ''}`}>
|
||||
{children}
|
||||
</span>
|
||||
)
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:26:03
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 16:26:03
|
||||
*/
|
||||
/**
|
||||
* Tool List Component
|
||||
* Manages tool configurations for the application
|
||||
* Allows adding, removing, and enabling/disabling tools
|
||||
*/
|
||||
|
||||
import { type FC, useRef, useState, useEffect } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Space, Button, List, Switch } from 'antd'
|
||||
|
||||
import Card from '../Card'
|
||||
import type {
|
||||
ToolModalRef,
|
||||
@@ -10,6 +23,11 @@ import Empty from '@/components/Empty'
|
||||
import ToolModal from './ToolModal'
|
||||
import { getToolMethods, getToolDetail } from '@/api/tools'
|
||||
|
||||
/**
|
||||
* Tool list management component
|
||||
* @param value - Current tool configurations
|
||||
* @param onChange - Callback when tools change
|
||||
*/
|
||||
const ToolList: FC<{ value?: ToolOption[]; onChange?: (config: ToolOption[]) => void}> = ({value, onChange}) => {
|
||||
const { t } = useTranslation()
|
||||
const toolModalRef = useRef<ToolModalRef>(null)
|
||||
@@ -79,19 +97,23 @@ const ToolList: FC<{ value?: ToolOption[]; onChange?: (config: ToolOption[]) =>
|
||||
}
|
||||
}, [value])
|
||||
|
||||
/** Open tool selection modal */
|
||||
const handleAddTool = () => {
|
||||
toolModalRef.current?.handleOpen()
|
||||
}
|
||||
/** Add new tool to list */
|
||||
const updateTools = (tool: ToolOption) => {
|
||||
const list = [...toolList, tool]
|
||||
setToolList(list)
|
||||
onChange && onChange(list)
|
||||
}
|
||||
/** Remove tool from list */
|
||||
const handleDeleteTool = (index: number) => {
|
||||
const list = toolList.filter((_item, idx) => idx !== index)
|
||||
setToolList([...list])
|
||||
onChange && onChange(list)
|
||||
}
|
||||
/** Toggle tool enabled state */
|
||||
const handleChangeEnabled = (index: number) => {
|
||||
const list = toolList.map((item, idx) => {
|
||||
if (idx === index) {
|
||||
|
||||
@@ -1,18 +1,37 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:26:06
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 16:26:06
|
||||
*/
|
||||
/**
|
||||
* Tool Selection Modal
|
||||
* Provides cascading selection of tools by type, tool, and method
|
||||
* Supports MCP, builtin, and custom tool types
|
||||
*/
|
||||
|
||||
import { forwardRef, useImperativeHandle, useState } from 'react';
|
||||
import { Form, Cascader, type CascaderProps } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import type { ToolModalRef, ToolOption } from '../types'
|
||||
import type { ToolModalRef, ToolOption } from './types'
|
||||
import RbModal from '@/components/RbModal'
|
||||
import { getToolMethods, getTools } from '@/api/tools'
|
||||
import type { ToolType, ToolItem } from '@/views/ToolManagement/types'
|
||||
|
||||
const FormItem = Form.Item;
|
||||
|
||||
/**
|
||||
* Component props
|
||||
*/
|
||||
interface ToolModalProps {
|
||||
/** Callback to add selected tool */
|
||||
refresh: (tool: ToolOption) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modal for selecting tools
|
||||
*/
|
||||
const ToolModal = forwardRef<ToolModalRef, ToolModalProps>(({
|
||||
refresh,
|
||||
}, ref) => {
|
||||
@@ -27,7 +46,7 @@ const ToolModal = forwardRef<ToolModalRef, ToolModalProps>(({
|
||||
])
|
||||
const [selectdTools, setSelectedTools] = useState<ToolOption[]>([])
|
||||
|
||||
// 封装取消方法,添加关闭弹窗逻辑
|
||||
/** Close modal and reset state */
|
||||
const handleClose = () => {
|
||||
setVisible(false);
|
||||
form.resetFields();
|
||||
@@ -35,12 +54,13 @@ const ToolModal = forwardRef<ToolModalRef, ToolModalProps>(({
|
||||
setSelectedTools([])
|
||||
};
|
||||
|
||||
/** Open modal */
|
||||
const handleOpen = () => {
|
||||
setVisible(true);
|
||||
form.resetFields();
|
||||
setSelectedTools([])
|
||||
};
|
||||
// 封装保存方法,添加提交逻辑
|
||||
/** Save selected tool */
|
||||
const handleSave = () => {
|
||||
form.validateFields().then(() => {
|
||||
setLoading(false)
|
||||
@@ -64,6 +84,7 @@ const ToolModal = forwardRef<ToolModalRef, ToolModalProps>(({
|
||||
handleClose()
|
||||
})
|
||||
}
|
||||
/** Load cascader options dynamically */
|
||||
const loadData = (selectedOptions: ToolOption[]) => {
|
||||
const targetOption = selectedOptions[selectedOptions.length - 1];
|
||||
if (selectedOptions.length === 1) {
|
||||
@@ -98,12 +119,13 @@ const ToolModal = forwardRef<ToolModalRef, ToolModalProps>(({
|
||||
}
|
||||
};
|
||||
|
||||
/** Handle cascader selection change */
|
||||
const handleChange: CascaderProps<ToolOption>['onChange'] = (_value, selectedOptions) => {
|
||||
console.log('selectedOptions', selectedOptions)
|
||||
setSelectedTools(selectedOptions)
|
||||
}
|
||||
|
||||
// 暴露给父组件的方法
|
||||
/** Expose methods to parent component */
|
||||
useImperativeHandle(ref, () => ({
|
||||
handleOpen,
|
||||
handleClose
|
||||
|
||||
@@ -1,26 +1,67 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:26:10
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 16:26:10
|
||||
*/
|
||||
/**
|
||||
* Type definitions for tool configuration in application settings
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tool option for cascader selection
|
||||
*/
|
||||
export interface ToolOption {
|
||||
/** Option value */
|
||||
value?: string | number | null;
|
||||
/** Display label */
|
||||
label?: React.ReactNode;
|
||||
/** Tool description */
|
||||
description?: string;
|
||||
/** Child options for nested selection */
|
||||
children?: ToolOption[];
|
||||
/** Whether this is a leaf node */
|
||||
isLeaf?: boolean;
|
||||
/** Method ID for API operations */
|
||||
method_id?: string;
|
||||
/** Operation name */
|
||||
operation?: string;
|
||||
/** Method parameters */
|
||||
parameters?: Parameter[];
|
||||
/** Tool ID */
|
||||
tool_id?: string;
|
||||
/** Whether tool is enabled */
|
||||
enabled?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameter definition for tool methods
|
||||
*/
|
||||
export interface Parameter {
|
||||
/** Parameter name */
|
||||
name: string;
|
||||
/** Parameter data type */
|
||||
type: string;
|
||||
/** Parameter description */
|
||||
description: string;
|
||||
/** Whether parameter is required */
|
||||
required: boolean;
|
||||
/** Default value */
|
||||
default: any;
|
||||
/** Enum values if applicable */
|
||||
enum: null | string[];
|
||||
/** Minimum value for numeric types */
|
||||
minimum: number;
|
||||
/** Maximum value for numeric types */
|
||||
maximum: number;
|
||||
/** Regex pattern for validation */
|
||||
pattern: null | string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modal ref for tool selection
|
||||
*/
|
||||
export interface ToolModalRef {
|
||||
/** Open tool selection modal */
|
||||
handleOpen: () => void;
|
||||
}
|
||||
@@ -1,3 +1,14 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:26:18
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 16:26:18
|
||||
*/
|
||||
/**
|
||||
* API Extension Modal
|
||||
* Allows configuring external API endpoints for dynamic variable options
|
||||
*/
|
||||
|
||||
import { forwardRef, useImperativeHandle, useState } from 'react';
|
||||
import { Form, Input } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -7,10 +18,17 @@ import RbModal from '@/components/RbModal'
|
||||
|
||||
const FormItem = Form.Item;
|
||||
|
||||
/**
|
||||
* Component props
|
||||
*/
|
||||
interface ApiExtensionModalProps {
|
||||
/** Callback to refresh API extension list */
|
||||
refresh: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modal for adding API extensions
|
||||
*/
|
||||
const ApiExtensionModal = forwardRef<ApiExtensionModalRef, ApiExtensionModalProps>(({
|
||||
refresh
|
||||
}, ref) => {
|
||||
@@ -21,18 +39,19 @@ const ApiExtensionModal = forwardRef<ApiExtensionModalRef, ApiExtensionModalProp
|
||||
|
||||
const values = Form.useWatch([], form);
|
||||
|
||||
// 封装取消方法,添加关闭弹窗逻辑
|
||||
/** Close modal and reset form */
|
||||
const handleClose = () => {
|
||||
setVisible(false);
|
||||
form.resetFields();
|
||||
setLoading(false)
|
||||
};
|
||||
|
||||
/** Open modal */
|
||||
const handleOpen = () => {
|
||||
form.resetFields();
|
||||
setVisible(true);
|
||||
};
|
||||
// 封装保存方法,添加提交逻辑
|
||||
/** Save API extension configuration */
|
||||
const handleSave = () => {
|
||||
form
|
||||
.validateFields()
|
||||
@@ -46,7 +65,7 @@ const ApiExtensionModal = forwardRef<ApiExtensionModalRef, ApiExtensionModalProp
|
||||
});
|
||||
}
|
||||
|
||||
// 暴露给父组件的方法
|
||||
/** Expose methods to parent component */
|
||||
useImperativeHandle(ref, () => ({
|
||||
handleOpen,
|
||||
handleClose
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:26:27
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 16:26:27
|
||||
*/
|
||||
/**
|
||||
* Variable Edit Modal
|
||||
* Allows creating and editing application input variables
|
||||
* Supports multiple variable types: text, paragraph, number, dropdown, checkbox, API variable
|
||||
*/
|
||||
|
||||
import { forwardRef, useImperativeHandle, useState, useRef } from 'react';
|
||||
import { Form, Input, Select, InputNumber, Checkbox, Tag, Divider, Button } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -9,10 +21,17 @@ import ApiExtensionModal from './ApiExtensionModal'
|
||||
|
||||
const FormItem = Form.Item;
|
||||
|
||||
/**
|
||||
* Component props
|
||||
*/
|
||||
interface VariableEditModalProps {
|
||||
/** Callback to update variable */
|
||||
refreshTable: (values: Variable) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Supported variable types
|
||||
*/
|
||||
const types = [
|
||||
'text',
|
||||
'paragraph',
|
||||
@@ -21,6 +40,9 @@ const types = [
|
||||
// 'checkbox',
|
||||
// 'apiVariable'
|
||||
]
|
||||
/**
|
||||
* Variable type to data type mapping
|
||||
*/
|
||||
const variableType = {
|
||||
text: 'string',
|
||||
paragraph: 'string',
|
||||
@@ -30,6 +52,9 @@ const variableType = {
|
||||
apiVariable: 'string',
|
||||
}
|
||||
|
||||
/**
|
||||
* Modal for editing variables
|
||||
*/
|
||||
const VariableEditModal = forwardRef<VariableEditModalRef, VariableEditModalProps>(({
|
||||
refreshTable
|
||||
}, ref) => {
|
||||
@@ -42,7 +67,7 @@ const VariableEditModal = forwardRef<VariableEditModalRef, VariableEditModalProp
|
||||
|
||||
const values = Form.useWatch([], form);
|
||||
|
||||
// 封装取消方法,添加关闭弹窗逻辑
|
||||
/** Close modal and reset form */
|
||||
const handleClose = () => {
|
||||
setVisible(false);
|
||||
form.resetFields();
|
||||
@@ -50,6 +75,7 @@ const VariableEditModal = forwardRef<VariableEditModalRef, VariableEditModalProp
|
||||
setEditVo(null)
|
||||
};
|
||||
|
||||
/** Open modal with optional variable data */
|
||||
const handleOpen = (variable?: Variable) => {
|
||||
setVisible(true);
|
||||
if (variable) {
|
||||
@@ -59,7 +85,7 @@ const VariableEditModal = forwardRef<VariableEditModalRef, VariableEditModalProp
|
||||
form.resetFields();
|
||||
}
|
||||
};
|
||||
// 封装保存方法,添加提交逻辑
|
||||
/** Save variable configuration */
|
||||
const handleSave = () => {
|
||||
form.validateFields().then((values) => {
|
||||
refreshTable({
|
||||
@@ -70,12 +96,12 @@ const VariableEditModal = forwardRef<VariableEditModalRef, VariableEditModalProp
|
||||
})
|
||||
}
|
||||
|
||||
// 暴露给父组件的方法
|
||||
/** Expose methods to parent component */
|
||||
useImperativeHandle(ref, () => ({
|
||||
handleOpen,
|
||||
handleClose
|
||||
}));
|
||||
// 变量类型改变时,更新初始化其他字段值
|
||||
/** Handle variable type change */
|
||||
const handleChangeType = (value: Variable['type']) => {
|
||||
if (value) {
|
||||
form.setFieldsValue({
|
||||
@@ -90,7 +116,7 @@ const VariableEditModal = forwardRef<VariableEditModalRef, VariableEditModalProp
|
||||
})
|
||||
}
|
||||
}
|
||||
// 添加 API 扩展
|
||||
/** Add API extension */
|
||||
const addApiExtension = () => {
|
||||
apiExtensionModalRef.current?.handleOpen()
|
||||
}
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:26:32
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 16:26:32
|
||||
*/
|
||||
/**
|
||||
* Variable List Component
|
||||
* Manages application input variables configuration
|
||||
* Allows adding, editing, and removing variables
|
||||
*/
|
||||
|
||||
import { type FC, useRef } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Space, Button, Switch, Form } from 'antd'
|
||||
|
||||
import variablesEmpty from '@/assets/images/application/variablesEmpty.svg'
|
||||
import Card from '../Card'
|
||||
import Table from '@/components/Table';
|
||||
@@ -8,17 +21,27 @@ import type { Variable, VariableEditModalRef } from './types'
|
||||
import Empty from '@/components/Empty'
|
||||
import VariableEditModal from './VariableEditModal'
|
||||
|
||||
/**
|
||||
* Component props
|
||||
*/
|
||||
interface VariableListProps {
|
||||
/** Current variable list */
|
||||
value?: Variable[];
|
||||
/** Callback when variables change */
|
||||
onChange?: (value: Variable[]) => void;
|
||||
}
|
||||
const VariableList: FC<VariableListProps> = ({value = [], onChange}) => {
|
||||
|
||||
/**
|
||||
* Variable list management component
|
||||
*/const VariableList: FC<VariableListProps> = ({value = [], onChange}) => {
|
||||
const { t } = useTranslation()
|
||||
const variableEditModalRef = useRef<VariableEditModalRef>(null)
|
||||
|
||||
/** Open variable edit modal */
|
||||
const handleAddVariable = () => {
|
||||
variableEditModalRef.current?.handleOpen()
|
||||
}
|
||||
/** Save variable changes */
|
||||
const handleSaveVariable = (variable: Variable) => {
|
||||
const newList = [...(value || [])]
|
||||
if (variable.index !== undefined && variable.index >= 0) {
|
||||
|
||||
@@ -1,28 +1,69 @@
|
||||
export interface Variable {
|
||||
index?: number;
|
||||
name: string;
|
||||
display_name: string;
|
||||
type: string;
|
||||
required: boolean;
|
||||
max_length?: number;
|
||||
description?: string;
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:26:21
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 16:26:21
|
||||
*/
|
||||
/**
|
||||
* Type definitions for variable configuration in application settings
|
||||
*/
|
||||
|
||||
/**
|
||||
* Variable definition for application input
|
||||
*/
|
||||
export interface Variable {
|
||||
/** Variable index in list */
|
||||
index?: number;
|
||||
/** Variable name (identifier) */
|
||||
name: string;
|
||||
/** Display name shown to users */
|
||||
display_name: string;
|
||||
/** Variable data type (string, number, select, etc.) */
|
||||
type: string;
|
||||
/** Whether variable is required */
|
||||
required: boolean;
|
||||
/** Maximum length for string types */
|
||||
max_length?: number;
|
||||
/** Variable description */
|
||||
description?: string;
|
||||
/** Unique key for React rendering */
|
||||
key?: string;
|
||||
/** Default value */
|
||||
default_value?: string;
|
||||
/** Options for select type variables */
|
||||
options?: string[];
|
||||
/** API extension for dynamic options */
|
||||
api_extension?: string;
|
||||
/** Whether variable is hidden from UI */
|
||||
hidden?: boolean;
|
||||
/** Current value */
|
||||
value?: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modal ref for variable editing
|
||||
*/
|
||||
export interface VariableEditModalRef {
|
||||
/** Open modal with optional existing variable data */
|
||||
handleOpen: (values?: Variable) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* API extension configuration data
|
||||
*/
|
||||
export interface ApiExtensionModalData {
|
||||
/** Extension name */
|
||||
name: string;
|
||||
/** API endpoint URL */
|
||||
apiEndpoint: string;
|
||||
/** API authentication key */
|
||||
apiKey: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modal ref for API extension configuration
|
||||
*/
|
||||
export interface ApiExtensionModalRef {
|
||||
/** Open API extension modal */
|
||||
handleOpen: () => void;
|
||||
}
|
||||
Reference in New Issue
Block a user