Merge branch 'develop' into feature/end_zy
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
import type { FC } from 'react';
|
||||
import { Select, Divider } from 'antd';
|
||||
import { PlusOutlined, MinusOutlined, FileAddOutlined } from '@ant-design/icons'
|
||||
import { Select, Divider, Tooltip } from 'antd';
|
||||
import { PlusOutlined, MinusOutlined, FileAddOutlined, UndoOutlined, RedoOutlined } from '@ant-design/icons'
|
||||
import clsx from 'clsx'
|
||||
import { Node } from '@antv/x6';
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import type { GraphRef } from '../types'
|
||||
|
||||
@@ -15,6 +16,10 @@ interface CanvasToolbarProps {
|
||||
setIsHandMode: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
zoomLevel: number;
|
||||
addNotes: () => void;
|
||||
canUndo: boolean;
|
||||
canRedo: boolean;
|
||||
onUndo: () => void;
|
||||
onRedo: () => void;
|
||||
}
|
||||
|
||||
const CanvasToolbar: FC<CanvasToolbarProps> = ({
|
||||
@@ -22,12 +27,13 @@ const CanvasToolbar: FC<CanvasToolbarProps> = ({
|
||||
miniMapRef,
|
||||
graphRef,
|
||||
zoomLevel,
|
||||
// canUndo,
|
||||
// canRedo,
|
||||
// onUndo,
|
||||
// onRedo,
|
||||
canUndo,
|
||||
canRedo,
|
||||
onUndo,
|
||||
onRedo,
|
||||
addNotes,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<>
|
||||
{/* 小地图 */}
|
||||
@@ -63,13 +69,16 @@ const CanvasToolbar: FC<CanvasToolbarProps> = ({
|
||||
{ label: '125%', value: 125 },
|
||||
{ label: '150%', value: 150 },
|
||||
{ label: '200%', value: 200 },
|
||||
{ label: '自适应', value: 'fit' },
|
||||
{ label: t('workflow.fit'), value: 'fit' },
|
||||
]}
|
||||
variant='borderless'
|
||||
size="small"
|
||||
/>
|
||||
<PlusOutlined className="rb:text-[16px] rb:cursor-pointer" onClick={() => graphRef.current?.zoom(0.1)} />
|
||||
<Divider type="vertical" className="rb:h-4" />
|
||||
<Tooltip title={`${t('workflow.undo')} (Ctrl+Z)`}><UndoOutlined className={clsx('rb:text-[16px]', canUndo ? 'rb:cursor-pointer' : 'rb:opacity-30 rb:cursor-not-allowed')} onClick={onUndo} /></Tooltip>
|
||||
<Tooltip title={`${t('workflow.redo')} (Ctrl+Y)`}><RedoOutlined className={clsx('rb:text-[16px]', canRedo ? 'rb:cursor-pointer' : 'rb:opacity-30 rb:cursor-not-allowed')} onClick={onRedo} /></Tooltip>
|
||||
<Divider type="vertical" className="rb:h-4" />
|
||||
<FileAddOutlined onClick={addNotes} />
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-06 21:10:56
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-04-07 18:07:38
|
||||
* @Last Modified time: 2026-04-21 14:59:13
|
||||
*/
|
||||
/**
|
||||
* Workflow Chat Component
|
||||
@@ -41,13 +41,17 @@ import type { ChatToolbarRef } from '@/components/Chat/ChatToolbar'
|
||||
import Runtime from './Runtime';
|
||||
import type { FeaturesConfigForm } from '@/views/ApplicationConfig/types';
|
||||
import { replaceVariables } from '@/views/ApplicationConfig/Agent';
|
||||
import { useWorkflowStore } from '@/store/workflow';
|
||||
|
||||
const Chat = forwardRef<ChatRef, { appId: string; graphRef: GraphRef; data: WorkflowConfig | null; features?: FeaturesConfigForm }>(({
|
||||
const Chat = forwardRef<ChatRef, { appId: string; graphRef: GraphRef; data: WorkflowConfig | null; features?: FeaturesConfigForm }>(({ // eslint-disable-line
|
||||
appId, graphRef, features
|
||||
}, ref) => {
|
||||
const { t } = useTranslation()
|
||||
const { message: messageApi } = App.useApp()
|
||||
const { setChatHistory } = useWorkflowStore()
|
||||
const conversationIdRef = useRef<string>('draft')
|
||||
const toolbarRef = useRef<ChatToolbarRef>(null)
|
||||
const abortRef = useRef<(() => void) | null>(null)
|
||||
const [toolbarReady, setToolbarReady] = useState(false)
|
||||
const toolbarCallbackRef = useCallback((node: ChatToolbarRef | null) => {
|
||||
(toolbarRef as React.MutableRefObject<ChatToolbarRef | null>).current = node
|
||||
@@ -62,6 +66,8 @@ const Chat = forwardRef<ChatRef, { appId: string; graphRef: GraphRef; data: Work
|
||||
const [fileList, setFileList] = useState<any[]>([])
|
||||
const [message, setMessage] = useState<string | undefined>(undefined)
|
||||
|
||||
console.log('abortRef', abortRef)
|
||||
|
||||
/**
|
||||
* Opens the chat drawer and loads workflow variables from the start node
|
||||
*/
|
||||
@@ -113,11 +119,14 @@ const Chat = forwardRef<ChatRef, { appId: string; graphRef: GraphRef; data: Work
|
||||
* Closes the drawer and resets all state
|
||||
*/
|
||||
const handleClose = () => {
|
||||
abortRef.current?.()
|
||||
abortRef.current = null;
|
||||
setOpen(false)
|
||||
setToolbarReady(false)
|
||||
setChatList([])
|
||||
setVariables([])
|
||||
setConversationId(null)
|
||||
conversationIdRef.current = 'draft'
|
||||
setMessage(undefined)
|
||||
toolbarRef.current?.setFiles([])
|
||||
toolbarRef.current?.setVariables([])
|
||||
@@ -189,7 +198,7 @@ const Chat = forwardRef<ChatRef, { appId: string; graphRef: GraphRef; data: Work
|
||||
elapsed_time?: string;
|
||||
error?: any;
|
||||
state: Record<string, any>;
|
||||
status?: 'completed' | 'failed',
|
||||
status?: 'completed' | 'failed' | 'running',
|
||||
citations?: {
|
||||
document_id: string;
|
||||
file_name: string;
|
||||
@@ -231,6 +240,7 @@ const Chat = forwardRef<ChatRef, { appId: string; graphRef: GraphRef; data: Work
|
||||
node_name: name,
|
||||
node_type: type,
|
||||
icon,
|
||||
status: 'running',
|
||||
content: {},
|
||||
}
|
||||
} else {
|
||||
@@ -240,6 +250,7 @@ const Chat = forwardRef<ChatRef, { appId: string; graphRef: GraphRef; data: Work
|
||||
node_name: name,
|
||||
node_type: type,
|
||||
icon,
|
||||
status: 'running',
|
||||
content: {},
|
||||
})
|
||||
}
|
||||
@@ -344,6 +355,7 @@ const Chat = forwardRef<ChatRef, { appId: string; graphRef: GraphRef; data: Work
|
||||
}
|
||||
|
||||
if (conversation_id && conversationId !== conversation_id) {
|
||||
conversationIdRef.current = conversation_id
|
||||
setConversationId(conversation_id)
|
||||
}
|
||||
})
|
||||
@@ -388,7 +400,7 @@ const Chat = forwardRef<ChatRef, { appId: string; graphRef: GraphRef; data: Work
|
||||
])
|
||||
setLoading(true)
|
||||
setStreamLoading(true)
|
||||
draftRun(appId, data, handleStreamMessage)
|
||||
draftRun(appId, data, handleStreamMessage, abort => { abortRef.current = abort })
|
||||
.catch((error) => {
|
||||
const errorInfo = JSON.parse(error.message)
|
||||
setChatList(prev => {
|
||||
@@ -440,6 +452,10 @@ const Chat = forwardRef<ChatRef, { appId: string; graphRef: GraphRef; data: Work
|
||||
}
|
||||
}, [chatList.length, features?.opening_statement, variables])
|
||||
|
||||
useEffect(() => {
|
||||
setChatHistory(conversationIdRef.current, chatList)
|
||||
}, [chatList])
|
||||
|
||||
return (
|
||||
<RbDrawer
|
||||
title={<Flex align="center" gap={10}>
|
||||
|
||||
@@ -34,29 +34,24 @@ const NodeLibrary: FC<{ collapsed: boolean; handleToggle: () => void }> = ({ col
|
||||
>
|
||||
<Flex vertical align={collapsed ? 'center' : undefined} gap={collapsed ? 8 : 16}>
|
||||
{collapsed
|
||||
? <>
|
||||
{nodeLibrary.map(category => (
|
||||
<>
|
||||
{category.nodes
|
||||
.filter(node => node.type !== 'cycle-start' && node.type !== 'break')
|
||||
.map((node, nodeIndex) => (
|
||||
<Tooltip key={nodeIndex} title={t(`workflow.${node.type}`)} placement="right">
|
||||
<div
|
||||
className="rb:p-2 rb:rounded-lg rb:hover:bg-[rgba(33,35,50,0.08)]"
|
||||
draggable
|
||||
onDragStart={(e) => {
|
||||
e.dataTransfer.setData('application/reactflow', node.type);
|
||||
e.dataTransfer.setData('application/json', JSON.stringify(node));
|
||||
}}
|
||||
>
|
||||
<div className={`rb:size-6 rb:cursor-pointer rb:bg-cover ${node.icon}`} />
|
||||
</div>
|
||||
</Tooltip>
|
||||
))
|
||||
}
|
||||
</>
|
||||
))}
|
||||
</>
|
||||
? nodeLibrary.flatMap(category =>
|
||||
category.nodes
|
||||
.filter(node => node.type !== 'cycle-start' && node.type !== 'break')
|
||||
.map(node => (
|
||||
<Tooltip key={node.type} title={t(`workflow.${node.type}`)} placement="right">
|
||||
<div
|
||||
className="rb:p-2 rb:rounded-lg rb:hover:bg-[rgba(33,35,50,0.08)]"
|
||||
draggable
|
||||
onDragStart={(e) => {
|
||||
e.dataTransfer.setData('application/reactflow', node.type);
|
||||
e.dataTransfer.setData('application/json', JSON.stringify(node));
|
||||
}}
|
||||
>
|
||||
<div className={`rb:size-6 rb:cursor-pointer rb:bg-cover ${node.icon}`} />
|
||||
</div>
|
||||
</Tooltip>
|
||||
))
|
||||
)
|
||||
: nodeLibrary.map(category => (
|
||||
<div
|
||||
key={category.category}
|
||||
@@ -65,9 +60,9 @@ const NodeLibrary: FC<{ collapsed: boolean; handleToggle: () => void }> = ({ col
|
||||
<Flex gap={6} vertical>
|
||||
{category.nodes
|
||||
.filter(node => node.type !== 'cycle-start' && node.type !== 'break')
|
||||
.map((node, nodeIndex) => (
|
||||
.map((node) => (
|
||||
<Flex
|
||||
key={nodeIndex}
|
||||
key={node.type}
|
||||
align="center"
|
||||
gap={8}
|
||||
className="rb:rounded-xl rb:p-2! rb:border rb:border-[#EBEBEB] rb:cursor-pointer rb:hover:border rb:hover:border-[#171719]!"
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useTranslation } from 'react-i18next'
|
||||
import clsx from 'clsx';
|
||||
import type { ReactShapeConfig } from '@antv/x6-react-shape';
|
||||
import { Flex } from 'antd';
|
||||
import { CheckCircleFilled, CloseCircleFilled, LoadingOutlined } from '@ant-design/icons';
|
||||
|
||||
import NodeTools from './NodeTools'
|
||||
import { useVariableList } from '../Properties/hooks/useVariableList'
|
||||
@@ -64,13 +65,23 @@ const ConditionNode: ReactShapeConfig['component'] = ({ node }) => {
|
||||
|
||||
return (
|
||||
<div className={clsx('rb:cursor-pointer rb:group rb:relative rb:h-full rb:w-full rb:p-3 rb:border rb:rounded-2xl rb:bg-[#FCFCFD] rb:shadow-[0px_2px_4px_0px_rgba(23,23,25,0.03)]', {
|
||||
'rb:border-[#171719]': data.isSelected,
|
||||
'rb:border-[#FCFCFD]': !data.isSelected
|
||||
'rb:border-[#171719]!': data.isSelected,
|
||||
'rb:border-[#FCFCFD]': !data.isSelected,
|
||||
'rb:border-[#369F21]!': !data.isSelected && data.executionStatus === 'completed',
|
||||
'rb:border-[#FF5D34]!': !data.isSelected && data.executionStatus === 'failed',
|
||||
})}>
|
||||
<NodeTools node={node} />
|
||||
<Flex align="center" gap={8} className="rb:flex-1">
|
||||
<div className={`rb:size-6 rb:bg-cover ${data.icon}`} />
|
||||
<div className="rb:wrap-break-word rb:line-clamp-1">{data.name ?? t(`workflow.${data.type}`)}</div>
|
||||
<div className="rb:wrap-break-word rb:line-clamp-1 rb:flex-1">{data.name ?? t(`workflow.${data.type}`)}</div>
|
||||
{data.executionStatus === 'completed'
|
||||
? <CheckCircleFilled style={{ color: '#369F21', fontSize: 16 }} />
|
||||
: data.executionStatus === 'failed'
|
||||
? <CloseCircleFilled style={{ color: '#FF5D34', fontSize: 16 }} />
|
||||
: data.executionStatus === 'running'
|
||||
? <LoadingOutlined style={{ color: '#5B6167', fontSize: 16 }} />
|
||||
: null
|
||||
}
|
||||
</Flex>
|
||||
|
||||
{data.type === 'question-classifier' &&
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useTranslation } from 'react-i18next'
|
||||
import clsx from 'clsx';
|
||||
import type { ReactShapeConfig } from '@antv/x6-react-shape';
|
||||
import { Flex } from 'antd';
|
||||
import { CheckCircleFilled, CloseCircleFilled, LoadingOutlined } from '@ant-design/icons';
|
||||
|
||||
import { graphNodeLibrary, edgeAttrs } from '../../constant';
|
||||
import NodeTools from './NodeTools'
|
||||
@@ -131,12 +132,22 @@ const LoopNode: ReactShapeConfig['component'] = ({ node, graph }) => {
|
||||
return (
|
||||
<div className={clsx('rb:cursor-pointer rb:group rb:relative rb:h-full rb:w-full rb:p-3 rb:border rb:rounded-2xl rb:bg-[#FCFCFD] rb:shadow-[0px_2px_4px_0px_rgba(23,23,25,0.03)]', {
|
||||
'rb:border-[#171719]': data.isSelected,
|
||||
'rb:border-[#FCFCFD]': !data.isSelected
|
||||
'rb:border-[#FCFCFD]': !data.isSelected,
|
||||
'rb:border-[#369F21]!': !data.isSelected && data.executionStatus === 'completed',
|
||||
'rb:border-[#FF5D34]!': !data.isSelected && data.executionStatus === 'failed',
|
||||
})}>
|
||||
<NodeTools node={node} />
|
||||
<Flex align="center" gap={8} className="rb:flex-1">
|
||||
<div className={`rb:size-6 rb:bg-cover ${data.icon}`} />
|
||||
<div className="rb:wrap-break-word rb:line-clamp-1">{data.name ?? t(`workflow.${data.type}`)}</div>
|
||||
<div className="rb:wrap-break-word rb:line-clamp-1 rb:flex-1">{data.name ?? t(`workflow.${data.type}`)}</div>
|
||||
{data.executionStatus === 'completed'
|
||||
? <CheckCircleFilled style={{ color: '#369F21', fontSize: 16 }} />
|
||||
: data.executionStatus === 'failed'
|
||||
? <CloseCircleFilled style={{ color: '#FF5D34', fontSize: 16 }} />
|
||||
: data.executionStatus === 'running'
|
||||
? <LoadingOutlined style={{ color: '#5B6167', fontSize: 16 }} />
|
||||
: null
|
||||
}
|
||||
</Flex>
|
||||
<div className="rb:mt-3 rb:min-h-[calc(100%-36px)] rb:w-full rb:bg-[radial-gradient(circle,#939AB1_1px,#F0F3F8_1px)] rb:shadow-[0px_2px_4px_0px_rgba(23,23,25,0.03)] rb:rounded-[10px] rb:bg-size-[12px_12px]"></div>
|
||||
</div>
|
||||
|
||||
@@ -2,6 +2,7 @@ import clsx from 'clsx';
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import type { ReactShapeConfig } from '@antv/x6-react-shape';
|
||||
import { Flex } from 'antd';
|
||||
import { CheckCircleFilled, CloseCircleFilled, LoadingOutlined } from '@ant-design/icons';
|
||||
|
||||
import NodeTools from './NodeTools'
|
||||
|
||||
@@ -11,13 +12,23 @@ const NormalNode: ReactShapeConfig['component'] = ({ node }) => {
|
||||
|
||||
return (
|
||||
<div className={clsx('rb:cursor-pointer rb:group rb:relative rb:h-full rb:w-full rb:p-3 rb:border rb:rounded-2xl rb:bg-[#FCFCFD] rb:shadow-[0px_2px_4px_0px_rgba(23,23,25,0.03)]', {
|
||||
'rb:border-[#171719]': data.isSelected,
|
||||
'rb:border-[#FCFCFD]': !data.isSelected
|
||||
'rb:border-[#171719]!': data.isSelected,
|
||||
'rb:border-[#FCFCFD]': !data.isSelected,
|
||||
'rb:border-[#369F21]!': !data.isSelected && data.executionStatus === 'completed',
|
||||
'rb:border-[#FF5D34]!': !data.isSelected && data.executionStatus === 'failed',
|
||||
})}>
|
||||
<NodeTools node={node} />
|
||||
<Flex align="center" gap={8} className="rb:flex-1">
|
||||
<div className={`rb:size-6 rb:bg-cover ${data.icon}`} />
|
||||
<div className="rb:wrap-break-word rb:line-clamp-1">{data.name ?? t(`workflow.${data.type}`)}</div>
|
||||
<div className="rb:wrap-break-word rb:line-clamp-1 rb:flex-1">{data.name ?? t(`workflow.${data.type}`)}</div>
|
||||
{data.executionStatus === 'completed'
|
||||
? <CheckCircleFilled style={{ color: '#369F21', fontSize: 16 }} />
|
||||
: data.executionStatus === 'failed'
|
||||
? <CloseCircleFilled style={{ color: '#FF5D34', fontSize: 16 }} />
|
||||
: data.executionStatus === 'running'
|
||||
? <LoadingOutlined style={{ color: '#5B6167', fontSize: 16 }} />
|
||||
: null
|
||||
}
|
||||
</Flex>
|
||||
|
||||
<div className="rb:text-[#5B6167] rb:text-[12px] rb:leading-4 rb:mt-3">{t('workflow.clickToConfigure')}</div>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 15:39:59
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-04-20 15:42:36
|
||||
* @Last Modified time: 2026-04-21 14:15:33
|
||||
*/
|
||||
import { type FC, useEffect, useState, useMemo } from "react";
|
||||
import clsx from 'clsx'
|
||||
@@ -154,7 +154,9 @@ const Properties: FC<PropertiesProps> = ({
|
||||
selectedNode?.setData({
|
||||
...nodeData,
|
||||
...allRest,
|
||||
})
|
||||
},
|
||||
// { deep: false }
|
||||
)
|
||||
}
|
||||
}, [values, selectedNode, form])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user