#
vincentlu
2026-01-19 3c74b6ffe9e246c66d44f2c6be463a630328d30a
#
2个文件已删除
2 文件已重命名
4个文件已添加
12个文件已修改
578 ■■■■ 已修改文件
zy-acs-common/src/main/java/com/zy/acs/common/enums/SystemModeType.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/public/login_bg.jpg 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/public/login_bg.mp4 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/public/login_bg3.jpg 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/public/login_bg4.jpg 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/src/App.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/src/api/auth/index.js 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/src/config/setting.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/src/i18n/en.js 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/src/i18n/zh.js 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/src/map/MapPage.jsx 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/src/page/login/Login.jsx 243 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-flow/src/page/login/index.jsx 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/common/config/ConfigProperties.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/common/domain/BusinessRes.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/common/security/SecurityConfig.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/system/controller/AuthController.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/system/controller/result/SystemInfoVo.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/java/com/zy/acs/manager/system/entity/Tenant.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-manager/src/main/resources/application.yml 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zy-acs-common/src/main/java/com/zy/acs/common/enums/SystemModeType.java
New file
@@ -0,0 +1,9 @@
package com.zy.acs.common.enums;
public enum SystemModeType {
    ONLINE,
    OFFLINE,
    ;
}
zy-acs-flow/public/login_bg.jpg

zy-acs-flow/public/login_bg.mp4
Binary files differ
zy-acs-flow/public/login_bg3.jpg
Binary files differ
zy-acs-flow/public/login_bg4.jpg
Binary files differ
zy-acs-flow/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/Login";
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";
zy-acs-flow/src/api/auth/index.js
@@ -1,5 +1,21 @@
import request from '../../utils/request';
export async function getSystemInfo() {
    const res = await request.get('/system/info');
    if (res.data.code === 200) {
        return res.data.data;
    }
    return Promise.reject(new Error(res.data.msg));
}
export async function tenants(_params) {
    const res = await request.get('/tenant/list', { params: _params });
    if (res.data.code === 200) {
        return res.data.data;
    }
    return Promise.reject(new Error(res.data.msg));
}
export async function login(_params) {
    const res = await request.post('/login', _params);
    if (res.data.code === 200) {
@@ -8,10 +24,20 @@
    return Promise.reject(new Error(res.data.msg));
}
export async function register(_params) {
    const res = await request.post('/register', _params);
    return res.data;
}
export async function menus(_params) {
    return await request.get('/auth/menu', _params);
    if (res.data.code === 200) {
        return res.data.data;
    }
    return Promise.reject(new Error(res.data.msg));
}
export async function sendEmailCode(_params) {
    const res = await request.get('/email/code', { params: _params });
    return res.data;
}
zy-acs-flow/src/config/setting.js
@@ -37,6 +37,6 @@
export const EDIT_MODE = 'pessimistic'; // pessimistic | undoable
export const LOGIN_BACKGROUND = 'image';  // image | media
export const LOGIN_BACKGROUND = 'media';  // image | media
export const CUSTOM_PAGES_DATA_INTERVAL = 1000;
zy-acs-flow/src/i18n/en.js
@@ -599,6 +599,25 @@
        }
    },
    page: {
        login: {
            title: 'Welcome to',
            footer: '',
            tenant: 'Company',
            email: 'Email Address',
            username: 'Username',
            password: 'Password',
            confirmPwd: 'Confirm Password',
            code: 'Verification Code',
            tab: {
                login: 'SIGN IN',
                register: 'SIGN UP',
            },
            button: {
                login: 'SIGN IN',
                register: 'SIGN UP',
                code: 'Send Code',
            },
        },
        code: {
            dirRule: {
                helper: 'Select one direction to disable; other directions remain available.',
zy-acs-flow/src/i18n/zh.js
@@ -599,6 +599,25 @@
        }
    },
    page: {
        login: {
            title: '欢迎使用',
            footer: '',
            tenant: '公司',
            email: '邮箱地址',
            username: '登录账号',
            password: '密码',
            confirmPwd: '确认密码',
            code: '验证码',
            tab: {
                login: '登录',
                register: '注册',
            },
            button: {
                login: '登录',
                register: '注册',
                code: '获取验证码',
            },
        },
        code: {
            dirRule: {
                helper: '选择一个方向禁用,其余方向保持可用',
zy-acs-flow/src/map/MapPage.jsx
@@ -68,7 +68,9 @@
    });
    const handleResize = () => {
        if (!contentRef) { return; }
        if (!contentRef.current || !player) {
            return;
        }
        const width = contentRef.current.offsetWidth;
        const height = contentRef.current.offsetHeight;
        player.resize(width, height);
@@ -125,6 +127,9 @@
    }, [sidebarOpen]);
    useEffect(() => {
        if (!player) {
            return;
        }
        player.setTheme(themeMode);
        Tool.setThemeMode(themeMode);
    }, [themeMode])
zy-acs-flow/src/page/login/Login.jsx
@@ -1,164 +1,143 @@
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,
    useNotify,
} from 'react-admin';
import { LOGIN_BACKGROUND } from '@/config/setting';
import { useForm, Controller, useWatch, FormProvider, useFormContext } from "react-hook-form";
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 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 onSubmit = (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_bg2.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_bg1.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' }}>
                            <TextInput
                <Stack spacing={2}>
                    <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"
                        )}
                    />
                    <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}
                                validate={required()}
                                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>
                    </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>
    );
};
                        )}
                    />
                    <Box />
                    <Button
                        type="submit"
                        variant="contained"
                        disabled={loading || !username || !password}
                    >
                        {loading && <CircularProgress size={25} thickness={2} />}
                        {translate('page.login.button.login')}
                    </Button>
                </Stack>
            </Box >
        </>
    )
}
export default Login;
zy-acs-flow/src/page/login/index.jsx
New file
@@ -0,0 +1,99 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import {
    Box,
    AppBar,
    Card,
    Toolbar,
    CircularProgress,
    Typography,
    Tabs,
    Tab,
    useTheme,
    useMediaQuery,
    Button,
} from '@mui/material';
import {
    useTranslate,
    useLogin,
    useNotify,
} from 'react-admin';
import { LOGIN_BACKGROUND, DEFAULT_THEME_MODE } from '@/config/setting';
import Login from "./Login";
const Index = () => {
    const translate = useTranslate();
    const theme = useTheme();
    const isSmallScreen = useMediaQuery(theme.breakpoints.down('md'));
    const [tab, setTab] = useState(0)
    const formPosition = isSmallScreen ? 'middle' : 'right'
    return (
        <Box
            sx={{
                display: 'flex',
                flexDirection: 'column',
                minHeight: '100vh',
                alignItems: formPosition === 'middle' ? 'center' : 'flex-end',
                paddingRight: formPosition === 'middle' ? 0 : '6em',
                justifyContent: formPosition === 'middle' ? 'flex-start' : '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,
                }}
            >
                {LOGIN_BACKGROUND === 'media' && (
                    <source src="/login_bg.mp4" type="video/mp4" />
                )}
            </video>
            <Card sx={{
                width: 400,
                marginTop: formPosition === 'middle' ? '6em' : 0,
                zIndex: 1,
                bgcolor: DEFAULT_THEME_MODE === 'light' ? '#fff' : '#121212',
                border: 'inherit'
            }}>
                <div>
                    <AppBar position="static" sx={{
                        backgroundColor: '#3D4BA7',
                        border: 'inherit',
                    }}>
                        <Toolbar sx={{
                            display: 'flex',
                            justifyContent: 'space-between'
                        }}>
                            <Typography variant="h6" color="inherit">{translate("page.login.title")} {"RMS"}</Typography>
                        </Toolbar>
                    </AppBar>
                </div>
                <Login />
                <Box mb={1} sx={{ textAlign: 'center' }}>
                    <Typography variant="caption" align="center">{translate("page.login.footer")}</Typography>
                </Box>
            </Card>
        </Box >
    );
};
export default Index;
zy-acs-manager/src/main/java/com/zy/acs/manager/common/config/ConfigProperties.java
@@ -1,8 +1,13 @@
package com.zy.acs.manager.common.config;
import com.zy.acs.common.enums.SystemModeType;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
 * 系统配置属性
