feat: 注册登录界面框架搭建完成

This commit is contained in:
landaiqing
2024-04-15 22:55:47 +08:00
parent 9dfcc7d7db
commit 0b7d88b74e
14 changed files with 1408 additions and 365 deletions

View File

@@ -1,243 +0,0 @@
import {
GithubOutlined,
GitlabOutlined,
LockOutlined,
MobileOutlined,
QqOutlined,
UserOutlined,
WechatOutlined,
} from '@ant-design/icons'
import {
LoginFormPage,
ProFormCaptcha,
ProFormCheckbox,
ProFormText,
} from '@ant-design/pro-components'
import { Divider, Space, Tabs, message, Button } from 'antd'
import { CSSProperties } from 'react'
import { useState } from 'react'
import logo from '@/assets/icons/schisandra.svg'
import background from '@/assets/images/background.png'
import { observer } from 'mobx-react'
// import useStore from '@/utils/store/useStore.tsx'
type LoginType = 'account' | 'phone'
const iconStyles: CSSProperties = {
color: 'rgba(0, 0, 0, 0.2)',
fontSize: '18px',
verticalAlign: 'middle',
cursor: 'pointer',
}
export default observer(() => {
// const store = useStore('user')
const items = [
{ label: '账户密码登录', key: 'account' },
{ label: '手机号登录', key: 'phone' },
]
const [loginType, setLoginType] = useState<LoginType>('account')
const onSubmit = async (formData: object) => {
console.log(formData)
}
return (
<div
style={{
backgroundColor: 'white',
height: '100vh',
width: '100vw',
}}>
<LoginFormPage
onFinish={onSubmit}
backgroundImageUrl={background}
logo={logo}
title='五味子云相册'
subTitle='随时随地分享你的美好瞬间'
activityConfig={{
style: {
boxShadow: '0px 0px 8px rgba(0, 0, 0, 0.2)',
color: '#fff',
borderRadius: 8,
backgroundColor: '#1677FF',
},
title: '活动标题,可配置图片',
subTitle: '活动介绍说明文字',
action: (
<Button
size='large'
style={{
borderRadius: 20,
background: '#fff',
color: '#1677FF',
width: 120,
}}>
</Button>
),
}}
actions={
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
flexDirection: 'column',
}}>
<Divider plain>
<span style={{ color: '#CCC', fontWeight: 'normal', fontSize: 14 }}>
</span>
</Divider>
<Space align='center' size={24}>
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
flexDirection: 'column',
height: 40,
width: 40,
border: '1px solid #D4D8DD',
borderRadius: '50%',
}}>
<QqOutlined style={{ ...iconStyles, color: '#1677FF' }} />
</div>
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
flexDirection: 'column',
height: 40,
width: 40,
border: '1px solid #D4D8DD',
borderRadius: '50%',
}}>
<WechatOutlined style={{ ...iconStyles, color: '#08a327' }} />
</div>
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
flexDirection: 'column',
height: 40,
width: 40,
border: '1px solid #D4D8DD',
borderRadius: '50%',
}}>
<GithubOutlined style={{ ...iconStyles, color: '#333333' }} />
</div>
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
flexDirection: 'column',
height: 40,
width: 40,
border: '1px solid #D4D8DD',
borderRadius: '50%',
}}>
<GitlabOutlined style={{ ...iconStyles, color: '#FF6A10' }} />
</div>
</Space>
</div>
}>
<Tabs
centered
items={items}
activeKey={loginType}
onChange={(activeKey) => setLoginType(activeKey as LoginType)}></Tabs>
{loginType === 'account' && (
<>
<ProFormText
name='username'
fieldProps={{
size: 'large',
prefix: <UserOutlined className={'prefixIcon'} />,
}}
placeholder={'请输入账号/邮箱/电话号码'}
rules={[
{
required: true,
message: '请输入用户名!',
},
]}
/>
<ProFormText.Password
name='password'
fieldProps={{
size: 'large',
prefix: <LockOutlined className={'prefixIcon'} />,
}}
placeholder={'请输入密码'}
rules={[
{
required: true,
message: '请输入密码!',
},
]}
/>
</>
)}
{loginType === 'phone' && (
<>
<ProFormText
fieldProps={{
size: 'large',
prefix: <MobileOutlined className={'prefixIcon'} />,
}}
name='mobile'
placeholder={'手机号'}
rules={[
{
required: true,
message: '请输入手机号!',
},
{
pattern: /^1\d{10}$/,
message: '手机号格式错误!',
},
]}
/>
<ProFormCaptcha
fieldProps={{
size: 'large',
prefix: <LockOutlined className={'prefixIcon'} />,
}}
captchaProps={{
size: 'large',
}}
placeholder={'请输入验证码'}
captchaTextRender={(timing, count) => {
if (timing) {
return `${count} ${'获取验证码'}`
}
return '获取验证码'
}}
name='captcha'
rules={[
{
required: true,
message: '请输入验证码!',
},
]}
onGetCaptcha={async () => {
message.success('获取验证码成功验证码为1234')
}}
/>
</>
)}
<div style={{ marginBlockEnd: 24 }}>
<ProFormCheckbox noStyle name='autoLogin'>
</ProFormCheckbox>
<a style={{ float: 'right' }}> </a>
</div>
</LoginFormPage>
</div>
)
})

