feat: 音乐人登录

main
fadeaway 4 months ago
parent ceeb490fb6
commit a761ea3d0d

@ -50,6 +50,13 @@ module.exports = {
'^/user/artist': '', '^/user/artist': '',
}, },
}, },
'/user/user': {
target: 'http://39.103.180.196:9012/user/user/',
changeOrigin: true,
pathRewrite: {
'^/user/user': '',
},
},
}; };
return config; return config;
}), }),

@ -1,9 +1,11 @@
import React, { memo } from 'react'; import React, { memo } from 'react';
import { useSelector } from 'react-redux';
import cs from 'classnames'; import cs from 'classnames';
import { Layout, Avatar } from '@arco-design/web-react'; import { Layout, Avatar } from '@arco-design/web-react';
import { IconUser } from '@arco-design/web-react/icon'; import { IconUser } from '@arco-design/web-react/icon';
import Logo from '@/assets/logo.svg'; import Logo from '@/assets/logo.svg';
import LogoBlack from '@/assets/logo_black.svg'; import LogoBlack from '@/assets/logo_black.svg';
import { GlobalState } from '@/store';
import styles from './style/index.module.less'; import styles from './style/index.module.less';
interface HeaderProps { interface HeaderProps {
@ -13,6 +15,7 @@ interface HeaderProps {
const LayoutHeader = Layout.Header; const LayoutHeader = Layout.Header;
function Header({ mode }: HeaderProps) { function Header({ mode }: HeaderProps) {
const userInfo = useSelector((state: GlobalState) => state.userInfo);
return ( return (
<LayoutHeader <LayoutHeader
className={cs( className={cs(
@ -25,12 +28,14 @@ function Header({ mode }: HeaderProps) {
{mode === 'dark' ? <Logo /> : <LogoBlack />} {mode === 'dark' ? <Logo /> : <LogoBlack />}
<h5 className={styles.brandName}></h5> <h5 className={styles.brandName}></h5>
</div> </div>
<div className={styles.avatar}> {!userInfo && (
<div className={styles.loginText}></div> <div className={styles.avatar}>
<Avatar size={32}> <div className={styles.loginText}></div>
<IconUser /> <Avatar size={32}>
</Avatar> <IconUser />
</div> </Avatar>
</div>
)}
</div> </div>
</LayoutHeader> </LayoutHeader>
); );

@ -0,0 +1,130 @@
import React, {
memo,
useState,
useCallback,
forwardRef,
useImperativeHandle,
} from 'react';
import cs from 'classnames';
import { Modal, Form, Input, Button, Message } from '@arco-design/web-react';
import { IconClose } from '@arco-design/web-react/icon';
import request from '@/utils/request';
import styles from './style/index.module.less';
export interface LoginModalProps {
onLoginSuccess?: (resData: any) => void;
}
const { useForm, useWatch, Item: FormItem } = Form;
const InputPassword = Input.Password;
function LoginModal(props: LoginModalProps, ref: React.Ref<any>) {
const { onLoginSuccess } = props;
const [visible, setVisible] = useState(false);
const [form] = useForm();
const userNameVal = useWatch('userName', form);
const passwordVal = useWatch('password', form);
const [submiting, setSubmiting] = useState(false);
const open = useCallback(() => {
setVisible(true);
}, []);
const close = useCallback(() => {
if (submiting) return;
setVisible(false);
form.resetFields();
}, [form, submiting]);
const handleLogin = () => {
form.validate().then(async (values) => {
setSubmiting(true);
const { userName, password } = values;
request
.post(`/user/user/login/${userName}/${password}`, values)
.then((res: any) => {
if (res?.code === 200 && res?.data?.token) {
Message.success('登录成功');
localStorage.setItem('token', res?.data?.token);
onLoginSuccess?.(res);
}
})
.finally(() => {
setSubmiting(false);
});
});
};
const handleProtocal = async () => {
// TODO:
};
useImperativeHandle(ref, () => ({ open }), [open]);
return (
<Modal
className={styles.loginModal}
visible={visible}
simple
autoFocus={true}
focusLock={true}
escToExit={false}
maskClosable={false}
onOk={() => setVisible(false)}
onCancel={() => setVisible(false)}
footer={null}
>
<section className={styles.loginModalCont}>
<IconClose className={styles.closeIcon} onClick={close} />
<h3 className={styles.loginTitle}></h3>
<p className={styles.loginTip}>
</p>
<Form form={form}>
<FormItem
field="userName"
rules={[{ required: true, message: '请输入账户名称' }]}
>
<Input
className={cs(styles.formInp, styles.formInpUserName)}
placeholder="输入账户名称"
minLength={5}
maxLength={32}
/>
</FormItem>
<FormItem
field="password"
rules={[{ required: true, message: '请输入密码' }]}
className={styles.pwdWrap}
>
<InputPassword
className={styles.formInp}
placeholder="输入密码"
minLength={4}
maxLength={16}
/>
</FormItem>
<FormItem className={styles.loginBtnWrap}>
<Button
className={styles.loginBtn}
type="primary"
disabled={!userNameVal || !passwordVal}
loading={submiting}
onClick={handleLogin}
>
</Button>
</FormItem>
</Form>
<p className={styles.loginDesc}>
<span className={styles.protocal} onClick={handleProtocal}>
</span>
</p>
</section>
</Modal>
);
}
export default memo(forwardRef(LoginModal));

@ -0,0 +1,111 @@
.loginModal {
width: 420px;
// height: 438px;
background: #f2f3f7;
border-radius: 18px;
padding: 24px;
}
.loginModalCont {
width: 100%;
height: 100%;
padding-top: 24px;
padding-left: 16px;
padding-right: 16px;
padding-bottom: 4px;
box-sizing: border-box;
}
.closeIcon {
position: absolute;
top: 0;
right: 0;
width: 24px;
height: 24px;
cursor: pointer;
}
.closeIcon:hover {
opacity: 0.8;
}
.loginTitle {
margin-top: 0;
margin-bottom: 6px;
width: 48px;
height: 34px;
top: 334px;
left: 694px;
font-family: PingFang SC;
font-size: 24px;
font-weight: 600;
line-height: 33.6px;
text-align: left;
color: #1d2129;
}
.loginTip {
height: 18px;
font-family: PingFang SC;
font-size: 13px;
font-weight: 400;
line-height: 18.2px;
color: #4e5969;
margin-top: 0;
margin-bottom: 30px;
}
.pwdWrap {
padding-top: 4px;
}
.formInp {
width: 340px;
height: 50px;
border-radius: 2px;
box-sizing: border-box;
&.formInpUserName,
:global(.arco-input-inner-wrapper) {
background-color: #fff;
padding-left: 24px;
padding-right: 24px;
}
:global(.arco-input-inner-wrapper svg) {
width: 24px;
height: 24px;
color: rgba(#4e5969, 0.5);
}
}
.loginBtnWrap {
padding-top: 10px;
margin-bottom: 0;
}
.loginBtn {
width: 340px;
height: 50px;
border-radius: 2px;
}
.loginDesc {
line-height: 18px;
font-family: PingFang SC;
font-size: 13px;
font-weight: 400;
line-height: 18.2px;
text-align: center;
color: #00000066;
margin-top: 50px;
margin-bottom: 0;
}
.protocal {
font-family: PingFang SC;
font-size: 13px;
font-weight: 400;
line-height: 18.2px;
text-align: center;
color: #000000f2;
}

@ -1,5 +1,5 @@
import './style/global.less'; import './style/global.less';
import React, { useEffect } from 'react'; import React, { useEffect, useMemo } from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { createStore } from 'redux'; import { createStore } from 'redux';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
@ -13,7 +13,6 @@ import PageLayout from './layout';
import { GlobalContext } from './context'; import { GlobalContext } from './context';
import Login from './pages/login'; import Login from './pages/login';
import Home from './pages/home'; import Home from './pages/home';
import SettleIn from './pages/settle-in';
import checkLogin from './utils/checkLogin'; import checkLogin from './utils/checkLogin';
import changeTheme from './utils/changeTheme'; import changeTheme from './utils/changeTheme';
import useStorage from './utils/useStorage'; import useStorage from './utils/useStorage';
@ -24,6 +23,7 @@ const store = createStore(rootReducer);
function Index() { function Index() {
const [lang, setLang] = useStorage('arco-lang', 'en-US'); const [lang, setLang] = useStorage('arco-lang', 'en-US');
const [theme, setTheme] = useStorage('arco-theme', 'light'); const [theme, setTheme] = useStorage('arco-theme', 'light');
const isLogin = useMemo(() => checkLogin(), []);
function getArcoLocale() { function getArcoLocale() {
switch (lang) { switch (lang) {
@ -89,8 +89,7 @@ function Index() {
<Switch> <Switch>
{/* <Route path="/login" component={Login} /> */} {/* <Route path="/login" component={Login} /> */}
{/* <Route path="/" component={PageLayout} /> */} {/* <Route path="/" component={PageLayout} /> */}
<Route path="/settle-in" component={SettleIn} /> <Route path="/" component={isLogin ? PageLayout : Home} />
<Route path="/" component={Home} />
</Switch> </Switch>
</GlobalContext.Provider> </GlobalContext.Provider>
</Provider> </Provider>

@ -1,7 +1,8 @@
import React from 'react'; import React, { useRef } from 'react';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import { Layout, Typography, Button } from '@arco-design/web-react'; import { Layout, Typography, Button } from '@arco-design/web-react';
import Header from '@/components/Header'; import Header from '@/components/Header';
import LoginModal from '@/components/LoginModal';
import styles from './style/index.module.less'; import styles from './style/index.module.less';
const Content = Layout.Content; const Content = Layout.Content;
@ -9,9 +10,14 @@ const { Title, Paragraph } = Typography;
function Home() { function Home() {
const history = useHistory(); const history = useHistory();
const loginModalRef = useRef(null);
const handleClick = () => { const handleClick = () => {
history.push('/settle-in'); loginModalRef?.current?.open();
};
const handleLoginSuccess = () => {
window.location.reload();
}; };
return ( return (
@ -28,10 +34,11 @@ function Home() {
className={styles.btn} className={styles.btn}
onClick={handleClick} onClick={handleClick}
> >
</Button> </Button>
</section> </section>
</Content> </Content>
<LoginModal ref={loginModalRef} onLoginSuccess={handleLoginSuccess} />
</Layout> </Layout>
); );
} }

@ -1,3 +1,3 @@
export default function checkLogin() { export default function checkLogin() {
return localStorage.getItem('userStatus') === 'login'; return !!localStorage.getItem('token');
} }

@ -0,0 +1,61 @@
import axios from 'axios';
// 创建axios实例
const request = axios.create({
// baseURL: process.env.APP_BASE_API, // api的base_url
baseURL: '', // api的base_url
timeout: 30000, // 请求超时时间
});
// 请求拦截器
request.interceptors.request.use(
(config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers['Authorization'] = `Bearer ${token}`;
}
return config;
},
(error) => {
// 请求错误处理
console.log(error); // for debug
Promise.reject(error);
}
);
// 响应拦截器
request.interceptors.response.use(
(response) => {
// 对响应数据做处理例如只返回data部分
const res = response.data;
// if (res.code !== 20000) {
// Message({
// message: res.message,
// type: 'error',
// duration: 5 * 1000
// });
// // 50001: 非法的token; 50002: 其他客户端错误; 50003: 认证失败; 50004: 授权失败; 50005: 未找到用户
// if (res.code === 50001 || res.code === 50002 || res.code === 50003 || res.code === 50004 || res.code === 50005) {
// // 移除token
// resetToken();
// // 跳转到登录页面
// location.reload();
// }
// return Promise.reject('error');
// } else {
// return res;
// }
return res;
},
(error) => {
console.log('err' + error); // for debug
// Message({
// message: error.message,
// type: 'error',
// duration: 5 * 1000
// });
return Promise.reject(error);
}
);
export default request;
Loading…
Cancel
Save