Files
TERES_web_frontend/src/pages/knowledge/components/GeneralForm.tsx
guangfei.zhao 9f6785672f feat(knowledge): restructure knowledge base pages and components
- Implement new setting and testing pages with breadcrumbs
2025-10-14 18:06:12 +08:00

226 lines
6.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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';
export 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;