Files
schisandra-cloud-storage-fr…/src/views/User/Login/index.tsx
2024-06-05 01:53:04 +08:00

454 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/** @format */
import {
GithubOutlined,
GitlabOutlined,
LockOutlined,
MobileOutlined,
QqOutlined,
SafetyOutlined,
UserOutlined,
WechatOutlined,
} from "@ant-design/icons";
import {
CaptFieldRef,
ProFormCaptcha,
ProFormCheckbox,
ProFormText,
} from "@ant-design/pro-components";
import {
Alert,
Button,
ConfigProvider,
Divider,
Form,
Image,
message,
Space,
Tabs,
Tooltip,
} from "antd";
import { CSSProperties, useRef, useState } from "react";
import logo from "@/assets/icons/schisandra.svg";
import qrCode from "@/assets/images/login_qrcode-landaiqing.jpg";
import styles from "./index.module.less";
import { observer } from "mobx-react";
import FooterComponent from "@/components/Footer";
import RotateCaptcha, { CaptchaInstance, type TicketInfoType } from "react-rotate-captcha";
import { get, load } from "@/api/captcha/index.ts";
import { getSms, login, oauthLogin } from "@/api/user";
import { TinyColor } from "@ctrl/tinycolor";
type LoginType = "account" | "phone";
const iconStyles: CSSProperties = {
color: "rgba(0, 0, 0, 0.2)",
fontSize: "18px",
verticalAlign: "middle",
cursor: "pointer",
};
export default observer(() => {
const [form] = Form.useForm();
const smsCaptcha = useRef<CaptchaInstance>(null);
const loginCaptcha = useRef<CaptchaInstance>(null);
const captchaRef = useRef<CaptFieldRef | null | undefined>();
const colors = ["#40e495", "#30dd8a", "#2bb673"];
const getHoverColors = (colors: string[]) =>
colors.map((color) => new TinyColor(color).lighten(5).toString());
const getActiveColors = (colors: string[]) =>
colors.map((color) => new TinyColor(color).darken(5).toString());
async function smsVerify(token: string, deg: number): Promise<TicketInfoType> {
const phone = form.getFieldValue("mobile");
const data: any = {
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 {
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;
}
const items = [
{
label: (
<span>
<UserOutlined />
</span>
),
key: "account",
},
{
label: (
<span>
<MobileOutlined />
</span>
),
key: "phone",
},
];
async function oAuthLogin(type: string) {
const res: any = await oauthLogin(type);
window.open(res.data, "_blank");
}
const [loginType, setLoginType] = useState<LoginType>("account");
async function openSmsCaptcha() {
smsCaptcha.current!.open();
}
async function openLoginCaptcha() {
loginCaptcha.current!.open();
}
return (
<div className={styles.container}>
<RotateCaptcha get={get} load={load} verify={smsVerify} limit={2} ref={smsCaptcha} />
<RotateCaptcha get={get} load={load} verify={loginVerify} ref={loginCaptcha} />
<div className={styles.content}>
<Space className={styles.content_content}>
<Space className={styles.login_content}>
<Space align="center" className={styles.mp_code}>
<Space direction="vertical" align="center">
<span className={styles.mp_code_title}></span>
<Image
preview={false}
height={210}
width={200}
className={styles.mp_code_img}
// src={generateMpRegCodeData.data?.qrCodeUrl}
src={qrCode}
/>
<Alert
// message={(<span>微信扫码<span>关注公众号</span></span>)}
description={
<div>
<span>
<span className={styles.mp_tips}></span>
</span>
<br />
</div>
}
// type="success"
showIcon={true}
className={styles.alert}
icon={<WechatOutlined />}
/>
</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>
<FooterComponent></FooterComponent>
</div>
);
});