@@ -30,4 +35,62 @@
     */
    private String tokenKey;
    /**
     * 系统名称
     */
    private String systemName;
    /**
     * 系统版本
     */
    private String systemVersion;
    /**
     * 系统模式( ONLINE / OFFLINE )
     */
    private String systemMode;
    /**
     * 超级管理员
     */
    private String superUsername;
    /**
     * 验证码长度
     */
    private Integer codeLength = 4;
    /**
     * 验证码有效期 ( 秒 )
     */
    private Integer codeTime = 300;
    /**
     * 超级验证码
     */
    private String securityCode;
    /**
     * 文件保存路径
     */
    private String fileSavePath;
    /**
     * 文件服务器url前缀
     */
    private String fileDomainPrefix;
    /**
     * 图片最大体积
     */
    private Integer imageMaxSize;
    public List<String> getSuperUserList() {
        return Arrays.stream(superUsername.split(",")).collect(Collectors.toList());
    }
    public SystemModeType getSystemMode() {
        return SystemModeType.valueOf(systemMode);
    }
}
zy-acs-manager/src/main/java/com/zy/acs/manager/common/domain/BusinessRes.java
New file
@@ -0,0 +1,20 @@
package com.zy.acs.manager.common.domain;
import com.zy.acs.framework.common.BaseRes;
public class BusinessRes implements BaseRes {
    public final static String INVALID_PASSWORD = "10001 - The current password was incorrect";
    public final static String USERNAME_EXIST = "10002 - Username already exist";
    public final static String USERNAME_NOT_EXIST = "10003 - Username doesn't exist";
    public final static String USERNAME_FROZEN = "10004 - Account frozen";
    public final static String INVALID_EMAIL = "10005 - Invalid email address";
    public final static String EMAIL_EXIT = "10006 - Email address already exist";
}
zy-acs-manager/src/main/java/com/zy/acs/manager/common/security/SecurityConfig.java
@@ -34,6 +34,8 @@
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    public static final String[] FILTER_PATH = new String[]{
            "/api/system/info",
            "/api/tenant/list",
            "/api/open/**",
            "/demo/**",
            "/test/**",
zy-acs-manager/src/main/java/com/zy/acs/manager/system/controller/AuthController.java
@@ -5,12 +5,15 @@
import com.zy.acs.framework.common.R;
import com.zy.acs.manager.common.annotation.OperationLog;
import com.zy.acs.manager.common.config.ConfigProperties;
import com.zy.acs.manager.common.domain.BusinessRes;
import com.zy.acs.manager.common.security.JwtSubject;
import com.zy.acs.manager.common.utils.JwtUtil;
import com.zy.acs.manager.manager.enums.StatusType;
import com.zy.acs.manager.system.controller.param.LoginParam;
import com.zy.acs.manager.system.controller.param.UpdatePasswordParam;
import com.zy.acs.manager.system.controller.result.LoginResult;
import com.zy.acs.manager.system.controller.result.MenuVo;
import com.zy.acs.manager.system.controller.result.SystemInfoVo;
import com.zy.acs.manager.system.entity.Menu;
import com.zy.acs.manager.system.entity.User;
import com.zy.acs.manager.system.entity.UserLogin;
@@ -53,18 +56,18 @@
        Long tenantId = param.getTenantId();
        User user = userService.getByUsername(username, tenantId);
        if (user == null) {
            return R.error("Username Does Not Exist");
            return R.parse(BusinessRes.USERNAME_NOT_EXIST);
        }
        if (!user.getStatus().equals(1)) {
            return R.error("Account Frozen");
        if (!user.getStatus().equals(StatusType.ENABLE.val)) {
            return R.parse(BusinessRes.USERNAME_FROZEN);
        }
        if (!userService.comparePassword(user.getPassword(), param.getPassword())) {
            return R.error("Wrong Password");
            return R.parse(BusinessRes.INVALID_PASSWORD);
        }
        String accessToken = JwtUtil.buildToken(new JwtSubject(username, user.getTenantId()),
                configProperties.getTokenExpireTime(), configProperties.getTokenKey());
        userLoginService.saveAsync(user.getId(), accessToken, UserLogin.TYPE_LOGIN, tenantId, null, request);
        return R.ok("Login Success").add(new LoginResult(accessToken, user));
        return R.ok("Sign In Success").add(new LoginResult(accessToken, user));
    }
    @GetMapping("/auth/user")
@@ -78,13 +81,6 @@
//        return R.ok().add(Utils.toTreeData(menus, 0L, Menu::getParentId, Menu::getId, Menu::setChildren));
        List<MenuVo> voList = menus.stream().map(this::convertToVo).collect(Collectors.toList());
        return R.ok().add(Utils.toTreeData(voList, 0L, MenuVo::getParentId, MenuVo::getId, MenuVo::setChildren));
    }
    @GetMapping("/auth/tenant")
    public R authHost() {
        return R.ok().add(tenantService.list());
    }
    @PreAuthorize("hasAuthority('sys:auth:user')")
