Files
TERES_web_frontend/src/pages/knowledge/components/GeneralForm.tsx

219 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 { useFormContext, Controller, type UseFormReturn } from 'react-hook-form';
import {
Box,
Typography,
TextField,
FormControl,
InputLabel,
Select,
MenuItem,
Grid,
Avatar,
Button,
IconButton,
} from '@mui/material';
import {
PhotoCamera as PhotoCameraIcon,
Delete as DeleteIcon,
} from '@mui/icons-material';
import { useTranslation } from 'react-i18next';
interface GeneralFormProps {
form?: UseFormReturn;
onSubmit?: (data: any) => void;
isSubmitting?: boolean;
onCancel?: () => void;
submitButtonText?: string;
cancelButtonText?: string;
}
function GeneralForm({
form: propForm,
onSubmit,
isSubmitting,
onCancel,
submitButtonText,
cancelButtonText,
}: GeneralFormProps = {}) {
const { t } = useTranslation();
const defaultSubmitButtonText = submitButtonText || t('common.save');
const defaultCancelButtonText = cancelButtonText || t('common.cancel');
// 优先使用props传递的form否则使用FormProvider的context
let contextForm: UseFormReturn | null = null;
try {
contextForm = useFormContext();
} catch (error) {
contextForm = null;
}
const form = propForm || contextForm;
if (!form) {
console.error('GeneralForm: No form context found. Component must be used within a FormProvider or receive a form prop.');
return (
<Box sx={{ p: 2, textAlign: 'center' }}>
<Typography color="error">
{t('form.configurationError')}
</Typography>
</Box>
);
}
const { control, watch, setValue, handleSubmit } = form;
const fileInputRef = useRef<HTMLInputElement>(null);
const handleAvatarUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (file && form) {
const reader = new FileReader();
reader.onload = (e) => {
form.setValue('avatar', e.target?.result as string);
};
reader.readAsDataURL(file);
}
};
const handleAvatarDelete = () => {
if (form) {
form.setValue('avatar', undefined);
}
};
const handleAvatarClick = () => {
fileInputRef.current?.click();
};
const avatar = watch('avatar', '');
return (
<Box sx={{ p: 3 }}>
<Typography variant="h6" gutterBottom>
{t('knowledge.basicInfo')}
</Typography>
<Grid container spacing={3}>
<Grid size={{xs:12, md:6}}>
<Box sx={{ display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 2 }}>
<Avatar
src={avatar}
sx={{ width: 120, height: 120, cursor: 'pointer' }}
onClick={handleAvatarClick}
>
{!avatar && <PhotoCameraIcon sx={{ fontSize: 40 }} />}
</Avatar>
<Box sx={{ display: 'flex', gap: 1 }}>
<Button
variant="outlined"
size="small"
startIcon={<PhotoCameraIcon />}
onClick={handleAvatarClick}
>
{t('knowledge.uploadAvatar')}
</Button>
{avatar && (
<IconButton
size="small"
color="error"
onClick={handleAvatarDelete}
>
<DeleteIcon />
</IconButton>
)}
</Box>
<input
ref={fileInputRef}
type="file"
accept="image/*"
style={{ display: 'none' }}
onChange={handleAvatarUpload}
/>
</Box>
</Grid>
{/* 表单字段 */}
<Grid size={{xs:12,md:8}}>
<Grid container spacing={2}>
<Grid size={{xs:12}}>
<Controller
name="name"
control={control}
rules={{ required: t('knowledge.nameRequired') }}
render={({ field, fieldState: { error } }) => (
<TextField
{...field}
label={t('knowledge.knowledgeBaseName')}
fullWidth
required
error={!!error}
helperText={error?.message}
/>
)}
/>
</Grid>
<Grid size={{xs:12}}>
<Controller
name="description"
control={control}
render={({ field }) => (
<TextField
{...field}
label={t('common.description')}
fullWidth
multiline
rows={3}
placeholder={t('knowledge.descriptionPlaceholder')}
/>
)}
/>
</Grid>
<Grid size={{xs:12}}>
<Controller
name="permission"
control={control}
render={({ field }) => (
<FormControl fullWidth>
<InputLabel>{t('knowledge.permissionSettings')}</InputLabel>
<Select {...field} label={t('knowledge.permissionSettings')}>
<MenuItem value="me">{t('knowledge.onlyMe')}</MenuItem>
<MenuItem value="team">{t('knowledge.teamMembers')}</MenuItem>
</Select>
</FormControl>
)}
/>
</Grid>
</Grid>
</Grid>
</Grid>
{/* 表单操作按钮 - 仅在有onSubmit回调时显示 */}
{onSubmit && (
<Box sx={{ mt: 4, display: 'flex', justifyContent: 'flex-end', gap: 2 }}>
{onCancel && (
<Button
variant="outlined"
onClick={onCancel}
disabled={isSubmitting}
>
{defaultCancelButtonText}
</Button>
)}
<Button
variant="contained"
onClick={form ? form.handleSubmit(onSubmit) : undefined}
disabled={isSubmitting || !form}
>
{isSubmitting ? t('common.submitting') : defaultSubmitButtonText}
</Button>
</Box>
)}
</Box>
);
}
export default GeneralForm;