mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-01-04 03:25:30 +08:00
### What problem does this PR solve? fix(edit-tag): Fix the bug that the edit-tag tag cannot be deleted #9869 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue)
449 lines
14 KiB
TypeScript
449 lines
14 KiB
TypeScript
import FileStatusBadge from '@/components/file-status-badge';
|
|
import { FileIcon, IconFontFill } from '@/components/icon-font';
|
|
import { RAGFlowAvatar } from '@/components/ragflow-avatar';
|
|
import { Button } from '@/components/ui/button';
|
|
import { RAGFlowPagination } from '@/components/ui/ragflow-pagination';
|
|
import {
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableHead,
|
|
TableHeader,
|
|
TableRow,
|
|
} from '@/components/ui/table';
|
|
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,
|
|
ColumnFiltersState,
|
|
Row,
|
|
SortingState,
|
|
flexRender,
|
|
getCoreRowModel,
|
|
getFilteredRowModel,
|
|
getPaginationRowModel,
|
|
getSortedRowModel,
|
|
useReactTable,
|
|
} from '@tanstack/react-table';
|
|
import { TFunction } from 'i18next';
|
|
import { ArrowUpDown, ClipboardList, Eye } from 'lucide-react';
|
|
import { FC, useMemo, useState } from 'react';
|
|
import { useParams } from 'umi';
|
|
import { RunningStatus } from '../dataset/constant';
|
|
import ProcessLogModal from '../process-log-modal';
|
|
import { LogTabs, ProcessingType, ProcessingTypeMap } from './dataset-common';
|
|
import { DocumentLog, FileLogsTableProps, IFileLogItem } from './interface';
|
|
|
|
export const getFileLogsTableColumns = (
|
|
t: TFunction<'translation', string>,
|
|
showLog: (row: Row<IFileLogItem & DocumentLog>, active: LogTabs) => void,
|
|
kowledgeId: string,
|
|
navigateToDataflowResult: (
|
|
props: NavigateToDataflowResultProps,
|
|
) => () => void,
|
|
) => {
|
|
// const { t } = useTranslate('knowledgeDetails');
|
|
const columns: ColumnDef<IFileLogItem & DocumentLog>[] = [
|
|
// {
|
|
// id: 'select',
|
|
// header: ({ table }) => (
|
|
// <input
|
|
// type="checkbox"
|
|
// checked={table.getIsAllRowsSelected()}
|
|
// onChange={table.getToggleAllRowsSelectedHandler()}
|
|
// className="rounded bg-gray-900 text-blue-500 focus:ring-blue-500"
|
|
// />
|
|
// ),
|
|
// cell: ({ row }) => (
|
|
// <input
|
|
// type="checkbox"
|
|
// checked={row.getIsSelected()}
|
|
// onChange={row.getToggleSelectedHandler()}
|
|
// className="rounded border-gray-600 bg-gray-900 text-blue-500 focus:ring-blue-500"
|
|
// />
|
|
// ),
|
|
// },
|
|
{
|
|
accessorKey: 'id',
|
|
header: 'ID',
|
|
cell: ({ row }) => (
|
|
<div className="text-text-primary">{row.original.id}</div>
|
|
),
|
|
},
|
|
{
|
|
accessorKey: 'fileName',
|
|
header: t('fileName'),
|
|
cell: ({ row }) => (
|
|
<div
|
|
className="flex items-center gap-2 text-text-primary"
|
|
// onClick={navigateToDataflowResult(
|
|
// row.original.id,
|
|
// row.original.kb_id,
|
|
// )}
|
|
>
|
|
<FileIcon name={row.original.fileName}></FileIcon>
|
|
{row.original.fileName}
|
|
</div>
|
|
),
|
|
},
|
|
{
|
|
accessorKey: 'source_from',
|
|
header: t('source'),
|
|
cell: ({ row }) => (
|
|
<div className="text-text-primary">
|
|
{row.original.source_from || t('localUpload')}
|
|
</div>
|
|
),
|
|
},
|
|
{
|
|
accessorKey: 'pipeline_title',
|
|
header: t('dataPipeline'),
|
|
cell: ({ row }) => (
|
|
<div className="flex items-center gap-2 text-text-primary">
|
|
<RAGFlowAvatar
|
|
avatar={row.original.avatar}
|
|
name={row.original.pipeline_title}
|
|
className="size-4"
|
|
/>
|
|
{row.original.pipeline_title === 'naive'
|
|
? 'general'
|
|
: row.original.pipeline_title}
|
|
</div>
|
|
),
|
|
},
|
|
{
|
|
accessorKey: 'process_begin_at',
|
|
header: ({ column }) => {
|
|
return (
|
|
<Button
|
|
variant="transparent"
|
|
className="border-none"
|
|
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
|
>
|
|
{t('startDate')}
|
|
<ArrowUpDown />
|
|
</Button>
|
|
);
|
|
},
|
|
cell: ({ row }) => (
|
|
<div className="text-text-primary">
|
|
{formatDate(row.original.process_begin_at)}
|
|
</div>
|
|
),
|
|
},
|
|
{
|
|
accessorKey: 'task_type',
|
|
header: t('task'),
|
|
cell: ({ row }) => (
|
|
<div className="text-text-primary">{row.original.task_type}</div>
|
|
),
|
|
},
|
|
{
|
|
accessorKey: 'operation_status',
|
|
header: t('status'),
|
|
cell: ({ row }) => (
|
|
<FileStatusBadge
|
|
status={row.original.operation_status as RunningStatus}
|
|
name={
|
|
RunningStatusMap[row.original.operation_status as RunningStatus]
|
|
}
|
|
/>
|
|
),
|
|
},
|
|
{
|
|
id: 'operations',
|
|
header: t('operations'),
|
|
cell: ({ row }) => (
|
|
<div className="flex justify-start space-x-2 opacity-0 group-hover:opacity-100 transition-opacity">
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
className="p-1"
|
|
onClick={() => {
|
|
showLog(row, LogTabs.FILE_LOGS);
|
|
}}
|
|
>
|
|
<Eye />
|
|
</Button>
|
|
{row.original.pipeline_id && (
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
className="p-1"
|
|
onClick={navigateToDataflowResult({
|
|
id: row.original.id,
|
|
[PipelineResultSearchParams.KnowledgeId]: kowledgeId,
|
|
[PipelineResultSearchParams.DocumentId]:
|
|
row.original.document_id,
|
|
[PipelineResultSearchParams.IsReadOnly]: 'false',
|
|
[PipelineResultSearchParams.Type]: 'dataflow',
|
|
})}
|
|
>
|
|
<ClipboardList />
|
|
</Button>
|
|
)}
|
|
</div>
|
|
),
|
|
},
|
|
];
|
|
|
|
return columns;
|
|
};
|
|
|
|
export const getDatasetLogsTableColumns = (
|
|
t: TFunction<'translation', string>,
|
|
showLog: (row: Row<IFileLogItem & DocumentLog>, active: LogTabs) => void,
|
|
) => {
|
|
// const { t } = useTranslate('knowledgeDetails');
|
|
const columns: ColumnDef<IFileLogItem & DocumentLog>[] = [
|
|
// {
|
|
// id: 'select',
|
|
// header: ({ table }) => (
|
|
// <input
|
|
// type="checkbox"
|
|
// checked={table.getIsAllRowsSelected()}
|
|
// onChange={table.getToggleAllRowsSelectedHandler()}
|
|
// className="rounded bg-gray-900 text-blue-500 focus:ring-blue-500"
|
|
// />
|
|
// ),
|
|
// cell: ({ row }) => (
|
|
// <input
|
|
// type="checkbox"
|
|
// checked={row.getIsSelected()}
|
|
// onChange={row.getToggleSelectedHandler()}
|
|
// className="rounded border-gray-600 bg-gray-900 text-blue-500 focus:ring-blue-500"
|
|
// />
|
|
// ),
|
|
// },
|
|
{
|
|
accessorKey: 'id',
|
|
header: 'ID',
|
|
cell: ({ row }) => (
|
|
<div className="text-text-primary">{row.original.id}</div>
|
|
),
|
|
},
|
|
{
|
|
accessorKey: 'process_begin_at',
|
|
header: ({ column }) => {
|
|
return (
|
|
<Button
|
|
variant="transparent"
|
|
className="border-none"
|
|
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
|
>
|
|
{t('startDate')}
|
|
<ArrowUpDown />
|
|
</Button>
|
|
);
|
|
},
|
|
cell: ({ row }) => (
|
|
<div className="text-text-primary">
|
|
{formatDate(row.original.process_begin_at)}
|
|
</div>
|
|
),
|
|
},
|
|
{
|
|
accessorKey: 'task_type',
|
|
header: t('processingType'),
|
|
cell: ({ row }) => (
|
|
<div className="flex items-center gap-2 text-text-primary">
|
|
{ProcessingType.knowledgeGraph === row.original.task_type && (
|
|
<IconFontFill
|
|
name={`knowledgegraph`}
|
|
className="text-text-secondary"
|
|
></IconFontFill>
|
|
)}
|
|
{ProcessingType.raptor === row.original.task_type && (
|
|
<IconFontFill
|
|
name={`dataflow-01`}
|
|
className="text-text-secondary"
|
|
></IconFontFill>
|
|
)}
|
|
{ProcessingTypeMap[row.original.task_type as ProcessingType] ||
|
|
row.original.task_type}
|
|
</div>
|
|
),
|
|
},
|
|
{
|
|
accessorKey: 'operation_status',
|
|
header: t('status'),
|
|
cell: ({ row }) => (
|
|
// <FileStatusBadge
|
|
// status={row.original.status}
|
|
// name={row.original.statusName}
|
|
// />
|
|
<FileStatusBadge
|
|
status={row.original.operation_status as RunningStatus}
|
|
name={
|
|
RunningStatusMap[row.original.operation_status as RunningStatus]
|
|
}
|
|
/>
|
|
),
|
|
},
|
|
{
|
|
id: 'operations',
|
|
header: t('operations'),
|
|
cell: ({ row }) => (
|
|
<div className="flex justify-start space-x-2 opacity-0 group-hover:opacity-100 transition-opacity">
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
className="p-1"
|
|
onClick={() => {
|
|
showLog(row, LogTabs.DATASET_LOGS);
|
|
}}
|
|
>
|
|
<Eye />
|
|
</Button>
|
|
</div>
|
|
),
|
|
},
|
|
];
|
|
|
|
return columns;
|
|
};
|
|
|
|
const FileLogsTable: FC<FileLogsTableProps> = ({
|
|
data,
|
|
pagination,
|
|
setPagination,
|
|
active = LogTabs.FILE_LOGS,
|
|
}) => {
|
|
const [sorting, setSorting] = useState<SortingState>([]);
|
|
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
|
|
const [rowSelection, setRowSelection] = useState({});
|
|
const { t } = useTranslate('knowledgeDetails');
|
|
const [isModalVisible, setIsModalVisible] = useState(false);
|
|
const { navigateToDataflowResult } = useNavigatePage();
|
|
const [logInfo, setLogInfo] = useState<IFileLogItem>();
|
|
const kowledgeId = useParams().id;
|
|
const showLog = (row: Row<IFileLogItem & DocumentLog>) => {
|
|
const logDetail = {
|
|
taskId: row.original?.dsl?.task_id,
|
|
fileName: row.original.document_name,
|
|
source: row.original.source_from,
|
|
task: row.original?.task_type,
|
|
status: row.original.status as RunningStatus,
|
|
startDate: formatDate(row.original.process_begin_at),
|
|
duration: formatSecondsToHumanReadable(
|
|
row.original.process_duration || 0,
|
|
),
|
|
details: row.original.progress_msg,
|
|
} as unknown as IFileLogItem;
|
|
console.log('logDetail', logDetail);
|
|
setLogInfo(logDetail);
|
|
setIsModalVisible(true);
|
|
};
|
|
|
|
const columns = useMemo(() => {
|
|
return active === LogTabs.FILE_LOGS
|
|
? getFileLogsTableColumns(
|
|
t,
|
|
showLog,
|
|
kowledgeId || '',
|
|
navigateToDataflowResult,
|
|
)
|
|
: getDatasetLogsTableColumns(t, showLog);
|
|
}, [active, t]);
|
|
|
|
const currentPagination = useMemo(
|
|
() => ({
|
|
pageIndex: (pagination.current || 1) - 1,
|
|
pageSize: pagination.pageSize || 10,
|
|
}),
|
|
[pagination],
|
|
);
|
|
|
|
const table = useReactTable<IFileLogItem & DocumentLog>({
|
|
data: data || [],
|
|
columns,
|
|
manualPagination: true,
|
|
getCoreRowModel: getCoreRowModel(),
|
|
getPaginationRowModel: getPaginationRowModel(),
|
|
getSortedRowModel: getSortedRowModel(),
|
|
getFilteredRowModel: getFilteredRowModel(),
|
|
onSortingChange: setSorting,
|
|
onColumnFiltersChange: setColumnFilters,
|
|
onRowSelectionChange: setRowSelection,
|
|
state: {
|
|
sorting,
|
|
columnFilters,
|
|
rowSelection,
|
|
pagination: currentPagination,
|
|
},
|
|
pageCount: pagination.total
|
|
? Math.ceil(pagination.total / pagination.pageSize)
|
|
: 0,
|
|
});
|
|
|
|
return (
|
|
<div className="w-full h-[calc(100vh-360px)]">
|
|
<Table rootClassName="max-h-[calc(100vh-380px)]">
|
|
<TableHeader>
|
|
{table.getHeaderGroups().map((headerGroup) => (
|
|
<TableRow key={headerGroup.id}>
|
|
{headerGroup.headers.map((header) => (
|
|
<TableHead key={header.id}>
|
|
{flexRender(
|
|
header.column.columnDef.header,
|
|
header.getContext(),
|
|
)}
|
|
</TableHead>
|
|
))}
|
|
</TableRow>
|
|
))}
|
|
</TableHeader>
|
|
<TableBody className="relative min-w-[1280px] overflow-auto">
|
|
{table.getRowModel().rows?.length ? (
|
|
table.getRowModel().rows.map((row) => (
|
|
<TableRow
|
|
key={row.id}
|
|
data-state={row.getIsSelected() && 'selected'}
|
|
className="group"
|
|
>
|
|
{row.getVisibleCells().map((cell) => (
|
|
<TableCell
|
|
key={cell.id}
|
|
className={cell.column.columnDef.meta?.cellClassName}
|
|
>
|
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
</TableCell>
|
|
))}
|
|
</TableRow>
|
|
))
|
|
) : (
|
|
<TableRow>
|
|
<TableCell colSpan={columns.length} className="h-24 text-center">
|
|
No results.
|
|
</TableCell>
|
|
</TableRow>
|
|
)}
|
|
</TableBody>
|
|
</Table>
|
|
<div className="flex items-center justify-end absolute bottom-3 right-12">
|
|
<div className="space-x-2">
|
|
<RAGFlowPagination
|
|
{...{ current: pagination.current, pageSize: pagination.pageSize }}
|
|
total={pagination.total}
|
|
onChange={(page, pageSize) => setPagination({ page, pageSize })}
|
|
/>
|
|
</div>
|
|
</div>
|
|
{isModalVisible && (
|
|
<ProcessLogModal
|
|
title={active === LogTabs.FILE_LOGS ? t('fileLogs') : t('datasetLog')}
|
|
visible={isModalVisible}
|
|
onCancel={() => setIsModalVisible(false)}
|
|
logInfo={logInfo}
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default FileLogsTable;
|