diff --git a/web/src/components/Table/index.tsx b/web/src/components/Table/index.tsx index bb79b4bc..d6cb3c68 100644 --- a/web/src/components/Table/index.tsx +++ b/web/src/components/Table/index.tsx @@ -2,7 +2,7 @@ * @Author: ZhaoYing * @Date: 2026-02-02 15:29:46 * @Last Modified by: ZhaoYing - * @Last Modified time: 2026-03-26 14:52:23 + * @Last Modified time: 2026-04-14 17:55:15 */ /** * RbTable Component @@ -27,7 +27,7 @@ import { useTranslation } from 'react-i18next'; import { request } from '@/utils/request'; import Empty from '@/components/Empty'; -interface TablePaginationConfig { pagesize: number; page: number; } +interface TablePaginationConfig { pagesize?: number; page?: number; } /** Props interface for Table component */ interface TableComponentProps, Q = Record> extends Omit, 'pagination'> { @@ -102,7 +102,7 @@ const RbTable = forwardRef(, Q = Record setCheckResults: (appId: string, results: NodeCheckResult[]) => void getCheckResults: (appId: string) => NodeCheckResult[] + chatHistoryMap: Record + setChatHistory: (conversationId: string, history: ChatItem[]) => void + getChatHistory: (conversationId: string) => ChatItem[] } export const useWorkflowStore = create((set, get) => ({ @@ -18,4 +22,8 @@ export const useWorkflowStore = create((set, get) => ({ setCheckResults: (appId, results) => set(state => ({ checkResults: { ...state.checkResults, [appId]: results } })), getCheckResults: (appId) => get().checkResults[appId] ?? [], + chatHistoryMap: {}, + setChatHistory: (conversationId, history) => + set(state => ({ chatHistoryMap: { ...state.chatHistoryMap, [conversationId]: history } })), + getChatHistory: (conversationId) => get().chatHistoryMap[conversationId] ?? [], })) diff --git a/web/src/views/Index/index.tsx b/web/src/views/Index/index.tsx index dece9770..1e4bdcdd 100644 --- a/web/src/views/Index/index.tsx +++ b/web/src/views/Index/index.tsx @@ -120,6 +120,7 @@ const Index = () => { rowKey="id" bordered={false} scrollY="100%" + pagination={{pagesize: 10}} /> diff --git a/web/src/views/Workflow/components/Chat/Chat.tsx b/web/src/views/Workflow/components/Chat/Chat.tsx index e1a0ad95..19b06a0d 100644 --- a/web/src/views/Workflow/components/Chat/Chat.tsx +++ b/web/src/views/Workflow/components/Chat/Chat.tsx @@ -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-15 15:57:35 */ /** * Workflow Chat Component @@ -41,12 +41,15 @@ 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(({ +const Chat = forwardRef(({ // eslint-disable-line appId, graphRef, features }, ref) => { const { t } = useTranslation() const { message: messageApi } = App.useApp() + const { setChatHistory } = useWorkflowStore() + const conversationIdRef = useRef('draft') const toolbarRef = useRef(null) const [toolbarReady, setToolbarReady] = useState(false) const toolbarCallbackRef = useCallback((node: ChatToolbarRef | null) => { @@ -118,6 +121,7 @@ const Chat = forwardRef; - status?: 'completed' | 'failed', + status?: 'completed' | 'failed' | 'running', citations?: { document_id: string; file_name: string; @@ -231,6 +235,7 @@ const Chat = forwardRef { + setChatHistory(conversationIdRef.current, chatList) + }, [chatList]) + return ( diff --git a/web/src/views/Workflow/components/NodeLibrary.tsx b/web/src/views/Workflow/components/NodeLibrary.tsx index e6190adb..525c09ae 100644 --- a/web/src/views/Workflow/components/NodeLibrary.tsx +++ b/web/src/views/Workflow/components/NodeLibrary.tsx @@ -34,29 +34,24 @@ const NodeLibrary: FC<{ collapsed: boolean; handleToggle: () => void }> = ({ col > {collapsed - ? <> - {nodeLibrary.map(category => ( - <> - {category.nodes - .filter(node => node.type !== 'cycle-start' && node.type !== 'break') - .map((node, nodeIndex) => ( - -
{ - e.dataTransfer.setData('application/reactflow', node.type); - e.dataTransfer.setData('application/json', JSON.stringify(node)); - }} - > -
-
- - )) - } - - ))} - + ? nodeLibrary.flatMap(category => + category.nodes + .filter(node => node.type !== 'cycle-start' && node.type !== 'break') + .map(node => ( + +
{ + e.dataTransfer.setData('application/reactflow', node.type); + e.dataTransfer.setData('application/json', JSON.stringify(node)); + }} + > +
+
+ + )) + ) : nodeLibrary.map(category => (
void }> = ({ col {category.nodes .filter(node => node.type !== 'cycle-start' && node.type !== 'break') - .map((node, nodeIndex) => ( + .map((node) => ( { return (
-
{data.name ?? t(`workflow.${data.type}`)}
+
{data.name ?? t(`workflow.${data.type}`)}
+ {data.executionStatus === 'completed' + ? + : data.executionStatus === 'failed' + ? + : data.executionStatus === 'running' + ? + : null + } {data.type === 'question-classifier' && diff --git a/web/src/views/Workflow/components/Nodes/LoopNode.tsx b/web/src/views/Workflow/components/Nodes/LoopNode.tsx index ca0eaeff..c540db76 100644 --- a/web/src/views/Workflow/components/Nodes/LoopNode.tsx +++ b/web/src/views/Workflow/components/Nodes/LoopNode.tsx @@ -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 (
-
{data.name ?? t(`workflow.${data.type}`)}
+
{data.name ?? t(`workflow.${data.type}`)}
+ {data.executionStatus === 'completed' + ? + : data.executionStatus === 'failed' + ? + : data.executionStatus === 'running' + ? + : null + }
diff --git a/web/src/views/Workflow/components/Nodes/NormalNode.tsx b/web/src/views/Workflow/components/Nodes/NormalNode.tsx index f947d004..ce936be9 100644 --- a/web/src/views/Workflow/components/Nodes/NormalNode.tsx +++ b/web/src/views/Workflow/components/Nodes/NormalNode.tsx @@ -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 (
-
{data.name ?? t(`workflow.${data.type}`)}
+
{data.name ?? t(`workflow.${data.type}`)}
+ {data.executionStatus === 'completed' + ? + : data.executionStatus === 'failed' + ? + : data.executionStatus === 'running' + ? + : null + }
{t('workflow.clickToConfigure')}
diff --git a/web/src/views/Workflow/hooks/useWorkflowGraph.ts b/web/src/views/Workflow/hooks/useWorkflowGraph.ts index 535a4eb0..9f05cd27 100644 --- a/web/src/views/Workflow/hooks/useWorkflowGraph.ts +++ b/web/src/views/Workflow/hooks/useWorkflowGraph.ts @@ -2,7 +2,7 @@ * @Author: ZhaoYing * @Date: 2026-02-03 15:17:48 * @Last Modified by: ZhaoYing - * @Last Modified time: 2026-04-14 17:43:14 + * @Last Modified time: 2026-04-15 16:02:49 */ import { Clipboard, Graph, Keyboard, MiniMap, Node, Snapline, type Edge } from '@antv/x6'; import { register } from '@antv/x6-react-shape'; @@ -18,6 +18,7 @@ import type { FeaturesConfigForm } from '@/views/ApplicationConfig/types'; import { conditionNodeHeight, conditionNodeItemHeight, conditionNodePortItemArgsY, defaultAbsolutePortGroups, defaultPortItems, edgeAttrs, edgeHoverTool, edge_color, edge_selected_color, edge_width, graphNodeLibrary, nodeLibrary, nodeRegisterLibrary, nodeWidth, notesConfig, portAttrs, portItemArgsY, portMarkup, portTextAttrs, unknownNode } from '../constant'; import type { ChatVariable, NodeProperties, WorkflowConfig } from '../types'; import { calcConditionNodeTotalHeight, getConditionNodeCasePortY } from '../utils'; +import { useWorkflowStore } from '@/store/workflow'; /** * Props for useWorkflowGraph hook @@ -94,6 +95,8 @@ export const useWorkflowGraph = ({ const { message } = App.useApp(); const { t } = useTranslation() const { user } = useUser(); + const { chatHistoryMap } = useWorkflowStore() + const chatHistory = Object.values(chatHistoryMap).at(-1) ?? [] // Refs const graphRef = useRef(); @@ -1446,6 +1449,31 @@ export const useWorkflowGraph = ({ } } } + useEffect(() => { + if (!graphRef.current) return; + const nodes = graphRef.current.getNodes(); + + const lastWithSub = [...chatHistory].reverse().find(item => item.subContent?.length); + // Reset all node execution status first + nodes.forEach(node => { + const data = node.getData(); + if (typeof data.status === 'string') { + node.setData({ ...data, executionStatus: undefined }); + } + }); + if (!lastWithSub?.subContent) return; + // Build a nodeId -> status map first + const statusMap: Record = {}; + lastWithSub.subContent.forEach(sub => { + if (typeof sub.status === 'string') { + statusMap[sub.node_id] = sub.status; + const node = nodes.find(n => n.getData()?.id === sub.node_id); + if (node) { + node.setData({ ...node.getData(), executionStatus: sub.status }); + } + } + }); + }, [chatHistory, graphRef.current]); return { config,