diff --git a/web/public/iconfont.js b/web/public/iconfont.js index 1516f1700..f514c4cfd 100644 --- a/web/public/iconfont.js +++ b/web/public/iconfont.js @@ -1,5 +1,6 @@ (window._iconfont_svg_string_4909832 = '' + + '' + '' + '' + '' + diff --git a/web/src/components/originui/timeline.tsx b/web/src/components/originui/timeline.tsx index d1090aeaa..4c1b48526 100644 --- a/web/src/components/originui/timeline.tsx +++ b/web/src/components/originui/timeline.tsx @@ -246,7 +246,7 @@ const CustomTimeline = ({ orientation = 'horizontal', lineStyle = 'solid', lineColor = 'var(--text-secondary)', - indicatorColor = 'var(--accent-primary)', + indicatorColor = 'rgb(var(--accent-primary))', defaultValue = 1, className, activeStyle, diff --git a/web/src/hooks/logic-hooks/navigate-hooks.ts b/web/src/hooks/logic-hooks/navigate-hooks.ts index 8ca2ab5b6..642b8c3c0 100644 --- a/web/src/hooks/logic-hooks/navigate-hooks.ts +++ b/web/src/hooks/logic-hooks/navigate-hooks.ts @@ -1,3 +1,4 @@ +import { NavigateToDataflowResultProps } from '@/pages/dataflow-result/interface'; import { Routes } from '@/routes'; import { useCallback } from 'react'; import { useNavigate, useParams, useSearchParams } from 'umi'; @@ -23,6 +24,13 @@ export const useNavigatePage = () => { [navigate], ); + const navigateToDataFile = useCallback( + (id: string) => () => { + navigate(`${Routes.DatasetBase}${Routes.DatasetBase}/${id}`); + }, + [navigate], + ); + const navigateToHome = useCallback(() => { navigate(Routes.Root); }, [navigate]); @@ -133,10 +141,16 @@ export const useNavigatePage = () => { ); const navigateToDataflowResult = useCallback( - (id: string, knowledgeId: string, doc_id?: string) => () => { + (props: NavigateToDataflowResultProps) => () => { + let params: string[] = []; + Object.keys(props).forEach((key) => { + if (props[key]) { + params.push(`${key}=${props[key]}`); + } + }); navigate( // `${Routes.ParsedResult}/${id}?${QueryStringMap.KnowledgeId}=${knowledgeId}`, - `${Routes.DataflowResult}?id=${id}&doc_id=${doc_id}&${QueryStringMap.KnowledgeId}=${knowledgeId}&type=dataflow`, + `${Routes.DataflowResult}?${params.join('&')}`, ); }, [navigate], @@ -163,5 +177,6 @@ export const useNavigatePage = () => { navigateToOldProfile, navigateToDataflowResult, navigateToDataflow, + navigateToDataFile, }; }; diff --git a/web/src/pages/agent/canvas/edge/index.tsx b/web/src/pages/agent/canvas/edge/index.tsx index 0fa23b3b9..a31c98abe 100644 --- a/web/src/pages/agent/canvas/edge/index.tsx +++ b/web/src/pages/agent/canvas/edge/index.tsx @@ -39,7 +39,9 @@ function InnerButtonEdge({ targetPosition, }); const selectedStyle = useMemo(() => { - return selected ? { strokeWidth: 1, stroke: 'var(--accent-primary)' } : {}; + return selected + ? { strokeWidth: 1, stroke: 'rgb(var(--accent-primary))' } + : {}; }, [selected]); const onEdgeClick = () => { @@ -56,7 +58,7 @@ function InnerButtonEdge({ let index = idx - 1; while (index >= 0) { if (path[index] === source) { - return { strokeWidth: 1, stroke: 'var(--accent-primary)' }; + return { strokeWidth: 1, stroke: 'rgb(var(--accent-primary))' }; } index--; } diff --git a/web/src/pages/chunk/parsed-result/add-knowledge/components/knowledge-chunk/index.tsx b/web/src/pages/chunk/parsed-result/add-knowledge/components/knowledge-chunk/index.tsx index f5b1b751a..d90147819 100644 --- a/web/src/pages/chunk/parsed-result/add-knowledge/components/knowledge-chunk/index.tsx +++ b/web/src/pages/chunk/parsed-result/add-knowledge/components/knowledge-chunk/index.tsx @@ -72,7 +72,7 @@ const Chunk = () => { chunkUpdatingVisible, documentId, } = useUpdateChunk(); - const { navigateToDataset, getQueryString, navigateToDatasetList } = + const { navigateToDataFile, getQueryString, navigateToDatasetList } = useNavigatePage(); const fileUrl = useGetDocumentUrl(); useEffect(() => { @@ -188,7 +188,7 @@ const Chunk = () => { diff --git a/web/src/pages/data-flow/log-sheet/index.tsx b/web/src/pages/data-flow/log-sheet/index.tsx index 6dba8ccb2..3ef574ec6 100644 --- a/web/src/pages/data-flow/log-sheet/index.tsx +++ b/web/src/pages/data-flow/log-sheet/index.tsx @@ -6,8 +6,10 @@ import { SheetHeader, SheetTitle, } from '@/components/ui/sheet'; +import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; import { IModalProps } from '@/interfaces/common'; import { cn } from '@/lib/utils'; +import { PipelineResultSearchParams } from '@/pages/dataflow-result/constant'; import { ArrowUpRight, CirclePause, @@ -41,7 +43,7 @@ export function LogSheet({ const { t } = useTranslation(); const { handleDownloadJson } = useDownloadOutput(logs); - + const { navigateToDataflowResult } = useNavigatePage(); return ( {t('flow.log')} - diff --git a/web/src/pages/dataflow-result/components/chunk-result-bar/index.tsx b/web/src/pages/dataflow-result/components/chunk-result-bar/index.tsx index 9e1cd57ad..875e51596 100644 --- a/web/src/pages/dataflow-result/components/chunk-result-bar/index.tsx +++ b/web/src/pages/dataflow-result/components/chunk-result-bar/index.tsx @@ -7,28 +7,17 @@ import { ChunkTextMode } from '../../constant'; interface ChunkResultBarProps { changeChunkTextMode: React.Dispatch>; createChunk: (text: string) => void; + isReadonly: boolean; } -export default ({ changeChunkTextMode, createChunk }: ChunkResultBarProps) => { +export default ({ + changeChunkTextMode, + createChunk, + isReadonly, +}: ChunkResultBarProps) => { const { t } = useTranslate('chunk'); const [textSelectValue, setTextSelectValue] = useState( ChunkTextMode.Full, ); - // const handleFilterChange = (e: string | number) => { - // const value = e === -1 ? undefined : (e as number); - // selectAllChunk(false); - // handleSetAvailable(value); - // }; - // const filterContent = ( - //
- // - //
- // {t('all')} - // {t('enabled')} - // {t('disabled')} - //
- //
- //
- // ); const textSelectOptions = [ { label: t(ChunkTextMode.Full), value: ChunkTextMode.Full }, { label: t(ChunkTextMode.Ellipse), value: ChunkTextMode.Ellipse }, @@ -57,31 +46,15 @@ export default ({ changeChunkTextMode, createChunk }: ChunkResultBarProps) => { ))} - {/* } - onChange={handleInputChange} - value={searchString} - /> - - - - - - {filterContent} - - */} - + {!isReadonly && ( + + )} ); }; diff --git a/web/src/pages/dataflow-result/components/parse-editer/hook.ts b/web/src/pages/dataflow-result/components/parse-editer/hook.ts new file mode 100644 index 000000000..77acbbaf6 --- /dev/null +++ b/web/src/pages/dataflow-result/components/parse-editer/hook.ts @@ -0,0 +1,30 @@ +import { useEffect, useRef, useState } from 'react'; +import { IJsonContainerProps, IObjContainerProps } from './interface'; + +export const useParserInit = ({ + initialValue, +}: { + initialValue: + | Pick + | Pick; +}) => { + const [content, setContent] = useState(initialValue); + + useEffect(() => { + setContent(initialValue); + console.log('initialValue json parse', initialValue); + }, [initialValue]); + + const [activeEditIndex, setActiveEditIndex] = useState( + undefined, + ); + const editDivRef = useRef(null); + + return { + content, + setContent, + activeEditIndex, + setActiveEditIndex, + editDivRef, + }; +}; diff --git a/web/src/pages/dataflow-result/components/parse-editer/index.tsx b/web/src/pages/dataflow-result/components/parse-editer/index.tsx index fe23f6e39..799cb11c9 100644 --- a/web/src/pages/dataflow-result/components/parse-editer/index.tsx +++ b/web/src/pages/dataflow-result/components/parse-editer/index.tsx @@ -1,22 +1,8 @@ import { CheckedState } from '@radix-ui/react-checkbox'; -import { ChunkTextMode } from '../../constant'; -import { ArrayContainer, parserKeyMap } from './json-parser'; +import { FormatPreserveEditorProps } from './interface'; +import { ArrayContainer } from './json-parser'; import { ObjectContainer } from './object-parser'; -interface FormatPreserveEditorProps { - initialValue: { - key: keyof typeof parserKeyMap | 'text' | 'html'; - type: string; - value: Array<{ [key: string]: string }>; - }; - onSave: (value: any) => void; - className?: string; - isSelect?: boolean; - isDelete?: boolean; - isChunck?: boolean; - handleCheckboxClick?: (id: string | number, checked: boolean) => void; - selectedChunkIds?: string[]; - textMode?: ChunkTextMode; -} + const FormatPreserveEditor = ({ initialValue, onSave, @@ -25,6 +11,8 @@ const FormatPreserveEditor = ({ handleCheckboxClick, selectedChunkIds, textMode, + clickChunk, + isReadonly, }: FormatPreserveEditorProps) => { console.log('initialValue', initialValue); @@ -42,6 +30,7 @@ const FormatPreserveEditor = ({
{['json', 'chunks'].includes(initialValue.key) && ( )} {['text', 'html'].includes(initialValue.key) && ( )}
diff --git a/web/src/pages/dataflow-result/components/parse-editer/interface.ts b/web/src/pages/dataflow-result/components/parse-editer/interface.ts new file mode 100644 index 000000000..e45d20972 --- /dev/null +++ b/web/src/pages/dataflow-result/components/parse-editer/interface.ts @@ -0,0 +1,65 @@ +import { CheckedState } from '@radix-ui/react-checkbox'; +import { ChunkTextMode } from '../../constant'; +import { IChunk } from '../../interface'; +import { parserKeyMap } from './json-parser'; + +export interface FormatPreserveEditorProps { + initialValue: { + key: keyof typeof parserKeyMap | 'text' | 'html'; + type: string; + value: Array<{ [key: string]: string }>; + }; + onSave: (value: any) => void; + className?: string; + isSelect?: boolean; + isDelete?: boolean; + isChunck?: boolean; + handleCheckboxClick?: (id: string | number, checked: boolean) => void; + selectedChunkIds?: string[]; + textMode?: ChunkTextMode; + clickChunk: (chunk: IChunk) => void; + isReadonly: boolean; +} + +export type IJsonContainerProps = { + initialValue: { + key: keyof typeof parserKeyMap; + type: string; + value: { + [key: string]: string; + }[]; + }; + isChunck?: boolean; + handleCheck: (e: CheckedState, index: number) => void; + selectedChunkIds: string[] | undefined; + unescapeNewlines: (text: string) => string; + escapeNewlines: (text: string) => string; + onSave: (data: { + value: { + text: string; + }[]; + key: string; + type: string; + }) => void; + className?: string; + textMode?: ChunkTextMode; + clickChunk: (chunk: IChunk) => void; + isReadonly: boolean; +}; + +export type IObjContainerProps = { + initialValue: { + key: string; + type: string; + value: string; + }; + isChunck?: boolean; + handleCheck: (e: CheckedState, index: number) => void; + unescapeNewlines: (text: string) => string; + escapeNewlines: (text: string) => string; + onSave: (data: { value: string; key: string; type: string }) => void; + className?: string; + textMode?: ChunkTextMode; + clickChunk: (chunk: IChunk) => void; + isReadonly: boolean; +}; diff --git a/web/src/pages/dataflow-result/components/parse-editer/json-parser.tsx b/web/src/pages/dataflow-result/components/parse-editer/json-parser.tsx index c43a6cb73..33b988f5a 100644 --- a/web/src/pages/dataflow-result/components/parse-editer/json-parser.tsx +++ b/web/src/pages/dataflow-result/components/parse-editer/json-parser.tsx @@ -1,37 +1,16 @@ import { Checkbox } from '@/components/ui/checkbox'; import { cn } from '@/lib/utils'; -import { CheckedState } from '@radix-ui/react-checkbox'; -import { useCallback, useEffect, useRef, useState } from 'react'; +import { useCallback, useEffect } from 'react'; import { ChunkTextMode } from '../../constant'; import styles from '../../index.less'; +import { useParserInit } from './hook'; +import { IJsonContainerProps } from './interface'; export const parserKeyMap = { json: 'text', - chunks: 'content_with_weight', + chunks: 'text', }; -type IProps = { - initialValue: { - key: keyof typeof parserKeyMap; - type: string; - value: { - [key: string]: string; - }[]; - }; - isChunck?: boolean; - handleCheck: (e: CheckedState, index: number) => void; - selectedChunkIds: string[] | undefined; - unescapeNewlines: (text: string) => string; - escapeNewlines: (text: string) => string; - onSave: (data: { - value: { - text: string; - }[]; - key: string; - type: string; - }) => void; - className?: string; - textMode?: ChunkTextMode; -}; -export const ArrayContainer = (props: IProps) => { + +export const ArrayContainer = (props: IJsonContainerProps) => { const { initialValue, isChunck, @@ -42,29 +21,27 @@ export const ArrayContainer = (props: IProps) => { onSave, className, textMode, + clickChunk, + isReadonly, } = props; - const [content, setContent] = useState(initialValue); + const { + content, + setContent, + activeEditIndex, + setActiveEditIndex, + editDivRef, + } = useParserInit({ initialValue }); - useEffect(() => { - setContent(initialValue); - console.log('initialValue json parse', initialValue); - }, [initialValue]); - - const [activeEditIndex, setActiveEditIndex] = useState( - undefined, - ); - const editDivRef = useRef(null); const handleEdit = useCallback( (e?: any, index?: number) => { - console.log(e, e.target.innerText); setContent((pre) => ({ ...pre, value: pre.value.map((item, i) => { if (i === index) { return { ...item, - [parserKeyMap[content.key]]: e.target.innerText, + [parserKeyMap[content.key]]: unescapeNewlines(e.target.innerText), }; } return item; @@ -76,14 +53,13 @@ export const ArrayContainer = (props: IProps) => { ); const handleSave = useCallback( (e: any) => { - console.log(e, e.target.innerText); const saveData = { ...content, value: content.value?.map((item, index) => { if (index === activeEditIndex) { return { ...item, - [parserKeyMap[content.key]]: unescapeNewlines(e.target.innerText), + [parserKeyMap[content.key]]: e.target.innerText, }; } else { return item; @@ -99,8 +75,9 @@ export const ArrayContainer = (props: IProps) => { useEffect(() => { if (activeEditIndex !== undefined && editDivRef.current) { editDivRef.current.focus(); - editDivRef.current.textContent = - content.value[activeEditIndex][parserKeyMap[content.key]]; + editDivRef.current.textContent = escapeNewlines( + content.value[activeEditIndex][parserKeyMap[content.key]], + ); } }, [activeEditIndex, content]); @@ -119,7 +96,7 @@ export const ArrayContainer = (props: IProps) => { : '' } > - {isChunck && ( + {isChunck && !isReadonly && ( { handleCheck(e, index); @@ -132,16 +109,8 @@ export const ArrayContainer = (props: IProps) => { {activeEditIndex === index && (
{ )} key={index} onClick={(e) => { - handleEdit(e, index); + clickChunk(item); + if (!isReadonly) { + handleEdit(e, index); + } }} > {escapeNewlines(item[parserKeyMap[content.key]])} diff --git a/web/src/pages/dataflow-result/components/parse-editer/object-parser.tsx b/web/src/pages/dataflow-result/components/parse-editer/object-parser.tsx index dbd3cec74..938aed6e3 100644 --- a/web/src/pages/dataflow-result/components/parse-editer/object-parser.tsx +++ b/web/src/pages/dataflow-result/components/parse-editer/object-parser.tsx @@ -1,24 +1,10 @@ import { cn } from '@/lib/utils'; -import { CheckedState } from '@radix-ui/react-checkbox'; -import { useCallback, useEffect, useRef, useState } from 'react'; +import { useCallback, useEffect } from 'react'; import { ChunkTextMode } from '../../constant'; import styles from '../../index.less'; - -type IProps = { - initialValue: { - key: string; - type: string; - value: string; - }; - isChunck?: boolean; - handleCheck: (e: CheckedState, index: number) => void; - unescapeNewlines: (text: string) => string; - escapeNewlines: (text: string) => string; - onSave: (data: { value: string; key: string; type: string }) => void; - className?: string; - textMode?: ChunkTextMode; -}; -export const ObjectContainer = (props: IProps) => { +import { useParserInit } from './hook'; +import { IObjContainerProps } from './interface'; +export const ObjectContainer = (props: IObjContainerProps) => { const { initialValue, isChunck, @@ -27,36 +13,34 @@ export const ObjectContainer = (props: IProps) => { onSave, className, textMode, + clickChunk, + isReadonly, } = props; - const [content, setContent] = useState(initialValue); + const { + content, + setContent, + activeEditIndex, + setActiveEditIndex, + editDivRef, + } = useParserInit({ initialValue }); - useEffect(() => { - setContent(initialValue); - console.log('initialValue object parse', initialValue); - }, [initialValue]); - - const [activeEditIndex, setActiveEditIndex] = useState( - undefined, - ); - const editDivRef = useRef(null); const handleEdit = useCallback( (e?: any) => { - console.log(e, e.target.innerText); setContent((pre) => ({ ...pre, - value: e.target.innerText, + value: escapeNewlines(e.target.innerText), })); setActiveEditIndex(1); }, [setContent, setActiveEditIndex], ); + const handleSave = useCallback( (e: any) => { - console.log(e, e.target.innerText); const saveData = { ...content, - value: unescapeNewlines(e.target.innerText), + value: e.target.innerText, }; onSave(saveData); setActiveEditIndex(undefined); @@ -83,7 +67,7 @@ export const ObjectContainer = (props: IProps) => { {activeEditIndex && (
{ }, )} onClick={(e) => { - handleEdit(e); + clickChunk(content); + if (!isReadonly) { + handleEdit(e); + } }} > {escapeNewlines(content.value)} diff --git a/web/src/pages/dataflow-result/components/time-line/index.tsx b/web/src/pages/dataflow-result/components/time-line/index.tsx index e47992f48..92a1d236d 100644 --- a/web/src/pages/dataflow-result/components/time-line/index.tsx +++ b/web/src/pages/dataflow-result/components/time-line/index.tsx @@ -83,8 +83,8 @@ const TimelineDataFlow = ({ nodeSize={24} activeStyle={{ nodeSize: 30, - iconColor: 'var(--accent-primary)', - textColor: 'var(--accent-primary)', + iconColor: 'rgb(var(--accent-primary))', + textColor: 'rgb(var(--accent-primary))', }} />
diff --git a/web/src/pages/dataflow-result/constant.ts b/web/src/pages/dataflow-result/constant.ts index c1aa6ffb6..783f1745f 100644 --- a/web/src/pages/dataflow-result/constant.ts +++ b/web/src/pages/dataflow-result/constant.ts @@ -9,6 +9,15 @@ export enum TimelineNodeType { contextGenerator = 'extractor', titleSplitter = 'hierarchicalMerger', characterSplitter = 'splitter', - tokenizer = 'indexer', + tokenizer = 'tokenizer', end = 'end', } + +export enum PipelineResultSearchParams { + DocumentId = 'doc_id', + KnowledgeId = 'knowledgeId', + Type = 'type', + IsReadOnly = 'is_read_only', + AgentId = 'agent_id', + AgentTitle = 'agent_title', +} diff --git a/web/src/pages/dataflow-result/hooks.ts b/web/src/pages/dataflow-result/hooks.ts index 649bff6dd..9ef8c9598 100644 --- a/web/src/pages/dataflow-result/hooks.ts +++ b/web/src/pages/dataflow-result/hooks.ts @@ -1,10 +1,6 @@ import { TimelineNode } from '@/components/originui/timeline'; import message from '@/components/ui/message'; -import { - useCreateChunk, - useDeleteChunk, - useSelectChunkList, -} from '@/hooks/chunk-hooks'; +import { useCreateChunk, useDeleteChunk } from '@/hooks/chunk-hooks'; import { useSetModalState, useShowDeleteConfirm } from '@/hooks/common-hooks'; import { useGetKnowledgeSearchParams } from '@/hooks/route-hook'; import { IChunk } from '@/interfaces/database/knowledge'; @@ -18,7 +14,11 @@ import { useCallback, useMemo, useState } from 'react'; import { IHighlight } from 'react-pdf-highlighter'; import { useParams, useSearchParams } from 'umi'; import { ITimelineNodeObj, TimelineNodeObj } from './components/time-line'; -import { ChunkTextMode, TimelineNodeType } from './constant'; +import { + ChunkTextMode, + PipelineResultSearchParams, + TimelineNodeType, +} from './constant'; import { IDslComponent, IPipelineFileLogDetail } from './interface'; export const useFetchPipelineFileLogDetail = (props?: { @@ -55,28 +55,21 @@ export const useFetchPipelineFileLogDetail = (props?: { }; export const useHandleChunkCardClick = () => { - const [selectedChunkId, setSelectedChunkId] = useState(''); + const [selectedChunk, setSelectedChunk] = useState(); - const handleChunkCardClick = useCallback((chunkId: string) => { - setSelectedChunkId(chunkId); + const handleChunkCardClick = useCallback((chunk: IChunk) => { + console.log('click-chunk-->', chunk); + setSelectedChunk(chunk); }, []); - return { handleChunkCardClick, selectedChunkId }; + return { handleChunkCardClick, selectedChunk }; }; -export const useGetSelectedChunk = (selectedChunkId: string) => { - const data = useSelectChunkList(); - return ( - data?.data?.find((x) => x.chunk_id === selectedChunkId) ?? ({} as IChunk) - ); -}; - -export const useGetChunkHighlights = (selectedChunkId: string) => { +export const useGetChunkHighlights = (selectedChunk?: IChunk) => { const [size, setSize] = useState({ width: 849, height: 1200 }); - const selectedChunk: IChunk = useGetSelectedChunk(selectedChunkId); const highlights: IHighlight[] = useMemo(() => { - return buildChunkHighlights(selectedChunk, size); + return selectedChunk ? buildChunkHighlights(selectedChunk, size) : []; }, [selectedChunk, size]); const setWidthAndHeight = useCallback((width: number, height: number) => { @@ -276,3 +269,23 @@ export const useTimelineDataFlow = (data: IPipelineFileLogDetail) => { timelineNodes, }; }; + +export const useGetPipelineResultSearchParams = () => { + const [currentQueryParameters] = useSearchParams(); + const is_read_only = currentQueryParameters.get( + PipelineResultSearchParams.IsReadOnly, + ) as 'true' | 'false'; + console.log('is_read_only', is_read_only); + return { + type: currentQueryParameters.get(PipelineResultSearchParams.Type) || '', + documentId: + currentQueryParameters.get(PipelineResultSearchParams.DocumentId) || '', + knowledgeId: + currentQueryParameters.get(PipelineResultSearchParams.KnowledgeId) || '', + isReadOnly: is_read_only === 'true', + agentId: + currentQueryParameters.get(PipelineResultSearchParams.AgentId) || '', + agentTitle: + currentQueryParameters.get(PipelineResultSearchParams.AgentTitle) || '', + }; +}; diff --git a/web/src/pages/dataflow-result/index.tsx b/web/src/pages/dataflow-result/index.tsx index 47d1e8e4a..3da8581be 100644 --- a/web/src/pages/dataflow-result/index.tsx +++ b/web/src/pages/dataflow-result/index.tsx @@ -5,6 +5,7 @@ import DocumentPreview from './components/document-preview'; import { useFetchPipelineFileLogDetail, useGetChunkHighlights, + useGetPipelineResultSearchParams, useHandleChunkCardClick, useRerunDataflow, useTimelineDataFlow, @@ -25,10 +26,7 @@ import { } from '@/components/ui/breadcrumb'; import { Button } from '@/components/ui/button'; import { Modal } from '@/components/ui/modal/modal'; -import { - QueryStringMap, - useNavigatePage, -} from '@/hooks/logic-hooks/navigate-hooks'; +import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; import { useGetKnowledgeSearchParams } from '@/hooks/route-hook'; import { useGetDocumentUrl } from './components/document-preview/hooks'; import TimelineDataFlow from './components/time-line'; @@ -38,22 +36,29 @@ import { IDslComponent } from './interface'; import ParserContainer from './parser'; const Chunk = () => { + const { isReadOnly, knowledgeId, agentId, agentTitle } = + useGetPipelineResultSearchParams(); + const { data: { documentInfo }, } = useFetchNextChunkList(); - const { selectedChunkId } = useHandleChunkCardClick(); + const { selectedChunk, handleChunkCardClick } = useHandleChunkCardClick(); const [activeStepId, setActiveStepId] = useState(2); const { data: dataset } = useFetchPipelineFileLogDetail(); const { t } = useTranslation(); const { timelineNodes } = useTimelineDataFlow(dataset); - const { navigateToDataset, getQueryString, navigateToDatasetList } = - useNavigatePage(); + const { + navigateToDataset, + navigateToDatasetList, + navigateToAgents, + navigateToDataflow, + } = useNavigatePage(); const fileUrl = useGetDocumentUrl(); const { highlights, setWidthAndHeight } = - useGetChunkHighlights(selectedChunkId); + useGetChunkHighlights(selectedChunk); const fileType = useMemo(() => { switch (documentInfo?.type) { @@ -77,6 +82,7 @@ const Chunk = () => { } = useRerunDataflow({ data: dataset, }); + const handleStepChange = (id: number | string, step: TimelineNode) => { if (isChange) { Modal.show({ @@ -135,18 +141,32 @@ const Chunk = () => { - - {t('knowledgeDetails.dataset')} + { + if (knowledgeId) { + navigateToDatasetList(); + } + if (agentId) { + navigateToAgents(); + } + }} + > + {knowledgeId ? t('knowledgeDetails.dataset') : t('header.flow')} { + if (knowledgeId) { + navigateToDataset(knowledgeId)(); + } + if (agentId) { + navigateToDataflow(agentId)(); + } + }} > - {t('knowledgeDetails.overview')} + {knowledgeId ? t('knowledgeDetails.overview') : agentTitle} @@ -194,8 +214,10 @@ const Chunk = () => { {/* {currentTimeNode?.type === TimelineNodeType.parser && ( */} {(currentTimeNode?.type === TimelineNodeType.parser || currentTimeNode?.type === TimelineNodeType.characterSplitter || - currentTimeNode?.type === TimelineNodeType.titleSplitter) && ( + currentTimeNode?.type === TimelineNodeType.titleSplitter || + currentTimeNode?.type === TimelineNodeType.contextGenerator) && ( { key: string; } } + clickChunk={handleChunkCardClick} reRunFunc={handleReRunFunc} /> )} diff --git a/web/src/pages/dataflow-result/interface.ts b/web/src/pages/dataflow-result/interface.ts index 66163d76b..81dd55b65 100644 --- a/web/src/pages/dataflow-result/interface.ts +++ b/web/src/pages/dataflow-result/interface.ts @@ -1,3 +1,5 @@ +import { PipelineResultSearchParams } from './constant'; + interface ComponentParams { debug_inputs: Record; delay_after_error: number; @@ -60,3 +62,19 @@ export interface IPipelineFileLogDetail { update_date: string; update_time: number; } + +export interface IChunk { + positions: number[][]; + image_id: string; + text: string; +} + +export interface NavigateToDataflowResultProps { + id: string; + [PipelineResultSearchParams.KnowledgeId]?: string; + [PipelineResultSearchParams.DocumentId]: string; + [PipelineResultSearchParams.AgentId]?: string; + [PipelineResultSearchParams.AgentTitle]?: string; + [PipelineResultSearchParams.IsReadOnly]?: string; + [PipelineResultSearchParams.Type]: string; +} diff --git a/web/src/pages/dataflow-result/parser.tsx b/web/src/pages/dataflow-result/parser.tsx index d705e3540..41f4e2a39 100644 --- a/web/src/pages/dataflow-result/parser.tsx +++ b/web/src/pages/dataflow-result/parser.tsx @@ -10,17 +10,28 @@ import FormatPreserEditor from './components/parse-editer'; import RerunButton from './components/rerun-button'; import { TimelineNodeType } from './constant'; import { useChangeChunkTextMode } from './hooks'; -import { IDslComponent } from './interface'; +import { IChunk, IDslComponent } from './interface'; interface IProps { + isReadonly: boolean; isChange: boolean; setIsChange: (isChange: boolean) => void; step?: TimelineNode; data: { value: IDslComponent; key: string }; reRunLoading: boolean; + clickChunk: (chunk: IChunk) => void; reRunFunc: (data: { value: IDslComponent; key: string }) => void; } const ParserContainer = (props: IProps) => { - const { isChange, setIsChange, step, data, reRunFunc, reRunLoading } = props; + const { + isChange, + setIsChange, + step, + data, + reRunFunc, + reRunLoading, + clickChunk, + isReadonly, + } = props; const { t } = useTranslation(); const [selectedChunkIds, setSelectedChunkIds] = useState([]); const { changeChunkTextMode, textMode } = useChangeChunkTextMode(); @@ -83,13 +94,13 @@ const ParserContainer = (props: IProps) => { initialText.value = initialText.value.filter( (item: any, index: number) => !selectedChunkIds.includes(index + ''), ); + setIsChange(true); setSelectedChunkIds([]); } - }, [selectedChunkIds, initialText]); + }, [selectedChunkIds, initialText, setIsChange]); const handleCheckboxClick = useCallback( (id: string | number, checked: boolean) => { - console.log('handleCheckboxClick', id, checked, selectedChunkIds); setSelectedChunkIds((prev) => { if (checked) { return [...prev, id.toString()]; @@ -116,20 +127,18 @@ const ParserContainer = (props: IProps) => { const handleCreateChunk = useCallback( (text: string) => { - console.log('handleCreateChunk', text); const newText = [...initialText.value, { text: text || ' ' }]; setInitialText({ ...initialText, value: newText, }); - console.log('newText', newText, initialText); }, [initialText], ); return ( <> - {isChange && ( + {isChange && !isReadonly && (
{ {isChunck && (
- + {!isReadonly && ( + + )} @@ -189,15 +201,14 @@ const ParserContainer = (props: IProps) => { diff --git a/web/src/pages/dataset/dataset-overview/hook.ts b/web/src/pages/dataset/dataset-overview/hook.ts index 751483287..164910ef3 100644 --- a/web/src/pages/dataset/dataset-overview/hook.ts +++ b/web/src/pages/dataset/dataset-overview/hook.ts @@ -1,3 +1,4 @@ +import { useHandleFilterSubmit } from '@/components/list-filter-bar/use-handle-filter-submit'; import { useGetPaginationWithRouter, useHandleSearchChange, @@ -32,6 +33,7 @@ const useFetchFileLogList = () => { const [searchParams] = useSearchParams(); const { searchString, handleInputChange } = useHandleSearchChange(); const { pagination, setPagination } = useGetPaginationWithRouter(); + const { filterValue, handleFilterSubmit } = useHandleFilterSubmit(); const { id } = useParams(); const [active, setActive] = useState<(typeof LogTabs)[keyof typeof LogTabs]>( LogTabs.FILE_LOGS, @@ -48,6 +50,7 @@ const useFetchFileLogList = () => { pagination, searchString, active, + filterValue, ], placeholderData: (previousData) => { if (previousData === undefined) { @@ -57,13 +60,16 @@ const useFetchFileLogList = () => { }, enabled: true, queryFn: async () => { - const { data: res = {} } = await fetchFunc({ - kb_id: knowledgeBaseId, - page: pagination.current, - page_size: pagination.pageSize, - keywords: searchString, - // order_by: '', - }); + const { data: res = {} } = await fetchFunc( + { + kb_id: knowledgeBaseId, + page: pagination.current, + page_size: pagination.pageSize, + keywords: searchString, + // order_by: '', + }, + { ...filterValue }, + ); return res.data || []; }, }); @@ -82,6 +88,8 @@ const useFetchFileLogList = () => { setPagination, active, setActive, + filterValue, + handleFilterSubmit, }; }; diff --git a/web/src/pages/dataset/dataset-overview/index.tsx b/web/src/pages/dataset/dataset-overview/index.tsx index 0a71acb2f..623e7f780 100644 --- a/web/src/pages/dataset/dataset-overview/index.tsx +++ b/web/src/pages/dataset/dataset-overview/index.tsx @@ -1,10 +1,12 @@ +import { FilterCollection } from '@/components/list-filter-bar/interface'; import SvgIcon from '@/components/svg-icon'; import { useIsDarkTheme } from '@/components/theme-provider'; import { useFetchDocumentList } from '@/hooks/use-document-request'; -import { parseColorToRGBA } from '@/utils/common-util'; +import { t } from 'i18next'; import { CircleQuestionMark } from 'lucide-react'; import { FC, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { RunningStatus, RunningStatusMap } from '../dataset/constant'; import { LogTabs } from './dataset-common'; import { DatasetFilter } from './dataset-filter'; import { useFetchFileLogList, useFetchOverviewTital } from './hook'; @@ -16,6 +18,10 @@ interface StatCardProps { icon: JSX.Element; children?: JSX.Element; } +interface CardFooterProcessProps { + success: number; + failed: number; +} const StatCard: FC = ({ title, value, children, icon }) => { return ( @@ -35,10 +41,6 @@ const StatCard: FC = ({ title, value, children, icon }) => { ); }; -interface CardFooterProcessProps { - success: number; - failed: number; -} const CardFooterProcess: FC = ({ success = 0, failed = 0, @@ -47,12 +49,7 @@ const CardFooterProcess: FC = ({ return (
-
+
{t('knowledgeDetails.success')}
@@ -70,6 +67,35 @@ const CardFooterProcess: FC = ({
); }; + +const filters = [ + { + field: 'operation_status', + label: t('knowledgeDetails.status'), + list: Object.values(RunningStatus).map((value) => { + // const value = key as RunningStatus; + console.log(value); + return { + id: value, + label: RunningStatusMap[value].label, + }; + }), + }, + { + field: 'types', + label: t('knowledgeDetails.task'), + list: [ + { + id: 'Parse', + label: 'Parse', + }, + { + id: 'Download', + label: 'Download', + }, + ], + }, +]; const FileLogsPage: FC = () => { const { t } = useTranslation(); @@ -89,12 +115,11 @@ const FileLogsPage: FC = () => { failed: 0, }, }); - const { data: topData } = useFetchOverviewTital(); const { pagination: { total: fileTotal }, } = useFetchDocumentList(); - console.log('topData --> ', topData); + useEffect(() => { setTopAllData((prev) => { return { @@ -127,6 +152,8 @@ const FileLogsPage: FC = () => { pagination, setPagination, active, + filterValue, + handleFilterSubmit, setActive, } = useFetchFileLogList(); @@ -156,6 +183,7 @@ const FileLogsPage: FC = () => { }; const isDark = useIsDarkTheme(); + return (
{/* Stats Cards */} @@ -215,10 +243,13 @@ const FileLogsPage: FC = () => { {/* Tabs & Search */} {/* Table */} diff --git a/web/src/pages/dataset/dataset-overview/overview-table.tsx b/web/src/pages/dataset/dataset-overview/overview-table.tsx index 5bb170e31..993d39dbf 100644 --- a/web/src/pages/dataset/dataset-overview/overview-table.tsx +++ b/web/src/pages/dataset/dataset-overview/overview-table.tsx @@ -14,6 +14,8 @@ import { import { RunningStatusMap } from '@/constants/knowledge'; import { useTranslate } from '@/hooks/common-hooks'; import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; +import { PipelineResultSearchParams } from '@/pages/dataflow-result/constant'; +import { NavigateToDataflowResultProps } from '@/pages/dataflow-result/interface'; import { formatDate, formatSecondsToHumanReadable } from '@/utils/date'; import { ColumnDef, @@ -41,9 +43,7 @@ export const getFileLogsTableColumns = ( showLog: (row: Row, active: LogTabs) => void, kowledgeId: string, navigateToDataflowResult: ( - id: string, - knowledgeId: string, - doc_id?: string, + props: NavigateToDataflowResultProps, ) => () => void, ) => { // const { t } = useTranslate('knowledgeDetails'); @@ -165,18 +165,23 @@ export const getFileLogsTableColumns = ( > - + {row.original.pipeline_id && ( + + )}
), }, @@ -371,6 +376,7 @@ const FileLogsTable: FC = ({ ? Math.ceil(pagination.total / pagination.pageSize) : 0, }); + return (
diff --git a/web/src/pages/dataset/dataset/constant.ts b/web/src/pages/dataset/dataset/constant.ts index bdf73bfd7..b4df26100 100644 --- a/web/src/pages/dataset/dataset/constant.ts +++ b/web/src/pages/dataset/dataset/constant.ts @@ -3,14 +3,20 @@ import { RunningStatus } from '@/constants/knowledge'; export const RunningStatusMap = { [RunningStatus.UNSTART]: { label: 'UNSTART', - color: 'var(--accent-primary)', + color: 'rgba(var(--accent-primary))', }, [RunningStatus.RUNNING]: { label: 'Parsing', color: 'var(--team-member)', }, - [RunningStatus.CANCEL]: { label: 'CANCEL', color: 'var(--state-warning)' }, - [RunningStatus.DONE]: { label: 'SUCCESS', color: 'var(--state-success)' }, + [RunningStatus.CANCEL]: { + label: 'CANCEL', + color: 'rgba(var(--state-warning))', + }, + [RunningStatus.DONE]: { + label: 'SUCCESS', + color: 'rgba(var(--state-success))', + }, [RunningStatus.FAIL]: { label: 'FAIL', color: 'rgba(var(--state-error))' }, }; diff --git a/web/src/pages/dataset/dataset/parsing-status-cell.tsx b/web/src/pages/dataset/dataset/parsing-status-cell.tsx index 2bf8b0524..89319755c 100644 --- a/web/src/pages/dataset/dataset/parsing-status-cell.tsx +++ b/web/src/pages/dataset/dataset/parsing-status-cell.tsx @@ -21,7 +21,7 @@ import { UseSaveMetaShowType } from './use-save-meta'; import { isParserRunning } from './utils'; const IconMap = { [RunningStatus.UNSTART]: ( -
+ ), [RunningStatus.RUNNING]: ( diff --git a/web/src/services/knowledge-service.ts b/web/src/services/knowledge-service.ts index e91b28cc5..350fa4e2a 100644 --- a/web/src/services/knowledge-service.ts +++ b/web/src/services/knowledge-service.ts @@ -210,6 +210,10 @@ const methods = { url: traceRaptor, method: 'get', }, + pipelineRerun: { + url: api.pipelineRerun, + method: 'post', + }, }; const kbService = registerServer(methods, request); diff --git a/web/src/utils/common-util.ts b/web/src/utils/common-util.ts index dce5853f8..67e0c3f97 100644 --- a/web/src/utils/common-util.ts +++ b/web/src/utils/common-util.ts @@ -174,6 +174,32 @@ export function parseColorToRGB(color: string): [number, number, number] { colorStr = getCSSVariableValue(varName); } + // Handle rgb(var(--accent-primary)) format + if (colorStr.startsWith('rgb(var(')) { + const varMatch = colorStr.match(/rgb\(var\(([^)]+)\)\)/); + if (!varMatch) { + console.error(`Invalid nested CSS variable: ${color}`); + return [0, 0, 0]; + } + const varName = varMatch[1]; + if (!varName) { + console.error(`Invalid nested CSS variable: ${colorStr}`); + return [0, 0, 0]; + } + // Get the CSS variable value which should be in format "r, g, b" + const rgbValues = getCSSVariableValue(varName); + const rgbMatch = rgbValues.match(/^(\d+),?\s*(\d+),?\s*(\d+)$/); + if (rgbMatch) { + return [ + parseInt(rgbMatch[1]), + parseInt(rgbMatch[2]), + parseInt(rgbMatch[3]), + ]; + } + console.error(`Unsupported RGB CSS variable format: ${rgbValues}`); + return [0, 0, 0]; + } + // Handles hexadecimal colors (e.g. #FF5733) if (colorStr.startsWith('#')) { const cleanedHex = colorStr.replace(/^#/, ''); diff --git a/web/tailwind.config.js b/web/tailwind.config.js index 771f9d32e..17a8acfc3 100644 --- a/web/tailwind.config.js +++ b/web/tailwind.config.js @@ -81,8 +81,14 @@ module.exports = { 5: 'rgba(var(--accent-primary) / 0.05)', // 5% }, 'bg-accent': 'var(--bg-accent)', - 'state-success': 'var(--state-success)', - 'state-warning': 'var(--state-warning)', + 'state-success': { + DEFAULT: 'rgb(var(--state-success) / )', + 5: 'rgba(var(--state-success) / 0.05)', // 5% + }, + 'state-warning': { + DEFAULT: 'rgb(var(--state-warning) / )', + 5: 'rgba(var(--state-warning) / 0.05)', // 5% + }, 'state-error': { DEFAULT: 'rgb(var(--state-error) / )', 5: 'rgba(var(--state-error) / 0.05)', // 5% diff --git a/web/tailwind.css b/web/tailwind.css index e45a31e1d..fc96e6232 100644 --- a/web/tailwind.css +++ b/web/tailwind.css @@ -116,8 +116,10 @@ /* Output Variables Box */ --bg-accent: rgba(76, 164, 231, 0.05); - --state-success: #3ba05c; - --state-warning: #faad14; + /* --state-success: #3ba05c; */ + --state-success: 59 160 92; + /* --state-warning: #767573; */ + --state-warning: 118 117 115; --state-error: 216 73 75; --team-group: #5ab77e;