feat: update

This commit is contained in:
landaiqing
2024-05-29 21:33:58 +08:00
parent cb9c827220
commit 4ce065214a
11 changed files with 173 additions and 144 deletions

View File

@@ -1,5 +1,7 @@
import type { TicketInfoType, TokenInfoType } from "react-rotate-captcha"; /** @format */
import { getCaptcha, VerfiyCaptcha } from "@/api/captcha/api.ts";
import type { TokenInfoType } from "react-rotate-captcha";
import { getCaptcha } from "@/api/captcha/api.ts";
export type ActionType = { export type ActionType = {
code: 0 | 1; code: 0 | 1;
@@ -36,11 +38,10 @@ export function sleep(time: number) {
}); });
} }
export async function verify(token: string, deg: number): Promise<TicketInfoType> { // export async function verify(token: string, deg: number): Promise<TicketInfoType> {
const data: any = { // const data: any = {
token: token, // token: token,
deg: deg, // deg: deg,
}; // };
const res: any = await VerfiyCaptcha(data); // return await VerfiyCaptcha(data);
return res; // }
}

View File

@@ -21,3 +21,26 @@ export const oauthLogin = (type: string) => {
method: "get", method: "get",
}); });
}; };
/**
* 获取短信验证码
* @param phone
*/
export const getSms = (phone: string) => {
return web.request({
url: "/sms/sendByTemplate/" + phone,
method: "post",
});
};
/**
* 注册
* @param data
*/
export const register = (data: API.PhoneRegisterRequest) => {
return web.request({
url: "/auth/user/register",
method: "post",
data: data,
});
};

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24"><path fill="#ea580c" d="M11.984 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12a12 12 0 0 0 12-12A12 12 0 0 0 12 0zm6.09 5.333c.328 0 .593.266.592.593v1.482a.594.594 0 0 1-.593.592H9.777c-.982 0-1.778.796-1.778 1.778v5.63c0 .327.266.592.593.592h5.63c.982 0 1.778-.796 1.778-1.778v-.296a.593.593 0 0 0-.592-.593h-4.15a.59.59 0 0 1-.592-.592v-1.482a.593.593 0 0 1 .593-.592h6.815c.327 0 .593.265.593.592v3.408a4 4 0 0 1-4 4H5.926a.593.593 0 0 1-.593-.593V9.778a4.444 4.444 0 0 1 4.445-4.444h8.296Z"/></svg>

After

Width:  |  Height:  |  Size: 579 B

View File

