From bb45d484db8daa9b6633c892e110a8dc8844d6e8 Mon Sep 17 00:00:00 2001 From: vincentlu <t1341870251@gmail.com> Date: 星期一, 10 二月 2025 15:16:35 +0800 Subject: [PATCH] # --- rsf-admin/src/page/tenant/TenantCreate.jsx | 400 ++++++++++++++++++++++++++++++++++++++++++++++---------- 1 files changed, 327 insertions(+), 73 deletions(-) diff --git a/rsf-admin/src/page/tenant/TenantCreate.jsx b/rsf-admin/src/page/tenant/TenantCreate.jsx index 37e3d05..4e0dea7 100644 --- a/rsf-admin/src/page/tenant/TenantCreate.jsx +++ b/rsf-admin/src/page/tenant/TenantCreate.jsx @@ -23,100 +23,354 @@ Stack, Grid, Box, + Stepper, + Step, + StepLabel, + StepContent, + Button, + TextField, + InputAdornment, + IconButton, } from '@mui/material'; +import { useForm, Controller, useWatch, FormProvider, useFormContext } from "react-hook-form"; +import { matchPath, useLocation } from 'react-router'; import DialogCloseButton from "../components/DialogCloseButton"; import StatusSelectInput from "../components/StatusSelectInput"; import MemoInput from "../components/MemoInput"; +import Visibility from '@mui/icons-material/Visibility'; +import VisibilityOff from '@mui/icons-material/VisibilityOff'; const TenantCreate = (props) => { const { open, setOpen } = props; - const translate = useTranslate(); const notify = useNotify(); + const { + control, + handleSubmit, + watch, + setValue, + getValues, + reset, + formState: { + errors, + isDirty, + }, + trigger + } = useForm(); + + const passwordVal = watch('password'); + + const [activeStep, setActiveStep] = useState(0); + const [showPassword, setShowPassword] = useState(false); + + const validateCurrentStep = async () => { + let fieldsToValidate = []; + if (activeStep === 0) { + fieldsToValidate = ['name', 'flag']; + } else if (activeStep === 1) { + fieldsToValidate = ['username', 'email', 'password', 'confirmPassword']; + } else if (activeStep === 2) { + fieldsToValidate = ['memo']; + } + return await trigger(fieldsToValidate); + }; + + const handleNext = async () => { + const isValid = await validateCurrentStep(); + if (!isValid) { + return; + } + setActiveStep(prev => prev + 1); + }; + + const handleBack = () => { + setActiveStep(prev => prev - 1); + }; const handleClose = (event, reason) => { if (reason !== "backdropClick") { + setActiveStep(0); setOpen(false); + reset(); } }; - const handleSuccess = async (data) => { - setOpen(false); - notify('common.response.success'); - }; + const onSubmit = (data) => { + console.log(data); + return; - const handleError = async (error) => { - notify(error.message || 'common.response.fail', { type: 'error', messageArgs: { _: error.message } }); - }; + // setOpen(false); + reset(); + // notify('common.response.success'); + + // notify(error.message || 'common.response.fail', { type: 'error', messageArgs: { _: error.message } }); + } return ( <> - <CreateBase - record={{}} - transform={(data) => { - return data; - }} - mutationOptions={{ onSuccess: handleSuccess, onError: handleError }} + <Dialog + open={open} + onClose={handleClose} + aria-labelledby="form-dialog-title" + fullWidth + disableRestoreFocus + maxWidth="md" // 'xs' | 'sm' | 'md' | 'lg' | 'xl' > - <Dialog - open={open} - onClose={handleClose} - aria-labelledby="form-dialog-title" - fullWidth - disableRestoreFocus - maxWidth="md" // 'xs' | 'sm' | 'md' | 'lg' | 'xl' - > - <Form> - <DialogTitle id="form-dialog-title" sx={{ - position: 'sticky', - top: 0, - backgroundColor: 'background.paper', - zIndex: 1000 - }} - > - {translate('create.title')} - <Box sx={{ position: 'absolute', top: 8, right: 8, zIndex: 1001 }}> - <DialogCloseButton onClose={handleClose} /> - </Box> - </DialogTitle> - <DialogContent> - <Grid container rowSpacing={2} columnSpacing={2}> - <Grid item xs={6} display="flex" gap={1}> - <TextInput - label="table.field.tenant.name" - source="name" - parse={v => v} - autoFocus - validate={required()} - /> - </Grid> - <Grid item xs={6} display="flex" gap={1}> - <TextInput - label="table.field.tenant.flag" - source="flag" - parse={v => v} - autoFocus - validate={required()} - /> - </Grid> - <Grid item xs={6} display="flex" gap={1}> - <StatusSelectInput /> - </Grid> - <Grid item xs={12} display="flex" gap={1}> - <Stack direction="column" spacing={1} width={'100%'}> - <MemoInput /> - </Stack> - </Grid> - </Grid> - </DialogContent> - <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}> - <Toolbar sx={{ width: '100%', justifyContent: 'space-between' }} > - <SaveButton /> - </Toolbar> - </DialogActions> - </Form> - </Dialog> - </CreateBase> + <DialogTitle id="form-dialog-title" sx={{ + position: 'sticky', + top: 0, + backgroundColor: 'background.paper', + zIndex: 1000 + }}> + {translate('create.title')} + <Box sx={{ position: 'absolute', top: 8, right: 8, zIndex: 1001 }}> + <DialogCloseButton onClose={handleClose} /> + </Box> + </DialogTitle> + <DialogContent sx={{ mt: 2 }}> + {open && ( + <form noValidate onSubmit={handleSubmit(onSubmit)} > + <Stepper activeStep={activeStep} orientation="vertical"> + <Step> + <StepLabel>{translate('page.tenant.create.title.basic')}</StepLabel> + <StepContent> + <Stack spacing={3} mt={2} direction='column' width={'50%'}> + <Controller + name="name" + control={control} + defaultValue="" + rules={{ required: true }} + parse={v => v} + render={({ field, fieldState: { error } }) => ( + <TextField + {...field} + label={translate('table.field.tenant.name')} + variant="outlined" + autoComplete="off" + error={!!error} + helperText={error ? translate('ra.validation.required') : ""} + /> + )} + /> + <Controller + name="flag" + control={control} + defaultValue="" + rules={{ + required: { + value: true, + message: translate('ra.validation.required') + }, + pattern: { + value: /^[A-Za-z]{3,20}$/, + message: translate('page.tenant.create.tip.onlyEn'), + } + }} + parse={v => v} + render={({ field, fieldState: { error } }) => ( + <TextField + {...field} + label={translate('table.field.tenant.flag')} + variant="outlined" + autoComplete="off" + error={!!error} + helperText={error ? error.message : ""} + /> + )} + /> + </Stack> + <Box sx={{ mt: 3 }}> + <Button onClick={handleNext} variant="contained"> + {translate('page.tenant.create.btn.next')} + </Button> + <Button disabled={activeStep === 0} onClick={handleBack}> + {translate('page.tenant.create.btn.back')} + </Button> + </Box> + </StepContent> + </Step> + + <Step> + <StepLabel>{translate('page.tenant.create.title.root')}</StepLabel> + <StepContent> + <Stack spacing={3} mt={2} direction='column' width={'50%'}> + <Controller + name="username" + control={control} + defaultValue="" + rules={{ + required: { + value: true, + message: translate('ra.validation.required') + }, + pattern: { + value: /^[A-Za-z0-9]{3,20}$/, + message: translate('page.settings.resetPwd.tip.usernameLimit'), + }, + }} + parse={v => v} + render={({ field, fieldState: { error } }) => ( + <TextField + {...field} + label={translate('table.field.user.username')} + variant="outlined" + autoComplete="off" + error={!!error} + helperText={error ? error.message : ""} + /> + )} + /> + <Controller + name="email" + control={control} + defaultValue="" + rules={{ + required: false, + pattern: { + value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, + message: translate("ra.validation.email"), + }, + }} + render={({ field, fieldState: { error } }) => ( + <TextField + {...field} + label={translate('table.field.user.email')} + variant="outlined" + autoComplete="off" + error={!!error} + helperText={error ? error.message : ""} + /> + )} + /> + <Controller + name="password" + control={control} + defaultValue="" + rules={{ + required: { + value: true, + message: translate('ra.validation.required'), + }, + pattern: { + value: /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{6,13}$/, + message: translate('page.settings.resetPwd.tip.pwdInputLimit'), + }, + }} + render={({ field, fieldState: { error } }) => ( + <TextField + {...field} + label={translate('page.settings.resetPwd.newPwd')} + type={showPassword ? 'text' : 'password'} + variant="outlined" + autoComplete="off" + error={!!error} + helperText={error ? error.message : ""} + InputProps={{ + endAdornment: ( + <InputAdornment position="end"> + <IconButton + aria-label="toggle password visibility" + onClick={() => setShowPassword((show) => !show)} + onMouseDown={(event) => { event.preventDefault() }} + edge="end" + > + {showPassword ? <VisibilityOff /> : <Visibility />} + </IconButton> + </InputAdornment> + ), + }} + /> + )} + /> + <Controller + name="confirmPassword" + control={control} + defaultValue="" + rules={{ + required: translate('ra.validation.required'), + validate: value => + value === passwordVal || translate('page.settings.resetPwd.tip.pwdNotMatch'), + }} + render={({ field, fieldState: { error } }) => ( + <TextField + {...field} + label={translate('page.settings.resetPwd.confirmNewPwd')} + type={showPassword ? 'text' : 'password'} + variant="outlined" + autoComplete="off" + error={!!error} + helperText={error?.message || ""} + InputProps={{ + endAdornment: ( + <InputAdornment position="end"> + <IconButton + aria-label="toggle password visibility" + onClick={() => setShowPassword((show) => !show)} + onMouseDown={(event) => { event.preventDefault() }} + edge="end" + > + {showPassword ? <VisibilityOff /> : <Visibility />} + </IconButton> + </InputAdornment> + ), + }} + /> + )} + /> + </Stack> + <Box sx={{ mt: 3 }}> + <Button onClick={handleNext} variant="contained"> + {translate('page.tenant.create.btn.next')} + </Button> + <Button onClick={handleBack}> + {translate('page.tenant.create.btn.back')} + </Button> + </Box> + </StepContent> + </Step> + + <Step> + <StepLabel>{translate('page.tenant.create.title.confirm')}</StepLabel> + <StepContent> + <Stack spacing={3} mt={2} direction='column' width={'50%'}> + <Controller + name="memo" + control={control} + defaultValue="" + rules={{ required: false }} + parse={v => v} + render={({ field, fieldState: { error } }) => ( + <TextField + {...field} + label={translate('common.field.memo')} + variant="outlined" + autoComplete="off" + fullWidth + multiline + minRows={2} + error={!!error} + helperText={error ? translate('ra.validation.required') : ""} + /> + )} + /> + </Stack> + <Box sx={{ mt: 3 }}> + <Button type="submit" variant="contained"> + {translate('ra.action.save')} + </Button> + <Button onClick={handleBack}> + {translate('page.tenant.create.btn.back')} + </Button> + </Box> + </StepContent> + </Step> + </Stepper> + </form> + )} + </DialogContent> + <DialogActions sx={{ height: 10, position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}> + </DialogActions> + </Dialog> </> ) } -- Gitblit v1.9.1