#
vincentlu
2025-02-07 7a00868dc7730b83dc1e3d232b9f2e2b43ad237a
#
5个文件已修改
3个文件已添加
1个文件已删除
1028 ■■■■■ 已修改文件
rsf-admin/src/App.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/i18n/en.js 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/i18n/zh.js 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/login/Login.jsx 159 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/login/Register.jsx 161 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/login/index.jsx 247 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/login/index1.jsx 261 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/login/index2.jsx 195 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/UserLoginServiceImpl.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/App.jsx
@@ -16,7 +16,7 @@
import DataProvider from "./config/dataProvider";
import Dashboard from "./page/dashboard/Dashboard";
import Settings from "./page/settings/Settings";
import Login from "./page/login/index1";
import Login from "./page/login";
import * as Common from './utils/common'
import { themes } from './themes/themes';
import { SPA_NAME, SPA_VERSION, DEFAULT_THEME_NAME, DEFAULT_THEME_MODE, DATA_PROVIDER_SPRING } from "./config/setting";
rsf-admin/src/i18n/en.js
@@ -223,6 +223,7 @@
            tenant: 'Tenant',
            button: {
                login: 'LOG IN',
                register: 'REGISTER',
            },
        },
    }
rsf-admin/src/i18n/zh.js
@@ -223,6 +223,7 @@
            tenant: '租户',
            button: {
                login: 'LOG IN',
                register: 'REGISTER',
            },
        },
    }
