| @@ -78,7 +78,6 @@ export default defineConfig({ | |||||
| */ | */ | ||||
| title: '智能材料科研平台', | title: '智能材料科研平台', | ||||
| layout: { | layout: { | ||||
| locale: false, | |||||
| ...defaultSettings, | ...defaultSettings, | ||||
| }, | }, | ||||
| // keepalive: [/./], | // keepalive: [/./], | ||||
| @@ -7,6 +7,7 @@ const Settings: ProLayoutProps & { | |||||
| pwa?: boolean; | pwa?: boolean; | ||||
| logo?: string; | logo?: string; | ||||
| } = { | } = { | ||||
| locale: 'zh-CN', | |||||
| navTheme: 'light', | navTheme: 'light', | ||||
| // 拂晓蓝 | // 拂晓蓝 | ||||
| colorPrimary: '#1664ff', | colorPrimary: '#1664ff', | ||||
| @@ -18,7 +19,6 @@ const Settings: ProLayoutProps & { | |||||
| colorWeak: false, | colorWeak: false, | ||||
| title: '智能材料科研平台', | title: '智能材料科研平台', | ||||
| pwa: true, | pwa: true, | ||||
| logo: '/assets/images/left-top-logo.png', | |||||
| token: { | token: { | ||||
| // 参见ts声明,demo 见文档,通过token 修改样式 | // 参见ts声明,demo 见文档,通过token 修改样式 | ||||
| //https://procomponents.ant.design/components/layout#%E9%80%9A%E8%BF%87-token-%E4%BF%AE%E6%94%B9%E6%A0%B7%E5%BC%8F | //https://procomponents.ant.design/components/layout#%E9%80%9A%E8%BF%87-token-%E4%BF%AE%E6%94%B9%E6%A0%B7%E5%BC%8F | ||||
| @@ -146,6 +146,7 @@ export const layout: RuntimeConfig['layout'] = ({ initialState }) => { | |||||
| }, | }, | ||||
| }, | }, | ||||
| ...initialState?.settings, | ...initialState?.settings, | ||||
| logo: require('@/assets/img/logo.png'), | |||||
| token: { | token: { | ||||
| sider: { | sider: { | ||||
| colorTextMenu: themes['textColor'], | colorTextMenu: themes['textColor'], | ||||
| @@ -1,7 +1,7 @@ | |||||
| .kf-modal { | .kf-modal { | ||||
| .ant-modal-content { | .ant-modal-content { | ||||
| padding: 40px 67px; | padding: 40px 67px; | ||||
| background-image: url(/assets/images/modal-back.png); | |||||
| background-image: url(@/assets/img/modal-back.png); | |||||
| background-repeat: no-repeat; | background-repeat: no-repeat; | ||||
| background-position: top center; | background-position: top center; | ||||
| background-size: 100%; | background-size: 100%; | ||||
| @@ -148,3 +148,7 @@ ol { | |||||
| left: 0; | left: 0; | ||||
| z-index: 999; | z-index: 999; | ||||
| } | } | ||||
| input:-webkit-autofill { | |||||
| transition: background-color 5000s ease-in-out 0s; | |||||
| } | |||||
| @@ -126,7 +126,7 @@ | |||||
| .ant-modal-confirm { | .ant-modal-confirm { | ||||
| .ant-modal-content { | .ant-modal-content { | ||||
| padding: 40px 67px; | padding: 40px 67px; | ||||
| background-image: url(/assets/images/modal-back.png); | |||||
| background-image: url(@/assets/img/modal-back.png); | |||||
| background-repeat: no-repeat; | background-repeat: no-repeat; | ||||
| background-position: top center; | background-position: top center; | ||||
| background-size: 100%; | background-size: 100%; | ||||
| @@ -6,7 +6,7 @@ | |||||
| height: 110px; | height: 110px; | ||||
| margin-bottom: 10px; | margin-bottom: 10px; | ||||
| padding: 20px 30px 0; | padding: 20px 30px 0; | ||||
| background-image: url(/assets/images/dataset-back.png); | |||||
| background-image: url(@/assets/img/dataset-intro-top.png); | |||||
| background-repeat: no-repeat; | background-repeat: no-repeat; | ||||
| background-position: top center; | background-position: top center; | ||||
| background-size: 100% 100%; | background-size: 100% 100%; | ||||
| @@ -84,7 +84,7 @@ function EditorCreate() { | |||||
| return ( | return ( | ||||
| <div className={styles['editor-create']}> | <div className={styles['editor-create']}> | ||||
| <PageTitle title="创建编辑器"></PageTitle> | |||||
| <PageTitle title="创建开发环境"></PageTitle> | |||||
| <div className={styles['editor-create__content']}> | <div className={styles['editor-create__content']}> | ||||
| <div> | <div> | ||||
| <Form | <Form | ||||
| @@ -96,6 +96,7 @@ function EditorCreate() { | |||||
| initialValues={{ computing_resource: ComputingResourceType.GPU }} | initialValues={{ computing_resource: ComputingResourceType.GPU }} | ||||
| onFinish={handleSubmit} | onFinish={handleSubmit} | ||||
| size="large" | size="large" | ||||
| autoComplete="off" | |||||
| > | > | ||||
| <SubAreaTitle | <SubAreaTitle | ||||
| title="基本信息" | title="基本信息" | ||||
| @@ -213,7 +213,7 @@ function EditorList() { | |||||
| icon={<KFIcon type="icon-tiaoshi" />} | icon={<KFIcon type="icon-tiaoshi" />} | ||||
| onClick={() => startEditor(record.id)} | onClick={() => startEditor(record.id)} | ||||
| > | > | ||||
| 再次调试 | |||||
| 启动 | |||||
| </Button> | </Button> | ||||
| )} | )} | ||||
| <ConfigProvider | <ConfigProvider | ||||
| @@ -247,7 +247,7 @@ function EditorList() { | |||||
| onClick={createEditor} | onClick={createEditor} | ||||
| icon={<KFIcon type="icon-xinjian2" />} | icon={<KFIcon type="icon-xinjian2" />} | ||||
| > | > | ||||
| 创建编辑器 | |||||
| 创建开发环境 | |||||
| </Button> | </Button> | ||||
| <Button | <Button | ||||
| style={{ marginLeft: '20px' }} | style={{ marginLeft: '20px' }} | ||||
| @@ -27,7 +27,7 @@ | |||||
| width: 100%; | width: 100%; | ||||
| height: calc(100% - 56px); | height: calc(100% - 56px); | ||||
| background-color: @background-color; | background-color: @background-color; | ||||
| background-image: url(/assets/images/pipeline-canvas-back.png); | |||||
| background-image: url(@/assets/img/pipeline-canvas-bg.png); | |||||
| background-size: 100% 100%; | background-size: 100% 100%; | ||||
| } | } | ||||
| @@ -49,7 +49,10 @@ function ExperimentParameter({ nodeData }: ExperimentParameterProps) { | |||||
| className={styles['experiment-parameter']} | className={styles['experiment-parameter']} | ||||
| > | > | ||||
| <div className={styles['experiment-parameter__title']}> | <div className={styles['experiment-parameter__title']}> | ||||
| <SubAreaTitle image="/assets/images/static-message.png" title="基本信息"></SubAreaTitle> | |||||
| <SubAreaTitle | |||||
| image={require('@/assets/img/static-message.png')} | |||||
| title="基本信息" | |||||
| ></SubAreaTitle> | |||||
| </div> | </div> | ||||
| <Form.Item | <Form.Item | ||||
| label="任务名称" | label="任务名称" | ||||
| @@ -76,7 +79,10 @@ function ExperimentParameter({ nodeData }: ExperimentParameterProps) { | |||||
| <Input disabled /> | <Input disabled /> | ||||
| </Form.Item> | </Form.Item> | ||||
| <div className={styles['experiment-parameter__title']}> | <div className={styles['experiment-parameter__title']}> | ||||
| <SubAreaTitle image="/assets/images/duty-message.png" title="任务信息"></SubAreaTitle> | |||||
| <SubAreaTitle | |||||
| image={require('@/assets/img/duty-message.png')} | |||||
| title="任务信息" | |||||
| ></SubAreaTitle> | |||||
| </div> | </div> | ||||
| <Form.Item | <Form.Item | ||||
| label="镜像" | label="镜像" | ||||
| @@ -128,7 +134,10 @@ function ExperimentParameter({ nodeData }: ExperimentParameterProps) { | |||||
| </Form.Item> | </Form.Item> | ||||
| ))} | ))} | ||||
| <div className={styles['experiment-parameter__title']}> | <div className={styles['experiment-parameter__title']}> | ||||
| <SubAreaTitle image="/assets/images/duty-message.png" title="输入参数"></SubAreaTitle> | |||||
| <SubAreaTitle | |||||
| image={require('@/assets/img/duty-message.png')} | |||||
| title="输入参数" | |||||
| ></SubAreaTitle> | |||||
| </div> | </div> | ||||
| {inParametersList.map((item) => ( | {inParametersList.map((item) => ( | ||||
| <Form.Item | <Form.Item | ||||
| @@ -145,7 +154,10 @@ function ExperimentParameter({ nodeData }: ExperimentParameterProps) { | |||||
| </Form.Item> | </Form.Item> | ||||
| ))} | ))} | ||||
| <div className={styles['experiment-parameter__title']}> | <div className={styles['experiment-parameter__title']}> | ||||
| <SubAreaTitle image="/assets/images/duty-message.png" title="输出参数"></SubAreaTitle> | |||||
| <SubAreaTitle | |||||
| image={require('@/assets/img/duty-message.png')} | |||||
| title="输出参数" | |||||
| ></SubAreaTitle> | |||||
| </div> | </div> | ||||
| {outParametersList.map((item) => ( | {outParametersList.map((item) => ( | ||||
| <Form.Item | <Form.Item | ||||
| @@ -8,7 +8,7 @@ | |||||
| height: 49px; | height: 49px; | ||||
| margin-bottom: 10px; | margin-bottom: 10px; | ||||
| padding-right: 30px; | padding-right: 30px; | ||||
| background-image: url(/assets/images/pipeline-back.png); | |||||
| background-image: url(@/assets/img/page-title-bg.png); | |||||
| background-repeat: no-repeat; | background-repeat: no-repeat; | ||||
| background-position: top center; | background-position: top center; | ||||
| background-size: 100% 100%; | background-size: 100% 100%; | ||||
| @@ -11,41 +11,41 @@ export const experimentStatusInfo: Record<ExperimentStatus, ExperimentStatusInfo | |||||
| Running: { | Running: { | ||||
| label: '运行中', | label: '运行中', | ||||
| color: themes.primaryColor, | color: themes.primaryColor, | ||||
| icon: '/assets/images/running-icon.png', | |||||
| icon: '/assets/images/experiment-status/running-icon.png', | |||||
| }, | }, | ||||
| Succeeded: { | Succeeded: { | ||||
| label: '成功', | label: '成功', | ||||
| color: themes.successColor, | color: themes.successColor, | ||||
| icon: '/assets/images/success-icon.png', | |||||
| icon: '/assets/images/experiment-status/success-icon.png', | |||||
| }, | }, | ||||
| Pending: { | Pending: { | ||||
| label: '等待中', | label: '等待中', | ||||
| color: themes.pendingColor, | color: themes.pendingColor, | ||||
| icon: '/assets/images/pending-icon.png', | |||||
| icon: '/assets/images/experiment-status/pending-icon.png', | |||||
| }, | }, | ||||
| Failed: { | Failed: { | ||||
| label: '失败', | label: '失败', | ||||
| color: themes.errorColor, | color: themes.errorColor, | ||||
| icon: '/assets/images/fail-icon.png', | |||||
| icon: '/assets/images/experiment-status/fail-icon.png', | |||||
| }, | }, | ||||
| Error: { | Error: { | ||||
| label: '错误', | label: '错误', | ||||
| color: themes.errorColor, | color: themes.errorColor, | ||||
| icon: '/assets/images/fail-icon.png', | |||||
| icon: '/assets/images/experiment-status/fail-icon.png', | |||||
| }, | }, | ||||
| Terminated: { | Terminated: { | ||||
| label: '终止', | label: '终止', | ||||
| color: themes.abortColor, | color: themes.abortColor, | ||||
| icon: '/assets/images/omitted-icon.png', | |||||
| icon: '/assets/images/experiment-status/omitted-icon.png', | |||||
| }, | }, | ||||
| Skipped: { | Skipped: { | ||||
| label: '未执行', | label: '未执行', | ||||
| color: themes.abortColor, | color: themes.abortColor, | ||||
| icon: '/assets/images/omitted-icon.png', | |||||
| icon: '/assets/images/experiment-status/omitted-icon.png', | |||||
| }, | }, | ||||
| Omitted: { | Omitted: { | ||||
| label: '未执行', | label: '未执行', | ||||
| color: themes.abortColor, | color: themes.abortColor, | ||||
| icon: '/assets/images/omitted-icon.png', | |||||
| icon: '/assets/images/experiment-status/omitted-icon.png', | |||||
| }, | }, | ||||
| }; | }; | ||||
| @@ -136,6 +136,7 @@ function MirrorCreate() { | |||||
| initialValues={{ upload_type: CommonTabKeys.Public }} | initialValues={{ upload_type: CommonTabKeys.Public }} | ||||
| onFinish={handleSubmit} | onFinish={handleSubmit} | ||||
| size="large" | size="large" | ||||
| autoComplete="off" | |||||
| > | > | ||||
| <SubAreaTitle | <SubAreaTitle | ||||
| title="基本信息" | title="基本信息" | ||||
| @@ -12,7 +12,7 @@ | |||||
| &__graph { | &__graph { | ||||
| height: calc(100% - 92px); | height: calc(100% - 92px); | ||||
| background-color: @background-color; | background-color: @background-color; | ||||
| background-image: url(/assets/images/pipeline-canvas-back.png); | |||||
| background-image: url(@/assets/img/pipeline-canvas-bg.png); | |||||
| background-size: 100% 100%; | background-size: 100% 100%; | ||||
| } | } | ||||
| } | } | ||||
| @@ -141,6 +141,7 @@ function ModelDeploymentCreate() { | |||||
| initialValues={{ upload_type: CommonTabKeys.Public }} | initialValues={{ upload_type: CommonTabKeys.Public }} | ||||
| onFinish={handleSubmit} | onFinish={handleSubmit} | ||||
| size="large" | size="large" | ||||
| autoComplete="off" | |||||
| > | > | ||||
| <SubAreaTitle | <SubAreaTitle | ||||
| title="基本信息" | title="基本信息" | ||||
| @@ -23,7 +23,7 @@ | |||||
| width: 100%; | width: 100%; | ||||
| height: calc(100% - 52px); | height: calc(100% - 52px); | ||||
| background-color: @background-color; | background-color: @background-color; | ||||
| background-image: url(/assets/images/pipeline-canvas-back.png); | |||||
| background-image: url(@/assets/img/pipeline-canvas-bg.png); | |||||
| background-size: 100% 100%; | background-size: 100% 100%; | ||||
| } | } | ||||
| } | } | ||||
| @@ -299,7 +299,10 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete | |||||
| scrollToFirstError | scrollToFirstError | ||||
| > | > | ||||
| <div className={styles['pipeline-drawer__title']}> | <div className={styles['pipeline-drawer__title']}> | ||||
| <SubAreaTitle image="/assets/images/static-message.png" title="基本信息"></SubAreaTitle> | |||||
| <SubAreaTitle | |||||
| image={require('@/assets/img/static-message.png')} | |||||
| title="基本信息" | |||||
| ></SubAreaTitle> | |||||
| </div> | </div> | ||||
| <Form.Item | <Form.Item | ||||
| label="任务名称" | label="任务名称" | ||||
| @@ -326,7 +329,10 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete | |||||
| <Input disabled /> | <Input disabled /> | ||||
| </Form.Item> | </Form.Item> | ||||
| <div className={styles['pipeline-drawer__title']}> | <div className={styles['pipeline-drawer__title']}> | ||||
| <SubAreaTitle image="/assets/images/duty-message.png" title="任务信息"></SubAreaTitle> | |||||
| <SubAreaTitle | |||||
| image={require('@/assets/img/duty-message.png')} | |||||
| title="任务信息" | |||||
| ></SubAreaTitle> | |||||
| </div> | </div> | ||||
| <Form.Item label="镜像" required> | <Form.Item label="镜像" required> | ||||
| <div className={styles['pipeline-drawer__ref-row']}> | <div className={styles['pipeline-drawer__ref-row']}> | ||||
| @@ -436,7 +442,10 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete | |||||
| </Form.Item> | </Form.Item> | ||||
| ))} | ))} | ||||
| <div className={styles['pipeline-drawer__title']}> | <div className={styles['pipeline-drawer__title']}> | ||||
| <SubAreaTitle image="/assets/images/duty-message.png" title="输入参数"></SubAreaTitle> | |||||
| <SubAreaTitle | |||||
| image={require('@/assets/img/duty-message.png')} | |||||
| title="输入参数" | |||||
| ></SubAreaTitle> | |||||
| </div> | </div> | ||||
| {inParametersList.map((item) => ( | {inParametersList.map((item) => ( | ||||
| <Form.Item | <Form.Item | ||||
| @@ -469,7 +478,10 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete | |||||
| </Form.Item> | </Form.Item> | ||||
| ))} | ))} | ||||
| <div className={styles['pipeline-drawer__title']}> | <div className={styles['pipeline-drawer__title']}> | ||||
| <SubAreaTitle image="/assets/images/duty-message.png" title="输出参数"></SubAreaTitle> | |||||
| <SubAreaTitle | |||||
| image={require('@/assets/img/duty-message.png')} | |||||
| title="输出参数" | |||||
| ></SubAreaTitle> | |||||
| </div> | </div> | ||||
| {outParametersList.map((item) => ( | {outParametersList.map((item) => ( | ||||
| <Form.Item | <Form.Item | ||||
| @@ -6,7 +6,7 @@ | |||||
| height: 49px; | height: 49px; | ||||
| margin-bottom: 10px; | margin-bottom: 10px; | ||||
| padding-right: 30px; | padding-right: 30px; | ||||
| background-image: url(/assets/images/pipeline-back.png); | |||||
| background-image: url(@/assets/img/page-title-bg.png); | |||||
| background-repeat: no-repeat; | background-repeat: no-repeat; | ||||
| background-position: top left; | background-position: top left; | ||||
| background-size: 100% 100%; | background-size: 100% 100%; | ||||
| @@ -1,187 +1,48 @@ | |||||
| import { clearSessionToken, setSessionToken } from '@/access'; | import { clearSessionToken, setSessionToken } from '@/access'; | ||||
| import { getFakeCaptcha } from '@/services/ant-design-pro/login'; | |||||
| import { getCaptchaImg, login } from '@/services/system/auth'; | import { getCaptchaImg, login } from '@/services/system/auth'; | ||||
| import { | |||||
| AlipayCircleOutlined, | |||||
| LockOutlined, | |||||
| MobileOutlined, | |||||
| TaobaoCircleOutlined, | |||||
| UserOutlined, | |||||
| WeiboCircleOutlined, | |||||
| } from '@ant-design/icons'; | |||||
| import { | |||||
| LoginForm, | |||||
| ProFormCaptcha, | |||||
| ProFormCheckbox, | |||||
| ProFormText, | |||||
| } from '@ant-design/pro-components'; | |||||
| import { useEmotionCss } from '@ant-design/use-emotion-css'; | |||||
| import { FormattedMessage, SelectLang, history, useIntl, useModel } from '@umijs/max'; | |||||
| import { Alert, Col, Image, Row, message } from 'antd'; | |||||
| import { loginPasswordKey, loginUserKey, rememberPasswordKey } from '@/utils/localStorage'; | |||||
| import { to } from '@/utils/promise'; | |||||
| import { history, useModel } from '@umijs/max'; | |||||
| import { Button, Checkbox, Flex, Form, Image, Input, message } from 'antd'; | |||||
| import React, { useEffect, useState } from 'react'; | import React, { useEffect, useState } from 'react'; | ||||
| import { flushSync } from 'react-dom'; | import { flushSync } from 'react-dom'; | ||||
| import styles from './login.less'; | import styles from './login.less'; | ||||
| const ActionIcons = () => { | |||||
| const langClassName = useEmotionCss(({ token }) => { | |||||
| return { | |||||
| marginLeft: '8px', | |||||
| color: 'rgba(0, 0, 0, 0.2)', | |||||
| fontSize: '24px', | |||||
| verticalAlign: 'middle', | |||||
| cursor: 'pointer', | |||||
| transition: 'color 0.3s', | |||||
| '&:hover': { | |||||
| color: token.colorPrimaryActive, | |||||
| }, | |||||
| }; | |||||
| }); | |||||
| return ( | |||||
| <> | |||||
| <AlipayCircleOutlined key="AlipayCircleOutlined" className={langClassName} /> | |||||
| <TaobaoCircleOutlined key="TaobaoCircleOutlined" className={langClassName} /> | |||||
| <WeiboCircleOutlined key="WeiboCircleOutlined" className={langClassName} /> | |||||
| </> | |||||
| ); | |||||
| }; | |||||
| const Lang = () => { | |||||
| const langClassName = useEmotionCss(({ token }) => { | |||||
| return { | |||||
| width: 42, | |||||
| height: 42, | |||||
| lineHeight: '42px', | |||||
| position: 'fixed', | |||||
| right: 16, | |||||
| borderRadius: token.borderRadius, | |||||
| ':hover': { | |||||
| backgroundColor: token.colorBgTextHover, | |||||
| }, | |||||
| }; | |||||
| }); | |||||
| const LoginInputPrefix = ({ icon }: { icon: string }) => { | |||||
| return ( | return ( | ||||
| <div className={langClassName} data-lang> | |||||
| {SelectLang && <SelectLang />} | |||||
| <div className={styles['login-input-prefix']}> | |||||
| <img className={styles['login-input-prefix__icon']} src={icon} alt="" /> | |||||
| <div className={styles['login-input-prefix__line']}></div> | |||||
| </div> | </div> | ||||
| ); | ); | ||||
| }; | }; | ||||
| const LoginMessage: React.FC<{ | |||||
| content: string; | |||||
| }> = ({ content }) => { | |||||
| return ( | |||||
| <Alert | |||||
| style={{ | |||||
| marginBottom: 24, | |||||
| }} | |||||
| message={content} | |||||
| type="error" | |||||
| showIcon | |||||
| /> | |||||
| ); | |||||
| }; | |||||
| const Login: React.FC = () => { | const Login: React.FC = () => { | ||||
| const [userLoginState, setUserLoginState] = useState<API.LoginResult>({ code: 200 }); | |||||
| const [type, setType] = useState<string>('account'); | |||||
| const { initialState, setInitialState } = useModel('@@initialState'); | const { initialState, setInitialState } = useModel('@@initialState'); | ||||
| const [captchaCode, setCaptchaCode] = useState<string>(''); | const [captchaCode, setCaptchaCode] = useState<string>(''); | ||||
| const [uuid, setUuid] = useState<string>(''); | const [uuid, setUuid] = useState<string>(''); | ||||
| const [form] = Form.useForm(); | |||||
| const [usernameReadOnly, setUsernameReadOnly] = useState<boolean>(true); | |||||
| const containerClassName = useEmotionCss(() => { | |||||
| return { | |||||
| display: 'flex', | |||||
| height: '100vh', | |||||
| backgroundColor: '#fff', | |||||
| backgroundSize: '100% 100%', | |||||
| }; | |||||
| }); | |||||
| const containerLeftBox = useEmotionCss(() => { | |||||
| return { | |||||
| background: 'linear-gradient(180deg,#e2ecff 0%,#f6fafe 100%)', | |||||
| width: '43%', | |||||
| height: '100%', | |||||
| position: 'relative', | |||||
| }; | |||||
| }); | |||||
| const leftTopBoX = useEmotionCss(() => { | |||||
| return { | |||||
| display: 'flex', | |||||
| position: 'absolute', | |||||
| top: '55px', | |||||
| left: '60px', | |||||
| fontWeight: '500', | |||||
| color: '#1d1d20', | |||||
| fontSize: '36px', | |||||
| fontFamily: 'Alibaba', | |||||
| }; | |||||
| }); | |||||
| const centerTitleBoX = useEmotionCss(() => { | |||||
| return { | |||||
| display: 'flex', | |||||
| position: 'absolute', | |||||
| top: '163px', | |||||
| left: '50%', | |||||
| transform: 'translateX(-50%)', | |||||
| fontWeight: '500', | |||||
| color: '#111111', | |||||
| fontSize: '45px', | |||||
| fontFamily: 'Alibaba', | |||||
| }; | |||||
| }); | |||||
| const centerMessage = useEmotionCss(() => { | |||||
| return { | |||||
| display: 'flex', | |||||
| position: 'absolute', | |||||
| top: '242px', | |||||
| left: '50%', | |||||
| transform: 'translateX(-50%)', | |||||
| fontWeight: '500', | |||||
| color: '#606b7a', | |||||
| fontSize: '26px', | |||||
| fontFamily: 'Alibaba', | |||||
| letterSpacing: '8px', | |||||
| }; | |||||
| }); | |||||
| const containerRightBox = useEmotionCss(() => { | |||||
| return { | |||||
| background: '#fff', | |||||
| width: '57%', | |||||
| height: '100%', | |||||
| position: 'relative', | |||||
| }; | |||||
| }); | |||||
| const rightTopTitle = useEmotionCss(() => { | |||||
| return { | |||||
| display: 'flex', | |||||
| position: 'absolute', | |||||
| alignItems: 'center', | |||||
| top: '147px', | |||||
| left: '230px', | |||||
| fontFamily: 'Alibaba', | |||||
| }; | |||||
| }); | |||||
| const containerLoginForm = useEmotionCss(() => { | |||||
| return { | |||||
| width: '640px', | |||||
| position: 'absolute', | |||||
| background: '#ffffff', | |||||
| borderRadius: '10px', | |||||
| boxShadow: '0px 3px 20px rgba(153, 153, 153, 0.16)', | |||||
| padding: '62px 36px 45px 36px', | |||||
| left: '230px', | |||||
| top: '220px', | |||||
| }; | |||||
| }); | |||||
| const intl = useIntl(); | |||||
| useEffect(() => { | |||||
| getCaptchaCode(); | |||||
| const autoLogin = localStorage.getItem(rememberPasswordKey) ?? 'false'; | |||||
| if (autoLogin === 'true') { | |||||
| const username = localStorage.getItem(loginUserKey); | |||||
| const password = localStorage.getItem(loginPasswordKey); | |||||
| form.setFieldsValue({ username: username, password: password, autoLogin: true }); | |||||
| } else { | |||||
| form.setFieldsValue({ username: '', password: '', autoLogin: false }); | |||||
| } | |||||
| }, []); | |||||
| const getCaptchaCode = async () => { | const getCaptchaCode = async () => { | ||||
| const response = await getCaptchaImg(); | |||||
| const imgdata = `data:image/png;base64,${response.img}`; | |||||
| setCaptchaCode(imgdata); | |||||
| setUuid(response.uuid); | |||||
| const [res] = await to(getCaptchaImg()); | |||||
| if (res) { | |||||
| const imgdata = `data:image/png;base64,${res.img}`; | |||||
| setCaptchaCode(imgdata); | |||||
| setUuid(res.uuid); | |||||
| } | |||||
| }; | }; | ||||
| const fetchUserInfo = async () => { | const fetchUserInfo = async () => { | ||||
| @@ -196,302 +57,134 @@ const Login: React.FC = () => { | |||||
| } | } | ||||
| }; | }; | ||||
| // 登录 | |||||
| const handleSubmit = async (values: API.LoginParams) => { | const handleSubmit = async (values: API.LoginParams) => { | ||||
| try { | |||||
| // 登录 | |||||
| const response = await login({ ...values, uuid }); | |||||
| if (response.code === 200) { | |||||
| const defaultLoginSuccessMessage = intl.formatMessage({ | |||||
| id: 'pages.login.success', | |||||
| defaultMessage: '登录成功!', | |||||
| }); | |||||
| const current = new Date(); | |||||
| const expireTime = current.setTime(current.getTime() + 1000 * 12 * 60 * 60); | |||||
| setSessionToken(response.data?.access_token, response.data?.access_token, expireTime); | |||||
| message.success(defaultLoginSuccessMessage); | |||||
| await fetchUserInfo(); | |||||
| const urlParams = new URL(window.location.href).searchParams; | |||||
| history.push(urlParams.get('redirect') || '/'); | |||||
| return; | |||||
| const [response] = await to(login({ ...values, uuid })); | |||||
| if (response && response.data) { | |||||
| const current = new Date(); | |||||
| const expireTime = current.setTime(current.getTime() + 1000 * 12 * 60 * 60); | |||||
| const { access_token } = response.data; | |||||
| setSessionToken(access_token, access_token, expireTime); | |||||
| message.success('登录成功!'); | |||||
| localStorage.setItem(rememberPasswordKey, values.autoLogin ? 'true' : 'false'); | |||||
| if (values.autoLogin) { | |||||
| localStorage.setItem(loginUserKey, values.username ?? ''); | |||||
| localStorage.setItem(loginPasswordKey, values.password ?? ''); | |||||
| } else { | } else { | ||||
| clearSessionToken(); | |||||
| // 如果失败去设置用户错误信息 | |||||
| setUserLoginState({ ...response, type }); | |||||
| getCaptchaCode(); | |||||
| localStorage.removeItem(loginUserKey); | |||||
| localStorage.removeItem(loginPasswordKey); | |||||
| } | } | ||||
| } catch (error) { | |||||
| await fetchUserInfo(); | |||||
| const urlParams = new URL(window.location.href).searchParams; | |||||
| history.push(urlParams.get('redirect') || '/'); | |||||
| } else { | |||||
| clearSessionToken(); | |||||
| getCaptchaCode(); | getCaptchaCode(); | ||||
| } | } | ||||
| }; | }; | ||||
| const { code } = userLoginState; | |||||
| const loginType = type; | |||||
| useEffect(() => { | |||||
| getCaptchaCode(); | |||||
| }, []); | |||||
| return ( | return ( | ||||
| <div className={containerClassName}> | |||||
| <div className={containerLeftBox}> | |||||
| <div className={leftTopBoX}> | |||||
| <div className={styles['user-login']}> | |||||
| <div className={styles['user-login__left']}> | |||||
| <div className={styles['user-login__left__top']}> | |||||
| <img | <img | ||||
| src="/assets/images/left-top-logo.png" | |||||
| style={{ height: '42px', marginRight: '10px' }} | |||||
| src={require('@/assets/img/logo.png')} | |||||
| style={{ width: '32px', marginRight: '12px' }} | |||||
| alt="" | alt="" | ||||
| /> | /> | ||||
| 智能材料科研平台 | 智能材料科研平台 | ||||
| </div> | </div> | ||||
| <div className={centerTitleBoX}> | |||||
| <span style={{ whiteSpace: 'nowrap' }}>智能材料科研平台</span> | |||||
| <div className={styles['user-login__left__title']}> | |||||
| <span>智能材料科研平台</span> | |||||
| <img | <img | ||||
| src="/assets/images/ai-logo.png" | |||||
| style={{ height: '47px', marginTop: '-10px' }} | |||||
| src={require('@/assets/img/login-ai-logo.png')} | |||||
| className={styles['user-login__left__title__img']} | |||||
| alt="" | alt="" | ||||
| /> | /> | ||||
| </div> | </div> | ||||
| <div className={centerMessage}> | |||||
| <span style={{ whiteSpace: 'nowrap' }}>大语言模型运维 统一管理平台</span> | |||||
| <div className={styles['user-login__left__message']}> | |||||
| <span>大语言模型运维 统一管理平台</span> | |||||
| </div> | </div> | ||||
| <img | <img | ||||
| src="/assets/images/left-back-logo.png" | |||||
| style={{ | |||||
| width: '90%', | |||||
| position: 'absolute', | |||||
| top: '326px', | |||||
| left: '50%', | |||||
| transform: 'translateX(-50%)', | |||||
| }} | |||||
| className={styles['user-login__left__bottom-img']} | |||||
| src={require('@/assets/img/login-left-image.png')} | |||||
| alt="" | alt="" | ||||
| /> | /> | ||||
| </div> | </div> | ||||
| <div className={containerRightBox}> | |||||
| <div className={rightTopTitle}> | |||||
| <span style={{ color: '#111111', fontSize: '36px' }}>hello~</span> | |||||
| <span style={{ color: '#606b7a', fontSize: '32px', marginLeft: '10px' }}>欢迎登陆</span> | |||||
| <span style={{ color: '#1664ff', fontSize: '32px' }}>智能材料科研平台</span> | |||||
| </div> | |||||
| <div className={containerLoginForm}> | |||||
| <div | |||||
| style={{ | |||||
| color: '#1d1d20', | |||||
| fontSize: '22px', | |||||
| marginLeft: '30px', | |||||
| fontFamily: 'Alibaba', | |||||
| }} | |||||
| > | |||||
| 账号登录 | |||||
| <div className={styles['user-login__right']}> | |||||
| <div> | |||||
| <div className={styles['user-login__right__title']}> | |||||
| <span style={{ color: '#111111' }}>欢迎登录</span> | |||||
| <span>智能材料科研平台</span> | |||||
| </div> | </div> | ||||
| <div className={styles.loginForm}> | |||||
| <LoginForm | |||||
| title="" | |||||
| initialValues={{ | |||||
| autoLogin: true, | |||||
| }} | |||||
| // actions={[ | |||||
| // <FormattedMessage | |||||
| // key="loginWith" | |||||
| // id="pages.login.loginWith" | |||||
| // defaultMessage="其他登录方式" | |||||
| // />, | |||||
| // <ActionIcons key="icons" />, | |||||
| // ]} | |||||
| onFinish={async (values) => { | |||||
| await handleSubmit(values as API.LoginParams); | |||||
| }} | |||||
| > | |||||
| {code !== 200 && loginType === 'account' && ( | |||||
| <LoginMessage | |||||
| content={intl.formatMessage({ | |||||
| id: 'pages.login.accountLogin.errorMessage', | |||||
| defaultMessage: '账户或密码错误(admin/admin123)', | |||||
| })} | |||||
| /> | |||||
| )} | |||||
| {type === 'account' && ( | |||||
| <> | |||||
| <ProFormText | |||||
| name="username" | |||||
| initialValue="admin" | |||||
| fieldProps={{ | |||||
| size: 'large', | |||||
| prefix: <UserOutlined />, | |||||
| }} | |||||
| placeholder={intl.formatMessage({ | |||||
| id: 'pages.login.username.placeholder', | |||||
| defaultMessage: '用户名: admin', | |||||
| })} | |||||
| rules={[ | |||||
| { | |||||
| required: true, | |||||
| message: ( | |||||
| <FormattedMessage | |||||
| id="pages.login.username.required" | |||||
| defaultMessage="请输入用户名!" | |||||
| /> | |||||
| ), | |||||
| }, | |||||
| ]} | |||||
| /> | |||||
| <ProFormText.Password | |||||
| name="password" | |||||
| initialValue="admin123" | |||||
| fieldProps={{ | |||||
| size: 'large', | |||||
| prefix: <LockOutlined />, | |||||
| }} | |||||
| placeholder={intl.formatMessage({ | |||||
| id: 'pages.login.password.placeholder', | |||||
| defaultMessage: '请输入密码', | |||||
| })} | |||||
| rules={[ | |||||
| { | |||||
| required: true, | |||||
| message: ( | |||||
| <FormattedMessage | |||||
| id="pages.login.password.required" | |||||
| defaultMessage="请输入密码!" | |||||
| /> | |||||
| ), | |||||
| }, | |||||
| ]} | |||||
| <div className={styles['user-login__right__content']}> | |||||
| <div className={styles['user-login__right__content__title']}>账号登录</div> | |||||
| <div className={styles['user-login__right__content__form']}> | |||||
| <Form | |||||
| labelCol={{ span: 0 }} | |||||
| wrapperCol={{ span: 24 }} | |||||
| initialValues={{ autoLogin: true }} | |||||
| onFinish={handleSubmit} | |||||
| autoComplete="off" | |||||
| form={form} | |||||
| > | |||||
| <Form.Item name="username" rules={[{ required: true, message: '请输入用户名' }]}> | |||||
| <Input | |||||
| placeholder="请输入用户名" | |||||
| prefix={<LoginInputPrefix icon={require('@/assets/img/login-user.png')} />} | |||||
| allowClear | |||||
| readOnly={usernameReadOnly} | |||||
| onFocus={() => setUsernameReadOnly(false)} | |||||
| /> | /> | ||||
| <Row> | |||||
| <Col flex={4}> | |||||
| <ProFormText | |||||
| style={{ | |||||
| float: 'right', | |||||
| }} | |||||
| name="code" | |||||
| placeholder={intl.formatMessage({ | |||||
| id: 'pages.login.captcha.placeholder', | |||||
| defaultMessage: '请输入验证', | |||||
| })} | |||||
| rules={[ | |||||
| { | |||||
| required: true, | |||||
| message: ( | |||||
| <FormattedMessage | |||||
| id="pages.login.captcha.placeholder" | |||||
| defaultMessage="请输入验证啊" | |||||
| /> | |||||
| ), | |||||
| }, | |||||
| ]} | |||||
| /> | |||||
| </Col> | |||||
| <Col> | |||||
| <Image | |||||
| src={captchaCode} | |||||
| alt="验证码" | |||||
| style={{ | |||||
| display: 'inline-block', | |||||
| verticalAlign: 'top', | |||||
| cursor: 'pointer', | |||||
| paddingLeft: '22px', | |||||
| width: '170px', | |||||
| height: '66px', | |||||
| }} | |||||
| preview={false} | |||||
| onClick={() => getCaptchaCode()} | |||||
| /> | |||||
| </Col> | |||||
| </Row> | |||||
| </> | |||||
| )} | |||||
| </Form.Item> | |||||
| {code !== 200 && loginType === 'mobile' && <LoginMessage content="验证码错误" />} | |||||
| {type === 'mobile' && ( | |||||
| <> | |||||
| <ProFormText | |||||
| fieldProps={{ | |||||
| size: 'large', | |||||
| prefix: <MobileOutlined />, | |||||
| }} | |||||
| name="mobile" | |||||
| placeholder={intl.formatMessage({ | |||||
| id: 'pages.login.phoneNumber.placeholder', | |||||
| defaultMessage: '手机号', | |||||
| })} | |||||
| rules={[ | |||||
| { | |||||
| required: true, | |||||
| message: ( | |||||
| <FormattedMessage | |||||
| id="pages.login.phoneNumber.required" | |||||
| defaultMessage="请输入手机号!" | |||||
| /> | |||||
| ), | |||||
| }, | |||||
| { | |||||
| pattern: /^1\d{10}$/, | |||||
| message: ( | |||||
| <FormattedMessage | |||||
| id="pages.login.phoneNumber.invalid" | |||||
| defaultMessage="手机号格式错误!" | |||||
| /> | |||||
| ), | |||||
| }, | |||||
| ]} | |||||
| <Form.Item name="password" rules={[{ required: true, message: '请输入密码' }]}> | |||||
| <Input.Password | |||||
| placeholder="请输入密码" | |||||
| prefix={<LoginInputPrefix icon={require('@/assets/img/login-password.png')} />} | |||||
| allowClear | |||||
| /> | /> | ||||
| <ProFormCaptcha | |||||
| fieldProps={{ | |||||
| size: 'large', | |||||
| prefix: <LockOutlined />, | |||||
| }} | |||||
| captchaProps={{ | |||||
| size: 'large', | |||||
| }} | |||||
| placeholder={intl.formatMessage({ | |||||
| id: 'pages.login.captcha.placeholder', | |||||
| defaultMessage: '请输入验证码', | |||||
| })} | |||||
| captchaTextRender={(timing, count) => { | |||||
| if (timing) { | |||||
| return `${count} ${intl.formatMessage({ | |||||
| id: 'pages.getCaptchaSecondText', | |||||
| defaultMessage: '获取验证码', | |||||
| })}`; | |||||
| } | |||||
| return intl.formatMessage({ | |||||
| id: 'pages.login.phoneLogin.getVerificationCode', | |||||
| defaultMessage: '获取验证码', | |||||
| }); | |||||
| }} | |||||
| name="captcha" | |||||
| rules={[ | |||||
| { | |||||
| required: true, | |||||
| message: ( | |||||
| <FormattedMessage | |||||
| id="pages.login.captcha.required" | |||||
| defaultMessage="请输入验证码!" | |||||
| /> | |||||
| ), | |||||
| }, | |||||
| ]} | |||||
| onGetCaptcha={async (phone) => { | |||||
| const result = await getFakeCaptcha({ | |||||
| phone, | |||||
| }); | |||||
| if (!result) { | |||||
| return; | |||||
| } | |||||
| message.success('获取验证码成功!验证码为:1234'); | |||||
| }} | |||||
| </Form.Item> | |||||
| <Flex align="start" style={{ height: '98px' }}> | |||||
| <div style={{ flex: 1 }}> | |||||
| <Form.Item name="code" rules={[{ required: true, message: '请输入验证码' }]}> | |||||
| <Input | |||||
| placeholder="请输入验证码" | |||||
| prefix={ | |||||
| <LoginInputPrefix icon={require('@/assets/img/login-captcha.png')} /> | |||||
| } | |||||
| allowClear | |||||
| /> | |||||
| </Form.Item> | |||||
| </div> | |||||
| <Image | |||||
| className={styles['user-login__right__content__form__captcha']} | |||||
| src={captchaCode} | |||||
| alt="验证码" | |||||
| preview={false} | |||||
| onClick={() => getCaptchaCode()} | |||||
| /> | /> | ||||
| </> | |||||
| )} | |||||
| <div | |||||
| style={{ | |||||
| marginBottom: 24, | |||||
| }} | |||||
| > | |||||
| <ProFormCheckbox noStyle name="autoLogin"> | |||||
| <FormattedMessage id="pages.login.rememberMe" defaultMessage="记住密码" /> | |||||
| </ProFormCheckbox> | |||||
| </div> | |||||
| </LoginForm> | |||||
| </Flex> | |||||
| <Form.Item | |||||
| name="autoLogin" | |||||
| valuePropName="checked" | |||||
| labelCol={{ span: 0 }} | |||||
| wrapperCol={{ span: 16 }} | |||||
| > | |||||
| <Checkbox>记住密码</Checkbox> | |||||
| </Form.Item> | |||||
| <Form.Item labelCol={{ span: 0 }} wrapperCol={{ span: 24 }}> | |||||
| <Button type="primary" htmlType="submit"> | |||||
| 登录 | |||||
| </Button> | |||||
| </Form.Item> | |||||
| </Form> | |||||
| </div> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| @@ -1,31 +1,160 @@ | |||||
| .loginForm { | |||||
| :global { | |||||
| .ant-pro-form-login-main { | |||||
| width: auto !important; | |||||
| max-width: auto !important; | |||||
| margin: unset; | |||||
| .user-login { | |||||
| display: flex; | |||||
| height: 100vh; | |||||
| background-color: #fff; | |||||
| &__left { | |||||
| position: relative; | |||||
| width: 43%; | |||||
| height: 100%; | |||||
| padding-top: 56px; | |||||
| background: linear-gradient(180deg, #e2ecff 0%, #f6fafe 100%); | |||||
| &__top { | |||||
| display: flex; | |||||
| align-items: center; | |||||
| margin-left: 50px; | |||||
| color: @text-color; | |||||
| font-size: 30px; | |||||
| font-family: Alibaba; | |||||
| } | } | ||||
| .ant-input-affix-wrapper-lg { | |||||
| padding: 19px 11px; | |||||
| color: rgba(29, 29, 32, 0.6); | |||||
| font-size: 18px; | |||||
| font-family: 'Alibaba'; | |||||
| border-radius: 13px; | |||||
| &__title { | |||||
| position: relative; | |||||
| display: flex; | |||||
| justify-content: center; | |||||
| margin-top: 70px; | |||||
| margin-left: 85px; | |||||
| color: #111111; | |||||
| font-weight: 500; | |||||
| font-size: 45px; | |||||
| font-family: Alibaba; | |||||
| &__img { | |||||
| position: relative; | |||||
| top: -10px; | |||||
| width: 85px; | |||||
| height: 47px; | |||||
| } | |||||
| } | } | ||||
| .ant-input-affix-wrapper { | |||||
| padding: 19px 11px; | |||||
| color: rgba(29, 29, 32, 0.6); | |||||
| font-size: 18px; | |||||
| font-family: 'Alibaba'; | |||||
| border-radius: 13px; | |||||
| &__message { | |||||
| display: flex; | |||||
| justify-content: center; | |||||
| margin-top: 18px; | |||||
| color: #606b7a; | |||||
| font-size: 26px; | |||||
| font-family: Alibaba; | |||||
| } | } | ||||
| .ant-btn.ant-btn-lg { | |||||
| height: 76px; | |||||
| color: #ffffff; | |||||
| font-size: 20px; | |||||
| font-family: 'Alibaba'; | |||||
| background: @primary-color; | |||||
| border-radius: 41px; | |||||
| &__bottom-img { | |||||
| width: 100%; | |||||
| margin-top: 50px; | |||||
| } | } | ||||
| } | } | ||||
| &__right { | |||||
| position: relative; | |||||
| display: flex; | |||||
| flex-direction: column; | |||||
| align-items: center; | |||||
| justify-content: center; | |||||
| width: 57%; | |||||
| height: 100%; | |||||
| background: #fff; | |||||
| &__title { | |||||
| margin-bottom: 24px; | |||||
| color: @primary-color; | |||||
| font-size: 36px; | |||||
| font-family: Alibaba; | |||||
| } | |||||
| &__content { | |||||
| width: 640px; | |||||
| padding: 60px 60px 45px; | |||||
| background-color: white; | |||||
| border-radius: 10px; | |||||
| box-shadow: 0px 3px 20px rgba(153, 153, 153, 0.16); | |||||
| &__title { | |||||
| margin-bottom: 40px; | |||||
| color: @text-color; | |||||
| font-size: 22px; | |||||
| font-family: Alibaba; | |||||
| } | |||||
| &__form { | |||||
| &__captcha { | |||||
| width: 170px; | |||||
| height: 66px; | |||||
| padding-left: 22px; | |||||
| cursor: pointer; | |||||
| } | |||||
| :global { | |||||
| .ant-form-item { | |||||
| margin-bottom: 32px; | |||||
| &:last-child { | |||||
| margin-bottom: 0; | |||||
| } | |||||
| } | |||||
| .ant-input-affix-wrapper { | |||||
| padding: 10px 11px; | |||||
| color: @text-color; | |||||
| font-size: 18px !important; | |||||
| background-color: white; | |||||
| border: 1px solid #caced8; | |||||
| border-radius: 13px; | |||||
| .ant-input { | |||||
| height: 44px; | |||||
| font-size: 18px !important; | |||||
| } | |||||
| } | |||||
| .ant-btn { | |||||
| width: 100%; | |||||
| height: 76px; | |||||
| font-size: 20px; | |||||
| border-radius: 38px; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| input { | |||||
| font-size: 18px !important; | |||||
| } | |||||
| input:-webkit-autofill { | |||||
| font-size: 18px !important; | |||||
| transition: background-color 5000s ease-in-out 0s; | |||||
| -webkit-text-fill-color: @text-color !important; | |||||
| } | |||||
| input:-webkit-autofill::first-line { | |||||
| font-size: 18px !important; | |||||
| } | |||||
| } | |||||
| .login-input-prefix { | |||||
| display: flex; | |||||
| align-items: center; | |||||
| height: 44px; | |||||
| margin-right: 15px; | |||||
| &__icon { | |||||
| width: 24px; | |||||
| height: 26px; | |||||
| margin-right: 18px; | |||||
| margin-left: 14px; | |||||
| } | |||||
| &__line { | |||||
| width: 1px; | |||||
| height: 30px; | |||||
| background-color: #caced8; | |||||
| } | |||||
| } | } | ||||
| @@ -0,0 +1,6 @@ | |||||
| // 登录的用户名 | |||||
| export const loginUserKey = 'login-user'; | |||||
| // 登录的密码 | |||||
| export const loginPasswordKey = 'login-password'; | |||||
| // 记住密码 | |||||
| export const rememberPasswordKey = 'login-remember-password'; | |||||
| @@ -19,7 +19,7 @@ export function modalConfirm({ title, content, onOk, ...rest }: ModalFuncProps) | |||||
| title: ( | title: ( | ||||
| <div> | <div> | ||||
| <img | <img | ||||
| src="/assets/images/delete-icon.png" | |||||
| src={require('@/assets/img/delete-icon.png')} | |||||
| style={{ width: '120px', marginBottom: '24px' }} | style={{ width: '120px', marginBottom: '24px' }} | ||||
| alt="" | alt="" | ||||
| /> | /> | ||||