mirror of
https://git.mirrors.martin98.com/https://github.com/infiniflow/ragflow.git
synced 2025-08-11 18:48:59 +08:00
### What problem does this PR solve? Feat: Add Dataset page #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
parent
cdae8d28fe
commit
4e8e4fe53f
32
web/package-lock.json
generated
32
web/package-lock.json
generated
@ -30,6 +30,7 @@
|
||||
"@tailwindcss/line-clamp": "^0.4.4",
|
||||
"@tanstack/react-query": "^5.40.0",
|
||||
"@tanstack/react-query-devtools": "^5.51.5",
|
||||
"@tanstack/react-table": "^8.20.5",
|
||||
"@uiw/react-markdown-preview": "^5.1.3",
|
||||
"ahooks": "^3.7.10",
|
||||
"antd": "^5.12.7",
|
||||
@ -5609,6 +5610,37 @@
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/react-table": {
|
||||
"version": "8.20.5",
|
||||
"resolved": "https://registry.npmmirror.com/@tanstack/react-table/-/react-table-8.20.5.tgz",
|
||||
"integrity": "sha512-WEHopKw3znbUZ61s9i0+i9g8drmDo6asTWbrQh8Us63DAk/M0FkmIqERew6P71HI75ksZ2Pxyuf4vvKh9rAkiA==",
|
||||
"dependencies": {
|
||||
"@tanstack/table-core": "8.20.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8",
|
||||
"react-dom": ">=16.8"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/table-core": {
|
||||
"version": "8.20.5",
|
||||
"resolved": "https://registry.npmmirror.com/@tanstack/table-core/-/table-core-8.20.5.tgz",
|
||||
"integrity": "sha512-P9dF7XbibHph2PFRz8gfBKEXEY/HJPOhym8CHmjF8y3q5mWpKx9xtZapXQUWCgkqvsK0R46Azuz+VaxD4Xl+Tg==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
}
|
||||
},
|
||||
"node_modules/@testing-library/dom": {
|
||||
"version": "10.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/@testing-library/dom/-/dom-10.1.0.tgz",
|
||||
|
@ -41,6 +41,7 @@
|
||||
"@tailwindcss/line-clamp": "^0.4.4",
|
||||
"@tanstack/react-query": "^5.40.0",
|
||||
"@tanstack/react-query-devtools": "^5.51.5",
|
||||
"@tanstack/react-table": "^8.20.5",
|
||||
"@uiw/react-markdown-preview": "^5.1.3",
|
||||
"ahooks": "^3.7.10",
|
||||
"antd": "^5.12.7",
|
||||
|
25
web/src/components/list-filter-bar.tsx
Normal file
25
web/src/components/list-filter-bar.tsx
Normal file
@ -0,0 +1,25 @@
|
||||
import { Filter, Search } from 'lucide-react';
|
||||
import { PropsWithChildren } from 'react';
|
||||
import { Button } from './ui/button';
|
||||
|
||||
interface IProps {
|
||||
title: string;
|
||||
}
|
||||
|
||||
export default function ListFilterBar({
|
||||
title,
|
||||
children,
|
||||
}: PropsWithChildren<IProps>) {
|
||||
return (
|
||||
<div className="flex justify-between mb-6">
|
||||
<span className="text-3xl font-bold ">{title}</span>
|
||||
<div className="flex gap-4 items-center">
|
||||
<Filter className="size-5" />
|
||||
<Search className="size-5" />
|
||||
<Button variant={'tertiary'} size={'sm'}>
|
||||
{children}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -11,7 +11,7 @@ export interface IDocumentInfo {
|
||||
name: string;
|
||||
parser_config: IParserConfig;
|
||||
parser_id: string;
|
||||
process_begin_at: null;
|
||||
process_begin_at?: string;
|
||||
process_duation: number;
|
||||
progress: number;
|
||||
progress_msg: string;
|
||||
@ -27,11 +27,11 @@ export interface IDocumentInfo {
|
||||
}
|
||||
|
||||
export interface IParserConfig {
|
||||
delimiter: string;
|
||||
html4excel: boolean;
|
||||
layout_recognize: boolean;
|
||||
delimiter?: string;
|
||||
html4excel?: boolean;
|
||||
layout_recognize?: boolean;
|
||||
pages: any[];
|
||||
raptor: Raptor;
|
||||
raptor?: Raptor;
|
||||
}
|
||||
|
||||
interface Raptor {
|
||||
|
268
web/src/pages/dataset/dataset/dataset-table.tsx
Normal file
268
web/src/pages/dataset/dataset/dataset-table.tsx
Normal file
@ -0,0 +1,268 @@
|
||||
'use client';
|
||||
|
||||
import {
|
||||
ColumnDef,
|
||||
ColumnFiltersState,
|
||||
SortingState,
|
||||
VisibilityState,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getFilteredRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
useReactTable,
|
||||
} from '@tanstack/react-table';
|
||||
import { ArrowUpDown, MoreHorizontal } from 'lucide-react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from '@/components/ui/table';
|
||||
import { RunningStatus } from '@/constants/knowledge';
|
||||
import { IDocumentInfo } from '@/interfaces/database/document';
|
||||
|
||||
const data: IDocumentInfo[] = [
|
||||
{
|
||||
chunk_num: 1,
|
||||
create_date: 'Thu, 28 Nov 2024 17:10:22 GMT',
|
||||
create_time: 1732785022792,
|
||||
created_by: 'b0975cb4bc3111ee9b830aef05f5e94f',
|
||||
id: '990cb30ead6811efb9b9fa163e197198',
|
||||
kb_id: '25a8cfbe9cd411efbc12fa163e197198',
|
||||
location: 'mian.jpg',
|
||||
name: 'mian.jpg',
|
||||
parser_config: {
|
||||
pages: [[1, 1000000]],
|
||||
},
|
||||
parser_id: 'picture',
|
||||
process_begin_at: 'Thu, 28 Nov 2024 17:10:25 GMT',
|
||||
process_duation: 8.46185,
|
||||
progress: 1,
|
||||
progress_msg:
|
||||
'\nTask has been received.\nPage(1~100000001): Finish OCR: (用小麦粉\n金\nONGXI ...)\nPage(1~100000001): OCR results is too long to use CV LLM.\nPage(1~100000001): Finished slicing files (1 chunks in 0.34s). Start to embedding the content.\nPage(1~100000001): Finished embedding (in 0.35s)! Start to build index!\nPage(1~100000001): Indexing elapsed in 0.02s.\nPage(1~100000001): Done!',
|
||||
run: RunningStatus.RUNNING,
|
||||
size: 19692,
|
||||
source_type: 'local',
|
||||
status: '1',
|
||||
thumbnail:
|
||||
'/v1/document/image/25a8cfbe9cd411efbc12fa163e197198-thumbnail_990cb30ead6811efb9b9fa163e197198.png',
|
||||
token_num: 115,
|
||||
type: 'visual',
|
||||
update_date: 'Thu, 28 Nov 2024 17:10:33 GMT',
|
||||
update_time: 1732785033462,
|
||||
},
|
||||
];
|
||||
|
||||
export const columns: ColumnDef<IDocumentInfo>[] = [
|
||||
{
|
||||
id: 'select',
|
||||
header: ({ table }) => (
|
||||
<Checkbox
|
||||
checked={
|
||||
table.getIsAllPageRowsSelected() ||
|
||||
(table.getIsSomePageRowsSelected() && 'indeterminate')
|
||||
}
|
||||
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
|
||||
aria-label="Select all"
|
||||
/>
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
<Checkbox
|
||||
checked={row.getIsSelected()}
|
||||
onCheckedChange={(value) => row.toggleSelected(!!value)}
|
||||
aria-label="Select row"
|
||||
/>
|
||||
),
|
||||
enableSorting: false,
|
||||
enableHiding: false,
|
||||
},
|
||||
{
|
||||
accessorKey: 'status',
|
||||
header: 'Status',
|
||||
cell: ({ row }) => (
|
||||
<div className="capitalize">{row.getValue('status')}</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: 'email',
|
||||
header: ({ column }) => {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
||||
>
|
||||
Email
|
||||
<ArrowUpDown />
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
cell: ({ row }) => <div className="lowercase">{row.getValue('email')}</div>,
|
||||
},
|
||||
{
|
||||
accessorKey: 'amount',
|
||||
header: () => <div className="text-right">Amount</div>,
|
||||
cell: ({ row }) => {
|
||||
const amount = parseFloat(row.getValue('amount'));
|
||||
|
||||
// Format the amount as a dollar amount
|
||||
const formatted = new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: 'USD',
|
||||
}).format(amount);
|
||||
|
||||
return <div className="text-right font-medium">{formatted}</div>;
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'actions',
|
||||
enableHiding: false,
|
||||
cell: ({ row }) => {
|
||||
const payment = row.original;
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" className="h-8 w-8 p-0">
|
||||
<span className="sr-only">Open menu</span>
|
||||
<MoreHorizontal />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuLabel>Actions</DropdownMenuLabel>
|
||||
<DropdownMenuItem
|
||||
onClick={() => navigator.clipboard.writeText(payment.id)}
|
||||
>
|
||||
Copy payment ID
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem>View customer</DropdownMenuItem>
|
||||
<DropdownMenuItem>View payment details</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export function DatasetTable() {
|
||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||
[],
|
||||
);
|
||||
const [columnVisibility, setColumnVisibility] =
|
||||
React.useState<VisibilityState>({});
|
||||
const [rowSelection, setRowSelection] = React.useState({});
|
||||
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
onSortingChange: setSorting,
|
||||
onColumnFiltersChange: setColumnFilters,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
onColumnVisibilityChange: setColumnVisibility,
|
||||
onRowSelectionChange: setRowSelection,
|
||||
state: {
|
||||
sorting,
|
||||
columnFilters,
|
||||
columnVisibility,
|
||||
rowSelection,
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div className="rounded-md border">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => {
|
||||
return (
|
||||
<TableHead key={header.id}>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext(),
|
||||
)}
|
||||
</TableHead>
|
||||
);
|
||||
})}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{table.getRowModel().rows?.length ? (
|
||||
table.getRowModel().rows.map((row) => (
|
||||
<TableRow
|
||||
key={row.id}
|
||||
data-state={row.getIsSelected() && 'selected'}
|
||||
>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell key={cell.id}>
|
||||
{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>
|
||||
<div className="flex items-center justify-end space-x-2 py-4">
|
||||
<div className="flex-1 text-sm text-muted-foreground">
|
||||
{table.getFilteredSelectedRowModel().rows.length} of{' '}
|
||||
{table.getFilteredRowModel().rows.length} row(s) selected.
|
||||
</div>
|
||||
<div className="space-x-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => table.previousPage()}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
Previous
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => table.nextPage()}
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,3 +1,15 @@
|
||||
import ListFilterBar from '@/components/list-filter-bar';
|
||||
import { Upload } from 'lucide-react';
|
||||
import { DatasetTable } from './dataset-table';
|
||||
|
||||
export default function Dataset() {
|
||||
return <div>Outset</div>;
|
||||
return (
|
||||
<section className="p-8 text-foreground">
|
||||
<ListFilterBar title="Files">
|
||||
<Upload />
|
||||
Upload file
|
||||
</ListFilterBar>
|
||||
<DatasetTable></DatasetTable>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ export default function DatasetWrapper() {
|
||||
return (
|
||||
<div className="text-foreground flex">
|
||||
<SideBar></SideBar>
|
||||
<div className="p-6">
|
||||
<div className="flex-1">
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,12 +1,7 @@
|
||||
import ListFilterBar from '@/components/list-filter-bar';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import {
|
||||
ChevronRight,
|
||||
Filter,
|
||||
MoreHorizontal,
|
||||
Plus,
|
||||
Search,
|
||||
} from 'lucide-react';
|
||||
import { ChevronRight, MoreHorizontal, Plus } from 'lucide-react';
|
||||
|
||||
const datasets = [
|
||||
{
|
||||
@ -86,17 +81,10 @@ const datasets = [
|
||||
export default function Datasets() {
|
||||
return (
|
||||
<section className="p-8 text-foreground">
|
||||
<div className="flex justify-between mb-6">
|
||||
<span className="text-3xl font-bold ">Datasets</span>
|
||||
<div className="flex gap-4 items-center">
|
||||
<Filter className="size-5" />
|
||||
<Search className="size-5" />
|
||||
<Button variant={'tertiary'} size={'sm'}>
|
||||
<Plus className="mr-2 h-4 w-4" />
|
||||
Create dataset
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<ListFilterBar title="Datasets">
|
||||
<Plus className="mr-2 h-4 w-4" />
|
||||
Create dataset
|
||||
</ListFilterBar>
|
||||
<div className="grid gap-6 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-6 2xl:grid-cols-8">
|
||||
{datasets.map((dataset) => (
|
||||
<Card
|
||||
|
Loading…
x
Reference in New Issue
Block a user