mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
Fix: Updated color parsing functions and optimized component logic. (#10159)
### What problem does this PR solve? refactor(timeline, modal, dataflow-result, dataset-overview): Updated color parsing functions and optimized component logic. ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
@ -1,7 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { parseColorToRGBA } from '@/utils/common-util';
|
import { parseColorToRGB } from '@/utils/common-util';
|
||||||
import { Slot } from '@radix-ui/react-slot';
|
import { Slot } from '@radix-ui/react-slot';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
@ -251,7 +251,7 @@ const CustomTimeline = ({
|
|||||||
}: CustomTimelineProps) => {
|
}: CustomTimelineProps) => {
|
||||||
const [internalActiveStep, setInternalActiveStep] =
|
const [internalActiveStep, setInternalActiveStep] =
|
||||||
React.useState(defaultValue);
|
React.useState(defaultValue);
|
||||||
const _lineColor = `rgb(${parseColorToRGBA(lineColor)})`;
|
const _lineColor = `rgb(${parseColorToRGB(lineColor)})`;
|
||||||
console.log(lineColor, _lineColor);
|
console.log(lineColor, _lineColor);
|
||||||
const currentActiveStep = activeStep ?? internalActiveStep;
|
const currentActiveStep = activeStep ?? internalActiveStep;
|
||||||
|
|
||||||
@ -261,7 +261,7 @@ const CustomTimeline = ({
|
|||||||
}
|
}
|
||||||
onStepChange?.(step, id);
|
onStepChange?.(step, id);
|
||||||
};
|
};
|
||||||
const [r, g, b] = parseColorToRGBA(indicatorColor);
|
const [r, g, b] = parseColorToRGB(indicatorColor);
|
||||||
return (
|
return (
|
||||||
<Timeline
|
<Timeline
|
||||||
value={currentActiveStep}
|
value={currentActiveStep}
|
||||||
|
|||||||
@ -177,7 +177,7 @@ const Modal: ModalType = ({
|
|||||||
<DialogPrimitive.Close asChild>
|
<DialogPrimitive.Close asChild>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="flex h-7 w-7 items-center justify-center rounded-full hover:bg-muted"
|
className="flex h-7 w-7 items-center justify-center rounded-full hover:bg-muted focus-visible:outline-none"
|
||||||
>
|
>
|
||||||
{closeIcon}
|
{closeIcon}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@ -1628,6 +1628,24 @@ This delimiter is used to split the input text into several text pieces echo of
|
|||||||
parseSummaryTip: 'Parser:deepdoc',
|
parseSummaryTip: 'Parser:deepdoc',
|
||||||
rerunFromCurrentStep: 'Rerun From Current Step',
|
rerunFromCurrentStep: 'Rerun From Current Step',
|
||||||
rerunFromCurrentStepTip: 'Changes detected. Click to re-run.',
|
rerunFromCurrentStepTip: 'Changes detected. Click to re-run.',
|
||||||
|
confirmRerun: 'Confirm Rerun Process',
|
||||||
|
confirmRerunModalContent: `
|
||||||
|
<p class="text-sm text-text-disabled font-medium mb-2">
|
||||||
|
You are about to rerun the process starting from the <strong class="text-text-primary">{{step}}</strong> step.
|
||||||
|
</p>
|
||||||
|
<p class="text-sm mb-3 text-text-secondary">This will:</p>
|
||||||
|
<ul class="list-disc list-inside space-y-1 text-sm text-text-secondary">
|
||||||
|
<li>Overwrite existing results from the current step onwards</li>
|
||||||
|
<li>Create a new log entry for tracking</li>
|
||||||
|
<li>Previous steps will remain unchanged</li>
|
||||||
|
</ul>`,
|
||||||
|
changeStepModalTitle: 'Step Switch Warning',
|
||||||
|
changeStepModalContent: `
|
||||||
|
<p>You are currently editing the results of this stage.</p>
|
||||||
|
<p>If you switch to a later stage, your changes will be lost. </p>
|
||||||
|
<p>To keep them, please click Rerun to re-run the current stage.</p> `,
|
||||||
|
changeStepModalConfirmText: 'Switch Anyway',
|
||||||
|
changeStepModalCancelText: 'Cancel',
|
||||||
},
|
},
|
||||||
dataflow: {
|
dataflow: {
|
||||||
parser: 'Parser',
|
parser: 'Parser',
|
||||||
|
|||||||
@ -1536,6 +1536,24 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于
|
|||||||
parseSummaryTip: '解析器: deepdoc',
|
parseSummaryTip: '解析器: deepdoc',
|
||||||
rerunFromCurrentStep: '从当前步骤重新运行',
|
rerunFromCurrentStep: '从当前步骤重新运行',
|
||||||
rerunFromCurrentStepTip: '已修改,点击重新运行。',
|
rerunFromCurrentStepTip: '已修改,点击重新运行。',
|
||||||
|
confirmRerun: '确认重新运行流程',
|
||||||
|
confirmRerunModalContent: `
|
||||||
|
<p class="text-sm text-text-disabled font-medium mb-2">
|
||||||
|
您即将从 <strong class="text-text-primary">{{step}}</strong> 步骤开始重新运行该过程
|
||||||
|
</p>
|
||||||
|
<p class="text-sm mb-3 text-text-secondary">这将:</p>
|
||||||
|
<ul class="list-disc list-inside space-y-1 text-sm text-text-secondary">
|
||||||
|
<li>从当前步骤开始覆盖现有结果</li>
|
||||||
|
<li>创建新的日志条目进行跟踪</li>
|
||||||
|
<li>之前的步骤将保持不变</li>
|
||||||
|
</ul>`,
|
||||||
|
changeStepModalTitle: '切换步骤警告',
|
||||||
|
changeStepModalContent: `
|
||||||
|
<p>您目前正在编辑此阶段的结果。</p>
|
||||||
|
<p>如果您切换到后续阶段,您的更改将会丢失。</p>
|
||||||
|
<p>要保留这些更改,请点击“重新运行”以重新运行当前阶段。</p> `,
|
||||||
|
changeStepModalConfirmText: '继续切换',
|
||||||
|
changeStepModalCancelText: '取消',
|
||||||
},
|
},
|
||||||
dataflow: {
|
dataflow: {
|
||||||
parser: '解析器',
|
parser: '解析器',
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { TimelineNode } from '@/components/originui/timeline';
|
||||||
import message from '@/components/ui/message';
|
import message from '@/components/ui/message';
|
||||||
import {
|
import {
|
||||||
RAGFlowPagination,
|
RAGFlowPagination,
|
||||||
@ -23,9 +24,16 @@ import {
|
|||||||
useUpdateChunk,
|
useUpdateChunk,
|
||||||
} from './hooks';
|
} from './hooks';
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
const ChunkerContainer = () => {
|
|
||||||
|
interface IProps {
|
||||||
|
isChange: boolean;
|
||||||
|
setIsChange: (isChange: boolean) => void;
|
||||||
|
step?: TimelineNode;
|
||||||
|
}
|
||||||
|
const ChunkerContainer = (props: IProps) => {
|
||||||
|
const { isChange, setIsChange, step } = props;
|
||||||
const [selectedChunkIds, setSelectedChunkIds] = useState<string[]>([]);
|
const [selectedChunkIds, setSelectedChunkIds] = useState<string[]>([]);
|
||||||
const [isChange, setIsChange] = useState(false);
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const {
|
const {
|
||||||
data: { documentInfo, data = [], total },
|
data: { documentInfo, data = [], total },
|
||||||
@ -135,17 +143,21 @@ const ChunkerContainer = () => {
|
|||||||
setIsChange(true);
|
setIsChange(true);
|
||||||
onChunkUpdatingOk(e);
|
onChunkUpdatingOk(e);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleReRunFunc = () => {
|
||||||
|
setIsChange(false);
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="w-full h-full">
|
||||||
{isChange && (
|
{isChange && (
|
||||||
<div className=" absolute top-2 right-6">
|
<div className=" absolute top-2 right-6">
|
||||||
<RerunButton />
|
<RerunButton step={step} onRerun={handleReRunFunc} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
{ [styles.pagePdfWrapper]: isPdf },
|
{ [styles.pagePdfWrapper]: isPdf },
|
||||||
'flex flex-col w-3/5',
|
'flex flex-col w-full',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Spin spinning={loading} className={styles.spin} size="large">
|
<Spin spinning={loading} className={styles.spin} size="large">
|
||||||
@ -176,7 +188,7 @@ const ChunkerContainer = () => {
|
|||||||
selectedChunkIds={selectedChunkIds}
|
selectedChunkIds={selectedChunkIds}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="h-[calc(100vh-280px)] overflow-y-auto pr-2 scrollbar-thin">
|
<div className="h-[calc(100vh-280px)] overflow-y-auto pr-2 scrollbar-auto">
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
styles.chunkContainer,
|
styles.chunkContainer,
|
||||||
@ -227,7 +239,7 @@ const ChunkerContainer = () => {
|
|||||||
parserId={documentInfo.parser_id}
|
parserId={documentInfo.parser_id}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,16 +1,45 @@
|
|||||||
|
import { TimelineNode } from '@/components/originui/timeline';
|
||||||
import SvgIcon from '@/components/svg-icon';
|
import SvgIcon from '@/components/svg-icon';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Modal } from '@/components/ui/modal/modal';
|
||||||
import { CircleAlert } from 'lucide-react';
|
import { CircleAlert } from 'lucide-react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useRerunDataflow } from '../../hooks';
|
import { useRerunDataflow } from '../../hooks';
|
||||||
interface RerunButtonProps {
|
interface RerunButtonProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
|
step?: TimelineNode;
|
||||||
|
onRerun?: () => void;
|
||||||
}
|
}
|
||||||
const RerunButton = (props: RerunButtonProps) => {
|
const RerunButton = (props: RerunButtonProps) => {
|
||||||
|
const { className, step, onRerun } = props;
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { loading } = useRerunDataflow();
|
const { loading } = useRerunDataflow();
|
||||||
const clickFunc = () => {
|
const clickFunc = () => {
|
||||||
console.log('click rerun button');
|
console.log('click rerun button');
|
||||||
|
Modal.show({
|
||||||
|
visible: true,
|
||||||
|
className: '!w-[560px]',
|
||||||
|
title: t('dataflowParser.confirmRerun'),
|
||||||
|
children: (
|
||||||
|
<div
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: t('dataflowParser.confirmRerunModalContent', {
|
||||||
|
step: step?.title,
|
||||||
|
}),
|
||||||
|
}}
|
||||||
|
></div>
|
||||||
|
),
|
||||||
|
onVisibleChange: () => {
|
||||||
|
Modal.hide();
|
||||||
|
},
|
||||||
|
onOk: () => {
|
||||||
|
onRerun?.();
|
||||||
|
Modal.hide();
|
||||||
|
},
|
||||||
|
onCancel: () => {
|
||||||
|
Modal.hide();
|
||||||
|
},
|
||||||
|
});
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
|
|||||||
@ -7,40 +7,61 @@ import {
|
|||||||
PlayIcon,
|
PlayIcon,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
export const TimelineNodeObj = {
|
export enum TimelineNodeType {
|
||||||
begin: {
|
begin = 'begin',
|
||||||
|
parser = 'parser',
|
||||||
|
chunk = 'chunk',
|
||||||
|
indexer = 'indexer',
|
||||||
|
complete = 'complete',
|
||||||
|
end = 'end',
|
||||||
|
}
|
||||||
|
export const TimelineNodeArr = [
|
||||||
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
title: 'Begin',
|
title: 'Begin',
|
||||||
icon: <PlayIcon size={13} />,
|
icon: <PlayIcon size={13} />,
|
||||||
clickable: false,
|
clickable: false,
|
||||||
|
type: TimelineNodeType.begin,
|
||||||
},
|
},
|
||||||
parser: { id: 2, title: 'Parser', icon: <FilePlayIcon size={13} /> },
|
{
|
||||||
chunker: { id: 3, title: 'Chunker', icon: <Grid3x2 size={13} /> },
|
id: 2,
|
||||||
indexer: {
|
title: 'Parser',
|
||||||
|
icon: <FilePlayIcon size={13} />,
|
||||||
|
type: TimelineNodeType.parser,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
title: 'Chunker',
|
||||||
|
icon: <Grid3x2 size={13} />,
|
||||||
|
type: TimelineNodeType.chunk,
|
||||||
|
},
|
||||||
|
{
|
||||||
id: 4,
|
id: 4,
|
||||||
title: 'Indexer',
|
title: 'Indexer',
|
||||||
icon: <ListPlus size={13} />,
|
icon: <ListPlus size={13} />,
|
||||||
clickable: false,
|
clickable: false,
|
||||||
|
type: TimelineNodeType.indexer,
|
||||||
},
|
},
|
||||||
complete: {
|
{
|
||||||
id: 5,
|
id: 5,
|
||||||
title: 'Complete',
|
title: 'Complete',
|
||||||
icon: <CheckLine size={13} />,
|
icon: <CheckLine size={13} />,
|
||||||
clickable: false,
|
clickable: false,
|
||||||
|
type: TimelineNodeType.complete,
|
||||||
},
|
},
|
||||||
};
|
];
|
||||||
|
|
||||||
export interface TimelineDataFlowProps {
|
export interface TimelineDataFlowProps {
|
||||||
activeId: number | string;
|
activeId: number | string;
|
||||||
activeFunc: (id: number | string) => void;
|
activeFunc: (id: number | string, step: TimelineNode) => void;
|
||||||
}
|
}
|
||||||
const TimelineDataFlow = ({ activeFunc, activeId }: TimelineDataFlowProps) => {
|
const TimelineDataFlow = ({ activeFunc, activeId }: TimelineDataFlowProps) => {
|
||||||
// const [activeStep, setActiveStep] = useState(2);
|
// const [activeStep, setActiveStep] = useState(2);
|
||||||
const timelineNodes: TimelineNode[] = useMemo(() => {
|
const timelineNodes: TimelineNode[] = useMemo(() => {
|
||||||
const nodes: TimelineNode[] = [];
|
const nodes: TimelineNode[] = [];
|
||||||
Object.keys(TimelineNodeObj).forEach((key) => {
|
TimelineNodeArr.forEach((node) => {
|
||||||
nodes.push({
|
nodes.push({
|
||||||
...TimelineNodeObj[key as keyof typeof TimelineNodeObj],
|
...node,
|
||||||
className: 'w-32',
|
className: 'w-32',
|
||||||
completed: false,
|
completed: false,
|
||||||
});
|
});
|
||||||
@ -54,7 +75,10 @@ const TimelineDataFlow = ({ activeFunc, activeId }: TimelineDataFlowProps) => {
|
|||||||
}, [activeId, timelineNodes]);
|
}, [activeId, timelineNodes]);
|
||||||
const handleStepChange = (step: number, id: string | number) => {
|
const handleStepChange = (step: number, id: string | number) => {
|
||||||
// setActiveStep(step);
|
// setActiveStep(step);
|
||||||
activeFunc?.(id);
|
activeFunc?.(
|
||||||
|
id,
|
||||||
|
timelineNodes.find((node) => node.id === activeStep) as TimelineNode,
|
||||||
|
);
|
||||||
console.log(step, id);
|
console.log(step, id);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -140,8 +140,12 @@ export const useFetchParserList = () => {
|
|||||||
|
|
||||||
export const useRerunDataflow = () => {
|
export const useRerunDataflow = () => {
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [isChange, setIsChange] = useState(false);
|
||||||
return {
|
return {
|
||||||
loading,
|
loading,
|
||||||
|
setLoading,
|
||||||
|
isChange,
|
||||||
|
setIsChange,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -2,11 +2,17 @@ import { useFetchNextChunkList } from '@/hooks/use-chunk-request';
|
|||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import DocumentPreview from './components/document-preview';
|
import DocumentPreview from './components/document-preview';
|
||||||
import { useGetChunkHighlights, useHandleChunkCardClick } from './hooks';
|
import {
|
||||||
|
useGetChunkHighlights,
|
||||||
|
useHandleChunkCardClick,
|
||||||
|
useRerunDataflow,
|
||||||
|
} from './hooks';
|
||||||
|
|
||||||
import DocumentHeader from './components/document-preview/document-header';
|
import DocumentHeader from './components/document-preview/document-header';
|
||||||
|
|
||||||
|
import { TimelineNode } from '@/components/originui/timeline';
|
||||||
import { PageHeader } from '@/components/page-header';
|
import { PageHeader } from '@/components/page-header';
|
||||||
|
import Spotlight from '@/components/spotlight';
|
||||||
import {
|
import {
|
||||||
Breadcrumb,
|
Breadcrumb,
|
||||||
BreadcrumbItem,
|
BreadcrumbItem,
|
||||||
@ -15,6 +21,8 @@ import {
|
|||||||
BreadcrumbPage,
|
BreadcrumbPage,
|
||||||
BreadcrumbSeparator,
|
BreadcrumbSeparator,
|
||||||
} from '@/components/ui/breadcrumb';
|
} from '@/components/ui/breadcrumb';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Modal } from '@/components/ui/modal/modal';
|
||||||
import {
|
import {
|
||||||
QueryStringMap,
|
QueryStringMap,
|
||||||
useNavigatePage,
|
useNavigatePage,
|
||||||
@ -23,7 +31,10 @@ import { useGetKnowledgeSearchParams } from '@/hooks/route-hook';
|
|||||||
import { useFetchKnowledgeBaseConfiguration } from '@/hooks/use-knowledge-request';
|
import { useFetchKnowledgeBaseConfiguration } from '@/hooks/use-knowledge-request';
|
||||||
import { ChunkerContainer } from './chunker';
|
import { ChunkerContainer } from './chunker';
|
||||||
import { useGetDocumentUrl } from './components/document-preview/hooks';
|
import { useGetDocumentUrl } from './components/document-preview/hooks';
|
||||||
import TimelineDataFlow, { TimelineNodeObj } from './components/time-line';
|
import TimelineDataFlow, {
|
||||||
|
TimelineNodeArr,
|
||||||
|
TimelineNodeType,
|
||||||
|
} from './components/time-line';
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
import ParserContainer from './parser';
|
import ParserContainer from './parser';
|
||||||
|
|
||||||
@ -34,7 +45,7 @@ const Chunk = () => {
|
|||||||
const { selectedChunkId } = useHandleChunkCardClick();
|
const { selectedChunkId } = useHandleChunkCardClick();
|
||||||
const [activeStepId, setActiveStepId] = useState<number | string>(0);
|
const [activeStepId, setActiveStepId] = useState<number | string>(0);
|
||||||
const { data: dataset } = useFetchKnowledgeBaseConfiguration();
|
const { data: dataset } = useFetchKnowledgeBaseConfiguration();
|
||||||
|
const { isChange, setIsChange } = useRerunDataflow();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const { navigateToDataset, getQueryString, navigateToDatasetList } =
|
const { navigateToDataset, getQueryString, navigateToDatasetList } =
|
||||||
@ -58,10 +69,57 @@ const Chunk = () => {
|
|||||||
return 'unknown';
|
return 'unknown';
|
||||||
}, [documentInfo]);
|
}, [documentInfo]);
|
||||||
|
|
||||||
const handleStepChange = (id: number | string) => {
|
const handleStepChange = (id: number | string, step: TimelineNode) => {
|
||||||
setActiveStepId(id);
|
console.log(id, step);
|
||||||
|
if (isChange) {
|
||||||
|
Modal.show({
|
||||||
|
visible: true,
|
||||||
|
className: '!w-[560px]',
|
||||||
|
title: t('dataflowParser.changeStepModalTitle'),
|
||||||
|
children: (
|
||||||
|
<div
|
||||||
|
className="text-sm text-text-secondary"
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: t('dataflowParser.changeStepModalContent', {
|
||||||
|
step: step?.title,
|
||||||
|
}),
|
||||||
|
}}
|
||||||
|
></div>
|
||||||
|
),
|
||||||
|
onVisibleChange: () => {
|
||||||
|
Modal.hide();
|
||||||
|
},
|
||||||
|
footer: (
|
||||||
|
<div className="flex justify-end gap-2">
|
||||||
|
<Button variant={'outline'} onClick={() => Modal.hide()}>
|
||||||
|
{t('dataflowParser.changeStepModalCancelText')}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant={'secondary'}
|
||||||
|
className="!bg-state-error text-text-primary"
|
||||||
|
onClick={() => {
|
||||||
|
Modal.hide();
|
||||||
|
setActiveStepId(id);
|
||||||
|
setIsChange(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('dataflowParser.changeStepModalConfirmText')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setActiveStepId(id);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const { type } = useGetKnowledgeSearchParams();
|
const { type } = useGetKnowledgeSearchParams();
|
||||||
|
const currentTimeNode: TimelineNode = useMemo(() => {
|
||||||
|
return (
|
||||||
|
TimelineNodeArr.find((node) => node.id === activeStepId) ||
|
||||||
|
({} as TimelineNode)
|
||||||
|
);
|
||||||
|
}, [activeStepId]);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageHeader>
|
<PageHeader>
|
||||||
@ -114,9 +172,23 @@ const Chunk = () => {
|
|||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<div className="h-dvh border-r -mt-3"></div>
|
<div className="h-dvh border-r -mt-3"></div>
|
||||||
{(activeStepId === TimelineNodeObj.chunker.id ||
|
<div className="w-3/5 h-full">
|
||||||
type === 'chunk') && <ChunkerContainer />}
|
{currentTimeNode?.type === TimelineNodeType.chunk && (
|
||||||
{activeStepId === TimelineNodeObj.parser.id && <ParserContainer />}
|
<ChunkerContainer
|
||||||
|
isChange={isChange}
|
||||||
|
setIsChange={setIsChange}
|
||||||
|
step={currentTimeNode as TimelineNode}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{currentTimeNode?.type === TimelineNodeType.parser && (
|
||||||
|
<ParserContainer
|
||||||
|
isChange={isChange}
|
||||||
|
setIsChange={setIsChange}
|
||||||
|
step={currentTimeNode as TimelineNode}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Spotlight opcity={0.6} coverage={60} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { TimelineNode } from '@/components/originui/timeline';
|
||||||
import Spotlight from '@/components/spotlight';
|
import Spotlight from '@/components/spotlight';
|
||||||
import { Spin } from '@/components/ui/spin';
|
import { Spin } from '@/components/ui/spin';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
@ -6,13 +7,18 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import FormatPreserveEditor from './components/parse-editer';
|
import FormatPreserveEditor from './components/parse-editer';
|
||||||
import RerunButton from './components/rerun-button';
|
import RerunButton from './components/rerun-button';
|
||||||
import { useFetchParserList, useFetchPaserText } from './hooks';
|
import { useFetchParserList, useFetchPaserText } from './hooks';
|
||||||
const ParserContainer = () => {
|
interface IProps {
|
||||||
|
isChange: boolean;
|
||||||
|
setIsChange: (isChange: boolean) => void;
|
||||||
|
step?: TimelineNode;
|
||||||
|
}
|
||||||
|
const ParserContainer = (props: IProps) => {
|
||||||
|
const { isChange, setIsChange, step } = props;
|
||||||
const { data: initialValue, rerun: onSave } = useFetchPaserText();
|
const { data: initialValue, rerun: onSave } = useFetchPaserText();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { loading } = useFetchParserList();
|
const { loading } = useFetchParserList();
|
||||||
|
|
||||||
const [initialText, setInitialText] = useState(initialValue);
|
const [initialText, setInitialText] = useState(initialValue);
|
||||||
const [isChange, setIsChange] = useState(false);
|
|
||||||
const handleSave = (newContent: string) => {
|
const handleSave = (newContent: string) => {
|
||||||
console.log('保存内容:', newContent);
|
console.log('保存内容:', newContent);
|
||||||
if (newContent !== initialText) {
|
if (newContent !== initialText) {
|
||||||
@ -23,14 +29,17 @@ const ParserContainer = () => {
|
|||||||
}
|
}
|
||||||
// Here, the API is called to send newContent to the backend
|
// Here, the API is called to send newContent to the backend
|
||||||
};
|
};
|
||||||
|
const handleReRunFunc = () => {
|
||||||
|
setIsChange(false);
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{isChange && (
|
{isChange && (
|
||||||
<div className=" absolute top-2 right-6">
|
<div className=" absolute top-2 right-6">
|
||||||
<RerunButton />
|
<RerunButton step={step} onRerun={handleReRunFunc} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className={classNames('flex flex-col w-3/5')}>
|
<div className={classNames('flex flex-col w-full')}>
|
||||||
<Spin spinning={loading} className="" size="large">
|
<Spin spinning={loading} className="" size="large">
|
||||||
<div className="h-[50px] flex flex-col justify-end pb-[5px]">
|
<div className="h-[50px] flex flex-col justify-end pb-[5px]">
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
27
web/src/pages/dataset/dataset-overview/hook.ts
Normal file
27
web/src/pages/dataset/dataset-overview/hook.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import kbService from '@/services/knowledge-service';
|
||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import { useParams, useSearchParams } from 'umi';
|
||||||
|
|
||||||
|
export interface IOverviewTital {
|
||||||
|
cancelled: number;
|
||||||
|
failed: number;
|
||||||
|
finished: number;
|
||||||
|
processing: number;
|
||||||
|
}
|
||||||
|
const useFetchOverviewTital = () => {
|
||||||
|
const [searchParams] = useSearchParams();
|
||||||
|
const { id } = useParams();
|
||||||
|
const knowledgeBaseId = searchParams.get('id') || id;
|
||||||
|
const { data } = useQuery<IOverviewTital>({
|
||||||
|
queryKey: ['overviewTital'],
|
||||||
|
queryFn: async () => {
|
||||||
|
const { data: res = {} } = await kbService.getKnowledgeBasicInfo({
|
||||||
|
kb_id: knowledgeBaseId,
|
||||||
|
});
|
||||||
|
return res.data || [];
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return { data };
|
||||||
|
};
|
||||||
|
|
||||||
|
export { useFetchOverviewTital };
|
||||||
@ -1,11 +1,12 @@
|
|||||||
import SvgIcon from '@/components/svg-icon';
|
import SvgIcon from '@/components/svg-icon';
|
||||||
import { useIsDarkTheme } from '@/components/theme-provider';
|
import { useIsDarkTheme } from '@/components/theme-provider';
|
||||||
import { toFixed } from '@/utils/common-util';
|
import { parseColorToRGBA } from '@/utils/common-util';
|
||||||
import { CircleQuestionMark } from 'lucide-react';
|
import { CircleQuestionMark } from 'lucide-react';
|
||||||
import { FC, useMemo, useState } from 'react';
|
import { FC, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { LogTabs } from './dataset-common';
|
import { LogTabs } from './dataset-common';
|
||||||
import { DatasetFilter } from './dataset-filter';
|
import { DatasetFilter } from './dataset-filter';
|
||||||
|
import { useFetchOverviewTital } from './hook';
|
||||||
import FileLogsTable from './overview-table';
|
import FileLogsTable from './overview-table';
|
||||||
|
|
||||||
interface StatCardProps {
|
interface StatCardProps {
|
||||||
@ -34,46 +35,36 @@ const StatCard: FC<StatCardProps> = ({ title, value, children, icon }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
interface CardFooterProcessProps {
|
interface CardFooterProcessProps {
|
||||||
total: number;
|
|
||||||
success: number;
|
success: number;
|
||||||
failed: number;
|
failed: number;
|
||||||
}
|
}
|
||||||
const CardFooterProcess: FC<CardFooterProcessProps> = ({
|
const CardFooterProcess: FC<CardFooterProcessProps> = ({
|
||||||
total,
|
|
||||||
success = 0,
|
success = 0,
|
||||||
failed = 0,
|
failed = 0,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const successPrecentage = total ? (success / total) * 100 : 0;
|
|
||||||
const failedPrecentage = total ? (failed / total) * 100 : 0;
|
|
||||||
const completedPercentage = total ? ((success + failed) / total) * 100 : 0;
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center flex-col gap-2">
|
<div className="flex items-center flex-col gap-2">
|
||||||
<div className="flex justify-between w-full text-sm text-text-secondary">
|
<div className="w-full flex justify-between gap-4 rounded-lg text-sm font-bold text-text-primary">
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<div className="flex items-center gap-1">
|
|
||||||
{success || 0}
|
|
||||||
<span>{t('knowledgeDetails.success')}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-1">
|
|
||||||
{failed || 0}
|
|
||||||
<span>{t('knowledgeDetails.failed')}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-1">
|
|
||||||
{toFixed(completedPercentage) as string}%
|
|
||||||
<span>{t('knowledgeDetails.completed')}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="w-full flex rounded-full h-1.5 bg-bg-card text-sm font-bold text-text-primary">
|
|
||||||
<div
|
<div
|
||||||
className=" rounded-full h-1.5 bg-accent-primary"
|
className="flex items-center justify-between rounded-md w-1/2 p-2"
|
||||||
style={{ width: successPrecentage + '%' }}
|
style={{
|
||||||
></div>
|
backgroundColor: `${parseColorToRGBA('var(--state-success)', 0.05)}`,
|
||||||
<div
|
}}
|
||||||
className=" rounded-full h-1.5 bg-state-error"
|
>
|
||||||
style={{ width: failedPrecentage + '%' }}
|
<div className="flex items-center rounded-lg gap-1">
|
||||||
></div>
|
<div className="w-2 h-2 rounded-full bg-state-success"></div>
|
||||||
|
<div>{t('knowledgeDetails.success')}</div>
|
||||||
|
</div>
|
||||||
|
<div>{success || 0}</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between rounded-md w-1/2 bg-state-error-5 p-2">
|
||||||
|
<div className="flex items-center rounded-lg gap-1">
|
||||||
|
<div className="w-2 h-2 rounded-full bg-state-error"></div>
|
||||||
|
<div>{t('knowledgeDetails.failed')}</div>
|
||||||
|
</div>
|
||||||
|
<div>{failed || 0}</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -99,6 +90,10 @@ const FileLogsPage: FC = () => {
|
|||||||
failed: 2,
|
failed: 2,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const { data: topData } = useFetchOverviewTital();
|
||||||
|
console.log('topData --> ', topData);
|
||||||
|
|
||||||
const mockData = useMemo(() => {
|
const mockData = useMemo(() => {
|
||||||
if (active === LogTabs.FILE_LOGS) {
|
if (active === LogTabs.FILE_LOGS) {
|
||||||
return Array(30)
|
return Array(30)
|
||||||
@ -133,7 +128,8 @@ const FileLogsPage: FC = () => {
|
|||||||
task: i === 0 ? 'chunck' : 'Parser',
|
task: i === 0 ? 'chunck' : 'Parser',
|
||||||
pipeline:
|
pipeline:
|
||||||
i === 0 ? 'data demo for...' : i === 1 ? 'test' : 'kiki’s demo',
|
i === 0 ? 'data demo for...' : i === 1 ? 'test' : 'kiki’s demo',
|
||||||
status:
|
status: i === 0 ? 3 : i === 1 ? 4 : i === 2 ? 1 : 0,
|
||||||
|
statusName:
|
||||||
i === 0
|
i === 0
|
||||||
? 'Success'
|
? 'Success'
|
||||||
: i === 1
|
: i === 1
|
||||||
@ -147,7 +143,7 @@ const FileLogsPage: FC = () => {
|
|||||||
|
|
||||||
const pagination = {
|
const pagination = {
|
||||||
current: 1,
|
current: 1,
|
||||||
pageSize: 30,
|
pageSize: 10,
|
||||||
total: 100,
|
total: 100,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -194,7 +190,6 @@ const FileLogsPage: FC = () => {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<CardFooterProcess
|
<CardFooterProcess
|
||||||
total={topMockData.downloads.value}
|
|
||||||
success={topMockData.downloads.success}
|
success={topMockData.downloads.success}
|
||||||
failed={topMockData.downloads.failed}
|
failed={topMockData.downloads.failed}
|
||||||
/>
|
/>
|
||||||
@ -211,7 +206,6 @@ const FileLogsPage: FC = () => {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<CardFooterProcess
|
<CardFooterProcess
|
||||||
total={topMockData.processing.value}
|
|
||||||
success={topMockData.processing.success}
|
success={topMockData.processing.success}
|
||||||
failed={topMockData.processing.failed}
|
failed={topMockData.processing.failed}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -337,7 +337,7 @@ const FileLogsTable: FC<FileLogsTableProps> = ({
|
|||||||
: 0,
|
: 0,
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<div className="w-full h-[calc(100vh-350px)]">
|
<div className="w-full h-[calc(100vh-360px)]">
|
||||||
<Table rootClassName="max-h-[calc(100vh-380px)]">
|
<Table rootClassName="max-h-[calc(100vh-380px)]">
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
|||||||
@ -39,6 +39,7 @@ const {
|
|||||||
setMeta,
|
setMeta,
|
||||||
getMeta,
|
getMeta,
|
||||||
retrievalTestShare,
|
retrievalTestShare,
|
||||||
|
getKnowledgeBasicInfo,
|
||||||
} = api;
|
} = api;
|
||||||
|
|
||||||
const methods = {
|
const methods = {
|
||||||
@ -169,6 +170,10 @@ const methods = {
|
|||||||
url: retrievalTestShare,
|
url: retrievalTestShare,
|
||||||
method: 'post',
|
method: 'post',
|
||||||
},
|
},
|
||||||
|
getKnowledgeBasicInfo: {
|
||||||
|
url: getKnowledgeBasicInfo,
|
||||||
|
method: 'get',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const kbService = registerServer<keyof typeof methods>(methods, request);
|
const kbService = registerServer<keyof typeof methods>(methods, request);
|
||||||
|
|||||||
@ -45,6 +45,7 @@ export default {
|
|||||||
getKnowledgeGraph: (knowledgeId: string) =>
|
getKnowledgeGraph: (knowledgeId: string) =>
|
||||||
`${api_host}/kb/${knowledgeId}/knowledge_graph`,
|
`${api_host}/kb/${knowledgeId}/knowledge_graph`,
|
||||||
getMeta: `${api_host}/kb/get_meta`,
|
getMeta: `${api_host}/kb/get_meta`,
|
||||||
|
getKnowledgeBasicInfo: `${api_host}/kb/basic_info`,
|
||||||
|
|
||||||
// tags
|
// tags
|
||||||
listTag: (knowledgeId: string) => `${api_host}/kb/${knowledgeId}/tags`,
|
listTag: (knowledgeId: string) => `${api_host}/kb/${knowledgeId}/tags`,
|
||||||
@ -192,7 +193,6 @@ export default {
|
|||||||
retrievalTestShare: `${ExternalApi}${api_host}/searchbots/retrieval_test`,
|
retrievalTestShare: `${ExternalApi}${api_host}/searchbots/retrieval_test`,
|
||||||
|
|
||||||
// data pipeline
|
// data pipeline
|
||||||
|
|
||||||
fetchDataflow: (id: string) => `${api_host}/dataflow/get/${id}`,
|
fetchDataflow: (id: string) => `${api_host}/dataflow/get/${id}`,
|
||||||
setDataflow: `${api_host}/dataflow/set`,
|
setDataflow: `${api_host}/dataflow/set`,
|
||||||
removeDataflow: `${api_host}/dataflow/rm`,
|
removeDataflow: `${api_host}/dataflow/rm`,
|
||||||
|
|||||||
@ -152,8 +152,11 @@ function getCSSVariableValue(variableName: string): string {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the color and convert to RGBA
|
/**Parse the color and convert to RGB,
|
||||||
export function parseColorToRGBA(color: string): [number, number, number] {
|
* #fff -> [255, 255, 255]
|
||||||
|
* var(--text-primary) -> [var(--text-primary-r), var(--text-primary-g), var(--text-primary-b)]
|
||||||
|
* */
|
||||||
|
export function parseColorToRGB(color: string): [number, number, number] {
|
||||||
// Handling CSS variables (e.g. var(--accent-primary))
|
// Handling CSS variables (e.g. var(--accent-primary))
|
||||||
let colorStr = color;
|
let colorStr = color;
|
||||||
if (colorStr.startsWith('var(')) {
|
if (colorStr.startsWith('var(')) {
|
||||||
@ -203,3 +206,14 @@ export function parseColorToRGBA(color: string): [number, number, number] {
|
|||||||
console.error(`Unsupported colorStr format: ${colorStr}`);
|
console.error(`Unsupported colorStr format: ${colorStr}`);
|
||||||
return [0, 0, 0];
|
return [0, 0, 0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param color eg: #fff, or var(--color-text-primary)
|
||||||
|
* @param opcity 0~1
|
||||||
|
* @return rgba(r,g,b,opcity)
|
||||||
|
*/
|
||||||
|
export function parseColorToRGBA(color: string, opcity = 1): string {
|
||||||
|
const [r, g, b] = parseColorToRGB(color);
|
||||||
|
return `rgba(${r},${g},${b},${opcity})`;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user