fix(web): app features
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-03-17 14:22:25
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-03-17 18:39:49
|
||||
* @Last Modified time: 2026-03-18 15:55:13
|
||||
*/
|
||||
// Toolbar component for chat input area, supporting file upload, audio recording, and variable configuration
|
||||
import { useRef, forwardRef, useImperativeHandle, type ReactNode, useEffect } from 'react'
|
||||
@@ -120,7 +120,10 @@ const ChatToolbar = forwardRef<ChatToolbarRef, ChatToolbarProps>(({
|
||||
|
||||
// Build dropdown menu items based on allowed transfer methods
|
||||
const fileMenus: MenuProps['items'] = []
|
||||
if (file_upload?.allowed_transfer_methods?.includes('remote_url')) {
|
||||
const enabledTypes = ['image', 'document', 'video', 'audio'].filter(
|
||||
type => file_upload?.[`${type}_enabled` as keyof FeaturesConfigForm['file_upload']]
|
||||
)
|
||||
if (file_upload?.allowed_transfer_methods?.includes('remote_url') && enabledTypes.length > 0) {
|
||||
fileMenus.push({
|
||||
key: 'url',
|
||||
label: t('memoryConversation.addRemoteFile'),
|
||||
@@ -133,9 +136,6 @@ const ChatToolbar = forwardRef<ChatToolbarRef, ChatToolbarProps>(({
|
||||
}
|
||||
})
|
||||
}
|
||||
const enabledTypes = ['image', 'document', 'video', 'audio'].filter(
|
||||
type => file_upload?.[`${type}_enabled` as keyof FeaturesConfigForm['file_upload']]
|
||||
)
|
||||
if (file_upload?.allowed_transfer_methods?.includes('local_file') && enabledTypes.length > 0) {
|
||||
fileMenus.push({
|
||||
key: 'upload',
|
||||
@@ -155,7 +155,7 @@ const ChatToolbar = forwardRef<ChatToolbarRef, ChatToolbarProps>(({
|
||||
<Form form={form} initialValues={{ files: [], variables: [] }}>
|
||||
<Flex justify="space-between" className="rb:flex-1">
|
||||
<Flex gap={8} align="center">
|
||||
<Form.Item name="files" noStyle hidden={!file_upload?.enabled}>
|
||||
<Form.Item name="files" noStyle hidden={!file_upload?.enabled || fileMenus.length === 0}>
|
||||
<Dropdown menu={{ items: fileMenus }}>
|
||||
<div className="rb:size-6 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/conversation/link.svg')] rb:hover:bg-[url('@/assets/images/conversation/link_hover.svg')]" />
|
||||
</Dropdown>
|
||||
|
||||
@@ -776,7 +776,7 @@ export const zh = {
|
||||
singleMaxSize: '单文件最大大小',
|
||||
unix: '个',
|
||||
text_to_speech: '文字转语音',
|
||||
text_to_speech_desc: '文本可以转换成语言',
|
||||
text_to_speech_desc: '文本可以转换成语音',
|
||||
|
||||
apps: '我的应用',
|
||||
sharing: '共享',
|
||||
|
||||
@@ -129,7 +129,7 @@ const SelectWrapper: FC<{ title: string, desc: string, name: string | string[],
|
||||
* Agent configuration component
|
||||
* Manages single agent configuration including prompts, knowledge, memory, variables, and tools
|
||||
*/
|
||||
const Agent = forwardRef<AgentRef>((_props, ref) => {
|
||||
const Agent = forwardRef<AgentRef, { onFeaturesLoad?: (features: FeaturesConfigForm | undefined) => void }>(({ onFeaturesLoad }, ref) => {
|
||||
const { t } = useTranslation()
|
||||
const { id } = useParams();
|
||||
const { message } = App.useApp()
|
||||
@@ -200,6 +200,7 @@ const Agent = forwardRef<AgentRef>((_props, ref) => {
|
||||
...response,
|
||||
tools: allTools
|
||||
})
|
||||
onFeaturesLoad?.(response.features)
|
||||
}).finally(() => {
|
||||
setLoading(false)
|
||||
})
|
||||
|
||||
@@ -38,7 +38,7 @@ const MAX_LENGTH = 5;
|
||||
* Multi-agent cluster configuration component
|
||||
* Manages multi-agent orchestration, sub-agents, and collaboration modes
|
||||
*/
|
||||
const Cluster = forwardRef<ClusterRef>((_props, ref) => {
|
||||
const Cluster = forwardRef<ClusterRef, { onFeaturesLoad?: (features: FeaturesConfigForm | undefined) => void }>(({ onFeaturesLoad }, ref) => {
|
||||
const { t } = useTranslation()
|
||||
const { message } = App.useApp()
|
||||
const [form] = Form.useForm()
|
||||
@@ -131,6 +131,7 @@ const Cluster = forwardRef<ClusterRef>((_props, ref) => {
|
||||
} else {
|
||||
setSubAgents(sub_agents)
|
||||
}
|
||||
onFeaturesLoad?.(response.features)
|
||||
})
|
||||
}
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-03-13 17:19:13
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-03-18 10:47:17
|
||||
* @Last Modified time: 2026-03-18 16:03:46
|
||||
*/
|
||||
import { forwardRef, useImperativeHandle, useState } from 'react';
|
||||
import { Checkbox, App, Form } from 'antd';
|
||||
@@ -78,7 +78,7 @@ const AppSharingModal = forwardRef<AppSharingModalRef, AppSharingModalProps>(({
|
||||
*/
|
||||
const handleToggle = (id: string, isShared: boolean) => {
|
||||
if (isShared) return;
|
||||
const prev = form.getFieldValue('target_workspace_ids') as string[] ?? [];
|
||||
const prev: string[] = form.getFieldValue('target_workspace_ids') ?? [];
|
||||
form.setFieldValue(
|
||||
'target_workspace_ids',
|
||||
prev.includes(id) ? prev.filter(i => i !== id) : [...prev, id]
|
||||
@@ -135,10 +135,16 @@ const AppSharingModal = forwardRef<AppSharingModalRef, AppSharingModalProps>(({
|
||||
|
||||
{/* Target space: scrollable list of workspaces with checkbox selection */}
|
||||
<Form.Item
|
||||
name="target_workspace_ids"
|
||||
label={t('application.selectTargetSpace')}
|
||||
rules={[{ required: true, message: t('common.pleaseSelect') }]}
|
||||
required
|
||||
>
|
||||
<Form.Item
|
||||
name="target_workspace_ids"
|
||||
noStyle
|
||||
rules={[{ required: true, message: t('common.pleaseSelect') }]}
|
||||
>
|
||||
<input type="hidden" />
|
||||
</Form.Item>
|
||||
<div className="rb:rounded-lg rb:border rb:border-[#EBEBEB] rb:divide-y rb:divide-[#EBEBEB] rb:max-h-50 rb:overflow-y-auto">
|
||||
{spaceList.map(space => {
|
||||
const isShared = sharedIds.includes(space.id);
|
||||
@@ -146,12 +152,11 @@ const AppSharingModal = forwardRef<AppSharingModalRef, AppSharingModalProps>(({
|
||||
<div key={space.id} className="rb:flex rb:items-center rb:gap-2 rb:px-4 rb:py-3 rb:cursor-pointer" onClick={() => handleToggle(space.id, isShared)}>
|
||||
<Checkbox
|
||||
checked={isShared || selectedIds.includes(space.id)}
|
||||
disabled={isShared} // already-shared workspaces cannot be unselected
|
||||
disabled={isShared}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onChange={() => handleToggle(space.id, isShared)}
|
||||
/>
|
||||
<span className="rb:flex-1 rb:text-sm">{space.name}</span>
|
||||
{/* Badge shown when the app is already shared with this workspace */}
|
||||
{isShared && (
|
||||
<span className="rb:text-xs rb:text-[#5B6167]">{t('application.alreadyShared')}</span>
|
||||
)}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:27:52
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-03-16 17:04:56
|
||||
* @Last Modified time: 2026-03-18 15:40:53
|
||||
*/
|
||||
import { type FC, useRef, useMemo, useCallback } from 'react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
@@ -61,6 +61,10 @@ interface ConfigHeaderProps {
|
||||
workflowRef: React.RefObject<WorkflowRef>
|
||||
/** App component ref (Agent/Cluster/Workflow) */
|
||||
appRef?: React.RefObject<AgentRef | ClusterRef | WorkflowRef>
|
||||
/** Features config from parent state */
|
||||
features?: FeaturesConfigForm;
|
||||
/** Callback to update features in parent */
|
||||
onFeaturesChange?: (value: FeaturesConfigForm) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,6 +75,8 @@ const ConfigHeader: FC<ConfigHeaderProps> = ({
|
||||
application, activeTab, handleChangeTab, refresh,
|
||||
workflowRef,
|
||||
appRef,
|
||||
features,
|
||||
onFeaturesChange,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
@@ -173,14 +179,10 @@ const ConfigHeader: FC<ConfigHeaderProps> = ({
|
||||
return items
|
||||
}, [t, handleClick, application])
|
||||
|
||||
const features = useMemo(() => {
|
||||
return (appRef?.current?.features || { file_type: [] }) as FeaturesConfigForm
|
||||
}, [appRef])
|
||||
const handleSaveFeaturesConfig = useCallback((value: FeaturesConfigForm) => {
|
||||
appRef?.current?.handleSaveFeaturesConfig?.(value)
|
||||
}, [appRef])
|
||||
|
||||
console.log('formatMenuItems', formatMenuItems)
|
||||
onFeaturesChange?.(value)
|
||||
}, [appRef, onFeaturesChange])
|
||||
return (
|
||||
<>
|
||||
<Header className="rb:w-full rb:h-16 rb:grid rb:grid-cols-3 rb:p-[16px_16px_16px_24px]! rb:border-b rb:border-[#EAECEE] rb:leading-8">
|
||||
@@ -211,7 +213,7 @@ const ConfigHeader: FC<ConfigHeaderProps> = ({
|
||||
</div>
|
||||
{application?.type === 'workflow'
|
||||
? <div className="rb:h-8 rb:flex rb:items-center rb:justify-end rb:gap-2.5">
|
||||
<FeaturesConfig value={features} refresh={handleSaveFeaturesConfig} />
|
||||
<FeaturesConfig source={application?.type} value={features} refresh={handleSaveFeaturesConfig} />
|
||||
<Button onClick={clear}>{t('workflow.clear')}</Button>
|
||||
<Button onClick={addvariable}>{t('workflow.addvariable')}</Button>
|
||||
<Button onClick={run}>{t('workflow.run')}</Button>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:27:56
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-03-16 18:31:58
|
||||
* @Last Modified time: 2026-03-18 15:38:14
|
||||
*/
|
||||
/**
|
||||
* Copy Application Modal
|
||||
@@ -18,9 +18,11 @@ import type { FeaturesConfigModalRef, FeaturesConfigForm } from '../../types'
|
||||
import RbModal from '@/components/RbModal'
|
||||
import SwitchFormItem from '@/components/FormItem/SwitchFormItem'
|
||||
import FileUploadSettingModal from './FileUploadSettingModal'
|
||||
import type { Application } from '@/views/ApplicationManagement/types';
|
||||
|
||||
interface FeaturesConfigModalProps {
|
||||
refresh: (value: FeaturesConfigForm) => void;
|
||||
source?: Application['type'];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -28,6 +30,7 @@ interface FeaturesConfigModalProps {
|
||||
*/
|
||||
const FeaturesConfigModal = forwardRef<FeaturesConfigModalRef, FeaturesConfigModalProps>(({
|
||||
refresh,
|
||||
source,
|
||||
}, ref) => {
|
||||
const { t } = useTranslation();
|
||||
const [visible, setVisible] = useState(false);
|
||||
@@ -44,6 +47,7 @@ const FeaturesConfigModal = forwardRef<FeaturesConfigModalRef, FeaturesConfigMod
|
||||
/** Open modal */
|
||||
const handleOpen = (initValue: FeaturesConfigForm) => {
|
||||
setVisible(true);
|
||||
console.log('initValue', initValue)
|
||||
form.setFieldsValue(initValue)
|
||||
};
|
||||
/** Copy application with new name */
|
||||
@@ -66,7 +70,6 @@ const FeaturesConfigModal = forwardRef<FeaturesConfigModalRef, FeaturesConfigMod
|
||||
handleClose
|
||||
}));
|
||||
|
||||
console.log('settings values', values)
|
||||
return (
|
||||
<>
|
||||
<RbModal
|
||||
@@ -81,20 +84,22 @@ const FeaturesConfigModal = forwardRef<FeaturesConfigModalRef, FeaturesConfigMod
|
||||
layout="vertical"
|
||||
>
|
||||
<Flex vertical gap={12}>
|
||||
<div className="rb:relative rb:border rb:border-[#DFE4ED] rb:p-3 rb:rounded-lg rb:bg-[#f5f7fc]">
|
||||
<SwitchFormItem
|
||||
title={t(`memoryConversation.web_search`)}
|
||||
name={['web_search', "enabled"]}
|
||||
/>
|
||||
</div>
|
||||
{source !== 'workflow' && <>
|
||||
<div className="rb:relative rb:border rb:border-[#DFE4ED] rb:p-3 rb:rounded-lg rb:bg-[#f5f7fc]">
|
||||
<SwitchFormItem
|
||||
title={t(`memoryConversation.web_search`)}
|
||||
name={['web_search', "enabled"]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="rb:relative rb:border rb:border-[#DFE4ED] rb:p-3 rb:rounded-lg rb:bg-[#f5f7fc]">
|
||||
<SwitchFormItem
|
||||
title={t('application.text_to_speech')}
|
||||
name={['text_to_speech', "enabled"]}
|
||||
desc={t('application.text_to_speech_desc')}
|
||||
/>
|
||||
</div>
|
||||
<div className="rb:relative rb:border rb:border-[#DFE4ED] rb:p-3 rb:rounded-lg rb:bg-[#f5f7fc]">
|
||||
<SwitchFormItem
|
||||
title={t('application.text_to_speech')}
|
||||
name={['text_to_speech', "enabled"]}
|
||||
desc={t('application.text_to_speech_desc')}
|
||||
/>
|
||||
</div>
|
||||
</>}
|
||||
|
||||
<div className="rb:relative rb:border rb:border-[#DFE4ED] rb:p-3 rb:rounded-lg rb:bg-[#f5f7fc]">
|
||||
<SwitchFormItem
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-03-13 17:20:21
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-03-16 18:31:43
|
||||
* @Last Modified time: 2026-03-18 15:38:59
|
||||
*/
|
||||
import { type FC, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -10,6 +10,7 @@ import { Button } from 'antd';
|
||||
|
||||
import FeaturesConfigModal from './FeaturesConfigModal'
|
||||
import type { FeaturesConfigModalRef, FeaturesConfigForm } from '../../types'
|
||||
import type { Application } from '@/views/ApplicationManagement/types';
|
||||
|
||||
/** Props for the FeaturesConfig component */
|
||||
interface FeaturesConfigProps {
|
||||
@@ -17,11 +18,13 @@ interface FeaturesConfigProps {
|
||||
value: FeaturesConfigForm;
|
||||
/** Callback to propagate updated config back to the parent */
|
||||
refresh: (value: FeaturesConfigForm) => void;
|
||||
source?: Application['type'];
|
||||
}
|
||||
|
||||
const FeaturesConfig: FC<FeaturesConfigProps> = ({
|
||||
value,
|
||||
refresh
|
||||
refresh,
|
||||
source
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
// Ref used to imperatively open the config modal
|
||||
@@ -29,6 +32,7 @@ const FeaturesConfig: FC<FeaturesConfigProps> = ({
|
||||
|
||||
/** Open the feature config modal pre-populated with the current values */
|
||||
const handleFeaturesConfig = () => {
|
||||
console.log('handleFeaturesConfig', value)
|
||||
funConfigModalRef.current?.handleOpen(value)
|
||||
}
|
||||
|
||||
@@ -41,6 +45,7 @@ const FeaturesConfig: FC<FeaturesConfigProps> = ({
|
||||
<FeaturesConfigModal
|
||||
ref={funConfigModalRef}
|
||||
refresh={refresh}
|
||||
source={source}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -37,6 +37,7 @@ const ApplicationConfig: React.FC = () => {
|
||||
// State
|
||||
const [application, setApplication] = useState<Application | null>(null);
|
||||
const [activeTab, setActiveTab] = useState('arrangement');
|
||||
const [features, setFeatures] = useState<import('./types').FeaturesConfigForm | undefined>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
setActiveTab(source === 'sharing' ? 'test' : 'arrangement')
|
||||
@@ -114,10 +115,12 @@ const ApplicationConfig: React.FC = () => {
|
||||
refresh={getApplicationInfo}
|
||||
appRef={application?.type === 'agent' ? agentRef : application?.type === 'multi_agent' ? clusterRef : application?.type === 'workflow' ? workflowRef : undefined}
|
||||
workflowRef={workflowRef}
|
||||
features={features}
|
||||
onFeaturesChange={setFeatures}
|
||||
/>
|
||||
{activeTab === 'arrangement' && application?.type === 'agent' && <Agent ref={agentRef} />}
|
||||
{activeTab === 'arrangement' && application?.type === 'multi_agent' && <Cluster ref={clusterRef} />}
|
||||
{activeTab === 'arrangement' && application?.type === 'workflow' && <Workflow ref={workflowRef} />}
|
||||
{activeTab === 'arrangement' && application?.type === 'agent' && <Agent ref={agentRef} onFeaturesLoad={setFeatures} />}
|
||||
{activeTab === 'arrangement' && application?.type === 'multi_agent' && <Cluster ref={clusterRef} onFeaturesLoad={setFeatures} />}
|
||||
{activeTab === 'arrangement' && application?.type === 'workflow' && <Workflow ref={workflowRef} onFeaturesLoad={setFeatures} />}
|
||||
{activeTab === 'api' && <Api application={application} />}
|
||||
{activeTab === 'release' && <ReleasePage data={application as Application} refresh={getApplicationInfo} />}
|
||||
{activeTab === 'statistics' && <Statistics application={application} />}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:34:12
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-03-18 11:20:45
|
||||
* @Last Modified time: 2026-03-18 15:58:36
|
||||
*/
|
||||
import React, { useState, useEffect, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -73,6 +73,11 @@ const MySharing: React.FC = () => {
|
||||
}
|
||||
});
|
||||
};
|
||||
/** Navigate to application configuration page */
|
||||
const handleEdit = (item: MySharedOutItem) => {
|
||||
let url = `/#/application/config/${item.source_app_id}`
|
||||
window.open(url);
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex vertical gap={12} className="rb:h-[calc(100vh-148px)]! rb:overflow-y-auto!">
|
||||
@@ -108,7 +113,7 @@ const MySharing: React.FC = () => {
|
||||
children: (
|
||||
<Row gutter={[12, 12]}>
|
||||
{items.map(item => (
|
||||
<Col key={item.id} span={6} className="rb:bg-[#F6F6F6] rb:rounded-lg rb:py-3! rb:px-4! rb:relative">
|
||||
<Col key={item.id} span={6} className="rb:bg-[#F6F6F6] rb:rounded-lg rb:py-3! rb:px-4! rb:relative" onClick={() => handleEdit(item)}>
|
||||
<div
|
||||
className="rb:absolute rb:top-3 rb:right-3 rb:cursor-pointer rb:size-4 rb:bg-cover rb:bg-[url('@/assets/images/close.svg')]"
|
||||
onClick={() => handleCancelOne(item)}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-06 21:09:47
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-03-17 10:28:04
|
||||
* @Last Modified time: 2026-03-18 15:50:31
|
||||
*/
|
||||
/**
|
||||
* Upload File List Modal Component
|
||||
@@ -115,7 +115,6 @@ const UploadFileListModal = forwardRef<UploadFileListModalRef, UploadFileListMod
|
||||
<FormItem
|
||||
{...restField}
|
||||
name={[name, 'type']}
|
||||
initialValue="image"
|
||||
className="rb:mb-0!"
|
||||
>
|
||||
<Select
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:58:03
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-03-17 18:30:58
|
||||
* @Last Modified time: 2026-03-18 15:35:05
|
||||
*/
|
||||
/**
|
||||
* Conversation Page
|
||||
@@ -60,6 +60,7 @@ const Conversation: FC = () => {
|
||||
const [shareToken, setShareToken] = useState<string | null>(localStorage.getItem(`shareToken_${token}`))
|
||||
const [fileList, setFileList] = useState<any[]>([])
|
||||
const [webSearch, setWebSearch] = useState(false)
|
||||
const [isHasMemory, setIsHasMemory] = useState(false)
|
||||
const [memory, setMemory] = useState(true)
|
||||
const [features, setFeatures] = useState<FeaturesConfigForm>({} as FeaturesConfigForm)
|
||||
|
||||
@@ -85,9 +86,10 @@ const Conversation: FC = () => {
|
||||
if (shareToken && token) {
|
||||
getExperienceConfig(token)
|
||||
.then(res => {
|
||||
const response = res as { variables: Variable[]; features: FeaturesConfigForm }
|
||||
const response = res as { variables: Variable[]; features: FeaturesConfigForm; app_type: string; memory?: boolean; }
|
||||
toolbarRef.current?.setVariables(response.variables || [])
|
||||
setFeatures(response.features)
|
||||
setIsHasMemory((response.app_type === 'workflow' && response.memory) || (response.app_type !== 'workflow'))
|
||||
})
|
||||
} else {
|
||||
setChatList([])
|
||||
@@ -379,14 +381,16 @@ const Conversation: FC = () => {
|
||||
{t('memoryConversation.web_search')}
|
||||
</ButtonCheckbox>
|
||||
}
|
||||
<ButtonCheckbox
|
||||
icon={MemoryFunctionIcon}
|
||||
checkedIcon={MemoryFunctionCheckedIcon}
|
||||
checked={memory}
|
||||
onChange={handleChangeMemory}
|
||||
>
|
||||
{t('memoryConversation.memory')}
|
||||
</ButtonCheckbox>
|
||||
{isHasMemory &&
|
||||
<ButtonCheckbox
|
||||
icon={MemoryFunctionIcon}
|
||||
checkedIcon={MemoryFunctionCheckedIcon}
|
||||
checked={memory}
|
||||
onChange={handleChangeMemory}
|
||||
>
|
||||
{t('memoryConversation.memory')}
|
||||
</ButtonCheckbox>
|
||||
}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -10,10 +10,6 @@ interface CanvasToolbarProps {
|
||||
isHandMode: boolean;
|
||||
setIsHandMode: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
zoomLevel: number;
|
||||
canUndo: boolean;
|
||||
canRedo: boolean;
|
||||
onUndo: () => void;
|
||||
onRedo: () => void;
|
||||
addNotes: () => void;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 15:17:48
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-03-18 12:07:03
|
||||
* @Last Modified time: 2026-03-18 16:08:17
|
||||
*/
|
||||
import { useRef, useEffect, useState } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
@@ -12,10 +12,11 @@ import { Graph, Node, MiniMap, Snapline, Clipboard, Keyboard, type Edge } from '
|
||||
import { register } from '@antv/x6-react-shape';
|
||||
import type { PortMetadata } from '@antv/x6/lib/model/port';
|
||||
|
||||
import { nodeRegisterLibrary, graphNodeLibrary, nodeLibrary, portMarkup, portAttrs, edgeAttrs, edge_color, edge_selected_color, portTextAttrs, defaultAbsolutePortGroups, nodeWidth, unknownNode, noteNode, notesConfig } from '../constant';
|
||||
import { nodeRegisterLibrary, graphNodeLibrary, nodeLibrary, portMarkup, portAttrs, edgeAttrs, edge_color, edge_selected_color, portTextAttrs, defaultAbsolutePortGroups, nodeWidth, unknownNode, notesConfig } from '../constant';
|
||||
import type { WorkflowConfig, NodeProperties, ChatVariable } from '../types';
|
||||
import { getWorkflowConfig, saveWorkflowConfig } from '@/api/application'
|
||||
import { useUser } from '@/store/user';
|
||||
import type { FeaturesConfigForm } from '@/views/ApplicationConfig/types'
|
||||
|
||||
/**
|
||||
* Props for useWorkflowGraph hook
|
||||
@@ -25,6 +26,8 @@ export interface UseWorkflowGraphProps {
|
||||
containerRef: React.RefObject<HTMLDivElement>;
|
||||
/** Reference to the minimap container element */
|
||||
miniMapRef: React.RefObject<HTMLDivElement>;
|
||||
/** Callback when features config is loaded */
|
||||
onFeaturesLoad?: (features: FeaturesConfigForm | undefined) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,6 +70,7 @@ export interface UseWorkflowGraphReturn {
|
||||
setChatVariables: React.Dispatch<React.SetStateAction<ChatVariable[]>>;
|
||||
|
||||
handleAddNotes: () => void;
|
||||
handleSaveFeaturesConfig: (value: FeaturesConfigForm) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,6 +82,7 @@ export interface UseWorkflowGraphReturn {
|
||||
export const useWorkflowGraph = ({
|
||||
containerRef,
|
||||
miniMapRef,
|
||||
onFeaturesLoad,
|
||||
}: UseWorkflowGraphProps): UseWorkflowGraphReturn => {
|
||||
// Hooks
|
||||
const { id } = useParams();
|
||||
@@ -115,6 +120,7 @@ export const useWorkflowGraph = ({
|
||||
})
|
||||
setChatVariables(initChatVariables)
|
||||
setConfig({ ...rest, variables: initChatVariables })
|
||||
onFeaturesLoad?.(rest.features)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -132,7 +138,7 @@ export const useWorkflowGraph = ({
|
||||
if (nodes.length) {
|
||||
const nodeList = nodes.map(node => {
|
||||
const { id, type, name, position, config = {} } = node
|
||||
let nodeLibraryConfig = [...nodeLibrary, { nodes: [unknownNode, notesConfig] }]
|
||||
let nodeLibraryConfig: NodeProperties | undefined = [...nodeLibrary, { nodes: [unknownNode, notesConfig] }]
|
||||
.flatMap(category => category.nodes)
|
||||
.find(n => n.type === type)
|
||||
nodeLibraryConfig = JSON.parse(JSON.stringify({ config: {}, ...nodeLibraryConfig })) as NodeProperties
|
||||
@@ -994,6 +1000,9 @@ export const useWorkflowGraph = ({
|
||||
}) || [];
|
||||
const edges = graphRef.current?.getEdges() || []
|
||||
|
||||
|
||||
console.log('config', config)
|
||||
|
||||
const params = {
|
||||
...config,
|
||||
variables: chatVariables.map(v => {
|
||||
@@ -1187,6 +1196,9 @@ export const useWorkflowGraph = ({
|
||||
data: { ...cleanNodeData },
|
||||
});
|
||||
}
|
||||
const handleSaveFeaturesConfig = (value?: FeaturesConfigForm) => {
|
||||
setConfig(prev => prev ? { ...prev, features: value } as WorkflowConfig : prev)
|
||||
}
|
||||
|
||||
return {
|
||||
config,
|
||||
@@ -1206,6 +1218,7 @@ export const useWorkflowGraph = ({
|
||||
handleSave,
|
||||
chatVariables,
|
||||
setChatVariables,
|
||||
handleAddNotes
|
||||
handleAddNotes,
|
||||
handleSaveFeaturesConfig
|
||||
};
|
||||
};
|
||||
|
||||
@@ -6,13 +6,13 @@ import Properties from './components/Properties';
|
||||
import CanvasToolbar from './components/CanvasToolbar';
|
||||
import PortClickHandler from './components/PortClickHandler';
|
||||
import { useWorkflowGraph } from './hooks/useWorkflowGraph';
|
||||
import type { WorkflowRef } from '@/views/ApplicationConfig/types'
|
||||
import type { WorkflowRef, FeaturesConfigForm } from '@/views/ApplicationConfig/types'
|
||||
import Chat from './components/Chat/Chat';
|
||||
import type { ChatRef, AddChatVariableRef } from './types'
|
||||
import arrowIcon from '@/assets/images/workflow/arrow.png'
|
||||
import AddChatVariable from './components/AddChatVariable';
|
||||
|
||||
const Workflow = forwardRef<WorkflowRef>((_props, ref) => {
|
||||
const Workflow = forwardRef<WorkflowRef, { onFeaturesLoad?: (features: FeaturesConfigForm | undefined) => void }>(({ onFeaturesLoad }, ref) => {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const miniMapRef = useRef<HTMLDivElement>(null);
|
||||
const addChatVariableRef = useRef<AddChatVariableRef>(null)
|
||||
@@ -25,12 +25,8 @@ const Workflow = forwardRef<WorkflowRef>((_props, ref) => {
|
||||
selectedNode,
|
||||
setSelectedNode,
|
||||
zoomLevel,
|
||||
canUndo,
|
||||
canRedo,
|
||||
isHandMode,
|
||||
setIsHandMode,
|
||||
onUndo,
|
||||
onRedo,
|
||||
onDrop,
|
||||
blankClick,
|
||||
deleteEvent,
|
||||
@@ -39,8 +35,9 @@ const Workflow = forwardRef<WorkflowRef>((_props, ref) => {
|
||||
handleSave,
|
||||
chatVariables,
|
||||
setChatVariables,
|
||||
handleAddNotes
|
||||
} = useWorkflowGraph({ containerRef, miniMapRef });
|
||||
handleAddNotes,
|
||||
handleSaveFeaturesConfig
|
||||
} = useWorkflowGraph({ containerRef, miniMapRef, onFeaturesLoad });
|
||||
|
||||
const onDragOver = (event: React.DragEvent) => {
|
||||
event.preventDefault();
|
||||
@@ -61,7 +58,8 @@ const Workflow = forwardRef<WorkflowRef>((_props, ref) => {
|
||||
graphRef,
|
||||
addVariable,
|
||||
config,
|
||||
features: config?.features
|
||||
features: config?.features,
|
||||
handleSaveFeaturesConfig
|
||||
}))
|
||||
return (
|
||||
<div className="rb:h-[calc(100vh-64px)] rb:relative">
|
||||
@@ -93,10 +91,6 @@ const Workflow = forwardRef<WorkflowRef>((_props, ref) => {
|
||||
isHandMode={isHandMode}
|
||||
setIsHandMode={setIsHandMode}
|
||||
zoomLevel={zoomLevel}
|
||||
canUndo={canUndo}
|
||||
canRedo={canRedo}
|
||||
onUndo={onUndo}
|
||||
onRedo={onRedo}
|
||||
addNotes={handleAddNotes}
|
||||
/>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user