feat: 新增重置密码页面
This commit is contained in:
36
package.json
36
package.json
@@ -11,16 +11,16 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/icons": "^5.3.7",
|
"@ant-design/icons": "^5.3.7",
|
||||||
"@ant-design/pro-components": "^2.7.1",
|
"@ant-design/pro-components": "^2.7.9",
|
||||||
"@ant-design/use-emotion-css": "^1.0.4",
|
"@ant-design/use-emotion-css": "^1.0.4",
|
||||||
"@babel/preset-env": "^7.24.5",
|
"@babel/preset-env": "^7.24.6",
|
||||||
"@types/crypto-js": "^4.2.2",
|
"@types/crypto-js": "^4.2.2",
|
||||||
"@types/uuid": "^9.0.8",
|
"@types/uuid": "^9.0.8",
|
||||||
"@vitejs/plugin-legacy": "^5.4.0",
|
"@vitejs/plugin-legacy": "^5.4.0",
|
||||||
"antd": "^5.17.0",
|
"antd": "^5.17.4",
|
||||||
"autoprefixer": "^10.4.19",
|
"autoprefixer": "^10.4.19",
|
||||||
"axios": "^1.6.8",
|
"axios": "^1.7.2",
|
||||||
"core-js": "^3.37.0",
|
"core-js": "^3.37.1",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"gsap": "^3.12.5",
|
"gsap": "^3.12.5",
|
||||||
"jsencrypt": "^3.3.2",
|
"jsencrypt": "^3.3.2",
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-rotate-captcha": "^1.0.26",
|
"react-rotate-captcha": "^1.0.26",
|
||||||
"react-router-dom": "^6.23.0",
|
"react-router-dom": "^6.23.1",
|
||||||
"regenerator-runtime": "^0.14.1",
|
"regenerator-runtime": "^0.14.1",
|
||||||
"tailwind-merge": "^2.3.0",
|
"tailwind-merge": "^2.3.0",
|
||||||
"tailwindcss": "^3.4.3",
|
"tailwindcss": "^3.4.3",
|
||||||
@@ -44,28 +44,28 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-babel": "^6.0.4",
|
"@rollup/plugin-babel": "^6.0.4",
|
||||||
"@types/gsap": "^3.0.0",
|
"@types/gsap": "^3.0.0",
|
||||||
"@types/node": "^20.12.11",
|
"@types/node": "^20.12.12",
|
||||||
"@types/react": "^18.3.1",
|
"@types/react": "^18.3.3",
|
||||||
"@types/react-dom": "^18.3.0",
|
"@types/react-dom": "^18.3.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.8.0",
|
"@typescript-eslint/eslint-plugin": "^7.11.0",
|
||||||
"@typescript-eslint/parser": "^7.8.0",
|
"@typescript-eslint/parser": "^7.11.0",
|
||||||
"@vitejs/plugin-react": "^4.2.1",
|
"@vitejs/plugin-react": "^4.3.0",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^9.3.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-prettier": "^5.1.3",
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
"eslint-plugin-react": "^7.34.1",
|
"eslint-plugin-react": "^7.34.2",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^4.6.2",
|
||||||
"eslint-plugin-react-refresh": "^0.4.6",
|
"eslint-plugin-react-refresh": "^0.4.7",
|
||||||
"less": "^4.2.0",
|
"less": "^4.2.0",
|
||||||
"postcss-less": "^6.0.0",
|
"postcss-less": "^6.0.0",
|
||||||
"postcss-preset-env": "^9.5.11",
|
"postcss-preset-env": "^9.5.14",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"stylelint": "^16.5.0",
|
"stylelint": "^16.6.1",
|
||||||
"stylelint-config-recess-order": "^5.0.1",
|
"stylelint-config-recess-order": "^5.0.1",
|
||||||
"stylelint-config-standard-less": "^3.0.1",
|
"stylelint-config-standard-less": "^3.0.1",
|
||||||
"stylelint-order": "^6.0.4",
|
"stylelint-order": "^6.0.4",
|
||||||
"typescript": "^5.4.5",
|
"typescript": "^5.4.5",
|
||||||
"unplugin-imagemin": "^0.5.18",
|
"unplugin-imagemin": "^0.5.18",
|
||||||
"vite": "^5.2.11"
|
"vite": "^5.2.12"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2566
pnpm-lock.yaml
generated
2566
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -12,7 +12,11 @@ import {
|
|||||||
} from "react-router-dom";
|
} from "react-router-dom";
|
||||||
import { getStorageFromKey } from "@/utils/localStorage/config.ts";
|
import { getStorageFromKey } from "@/utils/localStorage/config.ts";
|
||||||
|
|
||||||
//递归查询对应的路由
|
/**
|
||||||
|
* 递归查询对应的路由
|
||||||
|
* @param path
|
||||||
|
* @param routes
|
||||||
|
*/
|
||||||
export function searchRouteDetail(path: string, routes: RouteObject[]): RouteObject | null {
|
export function searchRouteDetail(path: string, routes: RouteObject[]): RouteObject | null {
|
||||||
for (const item of routes) {
|
for (const item of routes) {
|
||||||
if (item.path === path) return item;
|
if (item.path === path) return item;
|
||||||
@@ -23,7 +27,12 @@ export function searchRouteDetail(path: string, routes: RouteObject[]): RouteObj
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
//全局路由守卫
|
/**
|
||||||
|
* 全局路由守卫
|
||||||
|
* @param location
|
||||||
|
* @param navigate
|
||||||
|
* @param routes
|
||||||
|
*/
|
||||||
async function guard(location: Location, navigate: NavigateFunction, routes: RouteObject[]) {
|
async function guard(location: Location, navigate: NavigateFunction, routes: RouteObject[]) {
|
||||||
const { pathname } = location;
|
const { pathname } = location;
|
||||||
|
|
||||||
@@ -40,7 +49,8 @@ async function guard(location: Location, navigate: NavigateFunction, routes: Rou
|
|||||||
routerDetail.path !== "/login" &&
|
routerDetail.path !== "/login" &&
|
||||||
routerDetail.path !== "/register" &&
|
routerDetail.path !== "/register" &&
|
||||||
routerDetail.path !== "/" &&
|
routerDetail.path !== "/" &&
|
||||||
routerDetail.path !== "/404"
|
routerDetail.path !== "/404" &&
|
||||||
|
routerDetail.path !== "/forget"
|
||||||
) {
|
) {
|
||||||
const token: string | null = getStorageFromKey("token");
|
const token: string | null = getStorageFromKey("token");
|
||||||
if (!token) {
|
if (!token) {
|
||||||
@@ -52,6 +62,11 @@ async function guard(location: Location, navigate: NavigateFunction, routes: Rou
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 路由守卫
|
||||||
|
* @param routes
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
export const RouterGuard = (routes: RouteObject[]) => {
|
export const RouterGuard = (routes: RouteObject[]) => {
|
||||||
const location: Location<any> = useLocation();
|
const location: Location<any> = useLocation();
|
||||||
const navigate: NavigateFunction = useNavigate();
|
const navigate: NavigateFunction = useNavigate();
|
||||||
|
|||||||
11
src/router/modules/forget/index.ts
Normal file
11
src/router/modules/forget/index.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
/** @format */
|
||||||
|
|
||||||
|
import { lazy } from "react";
|
||||||
|
|
||||||
|
const forget = lazy(
|
||||||
|
() =>
|
||||||
|
new Promise((resolve: any) => {
|
||||||
|
setTimeout(() => resolve(import("@/views/User/Forget")), 500);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
export default forget;
|
||||||
@@ -9,6 +9,7 @@ import home from "./modules/home/index.ts";
|
|||||||
import Main from "./modules/main/index.ts";
|
import Main from "./modules/main/index.ts";
|
||||||
import ComponentLoading from "@/components/ComponentLoading";
|
import ComponentLoading from "@/components/ComponentLoading";
|
||||||
import Loading from "./modules/loading";
|
import Loading from "./modules/loading";
|
||||||
|
import Forget from "@/router/modules/forget";
|
||||||
|
|
||||||
const routes: RouteObject[] = [
|
const routes: RouteObject[] = [
|
||||||
{
|
{
|
||||||
@@ -27,6 +28,10 @@ const routes: RouteObject[] = [
|
|||||||
path: "/login",
|
path: "/login",
|
||||||
Component: (props) => ComponentLoading(Login, props),
|
Component: (props) => ComponentLoading(Login, props),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/forget",
|
||||||
|
Component: (props) => ComponentLoading(Forget, props),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "/main",
|
path: "/main",
|
||||||
Component: (props) => ComponentLoading(Main, props),
|
Component: (props) => ComponentLoading(Main, props),
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ export class useUserStore {
|
|||||||
userId: string = "";
|
userId: string = "";
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
// makeAutoObservable(this);
|
|
||||||
makeObservable(this, {
|
makeObservable(this, {
|
||||||
token: observable,
|
token: observable,
|
||||||
userId: observable,
|
userId: observable,
|
||||||
@@ -19,13 +18,23 @@ export class useUserStore {
|
|||||||
getUserId: action,
|
getUserId: action,
|
||||||
isHydrated: action,
|
isHydrated: action,
|
||||||
});
|
});
|
||||||
makePersistable(this, {
|
makePersistable(
|
||||||
|
this,
|
||||||
|
{
|
||||||
// 在构造函数内使用 makePersistable
|
// 在构造函数内使用 makePersistable
|
||||||
name: "userInfo", // 保存的name,用于在storage中的名称标识,只要不和storage中其他名称重复就可以
|
name: "userInfo", // 保存的name,用于在storage中的名称标识,只要不和storage中其他名称重复就可以
|
||||||
properties: ["token", "userId"], // 要保存的字段,这些字段会被保存在name对应的storage中,注意:不写在这里面的字段将不会被保存,刷新页面也将丢失:get字段例外。get数据会在数据返回后再自动计算
|
properties: ["token", "userId"], // 要保存的字段,这些字段会被保存在name对应的storage中,注意:不写在这里面的字段将不会被保存,刷新页面也将丢失:get字段例外。get数据会在数据返回后再自动计算
|
||||||
storage: handleLocalforage, // 保存的位置:看自己的业务情况选择,可以是localStorage,sessionstorage
|
storage: handleLocalforage, // 保存的位置:可以是localStorage,sessionstorage
|
||||||
// 。。还有一些其他配置参数,例如数据过期时间等等,可以康康文档,像storage这种字段可以配置在全局配置里,详见文档
|
removeOnExpiration: true, //如果 expireIn 具有值且已过期,则在调用 getItem 时将自动删除存储中的数据。默认值为 true。
|
||||||
}).then(
|
stringify: false, //如果为 true,则数据在传递给 setItem 之前将是 JSON.stringify。默认值为 true。
|
||||||
|
expireIn: 2592000000, // 一个以毫秒为单位的值,用于确定 getItem 何时不应检索存储中的数据。默认情况下永不过期。
|
||||||
|
debugMode: false, // 如果为 true,将为多个 mobx-persist-store 项调用 console.info。默认值为 false。
|
||||||
|
},
|
||||||
|
{
|
||||||
|
delay: 0, // 允许您设置一个 delay 选项来限制 write 函数的调用次数。默认情况下没有延迟。
|
||||||
|
fireImmediately: false, // 确定是应立即保留存储数据,还是等到存储中的属性发生更改。 false 默认情况下。
|
||||||
|
},
|
||||||
|
).then(
|
||||||
action(() => {
|
action(() => {
|
||||||
// persist 完成的回调,在这里可以执行一些拿到数据后需要执行的操作,如果在页面上要判断是否完成persist,使用 isHydrated
|
// persist 完成的回调,在这里可以执行一些拿到数据后需要执行的操作,如果在页面上要判断是否完成persist,使用 isHydrated
|
||||||
// console.log(persistStore)
|
// console.log(persistStore)
|
||||||
|
|||||||
1
src/types/user/user.d.ts
vendored
1
src/types/user/user.d.ts
vendored
@@ -4,6 +4,7 @@
|
|||||||
declare namespace API {
|
declare namespace API {
|
||||||
type PhoneRegisterRequest = {
|
type PhoneRegisterRequest = {
|
||||||
phone?: string;
|
phone?: string;
|
||||||
|
userName?: string;
|
||||||
password?: string;
|
password?: string;
|
||||||
confirmPassword?: string;
|
confirmPassword?: string;
|
||||||
activeCode?: string;
|
activeCode?: string;
|
||||||
|
|||||||
158
src/views/User/Forget/index.module.less
Normal file
158
src/views/User/Forget/index.module.less
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100vh;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login_content {
|
||||||
|
// margin-top: 40px;
|
||||||
|
// position: relative;
|
||||||
|
width: 790px;
|
||||||
|
background-color: rgb(255, 255, 255);
|
||||||
|
// height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: rgb(0 0 0 / 15%) 0px 2px 15px;
|
||||||
|
//float: right;
|
||||||
|
height: 550px;
|
||||||
|
position: relative;
|
||||||
|
padding: 30px 0 30px 0;
|
||||||
|
border-radius: 15px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.go_to_register {
|
||||||
|
cursor: pointer;
|
||||||
|
height: 70px;
|
||||||
|
width: 70px;
|
||||||
|
background-image: url('@/assets/icons/corner_markers.svg');
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
right: 0px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.go_to_register span {
|
||||||
|
font-size: 14px;
|
||||||
|
color: rgb(255, 255, 255);
|
||||||
|
font-weight: 500;
|
||||||
|
position: relative;
|
||||||
|
top: 10px;
|
||||||
|
right: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lang {
|
||||||
|
width: 100%;
|
||||||
|
height: 40px;
|
||||||
|
line-height: 44px;
|
||||||
|
text-align: right;
|
||||||
|
|
||||||
|
:global(.ant-dropdown-trigger) {
|
||||||
|
margin-right: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
//flex: 1;
|
||||||
|
//padding: 32px 0;
|
||||||
|
|
||||||
|
//box-sizing: border-box;
|
||||||
|
//margin: 60px auto;
|
||||||
|
//display: flex;
|
||||||
|
//align-items: center;
|
||||||
|
//justify-content: center;
|
||||||
|
//flex-direction: column;
|
||||||
|
//height: 100vh;
|
||||||
|
//overflow: auto;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.container {
|
||||||
|
background-image: url('@/assets/images/background.png');
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
padding: 32px 0 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content img {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
margin-left: 8px;
|
||||||
|
color: rgba(0, 0, 0, 0.2);
|
||||||
|
font-size: 24px;
|
||||||
|
vertical-align: middle;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: color 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: aquamarine;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mp_code {
|
||||||
|
padding: 0px 60px;
|
||||||
|
//width: 361px;
|
||||||
|
height: 490px;
|
||||||
|
background-color: rgb(255, 255, 255);
|
||||||
|
border-right: 0.5px solid rgb(196, 203, 215);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mp_code_title {
|
||||||
|
margin-top: 50px;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 24px;
|
||||||
|
color: rgb(24, 24, 24)
|
||||||
|
}
|
||||||
|
|
||||||
|
.mp_code_img {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert {
|
||||||
|
margin-top: 25px;
|
||||||
|
width: 250px;
|
||||||
|
padding: 5px;
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login_form {
|
||||||
|
// height: 100%;
|
||||||
|
// float: left;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mp_tips {
|
||||||
|
font-weight: bold;
|
||||||
|
color: rgb(7, 221, 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo span {
|
||||||
|
position: relative;
|
||||||
|
top: 2px;
|
||||||
|
color: rgba(0, 0, 0, .85);
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 33px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subTitle {
|
||||||
|
margin-top: 12px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
color: rgba(0, 0, 0, .45);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
256
src/views/User/Forget/index.tsx
Normal file
256
src/views/User/Forget/index.tsx
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
/** @format */
|
||||||
|
|
||||||
|
import { LockOutlined, MobileOutlined, SafetyOutlined, WechatOutlined } from "@ant-design/icons";
|
||||||
|
import { CaptFieldRef, ProFormCaptcha, ProFormText } from "@ant-design/pro-components";
|
||||||
|
import { Alert, Button, ConfigProvider, Form, Image, message, Space, Tabs } from "antd";
|
||||||
|
import { useRef, useState } from "react";
|
||||||
|
import { TinyColor } from "@ctrl/tinycolor";
|
||||||
|
import logo from "@/assets/icons/schisandra.svg";
|
||||||
|
// import background from '@/assets/images/background.png'
|
||||||
|
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 { getSms, register } from "@/api/user";
|
||||||
|
// import useStore from '@/utils/store/useStore.tsx'
|
||||||
|
type LoginType = "phone";
|
||||||
|
|
||||||
|
export default observer(() => {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const captchaRef = useRef<CaptFieldRef | null | undefined>();
|
||||||
|
const [loginType, setLoginType] = useState<LoginType>("phone");
|
||||||
|
const colors = ['#fc6076', '#ff9a44', '#ef9d43', '#e75516'];
|
||||||
|
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());
|
||||||
|
|
||||||
|
const items = [
|
||||||
|
{
|
||||||
|
key: "phone",
|
||||||
|
label: (
|
||||||
|
<span>
|
||||||
|
<SafetyOutlined />
|
||||||
|
重置密码
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const onSubmit = async (formData: object) => {
|
||||||
|
const res: any = await register(formData);
|
||||||
|
if (res && res.success) {
|
||||||
|
message.success(res.data);
|
||||||
|
} else {
|
||||||
|
message.error(res.data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div className={styles.container}>
|
||||||
|
<div className={styles.content}>
|
||||||
|
<Space>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<>
|
||||||
|
<ProFormText
|
||||||
|
fieldProps={{
|
||||||
|
size: "large",
|
||||||
|
prefix: <MobileOutlined className={"prefixIcon"} />,
|
||||||
|
autoComplete: "off",
|
||||||
|
}}
|
||||||
|
name="phone"
|
||||||
|
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 `${"获取验证码"}`;
|
||||||
|
}
|
||||||
|
return "获取验证码";
|
||||||
|
}}
|
||||||
|
name="activeCode"
|
||||||
|
phoneName={"phone"}
|
||||||
|
countDown={300}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: "请输入验证码!",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
fieldRef={captchaRef}
|
||||||
|
onGetCaptcha={async (phone: string) => {
|
||||||
|
const res: any = await getSms(phone);
|
||||||
|
if (res && res.success) {
|
||||||
|
message.success(res.data, 3);
|
||||||
|
} else {
|
||||||
|
message.warning(res.data, 3);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<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位字符,且必须包含字母和数字!",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ProFormText.Password
|
||||||
|
name="confirmPassword"
|
||||||
|
dependencies={["password"]}
|
||||||
|
fieldProps={{
|
||||||
|
size: "large",
|
||||||
|
prefix: <LockOutlined className={"prefixIcon"} />,
|
||||||
|
}}
|
||||||
|
placeholder="请再次确认密码"
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: "请再次确认密码!",
|
||||||
|
},
|
||||||
|
({ getFieldValue }) => ({
|
||||||
|
validator(_, value) {
|
||||||
|
if (!value || getFieldValue("password") === value) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
return Promise.reject(
|
||||||
|
new Error("两次输入的密码不一致!"),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
<ConfigProvider
|
||||||
|
theme={{
|
||||||
|
components: {
|
||||||
|
Button: {
|
||||||
|
colorPrimary: `linear-gradient(135deg, ${colors.join(", ")})`,
|
||||||
|
colorPrimaryHover: `linear-gradient(135deg, ${getHoverColors(colors).join(", ")})`,
|
||||||
|
colorPrimaryActive: `linear-gradient(135deg, ${getActiveColors(colors).join(", ")})`,
|
||||||
|
lineWidth: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
block
|
||||||
|
size="large"
|
||||||
|
onClick={async () => {
|
||||||
|
const validateFields = [
|
||||||
|
"phone",
|
||||||
|
"password",
|
||||||
|
"activeCode",
|
||||||
|
"confirmPassword",
|
||||||
|
];
|
||||||
|
await form
|
||||||
|
.validateFields(validateFields)
|
||||||
|
.then(async (values) => {
|
||||||
|
await onSubmit(values as API.PhoneRegisterRequest);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
}}>
|
||||||
|
重置
|
||||||
|
</Button>
|
||||||
|
</ConfigProvider>
|
||||||
|
</Form>
|
||||||
|
<a href="/login" className={styles.go_to_register}>
|
||||||
|
<span>登录</span>
|
||||||
|
</a>
|
||||||
|
</Space>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
<FooterComponent />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
LockOutlined,
|
LockOutlined,
|
||||||
MobileOutlined,
|
MobileOutlined,
|
||||||
QqOutlined,
|
QqOutlined,
|
||||||
|
SafetyOutlined,
|
||||||
UserOutlined,
|
UserOutlined,
|
||||||
WechatOutlined,
|
WechatOutlined,
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
@@ -15,7 +16,18 @@ import {
|
|||||||
ProFormCheckbox,
|
ProFormCheckbox,
|
||||||
ProFormText,
|
ProFormText,
|
||||||
} from "@ant-design/pro-components";
|
} from "@ant-design/pro-components";
|
||||||
import { Alert, Button, Divider, Form, Image, message, Space, Tabs, Tooltip } from "antd";
|
import {
|
||||||
|
Alert,
|
||||||
|
Button,
|
||||||
|
ConfigProvider,
|
||||||
|
Divider,
|
||||||
|
Form,
|
||||||
|
Image,
|
||||||
|
message,
|
||||||
|
Space,
|
||||||
|
Tabs,
|
||||||
|
Tooltip,
|
||||||
|
} from "antd";
|
||||||
import { CSSProperties, useEffect, useRef, useState } from "react";
|
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";
|
||||||
@@ -26,6 +38,7 @@ import RotateCaptcha, { CaptchaInstance, type TicketInfoType } from "react-rotat
|
|||||||
import { get, load } from "@/api/captcha/index.ts";
|
import { get, load } from "@/api/captcha/index.ts";
|
||||||
import { getSms, oauthLogin } from "@/api/user";
|
import { getSms, oauthLogin } from "@/api/user";
|
||||||
import { VerfiyCaptcha } from "@/api/captcha/api.ts";
|
import { VerfiyCaptcha } from "@/api/captcha/api.ts";
|
||||||
|
import { TinyColor } from "@ctrl/tinycolor";
|
||||||
// import useStore from '@/utils/store/useStore.tsx'
|
// import useStore from '@/utils/store/useStore.tsx'
|
||||||
type LoginType = "account" | "phone";
|
type LoginType = "account" | "phone";
|
||||||
|
|
||||||
@@ -40,6 +53,11 @@ 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>();
|
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 verify(token: string, deg: number): Promise<TicketInfoType> {
|
async function verify(token: string, deg: number): Promise<TicketInfoType> {
|
||||||
const data: any = {
|
const data: any = {
|
||||||
@@ -214,7 +232,7 @@ export default observer(() => {
|
|||||||
<ProFormCaptcha
|
<ProFormCaptcha
|
||||||
fieldProps={{
|
fieldProps={{
|
||||||
size: "large",
|
size: "large",
|
||||||
prefix: <LockOutlined className={"prefixIcon"} />,
|
prefix: <SafetyOutlined className={"prefixIcon"} />,
|
||||||
}}
|
}}
|
||||||
captchaProps={{
|
captchaProps={{
|
||||||
size: "large",
|
size: "large",
|
||||||
@@ -252,9 +270,21 @@ export default observer(() => {
|
|||||||
<ProFormCheckbox noStyle name="autoLogin">
|
<ProFormCheckbox noStyle name="autoLogin">
|
||||||
自动登录
|
自动登录
|
||||||
</ProFormCheckbox>
|
</ProFormCheckbox>
|
||||||
<a style={{ float: "right" }}>忘记密码</a>
|
<a href={"/forget"} style={{ float: "right" }}>
|
||||||
|
忘记密码
|
||||||
|
</a>
|
||||||
</div>
|
</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
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
block
|
block
|
||||||
@@ -278,6 +308,7 @@ export default observer(() => {
|
|||||||
}}>
|
}}>
|
||||||
登录
|
登录
|
||||||
</Button>
|
</Button>
|
||||||
|
</ConfigProvider>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
@@ -1,9 +1,15 @@
|
|||||||
/** @format */
|
/** @format */
|
||||||
|
|
||||||
import { LockOutlined, MobileOutlined, WechatOutlined } from "@ant-design/icons";
|
import {
|
||||||
import { ProFormCaptcha, ProFormText } from "@ant-design/pro-components";
|
LockOutlined,
|
||||||
import { Alert, Button, Form, Image, message, Space, Tabs } from "antd";
|
MobileOutlined,
|
||||||
import { useState } from "react";
|
SafetyOutlined,
|
||||||
|
UserOutlined,
|
||||||
|
WechatOutlined,
|
||||||
|
} from "@ant-design/icons";
|
||||||
|
import { CaptFieldRef, ProFormCaptcha, ProFormText } from "@ant-design/pro-components";
|
||||||
|
import { Alert, Button, ConfigProvider, Form, Image, message, Space, Tabs } from "antd";
|
||||||
|
import { useRef, 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'
|
||||||
import qrCode from "@/assets/images/login_qrcode-landaiqing.jpg";
|
import qrCode from "@/assets/images/login_qrcode-landaiqing.jpg";
|
||||||
@@ -11,11 +17,19 @@ 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 { getSms, register } from "@/api/user";
|
||||||
|
import { TinyColor } from "@ctrl/tinycolor";
|
||||||
// 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 captchaRef = useRef<CaptFieldRef | null | undefined>();
|
||||||
|
const colors = ["#6253E1", "#04BEFE"];
|
||||||
|
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());
|
||||||
|
|
||||||
const items = [
|
const items = [
|
||||||
{
|
{
|
||||||
key: "phone",
|
key: "phone",
|
||||||
@@ -99,6 +113,27 @@ export default observer(() => {
|
|||||||
}></Tabs>
|
}></Tabs>
|
||||||
|
|
||||||
<>
|
<>
|
||||||
|
<ProFormText
|
||||||
|
fieldProps={{
|
||||||
|
size: "large",
|
||||||
|
prefix: <UserOutlined className={"prefixIcon"} />,
|
||||||
|
autoComplete: "off",
|
||||||
|
}}
|
||||||
|
name="userName"
|
||||||
|
placeholder="请输入用户名"
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: "请输入用户名!",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^[a-zA-Z0-9_-]{3,16}$/,
|
||||||
|
message:
|
||||||
|
"用户名只能是4到16位(字母,数字,下划线,减号)",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
<ProFormText
|
<ProFormText
|
||||||
fieldProps={{
|
fieldProps={{
|
||||||
size: "large",
|
size: "large",
|
||||||
@@ -106,7 +141,7 @@ export default observer(() => {
|
|||||||
autoComplete: "off",
|
autoComplete: "off",
|
||||||
}}
|
}}
|
||||||
name="phone"
|
name="phone"
|
||||||
placeholder="请输入手机号!"
|
placeholder="请输入手机号"
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
@@ -119,6 +154,41 @@ export default observer(() => {
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<ProFormCaptcha
|
||||||
|
fieldProps={{
|
||||||
|
size: "large",
|
||||||
|
prefix: <SafetyOutlined className={"prefixIcon"} />,
|
||||||
|
}}
|
||||||
|
captchaProps={{
|
||||||
|
size: "large",
|
||||||
|
}}
|
||||||
|
placeholder={"请输入验证码"}
|
||||||
|
captchaTextRender={(timing: boolean) => {
|
||||||
|
if (timing) {
|
||||||
|
return `${"获取验证码"}`;
|
||||||
|
}
|
||||||
|
return "获取验证码";
|
||||||
|
}}
|
||||||
|
name="activeCode"
|
||||||
|
phoneName={"phone"}
|
||||||
|
countDown={300}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: "请输入验证码!",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
fieldRef={captchaRef}
|
||||||
|
onGetCaptcha={async (phone: string) => {
|
||||||
|
const res: any = await getSms(phone);
|
||||||
|
if (res && res.success) {
|
||||||
|
message.success(res.data, 3);
|
||||||
|
} else {
|
||||||
|
message.warning(res.data, 3);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
<ProFormText.Password
|
<ProFormText.Password
|
||||||
name="password"
|
name="password"
|
||||||
fieldProps={{
|
fieldProps={{
|
||||||
@@ -165,40 +235,18 @@ export default observer(() => {
|
|||||||
}),
|
}),
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<ProFormCaptcha
|
|
||||||
fieldProps={{
|
|
||||||
size: "large",
|
|
||||||
prefix: <LockOutlined className={"prefixIcon"} />,
|
|
||||||
}}
|
|
||||||
captchaProps={{
|
|
||||||
size: "large",
|
|
||||||
}}
|
|
||||||
placeholder={"请输入验证码"}
|
|
||||||
captchaTextRender={(timing: boolean) => {
|
|
||||||
if (timing) {
|
|
||||||
return `${"获取验证码"}`;
|
|
||||||
}
|
|
||||||
return "获取验证码";
|
|
||||||
}}
|
|
||||||
name="activeCode"
|
|
||||||
phoneName={"phone"}
|
|
||||||
countDown={300}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: "请输入验证码!",
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
onGetCaptcha={async (phone: string) => {
|
|
||||||
const res: any = await getSms(phone);
|
|
||||||
if (res && res.success) {
|
|
||||||
message.success(res.data, 3);
|
|
||||||
} else {
|
|
||||||
message.warning(res.data, 3);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
</>
|
||||||
|
<ConfigProvider
|
||||||
|
theme={{
|
||||||
|
components: {
|
||||||
|
Button: {
|
||||||
|
colorPrimary: `linear-gradient(90deg, ${colors.join(", ")})`,
|
||||||
|
colorPrimaryHover: `linear-gradient(90deg, ${getHoverColors(colors).join(", ")})`,
|
||||||
|
colorPrimaryActive: `linear-gradient(90deg, ${getActiveColors(colors).join(", ")})`,
|
||||||
|
lineWidth: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}>
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
block
|
block
|
||||||
@@ -206,6 +254,7 @@ export default observer(() => {
|
|||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
const validateFields = [
|
const validateFields = [
|
||||||
"phone",
|
"phone",
|
||||||
|
"userName",
|
||||||
"password",
|
"password",
|
||||||
"activeCode",
|
"activeCode",
|
||||||
"confirmPassword",
|
"confirmPassword",
|
||||||
@@ -221,6 +270,7 @@ export default observer(() => {
|
|||||||
}}>
|
}}>
|
||||||
注册
|
注册
|
||||||
</Button>
|
</Button>
|
||||||
|
</ConfigProvider>
|
||||||
</Form>
|
</Form>
|
||||||
<a href="/login" className={styles.go_to_register}>
|
<a href="/login" className={styles.go_to_register}>
|
||||||
<span>登录</span>
|
<span>登录</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user