From 669fe984048785873c20fbd0ce8abb3cd52c1fce Mon Sep 17 00:00:00 2001 From: skyouc Date: 星期二, 29 四月 2025 15:22:24 +0800 Subject: [PATCH] 新增出库单优化 --- rsf-admin/src/page/login/Login.jsx | 297 ++++++++++++++++++++++++++++++++-------------------------- 1 files changed, 164 insertions(+), 133 deletions(-) diff --git a/rsf-admin/src/page/login/Login.jsx b/rsf-admin/src/page/login/Login.jsx index f212d46..186f09a 100644 --- a/rsf-admin/src/page/login/Login.jsx +++ b/rsf-admin/src/page/login/Login.jsx @@ -1,164 +1,195 @@ -import * as React from 'react'; -import { useState } from 'react'; +import React, { useState, useRef, useEffect, useMemo } from "react"; import { useLocation } from 'react-router-dom'; import { - Avatar, Box, - Button, - Card, - CardActions, CircularProgress, + Typography, + Button, + TextField, + Stack, + Autocomplete, + InputAdornment, + IconButton, } from '@mui/material'; -import LockIcon from '@mui/icons-material/Lock'; import { - Form, - required, - TextInput, useTranslate, useLogin, + localStorageStore, useNotify, } from 'react-admin'; -import { LOGIN_BACKGROUND } from '@/config/setting'; +import { getSystemDicts } from "@/api/auth"; +import { useForm, Controller, useWatch, FormProvider, useFormContext } from "react-hook-form"; +import ProviderChoices from "./ProviderChoices"; +import Visibility from '@mui/icons-material/Visibility'; +import VisibilityOff from '@mui/icons-material/VisibilityOff'; -const Login = () => { - const [loading, setLoading] = useState(false); +const Login = (props) => { const translate = useTranslate(); - const notify = useNotify(); const login = useLogin(); const location = useLocation(); + const { systemInfo: { mode }, tenantList } = props; - const handleSubmit = (auth) => { + const { control, handleSubmit, watch, setValue, getValues, setError, clearErrors } = useForm(); + + const [loading, setLoading] = useState(false); + const [showPassword, setShowPassword] = useState(false); + + const username = watch('username'); + const password = watch('password'); + const tenantId = watch('tenantId'); + + useEffect(() => { + if (tenantList.length > 0 && !tenantId) { + const rememberTenantId = localStorage.getItem('remember_tenantId'); + if (rememberTenantId && tenantList.some(t => t.id === Number(rememberTenantId))) { + setValue('tenantId', Number(rememberTenantId)); + } else { + setValue('tenantId', tenantList[0].id); + } + } + }, [tenantList, setValue]); + + const onSubmit = (data) => { + getSystemDicts().then(data => { + localStorage.setItem('sys_dicts', JSON.stringify(data)); + }) setLoading(true); - // js native confirm && root login( - auth, + data, location.state ? (location.state).nextPathname : '/' - ).catch((error) => { + ).catch((res) => { setLoading(false); - notify( - typeof error === 'string' - ? error - : typeof error === 'undefined' || !error.message - ? 'ra.auth.sign_in_error' - : error.message, - { - type: 'error', - messageArgs: { - _: - typeof error === 'string' - ? error - : error && error.message - ? error.message - : undefined, - }, - } - ); + const { code, msg, data } = res; + if (code === 10003) { + setError('username', { + message: msg + }) + } else if (code === 10004) { + setError('username', { + message: msg + }) + } else if (code === 10001) { + setError('password', { + message: msg + }) + } else { + notify(msg, { type: 'error', messageArgs: { _: msg } }); + } }); }; return ( - <Form onSubmit={handleSubmit} noValidate> - {/* https://unsplash.com/ */} + <> <Box - sx={{ - display: 'flex', - flexDirection: 'column', - minHeight: '100vh', - alignItems: 'center', - justifyContent: 'flex-start', - // justifyContent: 'center', - background: `url(/login_bg.jpg)`, - backgroundRepeat: 'no-repeat', - backgroundSize: 'cover', - }} + p={2} + display="flex" + flexDirection='column' + component="form" onSubmit={handleSubmit(onSubmit)} noValidate > - <video - autoPlay - loop - muted - style={{ - position: 'fixed', - top: 0, - left: 0, - width: '100%', - height: '100%', - // objectFit: 'cover', - // objectFit: 'contain', - objectFit: 'fill', - // objectFit: 'scale-down', - zIndex: 0, - }} - > - {LOGIN_BACKGROUND === 'media' && ( - <source src="/login_bg.mp4" type="video/mp4" /> + <Stack spacing={2}> + {mode === 'OFFLINE' && ( + <Controller + name="tenantId" + control={control} + rules={{ required: true }} + defaultValue={tenantList.length > 0 ? tenantList[0].id : ''} + render={({ field: { onChange, value, ref } }) => { + const selectedTenant = tenantList.find(tenant => tenant.id === value) || null; + return ( + <Autocomplete + options={tenantList} + getOptionLabel={(option) => option.name} + value={selectedTenant} + onChange={(_, newValue) => { + const newTenantId = newValue ? newValue.id : ''; + onChange(newTenantId); + localStorage.setItem('remember_tenantId', newTenantId); + }} + renderInput={(params) => ( + <TextField + {...params} + label={translate("page.login.tenant")} + variant="standard" + inputRef={ref} + /> + )} + /> + ); + }} + /> )} - </video> - <Card sx={{ - minWidth: 300, - marginTop: '6em', - zIndex: 1 - }}> - <Box - sx={{ - margin: '1em', - display: 'flex', - justifyContent: 'center', - }} - > - <Avatar sx={{ bgcolor: 'secondary.main' }}> - <LockIcon /> - </Avatar> - </Box> - <Box - sx={{ - marginTop: '1em', - display: 'flex', - justifyContent: 'center', - color: theme => theme.palette.grey[500], - }} - > - Hint: root / 123456 - </Box> - <Box sx={{ padding: '0 1em 1em 1em' }}> - <Box sx={{ marginTop: '1em' }}> - <TextInput + <Controller + name="username" + control={control} + defaultValue="" + rules={{ required: true }} + render={({ field, fieldState: { error } }) => ( + <TextField + {...field} + label={translate("page.login.username")} + variant="standard" + disabled={loading} autoFocus - source="username" - label={translate('ra.auth.username')} - disabled={loading} - validate={required()} + autoComplete="off" + error={!!error} + helperText={error?.message || ""} /> - </Box> - <Box sx={{ marginTop: '1em' }}> - <TextInput - source="password" - label={translate('ra.auth.password')} - type="password" - disabled={loading} - validate={required()} - /> - </Box> - </Box> - <CardActions sx={{ padding: '0 1em 1em 1em' }}> - <Button - variant="contained" - type="submit" - color="primary" - disabled={loading} - fullWidth - > - {loading && ( - <CircularProgress size={25} thickness={2} /> - )} - {translate('ra.auth.sign_in')} - </Button> - </CardActions> - </Card> - </Box> - </Form> - ); -}; + )} + /> -export default Login; + <Controller + name="password" + control={control} + defaultValue="" + rules={{ required: true }} + render={({ field, fieldState: { error } }) => ( + <TextField + {...field} + label={translate("page.login.password")} + type={showPassword ? 'text' : 'password'} + variant="standard" + disabled={loading} + 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> + ), + }} + /> + )} + /> + + <Box /> + + <Button + type="submit" + variant="contained" + disabled={loading || !((mode === 'OFFLINE' ? tenantId : true) && username && password)} + > + {loading && <CircularProgress size={25} thickness={2} />} + {translate('page.login.button.login')} + </Button> + + </Stack> + {/* <Box mt={1} mb={1} sx={{ textAlign: 'center' }}>or</Box> */} + + {/* <ProviderChoices type="LOG IN" /> */} + </Box > + </> + ) +} + +export default Login; \ No newline at end of file -- Gitblit v1.9.1