diff --git a/src/components/Layout/Header.tsx b/src/components/Layout/Header.tsx index 76b7629..b555a63 100644 --- a/src/components/Layout/Header.tsx +++ b/src/components/Layout/Header.tsx @@ -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(); diff --git a/src/components/Provider/DialogComponent.tsx b/src/components/Provider/DialogComponent.tsx index 24d0cac..f517bbe 100644 --- a/src/components/Provider/DialogComponent.tsx +++ b/src/components/Provider/DialogComponent.tsx @@ -103,11 +103,14 @@ const DialogComponent: React.FC = ({ dialog, onClose }) => onClose={handleBackdropClick} maxWidth="sm" fullWidth - PaperProps={{ - sx: { - width: config.width || 'auto', - maxWidth: config.width || '500px', - }, + slotProps={{ + paper: { + sx: { + width: config.width || 'auto', + minWidth: config.width || '300px', + maxWidth: config.width || '500px', + } + } }} > {/* 标题栏 */} diff --git a/src/hooks/knowledge_hooks.ts b/src/hooks/knowledge-hooks.ts similarity index 100% rename from src/hooks/knowledge_hooks.ts rename to src/hooks/knowledge-hooks.ts diff --git a/src/hooks/login_hooks.ts b/src/hooks/login-hooks.ts similarity index 100% rename from src/hooks/login_hooks.ts rename to src/hooks/login-hooks.ts diff --git a/src/pages/knowledge/components/ChunkMethodForm.tsx b/src/pages/knowledge/components/ChunkMethodForm.tsx new file mode 100644 index 0000000..ae55be0 --- /dev/null +++ b/src/pages/knowledge/components/ChunkMethodForm.tsx @@ -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; + 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 ( + + + + + + + + + ); + + case DOCUMENT_PARSER_TYPES.Table: + return ( + + + + 表格解析器会自动处理表格结构,无需额外配置。 + + + + ); + + case DOCUMENT_PARSER_TYPES.KnowledgeGraph: + return ( + + + + + + ); + + case DOCUMENT_PARSER_TYPES.Picture: + return ( + + + + + + ); + + default: + return ( + + + + 该解析器使用默认配置,无需额外设置。 + + + + ); + } + }; + + return ( + + + 解析配置 + + + + {/* 解析器选择 */} + + + 解析器类型 + + + 选择适合您文档类型的解析器 + + + + + {/* 解析器特定配置 */} + + + + 解析器配置 + + {renderParserSpecificConfig()} + + + + {/* 通用配置 */} + + + + 通用配置 + + + + + + + + + + + + + {/* 操作按钮 */} + + + {onCancel && ( + + )} + + + + + + ); +} + +export default ChunkMethodForm; diff --git a/src/pages/knowledge/components/GeneralForm.tsx b/src/pages/knowledge/components/GeneralForm.tsx new file mode 100644 index 0000000..4eff388 --- /dev/null +++ b/src/pages/knowledge/components/GeneralForm.tsx @@ -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; + 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(null); + + // 处理头像上传 + const handleAvatarUpload = (event: React.ChangeEvent) => { + 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 ( + + + 基础信息 + + + + {/* 头像上传 */} + + + 头像 + + + + {!avatarValue && form.watch('name')?.charAt(0)?.toUpperCase()} + + + + + {avatarValue && ( + + + + )} + + + + 支持 PNG、JPG 格式,文件大小不超过 2MB + + + + {/* 知识库名称 */} + + + + + {/* 描述 */} + + + + + {/* 权限设置 */} + + + 权限设置 + + + + + {/* 操作按钮 */} + + + {onCancel && ( + + )} + + + + + + ); +} + +export default GeneralForm; \ No newline at end of file diff --git a/src/pages/knowledge/create.tsx b/src/pages/knowledge/create.tsx index b3aeea7..fe39e9b 100644 --- a/src/pages/knowledge/create.tsx +++ b/src/pages/knowledge/create.tsx @@ -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(''); - const [success, setSuccess] = useState(''); - const [createdKbId, setCreatedKbId] = useState(''); + const [createdKbId, setCreatedKbId] = useState(null); + + // 使用知识库操作 hooks + const { + loading: isSubmitting, + error, + createKnowledge, + updateKnowledgeModelConfig, + clearError + } = useKnowledgeOperations(); // 基础信息表单 const basicForm = useForm({ @@ -75,145 +71,80 @@ function KnowledgeBaseCreate() { name: '', description: '', permission: 'me', - avatar: undefined, + avatar: '', }, }); - // 配置设置表单 + // 配置表单 const configForm = useForm({ 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(() => { - navigate('/knowledge'); - }, 1500); - - } catch (err: any) { - console.error('更新知识库配置失败:', err); - setError(err.response?.data?.message || err.message || '更新配置失败,请重试'); - } finally { - setIsSubmitting(false); + await updateKnowledgeModelConfig({ + id: createdKbId, + ...data, + }); + navigate('/knowledge'); + } catch (err) { + console.error('配置知识库失败:', err); } }; - - // 跳过配置设置 + // 跳过配置 const handleSkipConfig = () => { - setSuccess('知识库创建完成!'); - setTimeout(() => { - navigate('/knowledge'); - }, 1000); + navigate('/knowledge'); }; + // 返回上一步 const handleBack = () => { - if (activeStep === 0) { - navigate('/knowledge'); - } else { - setActiveStep(0); - } + setActiveStep(0); }; return ( - + {/* 页面标题 */} - + 创建知识库 @@ -227,114 +158,30 @@ function KnowledgeBaseCreate() { ))} - {/* 表单卡片 */} - - - {/* 错误和成功提示 */} - {error && ( - - {error} - - )} - - {success && ( - - {success} - - )} + {/* 错误提示 */} + {error && ( + + {error} + + )} + + {/* 第一步:基础信息 */} {activeStep === 0 && ( - - - 基础信息 - - - - - {/* 知识库名称 */} - - - - - {/* 描述 */} - - - - - {/* 权限设置 */} - - - 权限设置 - - - - - {/* 提交按钮 */} - - - - - - - - + navigate('/knowledge')} + submitButtonText="下一步" + cancelButtonText="取消" + /> )} {/* 第二步:配置设置 */} {activeStep === 1 && ( - + @@ -346,152 +193,26 @@ function KnowledgeBaseCreate() { - - {/* 解析器设置 */} - - - 解析器设置 - - + handleConfigSubmit(data as any)} + isSubmitting={isSubmitting} + onCancel={handleBack} + submitButtonText="完成配置" + cancelButtonText="返回上一步" + /> - - - 解析器类型 - - - - - - - 嵌入模型 - - - - - {/* 分块设置 */} - - - 分块设置 - - - - - 分块大小: {configForm.watch('chunk_token_num')} - 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' }, - ]} - /> - - - - - 布局识别 - - - - - {/* 高级功能 */} - - - 高级功能 - - - - - configForm.setValue('use_raptor', e.target.checked)} - /> - } - label="启用 Raptor" - /> - - - - configForm.setValue('use_graphrag', e.target.checked)} - /> - } - label="启用 GraphRAG" - /> - - - {configForm.watch('use_graphrag') && ( - - - GraphRAG 方法 - - - - )} - - {/* 操作按钮 */} - - - - - - - + {/* 跳过配置按钮 */} + + + )} diff --git a/src/pages/knowledge/list.tsx b/src/pages/knowledge/list.tsx index ede1770..9f0d7bb 100644 --- a/src/pages/knowledge/list.tsx +++ b/src/pages/knowledge/list.tsx @@ -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'; diff --git a/src/pages/login/Login.tsx b/src/pages/login/Login.tsx index 0f6be04..2126bc7 100644 --- a/src/pages/login/Login.tsx +++ b/src/pages/login/Login.tsx @@ -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();