@@ -5,7 +5,7 @@ import "./index.less";
import { useNavigate, useSearchParams } from "react-router-dom"; import { useNavigate, useSearchParams } from "react-router-dom";
import useStore from "@/utils/store/useStore.tsx"; import useStore from "@/utils/store/useStore.tsx";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import localforage from "localforage"; import { setStorage } from "@/utils/localStorage/config.ts";
const LoadingPage = () => { const LoadingPage = () => {
const [search] = useSearchParams(); const [search] = useSearchParams();
@@ -15,7 +15,7 @@ const LoadingPage = () => {
const store = useStore("user"); const store = useStore("user");
store.setToken(token); store.setToken(token);
store.setUserId(userId); store.setUserId(userId);
localforage.setItem("token", token).then(); setStorage("token", token, 24 * 60 * 30);
useEffect(() => { useEffect(() => {
document.body.classList.add("loading-body"); document.body.classList.add("loading-body");
if (store.getToken() !== null && store.getUserId() !== null) { if (store.getToken() !== null && store.getUserId() !== null) {

View File

@@ -10,7 +10,7 @@ import {
useNavigate, useNavigate,
useRoutes, useRoutes,
} from "react-router-dom"; } from "react-router-dom";
import localforage from "localforage"; import { getStorageFromKey } from "@/utils/localStorage/config.ts";
//递归查询对应的路由 //递归查询对应的路由
export function searchRouteDetail(path: string, routes: RouteObject[]): RouteObject | null { export function searchRouteDetail(path: string, routes: RouteObject[]): RouteObject | null {
@@ -28,21 +28,21 @@ async function guard(location: Location, navigate: NavigateFunction, routes: Rou
const { pathname } = location; const { pathname } = location;
//找到对应的路由信息 //找到对应的路由信息
const routedetail: RouteObject | null = searchRouteDetail(pathname, routes); const routerDetail: RouteObject | null = searchRouteDetail(pathname, routes);
//没有找到路由跳转404 //没有找到路由跳转404
if (!routedetail) { if (!routerDetail) {
navigate("/404"); navigate("/404");
return false; return false;
} }
//如果需要权限验证 //如果需要权限验证
if ( if (
routedetail.path !== "/login" && routerDetail.path !== "/login" &&
routedetail.path !== "/register" && routerDetail.path !== "/register" &&
routedetail.path !== "/" && routerDetail.path !== "/" &&
routedetail.path !== "/404" routerDetail.path !== "/404"
) { ) {
const token: string | null = await localforage.getItem("token"); const token: string | null = getStorageFromKey("token");
if (!token) { if (!token) {
message.warning("请先登录!").then(); message.warning("请先登录!").then();
navigate("/login"); navigate("/login");

View File

@@ -1,7 +1,13 @@
// @ts-ignore /** @format */
/* eslint-disable */ /* eslint-disable */
declare namespace API { declare namespace API {
type PhoneRegisterRequest = {
phone?: string;
password?: string;
confirmPassword?: string;
activeCode?: string;
};
// type ApiResponse<T> = { // type ApiResponse<T> = {
// success?: boolean; // success?: boolean;
// code?: number; // code?: number;
@@ -124,23 +130,23 @@ declare namespace API {
// type?: NoticeIconItemType; // type?: NoticeIconItemType;
// }; // };
type GenerateMpRegCode = { // type GenerateMpRegCode = {
data?: { // data?: {
regCode?: string; // regCode?: string;
qrCodeUrl?: string; // qrCodeUrl?: string;
expireSeconds?: number; // expireSeconds?: number;
ticket?: string; // ticket?: string;
url?: string; // url?: string;
}; // };
} // }
// type GetClientId = { // type GetClientId = {
// data?: string; // data?: string;
// } // }
type GenerateBase64Code = { // type GenerateBase64Code = {
data?: string; // data?: string;
} // }
// type GetClientToken = { // type GetClientToken = {
// data?: { // data?: {
@@ -150,14 +156,6 @@ declare namespace API {
// }; // };
// } // }
type PhoneRegisterRequest = {
clientId?: string;
phone?: string;
password?: string;
confirmPassword?: string;
smsCode?: number;
}
// type PhoneRegisterResponse = ResponseStructure & { // type PhoneRegisterResponse = ResponseStructure & {
// data?: number; // data?: number;
// } // }

View File

@@ -2,6 +2,7 @@
import axios, { AxiosInstance, AxiosRequestConfig } from "axios"; import axios, { AxiosInstance, AxiosRequestConfig } from "axios";
import { message } from "antd"; import { message } from "antd";
import { getStorageFromKey } from "@/utils/localStorage/config.ts";
class Request { class Request {
private instance: AxiosInstance | undefined; private instance: AxiosInstance | undefined;
@@ -11,6 +12,11 @@ class Request {
// 全局请求拦截 // 全局请求拦截
this.instance.interceptors.request.use( this.instance.interceptors.request.use(
(config) => { (config) => {
const token: string | null = getStorageFromKey("token");
if (token) {
config.headers.Authorization = `schisandra ${token}`;
}
// if (config.method == "post") { // if (config.method == "post") {
// config.data = EncryptData(JSON.stringify(config.data)); // config.data = EncryptData(JSON.stringify(config.data));
// } // }

View File

@@ -1,17 +1,10 @@
/** @format */ /** @format */
import Request from "./request"; import Request from "./request";
import localforage from "localforage";
async function getToken() {
return await localforage.getItem("token");
}
const token = await getToken();
const web: Request = new Request({ const web: Request = new Request({
baseURL: import.meta.env.VITE_APP_BASE_API, baseURL: import.meta.env.VITE_APP_BASE_API,
headers: { timeout: 5000,
token: import.meta.env.VITE_APP_TOKEN_KEY + " " + token,
},
}); });
export default web; export default web;

View File

@@ -9,17 +9,23 @@ import {
UserOutlined, UserOutlined,
WechatOutlined, WechatOutlined,
} from "@ant-design/icons"; } from "@ant-design/icons";
import { ProFormCaptcha, ProFormCheckbox, ProFormText } from "@ant-design/pro-components"; import {
import { Alert, Button, Divider, Form, Image, message, Space, Tabs } from "antd"; CaptFieldRef,
import { CSSProperties, useRef, useState } from "react"; ProFormCaptcha,
ProFormCheckbox,
ProFormText,
} from "@ant-design/pro-components";
import { Alert, Button, Divider, Form, Image, message, Space, Tabs, Tooltip } from "antd";
import { CSSProperties, useEffect, useRef, useState } from "react";
import logo from "@/assets/icons/schisandra.svg"; import logo from "@/assets/icons/schisandra.svg";
import qrCode from "@/assets/images/login_qrcode-landaiqing.jpg"; import qrCode from "@/assets/images/login_qrcode-landaiqing.jpg";
import styles from "./index.module.less"; import styles from "./index.module.less";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import FooterComponent from "@/components/Footer"; import FooterComponent from "@/components/Footer";
import RotateCaptcha, { CaptchaInstance } from "react-rotate-captcha"; import RotateCaptcha, { CaptchaInstance, type TicketInfoType } from "react-rotate-captcha";
import { get, load, verify } from "@/api/captcha/index.ts"; import { get, load } from "@/api/captcha/index.ts";
import { oauthLogin } from "@/api/user"; import { getSms, oauthLogin } from "@/api/user";
import { VerfiyCaptcha } from "@/api/captcha/api.ts";
// import useStore from '@/utils/store/useStore.tsx' // import useStore from '@/utils/store/useStore.tsx'
type LoginType = "account" | "phone"; type LoginType = "account" | "phone";
@@ -33,6 +39,17 @@ const iconStyles: CSSProperties = {
export default observer(() => { export default observer(() => {
const [form] = Form.useForm(); const [form] = Form.useForm();
const captcha = useRef<CaptchaInstance>(null); const captcha = useRef<CaptchaInstance>(null);
const captchaRef = useRef<CaptFieldRef | null | undefined>();
async function verify(token: string, deg: number): Promise<TicketInfoType> {
const data: any = {
token: token,
deg: deg,
};
const res = await VerfiyCaptcha(data);
return res;
}
const items = [ const items = [
{ {
label: ( label: (
@@ -56,15 +73,21 @@ export default observer(() => {
async function oAuthLogin(type: string) { async function oAuthLogin(type: string) {
const res: any = await oauthLogin(type); const res: any = await oauthLogin(type);
console.log(res); window.open(res.data, "_blank");
window.location.href = res.data;
} }
const [loginType, setLoginType] = useState<LoginType>("account"); const [loginType, setLoginType] = useState<LoginType>("account");
async function openCaptcha() {
captcha.current!.open();
}
const onSubmit = async (formData: object) => { const onSubmit = async (formData: object) => {
console.log(formData); openCaptcha().then(() => {
console.log(formData);
});
}; };
useEffect(() => {}, []);
return ( return (
<RotateCaptcha get={get} load={load} verify={verify} limit={2} ref={captcha}> <RotateCaptcha get={get} load={load} verify={verify} limit={2} ref={captcha}>
<div className={styles.container}> <div className={styles.container}>
@@ -137,7 +160,7 @@ export default observer(() => {
size: "large", size: "large",
prefix: <UserOutlined className={"prefixIcon"} />, prefix: <UserOutlined className={"prefixIcon"} />,
}} }}
placeholder={"请输入账号/邮箱/电话号码"} placeholder={"请输入邮箱电话号码"}
rules={[ rules={[
{ {
required: true, required: true,
@@ -165,26 +188,6 @@ export default observer(() => {
}, },
]} ]}
/> />
{/*<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" && ( {loginType === "phone" && (
@@ -208,26 +211,6 @@ export default observer(() => {
}, },
]} ]}
/> />
{/*<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 <ProFormCaptcha
fieldProps={{ fieldProps={{
size: "large", size: "large",
@@ -237,22 +220,30 @@ export default observer(() => {
size: "large", size: "large",
}} }}
placeholder={"请输入验证码"} placeholder={"请输入验证码"}
captchaTextRender={(timing, count) => { captchaTextRender={(timing: boolean) => {
if (timing) { if (timing) {
return `${count} ${"获取验证码"}`; // return `${count} ${"获取验证码"}`;
return `${"获取验证码"}`;
} }
return "获取验证码"; return "获取验证码";
}} }}
name="captcha" name="captcha"
phoneName={"mobile"}
rules={[ rules={[
{ {
required: true, required: true,
message: "请输入验证码!", message: "请输入验证码!",
}, },
]} ]}
onGetCaptcha={async () => { fieldRef={captchaRef}
captcha.current!.open(); countDown={300}
message.success("获取验证码成功验证码为1234"); onGetCaptcha={async (mobile: string) => {
const res: any = await getSms(mobile);
if (res && res.success) {
message.success(res.data, 3);
} else {
message.warning(res.data, 3);
}
}} }}
/> />
</> </>
@@ -261,7 +252,7 @@ export default observer(() => {
<ProFormCheckbox noStyle name="autoLogin"> <ProFormCheckbox noStyle name="autoLogin">
</ProFormCheckbox> </ProFormCheckbox>
<a style={{ float: "right" }}> </a> <a style={{ float: "right" }}></a>
</div> </div>
<Button <Button
@@ -271,21 +262,18 @@ export default observer(() => {
onClick={async () => { onClick={async () => {
let validateFields; let validateFields;
if (loginType === "account") { if (loginType === "account") {
validateFields = ["username", "password", "code"]; validateFields = ["username", "password"];
} else { } else {
validateFields = ["mobile", "captcha", "code"]; validateFields = ["mobile", "captcha"];
} }
await form await form
.validateFields(validateFields) .validateFields(validateFields)
.then(async (values) => { .then(async (values) => {
if (loginType === "account") { await onSubmit(values);
captcha.current!.open();
}
await onSubmit(values as API.PhoneRegisterRequest);
}) })
.catch((errorInfo) => { .catch((error) => {
console.error(errorInfo); console.error(error);
}); });
}}> }}>
@@ -320,9 +308,11 @@ export default observer(() => {
border: "1px solid #D4D8DD", border: "1px solid #D4D8DD",
borderRadius: "50%", borderRadius: "50%",
}}> }}>
<QqOutlined <Tooltip title="QQ登录" color={"blue"}>
style={{ ...iconStyles, color: "#1677FF" }} <QqOutlined
/> style={{ ...iconStyles, color: "#1677FF" }}
/>
</Tooltip>
</div> </div>
<div <div
style={{ style={{
@@ -335,9 +325,11 @@ export default observer(() => {
border: "1px solid #D4D8DD", border: "1px solid #D4D8DD",
borderRadius: "50%", borderRadius: "50%",
}}> }}>
<WechatOutlined <Tooltip title="企业微信登录" color={"green"}>
style={{ ...iconStyles, color: "#08a327" }} <WechatOutlined
/> style={{ ...iconStyles, color: "#08a327" }}
/>
</Tooltip>
</div> </div>
<div <div
style={{ style={{
@@ -350,12 +342,14 @@ export default observer(() => {
border: "1px solid #D4D8DD", border: "1px solid #D4D8DD",
borderRadius: "50%", borderRadius: "50%",
}}> }}>
<GithubOutlined <Tooltip title="github登录" color={"black"}>
onClick={() => { <GithubOutlined
oAuthLogin("github").then(); onClick={() => {
}} oAuthLogin("github").then();
style={{ ...iconStyles, color: "#333333" }} }}
/> style={{ ...iconStyles, color: "#333333" }}
/>
</Tooltip>
</div> </div>
<div <div
style={{ style={{
@@ -368,12 +362,14 @@ export default observer(() => {
border: "1px solid #D4D8DD", border: "1px solid #D4D8DD",
borderRadius: "50%", borderRadius: "50%",
}}> }}>
<GitlabOutlined <Tooltip title="gitee登录" color={"orange"}>
onClick={() => { <GitlabOutlined
oAuthLogin("gitee").then(); onClick={() => {
}} oAuthLogin("gitee").then();
style={{ ...iconStyles, color: "#FF6A10" }} }}
/> style={{ ...iconStyles, color: "#FF6A10" }}
/>
</Tooltip>
</div> </div>
</Space> </Space>
</div> </div>

View File

@@ -2,7 +2,7 @@
import { LockOutlined, MobileOutlined, WechatOutlined } from "@ant-design/icons"; import { LockOutlined, MobileOutlined, WechatOutlined } from "@ant-design/icons";
import { ProFormCaptcha, ProFormText } from "@ant-design/pro-components"; import { ProFormCaptcha, ProFormText } from "@ant-design/pro-components";
import { Space, Tabs, message, Image, Alert, Form, Button } from "antd"; import { Alert, Button, Form, Image, message, Space, Tabs } from "antd";
import { useState } from "react"; import { useState } from "react";
import logo from "@/assets/icons/schisandra.svg"; import logo from "@/assets/icons/schisandra.svg";
// import background from '@/assets/images/background.png' // import background from '@/assets/images/background.png'
@@ -10,6 +10,7 @@ import qrCode from "@/assets/images/login_qrcode-landaiqing.jpg";
import styles from "./index.module.less"; import styles from "./index.module.less";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import FooterComponent from "@/components/Footer"; import FooterComponent from "@/components/Footer";
import { getSms, register } from "@/api/user";
// import useStore from '@/utils/store/useStore.tsx' // import useStore from '@/utils/store/useStore.tsx'
type LoginType = "phone"; type LoginType = "phone";
@@ -29,7 +30,12 @@ export default observer(() => {
const [loginType, setLoginType] = useState<LoginType>("phone"); const [loginType, setLoginType] = useState<LoginType>("phone");
const onSubmit = async (formData: object) => { const onSubmit = async (formData: object) => {
console.log(formData); const res: any = await register(formData);
if (res && res.success) {
message.success(res.data);
} else {
message.error(res.data);
}
}; };
return ( return (
<div className={styles.container}> <div className={styles.container}>
@@ -168,21 +174,28 @@ export default observer(() => {
size: "large", size: "large",
}} }}
placeholder={"请输入验证码"} placeholder={"请输入验证码"}
captchaTextRender={(timing, count) => { captchaTextRender={(timing: boolean) => {
if (timing) { if (timing) {
return `${count} ${"获取验证码"}`; return `${"获取验证码"}`;
} }
return "获取验证码"; return "获取验证码";
}} }}
name="captcha" name="activeCode"
phoneName={"phone"}
countDown={300}
rules={[ rules={[
{ {
required: true, required: true,
message: "请输入验证码!", message: "请输入验证码!",
}, },
]} ]}
onGetCaptcha={async () => { onGetCaptcha={async (phone: string) => {
message.success("获取验证码成功验证码为1234"); const res: any = await getSms(phone);
if (res && res.success) {
message.success(res.data, 3);
} else {
message.warning(res.data, 3);
}
}} }}
/> />
</> </>
@@ -193,10 +206,8 @@ export default observer(() => {
onClick={async () => { onClick={async () => {
const validateFields = [ const validateFields = [
"phone", "phone",
"username",
"password", "password",
"captcha", "activeCode",
"code",
"confirmPassword", "confirmPassword",
]; ];
await form await form
@@ -204,8 +215,8 @@ export default observer(() => {
.then(async (values) => { .then(async (values) => {
await onSubmit(values as API.PhoneRegisterRequest); await onSubmit(values as API.PhoneRegisterRequest);
}) })
.catch((errorInfo) => { .catch((error) => {
console.error(errorInfo); console.error(error);
}); });
}}> }}>

View File

@@ -2,10 +2,10 @@
import { defineConfig, loadEnv } from "vite"; import { defineConfig, loadEnv } from "vite";
import react from "@vitejs/plugin-react"; import react from "@vitejs/plugin-react";
import * as path from "path";
import { resolve } from "path"; import { resolve } from "path";
// icons plugin // icons plugin
import { createSvgIconsPlugin } from "vite-plugin-svg-icons"; import { createSvgIconsPlugin } from "vite-plugin-svg-icons";
import * as path from "path";
import imagemin from "unplugin-imagemin/vite"; import imagemin from "unplugin-imagemin/vite";
import viteCompression from "vite-plugin-compression"; import viteCompression from "vite-plugin-compression";
import { createHtmlPlugin } from "vite-plugin-html"; import { createHtmlPlugin } from "vite-plugin-html";