refactor(knowledge): restructure knowledge creation form and hooks

feat(knowledge): add GeneralForm and ChunkMethodForm components
This commit is contained in:
2025-10-13 18:25:44 +08:00
parent cedb0699f5
commit 34181cf025
9 changed files with 625 additions and 381 deletions

View File

@@ -15,7 +15,7 @@ import AccountCircleIcon from '@mui/icons-material/AccountCircle';
import LogoutIcon from '@mui/icons-material/Logout';
import PersonIcon from '@mui/icons-material/Person';
import LanguageSwitcher from '../LanguageSwitcher';
import { useAuth } from '@/hooks/login_hooks';
import { useAuth } from '@/hooks/login-hooks';
const Header = () => {
const { userInfo, logout } = useAuth();

View File

@@ -103,11 +103,14 @@ const DialogComponent: React.FC<DialogComponentProps> = ({ dialog, onClose }) =>
onClose={handleBackdropClick}
maxWidth="sm"
fullWidth
PaperProps={{
slotProps={{
paper: {
sx: {
width: config.width || 'auto',
minWidth: config.width || '300px',
maxWidth: config.width || '500px',
},
}
}
}}
>
{/* 标题栏 */}

View File

@@ -0,0 +1,294 @@
import React from 'react';
import { type UseFormReturn } from 'react-hook-form';
import {
Box,
Typography,
FormControl,
InputLabel,
Select,
MenuItem,
Grid,
TextField,
FormHelperText,
Button,
CircularProgress,
} from '@mui/material';
import {
Save as SaveIcon,
} from '@mui/icons-material';
import { DOCUMENT_PARSER_TYPES, type DocumentParserType } from '@/constants/knowledge';
// 解析器选项配置
const parserOptions = [
{ value: DOCUMENT_PARSER_TYPES.Naive, label: '通用解析器', description: '适用于大多数文档类型' },
{ value: DOCUMENT_PARSER_TYPES.Qa, label: 'Q&A解析器', description: '适用于问答格式文档' },
{ value: DOCUMENT_PARSER_TYPES.Resume, label: '简历解析器', description: '专门用于解析简历文档' },
{ value: DOCUMENT_PARSER_TYPES.Manual, label: '手册解析器', description: '适用于技术手册和说明书' },
{ value: DOCUMENT_PARSER_TYPES.Table, label: '表格解析器', description: '专门处理表格数据' },
{ value: DOCUMENT_PARSER_TYPES.Paper, label: '论文解析器', description: '适用于学术论文' },
{ value: DOCUMENT_PARSER_TYPES.Book, label: '书籍解析器', description: '适用于书籍和长文档' },
{ value: DOCUMENT_PARSER_TYPES.Laws, label: '法律解析器', description: '专门处理法律文档' },
{ value: DOCUMENT_PARSER_TYPES.Presentation, label: '演示文稿解析器', description: '适用于PPT等演示文档' },
{ value: DOCUMENT_PARSER_TYPES.Picture, label: '图片解析器', description: '处理图片中的文字内容' },
{ value: DOCUMENT_PARSER_TYPES.One, label: '整体解析器', description: '将整个文档作为一个块处理' },
{ value: DOCUMENT_PARSER_TYPES.Audio, label: '音频解析器', description: '处理音频文件转录' },
{ value: DOCUMENT_PARSER_TYPES.Email, label: '邮件解析器', description: '专门处理邮件格式' },
{ value: DOCUMENT_PARSER_TYPES.Tag, label: '标签解析器', description: '基于标签的文档解析' },
{ value: DOCUMENT_PARSER_TYPES.KnowledgeGraph, label: '知识图谱解析器', description: '构建知识图谱结构' },
];
interface ConfigFormData {
parser_id: DocumentParserType;
chunk_token_count?: number;
layout_recognize?: boolean;
task_page_size?: number;
[key: string]: any;
}
interface ChunkMethodFormProps {
form: UseFormReturn<ConfigFormData>;
onSubmit: (data: ConfigFormData) => void;
isSubmitting?: boolean;
onCancel?: () => void;
disabled?: boolean;
submitButtonText?: string;
cancelButtonText?: string;
}
function ChunkMethodForm({
form,
onSubmit,
isSubmitting = false,
onCancel,
disabled = false,
submitButtonText = '提交',
cancelButtonText = '取消'
}: ChunkMethodFormProps) {
const selectedParser: DocumentParserType = form.watch('parser_id');
// 根据选择的解析器显示不同的配置选项
const renderParserSpecificConfig = () => {
switch (selectedParser) {
case DOCUMENT_PARSER_TYPES.Naive:
return (
<Grid container spacing={3}>
<Grid size={{xs:12,sm:6}}>
<TextField
fullWidth
type="number"
label="分块大小"
placeholder="512"
disabled={disabled}
{...form.register('chunk_token_num', {
valueAsNumber: true,
min: {
value: 50,
message: '分块大小不能小于50',
},
max: {
value: 2048,
message: '分块大小不能超过2048',
},
})}
error={!!form.formState.errors.chunk_token_num}
helperText={form.formState.errors.chunk_token_num?.message?.toString() || '设置每个文本块的最大token数量'}
/>
</Grid>
<Grid size={{xs:12,sm:6}}>
<TextField
fullWidth
label="分隔符"
placeholder="\\n"
disabled={disabled}
{...form.register('delimiter')}
helperText="用于分割文本的分隔符"
/>
</Grid>
</Grid>
);
case DOCUMENT_PARSER_TYPES.Table:
return (
<Grid container spacing={3}>
<Grid size={12}>
<Typography variant="body2" color="text.secondary">
</Typography>
</Grid>
</Grid>
);
case DOCUMENT_PARSER_TYPES.KnowledgeGraph:
return (
<Grid container spacing={3}>
<Grid size={12}>
<TextField
fullWidth
label="实体类型"
placeholder="person,organization,location"
disabled={disabled}
{...form.register('entity_types')}
helperText="指定要提取的实体类型,用逗号分隔"
/>
</Grid>
</Grid>
);
case DOCUMENT_PARSER_TYPES.Picture:
return (
<Grid container spacing={3}>
<Grid size={12}>
<TextField
fullWidth
multiline
rows={3}
label="系统提示词"
placeholder="请描述图片中的内容..."
disabled={disabled}
{...form.register('system_prompt')}
helperText="用于指导AI理解图片内容的提示词"
/>
</Grid>
</Grid>
);
default:
return (
<Grid container spacing={3}>
<Grid size={12}>
<Typography variant="body2" color="text.secondary">
使
</Typography>
</Grid>
</Grid>
);
}
};
return (
<Box>
<Typography variant="h6" gutterBottom>
</Typography>
<Grid container spacing={3}>
{/* 解析器选择 */}
<Grid size={12}>
<FormControl fullWidth disabled={disabled}>
<InputLabel></InputLabel>
<Select
label="解析器类型"
defaultValue={DOCUMENT_PARSER_TYPES.Naive}
{...form.register('parser_id')}
value={form.watch('parser_id') || DOCUMENT_PARSER_TYPES.Naive}
>
{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>
<FormHelperText>
</FormHelperText>
</FormControl>
</Grid>
{/* 解析器特定配置 */}
<Grid size={12}>
<Box sx={{ mt: 2 }}>
<Typography variant="subtitle2" gutterBottom>
</Typography>
{renderParserSpecificConfig()}
</Box>
</Grid>
{/* 通用配置 */}
<Grid size={12}>
<Box sx={{ mt: 2 }}>
<Typography variant="subtitle2" gutterBottom>
</Typography>
<Grid container spacing={3}>
<Grid size={{xs:12,sm:6}}>
<TextField
fullWidth
type="number"
label="自动关键词数量"
placeholder="0"
disabled={disabled}
{...form.register('auto_keywords', {
valueAsNumber: true,
min: {
value: 0,
message: '关键词数量不能小于0',
},
max: {
value: 10,
message: '关键词数量不能超过10',
},
})}
error={!!form.formState.errors.auto_keywords}
helperText={form.formState.errors.auto_keywords?.message?.toString() || '自动提取的关键词数量0表示不提取'}
/>
</Grid>
<Grid size={{xs:12,sm:6}}>
<TextField
fullWidth
type="number"
label="自动问题数量"
placeholder="0"
disabled={disabled}
{...form.register('auto_questions', {
valueAsNumber: true,
min: {
value: 0,
message: '问题数量不能小于0',
},
max: {
value: 10,
message: '问题数量不能超过10',
},
})}
error={!!form.formState.errors.auto_questions}
helperText={form.formState.errors.auto_questions?.message?.toString() || '自动生成的问题数量0表示不生成'}
/>
</Grid>
</Grid>
</Box>
</Grid>
{/* 操作按钮 */}
<Grid size={12}>
<Box sx={{ display: 'flex', gap: 2, justifyContent: 'flex-end', mt: 2 }}>
{onCancel && (
<Button
variant="outlined"
onClick={onCancel}
disabled={isSubmitting}
>
{cancelButtonText}
</Button>
)}
<Button
variant="contained"
onClick={form.handleSubmit(onSubmit)}
disabled={disabled || isSubmitting}
startIcon={isSubmitting ? <CircularProgress size={20} /> : <SaveIcon />}
>
{submitButtonText}
</Button>
</Box>
</Grid>
</Grid>
</Box>
);
}
export default ChunkMethodForm;

View File

@@ -0,0 +1,226 @@
import React, { useRef } from 'react';
import { type UseFormReturn } from 'react-hook-form';
import {
Box,
Typography,
TextField,
FormControl,
InputLabel,
Select,
MenuItem,
Grid,
Avatar,
Button,
IconButton,
CircularProgress,
} from '@mui/material';
import {
PhotoCamera as PhotoCameraIcon,
Delete as DeleteIcon,
Save as SaveIcon,
} from '@mui/icons-material';
interface BasicFormData {
name: string;
description: string;
permission: string;
avatar?: string;
}
interface GeneralFormProps {
form: UseFormReturn<BasicFormData>;
onSubmit: (data: BasicFormData) => void;
isSubmitting?: boolean;
onCancel?: () => void;
disabled?: boolean;
submitButtonText?: string;
cancelButtonText?: string;
}
function GeneralForm({
form,
onSubmit,
isSubmitting = false,
onCancel,
disabled = false,
submitButtonText = '提交',
cancelButtonText = '取消'
}: GeneralFormProps) {
const fileInputRef = useRef<HTMLInputElement>(null);
// 处理头像上传
const handleAvatarUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (file) {
// 检查文件类型
if (!file.type.startsWith('image/')) {
alert('请选择图片文件');
return;
}
// 检查文件大小 (限制为 2MB)
if (file.size > 2 * 1024 * 1024) {
alert('图片大小不能超过 2MB');
return;
}
const reader = new FileReader();
reader.onload = (e) => {
const base64String = e.target?.result as string;
form.setValue('avatar', base64String);
};
reader.readAsDataURL(file);
}
};
// 删除头像
const handleAvatarDelete = () => {
form.setValue('avatar', undefined);
if (fileInputRef.current) {
fileInputRef.current.value = '';
}
};
const avatarValue = form.watch('avatar');
return (
<Box>
<Typography variant="h6" gutterBottom>
</Typography>
<Grid container spacing={3}>
{/* 头像上传 */}
<Grid size={12}>
<Typography variant="subtitle2" gutterBottom>
</Typography>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
<Avatar
src={avatarValue}
sx={{ width: 80, height: 80 }}
>
{!avatarValue && form.watch('name')?.charAt(0)?.toUpperCase()}
</Avatar>
<Box>
<input
ref={fileInputRef}
type="file"
accept="image/*"
style={{ display: 'none' }}
onChange={handleAvatarUpload}
disabled={disabled}
/>
<Button
variant="outlined"
startIcon={<PhotoCameraIcon />}
onClick={() => fileInputRef.current?.click()}
disabled={disabled}
size="small"
sx={{ mr: 1 }}
>
</Button>
{avatarValue && (
<IconButton
onClick={handleAvatarDelete}
disabled={disabled}
size="small"
color="error"
>
<DeleteIcon />
</IconButton>
)}
</Box>
</Box>
<Typography variant="caption" color="text.secondary">
PNGJPG 2MB
</Typography>
</Grid>
{/* 知识库名称 */}
<Grid size={12}>
<TextField
fullWidth
label="知识库名称"
placeholder="请输入知识库名称"
disabled={disabled}
{...form.register('name', {
required: '请输入知识库名称',
minLength: {
value: 2,
message: '知识库名称至少需要2个字符',
},
maxLength: {
value: 50,
message: '知识库名称不能超过50个字符',
},
})}
error={!!form.formState.errors.name}
helperText={form.formState.errors.name?.message?.toString() || '请输入知识库名称'}
/>
</Grid>
{/* 描述 */}
<Grid size={12}>
<TextField
fullWidth
multiline
rows={4}
label="描述"
placeholder="请输入知识库描述"
disabled={disabled}
{...form.register('description', {
maxLength: {
value: 500,
message: '描述不能超过500个字符',
},
})}
error={!!form.formState.errors.description}
helperText={form.formState.errors.description?.message?.toString() || '请输入知识库描述'}
/>
</Grid>
{/* 权限设置 */}
<Grid size={{xs:12,sm:6}}>
<FormControl fullWidth disabled={disabled}>
<InputLabel></InputLabel>
<Select
label="权限设置"
{...form.register('permission')}
value={form.watch('permission') || 'me'}
>
<MenuItem value="me"></MenuItem>
<MenuItem value="team"></MenuItem>
</Select>
</FormControl>
</Grid>
{/* 操作按钮 */}
<Grid size={12}>
<Box sx={{ display: 'flex', gap: 2, justifyContent: 'flex-end', mt: 2 }}>
{onCancel && (
<Button
variant="outlined"
onClick={onCancel}
disabled={isSubmitting}
>
{cancelButtonText}
</Button>
)}
<Button
variant="contained"
onClick={form.handleSubmit(onSubmit)}
disabled={disabled || isSubmitting}
startIcon={isSubmitting ? <CircularProgress size={20} /> : <SaveIcon />}
>
{submitButtonText}
</Button>
</Box>
</Grid>
</Grid>
</Box>
);
}
export default GeneralForm;

View File

@@ -1,38 +1,28 @@
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useForm } from 'react-hook-form';
import { useForm, type UseFormReturn } from 'react-hook-form';
import {
Box,
Typography,
Card,
CardContent,
TextField,
Button,
FormControl,
InputLabel,
Select,
MenuItem,
Alert,
Grid,
Divider,
CircularProgress,
Stepper,
Step,
StepLabel,
Switch,
FormControlLabel,
Slider,
Chip,
Stack,
} from '@mui/material';
import {
ArrowBack as ArrowBackIcon,
Save as SaveIcon,
Settings as SettingsIcon,
CheckCircle as CheckCircleIcon,
SkipNext as SkipNextIcon,
} from '@mui/icons-material';
import knowledgeService from '@/services/knowledge_service';
import { useKnowledgeOperations } from '@/hooks/knowledge-hooks';
import GeneralForm from './components/GeneralForm';
import ChunkMethodForm from './components/ChunkMethodForm';
// 基础信息表单数据
interface BasicFormData {
@@ -42,21 +32,21 @@ interface BasicFormData {
avatar?: string;
}
// 配置设置表单数据
// 配置表单数据
interface ConfigFormData {
parser_id: string;
embd_id: string;
chunk_token_num: number;
layout_recognize: string;
delimiter: string;
auto_keywords: number;
auto_questions: number;
html4excel: boolean;
topn_tags: number;
use_raptor: boolean;
use_graphrag: boolean;
graphrag_method: string;
pagerank: number;
embd_id?: string;
chunk_token_num?: number;
layout_recognize?: string;
delimiter?: string;
auto_keywords?: number;
auto_questions?: number;
html4excel?: boolean;
topn_tags?: number;
use_raptor?: boolean;
use_graphrag?: boolean;
graphrag_method?: string;
pagerank?: number;
}
const steps = ['基础信息', '配置设置'];
@@ -64,10 +54,16 @@ const steps = ['基础信息', '配置设置'];
function KnowledgeBaseCreate() {
const navigate = useNavigate();
const [activeStep, setActiveStep] = useState(0);
const [isSubmitting, setIsSubmitting] = useState(false);
const [error, setError] = useState<string>('');
const [success, setSuccess] = useState<string>('');
const [createdKbId, setCreatedKbId] = useState<string>('');
const [createdKbId, setCreatedKbId] = useState<string | null>(null);
// 使用知识库操作 hooks
const {
loading: isSubmitting,
error,
createKnowledge,
updateKnowledgeModelConfig,
clearError
} = useKnowledgeOperations();
// 基础信息表单
const basicForm = useForm<BasicFormData>({
@@ -75,145 +71,80 @@ function KnowledgeBaseCreate() {
name: '',
description: '',
permission: 'me',
avatar: undefined,
avatar: '',
},
});
// 配置设置表单
// 配置表单
const configForm = useForm<ConfigFormData>({
defaultValues: {
parser_id: 'naive',
embd_id: 'text-embedding-v3@Tongyi-Qianwen',
chunk_token_num: 512,
layout_recognize: 'DeepDOC',
delimiter: '\n',
auto_keywords: 0,
auto_questions: 0,
delimiter: '\n!?。;!?',
auto_keywords: 5,
auto_questions: 3,
html4excel: false,
topn_tags: 3,
topn_tags: 10,
use_raptor: false,
use_graphrag: false,
graphrag_method: 'light',
pagerank: 0,
pagerank: 0.3,
},
});
// 第一步:创建基础知识库
// 处理基础信息提交
const handleBasicSubmit = async (data: BasicFormData) => {
setIsSubmitting(true);
setError('');
setSuccess('');
clearError();
try {
// 只发送基础字段到 create API
const basicData = {
name: data.name,
avatar: data.avatar,
description: data.description,
permission: data.permission,
};
const response = await knowledgeService.createKnowledge(basicData);
// 假设 API 返回包含 kb_id 的响应
const kbId = response.data?.kb_id;
setCreatedKbId(kbId);
setSuccess('知识库创建成功!您可以继续配置解析设置,或直接跳过。');
setActiveStep(1); // 进入第二步
} catch (err: any) {
const result = await createKnowledge(data);
setCreatedKbId(result.kb_id);
setActiveStep(1);
} catch (err) {
console.error('创建知识库失败:', err);
setError(err.response?.data?.message || err.message || '创建知识库失败,请重试');
} finally {
setIsSubmitting(false);
}
};
// 第二步:更新配置设置
// 处理配置提交
const handleConfigSubmit = async (data: ConfigFormData) => {
if (!createdKbId) {
setError('未找到知识库ID请重新创建');
return;
}
if (!createdKbId) return;
setIsSubmitting(true);
setError('');
clearError();
try {
// 构建 update API 的数据结构
const updateData:any = {
kb_id: createdKbId,
name: basicForm.getValues('name'),
description: basicForm.getValues('description'),
permission: basicForm.getValues('permission'),
parser_id: data.parser_id,
embd_id: data.embd_id,
parser_config: {
layout_recognize: data.layout_recognize,
chunk_token_num: data.chunk_token_num,
delimiter: data.delimiter,
auto_keywords: data.auto_keywords,
auto_questions: data.auto_questions,
html4excel: data.html4excel,
topn_tags: data.topn_tags,
raptor: {
use_raptor: data.use_raptor,
},
graphrag: {
use_graphrag: data.use_graphrag,
entity_types: ["organization", "person", "geo", "event", "category"],
method: data.graphrag_method,
},
},
pagerank: data.pagerank,
};
await knowledgeService.updateKnowledge(updateData);
setSuccess('知识库配置更新成功!');
// 延迟跳转到知识库列表页面
setTimeout(() => {
await updateKnowledgeModelConfig({
id: createdKbId,
...data,
});
navigate('/knowledge');
}, 1500);
} catch (err: any) {
console.error('更新知识库配置失败:', err);
setError(err.response?.data?.message || err.message || '更新配置失败,请重试');
} finally {
setIsSubmitting(false);
} catch (err) {
console.error('配置知识库失败:', err);
}
};
// 跳过配置设置
// 跳过配置
const handleSkipConfig = () => {
setSuccess('知识库创建完成!');
setTimeout(() => {
navigate('/knowledge');
}, 1000);
};
// 返回上一步
const handleBack = () => {
if (activeStep === 0) {
navigate('/knowledge');
} else {
setActiveStep(0);
}
};
return (
<Box sx={{ p: 3, maxWidth: 900, mx: 'auto' }}>
<Box sx={{ maxWidth: 800, mx: 'auto', p: 3 }}>
{/* 页面标题 */}
<Box sx={{ display: 'flex', alignItems: 'center', mb: 3 }}>
<Button
startIcon={<ArrowBackIcon />}
onClick={handleBack}
onClick={() => navigate('/knowledge')}
sx={{ mr: 2 }}
>
{activeStep === 0 ? '返回' : '上一步'}
</Button>
<Typography variant="h4" component="h1" fontWeight={600}>
<Typography variant="h4" component="h1">
</Typography>
</Box>
@@ -227,114 +158,30 @@ function KnowledgeBaseCreate() {
))}
</Stepper>
{/* 表单卡片 */}
<Card>
<CardContent sx={{ p: 4 }}>
{/* 错误和成功提示 */}
{/* 错误提示 */}
{error && (
<Alert severity="error" sx={{ mb: 3 }}>
{error}
</Alert>
)}
{success && (
<Alert severity="success" sx={{ mb: 3 }}>
{success}
</Alert>
)}
<Card>
<CardContent>
{/* 第一步:基础信息 */}
{activeStep === 0 && (
<Box component="form" onSubmit={basicForm.handleSubmit(handleBasicSubmit)} noValidate>
<Typography variant="h6" gutterBottom>
</Typography>
<Divider sx={{ mb: 3 }} />
<Grid container spacing={3}>
{/* 知识库名称 */}
<Grid size={12}>
<TextField
fullWidth
label="知识库名称"
placeholder="请输入知识库名称"
{...basicForm.register('name', {
required: '请输入知识库名称',
minLength: {
value: 2,
message: '知识库名称至少需要2个字符',
},
maxLength: {
value: 50,
message: '知识库名称不能超过50个字符',
},
})}
error={!!basicForm.formState.errors.name}
helperText={basicForm.formState.errors.name?.message}
<GeneralForm
form={basicForm}
onSubmit={handleBasicSubmit}
isSubmitting={isSubmitting}
onCancel={() => navigate('/knowledge')}
submitButtonText="下一步"
cancelButtonText="取消"
/>
</Grid>
{/* 描述 */}
<Grid size={12}>
<TextField
fullWidth
multiline
rows={4}
label="描述"
placeholder="请输入知识库描述"
{...basicForm.register('description', {
maxLength: {
value: 500,
message: '描述不能超过500个字符',
},
})}
error={!!basicForm.formState.errors.description}
helperText={basicForm.formState.errors.description?.message}
/>
</Grid>
{/* 权限设置 */}
<Grid size={{xs:12, sm:6}}>
<FormControl fullWidth>
<InputLabel></InputLabel>
<Select
label="权限设置"
{...basicForm.register('permission')}
>
<MenuItem value="me"></MenuItem>
<MenuItem value="team"></MenuItem>
<MenuItem value="public"></MenuItem>
</Select>
</FormControl>
</Grid>
{/* 提交按钮 */}
<Grid size={{xs:12}}>
<Box sx={{ display: 'flex', gap: 2, justifyContent: 'flex-end', mt: 3 }}>
<Button
variant="outlined"
onClick={() => navigate('/knowledge')}
disabled={isSubmitting}
>
</Button>
<Button
type="submit"
variant="contained"
startIcon={isSubmitting ? <CircularProgress size={20} /> : <SaveIcon />}
disabled={isSubmitting}
>
{isSubmitting ? '创建中...' : '创建知识库'}
</Button>
</Box>
</Grid>
</Grid>
</Box>
)}
{/* 第二步:配置设置 */}
{activeStep === 1 && (
<Box component="form" onSubmit={configForm.handleSubmit(handleConfigSubmit)} noValidate>
<Box>
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
<CheckCircleIcon color="success" sx={{ mr: 1 }} />
<Typography variant="h6">
@@ -346,152 +193,26 @@ function KnowledgeBaseCreate() {
</Typography>
<Divider sx={{ mb: 3 }} />
<Grid container spacing={3}>
{/* 解析器设置 */}
<Grid size={{xs:12}}>
<Typography variant="subtitle1" gutterBottom>
</Typography>
</Grid>
<Grid size={{xs:12,sm:6}}>
<FormControl fullWidth>
<InputLabel></InputLabel>
<Select
label="解析器类型"
{...configForm.register('parser_id')}
>
<MenuItem value="naive"></MenuItem>
<MenuItem value="advanced"></MenuItem>
<MenuItem value="custom"></MenuItem>
</Select>
</FormControl>
</Grid>
<Grid size={{xs:12,sm:6}}>
<FormControl fullWidth>
<InputLabel></InputLabel>
<Select
label="嵌入模型"
{...configForm.register('embd_id')}
>
<MenuItem value="text-embedding-v3@Tongyi-Qianwen"> v3</MenuItem>
<MenuItem value="text-embedding-v2@Tongyi-Qianwen"> v2</MenuItem>
<MenuItem value="openai-embedding">OpenAI Embedding</MenuItem>
</Select>
</FormControl>
</Grid>
{/* 分块设置 */}
<Grid size={{xs:12}}>
<Typography variant="subtitle1" gutterBottom sx={{ mt: 2 }}>
</Typography>
</Grid>
<Grid size={{xs:12,sm:6}}>
<Typography gutterBottom>: {configForm.watch('chunk_token_num')}</Typography>
<Slider
{...configForm.register('chunk_token_num')}
value={configForm.watch('chunk_token_num')}
onChange={(_, value) => configForm.setValue('chunk_token_num', value as number)}
min={128}
max={2048}
step={128}
marks={[
{ value: 128, label: '128' },
{ value: 512, label: '512' },
{ value: 1024, label: '1024' },
{ value: 2048, label: '2048' },
]}
<ChunkMethodForm
form={configForm as any}
onSubmit={(data) => handleConfigSubmit(data as any)}
isSubmitting={isSubmitting}
onCancel={handleBack}
submitButtonText="完成配置"
cancelButtonText="返回上一步"
/>
</Grid>
<Grid size={{xs:12,sm:6}}>
<FormControl fullWidth>
<InputLabel></InputLabel>
<Select
label="布局识别"
{...configForm.register('layout_recognize')}
>
<MenuItem value="DeepDOC">DeepDOC</MenuItem>
<MenuItem value="OCR">OCR</MenuItem>
<MenuItem value="None"></MenuItem>
</Select>
</FormControl>
</Grid>
{/* 高级功能 */}
<Grid size={{xs:12}}>
<Typography variant="subtitle1" gutterBottom sx={{ mt: 2 }}>
</Typography>
</Grid>
<Grid size={{xs:12,sm:6}}>
<FormControlLabel
control={
<Switch
{...configForm.register('use_raptor')}
checked={configForm.watch('use_raptor')}
onChange={(e) => configForm.setValue('use_raptor', e.target.checked)}
/>
}
label="启用 Raptor"
/>
</Grid>
<Grid size={{xs:12,sm:6}}>
<FormControlLabel
control={
<Switch
{...configForm.register('use_graphrag')}
checked={configForm.watch('use_graphrag')}
onChange={(e) => configForm.setValue('use_graphrag', e.target.checked)}
/>
}
label="启用 GraphRAG"
/>
</Grid>
{configForm.watch('use_graphrag') && (
<Grid size={{xs:12,sm:6}}>
<FormControl fullWidth>
<InputLabel>GraphRAG </InputLabel>
<Select
label="GraphRAG 方法"
{...configForm.register('graphrag_method')}
>
<MenuItem value="light"></MenuItem>
<MenuItem value="standard"></MenuItem>
<MenuItem value="advanced"></MenuItem>
</Select>
</FormControl>
</Grid>
)}
{/* 操作按钮 */}
<Grid size={{xs:12}}>
<Box sx={{ display: 'flex', gap: 2, justifyContent: 'flex-end', mt: 3 }}>
{/* 跳过配置按钮 */}
<Box sx={{ display: 'flex', justifyContent: 'center', mt: 2 }}>
<Button
variant="outlined"
variant="text"
startIcon={<SkipNextIcon />}
onClick={handleSkipConfig}
disabled={isSubmitting}
>
</Button>
<Button
type="submit"
variant="contained"
startIcon={isSubmitting ? <CircularProgress size={20} /> : <SettingsIcon />}
disabled={isSubmitting}
>
{isSubmitting ? '配置中...' : '完成配置'}
</Button>
</Box>
</Grid>
</Grid>
</Box>
)}
</CardContent>

View File

@@ -19,7 +19,7 @@ import {
Refresh as RefreshIcon,
} from '@mui/icons-material';
import { useNavigate } from 'react-router-dom';
import { useKnowledgeList, useKnowledgeOperations } from '@/hooks/knowledge_hooks';
import { useKnowledgeList, useKnowledgeOperations } from '@/hooks/knowledge-hooks';
import { useUserData } from '@/hooks/useUserData';
import KnowledgeGridView from '@/components/knowledge/KnowledgeGridView';
import type { IKnowledge } from '@/interfaces/database/knowledge';

View File

@@ -17,7 +17,7 @@ import {
Tab
} from '@mui/material';
import LanguageSwitcher from '../../components/LanguageSwitcher';
import { useLoginPage } from '../../hooks/login_hooks';
import { useLoginPage } from '../../hooks/login-hooks';
const Login = () => {
const { t } = useTranslation();