@@ -127,6 +123,14 @@
        return R.error("Update Fail");
    }
    @GetMapping("/system/info")
    public R systemInfo() {
        SystemInfoVo infoVo = new SystemInfoVo();
        infoVo.setName(configProperties.getSystemName());
        infoVo.setVersion(configProperties.getSystemVersion());
        infoVo.setMode(configProperties.getSystemMode().toString());
        return R.ok(infoVo);
    }
    // ----------------------------------------------------
zy-acs-manager/src/main/java/com/zy/acs/manager/system/controller/result/SystemInfoVo.java
New file
@@ -0,0 +1,18 @@
package com.zy.acs.manager.system.controller.result;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SystemInfoVo {
    private String name;
    private String version;
    private String mode;
}
zy-acs-manager/src/main/java/com/zy/acs/manager/system/entity/Tenant.java
@@ -4,12 +4,10 @@
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.zy.acs.framework.common.Cools;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Date;
@Data
@@ -72,22 +70,6 @@
            default:
                return null;
        }
    }
    public String getCreateTime$(){
        if (Cools.isEmpty(this.createTime)){
            return "";
        }
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.createTime);
    }
    public String getUpdateTime$(){
        if (Cools.isEmpty(this.updateTime)){
            return "";
        }
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.updateTime);
    }
}
zy-acs-manager/src/main/resources/application.yml
@@ -83,6 +83,9 @@
    path: stock/out/rcs-manager/logs
config:
  system-name: @pom.artifactId@
  system-version: @pom.version@
  system-mode: ONLINE
  open-office-home: C
  swagger-base-package: com.zy.acs
  swagger-title: ACS API文档