mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-24 07:26:47 +08:00
fix: fix dataset-page's bugs (#8786)
### What problem does this PR solve? fix dataset-page's bugs,Input component supports icon, added Radio component, and removed antd from chunk-result-bar 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:
@ -12,7 +12,13 @@ import {
|
||||
type EntityTypesFormFieldProps = {
|
||||
name?: string;
|
||||
};
|
||||
|
||||
const initialEntityTypes = [
|
||||
'organization',
|
||||
'person',
|
||||
'geo',
|
||||
'event',
|
||||
'category',
|
||||
];
|
||||
export function EntityTypesFormField({
|
||||
name = 'parser_config.entity_types',
|
||||
}: EntityTypesFormFieldProps) {
|
||||
@ -23,24 +29,27 @@ export function EntityTypesFormField({
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={name}
|
||||
render={({ field }) => (
|
||||
<FormItem className=" items-center space-y-0 ">
|
||||
<div className="flex items-center">
|
||||
<FormLabel className="text-sm text-muted-foreground whitespace-nowrap w-1/4">
|
||||
<span className="text-red-600">*</span> {t('entityTypes')}
|
||||
</FormLabel>
|
||||
<div className="w-3/4">
|
||||
<FormControl>
|
||||
<EditTag {...field}></EditTag>
|
||||
</FormControl>
|
||||
defaultValue={initialEntityTypes}
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<FormItem className=" items-center space-y-0 ">
|
||||
<div className="flex items-center">
|
||||
<FormLabel className="text-sm text-muted-foreground whitespace-nowrap w-1/4">
|
||||
<span className="text-red-600">*</span> {t('entityTypes')}
|
||||
</FormLabel>
|
||||
<div className="w-3/4">
|
||||
<FormControl>
|
||||
<EditTag {...field}></EditTag>
|
||||
</FormControl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex pt-1">
|
||||
<div className="w-1/4"></div>
|
||||
<FormMessage />
|
||||
</div>
|
||||
</FormItem>
|
||||
)}
|
||||
<div className="flex pt-1">
|
||||
<div className="w-1/4"></div>
|
||||
<FormMessage />
|
||||
</div>
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -25,7 +25,7 @@ const EntityTypesItem = ({
|
||||
rules={[{ required: true }]}
|
||||
initialValue={initialEntityTypes}
|
||||
>
|
||||
<EditTag></EditTag>
|
||||
<EditTag value={field}></EditTag>
|
||||
</Form.Item>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,12 +1,17 @@
|
||||
import { useGetPaginationWithRouter } from '@/hooks/logic-hooks';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { FilterChange, FilterValue } from './interface';
|
||||
|
||||
export function useHandleFilterSubmit() {
|
||||
const [filterValue, setFilterValue] = useState<FilterValue>({});
|
||||
|
||||
const handleFilterSubmit: FilterChange = useCallback((value) => {
|
||||
setFilterValue(value);
|
||||
}, []);
|
||||
const { setPagination } = useGetPaginationWithRouter();
|
||||
const handleFilterSubmit: FilterChange = useCallback(
|
||||
(value) => {
|
||||
setFilterValue(value);
|
||||
setPagination({ page: 1 });
|
||||
},
|
||||
[setPagination],
|
||||
);
|
||||
|
||||
return { filterValue, setFilterValue, handleFilterSubmit };
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ interface IProps {
|
||||
max?: number;
|
||||
}
|
||||
|
||||
export function MaxTokenNumberFormField({ max = 2048 }: IProps) {
|
||||
export function MaxTokenNumberFormField({ max = 2048, initialValue }: IProps) {
|
||||
const { t } = useTranslate('knowledgeConfiguration');
|
||||
|
||||
return (
|
||||
@ -15,6 +15,7 @@ export function MaxTokenNumberFormField({ max = 2048 }: IProps) {
|
||||
name={'parser_config.chunk_token_num'}
|
||||
label={t('chunkTokenNumber')}
|
||||
max={max}
|
||||
defaultValue={initialValue ?? 0}
|
||||
layout={FormLayout.Horizontal}
|
||||
></SliderInputFormField>
|
||||
);
|
||||
|
||||
@ -1,24 +1,49 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { cn } from '@/lib/utils';
|
||||
type InputProps = React.ComponentProps<'input'> & {
|
||||
icon?: React.ReactNode;
|
||||
iconPosition?: 'left' | 'right';
|
||||
};
|
||||
|
||||
function Input({ className, type, ...props }: React.ComponentProps<'input'>) {
|
||||
function Input({
|
||||
className,
|
||||
type,
|
||||
icon,
|
||||
iconPosition = 'left',
|
||||
...props
|
||||
}: InputProps) {
|
||||
return (
|
||||
<input
|
||||
type={type}
|
||||
data-slot="input"
|
||||
className={cn(
|
||||
'border-input file:text-foreground placeholder:text-muted-foreground/70 flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-sm shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50',
|
||||
'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',
|
||||
'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',
|
||||
type === 'search' &&
|
||||
'[&::-webkit-search-cancel-button]:appearance-none [&::-webkit-search-decoration]:appearance-none [&::-webkit-search-results-button]:appearance-none [&::-webkit-search-results-decoration]:appearance-none',
|
||||
type === 'file' &&
|
||||
'text-muted-foreground/70 file:border-input file:text-foreground p-0 pr-3 italic file:me-3 file:h-full file:border-0 file:border-r file:border-solid file:bg-transparent file:px-3 file:text-sm file:font-medium file:not-italic',
|
||||
className,
|
||||
<div className="relative">
|
||||
{icon && (
|
||||
<div
|
||||
className={cn(
|
||||
'absolute w-1 top-0 flex h-full items-center justify-center pointer-events-none',
|
||||
iconPosition === 'left' ? 'left-5' : 'right-5',
|
||||
iconPosition === 'left' ? 'pr-2' : 'pl-2',
|
||||
)}
|
||||
>
|
||||
{icon}
|
||||
</div>
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
<input
|
||||
type={type}
|
||||
data-slot="input"
|
||||
className={cn(
|
||||
'border-input file:text-foreground placeholder:text-muted-foreground/70 flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-sm shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50',
|
||||
'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',
|
||||
'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',
|
||||
type === 'search' &&
|
||||
'[&::-webkit-search-cancel-button]:appearance-none [&::-webkit-search-decoration]:appearance-none [&::-webkit-search-results-button]:appearance-none [&::-webkit-search-results-decoration]:appearance-none',
|
||||
type === 'file' &&
|
||||
'text-muted-foreground/70 file:border-input file:text-foreground p-0 pr-3 italic file:me-3 file:h-full file:border-0 file:border-r file:border-solid file:bg-transparent file:px-3 file:text-sm file:font-medium file:not-italic',
|
||||
icon && iconPosition === 'left' && 'pl-7',
|
||||
icon && iconPosition === 'right' && 'pr-7',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -64,10 +64,10 @@ const RaptorFormFields = () => {
|
||||
control={form.control}
|
||||
name={UseRaptorField}
|
||||
render={({ field }) => {
|
||||
if (typeof field.value === 'undefined') {
|
||||
// default value set
|
||||
form.setValue('parser_config.raptor.use_raptor', false);
|
||||
}
|
||||
// if (typeof field.value === 'undefined') {
|
||||
// // default value set
|
||||
// form.setValue('parser_config.raptor.use_raptor', false);
|
||||
// }
|
||||
return (
|
||||
<FormItem
|
||||
defaultChecked={false}
|
||||
@ -102,27 +102,33 @@ const RaptorFormFields = () => {
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={'parser_config.raptor.prompt'}
|
||||
render={({ field }) => (
|
||||
<FormItem className=" items-center space-y-0 ">
|
||||
<div className="flex items-start">
|
||||
<FormLabel
|
||||
tooltip={t('promptTip')}
|
||||
className="text-sm text-muted-foreground whitespace-nowrap w-1/4"
|
||||
>
|
||||
{t('prompt')}
|
||||
</FormLabel>
|
||||
<div className="w-3/4">
|
||||
<FormControl>
|
||||
<Textarea {...field} rows={8} />
|
||||
</FormControl>
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<FormItem className=" items-center space-y-0 ">
|
||||
<div className="flex items-start">
|
||||
<FormLabel
|
||||
tooltip={t('promptTip')}
|
||||
className="text-sm text-muted-foreground whitespace-nowrap w-1/4"
|
||||
>
|
||||
{t('prompt')}
|
||||
</FormLabel>
|
||||
<div className="w-3/4">
|
||||
<FormControl>
|
||||
<Textarea
|
||||
{...field}
|
||||
rows={8}
|
||||
defaultValue={t('promptText')}
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex pt-1">
|
||||
<div className="w-1/4"></div>
|
||||
<FormMessage />
|
||||
</div>
|
||||
</FormItem>
|
||||
)}
|
||||
<div className="flex pt-1">
|
||||
<div className="w-1/4"></div>
|
||||
<FormMessage />
|
||||
</div>
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<SliderInputFormField
|
||||
name={'parser_config.raptor.max_token'}
|
||||
@ -164,7 +170,7 @@ const RaptorFormFields = () => {
|
||||
<div className="w-3/4">
|
||||
<FormControl defaultValue={0}>
|
||||
<div className="flex gap-4">
|
||||
<Input {...field} />
|
||||
<Input {...field} defaultValue={0} />
|
||||
<Button
|
||||
size={'sm'}
|
||||
onClick={handleGenerate}
|
||||
|
||||
@ -4,10 +4,12 @@ import { cn } from '@/lib/utils';
|
||||
import { Search } from 'lucide-react';
|
||||
|
||||
export interface InputProps
|
||||
extends React.InputHTMLAttributes<HTMLInputElement> {}
|
||||
extends React.InputHTMLAttributes<HTMLInputElement> {
|
||||
value?: string | number | readonly string[] | undefined;
|
||||
}
|
||||
|
||||
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
||||
({ className, type, ...props }, ref) => {
|
||||
({ className, type, value, ...props }, ref) => {
|
||||
return (
|
||||
<input
|
||||
type={type}
|
||||
@ -16,6 +18,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
||||
className,
|
||||
)}
|
||||
ref={ref}
|
||||
value={value ?? ''}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -5,7 +5,27 @@ import * as React from 'react';
|
||||
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const Popover = PopoverPrimitive.Root;
|
||||
const Popover = (props: PopoverPrimitive.PopoverProps) => {
|
||||
const { children, open: openState, onOpenChange } = props;
|
||||
const [open, setOpen] = React.useState(true);
|
||||
React.useEffect(() => {
|
||||
setOpen(!!openState);
|
||||
}, [openState]);
|
||||
const handleOnOpenChange = React.useCallback(
|
||||
(e: boolean) => {
|
||||
if (onOpenChange) {
|
||||
onOpenChange?.(e);
|
||||
}
|
||||
setOpen(e);
|
||||
},
|
||||
[onOpenChange],
|
||||
);
|
||||
return (
|
||||
<PopoverPrimitive.Root open={open} onOpenChange={handleOnOpenChange}>
|
||||
{children}
|
||||
</PopoverPrimitive.Root>
|
||||
);
|
||||
};
|
||||
|
||||
const PopoverTrigger = PopoverPrimitive.Trigger;
|
||||
|
||||
|
||||
133
web/src/components/ui/radio.tsx
Normal file
133
web/src/components/ui/radio.tsx
Normal file
@ -0,0 +1,133 @@
|
||||
import { cn } from '@/lib/utils';
|
||||
import { Radio as LucideRadio } from 'lucide-react';
|
||||
import React, { useContext, useState } from 'react';
|
||||
|
||||
const RadioGroupContext = React.createContext<{
|
||||
value: string | number;
|
||||
onChange: (value: string | number) => void;
|
||||
disabled?: boolean;
|
||||
} | null>(null);
|
||||
|
||||
type RadioProps = {
|
||||
value: string | number;
|
||||
checked?: boolean;
|
||||
disabled?: boolean;
|
||||
onChange?: (checked: boolean) => void;
|
||||
children?: React.ReactNode;
|
||||
};
|
||||
|
||||
function Radio({ value, checked, disabled, onChange, children }: RadioProps) {
|
||||
const groupContext = useContext(RadioGroupContext);
|
||||
const isControlled = checked !== undefined;
|
||||
// const [internalChecked, setInternalChecked] = useState(false);
|
||||
|
||||
const isChecked = isControlled ? checked : groupContext?.value === value;
|
||||
const mergedDisabled = disabled || groupContext?.disabled;
|
||||
|
||||
const handleClick = () => {
|
||||
if (mergedDisabled) return;
|
||||
|
||||
// if (!isControlled) {
|
||||
// setInternalChecked(!isChecked);
|
||||
// }
|
||||
|
||||
if (onChange) {
|
||||
onChange(!isChecked);
|
||||
}
|
||||
|
||||
if (groupContext && !groupContext.disabled) {
|
||||
groupContext.onChange(value);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<label
|
||||
className={cn(
|
||||
'flex items-center cursor-pointer gap-2 text-sm',
|
||||
mergedDisabled && 'cursor-not-allowed opacity-50',
|
||||
)}
|
||||
>
|
||||
<span
|
||||
className={cn(
|
||||
'flex h-4 w-4 items-center justify-center rounded-full border border-input transition-colors',
|
||||
'peer ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
|
||||
isChecked && 'border-primary bg-primary/10',
|
||||
mergedDisabled && 'border-muted',
|
||||
)}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{isChecked && (
|
||||
<LucideRadio className="h-3 w-3 fill-primary text-primary" />
|
||||
)}
|
||||
</span>
|
||||
{children && <span className="text-foreground">{children}</span>}
|
||||
</label>
|
||||
);
|
||||
}
|
||||
|
||||
type RadioGroupProps = {
|
||||
value?: string | number;
|
||||
defaultValue?: string | number;
|
||||
onChange?: (value: string | number) => void;
|
||||
disabled?: boolean;
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
direction?: 'horizontal' | 'vertical';
|
||||
};
|
||||
|
||||
function Group({
|
||||
value,
|
||||
defaultValue,
|
||||
onChange,
|
||||
disabled,
|
||||
children,
|
||||
className,
|
||||
direction = 'horizontal',
|
||||
}: RadioGroupProps) {
|
||||
const [internalValue, setInternalValue] = useState(defaultValue || '');
|
||||
|
||||
const isControlled = value !== undefined;
|
||||
const mergedValue = isControlled ? value : internalValue;
|
||||
|
||||
const handleChange = (val: string | number) => {
|
||||
if (disabled) return;
|
||||
|
||||
if (!isControlled) {
|
||||
setInternalValue(val);
|
||||
}
|
||||
|
||||
if (onChange) {
|
||||
onChange(val);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<RadioGroupContext.Provider
|
||||
value={{
|
||||
value: mergedValue,
|
||||
onChange: handleChange,
|
||||
disabled,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'flex gap-4',
|
||||
direction === 'vertical' ? 'flex-col' : 'flex-row',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{React.Children.map(children, (child) =>
|
||||
React.cloneElement(child as React.ReactElement, {
|
||||
disabled: disabled || child?.props?.disabled,
|
||||
}),
|
||||
)}
|
||||
</div>
|
||||
</RadioGroupContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
const RadioComponent = Object.assign(Radio, {
|
||||
Group,
|
||||
});
|
||||
|
||||
export { RadioComponent as Radio };
|
||||
@ -31,6 +31,15 @@ export function Segmented({
|
||||
onChange,
|
||||
className,
|
||||
}: SegmentedProps) {
|
||||
const [selectedValue, setSelectedValue] = React.useState<
|
||||
SegmentedValue | undefined
|
||||
>(value);
|
||||
const handleOnChange = (e: SegmentedValue) => {
|
||||
if (onChange) {
|
||||
onChange(e);
|
||||
}
|
||||
setSelectedValue(e);
|
||||
};
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
@ -48,11 +57,11 @@ export function Segmented({
|
||||
className={cn(
|
||||
'inline-flex items-center px-6 py-2 text-base font-normal rounded-3xl cursor-pointer text-text-badge',
|
||||
{
|
||||
'bg-text-title': value === actualValue,
|
||||
'text-text-title-invert': value === actualValue,
|
||||
'bg-text-title': selectedValue === actualValue,
|
||||
'text-text-title-invert': selectedValue === actualValue,
|
||||
},
|
||||
)}
|
||||
onClick={() => onChange?.(actualValue)}
|
||||
onClick={() => handleOnChange(actualValue)}
|
||||
>
|
||||
{isObject ? option.label : option}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user