View File

@@ -0,0 +1,154 @@
.container {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
height: 100vh;
width: 100vw;
overflow: auto;
}
.login_content {
// margin-top: 40px;
// position: relative;
width: 782px;
background-color: rgb(255, 255, 255);
// height: 100%;
overflow: hidden;
box-shadow: rgb(0 0 0 / 15%) 0px 2px 15px;
//float: right;
//height: 538px;
position: relative;
padding: 30px 0 30px 0;
}
.go_to_register {
cursor: pointer;
height: 70px;
width: 70px;
background-image: url('@/assets/icons/corner_markers.svg');
position: absolute;
top: 0px;
right: 0px;
text-align: right;
}
.go_to_register span {
font-size: 14px;
color: rgb(255, 255, 255);
font-weight: 500;
position: relative;
top: 10px;
right: 7px;
}
.lang {
width: 100%;
height: 40px;
line-height: 44px;
text-align: right;
:global(.ant-dropdown-trigger) {
margin-right: 24px;
}
}
.content {
//flex: 1;
//padding: 32px 0;
//box-sizing: border-box;
//margin: 60px auto;
//display: flex;
//align-items: center;
//justify-content: center;
//flex-direction: column;
//height: 100vh;
//overflow: auto;
}
@media (min-width: 768px) {
.container {
background-image: url('@/assets/images/background.png');
background-size: cover;
}
.content {
padding: 32px 0 24px;
}
.content img {
text-align: center;
}
}
.icon {
margin-left: 8px;
color: rgba(0, 0, 0, 0.2);
font-size: 24px;
vertical-align: middle;
cursor: pointer;
transition: color 0.3s;
&:hover {
color: aquamarine;
}
}
.mp_code {
padding: 0px 60px;
width: 361px;
height: 490px;
background-color: rgb(255, 255, 255);
border-right: 0.5px solid rgb(196, 203, 215);
}
.mp_code_title {
margin-top: 50px;
font-weight: 500;
font-size: 24px;
color: rgb(24, 24, 24)
}
.mp_code_img {
margin-top: 10px;
}
.alert {
margin-top: 25px;
width: 250px;
padding: 5px;
text-align: left;
vertical-align: middle;
}
.login_form {
// height: 100%;
// float: left;
margin: 0;
padding: 0 32px;
}
.mp_tips {
font-weight: bold;
color: rgb(7, 221, 7);
}
.logo span {
position: relative;
top: 2px;
color: rgba(0, 0, 0, .85);
font-weight: 600;
font-size: 33px;
}
.subTitle {
margin-top: 12px;
margin-bottom: 10px;
color: rgba(0, 0, 0, .45);
font-size: 14px;
}

View File

