Files
ragflow/web/src/pages/dataflow-result/parser.tsx
chanx 14273b4595 Fix: Optimized knowledge base file parsing and display #9869 (#10292)
### What problem does this PR solve?

Fix: Optimized knowledge base file parsing and display #9869

- Optimized the ChunkMethodDialog component logic and adjusted
FormSchema validation rules
- Updated the document information interface definition, adding
pipeline_id, pipeline_name, and suffix fields
- Refactored the ChunkResultBar component, removing filter-related logic
and simplifying the input box and chunk creation functionality
- Improved FormatPreserveEditor to support text mode switching
(full/omitted) display control
- Updated timeline node titles to more accurate semantic descriptions
(e.g., character splitters)
- Optimized the data flow result page structure and style, dynamically
adjusting height and content display
- Fixed the table sorting function on the dataset overview page and
enhanced the display of task type icons and status mapping.

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
2025-09-25 19:53:49 +08:00

209 lines
6.8 KiB
TypeScript

import { TimelineNode } from '@/components/originui/timeline';
import Spotlight from '@/components/spotlight';
import { Spin } from '@/components/ui/spin';
import { cn } from '@/lib/utils';
import classNames from 'classnames';
import { useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import ChunkResultBar from './components/chunk-result-bar';
import CheckboxSets from './components/chunk-result-bar/checkbox-sets';
import FormatPreserEditor from './components/parse-editer';
import RerunButton from './components/rerun-button';
import { TimelineNodeType } from './constant';
import { useChangeChunkTextMode, useFetchParserList } from './hooks';
import { IDslComponent } from './interface';
interface IProps {
isChange: boolean;
setIsChange: (isChange: boolean) => void;
step?: TimelineNode;
data: { value: IDslComponent; key: string };
reRunLoading: boolean;
reRunFunc: (data: { value: IDslComponent; key: string }) => void;
}
const ParserContainer = (props: IProps) => {
const { isChange, setIsChange, step, data, reRunFunc, reRunLoading } = props;
const { t } = useTranslation();
const { loading } = useFetchParserList();
const [selectedChunkIds, setSelectedChunkIds] = useState<string[]>([]);
const { changeChunkTextMode, textMode } = useChangeChunkTextMode();
const initialValue = useMemo(() => {
const outputs = data?.value?.obj?.params?.outputs;
const key = outputs?.output_format?.value;
const value = outputs[key]?.value;
const type = outputs[key]?.type;
console.log('outputs-->', outputs);
return {
key,
type,
value,
};
}, [data]);
const [initialText, setInitialText] = useState(initialValue);
const handleSave = (newContent: any) => {
console.log('newContent-change-->', newContent, initialValue);
if (JSON.stringify(newContent) !== JSON.stringify(initialValue)) {
setIsChange(true);
setInitialText(newContent);
} else {
setIsChange(false);
}
// Here, the API is called to send newContent to the backend
};
const handleReRunFunc = useCallback(() => {
const newData: { value: IDslComponent; key: string } = {
...data,
value: {
...data.value,
obj: {
...data.value.obj,
params: {
...(data.value?.obj?.params || {}),
outputs: {
...(data.value?.obj?.params?.outputs || {}),
[initialText.key]: {
type: initialText.type,
value: initialText.value,
},
},
},
},
},
};
reRunFunc(newData);
setIsChange(false);
}, [data, initialText, reRunFunc, setIsChange]);
const handleRemoveChunk = useCallback(async () => {
if (selectedChunkIds.length > 0) {
initialText.value = initialText.value.filter(
(item: any, index: number) => !selectedChunkIds.includes(index + ''),
);
setSelectedChunkIds([]);
}
}, [selectedChunkIds, initialText]);
const handleCheckboxClick = useCallback(
(id: string | number, checked: boolean) => {
console.log('handleCheckboxClick', id, checked, selectedChunkIds);
setSelectedChunkIds((prev) => {
if (checked) {
return [...prev, id.toString()];
} else {
return prev.filter((item) => item.toString() !== id.toString());
}
});
},
[],
);
const selectAllChunk = useCallback(
(checked: boolean) => {
setSelectedChunkIds(
checked ? initialText.value.map((x, index: number) => index) : [],
);
},
[initialText.value],
);
const isChunck =
step?.type === TimelineNodeType.characterSplitter ||
step?.type === TimelineNodeType.titleSplitter ||
step?.type === TimelineNodeType.splitter;
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 && (
<div className=" absolute top-2 right-6">
<RerunButton
step={step}
onRerun={handleReRunFunc}
loading={reRunLoading}
/>
</div>
)}
<div className={classNames('flex flex-col w-full')}>
<Spin spinning={loading} className="" size="large">
<div className="h-[50px] flex flex-col justify-end pb-[5px]">
{!isChunck && (
<div>
<h2 className="text-[16px]">
{t('dataflowParser.parseSummary')}
</h2>
<div className="text-[12px] text-text-secondary italic ">
{t('dataflowParser.parseSummaryTip')}
</div>
</div>
)}
{isChunck && (
<div>
<h2 className="text-[16px]">{t('chunk.chunkResult')}</h2>
<div className="text-[12px] text-text-secondary italic">
{t('chunk.chunkResultTip')}
</div>
</div>
)}
</div>
{isChunck && (
<div className="pt-[5px] pb-[5px] flex justify-between items-center">
<CheckboxSets
selectAllChunk={selectAllChunk}
removeChunk={handleRemoveChunk}
checked={selectedChunkIds.length === initialText.value.length}
selectedChunkIds={selectedChunkIds}
/>
<ChunkResultBar
changeChunkTextMode={changeChunkTextMode}
createChunk={handleCreateChunk}
/>
</div>
)}
<div
className={cn(
' border rounded-lg p-[20px] box-border w-[calc(100%-20px)] overflow-auto scrollbar-none',
{
'h-[calc(100vh-240px)]': isChunck,
'h-[calc(100vh-180px)]': !isChunck,
},
)}
>
<FormatPreserEditor
initialValue={initialText}
onSave={handleSave}
className={
initialText.key !== 'json' ? '!h-[calc(100vh-220px)]' : ''
}
isChunck={isChunck}
textMode={textMode}
isDelete={
step?.type === TimelineNodeType.characterSplitter ||
step?.type === TimelineNodeType.titleSplitter ||
step?.type === TimelineNodeType.splitter
}
handleCheckboxClick={handleCheckboxClick}
selectedChunkIds={selectedChunkIds}
/>
<Spotlight opcity={0.6} coverage={60} />
</div>
</Spin>
</div>
</>
);
};
export default ParserContainer;