mirror of
https://git.mirrors.martin98.com/https://github.com/infiniflow/ragflow.git
synced 2025-08-12 04:29:10 +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",
|
"@tailwindcss/line-clamp": "^0.4.4",
|
||||||
"@tanstack/react-query": "^5.40.0",
|
"@tanstack/react-query": "^5.40.0",
|
||||||
"@tanstack/react-query-devtools": "^5.51.5",
|
"@tanstack/react-query-devtools": "^5.51.5",
|
||||||
|
"@tanstack/react-table": "^8.20.5",
|
||||||
"@uiw/react-markdown-preview": "^5.1.3",
|
"@uiw/react-markdown-preview": "^5.1.3",
|
||||||
"ahooks": "^3.7.10",
|
"ahooks": "^3.7.10",
|
||||||
"antd": "^5.12.7",
|
"antd": "^5.12.7",
|
||||||
@ -5609,6 +5610,37 @@
|
|||||||
"url": "https://github.com/sponsors/tannerlinsley"
|
"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": {
|
"node_modules/@testing-library/dom": {
|
||||||
"version": "10.1.0",
|
"version": "10.1.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@testing-library/dom/-/dom-10.1.0.tgz",
|
"resolved": "https://registry.npmmirror.com/@testing-library/dom/-/dom-10.1.0.tgz",
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
"@tailwindcss/line-clamp": "^0.4.4",
|
"@tailwindcss/line-clamp": "^0.4.4",
|
||||||
"@tanstack/react-query": "^5.40.0",
|
"@tanstack/react-query": "^5.40.0",
|
||||||
"@tanstack/react-query-devtools": "^5.51.5",
|
"@tanstack/react-query-devtools": "^5.51.5",
|
||||||
|
"@tanstack/react-table": "^8.20.5",
|
||||||
"@uiw/react-markdown-preview": "^5.1.3",
|
"@uiw/react-markdown-preview": "^5.1.3",
|
||||||
"ahooks": "^3.7.10",
|
"ahooks": "^3.7.10",
|
||||||
"antd": "^5.12.7",
|
"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;
|
name: string;
|
||||||
parser_config: IParserConfig;
|
parser_config: IParserConfig;
|
||||||
parser_id: string;
|
parser_id: string;
|
||||||
process_begin_at: null;
|
process_begin_at?: string;
|
||||||
process_duation: number;
|
process_duation: number;
|
||||||
progress: number;
|
progress: number;
|
||||||
progress_msg: string;
|
progress_msg: string;
|
||||||
@ -27,11 +27,11 @@ export interface IDocumentInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface IParserConfig {
|
export interface IParserConfig {
|
||||||
delimiter: string;
|
delimiter?: string;
|
||||||
html4excel: boolean;
|
html4excel?: boolean;
|
||||||
layout_recognize: boolean;
|
layout_recognize?: boolean;
|
||||||
pages: any[];
|
pages: any[];
|
||||||
raptor: Raptor;
|
raptor?: Raptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface 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() {
|
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 (
|
return (
|
||||||
<div className="text-foreground flex">
|
<div className="text-foreground flex">
|
||||||
<SideBar></SideBar>
|
<SideBar></SideBar>
|
||||||
<div className="p-6">
|
<div className="flex-1">
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,12 +1,7 @@
|
|||||||
|
import ListFilterBar from '@/components/list-filter-bar';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Card, CardContent } from '@/components/ui/card';
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
import {
|
import { ChevronRight, MoreHorizontal, Plus } from 'lucide-react';
|
||||||
ChevronRight,
|
|
||||||
Filter,
|
|
||||||
MoreHorizontal,
|
|
||||||
Plus,
|
|
||||||
Search,
|
|
||||||
} from 'lucide-react';
|
|
||||||
|
|
||||||
const datasets = [
|
const datasets = [
|
||||||
{
|
{
|
||||||
@ -86,17 +81,10 @@ const datasets = [
|
|||||||
export default function Datasets() {
|
export default function Datasets() {
|
||||||
return (
|
return (
|
||||||
<section className="p-8 text-foreground">
|
<section className="p-8 text-foreground">
|
||||||
<div className="flex justify-between mb-6">
|
<ListFilterBar title="Datasets">
|
||||||
<span className="text-3xl font-bold ">Datasets</span>
|
<Plus className="mr-2 h-4 w-4" />
|
||||||
<div className="flex gap-4 items-center">
|
Create dataset
|
||||||
<Filter className="size-5" />
|
</ListFilterBar>
|
||||||
<Search className="size-5" />
|
|
||||||
<Button variant={'tertiary'} size={'sm'}>
|
|
||||||
<Plus className="mr-2 h-4 w-4" />
|
|
||||||
Create dataset
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<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">
|
<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) => (
|
{datasets.map((dataset) => (
|
||||||
<Card
|
<Card
|
||||||
|
Loading…
x
Reference in New Issue
Block a user