@@ -0,0 +1,377 @@
import {
BarcodeOutlined,
GithubOutlined,
GitlabOutlined,
LockOutlined,
MobileOutlined,
QqOutlined,
UserOutlined,
WechatOutlined,
} from '@ant-design/icons'
import { ProFormCaptcha, ProFormCheckbox, ProFormText } from '@ant-design/pro-components'
import { Divider, Space, Tabs, message, Image, Alert, Form, Button } from 'antd'
import { CSSProperties } from 'react'
import { useState } from 'react'
import logo from '@/assets/icons/schisandra.svg'
import qrCode from '@/assets/images/login_qrcode-landaiqing.jpg'
import './index.less'
import { observer } from 'mobx-react'
// import useStore from '@/utils/store/useStore.tsx'
type LoginType = 'account' | 'phone'
const iconStyles: CSSProperties = {
color: 'rgba(0, 0, 0, 0.2)',
fontSize: '18px',
verticalAlign: 'middle',
cursor: 'pointer',
}
export default observer(() => {
const [generateMpRegCodeData, setGenerateMpRegCodeData] = useState<API.GenerateMpRegCode>({})
const [form] = Form.useForm()
const [base64Code, setBase64Code] = useState<API.GenerateBase64Code>({ data: '' })
const clickGetBase64CodeMethod = async () => {
await getBase64CodeMethod()
}
const CodeImg = (
<img
src={'data:image/jpg;base64,' + base64Code.data}
onClick={clickGetBase64CodeMethod}
title='点击刷新'
style={{ cursor: 'pointer', height: 40, width: 90 }}
/>
)
const getBase64CodeMethod = async () => {}
const items = [
{
label: (
<span>
<UserOutlined />
</span>
),
key: 'account',
},
{
label: (
<span>
<MobileOutlined />
</span>
),
key: 'phone',
},
]
const [loginType, setLoginType] = useState<LoginType>('account')
const onSubmit = async (formData: object) => {
console.log(formData)
}
return (
<div className={'container'}>
<div className={'content'}>
<Space>
<Space className={'login_content'}>
<Space align='center' className={'mp_code'}>
<Space direction='vertical' align='center'>
<span className={'mp_code_title'}></span>
<Image
preview={false}
height={210}
width={200}
className={'mp_code_img'}
// src={generateMpRegCodeData.data?.qrCodeUrl}
src={qrCode}
/>
<Alert
// message={(<span>微信扫码<span>关注公众号</span></span>)}
description={
<div>
<span>
<span className={'mp_tips'}></span>
</span>
<br />
</div>
}
// type="success"
showIcon={true}
className={'alert'}
icon={<WechatOutlined />}
/>
</Space>
</Space>
<Form
form={form}
className={'login_form'}
initialValues={{
autoLogin: true,
}}>
<Space direction='vertical' align='center'>
<Space className={'logo'}>
<img
alt='logo'
src={logo}
style={{ width: '44px', height: '44px' }}
/>
<span></span>
</Space>
<div className={'subTitle'}></div>
</Space>
<Tabs
centered={true}
items={items}
activeKey={loginType}
onChange={(activeKey) =>
setLoginType(activeKey as LoginType)
}></Tabs>
{loginType === 'account' && (
<>
<ProFormText
name='username'
fieldProps={{
size: 'large',
prefix: <UserOutlined className={'prefixIcon'} />,
}}
placeholder={'请输入账号/邮箱/电话号码'}
rules={[
{
required: true,
message: '请输入用户名!',
},
]}
/>
<ProFormText.Password
name='password'
fieldProps={{
size: 'large',
prefix: <LockOutlined className={'prefixIcon'} />,
}}
placeholder={'请输入密码'}
rules={[
{
required: true,
message: '请输入密码!',
},
{
pattern:
/^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z\\W]{6,18}$/,
message:
'密码长度需在6~18位字符且必须包含字母和数字',
},
]}
/>
<ProFormText
addonAfter={CodeImg}
name='code'
fieldProps={{
size: 'large',
prefix: <BarcodeOutlined className={'prefixIcon'} />,
autoComplete: 'off',
}}
placeholder='请输入图形验证码'
rules={[
{
required: true,
message: '请输入图形验证码!',
},
{
pattern: /^[a-zA-Z0-9]{5}$/,
message: '图形验证码格式不正确',
},
]}
/>
</>
)}
{loginType === 'phone' && (
<>
<ProFormText
fieldProps={{
size: 'large',
prefix: <MobileOutlined className={'prefixIcon'} />,
autoComplete: 'off',
}}
name='mobile'
placeholder={'请输入手机号'}
rules={[
{
required: true,
message: '请输入手机号!',
},
{
pattern: /^1\d{10}$/,
message: '手机号格式错误!',
},
]}
/>
<ProFormText
addonAfter={CodeImg}
name='code'
fieldProps={{
size: 'large',
prefix: <BarcodeOutlined className={'prefixIcon'} />,
autoComplete: 'off',
}}
placeholder='请输入图形验证码'
rules={[
{
required: true,
message: '请输入图形验证码!',
},
{
pattern: /^[a-zA-Z0-9]{5}$/,
message: '图形验证码格式不正确',
},
]}
/>
<ProFormCaptcha
fieldProps={{
size: 'large',
prefix: <LockOutlined className={'prefixIcon'} />,
}}
captchaProps={{
size: 'large',
}}
placeholder={'请输入验证码'}
captchaTextRender={(timing, count) => {
if (timing) {
return `${count} ${'获取验证码'}`
}
return '获取验证码'
}}
name='captcha'
rules={[
{
required: true,
message: '请输入验证码!',
},
]}
onGetCaptcha={async () => {
message.success('获取验证码成功验证码为1234')
}}
/>
</>
)}
<div style={{ marginBlockEnd: 14 }}>
<ProFormCheckbox noStyle name='autoLogin'>
</ProFormCheckbox>
<a style={{ float: 'right' }}> </a>
</div>
<Button
type='primary'
block
size='large'
onClick={async () => {
let validateFields
if (loginType === 'account') {
validateFields = ['username', 'password', 'code']
} else {
validateFields = ['mobile', 'captcha', 'code']
}
await form
.validateFields(validateFields)
.then(async (values) => {
await onSubmit(values as API.PhoneRegisterRequest)
})
.catch((errorInfo) => {
console.error(errorInfo)
})
}}>
</Button>
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
flexDirection: 'column',
}}>
<Divider plain>
<span
style={{
color: '#CCC',
fontWeight: 'normal',
fontSize: 14,
}}>
</span>
</Divider>
<Space align='center' size={24}>
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
flexDirection: 'column',
height: 40,
width: 40,
border: '1px solid #D4D8DD',
borderRadius: '50%',
}}>
<QqOutlined style={{ ...iconStyles, color: '#1677FF' }} />
</div>
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
flexDirection: 'column',
height: 40,
width: 40,
border: '1px solid #D4D8DD',
borderRadius: '50%',
}}>
<WechatOutlined
style={{ ...iconStyles, color: '#08a327' }}
/>
</div>
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
flexDirection: 'column',
height: 40,
width: 40,
border: '1px solid #D4D8DD',
borderRadius: '50%',
}}>
<GithubOutlined
style={{ ...iconStyles, color: '#333333' }}
/>
</div>
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
flexDirection: 'column',
height: 40,
width: 40,
border: '1px solid #D4D8DD',
borderRadius: '50%',
}}>
<GitlabOutlined
style={{ ...iconStyles, color: '#FF6A10' }}
/>
</div>
</Space>
</div>
</Form>
<a href='/register' className={'go_to_register'}>
<span></span>
</a>
</Space>
</Space>
</div>
</div>
)
})