rsf-admin/src/page/login/Login.jsx
New file
@@ -0,0 +1,159 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import { useLocation } from 'react-router-dom';
import {
    Box,
    CircularProgress,
    Typography,
    Button,
    TextField,
    Stack,
    Autocomplete
} from '@mui/material';
import {
    useTranslate,
    useLogin,
    useNotify,
} from 'react-admin';
import { useForm, Controller } from 'react-hook-form';
const Login = (props) => {
    const translate = useTranslate();
    const notify = useNotify();
    const login = useLogin();
    const location = useLocation();
    const { tenantList } = props;
    const { control, watch, handleSubmit, setValue } = useForm();
    const [loading, setLoading] = useState(false);
    const username = watch('username');
    const password = watch('password');
    const tenantId = watch('tenantId');
    useEffect(() => {
        if (tenantList.length > 0 && !tenantId) {
            setValue('tenantId', tenantList[0].id);
        }
    }, [tenantList, setValue]);
    const onSubmit = (data) => {
        setLoading(true);
        login(
            data,
            location.state ? (location.state).nextPathname : '/'
        ).catch((error) => {
            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,
                    },
                }
            );
        });
    };
    return (
        <>
            <Box
                p={2}
                display="flex"
                flexDirection='column'
                component="form" onSubmit={handleSubmit(onSubmit)} noValidate
            >
                <Stack spacing={2}>
                    <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) => {
                                        onChange(newValue ? newValue.id : '');
                                    }}
                                    renderInput={(params) => (
                                        <TextField
                                            {...params}
                                            label={translate("page.login.tenant")}
                                            variant="standard"
                                            inputRef={ref}
                                        />
                                    )}
                                />
                            );
                        }}
                    />
                    <Controller
                        name="username"
                        control={control}
                        defaultValue=""
                        rules={{ required: true }}
                        render={({ field }) => (
                            <TextField
                                {...field}
                                label={translate('ra.auth.username')}
                                variant="standard"
                                disabled={loading}
                                autoFocus
                            />
                        )}
                    />
                    <Controller
                        name="password"
                        control={control}
                        defaultValue=""
                        rules={{ required: true }}
                        render={({ field }) => (
                            <TextField
                                {...field}
                                label={translate('ra.auth.password')}
                                type="password"
                                variant="standard"
                                disabled={loading}
                            />
                        )}
                    />
                    <Box mt={10}></Box>
                    <Button
                        type="submit"
                        variant="contained"
                        disabled={loading || !(tenantId && username && password)}
                        sx={{
                            backgroundColor: "#3D4BA7"
                        }}
                    >
                        {loading && <CircularProgress size={25} thickness={2} />}
                        {translate('page.login.button.login')}
                    </Button>
                </Stack>
                <Box mt={1} mb={1} sx={{ textAlign: 'center' }}>or</Box>
            </Box >
        </>
    )
}
export default Login;
rsf-admin/src/page/login/Register.jsx
New file
@@ -0,0 +1,161 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import { useLocation } from 'react-router-dom';
import {
    Box,
    CircularProgress,
    Typography,
    Button,
    TextField,
    Stack,
    Autocomplete
} from '@mui/material';
import {
    useTranslate,
    useLogin,
    useNotify,
} from 'react-admin';
import { useForm, Controller } from 'react-hook-form';
const Register = (props) => {
    const translate = useTranslate();
    const notify = useNotify();
    const login = useLogin();
    const location = useLocation();
    const { tab, tenantList } = props;
    const { control, watch, handleSubmit, setValue } = useForm();
    const [loading, setLoading] = useState(false);
    const username = watch('username');
    const password = watch('password');
    const tenantId = watch('tenantId');
    useEffect(() => {
        if (tenantList.length > 0 && !tenantId) {
            setValue('tenantId', tenantList[0].id);
        }
    }, [tenantList, setValue]);
    const onSubmit = (data) => {
        console.log(data);
        setLoading(true);
        // js native confirm && root
        login(
            data,
            location.state ? (location.state).nextPathname : '/'
        ).catch((error) => {
            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,
                    },
                }
            );
        });
    };
    return (
        <>
            <Box
                p={2}
                display="flex"
                flexDirection='column'
                component="form" onSubmit={handleSubmit(onSubmit)} noValidate
            >
                <Stack spacing={2}>
                    <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) => {
                                        onChange(newValue ? newValue.id : '');
                                    }}
                                    renderInput={(params) => (
                                        <TextField
                                            {...params}
                                            label={translate("page.login.tenant")}
                                            variant="standard"
                                            inputRef={ref}
                                        />
                                    )}
                                />
                            );
                        }}
                    />
                    <Controller
                        name="username"
                        control={control}
                        defaultValue=""
                        rules={{ required: true }}
                        render={({ field }) => (
                            <TextField
                                {...field}
                                label={translate('ra.auth.username')}
                                variant="standard"
                                disabled={loading}
                                autoFocus
                            />
                        )}
                    />
                    <Controller
                        name="password"
                        control={control}
                        defaultValue=""
                        rules={{ required: true }}
                        render={({ field }) => (
                            <TextField
                                {...field}
                                label={translate('ra.auth.password')}
                                type="password"
                                variant="standard"
                                disabled={loading}
                            />
                        )}
                    />
                    <Box mt={10}></Box>
                    <Button
                        type="submit"
                        variant="contained"
                        disabled={loading || !(tenantId && username && password)}
                        sx={{
                            backgroundColor: "#3D4BA7"
                        }}
                    >
                        {loading && <CircularProgress size={25} thickness={2} />}
                        {translate('page.login.button.register')}
                    </Button>
                </Stack>
                <Box mt={1} mb={1} sx={{ textAlign: 'center' }}>or</Box>
            </Box >
        </>
    )
}
export default Register;
rsf-admin/src/page/login/index.jsx
@@ -1,195 +1,108 @@
import * as React from 'react';
import { useState } from 'react';
import { useLocation } from 'react-router-dom';
import React, { useState, useRef, useEffect, useMemo } from "react";
import {
    Avatar,
    Box,
    Button,
    AppBar,
    Card,
    CardActions,
    Toolbar,
    CircularProgress,
    Typography,
    Tabs,
    Tab,
} from '@mui/material';
import LockIcon from '@mui/icons-material/Lock';
import {
    Form,
    required,
    TextInput,
    useTranslate,
    useLogin,
    useNotify,
    SelectInput,
} from 'react-admin';
import { LOGIN_BACKGROUND } from '@/config/setting';
import { tenants } from '@/api/auth';
import Login from "./Login";
import Register from "./Register";
const Login = () => {
    const [loading, setLoading] = useState(false);
const Index = () => {
    const translate = useTranslate();
    const notify = useNotify();
    const login = useLogin();
    const location = useLocation();
    const [tab, setTab] = useState(0)
    const [tenantList, setTenantList] = useState([]);
    const [tenantList, setTenantList] = React.useState([]);
    const [tenantId, setTenantId] = React.useState(null);
    React.useEffect(() => {
    useEffect(() => {
        tenants().then(data => {
            setTenantList(data);
            if (data.length > 0) {
                setTenantId(data[0].id);
            }
        })
    }, []);
    React.useEffect(() => {
        console.log(tenantId);
    }, [tenantId]);
    const handleSubmit = (auth) => {
        setLoading(true);
        // js native confirm && root
        login(
            auth,
            location.state ? (location.state).nextPathname : '/'
        ).catch((error) => {
            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,
                    },
                }
            );
        });
    };
    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',
        <Box
            sx={{
                display: 'flex',
                flexDirection: 'column',
                minHeight: '100vh',
                alignItems: 'center',
                justifyContent: 'flex-start',
                // justifyContent: 'center',
                background: `url(/login_bg.jpg)`, // https://unsplash.com/
                backgroundRepeat: 'no-repeat',
                backgroundSize: 'cover',
            }
            }
        >
            <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,
                }}
            >
                <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" />
                    )}
                </video>
                {LOGIN_BACKGROUND === 'media' && (
                    <source src="/login_bg.mp4" type="video/mp4" />
                )}
            </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' }}>
                            <SelectInput
                                label="page.login.tenant"
                                source="tenantId"
                                choices={tenantList.map(item => ({
                                    id: item.id,
                                    name: item.name
                                }))}
                                validate={required()}
                                value={tenantId}
                                onChange={e => setTenantId(e.target.value)}
                            />
                        </Box>
                        <Box sx={{ marginTop: '.1em' }}>
                            <TextInput
                                autoFocus
                                source="username"
                                label={translate('ra.auth.username')}
                                disabled={loading}
                                validate={required()}
                            />
                        </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>
            <Card sx={{
                width: 400,
                marginTop: '6em',
                zIndex: 1
            }}>
                <div>
                    <AppBar position="static" sx={{ backgroundColor: '#3D4BA7' }}>
                        <Toolbar>
                            <Typography variant="h6" color="inherit">Welcome</Typography>
                        </Toolbar>
                    </AppBar>
                </div>
                <Tabs
                    value={tab}
                    onChange={(event, value) => {
                        setTab(value);
                    }}
                    indicatorColor="primary"
                    textColor="primary"
                    variant="fullWidth"
                >
                    <Tab label="Login" sx={{ fontSize: '.8em' }} />
                    <Tab label="Register" sx={{ fontSize: '.8em' }} />
                </Tabs>
                {tab === 0 && <Login tenantList={tenantList} />}
                {tab === 1 && <Register tenantList={tenantList} />}
                <Box mt={1} mb={1} sx={{ textAlign: 'center' }}>
                    <Typography variant="caption" align="center">rsf - sever</Typography>
                </Box>
            </Card>
        </Box >
    );
};
export default Login;
export default Index;
rsf-admin/src/page/login/index1.jsx
File was deleted
rsf-admin/src/page/login/index2.jsx
New file
@@ -0,0 +1,195 @@
import * as React from 'react';
import { useState } from 'react';
import { useLocation } from 'react-router-dom';
import {
    Avatar,
    Box,
    Button,
    Card,
    CardActions,
    CircularProgress,
} from '@mui/material';
import LockIcon from '@mui/icons-material/Lock';
import {
    Form,
    required,
    TextInput,
    useTranslate,
    useLogin,
    useNotify,
    SelectInput,
} from 'react-admin';
import { LOGIN_BACKGROUND } from '@/config/setting';
import { tenants } from '@/api/auth';
const Login = () => {
    const [loading, setLoading] = useState(false);
    const translate = useTranslate();
    const notify = useNotify();
    const login = useLogin();
    const location = useLocation();
    const [tenantList, setTenantList] = React.useState([]);
    const [tenantId, setTenantId] = React.useState(null);
    React.useEffect(() => {
        tenants().then(data => {
            setTenantList(data);
            if (data.length > 0) {
                setTenantId(data[0].id);
            }
        })
    }, []);
    React.useEffect(() => {
        console.log(tenantId);
    }, [tenantId]);
    const handleSubmit = (auth) => {
        setLoading(true);
        // js native confirm && root
        login(
            auth,
            location.state ? (location.state).nextPathname : '/'
        ).catch((error) => {
            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,
                    },
                }
            );
        });
    };
    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',
                }}
            >
                <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" />
                    )}
                </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' }}>
                            <SelectInput
                                label="page.login.tenant"
                                source="tenantId"
                                choices={tenantList.map(item => ({
                                    id: item.id,
                                    name: item.name
                                }))}
                                validate={required()}
                                value={tenantId}
                                onChange={e => setTenantId(e.target.value)}
                            />
                        </Box>
                        <Box sx={{ marginTop: '.1em' }}>
                            <TextInput
                                autoFocus
                                source="username"
                                label={translate('ra.auth.username')}
                                disabled={loading}
                                validate={required()}
                            />
                        </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;
rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/UserLoginServiceImpl.java
@@ -23,6 +23,7 @@
        userLogin.setUserId(userId);
        userLogin.setToken(token);
        userLogin.setType(type);
        userLogin.setTenantId(tenantId);
        userLogin.setIp(IpTools.gainRealIp(request));
        userLogin.setMemo(memo);
        baseMapper.insert(userLogin);