Fix: Remove antd from dataset-page (#8830)

### What problem does this PR solve?

remove antd from dataset-page
[#3221](https://github.com/infiniflow/ragflow/issues/3221)
### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
chanx
2025-07-15 16:12:50 +08:00
committed by GitHub
parent 148fde8b1b
commit ed8d7291ff
27 changed files with 916 additions and 460 deletions

View File

@ -1,10 +1,28 @@
import EditTag from '@/components/edit-tag';
import Divider from '@/components/ui/divider';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@/components/ui/form';
import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from '@/components/ui/hover-card';
import { Modal } from '@/components/ui/modal';
import Space from '@/components/ui/space';
import { Switch } from '@/components/ui/switch';
import { Textarea } from '@/components/ui/textarea';
import { useFetchChunk } from '@/hooks/chunk-hooks';
import { IModalProps } from '@/interfaces/common';
import { IChunk } from '@/interfaces/database/knowledge';
import { DeleteOutlined } from '@ant-design/icons';
import { Divider, Form, Input, Modal, Space, Switch } from 'antd';
import { Trash2 } from 'lucide-react';
import React, { useCallback, useEffect, useState } from 'react';
import { FieldValues, FormProvider, useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { useDeleteChunkByIds } from '../../hooks';
import {
@ -32,28 +50,35 @@ const ChunkCreatingModal: React.FC<IModalProps<any> & kFProps> = ({
loading,
parserId,
}) => {
const [form] = Form.useForm();
// const [form] = Form.useForm();
// const form = useFormContext();
const form = useForm<FieldValues>({
defaultValues: {
content_with_weight: '',
tag_kwd: [],
question_kwd: [],
important_kwd: [],
tag_feas: [],
},
});
const [checked, setChecked] = useState(false);
const { removeChunk } = useDeleteChunkByIds();
const { data } = useFetchChunk(chunkId);
const { t } = useTranslation();
const isTagParser = parserId === 'tag';
const handleOk = useCallback(async () => {
try {
const values = await form.validateFields();
console.log('🚀 ~ handleOk ~ values:', values);
const onSubmit = useCallback(
(values: FieldValues) => {
onOk?.({
...values,
tag_feas: transformTagFeaturesArrayToObject(values.tag_feas),
available_int: checked ? 1 : 0, // available_int
available_int: checked ? 1 : 0,
});
} catch (errorInfo) {
console.log('Failed:', errorInfo);
}
}, [checked, form, onOk]);
},
[checked, onOk],
);
const handleOk = form.handleSubmit(onSubmit);
const handleRemove = useCallback(() => {
if (chunkId) {
@ -68,8 +93,8 @@ const ChunkCreatingModal: React.FC<IModalProps<any> & kFProps> = ({
useEffect(() => {
if (data?.code === 0) {
const { available_int, tag_feas } = data.data;
form.setFieldsValue({
...(data.data || {}),
form.reset({
...data.data,
tag_feas: transformTagFeaturesObjectToArray(tag_feas),
});
@ -83,54 +108,101 @@ const ChunkCreatingModal: React.FC<IModalProps<any> & kFProps> = ({
open={true}
onOk={handleOk}
onCancel={hideModal}
okButtonProps={{ loading }}
confirmLoading={loading}
destroyOnClose
>
<Form form={form} autoComplete="off" layout={'vertical'}>
<Form.Item<FieldType>
label={t('chunk.chunk')}
name="content_with_weight"
rules={[{ required: true, message: t('chunk.chunkMessage') }]}
>
<Input.TextArea autoSize={{ minRows: 4, maxRows: 10 }} />
</Form.Item>
<Form {...form}>
<div className="flex flex-col gap-4">
<FormField
control={form.control}
name="content_with_weight"
render={({ field }) => (
<FormItem>
<FormLabel>{t('chunk.chunk')}</FormLabel>
<FormControl>
<Textarea {...field} autoSize={{ minRows: 4, maxRows: 10 }} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="important_kwd"
render={({ field }) => (
<FormItem>
<FormLabel>{t('chunk.keyword')}</FormLabel>
<FormControl>
<EditTag {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Form.Item<FieldType> label={t('chunk.keyword')} name="important_kwd">
<EditTag></EditTag>
</Form.Item>
<Form.Item<FieldType>
label={t('chunk.question')}
name="question_kwd"
tooltip={t('chunk.questionTip')}
>
<EditTag></EditTag>
</Form.Item>
{isTagParser && (
<Form.Item<FieldType>
label={t('knowledgeConfiguration.tagName')}
name="tag_kwd"
>
<EditTag></EditTag>
</Form.Item>
)}
<FormField
control={form.control}
name="question_kwd"
render={({ field }) => (
<FormItem>
<FormLabel className="flex justify-start items-start">
<div className="flex items-center gap-0">
<span>{t('chunk.question')}</span>
<HoverCard>
<HoverCardTrigger asChild>
<span className="text-xs mt-[-3px] text-center scale-[90%] font-thin text-primary cursor-pointer rounded-full w-[16px] h-[16px] border-muted-foreground/50 border">
?
</span>
</HoverCardTrigger>
<HoverCardContent className="w-80" side="top">
{t('chunk.questionTip')}
</HoverCardContent>
</HoverCard>
</div>
</FormLabel>
<FormControl>
<EditTag {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{!isTagParser && <TagFeatureItem></TagFeatureItem>}
{isTagParser && (
<FormField
control={form.control}
name="tag_kwd"
render={({ field }) => (
<FormItem>
<FormLabel>{t('knowledgeConfiguration.tagName')}</FormLabel>
<FormControl>
<EditTag {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
)}
{!isTagParser && (
<FormProvider {...form}>
<TagFeatureItem />
</FormProvider>
)}
</div>
</Form>
{chunkId && (
<section>
<Divider></Divider>
<Divider />
<Space size={'large'}>
<Switch
checkedChildren={t('chunk.enabled')}
unCheckedChildren={t('chunk.disabled')}
onChange={handleCheck}
checked={checked}
/>
<span onClick={handleRemove}>
<DeleteOutlined /> {t('common.delete')}
</span>
<div className="flex items-center gap-2">
{t('chunk.enabled')}
<Switch checked={checked} onCheckedChange={handleCheck} />
</div>
<div className="flex items-center gap-1" onClick={handleRemove}>
<Trash2 size={16} /> {t('common.delete')}
</div>
</Space>
</section>
)}

View File

@ -1,22 +1,28 @@
import { SelectWithSearch } from '@/components/originui/select-with-search';
import { Button } from '@/components/ui/button';
import {
useFetchKnowledgeBaseConfiguration,
useFetchTagListByKnowledgeIds,
} from '@/hooks/knowledge-hooks';
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
import { Button, Form, InputNumber, Select } from 'antd';
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@/components/ui/form';
import { NumberInput } from '@/components/ui/input';
import { useFetchTagListByKnowledgeIds } from '@/hooks/knowledge-hooks';
import { useFetchKnowledgeBaseConfiguration } from '@/hooks/use-knowledge-request';
import { CircleMinus, Plus } from 'lucide-react';
import { useCallback, useEffect, useMemo } from 'react';
import { useFieldArray, useFormContext } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { FormListItem } from '../../utils';
const FieldKey = 'tag_feas';
export const TagFeatureItem = () => {
const form = Form.useFormInstance();
const { t } = useTranslation();
const { data: knowledgeConfiguration } = useFetchKnowledgeBaseConfiguration();
const { setKnowledgeIds, list } = useFetchTagListByKnowledgeIds();
const { data: knowledgeConfiguration } = useFetchKnowledgeBaseConfiguration();
const form = useFormContext();
const tagKnowledgeIds = useMemo(() => {
return knowledgeConfiguration?.parser_config?.tag_kb_ids ?? [];
}, [knowledgeConfiguration?.parser_config?.tag_kb_ids]);
@ -30,15 +36,17 @@ export const TagFeatureItem = () => {
const filterOptions = useCallback(
(index: number) => {
const tags: FormListItem[] = form.getFieldValue(FieldKey) ?? [];
const tags: FormListItem[] = form.getValues(FieldKey) ?? [];
// Exclude it's own current data
const list = tags
.filter((x, idx) => x && index !== idx)
.map((x) => x.tag);
// Exclude the selected data from other options from one's own options.
return options.filter((x) => !list.some((y) => x.value === y));
const resultList = options.filter(
(x) => !list.some((y) => x.value === y),
);
return resultList;
},
[form, options],
);
@ -47,61 +55,82 @@ export const TagFeatureItem = () => {
setKnowledgeIds(tagKnowledgeIds);
}, [setKnowledgeIds, tagKnowledgeIds]);
const { fields, append, remove } = useFieldArray({
control: form.control,
name: FieldKey,
});
return (
<Form.Item label={t('knowledgeConfiguration.tags')}>
<Form.List name={FieldKey} initialValue={[]}>
{(fields, { add, remove }) => (
<>
{fields.map(({ key, name, ...restField }) => (
<div key={key} className="flex gap-3 items-center">
<div className="flex flex-1 gap-8">
<Form.Item
{...restField}
name={[name, 'tag']}
rules={[
{ required: true, message: t('common.pleaseSelect') },
]}
className="w-2/3"
>
<Select
showSearch
placeholder={t('knowledgeConfiguration.tagName')}
options={filterOptions(name)}
<FormField
control={form.control}
name={FieldKey as any}
render={() => (
<FormItem>
<FormLabel>{t('knowledgeConfiguration.tags')}</FormLabel>
<div>
{fields.map((item, name) => {
return (
<div key={item.id} className="flex gap-3 items-center mb-4">
<div className="flex flex-1 gap-8">
<FormField
control={form.control}
name={`${FieldKey}.${name}.tag` as any}
render={({ field }) => (
<FormItem className="w-2/3">
<FormControl className="w-full">
<div>
<SelectWithSearch
options={filterOptions(name)}
placeholder={t(
'knowledgeConfiguration.tagName',
)}
value={field.value}
onChange={field.onChange}
/>
</div>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</Form.Item>
<Form.Item
{...restField}
name={[name, 'frequency']}
rules={[
{ required: true, message: t('common.pleaseInput') },
]}
>
<InputNumber
placeholder={t('knowledgeConfiguration.frequency')}
max={10}
min={0}
<FormField
control={form.control}
name={`${FieldKey}.${name}.frequency`}
render={({ field }) => (
<FormItem>
<FormControl>
<NumberInput
value={field.value}
onChange={field.onChange}
placeholder={t(
'knowledgeConfiguration.frequency',
)}
max={10}
min={0}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</Form.Item>
</div>
<CircleMinus
onClick={() => remove(name)}
className="text-red-500"
/>
</div>
<MinusCircleOutlined
onClick={() => remove(name)}
className="mb-6"
/>
</div>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => add()}
block
icon={<PlusOutlined />}
>
{t('knowledgeConfiguration.addTag')}
</Button>
</Form.Item>
</>
)}
</Form.List>
</Form.Item>
);
})}
<Button
variant="dashed"
className="w-full flex items-center justify-center gap-2"
onClick={() => append({ tag: '', frequency: 0 })}
>
<Plus size={16} />
{t('knowledgeConfiguration.addTag')}
</Button>
</div>
</FormItem>
)}
/>
);
};

View File

@ -61,12 +61,12 @@ export default ({
};
return (
<div className="flex pr-[25px]">
<div className="flex items-center gap-4 bg-card text-muted-foreground w-fit h-[35px] rounded-md px-4 py-2 text-base">
<div className="flex items-center gap-4 bg-background-card text-muted w-fit h-[35px] rounded-md px-4 py-2 text-base">
{textSelectOptions.map((option) => (
<div
key={option.value}
className={cn('flex items-center cursor-pointer', {
'text-white': option.value === textSelectValue,
'text-primary': option.value === textSelectValue,
})}
onClick={() => changeTextSelectValue(option.value)}
>
@ -76,7 +76,7 @@ export default ({
</div>
<div className="ml-auto"></div>
<Input
className="bg-card text-muted-foreground"
className="bg-background-card text-muted-foreground"
style={{ width: 200 }}
placeholder={t('search')}
icon={<SearchOutlined />}
@ -86,7 +86,7 @@ export default ({
<div className="w-[20px]"></div>
<Popover>
<PopoverTrigger asChild>
<Button className="bg-card text-muted-foreground hover:bg-card">
<Button className="bg-background-card text-muted-foreground hover:bg-card">
<ListFilter />
</Button>
</PopoverTrigger>
@ -95,7 +95,10 @@ export default ({
</PopoverContent>
</Popover>
<div className="w-[20px]"></div>
<Button onClick={() => createChunk()}>
<Button
onClick={() => createChunk()}
className="bg-background-card text-primary"
>
<Plus size={44} />
</Button>
</div>

View File

@ -1,4 +1,3 @@
import { Skeleton } from 'antd';
import { memo, useEffect, useRef } from 'react';
import {
AreaHighlight,
@ -11,6 +10,7 @@ import {
import { useGetDocumentUrl } from './hooks';
import { useCatchDocumentError } from '@/components/pdf-previewer/hooks';
import { Spin } from '@/components/ui/spin';
import FileError from '@/pages/document-viewer/file-error';
import styles from './index.less';
@ -50,7 +50,11 @@ const PdfPreview = ({ highlights: state, setWidthAndHeight }: IProps) => {
>
<PdfLoader
url={url}
beforeLoad={<Skeleton active />}
beforeLoad={
<div className="absolute inset-0 flex items-center justify-center">
<Spin />
</div>
}
workerSrc="/pdfjs-dist/pdf.worker.min.js"
errorMessage={<FileError>{error}</FileError>}
>

View File

@ -1,6 +1,6 @@
import message from '@/components/ui/message';
import { Spin } from '@/components/ui/spin';
import request from '@/utils/request';
import { Spin } from 'antd';
import classNames from 'classnames';
import { useEffect, useState } from 'react';
import { useGetDocumentUrl } from './hooks';

View File

@ -41,8 +41,7 @@
.chunkContainer {
display: flex;
// height: calc(100vh - 332px);
height: calc(100vh - 300px);
height: calc(100vh - 332px);
}
.chunkOtherContainer {

View File

@ -61,7 +61,7 @@ export function ParsingStatusCell({
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant={'ghost'} size={'sm'}>
{parser_id}
{parser_id === 'naive' ? 'general' : parser_id}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>

View File

@ -221,79 +221,77 @@ export function TagTable() {
</ConfirmDeleteDialog>
)}
</div>
<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>
);
})}
<Table rootClassName="rounded-none border max-h-80 overflow-y-auto">
<TableHeader className="bg-[#39393b]">
{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>
))}
</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>
))
) : (
<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">
{selectedRowLength} of {table.getFilteredRowModel().rows.length}{' '}
row(s) selected.
</div>
<div className="flex items-center justify-end space-x-2 py-4">
<div className="flex-1 text-sm text-muted-foreground">
{selectedRowLength} 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()}
>
{t('common.previousPage')}
</Button>
<Button
variant="outline"
size="sm"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
{t('common.nextPage')}
</Button>
</div>
<div className="space-x-2">
<Button
variant="outline"
size="sm"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
{t('common.previousPage')}
</Button>
<Button
variant="outline"
size="sm"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
{t('common.nextPage')}
</Button>
</div>
</div>
{tagRenameVisible && (

View File

@ -2,12 +2,12 @@ import { Images } from '@/constants/common';
import { api_host } from '@/utils/api';
import { Flex } from 'antd';
import { useParams, useSearchParams } from 'umi';
import Md from './md';
import Text from './text';
import Docx from './docx';
import Excel from './excel';
import Image from './image';
import Md from './md';
import Pdf from './pdf';
import Text from './text';
import { previewHtmlFile } from '@/utils/file-util';
import styles from './index.less';

View File

@ -19,13 +19,13 @@ const Md: React.FC<MdProps> = ({ filePath }) => {
return res.text();
})
.then((text) => setContent(text))
.catch((err) => setError(err.message))
.catch((err) => setError(err.message));
}, [filePath]);
if (error) return (<FileError>{error}</FileError>);
if (error) return <FileError>{error}</FileError>;
return (
<div style={{ padding: 24, height: "100vh", overflow: "scroll" }}>
<div style={{ padding: 24, height: '100vh', overflow: 'scroll' }}>
<ReactMarkdown remarkPlugins={[remarkGfm]}>{content}</ReactMarkdown>
</div>
);

View File

@ -17,14 +17,14 @@ const Md: React.FC<TxtProps> = ({ filePath }) => {
return res.text();
})
.then((text) => setContent(text))
.catch((err) => setError(err.message))
.catch((err) => setError(err.message));
}, [filePath]);
if (error) return (<FileError>{error}</FileError>);
if (error) return <FileError>{error}</FileError>;
return (
<div style={{ padding: 24, height: "100vh", overflow: "scroll" }}>
{content}
<div style={{ padding: 24, height: '100vh', overflow: 'scroll' }}>
{content}
</div>
);
};