View File

@@ -0,0 +1,154 @@
.container {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
height: 100vh;
width: 100vw;
overflow: auto;
}
.login_content {
// margin-top: 40px;
// position: relative;
width: 782px;
background-color: rgb(255, 255, 255);
// height: 100%;
overflow: hidden;
box-shadow: rgb(0 0 0 / 15%) 0px 2px 15px;
//float: right;
//height: 538px;
position: relative;
padding: 30px 0 30px 0;
}
.go_to_register {
cursor: pointer;
height: 70px;
width: 70px;
background-image: url('@/assets/icons/corner_markers.svg');
position: absolute;
top: 0px;
right: 0px;
text-align: right;
}
.go_to_register span {
font-size: 14px;
color: rgb(255, 255, 255);
font-weight: 500;
position: relative;
top: 10px;
right: 7px;
}
.lang {
width: 100%;
height: 40px;
line-height: 44px;
text-align: right;
:global(.ant-dropdown-trigger) {
margin-right: 24px;
}
}
.content {
//flex: 1;
//padding: 32px 0;
//box-sizing: border-box;
//margin: 60px auto;
//display: flex;
//align-items: center;
//justify-content: center;
//flex-direction: column;
//height: 100vh;
//overflow: auto;
}
@media (min-width: 768px) {
.container {
background-image: url('@/assets/images/background.png');
background-size: cover;
}
.content {
padding: 32px 0 24px;
}
.content img {
text-align: center;
}
}
.icon {
margin-left: 8px;
color: rgba(0, 0, 0, 0.2);
font-size: 24px;
vertical-align: middle;
cursor: pointer;
transition: color 0.3s;
&:hover {
color: aquamarine;
}
}
.mp_code {
padding: 0px 60px;
width: 361px;
height: 490px;
background-color: rgb(255, 255, 255);
border-right: 0.5px solid rgb(196, 203, 215);
}
.mp_code_title {
margin-top: 50px;
font-weight: 500;
font-size: 24px;
color: rgb(24, 24, 24)
}
.mp_code_img {
margin-top: 10px;
}
.alert {
margin-top: 25px;
width: 250px;
padding: 5px;
text-align: left;
vertical-align: middle;
}
.login_form {
// height: 100%;
// float: left;
margin: 0;
padding: 0 32px;
}
.mp_tips {
font-weight: bold;
color: rgb(7, 221, 7);
}
.logo span {
position: relative;
top: 2px;
color: rgba(0, 0, 0, .85);
font-weight: 600;
font-size: 33px;
}
.subTitle {
margin-top: 12px;
margin-bottom: 10px;
color: rgba(0, 0, 0, .45);
font-size: 14px;
}

