feat(form): add form field components and configuration updates
refactor(knowledge): restructure configuration components to use common items
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -24,4 +24,5 @@ dist-ssr
|
||||
*.sw?
|
||||
|
||||
# rag core
|
||||
rag_web_core
|
||||
rag_web_core_v0.20.5
|
||||
rag_web_core_deprecated
|
||||
@@ -19,6 +19,5 @@ export default defineConfig([
|
||||
ecmaVersion: 2020,
|
||||
globals: globals.browser,
|
||||
},
|
||||
ignores: ['rag_web_core/**'],
|
||||
},
|
||||
])
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
"@mui/icons-material": "^7.3.4",
|
||||
"@mui/material": "^7.3.4",
|
||||
"@mui/x-data-grid": "^8.14.0",
|
||||
"@mui/x-date-pickers": "^8.14.0",
|
||||
"ahooks": "^3.9.5",
|
||||
"axios": "^1.12.2",
|
||||
"dayjs": "^1.11.18",
|
||||
|
||||
60
pnpm-lock.yaml
generated
60
pnpm-lock.yaml
generated
@@ -23,6 +23,9 @@ importers:
|
||||
'@mui/x-data-grid':
|
||||
specifier: ^8.14.0
|
||||
version: 8.14.0(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react@18.3.1))(@mui/material@7.3.4(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@7.3.3(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@mui/x-date-pickers':
|
||||
specifier: ^8.14.0
|
||||
version: 8.14.0(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react@18.3.1))(@mui/material@7.3.4(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@7.3.3(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(dayjs@1.11.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
ahooks:
|
||||
specifier: ^3.9.5
|
||||
version: 3.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
@@ -588,6 +591,43 @@ packages:
|
||||
'@emotion/styled':
|
||||
optional: true
|
||||
|
||||
'@mui/x-date-pickers@8.14.0':
|
||||
resolution: {integrity: sha512-fz8z1hoi1tbG1QUcZAkQdiO3GsCOpUeRfyANXDDzDN88L4VqwNEyrv0wmzmCfIX2sgur4gWwWJzMuCvauMgXRw==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
peerDependencies:
|
||||
'@emotion/react': ^11.9.0
|
||||
'@emotion/styled': ^11.8.1
|
||||
'@mui/material': ^5.15.14 || ^6.0.0 || ^7.0.0
|
||||
'@mui/system': ^5.15.14 || ^6.0.0 || ^7.0.0
|
||||
date-fns: ^2.25.0 || ^3.2.0 || ^4.0.0
|
||||
date-fns-jalali: ^2.13.0-0 || ^3.2.0-0 || ^4.0.0-0
|
||||
dayjs: ^1.10.7
|
||||
luxon: ^3.0.2
|
||||
moment: ^2.29.4
|
||||
moment-hijri: ^2.1.2 || ^3.0.0
|
||||
moment-jalaali: ^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0
|
||||
react: ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
peerDependenciesMeta:
|
||||
'@emotion/react':
|
||||
optional: true
|
||||
'@emotion/styled':
|
||||
optional: true
|
||||
date-fns:
|
||||
optional: true
|
||||
date-fns-jalali:
|
||||
optional: true
|
||||
dayjs:
|
||||
optional: true
|
||||
luxon:
|
||||
optional: true
|
||||
moment:
|
||||
optional: true
|
||||
moment-hijri:
|
||||
optional: true
|
||||
moment-jalaali:
|
||||
optional: true
|
||||
|
||||
'@mui/x-internals@8.14.0':
|
||||
resolution: {integrity: sha512-esYyl61nuuFXiN631TWuPh2tqdoyTdBI/4UXgwH3rytF8jiWvy6prPBPRHEH1nvW3fgw9FoBI48FlOO+yEI8xg==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
@@ -2175,6 +2215,26 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
|
||||
'@mui/x-date-pickers@8.14.0(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react@18.3.1))(@mui/material@7.3.4(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@7.3.3(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(dayjs@1.11.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.4
|
||||
'@mui/material': 7.3.4(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@mui/system': 7.3.3(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react@18.3.1)
|
||||
'@mui/utils': 7.3.3(@types/react@19.2.2)(react@18.3.1)
|
||||
'@mui/x-internals': 8.14.0(@types/react@19.2.2)(react@18.3.1)
|
||||
'@types/react-transition-group': 4.4.12(@types/react@19.2.2)
|
||||
clsx: 2.1.1
|
||||
prop-types: 15.8.1
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
optionalDependencies:
|
||||
'@emotion/react': 11.14.0(@types/react@19.2.2)(react@18.3.1)
|
||||
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react@18.3.1)
|
||||
dayjs: 1.11.18
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
|
||||
'@mui/x-internals@8.14.0(@types/react@19.2.2)(react@18.3.1)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.4
|
||||
|
||||
74
src/components/FormField/CheckboxFormField.tsx
Normal file
74
src/components/FormField/CheckboxFormField.tsx
Normal file
@@ -0,0 +1,74 @@
|
||||
import React from 'react';
|
||||
import { Checkbox, FormControlLabel, FormControl, FormHelperText, Box, Typography } from '@mui/material';
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
import { type IFormLayoutType } from './index';
|
||||
|
||||
export interface CheckboxFormFieldProps extends IFormLayoutType {
|
||||
name: string;
|
||||
label: string;
|
||||
defaultValue?: boolean;
|
||||
required?: boolean;
|
||||
disabled?: boolean;
|
||||
helperText?: string;
|
||||
color?: 'primary' | 'secondary' | 'default';
|
||||
}
|
||||
|
||||
export function CheckboxFormField({
|
||||
name,
|
||||
label,
|
||||
defaultValue = false,
|
||||
required = false,
|
||||
disabled = false,
|
||||
layout = 'vertical',
|
||||
helperText,
|
||||
color = 'primary',
|
||||
}: CheckboxFormFieldProps) {
|
||||
const { control, formState: { errors } } = useFormContext();
|
||||
const error = errors[name];
|
||||
const isHorizontal = layout === 'horizontal';
|
||||
|
||||
const checkbox = (
|
||||
<Controller
|
||||
name={name}
|
||||
control={control}
|
||||
defaultValue={defaultValue}
|
||||
render={({ field }) => (
|
||||
<FormControl error={!!error} disabled={disabled}>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
{...field}
|
||||
checked={field.value || false}
|
||||
onChange={(e) => field.onChange(e.target.checked)}
|
||||
color={color}
|
||||
required={required}
|
||||
/>
|
||||
}
|
||||
label={isHorizontal ? undefined : label}
|
||||
/>
|
||||
{(error?.message || helperText) && (
|
||||
<FormHelperText>
|
||||
{error?.message as string || helperText}
|
||||
</FormHelperText>
|
||||
)}
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
|
||||
if (isHorizontal) {
|
||||
return (
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<Typography variant="subtitle1" sx={{ minWidth: 120, pt: 1 }}>
|
||||
{label}
|
||||
{required && <span style={{ color: 'red' }}>*</span>}
|
||||
</Typography>
|
||||
<Box sx={{ flex: 1 }}>
|
||||
{checkbox}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return checkbox;
|
||||
}
|
||||
140
src/components/FormField/ChipListFormField.tsx
Normal file
140
src/components/FormField/ChipListFormField.tsx
Normal file
@@ -0,0 +1,140 @@
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
Chip,
|
||||
IconButton,
|
||||
TextField,
|
||||
FormHelperText,
|
||||
} from '@mui/material';
|
||||
import { Add as AddIcon } from '@mui/icons-material';
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
|
||||
export interface ChipListFormFieldProps {
|
||||
name: string;
|
||||
label: string;
|
||||
helperText?: string;
|
||||
defaultValue?: string[];
|
||||
disabled?: boolean;
|
||||
required?: boolean;
|
||||
allowAdd?: boolean;
|
||||
allowDelete?: boolean;
|
||||
maxChips?: number;
|
||||
placeholder?: string;
|
||||
chipColor?: 'default' | 'primary' | 'secondary' | 'error' | 'info' | 'success' | 'warning';
|
||||
chipVariant?: 'filled' | 'outlined';
|
||||
}
|
||||
|
||||
export const ChipListFormField: React.FC<ChipListFormFieldProps> = ({
|
||||
name,
|
||||
label,
|
||||
helperText,
|
||||
defaultValue = [],
|
||||
disabled = false,
|
||||
required = false,
|
||||
allowAdd = true,
|
||||
allowDelete = true,
|
||||
maxChips,
|
||||
placeholder = '输入后按回车添加',
|
||||
chipColor = 'default',
|
||||
chipVariant = 'outlined',
|
||||
}) => {
|
||||
const { control, setValue, getValues } = useFormContext();
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
|
||||
const handleAddChip = (field: any) => {
|
||||
if (inputValue.trim() && (!maxChips || field.value.length < maxChips)) {
|
||||
const newChips = [...field.value, inputValue.trim()];
|
||||
field.onChange(newChips);
|
||||
setInputValue('');
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteChip = (field: any, chipToDelete: string) => {
|
||||
if (allowDelete) {
|
||||
const newChips = field.value.filter((chip: string) => chip !== chipToDelete);
|
||||
field.onChange(newChips);
|
||||
}
|
||||
};
|
||||
|
||||
const handleKeyPress = (event: React.KeyboardEvent, field: any) => {
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
handleAddChip(field);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography variant="subtitle1" gutterBottom>
|
||||
{required && <span style={{ color: 'red' }}>*</span>}
|
||||
{label}
|
||||
</Typography>
|
||||
<Controller
|
||||
name={name}
|
||||
control={control}
|
||||
defaultValue={defaultValue}
|
||||
rules={{
|
||||
required: required ? `${label}是必填项` : false,
|
||||
validate: (value) => {
|
||||
if (maxChips && value.length > maxChips) {
|
||||
return `${label}最多只能有${maxChips}个标签`;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
}}
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<Box>
|
||||
{/* 标签显示区域 */}
|
||||
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1, mb: 2, minHeight: '40px' }}>
|
||||
{field.value.map((chip: string, index: number) => (
|
||||
<Chip
|
||||
key={index}
|
||||
label={chip}
|
||||
onDelete={allowDelete && !disabled ? () => handleDeleteChip(field, chip) : undefined}
|
||||
variant={chipVariant}
|
||||
color={chipColor}
|
||||
disabled={disabled}
|
||||
/>
|
||||
))}
|
||||
{field.value.length === 0 && (
|
||||
<Typography variant="body2" color="text.secondary" sx={{ py: 1 }}>
|
||||
暂无标签
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{/* 添加标签输入框 */}
|
||||
{allowAdd && !disabled && (!maxChips || field.value.length < maxChips) && (
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<TextField
|
||||
value={inputValue}
|
||||
onChange={(e) => setInputValue(e.target.value)}
|
||||
onKeyPress={(e) => handleKeyPress(e, field)}
|
||||
placeholder={placeholder}
|
||||
size="small"
|
||||
fullWidth
|
||||
disabled={disabled}
|
||||
/>
|
||||
<IconButton
|
||||
onClick={() => handleAddChip(field)}
|
||||
disabled={!inputValue.trim() || disabled}
|
||||
size="small"
|
||||
>
|
||||
<AddIcon />
|
||||
</IconButton>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* 错误信息和帮助文本 */}
|
||||
{(error || helperText) && (
|
||||
<FormHelperText error={!!error}>
|
||||
{error?.message || helperText}
|
||||
</FormHelperText>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
64
src/components/FormField/DatePickerFormField.tsx
Normal file
64
src/components/FormField/DatePickerFormField.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import React from 'react';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
|
||||
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
|
||||
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
import dayjs, { Dayjs } from 'dayjs';
|
||||
|
||||
export interface DatePickerFormFieldProps {
|
||||
name: string;
|
||||
label: string;
|
||||
defaultValue?: string | Date | Dayjs | null;
|
||||
required?: boolean;
|
||||
disabled?: boolean;
|
||||
helperText?: string;
|
||||
minDate?: string | Date | Dayjs;
|
||||
maxDate?: string | Date | Dayjs;
|
||||
format?: string;
|
||||
}
|
||||
|
||||
export function DatePickerFormField({
|
||||
name,
|
||||
label,
|
||||
defaultValue = null,
|
||||
required = false,
|
||||
disabled = false,
|
||||
helperText,
|
||||
minDate,
|
||||
maxDate,
|
||||
format = 'YYYY-MM-DD',
|
||||
}: DatePickerFormFieldProps) {
|
||||
const { control, formState: { errors } } = useFormContext();
|
||||
const error = errors[name];
|
||||
|
||||
const datePicker = (
|
||||
<LocalizationProvider dateAdapter={AdapterDayjs}>
|
||||
<Controller
|
||||
name={name}
|
||||
control={control}
|
||||
defaultValue={defaultValue ? dayjs(defaultValue) : null}
|
||||
render={({ field }) => (
|
||||
<DatePicker
|
||||
{...field}
|
||||
disabled={disabled}
|
||||
minDate={minDate ? dayjs(minDate) : undefined}
|
||||
maxDate={maxDate ? dayjs(maxDate) : undefined}
|
||||
format={format}
|
||||
slotProps={{
|
||||
textField: {
|
||||
required,
|
||||
error: !!error,
|
||||
helperText: error?.message as string || helperText,
|
||||
fullWidth: true,
|
||||
variant: 'outlined',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</LocalizationProvider>
|
||||
);
|
||||
|
||||
return datePicker;
|
||||
}
|
||||
80
src/components/FormField/MultilineTextFormField.tsx
Normal file
80
src/components/FormField/MultilineTextFormField.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
TextField,
|
||||
} from '@mui/material';
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
|
||||
export interface MultilineTextFormFieldProps {
|
||||
name: string;
|
||||
label: string;
|
||||
helperText?: string;
|
||||
defaultValue?: string;
|
||||
disabled?: boolean;
|
||||
required?: boolean;
|
||||
fullWidth?: boolean;
|
||||
rows?: number;
|
||||
maxRows?: number;
|
||||
minRows?: number;
|
||||
placeholder?: string;
|
||||
size?: 'small' | 'medium';
|
||||
maxLength?: number;
|
||||
}
|
||||
|
||||
export const MultilineTextFormField: React.FC<MultilineTextFormFieldProps> = ({
|
||||
name,
|
||||
label,
|
||||
helperText,
|
||||
defaultValue = '',
|
||||
disabled = false,
|
||||
required = false,
|
||||
fullWidth = true,
|
||||
rows = 4,
|
||||
maxRows,
|
||||
minRows,
|
||||
placeholder,
|
||||
size = 'medium',
|
||||
maxLength,
|
||||
}) => {
|
||||
const { control } = useFormContext();
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography variant="subtitle1" gutterBottom>
|
||||
{required && <span style={{ color: 'red' }}>*</span>}
|
||||
{label}
|
||||
</Typography>
|
||||
<Controller
|
||||
name={name}
|
||||
control={control}
|
||||
defaultValue={defaultValue}
|
||||
rules={{
|
||||
required: required ? `${label}是必填项` : false,
|
||||
maxLength: maxLength ? {
|
||||
value: maxLength,
|
||||
message: `${label}不能超过${maxLength}个字符`
|
||||
} : undefined,
|
||||
}}
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
multiline
|
||||
rows={rows}
|
||||
maxRows={maxRows}
|
||||
minRows={minRows}
|
||||
fullWidth={fullWidth}
|
||||
disabled={disabled}
|
||||
placeholder={placeholder}
|
||||
size={size}
|
||||
error={!!error}
|
||||
helperText={error?.message || helperText}
|
||||
inputProps={{
|
||||
maxLength: maxLength,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
112
src/components/FormField/NumberInputFormField.tsx
Normal file
112
src/components/FormField/NumberInputFormField.tsx
Normal file
@@ -0,0 +1,112 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
TextField,
|
||||
IconButton,
|
||||
} from '@mui/material';
|
||||
import { Add as AddIcon } from '@mui/icons-material';
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
|
||||
export interface NumberInputFormFieldProps {
|
||||
name: string;
|
||||
label: string;
|
||||
helperText?: string;
|
||||
defaultValue?: number;
|
||||
disabled?: boolean;
|
||||
required?: boolean;
|
||||
fullWidth?: boolean;
|
||||
min?: number;
|
||||
max?: number;
|
||||
step?: number;
|
||||
placeholder?: string;
|
||||
size?: 'small' | 'medium';
|
||||
showRandomButton?: boolean;
|
||||
onRandomClick?: () => void;
|
||||
}
|
||||
|
||||
export const NumberInputFormField: React.FC<NumberInputFormFieldProps> = ({
|
||||
name,
|
||||
label,
|
||||
helperText,
|
||||
defaultValue = 0,
|
||||
disabled = false,
|
||||
required = false,
|
||||
fullWidth = true,
|
||||
min,
|
||||
max,
|
||||
step,
|
||||
placeholder,
|
||||
size = 'medium',
|
||||
showRandomButton = false,
|
||||
onRandomClick,
|
||||
}) => {
|
||||
const { control } = useFormContext();
|
||||
|
||||
const handleRandomClick = () => {
|
||||
if (onRandomClick) {
|
||||
onRandomClick();
|
||||
} else if (min !== undefined && max !== undefined) {
|
||||
const randomValue = Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
// 这里需要通过setValue来更新表单值,但为了保持组件的纯净性,
|
||||
// 建议通过onRandomClick回调来处理
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography variant="subtitle1" gutterBottom>
|
||||
{required && <span style={{ color: 'red' }}>*</span>}
|
||||
{label}
|
||||
</Typography>
|
||||
<Controller
|
||||
name={name}
|
||||
control={control}
|
||||
defaultValue={defaultValue}
|
||||
rules={{
|
||||
required: required ? `${label}是必填项` : false,
|
||||
min: min !== undefined ? {
|
||||
value: min,
|
||||
message: `${label}不能小于${min}`
|
||||
} : undefined,
|
||||
max: max !== undefined ? {
|
||||
value: max,
|
||||
message: `${label}不能大于${max}`
|
||||
} : undefined,
|
||||
}}
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<TextField
|
||||
{...field}
|
||||
type="number"
|
||||
fullWidth={fullWidth}
|
||||
disabled={disabled}
|
||||
placeholder={placeholder}
|
||||
size={size}
|
||||
error={!!error}
|
||||
helperText={error?.message || helperText}
|
||||
inputProps={{
|
||||
min,
|
||||
max,
|
||||
step,
|
||||
}}
|
||||
onChange={(e) => {
|
||||
const value = e.target.value === '' ? '' : Number(e.target.value);
|
||||
field.onChange(value);
|
||||
}}
|
||||
/>
|
||||
{showRandomButton && (
|
||||
<IconButton
|
||||
onClick={handleRandomClick}
|
||||
disabled={disabled}
|
||||
size={size}
|
||||
>
|
||||
<AddIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
96
src/components/FormField/SelectFormField.tsx
Normal file
96
src/components/FormField/SelectFormField.tsx
Normal file
@@ -0,0 +1,96 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
FormControl,
|
||||
Select,
|
||||
MenuItem,
|
||||
FormHelperText,
|
||||
InputLabel,
|
||||
} from '@mui/material';
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
|
||||
export interface SelectOption {
|
||||
value: string | number;
|
||||
label: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export interface SelectFormFieldProps {
|
||||
name: string;
|
||||
label: string;
|
||||
options: SelectOption[];
|
||||
helperText?: string;
|
||||
defaultValue?: string | number;
|
||||
disabled?: boolean;
|
||||
required?: boolean;
|
||||
fullWidth?: boolean;
|
||||
size?: 'small' | 'medium';
|
||||
displayEmpty?: boolean;
|
||||
emptyLabel?: string;
|
||||
multiple?: boolean;
|
||||
}
|
||||
|
||||
export const SelectFormField: React.FC<SelectFormFieldProps> = ({
|
||||
name,
|
||||
label,
|
||||
options,
|
||||
helperText,
|
||||
defaultValue = '',
|
||||
disabled = false,
|
||||
required = false,
|
||||
fullWidth = true,
|
||||
size = 'medium',
|
||||
displayEmpty = true,
|
||||
emptyLabel = '请选择',
|
||||
multiple = false,
|
||||
}) => {
|
||||
const { control } = useFormContext();
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography variant="subtitle1" gutterBottom>
|
||||
{required && <span style={{ color: 'red' }}>*</span>}
|
||||
{label}
|
||||
</Typography>
|
||||
<Controller
|
||||
name={name}
|
||||
control={control}
|
||||
defaultValue={multiple ? [] : defaultValue}
|
||||
rules={{
|
||||
required: required ? `${label}是必填项` : false,
|
||||
}}
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl fullWidth={fullWidth} error={!!error} size={size}>
|
||||
<Select
|
||||
{...field}
|
||||
disabled={disabled}
|
||||
displayEmpty={displayEmpty}
|
||||
multiple={multiple}
|
||||
>
|
||||
{displayEmpty && !multiple && (
|
||||
<MenuItem value="">
|
||||
{emptyLabel}
|
||||
</MenuItem>
|
||||
)}
|
||||
{options.map((option) => (
|
||||
<MenuItem
|
||||
key={option.value}
|
||||
value={option.value}
|
||||
disabled={option.disabled}
|
||||
>
|
||||
{option.label}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
{(error || helperText) && (
|
||||
<FormHelperText>
|
||||
{error?.message || helperText}
|
||||
</FormHelperText>
|
||||
)}
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
86
src/components/FormField/SliderFormField.tsx
Normal file
86
src/components/FormField/SliderFormField.tsx
Normal file
@@ -0,0 +1,86 @@
|
||||
import React from 'react';
|
||||
import { Box, Typography, Slider } from '@mui/material';
|
||||
import { useController, useFormContext } from 'react-hook-form';
|
||||
|
||||
export interface SliderFormFieldProps {
|
||||
name: string;
|
||||
label: string;
|
||||
min?: number;
|
||||
max?: number;
|
||||
step?: number;
|
||||
defaultValue?: number;
|
||||
valueLabelDisplay?: 'on' | 'auto' | 'off';
|
||||
marks?: boolean | Array<{ value: number; label?: string }>;
|
||||
disabled?: boolean;
|
||||
required?: boolean;
|
||||
sx?: object;
|
||||
}
|
||||
|
||||
export const SliderFormField: React.FC<SliderFormFieldProps> = ({
|
||||
name,
|
||||
label,
|
||||
min = 0,
|
||||
max = 100,
|
||||
step = 1,
|
||||
defaultValue = 0,
|
||||
valueLabelDisplay = 'on',
|
||||
marks = false,
|
||||
disabled = false,
|
||||
required = false,
|
||||
sx = {},
|
||||
}) => {
|
||||
const { control } = useFormContext();
|
||||
|
||||
const {
|
||||
field,
|
||||
fieldState: { error },
|
||||
} = useController({
|
||||
name,
|
||||
control,
|
||||
defaultValue,
|
||||
});
|
||||
|
||||
return (
|
||||
<Box sx={{ mb: 2, ...sx }}>
|
||||
<Typography
|
||||
variant="subtitle1"
|
||||
gutterBottom
|
||||
sx={{
|
||||
color: error ? 'error.main' : 'inherit',
|
||||
fontWeight: required ? 600 : 400,
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
{required && <span style={{ color: 'red' }}> *</span>}
|
||||
</Typography>
|
||||
|
||||
<Box sx={{ px: 2 }}>
|
||||
<Slider
|
||||
{...field}
|
||||
min={min}
|
||||
max={max}
|
||||
step={step}
|
||||
valueLabelDisplay={valueLabelDisplay}
|
||||
marks={marks}
|
||||
disabled={disabled}
|
||||
onChange={(_, value) => field.onChange(value)}
|
||||
sx={{
|
||||
color: error ? 'error.main' : 'primary.main',
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{error && (
|
||||
<Typography
|
||||
variant="caption"
|
||||
color="error"
|
||||
sx={{ mt: 0.5, display: 'block' }}
|
||||
>
|
||||
{error.message}
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default SliderFormField;
|
||||
105
src/components/FormField/SliderInputFormField.tsx
Normal file
105
src/components/FormField/SliderInputFormField.tsx
Normal file
@@ -0,0 +1,105 @@
|
||||
import {
|
||||
FormControl,
|
||||
FormHelperText,
|
||||
Box,
|
||||
Typography,
|
||||
Slider,
|
||||
TextField,
|
||||
Grid,
|
||||
} from '@mui/material';
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
import { type ReactNode } from 'react';
|
||||
import type { IFormLayoutType } from '.';
|
||||
|
||||
export type SliderInputFormFieldProps = {
|
||||
max?: number;
|
||||
min?: number;
|
||||
step?: number;
|
||||
name: string;
|
||||
label: string;
|
||||
tooltip?: ReactNode;
|
||||
defaultValue?: number;
|
||||
className?: string;
|
||||
} & IFormLayoutType;
|
||||
|
||||
export function SliderInputFormField({
|
||||
max = 100,
|
||||
min = 0,
|
||||
step = 1,
|
||||
label,
|
||||
name,
|
||||
tooltip,
|
||||
defaultValue = 0,
|
||||
className,
|
||||
layout = 'horizontal',
|
||||
}: SliderInputFormFieldProps) {
|
||||
const { control, formState: { errors } } = useFormContext();
|
||||
|
||||
const isHorizontal = layout === 'horizontal';
|
||||
|
||||
return (
|
||||
<Box sx={{ mb: 3 }} className={className}>
|
||||
<Controller
|
||||
name={name}
|
||||
control={control}
|
||||
defaultValue={defaultValue}
|
||||
render={({ field }) => (
|
||||
<Box>
|
||||
<Typography
|
||||
variant="h6"
|
||||
gutterBottom={!isHorizontal}
|
||||
sx={{
|
||||
mb: isHorizontal ? 1 : 2,
|
||||
fontSize: isHorizontal ? '0.875rem' : '1.25rem',
|
||||
color: isHorizontal ? 'text.secondary' : 'text.primary'
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
{tooltip && (
|
||||
<Typography
|
||||
component="span"
|
||||
variant="caption"
|
||||
sx={{ ml: 1, color: 'text.secondary' }}
|
||||
>
|
||||
{tooltip}
|
||||
</Typography>
|
||||
)}
|
||||
</Typography>
|
||||
|
||||
<Grid container spacing={2} alignItems="center">
|
||||
<Grid size={{xs: isHorizontal ? 8 : 9}}>
|
||||
<FormControl fullWidth>
|
||||
<Slider
|
||||
{...field}
|
||||
min={min}
|
||||
max={max}
|
||||
step={step}
|
||||
valueLabelDisplay="auto"
|
||||
sx={{ mt: 1 }}
|
||||
/>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
<Grid size={{xs: isHorizontal ? 4 : 3}}>
|
||||
<FormControl>
|
||||
<TextField
|
||||
{...field}
|
||||
type="number"
|
||||
size="small"
|
||||
inputProps={{ min, max, step }}
|
||||
sx={{ width: '80px' }}
|
||||
/>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{errors[name] && (
|
||||
<FormHelperText error sx={{ mt: 1 }}>
|
||||
{errors[name]?.message as string}
|
||||
</FormHelperText>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
72
src/components/FormField/SwitchFormField.tsx
Normal file
72
src/components/FormField/SwitchFormField.tsx
Normal file
@@ -0,0 +1,72 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
FormControlLabel,
|
||||
Switch,
|
||||
FormHelperText,
|
||||
} from '@mui/material';
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
|
||||
export interface SwitchFormFieldProps {
|
||||
name: string;
|
||||
label: string;
|
||||
helperText?: string;
|
||||
defaultValue?: boolean;
|
||||
disabled?: boolean;
|
||||
required?: boolean;
|
||||
size?: 'small' | 'medium';
|
||||
color?: 'primary' | 'secondary' | 'error' | 'info' | 'success' | 'warning';
|
||||
}
|
||||
|
||||
export const SwitchFormField: React.FC<SwitchFormFieldProps> = ({
|
||||
name,
|
||||
label,
|
||||
helperText,
|
||||
defaultValue = false,
|
||||
disabled = false,
|
||||
required = false,
|
||||
size = 'medium',
|
||||
color = 'primary',
|
||||
}) => {
|
||||
const { control } = useFormContext();
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Controller
|
||||
name={name}
|
||||
control={control}
|
||||
defaultValue={defaultValue}
|
||||
rules={{
|
||||
required: required ? `${label}是必填项` : false,
|
||||
}}
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={field.value || false}
|
||||
onChange={field.onChange}
|
||||
disabled={disabled}
|
||||
size={size}
|
||||
color={color}
|
||||
/>
|
||||
}
|
||||
label={
|
||||
<Typography variant="body1">
|
||||
{required && <span style={{ color: 'red' }}>*</span>}
|
||||
{label}
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
{(error || helperText) && (
|
||||
<FormHelperText error={!!error}>
|
||||
{error?.message || helperText}
|
||||
</FormHelperText>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
69
src/components/FormField/TextFormField.tsx
Normal file
69
src/components/FormField/TextFormField.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
import React from 'react';
|
||||
import { TextField, Box, Typography } from '@mui/material';
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
import { type IFormLayoutType } from './index';
|
||||
|
||||
export interface TextFormFieldProps extends IFormLayoutType {
|
||||
name: string;
|
||||
label: string;
|
||||
placeholder?: string;
|
||||
defaultValue?: string;
|
||||
required?: boolean;
|
||||
disabled?: boolean;
|
||||
helperText?: string;
|
||||
type?: 'text' | 'email' | 'password' | 'url' | 'tel';
|
||||
}
|
||||
|
||||
export function TextFormField({
|
||||
name,
|
||||
label,
|
||||
placeholder,
|
||||
defaultValue = '',
|
||||
required = false,
|
||||
disabled = false,
|
||||
layout = 'vertical',
|
||||
helperText,
|
||||
type = 'text',
|
||||
}: TextFormFieldProps) {
|
||||
const { control, formState: { errors } } = useFormContext();
|
||||
const error = errors[name];
|
||||
const isHorizontal = layout === 'horizontal';
|
||||
|
||||
const textField = (
|
||||
<Controller
|
||||
name={name}
|
||||
control={control}
|
||||
defaultValue={defaultValue}
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
type={type}
|
||||
label={isHorizontal ? undefined : label}
|
||||
placeholder={placeholder}
|
||||
required={required}
|
||||
disabled={disabled}
|
||||
error={!!error}
|
||||
helperText={error?.message as string || helperText}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
|
||||
if (isHorizontal) {
|
||||
return (
|
||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Typography variant="subtitle1" sx={{ minWidth: 120, pt: 1 }}>
|
||||
{label}
|
||||
{required && <span style={{ color: 'red' }}>*</span>}
|
||||
</Typography>
|
||||
<Box sx={{ flex: 1 }}>
|
||||
{textField}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return textField;
|
||||
}
|
||||
35
src/components/FormField/index.ts
Normal file
35
src/components/FormField/index.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
// 基础组件导出
|
||||
export { SliderInputFormField } from './SliderInputFormField';
|
||||
export { SwitchFormField } from './SwitchFormField';
|
||||
export { SelectFormField } from './SelectFormField';
|
||||
export { MultilineTextFormField } from './MultilineTextFormField';
|
||||
export { NumberInputFormField } from './NumberInputFormField';
|
||||
export { ChipListFormField } from './ChipListFormField';
|
||||
export { SliderFormField } from './SliderFormField';
|
||||
export { TextFormField } from './TextFormField';
|
||||
export { DatePickerFormField } from './DatePickerFormField';
|
||||
export { CheckboxFormField } from './CheckboxFormField';
|
||||
|
||||
// 类型导出
|
||||
export type { SliderInputFormFieldProps } from './SliderInputFormField';
|
||||
export type { SwitchFormFieldProps } from './SwitchFormField';
|
||||
export type { SelectFormFieldProps, SelectOption } from './SelectFormField';
|
||||
export type { MultilineTextFormFieldProps } from './MultilineTextFormField';
|
||||
export type { NumberInputFormFieldProps } from './NumberInputFormField';
|
||||
export type { ChipListFormFieldProps } from './ChipListFormField';
|
||||
export type { SliderFormFieldProps } from './SliderFormField';
|
||||
export type { TextFormFieldProps } from './TextFormField';
|
||||
export type { DatePickerFormFieldProps } from './DatePickerFormField';
|
||||
export type { CheckboxFormFieldProps } from './CheckboxFormField';
|
||||
|
||||
export type IFormLayoutType = {
|
||||
layout?: FormLayout;
|
||||
};
|
||||
|
||||
export const FORM_LAYOUTS = {
|
||||
Vertical: 'vertical',
|
||||
Horizontal: 'horizontal',
|
||||
} as const;
|
||||
|
||||
export type FormLayout = (typeof FORM_LAYOUTS)[keyof typeof FORM_LAYOUTS];
|
||||
|
||||
163
src/pages/FormFieldTest.tsx
Normal file
163
src/pages/FormFieldTest.tsx
Normal file
@@ -0,0 +1,163 @@
|
||||
import React from 'react';
|
||||
import { Box, Typography, Paper, Divider } from '@mui/material';
|
||||
import { useForm, FormProvider } from 'react-hook-form';
|
||||
import {
|
||||
SliderInputFormField,
|
||||
SwitchFormField,
|
||||
SelectFormField,
|
||||
MultilineTextFormField,
|
||||
NumberInputFormField,
|
||||
ChipListFormField,
|
||||
type SelectOption,
|
||||
} from '@/components/FormField';
|
||||
|
||||
const FormFieldTest: React.FC = () => {
|
||||
const methods = useForm({
|
||||
defaultValues: {
|
||||
parser_config: {
|
||||
auto_keywords: 5,
|
||||
auto_questions: 3,
|
||||
delimiter: '\n',
|
||||
chunk_token_num: 512,
|
||||
layout_recognize: 'DeepDOC',
|
||||
html4excel: false,
|
||||
},
|
||||
pagerank: 50,
|
||||
custom_slider: 25,
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = (data: any) => {
|
||||
console.log('Form Data:', data);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box sx={{ p: 3, maxWidth: 800, mx: 'auto' }}>
|
||||
<Typography variant="h4" gutterBottom>
|
||||
FormField 组件测试
|
||||
</Typography>
|
||||
|
||||
<Paper sx={{ p: 3, mt: 2 }}>
|
||||
<FormProvider {...methods}>
|
||||
<form onSubmit={methods.handleSubmit(onSubmit)}>
|
||||
|
||||
<Typography variant="h6" gutterBottom>
|
||||
自动关键词和问题
|
||||
</Typography>
|
||||
<NumberInputFormField
|
||||
name="parser_config.auto_keywords"
|
||||
label="自动关键词"
|
||||
defaultValue={5}
|
||||
min={0}
|
||||
placeholder="输入关键词数量"
|
||||
/>
|
||||
<NumberInputFormField
|
||||
name="parser_config.auto_questions"
|
||||
label="自动问题"
|
||||
defaultValue={3}
|
||||
min={0}
|
||||
placeholder="输入问题数量"
|
||||
/>
|
||||
<Divider sx={{ my: 3 }} />
|
||||
|
||||
<Typography variant="h6" gutterBottom>
|
||||
分隔符配置
|
||||
</Typography>
|
||||
<MultilineTextFormField
|
||||
name="parser_config.delimiter"
|
||||
label="分隔符"
|
||||
defaultValue="\n"
|
||||
rows={2}
|
||||
/>
|
||||
<Divider sx={{ my: 3 }} />
|
||||
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Token数量配置
|
||||
</Typography>
|
||||
<SliderInputFormField
|
||||
name="parser_config.chunk_token_num"
|
||||
label="最大Token数"
|
||||
min={64}
|
||||
max={2048}
|
||||
step={64}
|
||||
defaultValue={512}
|
||||
layout="horizontal"
|
||||
/>
|
||||
<Divider sx={{ my: 3 }} />
|
||||
|
||||
<Typography variant="h6" gutterBottom>
|
||||
页面排名
|
||||
</Typography>
|
||||
<NumberInputFormField
|
||||
name="pagerank"
|
||||
label="页面排名"
|
||||
defaultValue={50}
|
||||
min={0}
|
||||
placeholder="输入页面排名"
|
||||
/>
|
||||
<Divider sx={{ my: 3 }} />
|
||||
|
||||
<Typography variant="h6" gutterBottom>
|
||||
布局识别
|
||||
</Typography>
|
||||
<SelectFormField
|
||||
name="parser_config.layout_recognize"
|
||||
label="布局识别方法"
|
||||
options={[
|
||||
{ value: 'DeepDOC', label: 'DeepDOC' },
|
||||
{ value: 'LayoutLM', label: 'LayoutLM' },
|
||||
{ value: 'OCR', label: 'OCR' }
|
||||
]}
|
||||
defaultValue="DeepDOC"
|
||||
/>
|
||||
<Divider sx={{ my: 3 }} />
|
||||
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Excel转HTML
|
||||
</Typography>
|
||||
<SwitchFormField
|
||||
name="parser_config.html4excel"
|
||||
label="Excel转HTML"
|
||||
defaultValue={false}
|
||||
/>
|
||||
<Divider sx={{ my: 3 }} />
|
||||
|
||||
<Typography variant="h6" gutterBottom>
|
||||
自定义滑块(垂直布局)
|
||||
</Typography>
|
||||
<SliderInputFormField
|
||||
name="custom_slider"
|
||||
label="自定义滑块"
|
||||
tooltip="这是一个自定义的滑块组件"
|
||||
max={100}
|
||||
min={0}
|
||||
defaultValue={25}
|
||||
layout={'vertical'}
|
||||
/>
|
||||
|
||||
<Box sx={{ mt: 4, display: 'flex', gap: 2 }}>
|
||||
<button type="submit">提交测试</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => methods.reset()}
|
||||
>
|
||||
重置表单
|
||||
</button>
|
||||
</Box>
|
||||
</form>
|
||||
</FormProvider>
|
||||
</Paper>
|
||||
|
||||
<Paper sx={{ p: 3, mt: 3 }}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
当前表单值
|
||||
</Typography>
|
||||
<pre style={{ fontSize: '12px', overflow: 'auto' }}>
|
||||
{JSON.stringify(methods.watch(), null, 2)}
|
||||
</pre>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default FormFieldTest;
|
||||
@@ -1,49 +1,16 @@
|
||||
import React from 'react';
|
||||
import { ConfigurationFormContainer } from './configuration-form-container';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
import { ChunkMethodItem, EmbeddingModelItem, PageRankItem, AutoKeywordsItem, AutoQuestionsItem, TagsItem } from './common-items';
|
||||
|
||||
export function AudioConfiguration() {
|
||||
return (
|
||||
<ConfigurationFormContainer>
|
||||
<ChunkMethodItem />
|
||||
<EmbeddingModelItem />
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
PageRank配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
自动关键词配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
自动问题配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
Raptor配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
GraphRAG配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
标签配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
<PageRankItem />
|
||||
<AutoKeywordsItem />
|
||||
<AutoQuestionsItem />
|
||||
<TagsItem />
|
||||
</ConfigurationFormContainer>
|
||||
);
|
||||
}
|
||||
@@ -1,60 +1,60 @@
|
||||
import React from 'react';
|
||||
import { ConfigurationFormContainer, MainContainer } from './configuration-form-container';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
import {
|
||||
ChunkMethodItem,
|
||||
EmbeddingModelItem,
|
||||
LayoutRecognizeItem,
|
||||
PageRankItem,
|
||||
AutoKeywordsItem,
|
||||
AutoQuestionsItem,
|
||||
UseRaptorItem,
|
||||
RaptorPromptItem,
|
||||
RaptorMaxTokenItem,
|
||||
RaptorThresholdItem,
|
||||
RaptorMaxClusterItem,
|
||||
RaptorRandomSeedItem,
|
||||
UseGraphragItem,
|
||||
EntityTypesItem,
|
||||
GraphragMethodItem,
|
||||
EntityNormalizeItem,
|
||||
CommunityReportItem,
|
||||
TagsItem
|
||||
} from './common-items';
|
||||
|
||||
export function BookConfiguration() {
|
||||
return (
|
||||
<MainContainer>
|
||||
<ConfigurationFormContainer>
|
||||
<ChunkMethodItem />
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
布局识别配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
<LayoutRecognizeItem />
|
||||
<EmbeddingModelItem />
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
PageRank配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
<PageRankItem />
|
||||
</ConfigurationFormContainer>
|
||||
|
||||
<ConfigurationFormContainer>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
自动关键词配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
自动问题配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
<AutoKeywordsItem />
|
||||
<AutoQuestionsItem />
|
||||
</ConfigurationFormContainer>
|
||||
|
||||
<ConfigurationFormContainer>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
Raptor配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
<UseRaptorItem />
|
||||
<RaptorPromptItem />
|
||||
<RaptorMaxTokenItem />
|
||||
<RaptorThresholdItem />
|
||||
<RaptorMaxClusterItem />
|
||||
<RaptorRandomSeedItem />
|
||||
</ConfigurationFormContainer>
|
||||
|
||||
<Box sx={{ mb: 2, p: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
GraphRAG配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
<ConfigurationFormContainer>
|
||||
<UseGraphragItem />
|
||||
<EntityTypesItem />
|
||||
<GraphragMethodItem />
|
||||
<EntityNormalizeItem />
|
||||
<CommunityReportItem />
|
||||
</ConfigurationFormContainer>
|
||||
|
||||
<ConfigurationFormContainer>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
标签配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
<TagsItem />
|
||||
</ConfigurationFormContainer>
|
||||
</MainContainer>
|
||||
);
|
||||
|
||||
@@ -1,118 +0,0 @@
|
||||
import {
|
||||
FormControl,
|
||||
InputLabel,
|
||||
Select,
|
||||
MenuItem,
|
||||
FormHelperText,
|
||||
Box,
|
||||
Typography,
|
||||
ListSubheader,
|
||||
} from '@mui/material';
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
import { DOCUMENT_PARSER_TYPES } from '@/constants/knowledge';
|
||||
import { useSelectChunkMethodList } from '../hooks';
|
||||
import { useEmbeddingModelOptions } from '@/hooks/llm-hooks';
|
||||
|
||||
// 解析器选项配置
|
||||
const PARSER_OPTIONS = [
|
||||
{ value: DOCUMENT_PARSER_TYPES.Naive, label: 'General', description: '通用解析器' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Qa, label: 'Q&A', description: 'Q&A解析器' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Resume, label: 'Resume', description: 'Resume解析器' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Manual, label: 'Manual', description: 'Manual解析器' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Table, label: 'Table', description: 'Table解析器' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Paper, label: 'Paper', description: 'Paper解析器' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Book, label: 'Book', description: 'Book解析器' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Laws, label: 'Laws', description: 'Laws解析器' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Presentation, label: 'Presentation', description: 'Presentation解析器' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.One, label: 'One', description: 'One解析器' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Tag, label: 'Tag', description: 'Tag解析器' },
|
||||
];
|
||||
|
||||
export function ChunkMethodItem() {
|
||||
const { control, formState: { errors } } = useFormContext();
|
||||
const parserIds = useSelectChunkMethodList();
|
||||
|
||||
const parserOptions = parserIds.map((x) => ({
|
||||
value: x,
|
||||
label: PARSER_OPTIONS.find((y) => y.value === x)?.label || x,
|
||||
description: PARSER_OPTIONS.find((y) => y.value === x)?.description || x,
|
||||
}));
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
切片方法
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_id"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<FormControl fullWidth error={!!errors.parser_id}>
|
||||
<InputLabel>选择切片方法</InputLabel>
|
||||
<Select
|
||||
{...field}
|
||||
label="选择切片方法"
|
||||
>
|
||||
{parserOptions.map((option) => (
|
||||
<MenuItem key={option.value} value={option.value}>
|
||||
<Box>
|
||||
<Typography variant="body1">{option.label}</Typography>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
{option.description}
|
||||
</Typography>
|
||||
</Box>
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
{errors.parser_id && (
|
||||
<FormHelperText>{errors.parser_id.message as string}</FormHelperText>
|
||||
)}
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export function EmbeddingModelItem() {
|
||||
const { control, formState: { errors } } = useFormContext();
|
||||
|
||||
const { options: embdOptions } = useEmbeddingModelOptions();
|
||||
|
||||
return (
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
嵌入模型
|
||||
</Typography>
|
||||
<Controller
|
||||
name="embd_id"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<FormControl fullWidth error={!!errors.embd_id}>
|
||||
<InputLabel>选择嵌入模型</InputLabel>
|
||||
<Select
|
||||
{...field}
|
||||
label="选择嵌入模型"
|
||||
>
|
||||
{
|
||||
embdOptions.map((group) => [
|
||||
<ListSubheader key={`header-${group.label}`} disableSticky>
|
||||
{group.label}
|
||||
</ListSubheader>,
|
||||
...group.options.map((option) => (
|
||||
<MenuItem key={option.value} value={option.value} disabled={option.disabled}>
|
||||
{option.label}
|
||||
</MenuItem>
|
||||
))
|
||||
])
|
||||
}
|
||||
</Select>
|
||||
{errors.embd_id && (
|
||||
<FormHelperText>{errors.embd_id.message as string}</FormHelperText>
|
||||
)}
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
416
src/pages/knowledge/configuration/common-items.tsx
Normal file
416
src/pages/knowledge/configuration/common-items.tsx
Normal file
@@ -0,0 +1,416 @@
|
||||
import { useMemo } from 'react';
|
||||
import {
|
||||
FormControl,
|
||||
InputLabel,
|
||||
Select,
|
||||
MenuItem,
|
||||
FormHelperText,
|
||||
Box,
|
||||
Typography,
|
||||
ListSubheader,
|
||||
Button,
|
||||
} from '@mui/material';
|
||||
import { Shuffle as ShuffleIcon } from '@mui/icons-material';
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
import { DOCUMENT_PARSER_TYPES, LLM_MODEL_TYPES, type LlmModelType } from '@/constants/knowledge';
|
||||
import { useSelectChunkMethodList } from '../hooks';
|
||||
import { useEmbeddingModelOptions, useLlmOptionsByModelType } from '@/hooks/llm-hooks';
|
||||
import {
|
||||
SliderInputFormField,
|
||||
SwitchFormField,
|
||||
SelectFormField,
|
||||
MultilineTextFormField,
|
||||
NumberInputFormField,
|
||||
ChipListFormField,
|
||||
TextFormField,
|
||||
type SelectOption,
|
||||
} from '@/components/FormField';
|
||||
|
||||
// 解析器选项配置
|
||||
const PARSER_OPTIONS = [
|
||||
{ value: DOCUMENT_PARSER_TYPES.Naive, label: 'General', description: '通用解析器' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Qa, label: 'Q&A', description: 'Q&A解析器' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Resume, label: 'Resume', description: 'Resume解析器' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Manual, label: 'Manual', description: 'Manual解析器' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Table, label: 'Table', description: 'Table解析器' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Paper, label: 'Paper', description: 'Paper解析器' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Book, label: 'Book', description: 'Book解析器' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Laws, label: 'Laws', description: 'Laws解析器' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Presentation, label: 'Presentation', description: 'Presentation解析器' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.One, label: 'One', description: 'One解析器' },
|
||||
{ value: DOCUMENT_PARSER_TYPES.Tag, label: 'Tag', description: 'Tag解析器' },
|
||||
];
|
||||
|
||||
export function ChunkMethodItem() {
|
||||
const { control, formState: { errors } } = useFormContext();
|
||||
const parserIds = useSelectChunkMethodList();
|
||||
|
||||
const parserOptions = parserIds.map((x) => ({
|
||||
value: x,
|
||||
label: PARSER_OPTIONS.find((y) => y.value === x)?.label || x,
|
||||
description: PARSER_OPTIONS.find((y) => y.value === x)?.description || x,
|
||||
}));
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
切片方法
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_id"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<FormControl fullWidth error={!!errors.parser_id}>
|
||||
<InputLabel>选择切片方法</InputLabel>
|
||||
<Select
|
||||
{...field}
|
||||
label="选择切片方法"
|
||||
>
|
||||
{parserOptions.map((option) => (
|
||||
<MenuItem key={option.value} value={option.value}>
|
||||
<Box>
|
||||
<Typography variant="body1">{option.label}</Typography>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
{option.description}
|
||||
</Typography>
|
||||
</Box>
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
{errors.parser_id && (
|
||||
<FormHelperText>{errors.parser_id.message as string}</FormHelperText>
|
||||
)}
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
// 基于基础FormField组件的业务组合组件
|
||||
|
||||
// 分块token数量配置
|
||||
export function ChunkTokenNumberItem() {
|
||||
return (
|
||||
<SliderInputFormField
|
||||
name="parser_config.chunk_token_num"
|
||||
label="建议文本块大小"
|
||||
min={64}
|
||||
max={2048}
|
||||
step={64}
|
||||
defaultValue={512}
|
||||
layout="horizontal"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// 页面排名配置
|
||||
export function PageRankItem() {
|
||||
return (
|
||||
<NumberInputFormField
|
||||
name="parser_config.page_rank"
|
||||
label="页面排名"
|
||||
defaultValue={0}
|
||||
min={0}
|
||||
placeholder="输入页面排名"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// 自动关键词数量配置
|
||||
export function AutoKeywordsItem() {
|
||||
return (
|
||||
<NumberInputFormField
|
||||
name="parser_config.auto_keywords"
|
||||
label="自动关键词"
|
||||
defaultValue={0}
|
||||
min={0}
|
||||
placeholder="输入关键词数量"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// 自动问题数量配置
|
||||
export function AutoQuestionsItem() {
|
||||
return (
|
||||
<NumberInputFormField
|
||||
name="parser_config.auto_questions"
|
||||
label="自动问题"
|
||||
defaultValue={0}
|
||||
min={0}
|
||||
placeholder="输入问题数量"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// 表格转HTML开关
|
||||
export function HtmlForExcelItem() {
|
||||
return (
|
||||
<SwitchFormField
|
||||
name="parser_config.html4excel"
|
||||
label="表格转HTML"
|
||||
defaultValue={false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// 标签集选择
|
||||
export function TagsItem() {
|
||||
const tagsOptions: SelectOption[] = [
|
||||
{ value: '', label: '请选择' },
|
||||
// 这里可以根据实际需求添加标签选项
|
||||
];
|
||||
|
||||
return (
|
||||
<SelectFormField
|
||||
name="parser_config.tags"
|
||||
label="标签集"
|
||||
options={tagsOptions}
|
||||
defaultValue=""
|
||||
displayEmpty
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// RAPTOR策略开关
|
||||
export function UseRaptorItem() {
|
||||
return (
|
||||
<SwitchFormField
|
||||
name="parser_config.raptor.use_raptor"
|
||||
label="使用召回增强RAPTOR策略"
|
||||
defaultValue={false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// RAPTOR提示词配置
|
||||
export function RaptorPromptItem() {
|
||||
return (
|
||||
<MultilineTextFormField
|
||||
name="parser_config.raptor.prompt"
|
||||
label="提示词"
|
||||
defaultValue="请总结以下段落。小心数字,不要编造。段落如下:\n{cluster_content}\n以上就是你需要总结的内容。"
|
||||
rows={4}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// RAPTOR最大token数配置
|
||||
export function RaptorMaxTokenItem() {
|
||||
return (
|
||||
<SliderInputFormField
|
||||
name="parser_config.raptor.max_token"
|
||||
label="最大token数"
|
||||
min={64}
|
||||
max={512}
|
||||
step={32}
|
||||
defaultValue={256}
|
||||
layout="horizontal"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// RAPTOR阈值配置
|
||||
export function RaptorThresholdItem() {
|
||||
return (
|
||||
<SliderInputFormField
|
||||
name="parser_config.raptor.threshold"
|
||||
label="阈值"
|
||||
min={0}
|
||||
max={1}
|
||||
step={0.1}
|
||||
defaultValue={0.1}
|
||||
layout="horizontal"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// RAPTOR最大聚类数配置
|
||||
export function RaptorMaxClusterItem() {
|
||||
return (
|
||||
<SliderInputFormField
|
||||
name="parser_config.raptor.max_cluster"
|
||||
label="最大聚类数"
|
||||
min={16}
|
||||
max={128}
|
||||
step={16}
|
||||
defaultValue={64}
|
||||
layout="horizontal"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// RAPTOR随机种子配置
|
||||
export function RaptorRandomSeedItem() {
|
||||
return (
|
||||
<NumberInputFormField
|
||||
name="parser_config.raptor.random_seed"
|
||||
label="随机种子"
|
||||
defaultValue={0}
|
||||
showRandomButton
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// 知识图谱开关
|
||||
export function UseGraphragItem() {
|
||||
return (
|
||||
<SwitchFormField
|
||||
name="parser_config.graphrag.use_graphrag"
|
||||
label="提取知识图谱"
|
||||
defaultValue={false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// 实体类型配置
|
||||
export function EntityTypesItem() {
|
||||
return (
|
||||
<ChipListFormField
|
||||
name="parser_config.graphrag.entity_types"
|
||||
label="*实体类型"
|
||||
defaultValue={['organization', 'person', 'geo', 'event', 'category']}
|
||||
required
|
||||
allowAdd
|
||||
allowDelete
|
||||
maxChips={10}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// GraphRAG方法选择
|
||||
export function GraphragMethodItem() {
|
||||
const methodOptions: SelectOption[] = [
|
||||
{ value: 'Light', label: 'Light' },
|
||||
{ value: 'General', label: 'General' },
|
||||
];
|
||||
|
||||
return (
|
||||
<SelectFormField
|
||||
name="parser_config.graphrag.method"
|
||||
label="方法"
|
||||
options={methodOptions}
|
||||
defaultValue="Light"
|
||||
displayEmpty={false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// 实体归一化开关
|
||||
export function EntityNormalizeItem() {
|
||||
return (
|
||||
<SwitchFormField
|
||||
name="parser_config.graphrag.entity_normalize"
|
||||
label="实体归一化"
|
||||
defaultValue={false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// 社区报告生成开关
|
||||
export function CommunityReportItem() {
|
||||
return (
|
||||
<SwitchFormField
|
||||
name="parser_config.graphrag.community_report"
|
||||
label="社区报告生成"
|
||||
defaultValue={false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function EmbeddingModelItem() {
|
||||
const { control } = useFormContext();
|
||||
const { options } = useEmbeddingModelOptions();
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography variant="subtitle1" sx={{ minWidth: 120 }}>
|
||||
嵌入模型
|
||||
</Typography>
|
||||
<Box sx={{ flex: 1 }}>
|
||||
<Controller
|
||||
name="embd_id"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<FormControl fullWidth variant="outlined">
|
||||
<Select
|
||||
{...field}
|
||||
>
|
||||
{options.map((group) => [
|
||||
<ListSubheader key={group.label}>{group.label}</ListSubheader>,
|
||||
...group.options.map((option) => (
|
||||
<MenuItem key={option.value} value={option.value} disabled={option.disabled}>
|
||||
{option.label}
|
||||
</MenuItem>
|
||||
))
|
||||
])}
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
// PDF解析器配置
|
||||
export function LayoutRecognizeItem() {
|
||||
const { control, setValue, formState } = useFormContext();
|
||||
const { getOptionsByModelType } = useLlmOptionsByModelType();
|
||||
|
||||
const options = useMemo(() => {
|
||||
// 基础选项
|
||||
const basicOptions = [
|
||||
{ value: 'DeepDOC', label: 'DeepDOC' },
|
||||
{ value: 'Plain Text', label: '纯文本' }
|
||||
];
|
||||
|
||||
// 获取图像转文本模型选项
|
||||
const image2TextOptions = getOptionsByModelType(LLM_MODEL_TYPES.Image2text);
|
||||
|
||||
// 将图像转文本模型选项转换为SelectOption格式
|
||||
const image2TextSelectOptions = image2TextOptions.flatMap(group =>
|
||||
group.options.map(option => ({
|
||||
value: option.value,
|
||||
label: `${option.label} (实验性)`
|
||||
}))
|
||||
);
|
||||
|
||||
return [...basicOptions, ...image2TextSelectOptions];
|
||||
}, [getOptionsByModelType]);
|
||||
|
||||
return (
|
||||
<Controller
|
||||
name="parser_config.layout_recognize"
|
||||
control={control}
|
||||
render={({ field }) => {
|
||||
// 设置默认值
|
||||
if (typeof field.value === 'undefined') {
|
||||
const defaultValue = formState.defaultValues?.parser_config?.layout_recognize ?? 'DeepDOC';
|
||||
setValue('parser_config.layout_recognize', defaultValue);
|
||||
}
|
||||
|
||||
return (
|
||||
<SelectFormField
|
||||
name="parser_config.layout_recognize"
|
||||
label="PDF解析器"
|
||||
options={options}
|
||||
defaultValue="DeepDOC"
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// 文本分段标识符配置
|
||||
export function DelimiterItem() {
|
||||
return (
|
||||
<TextFormField
|
||||
name="parser_config.delimiter"
|
||||
label="分隔符"
|
||||
placeholder="请输入分隔符"
|
||||
defaultValue="\n!?。;!?"
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -1,49 +1,16 @@
|
||||
import React from 'react';
|
||||
import { ConfigurationFormContainer } from './configuration-form-container';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
import { ChunkMethodItem, EmbeddingModelItem, PageRankItem, AutoKeywordsItem, AutoQuestionsItem, TagsItem } from './common-items';
|
||||
|
||||
export function EmailConfiguration() {
|
||||
return (
|
||||
<ConfigurationFormContainer>
|
||||
<ChunkMethodItem />
|
||||
<EmbeddingModelItem />
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
PageRank配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
自动关键词配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
自动问题配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
Raptor配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
GraphRAG配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
标签配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
<PageRankItem />
|
||||
<AutoKeywordsItem />
|
||||
<AutoQuestionsItem />
|
||||
<TagsItem />
|
||||
</ConfigurationFormContainer>
|
||||
);
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
export { NaiveConfiguration } from './naive';
|
||||
export { QAConfiguration } from './qa';
|
||||
export { PaperConfiguration } from './paper';
|
||||
export { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
export { ChunkMethodItem, EmbeddingModelItem } from './common-items';
|
||||
export { ConfigurationFormContainer, MainContainer } from './configuration-form-container';
|
||||
|
||||
// 所有解析器配置组件
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-items';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
|
||||
export function KnowledgeGraphConfiguration() {
|
||||
|
||||
@@ -1,61 +1,16 @@
|
||||
import React from 'react';
|
||||
import { ConfigurationFormContainer, MainContainer } from './configuration-form-container';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
import { ConfigurationFormContainer } from './configuration-form-container';
|
||||
import { ChunkMethodItem, EmbeddingModelItem, PageRankItem, AutoKeywordsItem, AutoQuestionsItem, TagsItem } from './common-items';
|
||||
|
||||
export function LawsConfiguration() {
|
||||
return (
|
||||
<MainContainer>
|
||||
<ConfigurationFormContainer>
|
||||
<ChunkMethodItem />
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
布局识别配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
<EmbeddingModelItem />
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
PageRank配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
</ConfigurationFormContainer>
|
||||
|
||||
<ConfigurationFormContainer>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
自动关键词配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
自动问题配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
</ConfigurationFormContainer>
|
||||
|
||||
<ConfigurationFormContainer>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
Raptor配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
</ConfigurationFormContainer>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
GraphRAG配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<ConfigurationFormContainer>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
标签配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
</ConfigurationFormContainer>
|
||||
</MainContainer>
|
||||
<ConfigurationFormContainer>
|
||||
<ChunkMethodItem />
|
||||
<EmbeddingModelItem />
|
||||
<PageRankItem />
|
||||
<AutoKeywordsItem />
|
||||
<AutoQuestionsItem />
|
||||
<TagsItem />
|
||||
</ConfigurationFormContainer>
|
||||
);
|
||||
}
|
||||
@@ -1,59 +1,67 @@
|
||||
import React from 'react';
|
||||
import { ConfigurationFormContainer, MainContainer } from './configuration-form-container';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
import {
|
||||
ChunkMethodItem,
|
||||
EmbeddingModelItem,
|
||||
ChunkTokenNumberItem,
|
||||
DelimiterItem,
|
||||
LayoutRecognizeItem,
|
||||
PageRankItem,
|
||||
AutoKeywordsItem,
|
||||
AutoQuestionsItem,
|
||||
HtmlForExcelItem,
|
||||
UseRaptorItem,
|
||||
RaptorPromptItem,
|
||||
RaptorMaxTokenItem,
|
||||
RaptorThresholdItem,
|
||||
RaptorMaxClusterItem,
|
||||
RaptorRandomSeedItem,
|
||||
UseGraphragItem,
|
||||
EntityTypesItem,
|
||||
GraphragMethodItem,
|
||||
EntityNormalizeItem,
|
||||
CommunityReportItem,
|
||||
TagsItem
|
||||
} from './common-items';
|
||||
|
||||
export function ManualConfiguration() {
|
||||
return (
|
||||
<MainContainer>
|
||||
<ConfigurationFormContainer>
|
||||
<ChunkMethodItem />
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
布局识别配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
<ChunkTokenNumberItem />
|
||||
<DelimiterItem />
|
||||
<LayoutRecognizeItem />
|
||||
<EmbeddingModelItem />
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
PageRank配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
</ConfigurationFormContainer>
|
||||
|
||||
<ConfigurationFormContainer>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
自动关键词配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
自动问题配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
<PageRankItem />
|
||||
<AutoKeywordsItem />
|
||||
<AutoQuestionsItem />
|
||||
<HtmlForExcelItem />
|
||||
</ConfigurationFormContainer>
|
||||
|
||||
<ConfigurationFormContainer>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
Raptor配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
<UseRaptorItem />
|
||||
<RaptorPromptItem />
|
||||
<RaptorMaxTokenItem />
|
||||
<RaptorThresholdItem />
|
||||
<RaptorMaxClusterItem />
|
||||
<RaptorRandomSeedItem />
|
||||
</ConfigurationFormContainer>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
GraphRAG配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
<ConfigurationFormContainer>
|
||||
<UseGraphragItem />
|
||||
<EntityTypesItem />
|
||||
<GraphragMethodItem />
|
||||
<EntityNormalizeItem />
|
||||
<CommunityReportItem />
|
||||
</ConfigurationFormContainer>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
标签配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
<ConfigurationFormContainer>
|
||||
<TagsItem />
|
||||
</ConfigurationFormContainer>
|
||||
</MainContainer>
|
||||
);
|
||||
}
|
||||
@@ -2,27 +2,39 @@ import React from 'react';
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
FormControl,
|
||||
InputLabel,
|
||||
Select,
|
||||
MenuItem,
|
||||
TextField,
|
||||
FormControlLabel,
|
||||
Switch,
|
||||
Slider,
|
||||
Accordion,
|
||||
AccordionSummary,
|
||||
AccordionDetails,
|
||||
Chip,
|
||||
IconButton,
|
||||
} from '@mui/material';
|
||||
import { ExpandMore as ExpandMoreIcon, Add as AddIcon } from '@mui/icons-material';
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
import { ExpandMore as ExpandMoreIcon } from '@mui/icons-material';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import { ConfigurationFormContainer, MainContainer } from './configuration-form-container';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
import {
|
||||
ChunkMethodItem,
|
||||
EmbeddingModelItem,
|
||||
ChunkTokenNumberItem,
|
||||
PageRankItem,
|
||||
AutoKeywordsItem,
|
||||
AutoQuestionsItem,
|
||||
HtmlForExcelItem,
|
||||
TagsItem,
|
||||
UseRaptorItem,
|
||||
RaptorPromptItem,
|
||||
RaptorMaxTokenItem,
|
||||
RaptorThresholdItem,
|
||||
RaptorMaxClusterItem,
|
||||
RaptorRandomSeedItem,
|
||||
UseGraphragItem,
|
||||
EntityTypesItem,
|
||||
GraphragMethodItem,
|
||||
EntityNormalizeItem,
|
||||
CommunityReportItem,
|
||||
LayoutRecognizeItem,
|
||||
DelimiterItem
|
||||
} from './common-items';
|
||||
|
||||
export function NaiveConfiguration() {
|
||||
const { control, watch, formState: { errors } } = useFormContext();
|
||||
const { formState: { errors } } = useFormContext();
|
||||
|
||||
return (
|
||||
<ConfigurationFormContainer>
|
||||
@@ -38,72 +50,16 @@ export function NaiveConfiguration() {
|
||||
<ChunkMethodItem />
|
||||
|
||||
{/* PDF解析器 */}
|
||||
<Box>
|
||||
<Typography variant="subtitle1" gutterBottom>
|
||||
PDF解析器
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_config.pdf_parser"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<FormControl fullWidth>
|
||||
<Select
|
||||
{...field}
|
||||
displayEmpty
|
||||
defaultValue="Naive"
|
||||
>
|
||||
<MenuItem value="Naive">Naive</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
<LayoutRecognizeItem />
|
||||
|
||||
{/* 嵌入模型 */}
|
||||
<EmbeddingModelItem />
|
||||
|
||||
{/* 建议文本块大小 */}
|
||||
<Box>
|
||||
<Typography variant="subtitle1" gutterBottom>
|
||||
建议文本块大小
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_config.chunk_token_num"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Box sx={{ px: 2 }}>
|
||||
<Slider
|
||||
{...field}
|
||||
min={128}
|
||||
max={2048}
|
||||
step={64}
|
||||
valueLabelDisplay="on"
|
||||
defaultValue={512}
|
||||
onChange={(_, value) => field.onChange(value)}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
<ChunkTokenNumberItem />
|
||||
|
||||
{/* 文本分段标识符 */}
|
||||
<Box>
|
||||
<Typography variant="subtitle1" gutterBottom>
|
||||
*文本分段标识符
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_config.delimiter"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
fullWidth
|
||||
placeholder="\\n"
|
||||
defaultValue="\\n"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
<DelimiterItem />
|
||||
</Box>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
@@ -116,250 +72,47 @@ export function NaiveConfiguration() {
|
||||
<AccordionDetails>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
|
||||
{/* 页面排名 */}
|
||||
<Box>
|
||||
<Typography variant="subtitle1" gutterBottom>
|
||||
页面排名
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_config.page_rank"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
type="number"
|
||||
fullWidth
|
||||
defaultValue={0}
|
||||
inputProps={{ min: 0 }}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
<PageRankItem />
|
||||
|
||||
{/* 自动关键词提取 */}
|
||||
<Box>
|
||||
<Typography variant="subtitle1" gutterBottom>
|
||||
自动关键词提取
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_config.auto_keywords"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
type="number"
|
||||
fullWidth
|
||||
defaultValue={0}
|
||||
inputProps={{ min: 0 }}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
<AutoKeywordsItem />
|
||||
|
||||
{/* 自动问题提取 */}
|
||||
<Box>
|
||||
<Typography variant="subtitle1" gutterBottom>
|
||||
自动问题提取
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_config.auto_questions"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
type="number"
|
||||
fullWidth
|
||||
defaultValue={0}
|
||||
inputProps={{ min: 0 }}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
<AutoQuestionsItem />
|
||||
|
||||
{/* 表格转HTML */}
|
||||
<Box>
|
||||
<Controller
|
||||
name="parser_config.html4excel"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={field.value || false}
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
}
|
||||
label="表格转HTML"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
<HtmlForExcelItem />
|
||||
|
||||
{/* 标签集 */}
|
||||
<Box>
|
||||
<Typography variant="subtitle1" gutterBottom>
|
||||
标签集
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_config.tags"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<FormControl fullWidth>
|
||||
<Select
|
||||
{...field}
|
||||
displayEmpty
|
||||
defaultValue=""
|
||||
>
|
||||
<MenuItem value="">请选择</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
<TagsItem />
|
||||
</Box>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
|
||||
{/* 第三部分:RAPTOR策略 */}
|
||||
<Accordion defaultExpanded>
|
||||
<Accordion>
|
||||
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
<Typography variant="h6">RAPTOR策略</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
|
||||
{/* 使用召回增强RAPTOR策略 */}
|
||||
<Box>
|
||||
<Controller
|
||||
name="parser_config.raptor.use_raptor"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={field.value || false}
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
}
|
||||
label="使用召回增强RAPTOR策略"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
<UseRaptorItem />
|
||||
|
||||
{/* 提示词 */}
|
||||
<Box>
|
||||
<Typography variant="subtitle1" gutterBottom>
|
||||
提示词
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_config.raptor.prompt"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
multiline
|
||||
rows={4}
|
||||
fullWidth
|
||||
defaultValue="请总结以下段落。小心数字,不要编造。段落如下:\n{cluster_content}\n以上就是你需要总结的内容。"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
<RaptorPromptItem />
|
||||
|
||||
{/* 最大token数 */}
|
||||
<Box>
|
||||
<Typography variant="subtitle1" gutterBottom>
|
||||
最大token数
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_config.raptor.max_token"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Box sx={{ px: 2 }}>
|
||||
<Slider
|
||||
{...field}
|
||||
min={64}
|
||||
max={512}
|
||||
step={32}
|
||||
valueLabelDisplay="on"
|
||||
defaultValue={256}
|
||||
onChange={(_, value) => field.onChange(value)}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
<RaptorMaxTokenItem />
|
||||
|
||||
{/* 阈值 */}
|
||||
<Box>
|
||||
<Typography variant="subtitle1" gutterBottom>
|
||||
阈值
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_config.raptor.threshold"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Box sx={{ px: 2 }}>
|
||||
<Slider
|
||||
{...field}
|
||||
min={0}
|
||||
max={1}
|
||||
step={0.1}
|
||||
valueLabelDisplay="on"
|
||||
defaultValue={0.1}
|
||||
onChange={(_, value) => field.onChange(value)}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
<RaptorThresholdItem />
|
||||
|
||||
{/* 最大聚类数 */}
|
||||
<Box>
|
||||
<Typography variant="subtitle1" gutterBottom>
|
||||
最大聚类数
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_config.raptor.max_cluster"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Box sx={{ px: 2 }}>
|
||||
<Slider
|
||||
{...field}
|
||||
min={16}
|
||||
max={128}
|
||||
step={16}
|
||||
valueLabelDisplay="on"
|
||||
defaultValue={64}
|
||||
onChange={(_, value) => field.onChange(value)}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
<RaptorMaxClusterItem />
|
||||
|
||||
{/* 随机种子 */}
|
||||
<Box>
|
||||
<Typography variant="subtitle1" gutterBottom>
|
||||
随机种子
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_config.raptor.random_seed"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
|
||||
<TextField
|
||||
{...field}
|
||||
type="number"
|
||||
defaultValue={0}
|
||||
sx={{ flex: 1 }}
|
||||
/>
|
||||
<IconButton>
|
||||
<AddIcon />
|
||||
</IconButton>
|
||||
</Box>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
<RaptorRandomSeedItem />
|
||||
</Box>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
@@ -372,109 +125,19 @@ export function NaiveConfiguration() {
|
||||
<AccordionDetails>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
|
||||
{/* 提取知识图谱 */}
|
||||
<Box>
|
||||
<Controller
|
||||
name="parser_config.graphrag.use_graphrag"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={field.value || false}
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
}
|
||||
label="提取知识图谱"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
<UseGraphragItem />
|
||||
|
||||
{/* 实体类型 */}
|
||||
<Box>
|
||||
<Typography variant="subtitle1" gutterBottom>
|
||||
*实体类型
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_config.graphrag.entity_types"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1, mb: 2 }}>
|
||||
{['organization', 'person', 'geo', 'event', 'category'].map((type) => (
|
||||
<Chip
|
||||
key={type}
|
||||
label={type}
|
||||
onDelete={() => {}}
|
||||
variant="outlined"
|
||||
/>
|
||||
))}
|
||||
<IconButton size="small">
|
||||
<AddIcon />
|
||||
</IconButton>
|
||||
</Box>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
<EntityTypesItem />
|
||||
|
||||
{/* 方法 */}
|
||||
<Box>
|
||||
<Typography variant="subtitle1" gutterBottom>
|
||||
方法
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_config.graphrag.method"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<FormControl fullWidth>
|
||||
<Select
|
||||
{...field}
|
||||
defaultValue="Light"
|
||||
>
|
||||
<MenuItem value="Light">Light</MenuItem>
|
||||
<MenuItem value="General">General</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
<GraphragMethodItem />
|
||||
|
||||
{/* 实体归一化 */}
|
||||
<Box>
|
||||
<Controller
|
||||
name="parser_config.graphrag.entity_normalize"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={field.value || false}
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
}
|
||||
label="实体归一化"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
<EntityNormalizeItem />
|
||||
|
||||
{/* 社区报告生成 */}
|
||||
<Box>
|
||||
<Controller
|
||||
name="parser_config.graphrag.community_report"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={field.value || false}
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
}
|
||||
label="社区报告生成"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
<CommunityReportItem />
|
||||
</Box>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
|
||||
@@ -1,48 +1,67 @@
|
||||
import React from 'react';
|
||||
import { ConfigurationFormContainer } from './configuration-form-container';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
import { ConfigurationFormContainer, MainContainer } from './configuration-form-container';
|
||||
import {
|
||||
ChunkMethodItem,
|
||||
EmbeddingModelItem,
|
||||
ChunkTokenNumberItem,
|
||||
DelimiterItem,
|
||||
LayoutRecognizeItem,
|
||||
PageRankItem,
|
||||
AutoKeywordsItem,
|
||||
AutoQuestionsItem,
|
||||
HtmlForExcelItem,
|
||||
UseRaptorItem,
|
||||
RaptorPromptItem,
|
||||
RaptorMaxTokenItem,
|
||||
RaptorThresholdItem,
|
||||
RaptorMaxClusterItem,
|
||||
RaptorRandomSeedItem,
|
||||
UseGraphragItem,
|
||||
EntityTypesItem,
|
||||
GraphragMethodItem,
|
||||
EntityNormalizeItem,
|
||||
CommunityReportItem,
|
||||
TagsItem
|
||||
} from './common-items';
|
||||
|
||||
export function OneConfiguration() {
|
||||
return (
|
||||
<ConfigurationFormContainer>
|
||||
<ChunkMethodItem />
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
布局识别配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
<EmbeddingModelItem />
|
||||
<MainContainer>
|
||||
<ConfigurationFormContainer>
|
||||
<ChunkMethodItem />
|
||||
<ChunkTokenNumberItem />
|
||||
<DelimiterItem />
|
||||
<LayoutRecognizeItem />
|
||||
<EmbeddingModelItem />
|
||||
</ConfigurationFormContainer>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
PageRank配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
<ConfigurationFormContainer>
|
||||
<PageRankItem />
|
||||
<AutoKeywordsItem />
|
||||
<AutoQuestionsItem />
|
||||
<HtmlForExcelItem />
|
||||
</ConfigurationFormContainer>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
自动关键词配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
<ConfigurationFormContainer>
|
||||
<UseRaptorItem />
|
||||
<RaptorPromptItem />
|
||||
<RaptorMaxTokenItem />
|
||||
<RaptorThresholdItem />
|
||||
<RaptorMaxClusterItem />
|
||||
<RaptorRandomSeedItem />
|
||||
</ConfigurationFormContainer>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
自动问题配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
<ConfigurationFormContainer>
|
||||
<UseGraphragItem />
|
||||
<EntityTypesItem />
|
||||
<GraphragMethodItem />
|
||||
<EntityNormalizeItem />
|
||||
<CommunityReportItem />
|
||||
</ConfigurationFormContainer>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
GraphRAG配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
标签配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
</ConfigurationFormContainer>
|
||||
<ConfigurationFormContainer>
|
||||
<TagsItem />
|
||||
</ConfigurationFormContainer>
|
||||
</MainContainer>
|
||||
);
|
||||
}
|
||||
@@ -1,121 +1,61 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
FormControl,
|
||||
InputLabel,
|
||||
Select,
|
||||
MenuItem,
|
||||
TextField,
|
||||
Slider,
|
||||
} from '@mui/material';
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
import { ConfigurationFormContainer, MainContainer } from './configuration-form-container';
|
||||
import {
|
||||
ChunkMethodItem,
|
||||
EmbeddingModelItem,
|
||||
LayoutRecognizeItem,
|
||||
PageRankItem,
|
||||
AutoKeywordsItem,
|
||||
AutoQuestionsItem,
|
||||
UseRaptorItem,
|
||||
RaptorPromptItem,
|
||||
RaptorMaxTokenItem,
|
||||
RaptorThresholdItem,
|
||||
RaptorMaxClusterItem,
|
||||
RaptorRandomSeedItem,
|
||||
UseGraphragItem,
|
||||
EntityTypesItem,
|
||||
GraphragMethodItem,
|
||||
EntityNormalizeItem,
|
||||
CommunityReportItem,
|
||||
TagsItem
|
||||
} from './common-items';
|
||||
|
||||
export function PaperConfiguration() {
|
||||
const { control } = useFormContext();
|
||||
|
||||
return (
|
||||
<ConfigurationFormContainer>
|
||||
<MainContainer>
|
||||
{/* 布局识别 */}
|
||||
<Box>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
布局识别
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_config.layout_recognize"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<FormControl fullWidth>
|
||||
<InputLabel>布局识别方式</InputLabel>
|
||||
<Select
|
||||
{...field}
|
||||
label="布局识别方式"
|
||||
>
|
||||
<MenuItem value="DeepDOC">DeepDOC</MenuItem>
|
||||
<MenuItem value="Plain Text">Plain Text</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
<MainContainer>
|
||||
<ConfigurationFormContainer>
|
||||
<ChunkMethodItem />
|
||||
<LayoutRecognizeItem />
|
||||
<EmbeddingModelItem />
|
||||
<PageRankItem />
|
||||
</ConfigurationFormContainer>
|
||||
|
||||
{/* 自动关键词 */}
|
||||
<Box>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
自动关键词数量
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_config.auto_keywords"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Box sx={{ px: 2 }}>
|
||||
<Slider
|
||||
{...field}
|
||||
min={0}
|
||||
max={10}
|
||||
step={1}
|
||||
marks
|
||||
valueLabelDisplay="auto"
|
||||
onChange={(_, value) => field.onChange(value)}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
<ConfigurationFormContainer>
|
||||
<AutoKeywordsItem />
|
||||
<AutoQuestionsItem />
|
||||
</ConfigurationFormContainer>
|
||||
|
||||
{/* 自动问题 */}
|
||||
<Box>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
自动问题数量
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_config.auto_questions"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Box sx={{ px: 2 }}>
|
||||
<Slider
|
||||
{...field}
|
||||
min={0}
|
||||
max={10}
|
||||
step={1}
|
||||
marks
|
||||
valueLabelDisplay="auto"
|
||||
onChange={(_, value) => field.onChange(value)}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
<ConfigurationFormContainer>
|
||||
<UseRaptorItem />
|
||||
<RaptorPromptItem />
|
||||
<RaptorMaxTokenItem />
|
||||
<RaptorThresholdItem />
|
||||
<RaptorMaxClusterItem />
|
||||
<RaptorRandomSeedItem />
|
||||
</ConfigurationFormContainer>
|
||||
|
||||
{/* 标签数量 */}
|
||||
<Box>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Top N 标签数量
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_config.topn_tags"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
type="number"
|
||||
fullWidth
|
||||
label="标签数量"
|
||||
inputProps={{ min: 1, max: 10 }}
|
||||
onChange={(e) => field.onChange(parseInt(e.target.value))}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
<ConfigurationFormContainer>
|
||||
<UseGraphragItem />
|
||||
<EntityTypesItem />
|
||||
<GraphragMethodItem />
|
||||
<EntityNormalizeItem />
|
||||
<CommunityReportItem />
|
||||
</ConfigurationFormContainer>
|
||||
|
||||
<Box>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
论文解析器专门优化用于学术论文的解析,能够更好地识别论文的结构和内容。
|
||||
</Typography>
|
||||
</Box>
|
||||
</MainContainer>
|
||||
</ConfigurationFormContainer>
|
||||
<ConfigurationFormContainer>
|
||||
<TagsItem />
|
||||
</ConfigurationFormContainer>
|
||||
</MainContainer>
|
||||
);
|
||||
}
|
||||
@@ -1,37 +1,16 @@
|
||||
import React from 'react';
|
||||
import { ConfigurationFormContainer } from './configuration-form-container';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
import { ChunkMethodItem, EmbeddingModelItem, PageRankItem, AutoKeywordsItem, AutoQuestionsItem, TagsItem } from './common-items';
|
||||
|
||||
export function PictureConfiguration() {
|
||||
return (
|
||||
<ConfigurationFormContainer>
|
||||
<ChunkMethodItem />
|
||||
<EmbeddingModelItem />
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
PageRank配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
自动关键词配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
自动问题配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
标签配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
<PageRankItem />
|
||||
<AutoKeywordsItem />
|
||||
<AutoQuestionsItem />
|
||||
<TagsItem />
|
||||
</ConfigurationFormContainer>
|
||||
);
|
||||
}
|
||||
@@ -1,61 +1,16 @@
|
||||
import React from 'react';
|
||||
import { ConfigurationFormContainer, MainContainer } from './configuration-form-container';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
import { ConfigurationFormContainer } from './configuration-form-container';
|
||||
import { ChunkMethodItem, EmbeddingModelItem, PageRankItem, AutoKeywordsItem, AutoQuestionsItem, TagsItem } from './common-items';
|
||||
|
||||
export function PresentationConfiguration() {
|
||||
return (
|
||||
<MainContainer>
|
||||
<ConfigurationFormContainer>
|
||||
<ChunkMethodItem />
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
布局识别配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
<EmbeddingModelItem />
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
PageRank配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
</ConfigurationFormContainer>
|
||||
|
||||
<ConfigurationFormContainer>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
自动关键词配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
自动问题配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
</ConfigurationFormContainer>
|
||||
|
||||
<ConfigurationFormContainer>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
Raptor配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
</ConfigurationFormContainer>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
GraphRAG配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<ConfigurationFormContainer>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
标签配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
</ConfigurationFormContainer>
|
||||
</MainContainer>
|
||||
<ConfigurationFormContainer>
|
||||
<ChunkMethodItem />
|
||||
<EmbeddingModelItem />
|
||||
<PageRankItem />
|
||||
<AutoKeywordsItem />
|
||||
<AutoQuestionsItem />
|
||||
<TagsItem />
|
||||
</ConfigurationFormContainer>
|
||||
);
|
||||
}
|
||||
@@ -1,45 +1,14 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
TextField,
|
||||
} from '@mui/material';
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
import { ConfigurationFormContainer, MainContainer } from './configuration-form-container';
|
||||
import { ConfigurationFormContainer } from './configuration-form-container';
|
||||
import { ChunkMethodItem, EmbeddingModelItem, PageRankItem, TagsItem } from './common-items';
|
||||
|
||||
export function QAConfiguration() {
|
||||
const { control } = useFormContext();
|
||||
|
||||
return (
|
||||
<ConfigurationFormContainer>
|
||||
<MainContainer>
|
||||
{/* 标签数量 */}
|
||||
<Box>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Top N 标签数量
|
||||
</Typography>
|
||||
<Controller
|
||||
name="parser_config.topn_tags"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
type="number"
|
||||
fullWidth
|
||||
label="标签数量"
|
||||
inputProps={{ min: 1, max: 10 }}
|
||||
onChange={(e) => field.onChange(parseInt(e.target.value))}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Box>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
Q&A解析器专门用于处理问答格式的文档,会自动识别问题和答案的结构。
|
||||
</Typography>
|
||||
</Box>
|
||||
</MainContainer>
|
||||
<ChunkMethodItem />
|
||||
<EmbeddingModelItem />
|
||||
<PageRankItem />
|
||||
<TagsItem />
|
||||
</ConfigurationFormContainer>
|
||||
);
|
||||
}
|
||||
@@ -1,25 +1,16 @@
|
||||
import React from 'react';
|
||||
import { ConfigurationFormContainer } from './configuration-form-container';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
import { ChunkMethodItem, EmbeddingModelItem, PageRankItem, AutoKeywordsItem, AutoQuestionsItem, TagsItem } from './common-items';
|
||||
|
||||
export function ResumeConfiguration() {
|
||||
return (
|
||||
<ConfigurationFormContainer>
|
||||
<ChunkMethodItem />
|
||||
<EmbeddingModelItem />
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
PageRank配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
标签配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
<PageRankItem />
|
||||
<AutoKeywordsItem />
|
||||
<AutoQuestionsItem />
|
||||
<TagsItem />
|
||||
</ConfigurationFormContainer>
|
||||
);
|
||||
}
|
||||
@@ -1,19 +1,16 @@
|
||||
import React from 'react';
|
||||
import { ConfigurationFormContainer } from './configuration-form-container';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
import { ChunkMethodItem, EmbeddingModelItem, PageRankItem, AutoKeywordsItem, AutoQuestionsItem, TagsItem } from './common-items';
|
||||
|
||||
export function TableConfiguration() {
|
||||
return (
|
||||
<ConfigurationFormContainer>
|
||||
<ChunkMethodItem />
|
||||
<EmbeddingModelItem />
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
PageRank配置 - 待实现
|
||||
</Typography>
|
||||
</Box>
|
||||
<PageRankItem />
|
||||
<AutoKeywordsItem />
|
||||
<AutoQuestionsItem />
|
||||
<TagsItem />
|
||||
</ConfigurationFormContainer>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { ConfigurationFormContainer } from './configuration-form-container';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-item';
|
||||
import { ChunkMethodItem, EmbeddingModelItem } from './common-items';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
|
||||
export function TagConfiguration() {
|
||||
|
||||
@@ -71,6 +71,7 @@ function KnowledgeBaseSetting() {
|
||||
permission: knowledge.permission || 'me',
|
||||
avatar: knowledge.avatar,
|
||||
parser_id: knowledge.parser_id || DOCUMENT_PARSER_TYPES.Naive,
|
||||
embd_id: knowledge.embd_id || '',
|
||||
parser_config: {
|
||||
chunk_token_num: knowledge.parser_config?.chunk_token_num || 512,
|
||||
delimiter: knowledge.parser_config?.delimiter || '\n',
|
||||
|
||||
@@ -7,6 +7,7 @@ import Dashboard from '../pages/Dashboard';
|
||||
import ModelsResources from '../pages/ModelsResources';
|
||||
import { KnowledgeBaseList, KnowledgeBaseCreate, KnowledgeBaseDetail, KnowledgeBaseSetting, KnowledgeBaseTesting } from '../pages/knowledge';
|
||||
import MCP from '../pages/MCP';
|
||||
import FormFieldTest from '../pages/FormFieldTest';
|
||||
|
||||
const AppRoutes = () => {
|
||||
return (
|
||||
@@ -31,6 +32,7 @@ const AppRoutes = () => {
|
||||
<Route path="dashboard" element={<Dashboard />} />
|
||||
<Route path="models-resources" element={<ModelsResources />} />
|
||||
<Route path="mcp" element={<MCP />} />
|
||||
<Route path="form-field-test" element={<FormFieldTest />} />
|
||||
</Route>
|
||||
|
||||
{/* 处理未匹配的路由 */}
|
||||
|
||||
Reference in New Issue
Block a user