feat: 旋转验证对接成功
This commit is contained in:
@@ -24,12 +24,16 @@ export const oauthLogin = (type: string) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取短信验证码
|
* 获取短信验证码
|
||||||
* @param phone
|
* @param data
|
||||||
*/
|
*/
|
||||||
export const getSms = (phone: string) => {
|
export const getSms = (data: any) => {
|
||||||
return web.request({
|
return web.request({
|
||||||
url: "/sms/sendByTemplate/" + phone,
|
url: "/sms/sendByTemplate/",
|
||||||
method: "post",
|
method: "post",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json;charset=UTF-8",
|
||||||
|
},
|
||||||
|
data: data,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -44,3 +48,16 @@ export const register = (data: API.PhoneRegisterRequest) => {
|
|||||||
data: data,
|
data: data,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
export const login = (data: API.LoginRequest) => {
|
||||||
|
return web.request({
|
||||||
|
url: "/auth/user/login",
|
||||||
|
method: "post",
|
||||||
|
data: data,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
6
src/types/user/user.d.ts
vendored
6
src/types/user/user.d.ts
vendored
@@ -9,6 +9,12 @@ declare namespace API {
|
|||||||
confirmPassword?: string;
|
confirmPassword?: string;
|
||||||
activeCode?: string;
|
activeCode?: string;
|
||||||
};
|
};
|
||||||
|
type LoginRequest = {
|
||||||
|
userName?: string;
|
||||||
|
password?: string;
|
||||||
|
token: string;
|
||||||
|
deg: number;
|
||||||
|
};
|
||||||
// type ApiResponse<T> = {
|
// type ApiResponse<T> = {
|
||||||
// success?: boolean;
|
// success?: boolean;
|
||||||
// code?: number;
|
// code?: number;
|
||||||
|
@@ -14,7 +14,7 @@ class Request {
|
|||||||
(config) => {
|
(config) => {
|
||||||
const token: string | null = getStorageFromKey("token");
|
const token: string | null = getStorageFromKey("token");
|
||||||
if (token) {
|
if (token) {
|
||||||
config.headers.Authorization = `schisandra ${token}`;
|
config.headers.Authorization = `${import.meta.env.VITE_APP_TOKEN_KEY} ${token}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (config.method == "post") {
|
// if (config.method == "post") {
|
||||||
|
@@ -36,8 +36,7 @@ import { observer } from "mobx-react";
|
|||||||
import FooterComponent from "@/components/Footer";
|
import FooterComponent from "@/components/Footer";
|
||||||
import RotateCaptcha, { CaptchaInstance, type TicketInfoType } from "react-rotate-captcha";
|
import RotateCaptcha, { CaptchaInstance, type TicketInfoType } from "react-rotate-captcha";
|
||||||
import { get, load } from "@/api/captcha/index.ts";
|
import { get, load } from "@/api/captcha/index.ts";
|
||||||
import { getSms, oauthLogin } from "@/api/user";
|
import { getSms, login, oauthLogin } from "@/api/user";
|
||||||
import { VerfiyCaptcha } from "@/api/captcha/api.ts";
|
|
||||||
import { TinyColor } from "@ctrl/tinycolor";
|
import { TinyColor } from "@ctrl/tinycolor";
|
||||||
|
|
||||||
type LoginType = "account" | "phone";
|
type LoginType = "account" | "phone";
|
||||||
@@ -51,7 +50,8 @@ const iconStyles: CSSProperties = {
|
|||||||
|
|
||||||
export default observer(() => {
|
export default observer(() => {
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const captcha = useRef<CaptchaInstance>(null);
|
const smsCaptcha = useRef<CaptchaInstance>(null);
|
||||||
|
const loginCaptcha = useRef<CaptchaInstance>(null);
|
||||||
const captchaRef = useRef<CaptFieldRef | null | undefined>();
|
const captchaRef = useRef<CaptFieldRef | null | undefined>();
|
||||||
const colors = ["#40e495", "#30dd8a", "#2bb673"];
|
const colors = ["#40e495", "#30dd8a", "#2bb673"];
|
||||||
const getHoverColors = (colors: string[]) =>
|
const getHoverColors = (colors: string[]) =>
|
||||||
@@ -59,12 +59,53 @@ export default observer(() => {
|
|||||||
const getActiveColors = (colors: string[]) =>
|
const getActiveColors = (colors: string[]) =>
|
||||||
colors.map((color) => new TinyColor(color).darken(5).toString());
|
colors.map((color) => new TinyColor(color).darken(5).toString());
|
||||||
|
|
||||||
async function verify(token: string, deg: number): Promise<TicketInfoType> {
|
async function smsVerify(token: string, deg: number): Promise<TicketInfoType> {
|
||||||
|
const phone = form.getFieldValue("mobile");
|
||||||
const data: any = {
|
const data: any = {
|
||||||
token: token,
|
token: token,
|
||||||
deg: deg,
|
deg: deg,
|
||||||
|
phone: phone,
|
||||||
};
|
};
|
||||||
const res = await VerfiyCaptcha(data);
|
const res: any = await getSms(data);
|
||||||
|
if (res && res.code === 0) {
|
||||||
|
message.open({
|
||||||
|
content: res.data,
|
||||||
|
type: "success",
|
||||||
|
duration: 5,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
message.open({
|
||||||
|
content: res.data,
|
||||||
|
type: "warning",
|
||||||
|
duration: 5,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loginVerify(token: string, deg: number): Promise<TicketInfoType> {
|
||||||
|
const userName = form.getFieldValue("username");
|
||||||
|
const password = form.getFieldValue("password");
|
||||||
|
const data: any = {
|
||||||
|
token: token,
|
||||||
|
deg: deg,
|
||||||
|
userName: userName,
|
||||||
|
password: password,
|
||||||
|
};
|
||||||
|
const res: any = await login(data);
|
||||||
|
if (res && res.success && res.code === 0) {
|
||||||
|
message.open({
|
||||||
|
content: "登录成功!",
|
||||||
|
type: "success",
|
||||||
|
duration: 5,
|
||||||
|
});
|
||||||
|
} else if (res.code === 0 && !res.success) {
|
||||||
|
message.open({
|
||||||
|
content: "登录失败!用户名或密码错误!",
|
||||||
|
type: "error",
|
||||||
|
duration: 5,
|
||||||
|
});
|
||||||
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,322 +137,317 @@ export default observer(() => {
|
|||||||
|
|
||||||
const [loginType, setLoginType] = useState<LoginType>("account");
|
const [loginType, setLoginType] = useState<LoginType>("account");
|
||||||
|
|
||||||
async function openCaptcha() {
|
async function openSmsCaptcha() {
|
||||||
captcha.current!.open();
|
smsCaptcha.current!.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function openLoginCaptcha() {
|
||||||
|
loginCaptcha.current!.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
const onSubmit = async (formData: object) => {
|
|
||||||
openCaptcha().then(() => {
|
|
||||||
console.log("formData", formData);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
return (
|
return (
|
||||||
<RotateCaptcha get={get} load={load} verify={verify} limit={2} ref={captcha}>
|
<div className={styles.container}>
|
||||||
<div className={styles.container}>
|
<RotateCaptcha get={get} load={load} verify={smsVerify} limit={2} ref={smsCaptcha} />
|
||||||
<div className={styles.content}>
|
<RotateCaptcha get={get} load={load} verify={loginVerify} ref={loginCaptcha} />
|
||||||
<Space className={styles.content_content}>
|
<div className={styles.content}>
|
||||||
<Space className={styles.login_content}>
|
<Space className={styles.content_content}>
|
||||||
<Space align="center" className={styles.mp_code}>
|
<Space className={styles.login_content}>
|
||||||
<Space direction="vertical" align="center">
|
<Space align="center" className={styles.mp_code}>
|
||||||
<span className={styles.mp_code_title}>微信扫码登录</span>
|
<Space direction="vertical" align="center">
|
||||||
<Image
|
<span className={styles.mp_code_title}>微信扫码登录</span>
|
||||||
preview={false}
|
<Image
|
||||||
height={210}
|
preview={false}
|
||||||
width={200}
|
height={210}
|
||||||
className={styles.mp_code_img}
|
width={200}
|
||||||
// src={generateMpRegCodeData.data?.qrCodeUrl}
|
className={styles.mp_code_img}
|
||||||
src={qrCode}
|
// src={generateMpRegCodeData.data?.qrCodeUrl}
|
||||||
/>
|
src={qrCode}
|
||||||
<Alert
|
/>
|
||||||
// message={(<span>微信扫码<span>关注公众号</span></span>)}
|
<Alert
|
||||||
description={
|
// message={(<span>微信扫码<span>关注公众号</span></span>)}
|
||||||
<div>
|
description={
|
||||||
<span>
|
<div>
|
||||||
微信扫码
|
<span>
|
||||||
<span className={styles.mp_tips}>
|
微信扫码
|
||||||
关注公众号
|
<span className={styles.mp_tips}>关注公众号</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
<br />
|
||||||
<br />
|
登录更快更安全
|
||||||
登录更快更安全
|
</div>
|
||||||
</div>
|
}
|
||||||
}
|
// type="success"
|
||||||
// type="success"
|
showIcon={true}
|
||||||
showIcon={true}
|
className={styles.alert}
|
||||||
className={styles.alert}
|
icon={<WechatOutlined />}
|
||||||
icon={<WechatOutlined />}
|
/>
|
||||||
/>
|
|
||||||
</Space>
|
|
||||||
</Space>
|
</Space>
|
||||||
<Form
|
|
||||||
form={form}
|
|
||||||
className={styles.login_form}
|
|
||||||
initialValues={{
|
|
||||||
autoLogin: true,
|
|
||||||
}}>
|
|
||||||
<Space direction="vertical" align="center">
|
|
||||||
<Space className={styles.logo}>
|
|
||||||
<img
|
|
||||||
alt="logo"
|
|
||||||
src={logo}
|
|
||||||
style={{ width: "44px", height: "44px" }}
|
|
||||||
/>
|
|
||||||
<span>五味子云存储</span>
|
|
||||||
</Space>
|
|
||||||
<div className={styles.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位字符,且必须包含字母和数字!",
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{loginType === "phone" && (
|
|
||||||
<>
|
|
||||||
<ProFormText
|
|
||||||
fieldProps={{
|
|
||||||
size: "large",
|
|
||||||
prefix: <MobileOutlined className={"prefixIcon"} />,
|
|
||||||
autoComplete: "off",
|
|
||||||
}}
|
|
||||||
name="mobile"
|
|
||||||
placeholder={"请输入手机号"}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: "请输入手机号!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
pattern: /^1\d{10}$/,
|
|
||||||
message: "手机号格式错误!",
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
<ProFormCaptcha
|
|
||||||
fieldProps={{
|
|
||||||
size: "large",
|
|
||||||
prefix: <SafetyOutlined className={"prefixIcon"} />,
|
|
||||||
}}
|
|
||||||
captchaProps={{
|
|
||||||
size: "large",
|
|
||||||
}}
|
|
||||||
placeholder={"请输入验证码"}
|
|
||||||
captchaTextRender={(timing: boolean) => {
|
|
||||||
if (timing) {
|
|
||||||
// return `${count} ${"获取验证码"}`;
|
|
||||||
return `${"获取验证码"}`;
|
|
||||||
}
|
|
||||||
return "获取验证码";
|
|
||||||
}}
|
|
||||||
name="captcha"
|
|
||||||
phoneName={"mobile"}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: "请输入验证码!",
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
fieldRef={captchaRef}
|
|
||||||
countDown={300}
|
|
||||||
onGetCaptcha={async (mobile: string) => {
|
|
||||||
const res: any = getSms(mobile);
|
|
||||||
if (res && res.success) {
|
|
||||||
message.success(res.data, 3);
|
|
||||||
} else {
|
|
||||||
message.warning(res.data, 3);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<div style={{ marginBlockEnd: 14 }}>
|
|
||||||
<ProFormCheckbox noStyle name="autoLogin">
|
|
||||||
自动登录
|
|
||||||
</ProFormCheckbox>
|
|
||||||
<a href={"/forget"} style={{ float: "right" }}>
|
|
||||||
忘记密码
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<ConfigProvider
|
|
||||||
theme={{
|
|
||||||
components: {
|
|
||||||
Button: {
|
|
||||||
colorPrimary: `linear-gradient(116deg, ${colors.join(", ")})`,
|
|
||||||
colorPrimaryHover: `linear-gradient(116deg, ${getHoverColors(colors).join(", ")})`,
|
|
||||||
colorPrimaryActive: `linear-gradient(116deg, ${getActiveColors(colors).join(", ")})`,
|
|
||||||
lineWidth: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}>
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
block
|
|
||||||
size="large"
|
|
||||||
onClick={async () => {
|
|
||||||
let validateFields;
|
|
||||||
if (loginType === "account") {
|
|
||||||
validateFields = ["username", "password"];
|
|
||||||
} else {
|
|
||||||
validateFields = ["mobile", "captcha"];
|
|
||||||
}
|
|
||||||
|
|
||||||
await form
|
|
||||||
.validateFields(validateFields)
|
|
||||||
.then(async (values) => {
|
|
||||||
await onSubmit(values);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.error(error);
|
|
||||||
});
|
|
||||||
}}>
|
|
||||||
登录
|
|
||||||
</Button>
|
|
||||||
</ConfigProvider>
|
|
||||||
|
|
||||||
<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%",
|
|
||||||
}}>
|
|
||||||
<Tooltip title="QQ登录" color={"blue"}>
|
|
||||||
<QqOutlined
|
|
||||||
style={{ ...iconStyles, color: "#1677FF" }}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
flexDirection: "column",
|
|
||||||
height: 40,
|
|
||||||
width: 40,
|
|
||||||
border: "1px solid #D4D8DD",
|
|
||||||
borderRadius: "50%",
|
|
||||||
}}>
|
|
||||||
<Tooltip title="企业微信登录" color={"green"}>
|
|
||||||
<WechatOutlined
|
|
||||||
style={{ ...iconStyles, color: "#08a327" }}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
flexDirection: "column",
|
|
||||||
height: 40,
|
|
||||||
width: 40,
|
|
||||||
border: "1px solid #D4D8DD",
|
|
||||||
borderRadius: "50%",
|
|
||||||
}}>
|
|
||||||
<Tooltip title="github登录" color={"black"}>
|
|
||||||
<GithubOutlined
|
|
||||||
onClick={() => {
|
|
||||||
oAuthLogin("github").then();
|
|
||||||
}}
|
|
||||||
style={{ ...iconStyles, color: "#333333" }}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
flexDirection: "column",
|
|
||||||
height: 40,
|
|
||||||
width: 40,
|
|
||||||
border: "1px solid #D4D8DD",
|
|
||||||
borderRadius: "50%",
|
|
||||||
}}>
|
|
||||||
<Tooltip title="gitee登录" color={"orange"}>
|
|
||||||
<GitlabOutlined
|
|
||||||
onClick={() => {
|
|
||||||
oAuthLogin("gitee").then();
|
|
||||||
}}
|
|
||||||
style={{ ...iconStyles, color: "#FF6A10" }}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
</Space>
|
|
||||||
</div>
|
|
||||||
</Form>
|
|
||||||
<a href="/register" className={styles.go_to_register}>
|
|
||||||
<span>注册</span>
|
|
||||||
</a>
|
|
||||||
</Space>
|
</Space>
|
||||||
|
<Form
|
||||||
|
form={form}
|
||||||
|
className={styles.login_form}
|
||||||
|
initialValues={{
|
||||||
|
autoLogin: true,
|
||||||
|
}}>
|
||||||
|
<Space direction="vertical" align="center">
|
||||||
|
<Space className={styles.logo}>
|
||||||
|
<img
|
||||||
|
alt="logo"
|
||||||
|
src={logo}
|
||||||
|
style={{ width: "44px", height: "44px" }}
|
||||||
|
/>
|
||||||
|
<span>五味子云存储</span>
|
||||||
|
</Space>
|
||||||
|
<div className={styles.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位字符,且必须包含字母和数字!",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{loginType === "phone" && (
|
||||||
|
<>
|
||||||
|
<ProFormText
|
||||||
|
fieldProps={{
|
||||||
|
size: "large",
|
||||||
|
prefix: <MobileOutlined className={"prefixIcon"} />,
|
||||||
|
autoComplete: "off",
|
||||||
|
}}
|
||||||
|
name="mobile"
|
||||||
|
placeholder={"请输入手机号"}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: "请输入手机号!",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^1\d{10}$/,
|
||||||
|
message: "手机号格式错误!",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<ProFormCaptcha
|
||||||
|
fieldProps={{
|
||||||
|
size: "large",
|
||||||
|
prefix: <SafetyOutlined className={"prefixIcon"} />,
|
||||||
|
}}
|
||||||
|
captchaProps={{
|
||||||
|
size: "large",
|
||||||
|
}}
|
||||||
|
placeholder={"请输入验证码"}
|
||||||
|
captchaTextRender={(timing: boolean) => {
|
||||||
|
if (timing) {
|
||||||
|
// return `${count} ${"获取验证码"}`;
|
||||||
|
return `${"获取验证码"}`;
|
||||||
|
}
|
||||||
|
return "获取验证码";
|
||||||
|
}}
|
||||||
|
name="captcha"
|
||||||
|
phoneName={"mobile"}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: "请输入验证码!",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
fieldRef={captchaRef}
|
||||||
|
countDown={300}
|
||||||
|
onGetCaptcha={async () => {
|
||||||
|
await openSmsCaptcha();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<div style={{ marginBlockEnd: 14 }}>
|
||||||
|
<ProFormCheckbox noStyle name="autoLogin">
|
||||||
|
自动登录
|
||||||
|
</ProFormCheckbox>
|
||||||
|
<a href={"/forget"} style={{ float: "right" }}>
|
||||||
|
忘记密码
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<ConfigProvider
|
||||||
|
theme={{
|
||||||
|
components: {
|
||||||
|
Button: {
|
||||||
|
colorPrimary: `linear-gradient(116deg, ${colors.join(", ")})`,
|
||||||
|
colorPrimaryHover: `linear-gradient(116deg, ${getHoverColors(colors).join(", ")})`,
|
||||||
|
colorPrimaryActive: `linear-gradient(116deg, ${getActiveColors(colors).join(", ")})`,
|
||||||
|
lineWidth: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
block
|
||||||
|
size="large"
|
||||||
|
onClick={async () => {
|
||||||
|
let validateFields;
|
||||||
|
if (loginType === "account") {
|
||||||
|
validateFields = ["username", "password"];
|
||||||
|
} else {
|
||||||
|
validateFields = ["mobile", "captcha"];
|
||||||
|
}
|
||||||
|
|
||||||
|
await form
|
||||||
|
.validateFields(validateFields)
|
||||||
|
.then(async () => {
|
||||||
|
// await onSubmit(values);
|
||||||
|
if (loginType === "account") {
|
||||||
|
await openLoginCaptcha();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
}}>
|
||||||
|
登录
|
||||||
|
</Button>
|
||||||
|
</ConfigProvider>
|
||||||
|
|
||||||
|
<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%",
|
||||||
|
}}>
|
||||||
|
<Tooltip title="QQ登录" color={"blue"}>
|
||||||
|
<QqOutlined
|
||||||
|
style={{ ...iconStyles, color: "#1677FF" }}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
flexDirection: "column",
|
||||||
|
height: 40,
|
||||||
|
width: 40,
|
||||||
|
border: "1px solid #D4D8DD",
|
||||||
|
borderRadius: "50%",
|
||||||
|
}}>
|
||||||
|
<Tooltip title="企业微信登录" color={"green"}>
|
||||||
|
<WechatOutlined
|
||||||
|
style={{ ...iconStyles, color: "#08a327" }}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
flexDirection: "column",
|
||||||
|
height: 40,
|
||||||
|
width: 40,
|
||||||
|
border: "1px solid #D4D8DD",
|
||||||
|
borderRadius: "50%",
|
||||||
|
}}>
|
||||||
|
<Tooltip title="github登录" color={"black"}>
|
||||||
|
<GithubOutlined
|
||||||
|
onClick={() => {
|
||||||
|
oAuthLogin("github").then();
|
||||||
|
}}
|
||||||
|
style={{ ...iconStyles, color: "#333333" }}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
flexDirection: "column",
|
||||||
|
height: 40,
|
||||||
|
width: 40,
|
||||||
|
border: "1px solid #D4D8DD",
|
||||||
|
borderRadius: "50%",
|
||||||
|
}}>
|
||||||
|
<Tooltip title="gitee登录" color={"orange"}>
|
||||||
|
<GitlabOutlined
|
||||||
|
onClick={() => {
|
||||||
|
oAuthLogin("gitee").then();
|
||||||
|
}}
|
||||||
|
style={{ ...iconStyles, color: "#FF6A10" }}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
<a href="/register" className={styles.go_to_register}>
|
||||||
|
<span>注册</span>
|
||||||
|
</a>
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</Space>
|
||||||
<FooterComponent></FooterComponent>
|
|
||||||
</div>
|
</div>
|
||||||
</RotateCaptcha>
|
<FooterComponent></FooterComponent>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@@ -18,11 +18,15 @@ import { observer } from "mobx-react";
|
|||||||
import FooterComponent from "@/components/Footer";
|
import FooterComponent from "@/components/Footer";
|
||||||
import { getSms, register } from "@/api/user";
|
import { getSms, register } from "@/api/user";
|
||||||
import { TinyColor } from "@ctrl/tinycolor";
|
import { TinyColor } from "@ctrl/tinycolor";
|
||||||
|
import { get, load } from "@/api/captcha";
|
||||||
|
import RotateCaptcha, { CaptchaInstance, type TicketInfoType } from "react-rotate-captcha";
|
||||||
// import useStore from '@/utils/store/useStore.tsx'
|
// import useStore from '@/utils/store/useStore.tsx'
|
||||||
type LoginType = "phone";
|
type LoginType = "phone";
|
||||||
|
|
||||||
export default observer(() => {
|
export default observer(() => {
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
|
const registerCaptcha = useRef<CaptchaInstance>(null);
|
||||||
|
const smsCaptcha = useRef<CaptchaInstance>(null);
|
||||||
const captchaRef = useRef<CaptFieldRef | null | undefined>();
|
const captchaRef = useRef<CaptFieldRef | null | undefined>();
|
||||||
const colors = ["#6253E1", "#04BEFE"];
|
const colors = ["#6253E1", "#04BEFE"];
|
||||||
const getHoverColors = (colors: string[]) =>
|
const getHoverColors = (colors: string[]) =>
|
||||||
@@ -43,16 +47,72 @@ export default observer(() => {
|
|||||||
];
|
];
|
||||||
const [loginType, setLoginType] = useState<LoginType>("phone");
|
const [loginType, setLoginType] = useState<LoginType>("phone");
|
||||||
|
|
||||||
const onSubmit = async (formData: object) => {
|
async function smsVerify(token: string, deg: number): Promise<TicketInfoType> {
|
||||||
const res: any = await register(formData);
|
const phone = form.getFieldValue("phone");
|
||||||
if (res && res.success) {
|
const data: any = {
|
||||||
message.success(res.data);
|
token: token,
|
||||||
|
deg: deg,
|
||||||
|
phone: phone,
|
||||||
|
};
|
||||||
|
const res: any = await getSms(data);
|
||||||
|
if (res && res.code === 0) {
|
||||||
|
message.open({
|
||||||
|
content: res.data,
|
||||||
|
type: "success",
|
||||||
|
duration: 5,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
message.error(res.data);
|
message.open({
|
||||||
|
content: res.data,
|
||||||
|
type: "warning",
|
||||||
|
duration: 5,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function openRegisterCaptcha() {
|
||||||
|
registerCaptcha.current!.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function openSmsCaptcha() {
|
||||||
|
smsCaptcha.current!.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function registerVerify(token: string, deg: number): Promise<TicketInfoType> {
|
||||||
|
const userName = form.getFieldValue("username");
|
||||||
|
const password = form.getFieldValue("password");
|
||||||
|
const phone = form.getFieldValue("phone");
|
||||||
|
const activeCode = form.getFieldValue("activeCode");
|
||||||
|
const data: any = {
|
||||||
|
token: token,
|
||||||
|
deg: deg,
|
||||||
|
userName: userName,
|
||||||
|
password: password,
|
||||||
|
phone: phone,
|
||||||
|
activeCode: activeCode,
|
||||||
|
};
|
||||||
|
const res: any = await register(data);
|
||||||
|
if (res && res.success && res.code === 0) {
|
||||||
|
message.open({
|
||||||
|
content: res.data,
|
||||||
|
type: "success",
|
||||||
|
duration: 5,
|
||||||
|
});
|
||||||
|
} else if (res.code === 0 && !res.success) {
|
||||||
|
message.open({
|
||||||
|
content: res.data,
|
||||||
|
type: "error",
|
||||||
|
duration: 5,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
|
<RotateCaptcha get={get} load={load} verify={registerVerify} ref={registerCaptcha} />
|
||||||
|
<RotateCaptcha get={get} load={load} verify={smsVerify} ref={smsCaptcha} />
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
<Space>
|
<Space>
|
||||||
<Space className={styles.login_content}>
|
<Space className={styles.login_content}>
|
||||||
@@ -119,7 +179,7 @@ export default observer(() => {
|
|||||||
prefix: <UserOutlined className={"prefixIcon"} />,
|
prefix: <UserOutlined className={"prefixIcon"} />,
|
||||||
autoComplete: "off",
|
autoComplete: "off",
|
||||||
}}
|
}}
|
||||||
name="userName"
|
name="username"
|
||||||
placeholder="请输入用户名"
|
placeholder="请输入用户名"
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
@@ -179,13 +239,8 @@ export default observer(() => {
|
|||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
fieldRef={captchaRef}
|
fieldRef={captchaRef}
|
||||||
onGetCaptcha={async (phone: string) => {
|
onGetCaptcha={async () => {
|
||||||
const res: any = await getSms(phone);
|
await openSmsCaptcha();
|
||||||
if (res && res.success) {
|
|
||||||
message.success(res.data, 3);
|
|
||||||
} else {
|
|
||||||
message.warning(res.data, 3);
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -261,8 +316,8 @@ export default observer(() => {
|
|||||||
];
|
];
|
||||||
await form
|
await form
|
||||||
.validateFields(validateFields)
|
.validateFields(validateFields)
|
||||||
.then(async (values) => {
|
.then(async () => {
|
||||||
await onSubmit(values as API.PhoneRegisterRequest);
|
await openRegisterCaptcha();
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
Reference in New Issue
Block a user