style: login
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 108 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.8 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 4.2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 49 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
@@ -0,0 +1,11 @@
|
|||||||
|
// footer all styles
|
||||||
|
.footer {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
background-color: transparent;
|
||||||
|
padding: 20px 0;
|
||||||
|
text-align: center;
|
||||||
|
font-size: var(--font-size-middle);
|
||||||
|
color: var(--color-text-2);
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { useIntl } from '@umijs/max';
|
||||||
|
import { Space } from 'antd';
|
||||||
|
import './index.less';
|
||||||
|
|
||||||
|
const Footer: React.FC = () => {
|
||||||
|
const intl = useIntl();
|
||||||
|
return (
|
||||||
|
<div className="footer">
|
||||||
|
<div className="footer-content">
|
||||||
|
<div className="footer-content-left">
|
||||||
|
<div className="footer-content-left-text">
|
||||||
|
<Space size={4}>
|
||||||
|
<span>©</span>
|
||||||
|
<span> {new Date().getFullYear()}</span>
|
||||||
|
<span> {intl.formatMessage({ id: 'settings.company' })}</span>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Footer;
|
||||||
@@ -35,6 +35,7 @@ const Wrapper: React.FC<WrapperProps> = ({
|
|||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
wrapperStyle['wrapper-box'],
|
wrapperStyle['wrapper-box'],
|
||||||
|
'field-wrapper',
|
||||||
wrapperStyle[`validate-status-${status}`],
|
wrapperStyle[`validate-status-${status}`],
|
||||||
addAfter ? wrapperStyle['seal-input-wrapper-addafter'] : '',
|
addAfter ? wrapperStyle['seal-input-wrapper-addafter'] : '',
|
||||||
disabled ? wrapperStyle['seal-input-wrapper-disabled'] : '',
|
disabled ? wrapperStyle['seal-input-wrapper-disabled'] : '',
|
||||||
|
|||||||
+20
-11
@@ -22,6 +22,8 @@ html {
|
|||||||
--font-weight-medium: 600;
|
--font-weight-medium: 600;
|
||||||
--font-weight-bold: 700;
|
--font-weight-bold: 700;
|
||||||
--color-text-1: var(--ant-color-text);
|
--color-text-1: var(--ant-color-text);
|
||||||
|
--color-text-3: rgba(0, 0, 0, 45%);
|
||||||
|
--color-text-2: rgba(0, 0, 0, 65%);
|
||||||
--color-bg-light-1: #e6f6ff;
|
--color-bg-light-1: #e6f6ff;
|
||||||
--font-size-base: 12px;
|
--font-size-base: 12px;
|
||||||
--font-size-large: 16px;
|
--font-size-large: 16px;
|
||||||
@@ -59,7 +61,7 @@ html {
|
|||||||
--ant-font-size: var(--font-size-base);
|
--ant-font-size: var(--font-size-base);
|
||||||
|
|
||||||
&.ant-menu-css-var {
|
&.ant-menu-css-var {
|
||||||
--ant-menu-item-selected-bg: var(--color-white-1);
|
// --ant-menu-item-selected-bg: var(--color-white-1);
|
||||||
--ant-menu-item-border-radius: 8px;
|
--ant-menu-item-border-radius: 8px;
|
||||||
--ant-menu-item-selected-color: var(--ant-color-primary);
|
--ant-menu-item-selected-color: var(--ant-color-primary);
|
||||||
--ant-menu-item-hover-bg: var(--color-white-1);
|
--ant-menu-item-hover-bg: var(--color-white-1);
|
||||||
@@ -107,7 +109,7 @@ html {
|
|||||||
.css-var-ri.ant-menu-css-var,
|
.css-var-ri.ant-menu-css-var,
|
||||||
.css-var-rh.ant-menu-css-var {
|
.css-var-rh.ant-menu-css-var {
|
||||||
--ant-menu-item-height: 46px;
|
--ant-menu-item-height: 46px;
|
||||||
--ant-menu-item-selected-bg: var(--color-white-1);
|
// --ant-menu-item-selected-bg: var(--color-white-1);
|
||||||
--ant-menu-item-border-radius: 8px;
|
--ant-menu-item-border-radius: 8px;
|
||||||
--ant-menu-item-selected-color: var(--ant-color-primary);
|
--ant-menu-item-selected-color: var(--ant-color-primary);
|
||||||
--ant-menu-item-hover-bg: var(--color-white-1);
|
--ant-menu-item-hover-bg: var(--color-white-1);
|
||||||
@@ -265,6 +267,10 @@ body {
|
|||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ant-pro-sider-logo-collapsed {
|
||||||
|
padding-left: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.ant-pro-sider .ant-layout-sider-children {
|
.ant-pro-sider .ant-layout-sider-children {
|
||||||
background-color: var(--color-fill-sider);
|
background-color: var(--color-fill-sider);
|
||||||
// box-shadow: var(--box-shadow-base);
|
// box-shadow: var(--box-shadow-base);
|
||||||
@@ -394,15 +400,18 @@ body {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// .login-wrapper {
|
|
||||||
// position: fixed;
|
.login-wrapper {
|
||||||
// top: 0;
|
position: fixed;
|
||||||
// left: 0;
|
top: 0;
|
||||||
// bottom: 0;
|
left: 0;
|
||||||
// right: 0;
|
bottom: 0;
|
||||||
// background: url('@/assets/images/bg-2.png') center center no-repeat;
|
right: 0;
|
||||||
// background-size: cover;
|
min-height: 100vh;
|
||||||
// }
|
z-index: -1;
|
||||||
|
background: url('@/assets/images/bg-2.png') center center no-repeat;
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
|
||||||
.ant-pro-page-container {
|
.ant-pro-page-container {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
|||||||
@@ -2,10 +2,14 @@
|
|||||||
// This file is generated by Umi automatically
|
// This file is generated by Umi automatically
|
||||||
// DO NOT CHANGE IT MANUALLY!
|
// DO NOT CHANGE IT MANUALLY!
|
||||||
import GpustackLogo from '@/assets/images/gpustack-logo.png';
|
import GpustackLogo from '@/assets/images/gpustack-logo.png';
|
||||||
|
import SmallLogo from '@/assets/images/small-logo-200x200.png';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
const LogoIcon: React.FC = () => {
|
const LogoIcon: React.FC = () => {
|
||||||
return <img src={GpustackLogo} alt="logo" style={{ height: 16 }} />;
|
return <img src={GpustackLogo} alt="logo" style={{ height: 16 }} />;
|
||||||
};
|
};
|
||||||
|
const SLogoIcon: React.FC = () => {
|
||||||
|
return <img src={SmallLogo} alt="logo" style={{ height: 24 }} />;
|
||||||
|
};
|
||||||
|
|
||||||
export default LogoIcon;
|
export { LogoIcon, SLogoIcon };
|
||||||
|
|||||||
+11
-6
@@ -4,6 +4,7 @@ import { userAtom } from '@/atoms/user';
|
|||||||
import { logout } from '@/pages/login/apis';
|
import { logout } from '@/pages/login/apis';
|
||||||
import { useAccessMarkedRoutes } from '@@/plugin-access';
|
import { useAccessMarkedRoutes } from '@@/plugin-access';
|
||||||
import { useModel } from '@@/plugin-model';
|
import { useModel } from '@@/plugin-model';
|
||||||
|
|
||||||
import { ProLayout } from '@ant-design/pro-components';
|
import { ProLayout } from '@ant-design/pro-components';
|
||||||
import {
|
import {
|
||||||
Link,
|
Link,
|
||||||
@@ -17,13 +18,12 @@ import {
|
|||||||
type IRoute
|
type IRoute
|
||||||
} from '@umijs/max';
|
} from '@umijs/max';
|
||||||
import { useAtom } from 'jotai';
|
import { useAtom } from 'jotai';
|
||||||
import { useMemo } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import Exception from './Exception';
|
import Exception from './Exception';
|
||||||
import './Layout.css';
|
import './Layout.css';
|
||||||
import Logo from './Logo';
|
import { LogoIcon, SLogoIcon } from './Logo';
|
||||||
import { getRightRenderContent } from './rightRender';
|
import { getRightRenderContent } from './rightRender';
|
||||||
import { patchRoutes } from './runtime';
|
import { patchRoutes } from './runtime';
|
||||||
|
|
||||||
const loginPath = '/login';
|
const loginPath = '/login';
|
||||||
|
|
||||||
// 过滤出需要显示的路由, 这里的filterFn 指 不希望显示的层级
|
// 过滤出需要显示的路由, 这里的filterFn 指 不希望显示的层级
|
||||||
@@ -84,6 +84,7 @@ export default (props: any) => {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const { clientRoutes, pluginManager } = useAppData();
|
const { clientRoutes, pluginManager } = useAppData();
|
||||||
|
const [collapsed, setCollapsed] = useState(false);
|
||||||
|
|
||||||
const initialInfo = (useModel && useModel('@@initialState')) || {
|
const initialInfo = (useModel && useModel('@@initialState')) || {
|
||||||
initialState: undefined,
|
initialState: undefined,
|
||||||
@@ -94,7 +95,6 @@ export default (props: any) => {
|
|||||||
|
|
||||||
const userConfig = {
|
const userConfig = {
|
||||||
title: '',
|
title: '',
|
||||||
layout: 'mix',
|
|
||||||
locale: true
|
locale: true
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -145,12 +145,18 @@ export default (props: any) => {
|
|||||||
location={location}
|
location={location}
|
||||||
title={userConfig.title}
|
title={userConfig.title}
|
||||||
navTheme="light"
|
navTheme="light"
|
||||||
|
layout="side"
|
||||||
|
disableMobile={true}
|
||||||
siderWidth={220}
|
siderWidth={220}
|
||||||
onMenuHeaderClick={(e) => {
|
onMenuHeaderClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
navigate('/');
|
navigate('/');
|
||||||
}}
|
}}
|
||||||
|
collapsed={collapsed}
|
||||||
|
onCollapse={(collapsed) => {
|
||||||
|
setCollapsed(collapsed);
|
||||||
|
}}
|
||||||
onPageChange={(route) => {
|
onPageChange={(route) => {
|
||||||
const { location } = history;
|
const { location } = history;
|
||||||
|
|
||||||
@@ -178,9 +184,8 @@ export default (props: any) => {
|
|||||||
}}
|
}}
|
||||||
formatMessage={formatMessage}
|
formatMessage={formatMessage}
|
||||||
menu={{ locale: true }}
|
menu={{ locale: true }}
|
||||||
logo={Logo}
|
logo={collapsed ? SLogoIcon : LogoIcon}
|
||||||
menuItemRender={(menuItemProps, defaultDom) => {
|
menuItemRender={(menuItemProps, defaultDom) => {
|
||||||
console.log('meurender=========', { defaultDom });
|
|
||||||
if (menuItemProps.isUrl || menuItemProps.children) {
|
if (menuItemProps.isUrl || menuItemProps.children) {
|
||||||
return defaultDom;
|
return defaultDom;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
import {
|
||||||
|
AppstoreOutlined,
|
||||||
|
MailOutlined,
|
||||||
|
SettingOutlined
|
||||||
|
} from '@ant-design/icons';
|
||||||
|
import { Menu, type MenuProps } from 'antd';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
type MenuItem = Required<MenuProps>['items'][number];
|
||||||
|
const items: MenuItem[] = [
|
||||||
|
{
|
||||||
|
key: 'sub1',
|
||||||
|
icon: <MailOutlined />,
|
||||||
|
label: 'Navigation One',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
key: '1-1',
|
||||||
|
label: 'Item 1',
|
||||||
|
type: 'group',
|
||||||
|
children: [
|
||||||
|
{ key: '1', label: 'Option 1' },
|
||||||
|
{ key: '2', label: 'Option 2' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '1-2',
|
||||||
|
label: 'Item 2',
|
||||||
|
type: 'group',
|
||||||
|
children: [
|
||||||
|
{ key: '3', label: 'Option 3' },
|
||||||
|
{ key: '4', label: 'Option 4' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'sub2',
|
||||||
|
icon: <AppstoreOutlined />,
|
||||||
|
label: 'Navigation Two',
|
||||||
|
children: [
|
||||||
|
{ key: '5', label: 'Option 5' },
|
||||||
|
{ key: '6', label: 'Option 6' },
|
||||||
|
{
|
||||||
|
key: 'sub3',
|
||||||
|
label: 'Submenu'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'sub4',
|
||||||
|
label: 'Navigation Three',
|
||||||
|
icon: <SettingOutlined />,
|
||||||
|
children: [
|
||||||
|
{ key: '9', label: 'Option 9' },
|
||||||
|
{ key: '10', label: 'Option 10' },
|
||||||
|
{ key: '11', label: 'Option 11' },
|
||||||
|
{ key: '12', label: 'Option 12' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const RenderLinks: React.FC = () => {
|
||||||
|
return <Menu mode="vertical" items={items} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default React.memo(RenderLinks);
|
||||||
@@ -62,6 +62,7 @@ export function getRightRenderContent(opts: {
|
|||||||
<SelectLang
|
<SelectLang
|
||||||
style={{ padding: '10px 18px', marginBottom: '20px' }}
|
style={{ padding: '10px 18px', marginBottom: '20px' }}
|
||||||
reload={false}
|
reload={false}
|
||||||
|
mode="vertical"
|
||||||
icon={
|
icon={
|
||||||
<span style={{ fontSize: '12px' }}>
|
<span style={{ fontSize: '12px' }}>
|
||||||
<GlobalOutlined />
|
<GlobalOutlined />
|
||||||
@@ -113,18 +114,6 @@ export function getRightRenderContent(opts: {
|
|||||||
history.push('/profile');
|
history.push('/profile');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// key: 'theme',
|
|
||||||
// label: (
|
|
||||||
// <>
|
|
||||||
// <SunOutlined />
|
|
||||||
// 外观
|
|
||||||
// </>
|
|
||||||
// ),
|
|
||||||
// onClick: () => {
|
|
||||||
// console.log('theme');
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
key: 'logout',
|
key: 'logout',
|
||||||
label: (
|
label: (
|
||||||
@@ -158,7 +147,9 @@ export function getRightRenderContent(opts: {
|
|||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// 需要 antd 4.20.0 以上版本
|
// 需要 antd 4.20.0 以上版本
|
||||||
dropdownProps = { overlay: <Menu {...langMenu} mode="vertical" /> };
|
dropdownProps = {
|
||||||
|
overlay: <Menu {...langMenu} mode="vertical" />
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -186,5 +186,6 @@ export default {
|
|||||||
'common.filter.name': 'Filter by name',
|
'common.filter.name': 'Filter by name',
|
||||||
'common.form.password': 'Password',
|
'common.form.password': 'Password',
|
||||||
'common.form.username': 'Username',
|
'common.form.username': 'Username',
|
||||||
'common.login.rember': 'Remember me'
|
'common.login.rember': 'Remember me',
|
||||||
|
'settings.company': 'SEAL'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -179,5 +179,6 @@ export default {
|
|||||||
'common.filter.name': '名称查询',
|
'common.filter.name': '名称查询',
|
||||||
'common.form.password': '密码',
|
'common.form.password': '密码',
|
||||||
'common.form.username': '用户名',
|
'common.form.username': '用户名',
|
||||||
'common.login.rember': '记住我'
|
'common.login.rember': '记住我',
|
||||||
|
'settings.company': '数澈软件'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ const LoginForm = () => {
|
|||||||
<div>
|
<div>
|
||||||
<Form
|
<Form
|
||||||
form={form}
|
form={form}
|
||||||
style={{ width: '400px', margin: '0 auto', paddingTop: '10%' }}
|
style={{ width: '400px', margin: '0 auto' }}
|
||||||
onFinish={handleLogin}
|
onFinish={handleLogin}
|
||||||
>
|
>
|
||||||
{renderWelCome(intl)}
|
{renderWelCome(intl)}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ const PasswordForm: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
<Form
|
<Form
|
||||||
form={form}
|
form={form}
|
||||||
style={{ width: '400px', margin: '0 auto', paddingTop: '5%' }}
|
style={{ width: '400px', margin: '0 auto' }}
|
||||||
onFinish={handleSubmit}
|
onFinish={handleSubmit}
|
||||||
>
|
>
|
||||||
<h2 className="justify-center m-b-20 flex-column flex-center">
|
<h2 className="justify-center m-b-20 flex-column flex-center">
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
:local(.login-form-wrapper) {
|
||||||
|
margin: 0 auto;
|
||||||
|
border-radius: 8px;
|
||||||
|
width: max-content;
|
||||||
|
height: max-content;
|
||||||
|
padding: 32px;
|
||||||
|
background-color: rgba(255, 255, 255, 50%);
|
||||||
|
|
||||||
|
:global(.field-wrapper) {
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:local(.box) {
|
||||||
|
padding-top: 10%;
|
||||||
|
}
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
import { userAtom } from '@/atoms/user';
|
import { userAtom } from '@/atoms/user';
|
||||||
|
import Footer from '@/components/footer';
|
||||||
import { history } from '@umijs/max';
|
import { history } from '@umijs/max';
|
||||||
import { useAtom } from 'jotai';
|
import { useAtom } from 'jotai';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import LoginForm from './components/login-form';
|
import LoginForm from './components/login-form';
|
||||||
import PasswordForm from './components/password-form';
|
import PasswordForm from './components/password-form';
|
||||||
|
import styles from './components/styles.less';
|
||||||
|
|
||||||
const Login = () => {
|
const Login = () => {
|
||||||
const [userInfo, setUserInfo] = useAtom(userAtom);
|
const [userInfo, setUserInfo] = useAtom(userAtom);
|
||||||
@@ -21,9 +23,15 @@ const Login = () => {
|
|||||||
}, [userInfo]);
|
}, [userInfo]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="login-wrapper">
|
<>
|
||||||
{userInfo?.require_password_change ? <PasswordForm /> : <LoginForm />}
|
<div className="login-wrapper"></div>
|
||||||
</div>
|
<div className={styles.box}>
|
||||||
|
<div className={styles['login-form-wrapper']}>
|
||||||
|
{userInfo?.require_password_change ? <PasswordForm /> : <LoginForm />}
|
||||||
|
</div>
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ import AddModal from './components/add-modal';
|
|||||||
import { FormData, ListItem } from './config/types';
|
import { FormData, ListItem } from './config/types';
|
||||||
const { Column } = Table;
|
const { Column } = Table;
|
||||||
|
|
||||||
const Models: React.FC = () => {
|
const Users: React.FC = () => {
|
||||||
|
console.log('users======');
|
||||||
const rowSelection = useTableRowSelection();
|
const rowSelection = useTableRowSelection();
|
||||||
const { sortOrder, setSortOrder } = useTableSort({
|
const { sortOrder, setSortOrder } = useTableSort({
|
||||||
defaultSortOrder: 'descend'
|
defaultSortOrder: 'descend'
|
||||||
@@ -328,4 +329,4 @@ const Models: React.FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Models;
|
export default Users;
|
||||||
|
|||||||
Reference in New Issue
Block a user