From bdebd1b2e305ded7067722f8b59fcc7892bb0943 Mon Sep 17 00:00:00 2001 From: balibabu Date: Sun, 27 Apr 2025 14:39:05 +0800 Subject: [PATCH] Feat: Filter document by running status and file type. #3221 (#7340) ### What problem does this PR solve? Feat: Filter document by running status and file type. #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- .../components/chunk-method-dialog/index.tsx | 6 +- .../list-filter-bar/filter-popover.tsx | 163 ++++++++++++++++++ .../index.tsx} | 37 ++-- .../components/list-filter-bar/interface.ts | 15 ++ .../use-handle-filter-submit.ts | 12 ++ .../raptor-form-fields.tsx | 4 +- web/src/hooks/document-hooks.ts | 4 +- web/src/hooks/use-document-request.ts | 54 +++++- web/src/hooks/use-knowledge-request.ts | 16 +- web/src/interfaces/request/knowledge.ts | 6 + .../pages/dataset/dataset/dataset-table.tsx | 18 +- web/src/pages/dataset/dataset/index.tsx | 27 ++- .../dataset/dataset/use-select-filters.ts | 31 ++++ web/src/pages/datasets/index.tsx | 19 +- web/src/pages/datasets/use-select-owners.ts | 26 +-- web/src/services/knowledge-service.ts | 6 + web/src/utils/dataset-util.ts | 24 +++ 17 files changed, 392 insertions(+), 76 deletions(-) create mode 100644 web/src/components/list-filter-bar/filter-popover.tsx rename web/src/components/{list-filter-bar.tsx => list-filter-bar/index.tsx} (60%) create mode 100644 web/src/components/list-filter-bar/interface.ts create mode 100644 web/src/components/list-filter-bar/use-handle-filter-submit.ts create mode 100644 web/src/pages/dataset/dataset/use-select-filters.ts diff --git a/web/src/components/chunk-method-dialog/index.tsx b/web/src/components/chunk-method-dialog/index.tsx index 5205438d1..d45b9416c 100644 --- a/web/src/components/chunk-method-dialog/index.tsx +++ b/web/src/components/chunk-method-dialog/index.tsx @@ -281,7 +281,10 @@ export function ChunkMethodDialog({ )} /> )} - + {showOne && } {showMaxTokenNumber && ( <> @@ -298,6 +301,7 @@ export function ChunkMethodDialog({ {showAutoKeywords(selectedTag) && ( <> diff --git a/web/src/components/list-filter-bar/filter-popover.tsx b/web/src/components/list-filter-bar/filter-popover.tsx new file mode 100644 index 000000000..9064379eb --- /dev/null +++ b/web/src/components/list-filter-bar/filter-popover.tsx @@ -0,0 +1,163 @@ +import { + Popover, + PopoverContent, + PopoverTrigger, +} from '@/components/ui/popover'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { PropsWithChildren, useCallback, useEffect } from 'react'; +import { useForm } from 'react-hook-form'; +import { ZodArray, ZodString, z } from 'zod'; + +import { Button } from '@/components/ui/button'; +import { Checkbox } from '@/components/ui/checkbox'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { FilterChange, FilterCollection, FilterValue } from './interface'; + +export type CheckboxFormMultipleProps = { + filters?: FilterCollection[]; + value?: FilterValue; + onChange?: FilterChange; +}; + +function CheckboxFormMultiple({ + filters = [], + value, + onChange, +}: CheckboxFormMultipleProps) { + const fieldsDict = filters?.reduce>>((pre, cur) => { + pre[cur.field] = []; + return pre; + }, {}); + + const FormSchema = z.object( + filters.reduce>>((pre, cur) => { + pre[cur.field] = z.array(z.string()); + + // .refine((value) => value.some((item) => item), { + // message: 'You have to select at least one item.', + // }); + return pre; + }, {}), + ); + + const form = useForm>({ + resolver: zodResolver(FormSchema), + defaultValues: fieldsDict, + }); + + function onSubmit(data: z.infer) { + console.log('🚀 ~ onSubmit ~ data:', data); + // setOwnerIds(data.items); + onChange?.(data); + } + + const onReset = useCallback(() => { + onChange?.(fieldsDict); + }, [fieldsDict, onChange]); + + useEffect(() => { + form.reset(value); + }, [form, value]); + + return ( +
+ form.reset()} + > + {filters.map((x) => ( + ( + +
+ {x.label} +
+ {x.list.map((item) => ( + { + return ( +
+ + + { + return checked + ? field.onChange([...field.value, item.id]) + : field.onChange( + field.value?.filter( + (value) => value !== item.id, + ), + ); + }} + /> + + + {item.label} + + + {item.count} +
+ ); + }} + /> + ))} + +
+ )} + /> + ))} +
+ + +
+ + + ); +} + +export function FilterPopover({ + children, + value, + onChange, + filters, +}: PropsWithChildren & CheckboxFormMultipleProps) { + return ( + + {children} + + + + + ); +} diff --git a/web/src/components/list-filter-bar.tsx b/web/src/components/list-filter-bar/index.tsx similarity index 60% rename from web/src/components/list-filter-bar.tsx rename to web/src/components/list-filter-bar/index.tsx index 20795ddbc..d05a6d5f7 100644 --- a/web/src/components/list-filter-bar.tsx +++ b/web/src/components/list-filter-bar/index.tsx @@ -1,19 +1,18 @@ import { ChevronDown } from 'lucide-react'; import React, { ChangeEventHandler, - FunctionComponent, PropsWithChildren, ReactNode, + useMemo, } from 'react'; -import { Button, ButtonProps } from './ui/button'; -import { SearchInput } from './ui/input'; +import { Button, ButtonProps } from '../ui/button'; +import { SearchInput } from '../ui/input'; +import { CheckboxFormMultipleProps, FilterPopover } from './filter-popover'; interface IProps { title?: string; - FilterPopover?: FunctionComponent; searchString?: string; onSearchChange?: ChangeEventHandler; - count?: number; showFilter?: boolean; leftPanel?: ReactNode; } @@ -32,25 +31,31 @@ const FilterButton = React.forwardRef< export default function ListFilterBar({ title, children, - FilterPopover, searchString, onSearchChange, - count, showFilter = true, leftPanel, -}: PropsWithChildren) { + value, + onChange, + filters, +}: PropsWithChildren) { + const filterCount = useMemo(() => { + return typeof value === 'object' && value !== null + ? Object.values(value).reduce((pre, cur) => { + return pre + cur.length; + }, 0) + : 0; + }, [value]); + return (
{leftPanel || title}
- {showFilter && - (FilterPopover ? ( - - - - ) : ( - - ))} + {showFilter && ( + + + + )} >; + +export type FilterChange = (value: FilterValue) => void; diff --git a/web/src/components/list-filter-bar/use-handle-filter-submit.ts b/web/src/components/list-filter-bar/use-handle-filter-submit.ts new file mode 100644 index 000000000..55cc4c6fe --- /dev/null +++ b/web/src/components/list-filter-bar/use-handle-filter-submit.ts @@ -0,0 +1,12 @@ +import { useCallback, useState } from 'react'; +import { FilterChange, FilterValue } from './interface'; + +export function useHandleFilterSubmit() { + const [filterValue, setFilterValue] = useState({}); + + const handleFilterSubmit: FilterChange = useCallback((value) => { + setFilterValue(value); + }, []); + + return { filterValue, setFilterValue, handleFilterSubmit }; +} diff --git a/web/src/components/parse-configuration/raptor-form-fields.tsx b/web/src/components/parse-configuration/raptor-form-fields.tsx index 835108ae7..28cd79ced 100644 --- a/web/src/components/parse-configuration/raptor-form-fields.tsx +++ b/web/src/components/parse-configuration/raptor-form-fields.tsx @@ -76,7 +76,7 @@ const RaptorFormFields = () => { )} /> {useRaptor && ( - <> +
{ )} /> - +
)} ); diff --git a/web/src/hooks/document-hooks.ts b/web/src/hooks/document-hooks.ts index 0cddd1591..98ff5141f 100644 --- a/web/src/hooks/document-hooks.ts +++ b/web/src/hooks/document-hooks.ts @@ -7,7 +7,7 @@ import { } from '@/interfaces/request/document'; import i18n from '@/locales/config'; import chatService from '@/services/chat-service'; -import kbService from '@/services/knowledge-service'; +import kbService, { listDocument } from '@/services/knowledge-service'; import api, { api_host } from '@/utils/api'; import { buildChunkHighlights } from '@/utils/document-util'; import { post } from '@/utils/request'; @@ -73,7 +73,7 @@ export const useFetchNextDocumentList = () => { refetchInterval: 15000, enabled: !!knowledgeId || !!id, queryFn: async () => { - const ret = await kbService.get_document_list({ + const ret = await listDocument({ kb_id: knowledgeId || id, keywords: searchString, page_size: pagination.pageSize, diff --git a/web/src/hooks/use-document-request.ts b/web/src/hooks/use-document-request.ts index 103df4326..0fc2afdb8 100644 --- a/web/src/hooks/use-document-request.ts +++ b/web/src/hooks/use-document-request.ts @@ -1,10 +1,11 @@ +import { useHandleFilterSubmit } from '@/components/list-filter-bar/use-handle-filter-submit'; import { IDocumentInfo } from '@/interfaces/database/document'; import { IChangeParserConfigRequestBody, IDocumentMetaRequestBody, } from '@/interfaces/request/document'; import i18n from '@/locales/config'; -import kbService from '@/services/knowledge-service'; +import kbService, { listDocument } from '@/services/knowledge-service'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { useDebounce } from 'ahooks'; import { message } from 'antd'; @@ -26,6 +27,7 @@ export const enum DocumentApiAction { SaveDocumentName = 'saveDocumentName', SetDocumentParser = 'setDocumentParser', SetDocumentMeta = 'setDocumentMeta', + FetchAllDocumentList = 'fetchAllDocumentList', } export const useUploadNextDocument = () => { @@ -74,6 +76,7 @@ export const useFetchDocumentList = () => { const { pagination, setPagination } = useGetPaginationWithRouter(); const { id } = useParams(); const debouncedSearchString = useDebounce(searchString, { wait: 500 }); + const { filterValue, handleFilterSubmit } = useHandleFilterSubmit(); const { data, isFetching: loading } = useQuery<{ docs: IDocumentInfo[]; @@ -83,17 +86,24 @@ export const useFetchDocumentList = () => { DocumentApiAction.FetchDocumentList, debouncedSearchString, pagination, + filterValue, ], initialData: { docs: [], total: 0 }, refetchInterval: 15000, enabled: !!knowledgeId || !!id, queryFn: async () => { - const ret = await kbService.get_document_list({ - kb_id: knowledgeId || id, - keywords: debouncedSearchString, - page_size: pagination.pageSize, - page: pagination.current, - }); + const ret = await listDocument( + { + kb_id: knowledgeId || id, + keywords: debouncedSearchString, + page_size: pagination.pageSize, + page: pagination.current, + }, + { + types: filterValue.type, + run_status: filterValue.run, + }, + ); if (ret.data.code === 0) { return ret.data.data; } @@ -120,9 +130,39 @@ export const useFetchDocumentList = () => { pagination: { ...pagination, total: data?.total }, handleInputChange: onInputChange, setPagination, + filterValue, + handleFilterSubmit, }; }; +export function useFetchAllDocumentList() { + const { id } = useParams(); + const { data, isFetching: loading } = useQuery<{ + docs: IDocumentInfo[]; + total: number; + }>({ + queryKey: [DocumentApiAction.FetchAllDocumentList], + initialData: { docs: [], total: 0 }, + refetchInterval: 15000, + enabled: !!id, + queryFn: async () => { + const ret = await listDocument({ + kb_id: id, + }); + if (ret.data.code === 0) { + return ret.data.data; + } + + return { + docs: [], + total: 0, + }; + }, + }); + + return { data, loading }; +} + export const useSetDocumentStatus = () => { const queryClient = useQueryClient(); diff --git a/web/src/hooks/use-knowledge-request.ts b/web/src/hooks/use-knowledge-request.ts index 1530065e1..9523bb182 100644 --- a/web/src/hooks/use-knowledge-request.ts +++ b/web/src/hooks/use-knowledge-request.ts @@ -1,3 +1,4 @@ +import { useHandleFilterSubmit } from '@/components/list-filter-bar/use-handle-filter-submit'; import { IKnowledge, IKnowledgeResult, @@ -72,8 +73,8 @@ export const useTestRetrieval = () => { export const useFetchNextKnowledgeListByPage = () => { const { searchString, handleInputChange } = useHandleSearchChange(); const { pagination, setPagination } = useGetPaginationWithRouter(); - const [ownerIds, setOwnerIds] = useState([]); const debouncedSearchString = useDebounce(searchString, { wait: 500 }); + const { filterValue, handleFilterSubmit } = useHandleFilterSubmit(); const { data, isFetching: loading } = useQuery({ queryKey: [ @@ -81,7 +82,7 @@ export const useFetchNextKnowledgeListByPage = () => { { debouncedSearchString, ...pagination, - ownerIds, + filterValue, }, ], initialData: { @@ -97,7 +98,7 @@ export const useFetchNextKnowledgeListByPage = () => { page: pagination.current, }, { - owner_ids: ownerIds, + owner_ids: filterValue.owner, }, ); @@ -113,11 +114,6 @@ export const useFetchNextKnowledgeListByPage = () => { [handleInputChange], ); - const handleOwnerIdsChange = useCallback((ids: string[]) => { - // setPagination({ page: 1 }); // TODO: 这里导致重复请求 - setOwnerIds(ids); - }, []); - return { ...data, searchString, @@ -125,8 +121,8 @@ export const useFetchNextKnowledgeListByPage = () => { pagination: { ...pagination, total: data?.total }, setPagination, loading, - setOwnerIds: handleOwnerIdsChange, - ownerIds, + filterValue, + handleFilterSubmit, }; }; diff --git a/web/src/interfaces/request/knowledge.ts b/web/src/interfaces/request/knowledge.ts index 696f4a60f..f18fb35b9 100644 --- a/web/src/interfaces/request/knowledge.ts +++ b/web/src/interfaces/request/knowledge.ts @@ -14,7 +14,13 @@ export interface IFetchKnowledgeListRequestBody { } export interface IFetchKnowledgeListRequestParams { + kb_id?: string; keywords?: string; page?: number; page_size?: number; } + +export interface IFetchDocumentListRequestBody { + types?: string[]; + run_status?: string[]; +} diff --git a/web/src/pages/dataset/dataset/dataset-table.tsx b/web/src/pages/dataset/dataset/dataset-table.tsx index fe21ca65f..3b61d7ba6 100644 --- a/web/src/pages/dataset/dataset/dataset-table.tsx +++ b/web/src/pages/dataset/dataset/dataset-table.tsx @@ -35,14 +35,16 @@ import { useDatasetTableColumns } from './use-dataset-table-columns'; import { useRenameDocument } from './use-rename-document'; import { useSaveMeta } from './use-save-meta'; -export function DatasetTable() { - const { - // searchString, - documents, - pagination, - // handleInputChange, - setPagination, - } = useFetchDocumentList(); +export type DatasetTableProps = Pick< + ReturnType, + 'documents' | 'setPagination' | 'pagination' +>; + +export function DatasetTable({ + documents, + pagination, + setPagination, +}: DatasetTableProps) { const [sorting, setSorting] = React.useState([]); const [columnFilters, setColumnFilters] = React.useState( [], diff --git a/web/src/pages/dataset/dataset/index.tsx b/web/src/pages/dataset/dataset/index.tsx index f2c013cc4..723443bfe 100644 --- a/web/src/pages/dataset/dataset/index.tsx +++ b/web/src/pages/dataset/dataset/index.tsx @@ -2,9 +2,11 @@ import { BulkOperateBar } from '@/components/bulk-operate-bar'; import { FileUploadDialog } from '@/components/file-upload-dialog'; import ListFilterBar from '@/components/list-filter-bar'; import { Button } from '@/components/ui/button'; +import { useFetchDocumentList } from '@/hooks/use-document-request'; import { Upload } from 'lucide-react'; import { DatasetTable } from './dataset-table'; import { useBulkOperateDataset } from './use-bulk-operate-dataset'; +import { useSelectDatasetFilters } from './use-select-filters'; import { useHandleUploadDocument } from './use-upload-document'; export default function Dataset() { @@ -16,10 +18,27 @@ export default function Dataset() { documentUploadLoading, } = useHandleUploadDocument(); const { list } = useBulkOperateDataset(); + const { + searchString, + documents, + pagination, + handleInputChange, + setPagination, + filterValue, + handleFilterSubmit, + } = useFetchDocumentList(); + const { filters } = useSelectDatasetFilters(); return (
- +