View File

@@ -0,0 +1,256 @@
import { BarcodeOutlined, LockOutlined, MobileOutlined, WechatOutlined } from '@ant-design/icons'
import { ProFormCaptcha, ProFormText } from '@ant-design/pro-components'
import { Space, Tabs, message, Image, Alert, Form, Button } from 'antd'
import { useState } from 'react'
import logo from '@/assets/icons/schisandra.svg'
// import background from '@/assets/images/background.png'
import qrCode from '@/assets/images/login_qrcode-landaiqing.jpg'
import './index.less'
import { observer } from 'mobx-react'
// import useStore from '@/utils/store/useStore.tsx'
type LoginType = 'phone'
export default observer(() => {
const [generateMpRegCodeData, setGenerateMpRegCodeData] = useState<API.GenerateMpRegCode>({})
const [form] = Form.useForm()
const [base64Code, setBase64Code] = useState<API.GenerateBase64Code>({ data: '' })
const clickGetBase64CodeMethod = async () => {
await getBase64CodeMethod()
}
const CodeImg = (
<img
src={'data:image/jpg;base64,' + base64Code.data}
onClick={clickGetBase64CodeMethod}
title='点击刷新'
style={{ cursor: 'pointer', height: 40, width: 90 }}
/>
)
const getBase64CodeMethod = async () => {}
const items = [
{
key: 'phone',
label: (
<span>
<MobileOutlined />
</span>
),
},
]
const [loginType, setLoginType] = useState<LoginType>('phone')
const onSubmit = async (formData: object) => {
console.log(formData)
}
return (
<div className={'container'}>
<div className={'content'}>
<Space>
<Space className={'login_content'}>
<Space align='center' className={'mp_code'}>
<Space direction='vertical' align='center'>
<span className={'mp_code_title'}></span>
<Image
preview={false}
height={210}
width={200}
className={'mp_code_img'}
// src={generateMpRegCodeData.data?.qrCodeUrl}
src={qrCode}
/>
<Alert
// message={(<span>微信扫码<span>关注公众号</span></span>)}
description={
<div>
<span>
<span className={'mp_tips'}></span>
</span>
<br />
</div>
}
// type="success"
showIcon={true}
className={'alert'}
icon={<WechatOutlined />}
/>
</Space>
</Space>
<Form
form={form}
className={'login_form'}
initialValues={{
autoLogin: true,
}}>
<Space direction='vertical' align='center'>
<Space className={'logo'}>
<img
alt='logo'
src={logo}
style={{ width: '44px', height: '44px' }}
/>
<span></span>
</Space>
<div className={'subTitle'}></div>
</Space>
<Tabs
centered={true}
items={items}
activeKey={loginType}
onChange={(activeKey) =>
setLoginType(activeKey as LoginType)
}></Tabs>
<>
<ProFormText
fieldProps={{
size: 'large',
prefix: <MobileOutlined className={'prefixIcon'} />,
autoComplete: 'off',
}}
name='phone'
placeholder='请输入手机号!'
rules={[
{
required: true,
message: '请输入手机号!',
},
{
pattern: /^1\d{10}$/,
message: '手机号格式错误!',
},
]}
/>
<ProFormText.Password
name='password'
fieldProps={{
size: 'large',
prefix: <LockOutlined className={'prefixIcon'} />,
}}
placeholder='请输入密码'
rules={[
{
required: true,
message: '请输入密码!',
},
{
pattern:
/^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z\\W]{6,18}$/,
message:
'密码长度需在6~18位字符且必须包含字母和数字',
},
]}
/>
<ProFormText.Password
name='confirmPassword'
dependencies={['password']}
fieldProps={{
size: 'large',
prefix: <LockOutlined className={'prefixIcon'} />,
}}
placeholder='请再次确认密码'
rules={[
{
required: true,
message: '请再次确认密码!',
},
({ getFieldValue }) => ({
validator(_, value) {
if (!value || getFieldValue('password') === value) {
return Promise.resolve()
}
return Promise.reject(
new Error('两次输入的密码不一致!'),
)
},
}),
]}
/>
<ProFormText
addonAfter={CodeImg}
name='code'
fieldProps={{
size: 'large',
prefix: <BarcodeOutlined className={'prefixIcon'} />,
autoComplete: 'off',
}}
placeholder='请输入图形验证码'
rules={[
{
required: true,
message: '请输入图形验证码!',
},
{
pattern: /^[a-zA-Z0-9]{5}$/,
message: '图形验证码格式不正确!',
},
]}
/>
<ProFormCaptcha
fieldProps={{
size: 'large',
prefix: <LockOutlined className={'prefixIcon'} />,
}}
captchaProps={{
size: 'large',
}}
placeholder={'请输入验证码'}
captchaTextRender={(timing, count) => {
if (timing) {
return `${count} ${'获取验证码'}`
}
return '获取验证码'
}}
name='captcha'
rules={[
{
required: true,
message: '请输入验证码!',
},
]}
onGetCaptcha={async () => {
message.success('获取验证码成功验证码为1234')
}}
/>
</>
<Button
type='primary'
block
size='large'
onClick={async () => {
const validateFields = [
'phone',
'username',
'password',
'captcha',
'code',
'confirmPassword',
]
await form
.validateFields(validateFields)
.then(async (values) => {
await onSubmit(values as API.PhoneRegisterRequest)
})
.catch((errorInfo) => {
console.error(errorInfo)
})
}}>
</Button>
</Form>
<a href='/' className={'go_to_register'}>
<span></span>
</a>
</Space>
</Space>
</div>
</div>
)
})