提交 588f2cd4 作者: ZhangLingKun

功能:用户注册登录

上级 6fcbd8e3
流水线 #8035 已失败 于阶段
in 18 秒
......@@ -105,3 +105,40 @@ export type TestPhoneLogin = InterFunction<
userAccountId: number;
}
>;
// 获取修改手机获取验证码
export type GetVerifyCodeType = InterFunction<
{
userAccountId: number;
phoneNum: string;
},
{}
>;
// web注册获取手机号验证码
export type GetVerifyCodeAuthType = InterFunction<
{
phoneNum: string;
},
{}
>;
// web端注册
export type WebRegisterType = InterFunction<
{
code: string;
password: string;
phoneNum: string;
},
{}
>;
// web端账号密码登录
export type WebLoginType = InterFunction<
{
accountNo: string;
passWord: string;
},
{
nickName: string;
phoneNum: string;
token: string;
userAccountId: number;
}
>;
......@@ -211,3 +211,8 @@ export type LeaseGoodsDetailsType = InterFunction<
modeOfDeliveryInfo: string;
}
>;
// 租赁-商品-租期信息
export type LeaseTermInfoType = InterFunction<
any,
{ id: number; leaseDate: string }[]
>;
......@@ -2,7 +2,11 @@ import {
GetAccountInfo,
GetAppletQRCode,
GetLoginInfo,
GetVerifyCodeAuthType,
GetVerifyCodeType,
TestPhoneLogin,
WebLoginType,
WebRegisterType,
} from '@/api/interface/common';
import request from '../request';
......@@ -30,4 +34,20 @@ export class CommonAPI {
// 测试-手机号登录
static testPhoneLogin: TestPhoneLogin = (params) =>
request.get('/userapp/auth/testPhoneLogin', { params });
// 获取修改手机获取验证码
static getVerifyCode: GetVerifyCodeType = (params) =>
request.get('/userapp/user-account/getVerifyCode', { params });
// web注册获取手机号验证码
static getVerifyCodeAuth: GetVerifyCodeAuthType = (params) =>
request.get('/userapp/auth/getVerifyCode', { params });
// web端注册
static webRegister: WebRegisterType = (params) =>
request.post('/userapp/auth/webRegister', params);
// web端账号密码登录
static webLogin: WebLoginType = (params) =>
request.post('/userapp/auth/webLogin', params);
}
......@@ -4,6 +4,7 @@ import {
AppMallGoodsDetails,
GetCompanyInfoByBUId,
LeaseGoodsDetailsType,
LeaseTermInfoType,
} from '@/api/interface/mall';
import request from '../request';
......@@ -27,4 +28,8 @@ export class MallAPI {
// 租赁商品详情
static leaseGoodsDetails: LeaseGoodsDetailsType = (params) =>
request.get('/pms/app/lease/leaseGoodsDetails', { params });
// 租赁-商品-租期信息
static getLeaseTermInfo: LeaseTermInfoType = () =>
request.post('/pms/lease/goods/getLeaseTermInfo');
}
......@@ -4,7 +4,7 @@ import {
LogoutOutlined,
ReloadOutlined,
} from '@ant-design/icons';
import { Button, Dropdown, MenuProps, Modal } from 'antd';
import { App, Button, Dropdown, MenuProps, Modal } from 'antd';
import dayjs from 'dayjs';
import Cookies from 'js-cookie';
import { throttle } from 'lodash';
......@@ -25,6 +25,9 @@ const HeaderView: React.FC<{
autoChange: boolean;
topDistance: number;
}> = ({ placeholder, autoChange, topDistance }) => {
// 静态方法
const { message } = App.useApp();
// token
const token = Cookies.get('SHAREFLY-WEB-TOKEN');
// 当前的路由数据
const router = useRouter();
......@@ -54,6 +57,7 @@ const HeaderView: React.FC<{
const res = await CommonAPI.getAccountInfo();
if (res && res.code === '200') {
dispatch(setUserInfo(res.result));
if (!res.result) message.error('获取用户信息失败');
}
};
// 计算天数与当前时间的差值
......@@ -91,11 +95,13 @@ const HeaderView: React.FC<{
dispatch(setAddress(res));
});
}
console.log('当前是否登录 --->', system, token);
// 当前是否登录
if (!token) {
dispatch(setSystem({ token: undefined }));
dispatch(setUserInfo(null));
} else {
if (!system?.token) dispatch(setSystem({ token }));
getAccountInfo().then();
}
if (autoChange) window.addEventListener('scroll', handleThrottle);
......
......@@ -6,11 +6,15 @@ import { useRouter } from 'next/router';
import { useDispatch, useSelector } from 'react-redux';
import styled from 'styled-components';
import { CommonAPI } from '@/api';
import { InterReqType } from '@/api/interface';
import { WebLoginType } from '@/api/interface/common';
import { RootState } from '@/store';
import { GlobalDataState, setGlobalData } from '@/store/module/globalData';
import { setSystem } from '@/store/module/system';
import { setUserInfo } from '@/store/module/userInfo';
// 表单类型
type ReqType = InterReqType<WebLoginType>;
// 定时器暂存
let timer: NodeJS.Timer;
......@@ -30,10 +34,7 @@ const LoginModalView = ({
// store
const dispatch = useDispatch();
// 表单钩子
const [formRef] = Form.useForm<{
password: string;
username: string;
}>();
const [formRef] = Form.useForm<ReqType>();
// 是否选择协议
const [checkValue, setCheckValue] = useState<boolean>(false);
// 获取小程序二维码唯一标识
......@@ -142,13 +143,10 @@ const LoginModalView = ({
);
});
if (!valid) return;
if (valid?.password !== '123456') {
await message.warning('密码错误');
return;
}
// 获取token
const res = await CommonAPI.testPhoneLogin({
phone: valid?.username,
const res = await CommonAPI.webLogin({
accountNo: valid?.accountNo,
passWord: valid?.passWord,
});
if (res && res.code === '200') {
handleClose();
......@@ -231,13 +229,13 @@ const LoginModalView = ({
size="large"
>
<Form.Item
name="username"
name="accountNo"
rules={[{ required: true, message: '请输入登录的账号' }]}
>
<Input placeholder="请输入手机号" maxLength={11} allowClear />
</Form.Item>
<Form.Item
name="password"
name="passWord"
rules={[{ required: true, message: '请输入密码' }]}
>
<Input
......
import React, { useEffect, useState } from 'react';
import { ReloadOutlined } from '@ant-design/icons';
import { message, Modal } from 'antd';
import Cookies from 'js-cookie';
import { useDispatch, useSelector } from 'react-redux';
import styled from 'styled-components';
import { CommonAPI } from '@/api';
import { RootState } from '@/store';
import { GlobalDataState } from '@/store/module/globalData';
import { setSystem } from '@/store/module/system';
import { setUserInfo } from '@/store/module/userInfo';
export const LoginModalWrap = styled.div`
position: relative;
width: 100%;
display: flex;
align-items: center;
justify-content: flex-start;
flex-direction: column;
box-sizing: border-box;
padding: 0.68rem 0;
.qrcode {
position: relative;
width: 10.68rem;
height: 10.68rem;
margin: 1.5rem 0 1rem 0;
//background-image: url('https://file.iuav.com/file/sharefly-qrcode-wx.jpg');
//background-size: 100% 100%;
//background-size: cover;
//background-position: center;
.image {
width: 100%;
height: 100%;
object-fit: cover;
}
.shadow {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
flex-direction: column;
background: rgba(0, 0, 0, 0.68);
.refresh {
position: relative;
width: 3rem;
height: 3rem;
border-radius: 50%;
background: #fff;
margin-bottom: 1rem;
&:active {
filter: brightness(0.9);
transform: rotate(360deg);
transition: all 0.3s ease-in-out;
}
}
.text {
color: #fff;
}
}
}
.title {
color: #222;
font-size: 16px;
font-weight: bold;
}
.text {
color: #333;
font-size: 12px;
line-height: 20px;
text-align: center;
}
.action {
color: #999;
font-size: 12px;
line-height: 20px;
text-align: center;
margin: 1rem 0;
text-decoration: underline;
cursor: pointer;
&:hover,
&:active {
color: #ff552d;
}
}
`;
// 定时器暂存
let timer: NodeJS.Timer;
const LoginModalView = ({
open,
onCancel,
}: {
open: boolean;
onCancel: () => void;
}) => {
// system
const globalData = useSelector(
(state: RootState) => state.globalData,
) as GlobalDataState;
// store
const dispatch = useDispatch();
// 获取小程序二维码唯一标识
const [randomLoginCode, setRandomLoginCode] = useState<string>(
`${parseInt(String(Math.random() * 10001), 10)}`,
);
// 二维码是否过期
const [qrCodeExpired, setQrCodeExpired] = useState<boolean>(false);
// 登录二维码的地址
const [qrCodeData, setQrCodeData] = useState<string>();
// 获取登录二维码
const getQrcodeLogin = async () => {
// 获取二维码
const res = await CommonAPI.getAppletQRCode({
page: 'page-identity/identity-empower/index',
scene: `randomLoginCode=${randomLoginCode}&port=0`,
});
if (res && res.code === '200') {
if (!res.result) {
message.warning('获取登录二维码失败');
return;
}
// 设置当前登录的二维码
setQrCodeData(`data:image/png;base64,${res.result}`);
// 设置二维码倒计时
setQrCodeExpiredTimer();
}
};
// 跳转管理平台
const handleBackEnd = () => {
window.open('https://admin.iuav.com');
};
// 刷新二维码
const handleRefresh = () => {
setRandomLoginCode(`${parseInt(String(Math.random() * 10001), 10)}`);
getQrcodeLogin().then(() => {
setQrCodeExpired(false);
});
};
// 定时器设置二维码过期
const setQrCodeExpiredTimer = () => {
setTimeout(
() => {
setQrCodeExpired(true);
// 关闭定时器
if (timer) clearInterval(timer);
},
1000 * 60 * 5,
);
};
// 获取登录信息
const getLoginInfo = async () => {
if (!randomLoginCode) return;
const res = await CommonAPI.getLoginInfo({
randomLoginCode,
});
// console.log('获取登录信息 --->', res);
if (res && res.code === '200') {
// 关闭定时器
if (timer) clearInterval(timer);
// 刷新二维码
setQrCodeExpired(true);
// 关闭弹窗
onCancel?.();
// 设置用户信息
dispatch(setUserInfo(res.result));
// 设置token
dispatch(setSystem({ token: res.result.token }));
// 设置token
Cookies.set('SHAREFLY-WEB-TOKEN', res.result.token);
message.success('登录成功');
// 刷新当前页面
window.location.reload();
}
};
// 设置定时器轮询获取信息
const setLoginInfoTimer = () => {
timer = setInterval(async () => {
await getLoginInfo();
}, 2500);
};
// 关闭弹窗
const handleClose = () => {
onCancel?.();
// 关闭定时器
if (timer) clearInterval(timer);
};
// 组件挂载
useEffect(() => {
if (!open) {
// 关闭定时器
if (timer) clearInterval(timer);
return;
}
// 获取二维码
getQrcodeLogin().then(() => {
setQrCodeExpired(false);
setLoginInfoTimer();
});
}, [open]);
return (
<Modal open={open} footer={null} onCancel={handleClose}>
<LoginModalWrap>
<div className="title">
{globalData?.loginModalTitle || `微信扫码登录`}
</div>
<div className="qrcode">
{qrCodeData && (
<img src={qrCodeData} alt="登录二维码" className="image" />
)}
{qrCodeExpired && (
<div className="flex-center animate__animated animate__fast animate__fadeIn shadow">
<div className="refresh flex-center" onClick={handleRefresh}>
<ReloadOutlined style={{ fontSize: '20px' }} />
</div>
<div className="text">二维码已过期,请重新扫描</div>
</div>
)}
</div>
<div className="text">微信扫一扫,打开小程序</div>
<div className="text">即可登录/注册</div>
<div
className="action"
onClick={handleBackEnd}
style={{ visibility: 'hidden' }}
>
管理平台登录 {'>'}
</div>
<div className="text">「云享飞,让天空为世界所用」</div>
</LoginModalWrap>
</Modal>
);
};
export default LoginModalView;
......@@ -116,6 +116,7 @@ const ProductItemWrap = styled.div`
overflow: hidden;
//margin-bottom: 0.33rem;
max-height: 2rem;
line-height: 16px;
}
.product-desc {
position: relative;
......
......@@ -43,9 +43,16 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
// console.log('获取店铺详情 --->', res);
}
};
// 获取租期时间
const getLeaseTermInfo = async () => {
const res = await MallAPI.getLeaseTermInfo();
if (res && res.code === '200') {
console.log('获取租期时间 --->', res);
}
};
// 依次获取接口数据
await (async () => {
await getLeaseGoodsDetails();
await Promise.all([getLeaseGoodsDetails(), getLeaseTermInfo()]);
await getCompanyInfoById();
})();
return { props: { id, productDetail, storeDetail } };
......
import React, { useEffect, useState } from 'react';
import { Button, Checkbox, Col, Form, Input, message, Row } from 'antd';
import { App, Button, Checkbox, Col, Form, Input, Row } from 'antd';
import { useRouter } from 'next/router';
import { useDispatch, useSelector } from 'react-redux';
import styled from 'styled-components';
import { CommonAPI } from '@/api';
import { InterReqType } from '@/api/interface';
import { WebRegisterType } from '@/api/interface/common';
import LayoutView from '@/components/layout';
import { RootState } from '@/store';
import { setGlobalData } from '@/store/module/globalData';
import { SystemState } from '@/store/module/system';
// 表单类型
type ReqType = InterReqType<WebRegisterType>;
const UserSignupView = () => {
// useApp
const { message } = App.useApp();
// store
const dispatch = useDispatch();
// system
const system = useSelector((state: RootState) => state.system) as SystemState;
// router
const router = useRouter();
// 表单钩子
const [formRef] = Form.useForm<{
password: string;
username: string;
code: string;
}>();
const [formRef] = Form.useForm<ReqType>();
// 是否选择协议
const [checkValue, setCheckValue] = useState<boolean>(false);
// 验证码倒计时
const [countDown, setCountDown] = useState<number>(60);
// 是否可以点击获取验证码
const [isCanGetCode, setIsCanGetCode] = useState<boolean>(true);
// 提交数据
const handleSubmit = async () => {
if (!checkValue) {
......@@ -34,7 +45,14 @@ const UserSignupView = () => {
);
});
if (!values) return;
message.loading('注册中...');
message.loading('注册中...', 1000);
const res = await CommonAPI.webRegister(values);
if (res && res.code === '200') {
await message.success('注册成功');
// 跳转回登录
await router.replace('/');
dispatch(setGlobalData({ loginModalVisible: true }));
}
};
// 跳转回登录
const handleLogin = () => {
......@@ -42,7 +60,36 @@ const UserSignupView = () => {
};
// 获取二维码
const handleGetCode = async () => {
await message.loading('获取中...');
const values = await formRef.validateFields(['phoneNum']).catch((err) => {
Promise.all(
err?.errorFields?.map((i: { errors: string[] }) =>
message.warning(i?.errors[0]),
),
);
});
if (!values) return;
// 禁止再次发送
setIsCanGetCode(false);
message.loading('发送中...', 1000);
const res = await CommonAPI.getVerifyCodeAuth({
phoneNum: values.phoneNum,
});
// 提示
if (res && res.code === '200') {
message.success('验证码已发送');
} else {
message.success(res.message);
}
// 开始倒计时
let count = 60;
const timer = setInterval(() => {
count -= 1;
setCountDown(count);
if (count <= 0) {
clearInterval(timer);
setIsCanGetCode(true);
}
}, 1000);
};
// 打开协议弹窗
const handleAgree = (type: number) => {
......@@ -78,9 +125,16 @@ const UserSignupView = () => {
>
<Form.Item
label="手机号"
name="username"
name="phoneNum"
required={false}
rules={[{ required: true, message: '请输入登录的账号' }]}
rules={[
{ required: true, message: '请输入要注册的手机号' },
// 请输入正确的手机号
{
pattern: /^1[3456789]\d{9}$/,
message: '请输入正确的手机号',
},
]}
>
<Input placeholder="请输入手机号" maxLength={11} allowClear />
</Form.Item>
......@@ -119,7 +173,9 @@ const UserSignupView = () => {
</Form.Item>
</Col>
<Col span={8}>
<Button onClick={handleGetCode}>获取验证码</Button>
<Button onClick={handleGetCode} disabled={!isCanGetCode}>
{isCanGetCode ? '获取验证码' : `再次获取(${countDown}s)`}
</Button>
</Col>
</Row>
</Form.Item>
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论