From a430284aa21e3ae1f0d5654e55b2ad2852519cc2 Mon Sep 17 00:00:00 2001 From: wwf <yearningwang@iqtogether.com> Date: 星期三, 04 六月 2025 15:17:49 +0800 Subject: [PATCH] 初始化 --- app/components/workflow/panel/workflow-preview.tsx | 322 +++++++++++++++++++++++++++++------------------------ 1 files changed, 177 insertions(+), 145 deletions(-) diff --git a/app/components/workflow/panel/workflow-preview.tsx b/app/components/workflow/panel/workflow-preview.tsx index 34b0ec6..210a95f 100644 --- a/app/components/workflow/panel/workflow-preview.tsx +++ b/app/components/workflow/panel/workflow-preview.tsx @@ -2,6 +2,7 @@ memo, useCallback, useEffect, + // useRef, useState, } from 'react' import { @@ -10,6 +11,7 @@ } from '@remixicon/react' import { useTranslation } from 'react-i18next' import copy from 'copy-to-clipboard' +import { useBoolean } from 'ahooks' import ResultText from '../run/result-text' import ResultPanel from '../run/result-panel' import TracingPanel from '../run/tracing-panel' @@ -20,11 +22,14 @@ import { WorkflowRunningStatus, } from '../types' +import { SimpleBtn } from '../../app/text-generate/item' import Toast from '../../base/toast' +import IterationResultPanel from '../run/iteration-result-panel' +import RetryResultPanel from '../run/retry-result-panel' import InputsPanel from './inputs-panel' import cn from '@/utils/classnames' import Loading from '@/app/components/base/loading' -import Button from '@/app/components/base/button' +import type { IterationDurationMap, NodeTracing } from '@/types/workflow' const WorkflowPreview = () => { const { t } = useTranslation() @@ -48,163 +53,190 @@ switchTab('DETAIL') }, [workflowRunningData]) - const [panelWidth, setPanelWidth] = useState(420) - const [isResizing, setIsResizing] = useState(false) + const [iterationRunResult, setIterationRunResult] = useState<NodeTracing[][]>([]) + const [retryRunResult, setRetryRunResult] = useState<NodeTracing[]>([]) + const [iterDurationMap, setIterDurationMap] = useState<IterationDurationMap>({}) + const [isShowIterationDetail, { + setTrue: doShowIterationDetail, + setFalse: doHideIterationDetail, + }] = useBoolean(false) + const [isShowRetryDetail, { + setTrue: doShowRetryDetail, + setFalse: doHideRetryDetail, + }] = useBoolean(false) - const startResizing = useCallback((e: React.MouseEvent) => { - e.preventDefault() - setIsResizing(true) - }, []) + const handleShowIterationDetail = useCallback((detail: NodeTracing[][], iterationDurationMap: IterationDurationMap) => { + setIterDurationMap(iterationDurationMap) + setIterationRunResult(detail) + doShowIterationDetail() + }, [doShowIterationDetail]) - const stopResizing = useCallback(() => { - setIsResizing(false) - }, []) + const handleRetryDetail = useCallback((detail: NodeTracing[]) => { + setRetryRunResult(detail) + doShowRetryDetail() + }, [doShowRetryDetail]) - const resize = useCallback((e: MouseEvent) => { - if (isResizing) { - const newWidth = window.innerWidth - e.clientX - if (newWidth > 420 && newWidth < 1024) - setPanelWidth(newWidth) - } - }, [isResizing]) - - useEffect(() => { - window.addEventListener('mousemove', resize) - window.addEventListener('mouseup', stopResizing) - return () => { - window.removeEventListener('mousemove', resize) - window.removeEventListener('mouseup', stopResizing) - } - }, [resize, stopResizing]) + if (isShowIterationDetail) { + return ( + <div className={` + flex flex-col w-[420px] h-full rounded-l-2xl border-[0.5px] border-gray-200 shadow-xl bg-white + `}> + <IterationResultPanel + list={iterationRunResult} + onHide={doHideIterationDetail} + onBack={doHideIterationDetail} + iterDurationMap={iterDurationMap} + /> + </div> + ) + } return ( <div className={` - relative flex h-full flex-col rounded-l-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-xl - `} - style={{ width: `${panelWidth}px` }} - > - <div - className="absolute bottom-0 left-[3px] top-1/2 z-50 h-6 w-[3px] cursor-col-resize rounded bg-gray-300" - onMouseDown={startResizing} - /> - <div className='flex items-center justify-between p-4 pb-1 text-base font-semibold text-text-primary'> + flex flex-col w-[420px] h-full rounded-l-2xl border-[0.5px] border-gray-200 shadow-xl bg-white + `}> + <div className='flex items-center justify-between p-4 pb-1 text-base font-semibold text-gray-900'> {`Test Run${!workflowRunningData?.result.sequence_number ? '' : `#${workflowRunningData?.result.sequence_number}`}`} - <div className='cursor-pointer p-1' onClick={() => handleCancelDebugAndPreviewPanel()}> - <RiCloseLine className='h-4 w-4 text-text-tertiary' /> + <div className='p-1 cursor-pointer' onClick={() => handleCancelDebugAndPreviewPanel()}> + <RiCloseLine className='w-4 h-4 text-gray-500' /> </div> </div> - <div className='relative flex grow flex-col'> - <div className='flex shrink-0 items-center border-b-[0.5px] border-divider-subtle px-4'> - {showInputsPanel && ( - <div - className={cn( - 'mr-6 cursor-pointer border-b-2 border-transparent py-3 text-[13px] font-semibold leading-[18px] text-text-tertiary', - currentTab === 'INPUT' && '!border-[rgb(21,94,239)] text-text-secondary', - )} - onClick={() => switchTab('INPUT')} - >{t('runLog.input')}</div> - )} - <div - className={cn( - 'mr-6 cursor-pointer border-b-2 border-transparent py-3 text-[13px] font-semibold leading-[18px] text-text-tertiary', - currentTab === 'RESULT' && '!border-[rgb(21,94,239)] text-text-secondary', - !workflowRunningData && '!cursor-not-allowed opacity-30', - )} - onClick={() => { - if (!workflowRunningData) - return - switchTab('RESULT') - }} - >{t('runLog.result')}</div> - <div - className={cn( - 'mr-6 cursor-pointer border-b-2 border-transparent py-3 text-[13px] font-semibold leading-[18px] text-text-tertiary', - currentTab === 'DETAIL' && '!border-[rgb(21,94,239)] text-text-secondary', - !workflowRunningData && '!cursor-not-allowed opacity-30', - )} - onClick={() => { - if (!workflowRunningData) - return - switchTab('DETAIL') - }} - >{t('runLog.detail')}</div> - <div - className={cn( - 'mr-6 cursor-pointer border-b-2 border-transparent py-3 text-[13px] font-semibold leading-[18px] text-text-tertiary', - currentTab === 'TRACING' && '!border-[rgb(21,94,239)] text-text-secondary', - !workflowRunningData && '!cursor-not-allowed opacity-30', - )} - onClick={() => { - if (!workflowRunningData) - return - switchTab('TRACING') - }} - >{t('runLog.tracing')}</div> - </div> - <div className={cn( - 'h-0 grow overflow-y-auto rounded-b-2xl bg-components-panel-bg', - (currentTab === 'RESULT' || currentTab === 'TRACING') && '!bg-background-section-burn', - )}> - {currentTab === 'INPUT' && showInputsPanel && ( - <InputsPanel onRun={() => switchTab('RESULT')} /> - )} - {currentTab === 'RESULT' && ( + <div className='grow relative flex flex-col'> + {isShowIterationDetail + ? ( + <IterationResultPanel + list={iterationRunResult} + onHide={doHideIterationDetail} + onBack={doHideIterationDetail} + iterDurationMap={iterDurationMap} + /> + ) + : ( <> - <ResultText - isRunning={workflowRunningData?.result?.status === WorkflowRunningStatus.Running || !workflowRunningData?.result} - outputs={workflowRunningData?.resultText} - allFiles={workflowRunningData?.result?.files} - error={workflowRunningData?.result?.error} - onClick={() => switchTab('DETAIL')} - /> - {(workflowRunningData?.result.status === WorkflowRunningStatus.Succeeded && workflowRunningData?.resultText && typeof workflowRunningData?.resultText === 'string') && ( - <Button - className={cn('mb-4 ml-4 space-x-1')} + <div className='shrink-0 flex items-center px-4 border-b-[0.5px] border-[rgba(0,0,0,0.05)]'> + {showInputsPanel && ( + <div + className={cn( + 'mr-6 py-3 border-b-2 border-transparent text-[13px] font-semibold leading-[18px] text-gray-400 cursor-pointer', + currentTab === 'INPUT' && '!border-[rgb(21,94,239)] text-gray-700', + )} + onClick={() => switchTab('INPUT')} + >{t('runLog.input')}</div> + )} + <div + className={cn( + 'mr-6 py-3 border-b-2 border-transparent text-[13px] font-semibold leading-[18px] text-gray-400 cursor-pointer', + currentTab === 'RESULT' && '!border-[rgb(21,94,239)] text-gray-700', + !workflowRunningData && 'opacity-30 !cursor-not-allowed', + )} onClick={() => { - const content = workflowRunningData?.resultText - if (typeof content === 'string') - copy(content) - else - copy(JSON.stringify(content)) - Toast.notify({ type: 'success', message: t('common.actionMsg.copySuccessfully') }) - }}> - <RiClipboardLine className='h-3.5 w-3.5' /> - <div>{t('common.operation.copy')}</div> - </Button> - )} + if (!workflowRunningData) + return + switchTab('RESULT') + }} + >{t('runLog.result')}</div> + <div + className={cn( + 'mr-6 py-3 border-b-2 border-transparent text-[13px] font-semibold leading-[18px] text-gray-400 cursor-pointer', + currentTab === 'DETAIL' && '!border-[rgb(21,94,239)] text-gray-700', + !workflowRunningData && 'opacity-30 !cursor-not-allowed', + )} + onClick={() => { + if (!workflowRunningData) + return + switchTab('DETAIL') + }} + >{t('runLog.detail')}</div> + <div + className={cn( + 'mr-6 py-3 border-b-2 border-transparent text-[13px] font-semibold leading-[18px] text-gray-400 cursor-pointer', + currentTab === 'TRACING' && '!border-[rgb(21,94,239)] text-gray-700', + !workflowRunningData && 'opacity-30 !cursor-not-allowed', + )} + onClick={() => { + if (!workflowRunningData) + return + switchTab('TRACING') + }} + >{t('runLog.tracing')}</div> + </div> + <div className={cn( + 'grow bg-components-panel-bg h-0 overflow-y-auto rounded-b-2xl', + (currentTab === 'RESULT' || currentTab === 'TRACING') && '!bg-background-section-burn', + )}> + {currentTab === 'INPUT' && showInputsPanel && ( + <InputsPanel onRun={() => switchTab('RESULT')} /> + )} + {currentTab === 'RESULT' && ( + <> + <ResultText + isRunning={workflowRunningData?.result?.status === WorkflowRunningStatus.Running || !workflowRunningData?.result} + outputs={workflowRunningData?.resultText} + allFiles={workflowRunningData?.result?.files as any} + error={workflowRunningData?.result?.error} + onClick={() => switchTab('DETAIL')} + /> + {(workflowRunningData?.result.status === WorkflowRunningStatus.Succeeded && workflowRunningData?.resultText && typeof workflowRunningData?.resultText === 'string') && ( + <SimpleBtn + className={cn('ml-4 mb-4 inline-flex space-x-1')} + onClick={() => { + const content = workflowRunningData?.resultText + if (typeof content === 'string') + copy(content) + else + copy(JSON.stringify(content)) + Toast.notify({ type: 'success', message: t('common.actionMsg.copySuccessfully') }) + }}> + <RiClipboardLine className='w-3.5 h-3.5' /> + <div>{t('common.operation.copy')}</div> + </SimpleBtn> + )} + </> + )} + {currentTab === 'DETAIL' && ( + <ResultPanel + inputs={workflowRunningData?.result?.inputs} + outputs={workflowRunningData?.result?.outputs} + status={workflowRunningData?.result?.status || ''} + error={workflowRunningData?.result?.error} + elapsed_time={workflowRunningData?.result?.elapsed_time} + total_tokens={workflowRunningData?.result?.total_tokens} + created_at={workflowRunningData?.result?.created_at} + created_by={(workflowRunningData?.result?.created_by as any)?.name} + steps={workflowRunningData?.result?.total_steps} + exceptionCounts={workflowRunningData?.result?.exceptions_count} + /> + )} + {currentTab === 'DETAIL' && !workflowRunningData?.result && ( + <div className='flex h-full items-center justify-center bg-components-panel-bg'> + <Loading /> + </div> + )} + {currentTab === 'TRACING' && !isShowRetryDetail && ( + <TracingPanel + className='bg-background-section-burn' + list={workflowRunningData?.tracing || []} + onShowIterationDetail={handleShowIterationDetail} + onShowRetryDetail={handleRetryDetail} + /> + )} + {currentTab === 'TRACING' && !workflowRunningData?.tracing?.length && ( + <div className='flex h-full items-center justify-center !bg-background-section-burn'> + <Loading /> + </div> + )} + { + currentTab === 'TRACING' && isShowRetryDetail && ( + <RetryResultPanel + list={retryRunResult} + onBack={doHideRetryDetail} + /> + ) + } + </div> </> )} - {currentTab === 'DETAIL' && ( - <ResultPanel - inputs={workflowRunningData?.result?.inputs} - outputs={workflowRunningData?.result?.outputs} - status={workflowRunningData?.result?.status || ''} - error={workflowRunningData?.result?.error} - elapsed_time={workflowRunningData?.result?.elapsed_time} - total_tokens={workflowRunningData?.result?.total_tokens} - created_at={workflowRunningData?.result?.created_at} - created_by={(workflowRunningData?.result?.created_by as any)?.name} - steps={workflowRunningData?.result?.total_steps} - exceptionCounts={workflowRunningData?.result?.exceptions_count} - /> - )} - {currentTab === 'DETAIL' && !workflowRunningData?.result && ( - <div className='flex h-full items-center justify-center bg-components-panel-bg'> - <Loading /> - </div> - )} - {currentTab === 'TRACING' && ( - <TracingPanel - className='bg-background-section-burn' - list={workflowRunningData?.tracing || []} - /> - )} - {currentTab === 'TRACING' && !workflowRunningData?.tracing?.length && ( - <div className='flex h-full items-center justify-center !bg-background-section-burn'> - <Loading /> - </div> - )} - </div> + </div> </div> ) -- Gitblit v1.8.0