Browse Source

增加邮箱登录

main
lk 3 months ago
parent
commit
a5ec0dae95
  1. 206
      src/pages/login/index.tsx
  2. 35
      src/request-base-url-interceptors.ts
  3. 195
      src/request.ts
  4. 7
      src/routes.tsx
  5. 61
      src/service/system.ts
  6. 196
      src/store/system/user.ts
  7. 18
      src/types/system/login.d.ts
  8. 1
      vite.config.ts

206
src/pages/login/index.tsx

@ -1,37 +1,56 @@
import { Layout, Tabs, Input, Button, Typography, Row, Col, Form } from "antd";
import { QrcodeOutlined, UserOutlined, LockOutlined } from "@ant-design/icons";
import SelectLang from "@/components/select-lang"; import SelectLang from "@/components/select-lang";
import { createFileRoute } from "@tanstack/react-router"; import { createFileRoute } from "@tanstack/react-router";
import { Button, Form, Input, Radio, Space } from "antd";
import { useAtom, useAtomValue } from "jotai"; import { useAtom, useAtomValue } from "jotai";
import { useTranslation } from "@/i18n.ts"; import { useTranslation } from "@/i18n.ts";
import { loginAtom, loginFormAtom } from "@/store/system/user.ts";
import React, {memo, useLayoutEffect, useState} from "react";
import { emailCodeAtom, emailLoginAtom, upLoginAtom, upLoginFormAtom } from "@/store/system/user.ts";
import React, { memo, useEffect, useLayoutEffect, useState } from "react";
import { useStyles } from "./style.ts"; import { useStyles } from "./style.ts";
const { Title, Text, Link } = Typography;
const { TabPane } = Tabs;
const Login = memo(() => { const Login = memo(() => {
const { styles } = useStyles();
const { t } = useTranslation();
const [values, setValues] = useAtom(loginFormAtom);
const { isPending, mutate } = useAtomValue(loginAtom);
const [form] = Form.useForm();
const [upform] = Form.useForm();
const [emailform] = Form.useForm();
const { mutate: upMutate } = useAtomValue(upLoginAtom);
const { mutate: emailCodeMutate } = useAtomValue(emailCodeAtom);
const { mutate: emailLoginMutate } = useAtomValue(emailLoginAtom);
const handleSubmit = () => {
form.validateFields().then(() => {
mutate(values);
});
const uphandleSubmit = (values: any) => {
console.log(values);
upMutate(values);
};
const emailhandleSubmit = (values: any) => {
console.log(values);
emailLoginMutate(values);
}; };
const handleGetCode = () => {
const email = emailform.getFieldValue("email");
setCountdown(10);
setIsButtonDisabled(true);
emailCodeMutate({ email });
};
const [countdown, setCountdown] = useState<number>(0);
const [isButtonDisabled, setIsButtonDisabled] = useState<boolean>(false);
const [loginMod, setLoginMod] = useState([
useEffect(() => {
let timer: number;
if (countdown > 0) {
timer = setTimeout(() => setCountdown(countdown - 1), 1000);
} else {
setIsButtonDisabled(false);
}
return () => clearTimeout(timer);
}, [countdown]);
const [loginMod] = useState([
{ value: "single", info: "帐号密码登录" }, { value: "single", info: "帐号密码登录" },
{ value: "multiple-email", info: "邮箱登录" }, { value: "multiple-email", info: "邮箱登录" },
{ value: "multiple-plane", info: "飞机登录" }, { value: "multiple-plane", info: "飞机登录" },
]); ]);
const [selectedMode, setSelectedMode] = useState(loginMod[0].value);
const handleModeChange = (e: any) => {
setSelectedMode(e.target.value);
};
useLayoutEffect(() => { useLayoutEffect(() => {
document.body.className = "login"; document.body.className = "login";
return () => { return () => {
@ -40,58 +59,119 @@ const Login = memo(() => {
}, []); }, []);
return ( return (
<div className={styles.container}>
<div className={styles.language}>
<SelectLang />
</div>
<div className={styles.loginBlock}>
<div className={styles.innerBlock}>
<Radio.Group style={{ marginBottom: 8 }} value={selectedMode} onChange={handleModeChange}>
{loginMod.map((mod) => (
<Radio.Button key={mod.value} value={mod.value}>
{mod.info}
</Radio.Button>
))}
</Radio.Group>
<Form
form={form}
disabled={isPending}
initialValues={values}
onValuesChange={(_, allValues) => {
setValues(allValues);
<Layout
style={{
height: "100vh",
background: "url('https://placehold.co/1920x1080') no-repeat center center",
backgroundSize: "cover",
}}
>
<Row justify="center" align="middle" style={{ height: "100%" }}>
<Col>
<Row>
<Col span={12} style={{ padding: "20px" }}>
<Title level={3}></Title>
<Text></Text>
<br />
<Link href="#"> &gt;</Link>
</Col>
<Col
span={12}
style={{
padding: "20px",
background: "#fff",
borderRadius: "8px",
position: "relative",
width: "500px",
height: "400px",
}}
>
<div
style={{
position: "absolute",
top: 0,
right: 0,
width: 0,
height: 0,
borderLeft: "80px solid transparent",
borderTop: "80px solid #ff9800",
textAlign: "center",
cursor: "pointer", // 设置手型光标
}}
>
<text
style={{
cursor: "pointer", // 设置手型光标
position: "absolute",
top: "-70px",
right: "0px",
color: "#fff",
fontSize: "16px",
transform: "rotate(0deg)",
textAlign: "center",
width: "60px",
display: "block",
}} }}
size="large"
> >
<Form.Item name={"username"} rules={[{ required: true, message: t("login.usernameMsg") }]}>
<Input maxLength={20} placeholder={t("login.username")} />
</text>
</div>
<Tabs defaultActiveKey="1">
<TabPane tab="账号密码登录" key="1">
<Form form={upform} onFinish={uphandleSubmit}>
<Form.Item name="username" rules={[{ required: true, message: "请输入账号" }]}>
<Input size="large" placeholder="请输入账号" prefix={<UserOutlined />} />
</Form.Item> </Form.Item>
<Form.Item name={"password"} rules={[{ required: true, message: t("login.passwordMsg") }]}>
<Input.Password placeholder={t("login.password")} />
<Form.Item name="password" rules={[{ required: true, message: "请输入登录密码" }]}>
<Input.Password size="large" placeholder="请输入登录密码" prefix={<LockOutlined />} />
</Form.Item> </Form.Item>
<Form.Item noStyle>
<Space direction="horizontal">
<Form.Item name={"code"} rules={[{ required: true, message: t("login.codeMsg") }]}>
<Input placeholder={t("login.code")} />
{/*<img src="https://img.alicdn.com/tfs/TB1KtN6mKH2gK0jSZJnXXaT1FXa-1014-200.png" alt="验证码" />*/}
{/*<Form.Item name="code" rules={[{ required: true, message: "请输入验证码" }]}>*/}
{/* <Input.Password size="large" placeholder="验证码" prefix={<LockOutlined />} />*/}
{/*</Form.Item>*/}
<Button type="primary" htmlType="submit" style={{ width: "100%" }}>
</Button>
</Form>
</TabPane>
<TabPane tab="邮箱登录" key="2">
<Form form={emailform} onFinish={emailhandleSubmit}>
<Form.Item name="email" rules={[{ required: true, message: "请输入邮箱" }]}>
<Input
size="large"
placeholder="请输入邮箱"
prefix={<UserOutlined />}
addonAfter={
<Button onClick={handleGetCode} disabled={isButtonDisabled}>
{isButtonDisabled ? `${countdown}秒后重试` : "获得验证码"}
</Button>
}
/>
</Form.Item> </Form.Item>
</Space>
<Form.Item name="code" rules={[{ required: true, message: "请输入验证码" }]}>
<Input.Password size="large" placeholder="请输入验证码" prefix={<LockOutlined />} />
</Form.Item> </Form.Item>
<Form.Item style={{ marginBottom: 10 }}>
<Button
htmlType={"submit"}
type="primary"
onClick={handleSubmit}
className={"submitBtn"}
loading={isPending}
disabled={isPending}
>
{t("login.submit")}
<Button type="primary" htmlType="submit" style={{ width: "100%" }}>
</Button> </Button>
</Form.Item>
</Form> </Form>
</div>
</div>
</div>
</TabPane>
{/*<TabPane tab="飞机登录" key="3">*/}
{/* <Form form={form} onFinish={handleSubmit}>*/}
{/* <Form.Item name="planeCode" rules={[{ required: true, message: "请输入飞机代码" }]}>*/}
{/* <Input size="large" placeholder="请输入飞机代码" prefix={<UserOutlined />} />*/}
{/* </Form.Item>*/}
{/* </Form>*/}
{/*</TabPane>*/}
</Tabs>
<Row justify="space-between" style={{ marginTop: "10px" }}>
<Link href="#"></Link>
<Link href="#"></Link>
</Row>
</Col>
</Row>
</Col>
</Row>
</Layout>
); );
}); });

35
src/request-base-url-interceptors.ts

@ -1,32 +1,33 @@
import { AxiosInstance } from 'axios'
import { AxiosInstance } from "axios";
const baseURLMap = { const baseURLMap = {
package: 'http://154.88.7.8:45321/api/v1',
movie: 'http://47.113.117.106:10000/api/v1',
package: "http://154.88.7.8:45321/api/v1",
movie: "http://47.113.117.106:10000/api/v1",
default: 'http://127.0.0.1:8686/api/v1', default: 'http://127.0.0.1:8686/api/v1',
}
};
/** /**
* urlbaseURL * urlbaseURL
* @param axiosInstance * @param axiosInstance
*/ */
export const requestBaseUrlInterceptors = (axiosInstance: AxiosInstance) => { export const requestBaseUrlInterceptors = (axiosInstance: AxiosInstance) => {
//拦截url,适应不同的baseURL //拦截url,适应不同的baseURL
axiosInstance.interceptors.request.use((config) => {
const { url } = config
axiosInstance.interceptors.request.use(
(config) => {
const { url } = config;
//取url的第1个/后的字符串 //取url的第1个/后的字符串
const key = url?.split('/')[1]
const baseURL = baseURLMap[key!]
const key = url?.split("/")[1];
const baseURL = baseURLMap[key!];
if (baseURL) { if (baseURL) {
config.baseURL = baseURL
config.baseURL = baseURL;
} else { } else {
config.baseURL = baseURLMap['default']
config.baseURL = baseURLMap["default"];
} }
return config
}, (error) => {
return config;
},
(error) => {
// console.log('error', error) // console.log('error', error)
return Promise.reject(error)
})
}
return Promise.reject(error);
},
);
};

195
src/request.ts

@ -1,178 +1,179 @@
import { getToken, setToken } from '@/store/system.ts'
import { IApiResult } from '@/global'
import { Record } from '@icon-park/react'
import { message } from 'antd'
import axios, {
AxiosRequestConfig,
AxiosInstance, AxiosResponse,
} from 'axios'
import { getToken, setToken } from "@/store/system.ts";
import { IApiResult } from "@/global";
import { Record } from "@icon-park/react";
import { message } from "antd";
import axios, { AxiosRequestConfig, AxiosInstance, AxiosResponse } from "axios";
export type { AxiosRequestConfig };
type FetchMethod = <T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>) => Promise<IApiResult<T>>;
export type { AxiosRequestConfig }
type FetchMethod = <T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>) => Promise<IApiResult<T>>
interface RequestMethods extends Pick<AxiosInstance, 'get' | 'post' | 'put' | 'delete' | 'request' | 'postForm' | 'patch' | 'patchForm' | 'putForm' | 'options'> {
download: (url: string, data?: any) => Promise<BlobPart>
interface RequestMethods
extends Pick<
AxiosInstance,
"get" | "post" | "put" | "delete" | "request" | "postForm" | "patch" | "patchForm" | "putForm" | "options"
> {
download: (url: string, data?: any) => Promise<BlobPart>;
} }
const axiosInstance = axios.create({ const axiosInstance = axios.create({
baseURL: '/api/v1',
baseURL: "/api/v1",
// timeout: 1000, // timeout: 1000,
headers: { headers: {
'Content-Type': 'application/json',
"Content-Type": "application/json",
}, },
validateStatus: status => {
return status >= 200 && status < 300
}
})
validateStatus: (status) => {
return status >= 200 && status < 300;
},
});
//拦截request,添加token //拦截request,添加token
axiosInstance.interceptors.request.use((config) => {
const token = getToken()
axiosInstance.interceptors.request.use(
(config) => {
const token = getToken();
if (token) { if (token) {
config.headers.Authorization = `Bearer ${token}`
config.headers.Authorization = `Bearer ${token}`;
} }
return config
}, (error) => {
console.log('error', error)
return Promise.reject(error)
})
return config;
},
(error) => {
console.log("error", error);
return Promise.reject(error);
},
);
//拦截response,返回data //拦截response,返回data
axiosInstance.interceptors.response.use( axiosInstance.interceptors.response.use(
(response) => { (response) => {
// console.log('response', response.data) // console.log('response', response.data)
message.destroy()
message.destroy();
const result = response.data as IApiResult
const result = response.data as IApiResult;
switch (result.code) { switch (result.code) {
case 0: case 0:
case 200: case 200:
//login //login
if (response.config.url?.includes('/sys/login')) {
setToken(result.data.token)
const search = new URLSearchParams(window.location.search)
if (response.config.url?.includes("/sys/login")) {
setToken(result.data.token);
const search = new URLSearchParams(window.location.search);
// eslint-disable-next-line no-case-declarations // eslint-disable-next-line no-case-declarations
const redirect = search.get('redirect')
const redirect = search.get("redirect");
if (redirect) { if (redirect) {
window.location.href = redirect
window.location.href = redirect;
} else {
window.location.href = "/";
} }
} }
return response
return response;
case 401: case 401:
setToken('')
if (window.location.pathname === '/login') {
return Promise.reject(new Error('to login'))
setToken("");
if (window.location.pathname === "/login") {
return Promise.reject(new Error("to login"));
} }
// 401: 未登录 // 401: 未登录
message.error('登录失败,跳转重新登录')
message.error("登录失败,跳转重新登录");
// eslint-disable-next-line no-case-declarations // eslint-disable-next-line no-case-declarations
const search = new URLSearchParams(window.location.search)
const search = new URLSearchParams(window.location.search);
// eslint-disable-next-line no-case-declarations // eslint-disable-next-line no-case-declarations
let redirect = window.location.pathname
if (search.toString() !== '') {
redirect = window.location.pathname + '?=' + search.toString()
let redirect = window.location.pathname;
if (search.toString() !== "") {
redirect = window.location.pathname + "?=" + search.toString();
} }
window.location.href = `/login?redirect=${encodeURIComponent(redirect)}`
return Promise.reject(new Error('to login'))
window.location.href = `/login?redirect=${encodeURIComponent(redirect)}`;
return Promise.reject(new Error("to login"));
default: default:
message.error(result.message ?? '请求失败')
return Promise.reject(response)
message.error(result.message ?? "请求失败");
return Promise.reject(response);
} }
}, (error) => {
},
(error) => {
// console.log('error', error) // console.log('error', error)
message.destroy()
const { response } = error
message.destroy();
const { response } = error;
if (response) { if (response) {
switch (response.status) { switch (response.status) {
case 401: case 401:
if (window.location.pathname === '/login') {
return
if (window.location.pathname === "/login") {
return;
} }
setToken('')
setToken("");
// 401: 未登录 // 401: 未登录
message.error('登录失败,跳转重新登录')
message.error("登录失败,跳转重新登录");
// eslint-disable-next-line no-case-declarations // eslint-disable-next-line no-case-declarations
const search = new URLSearchParams(window.location.search)
const search = new URLSearchParams(window.location.search);
// eslint-disable-next-line no-case-declarations // eslint-disable-next-line no-case-declarations
let redirect = window.location.pathname
if (search.toString() !== '') {
redirect = window.location.pathname + '?=' + search.toString()
let redirect = window.location.pathname;
if (search.toString() !== "") {
redirect = window.location.pathname + "?=" + search.toString();
} }
window.location.href = `/login?redirect=${encodeURIComponent(redirect)}`
return
window.location.href = `/login?redirect=${encodeURIComponent(redirect)}`;
return;
case 403: case 403:
message.error('没有权限')
break
message.error("没有权限");
break;
case 404: case 404:
message.error('请求的资源不存在')
break
message.error("请求的资源不存在");
break;
default: default:
message.error(response.data.message ?? response.data ?? error.message ?? '请求失败')
return Promise.reject(response)
message.error(response.data.message ?? response.data ?? error.message ?? "请求失败");
return Promise.reject(response);
} }
} }
return Promise.reject(error)
})
return Promise.reject(error);
},
);
//扩展download方法 //扩展download方法
// @ts-ignore fix download // @ts-ignore fix download
axiosInstance.download = (url: string, data?: any) => { axiosInstance.download = (url: string, data?: any) => {
const formData = new FormData()
const formData = new FormData();
for (const key in data) { for (const key in data) {
formData.append(key, data[key])
formData.append(key, data[key]);
} }
const config = { const config = {
method: 'post',
method: "post",
url, url,
data: formData, data: formData,
responseType: 'blob',
responseType: "blob",
timeout: 40 * 1000, timeout: 40 * 1000,
} as AxiosRequestConfig
return axiosInstance.request(config)
}
} as AxiosRequestConfig;
return axiosInstance.request(config);
};
//创建返回IApiResult类型的request //创建返回IApiResult类型的request
export const createFetchMethods = () => { export const createFetchMethods = () => {
const methods = {}
const methods = {};
for (const method of Object.keys(axiosInstance)) { for (const method of Object.keys(axiosInstance)) {
methods[method] = async <T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>) => { methods[method] = async <T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>) => {
config = config ?? {}
config.url = url
config.method = method
const isGet = method === 'get'
config = config ?? {};
config.url = url;
config.method = method;
const isGet = method === "get";
if (isGet) { if (isGet) {
config.params = data
config.params = data;
} else { } else {
config.data = data
config.data = data;
} }
return axiosInstance(config) return axiosInstance(config)
.then((response: AxiosResponse<IApiResult<T>>) => { .then((response: AxiosResponse<IApiResult<T>>) => {
if (response.data.code !== 200 && response.data.code !== 0) { if (response.data.code !== 200 && response.data.code !== 0) {
throw new Error(response.data.message)
throw new Error(response.data.message);
} }
return response.data as IApiResult<T>
return response.data as IApiResult<T>;
}) })
.catch((err) => { .catch((err) => {
throw err
})
}
throw err;
});
};
} }
return methods as Record<keyof RequestMethods, FetchMethod>
}
return methods as Record<keyof RequestMethods, FetchMethod>;
};
export const request = createFetchMethods()
export default request
export const request = createFetchMethods();
export default request;

7
src/routes.tsx

@ -109,6 +109,11 @@ const loginRoute = LoginRouteImport.update({
getParentRoute: () => emptyRoute, getParentRoute: () => emptyRoute,
} as any) } as any)
// const login2Route = LoginRouteImport.update({
// path: '/login2',
// getParentRoute: () => emptyRoute,
// } as any)
// //
// const menusRoute = createRoute({ // const menusRoute = createRoute({
@ -303,7 +308,7 @@ const routeTree = rootRoute.addChildren(
//非Layout //非Layout
loginRoute, loginRoute,
emptyRoute, emptyRoute,
//login2Route,
// 添加新的自定义路由 // 添加新的自定义路由
createRoute({ createRoute({
getParentRoute: () => rootRoute, getParentRoute: () => rootRoute,

61
src/service/system.ts

@ -1,52 +1,55 @@
import { IPageResult } from '@/global'
import request from '../request.ts'
import { createCURD } from '@/service/base.ts'
import { System } from '@/types'
import { IPageResult } from "@/global";
import request from "../request.ts";
import { createCURD } from "@/service/base.ts";
import { System } from "@/types";
const systemServ = { const systemServ = {
dept: { dept: {
...createCURD<any, System.IDepartment>('/sys/dept'),
...createCURD<any, System.IDepartment>("/sys/dept"),
tree: () => { tree: () => {
return request.get<{ tree: System.IDepartment }>('/sys/dept/tree')
}
return request.get<{ tree: System.IDepartment }>("/sys/dept/tree");
},
}, },
menus: { menus: {
...createCURD<any, System.IMenu>('/sys/menu')
...createCURD<any, System.IMenu>("/sys/menu"),
},
uplogin: (data: any) => {
return request.post<System.LoginResponse>("/sys/login", data);
},
emailCode: (data: any) => {
return request.post<System.LoginResponse>("/sys/email", data);
}, },
login: (data: System.LoginRequest) => {
return request.post<System.LoginResponse>('/sys/login', data)
emailLogin: (data: any) => {
return request.post<System.LoginResponse>("/sys/login/email", data);
}, },
logout: () => { logout: () => {
// //
}, },
user: { user: {
...createCURD<any, System.IUser>('/sys/user'),
...createCURD<any, System.IUser>("/sys/user"),
current: () => { current: () => {
return request.get<System.IUserInfo>('/sys/user/info')
return request.get<System.IUserInfo>("/sys/user/info");
}, },
menus: () => { menus: () => {
return request.get<IPageResult<System.IMenu[]>>('/sys/user/menus')
return request.get<IPageResult<System.IMenu[]>>("/sys/user/menus");
}, },
resetPassword: (id: number) => { resetPassword: (id: number) => {
return request.post<any>(`/sys/user/reset/password`, { id })
}
return request.post<any>(`/sys/user/reset/password`, { id });
},
}, },
role: { role: {
...createCURD<any, System.IRole>('/sys/role')
...createCURD<any, System.IRole>("/sys/role"),
}, },
logs: { logs: {
login: { login: {
...createCURD<any, ILoginLog>('/sys/log/login'),
clear: (params: {
start: string,
end: string
}) => {
return request.post<any>('/sys/log/login/clear', params)
}
}
}
}
...createCURD<any, ILoginLog>("/sys/log/login"),
clear: (params: { start: string; end: string }) => {
return request.post<any>("/sys/log/login/clear", params);
},
},
},
};
export default systemServ
export default systemServ;

196
src/store/system/user.ts

@ -1,171 +1,203 @@
import { appAtom, setToken } from '@/store/system.ts'
import { atom } from 'jotai'
import { IApiResult, IAuth, IPage, IPageResult, MenuItem } from '@/global'
import { atomWithMutation, atomWithQuery, queryClientAtom } from 'jotai-tanstack-query'
import systemServ from '@/service/system.ts'
import { formatMenuData, isDev } from '@/utils'
import { message } from 'antd'
import { t } from 'i18next'
import { System } from '@/types'
import { atomWithStorage } from 'jotai/utils'
import { IUserInfo } from '@/types/system/user'
import { appAtom, setToken } from "@/store/system.ts";
import { atom } from "jotai";
import { IApiResult, IAuth, IPage, IPageResult, MenuItem } from "@/global";
import { atomWithMutation, atomWithQuery, queryClientAtom } from "jotai-tanstack-query";
import systemServ from "@/service/system.ts";
import { formatMenuData, isDev } from "@/utils";
import { message } from "antd";
import { t } from "i18next";
import { System } from "@/types";
import { atomWithStorage } from "jotai/utils";
import { IUserInfo } from "@/types/system/user";
export interface UPLoginRequest {
mfa_status: boolean;
account: string;
username: string;
password: string;
code: string;
}
export const authAtom = atom<IAuth>({ export const authAtom = atom<IAuth>({
isLogin: false, isLogin: false,
authKey: []
})
authKey: [],
});
const devLogin = { const devLogin = {
username: 'SupperAdmin',
password: 'kk123456',
code: '123456'
}
export const loginFormAtom = atom<System.LoginRequest>({
...(isDev ? devLogin : {})
} as System.LoginRequest)
username: "SupperAdmin",
password: "kk123456",
code: "123456",
};
export const upLoginFormAtom = atom<UPLoginRequest>({
...(isDev ? devLogin : {}),
} as UPLoginRequest);
export const upLoginAtom = atomWithMutation<any, System.LoginRequest>((get) => ({
mutationKey: ["uplogin"],
mutationFn: async (params) => {
return await systemServ.uplogin(params);
},
onSuccess: (res) => {
message.success(t("login.success"));
// console.log('login success', res)
get(userMenuDataAtom).refetch().then();
return res.data;
},
retry: false,
}));
export const loginAtom = atomWithMutation<any, System.LoginRequest>((get) => ({
mutationKey: [ 'login' ],
export const emailCodeAtom = atomWithMutation<any, any>((get) => ({
mutationKey: ["emailCode"],
mutationFn: async (params) => {
return await systemServ.emailCode(params);
},
onSuccess: (res) => {
message.success(t("login.success"));
// console.log('login success', res)
get(userMenuDataAtom).refetch().then();
return res.data;
},
retry: false,
}));
export const emailLoginAtom = atomWithMutation<any, any>((get) => ({
mutationKey: ["emailLogin"],
mutationFn: async (params) => { mutationFn: async (params) => {
return await systemServ.login(params)
return await systemServ.emailLogin(params);
}, },
onSuccess: (res) => { onSuccess: (res) => {
message.success(t('login.success'))
message.success(t("login.success"));
// console.log('login success', res) // console.log('login success', res)
get(userMenuDataAtom).refetch().then()
return res.data
get(userMenuDataAtom).refetch().then();
return res.data;
}, },
retry: false, retry: false,
}))
}));
export const logoutAtom = atomWithMutation(() => ({ export const logoutAtom = atomWithMutation(() => ({
mutationKey: [ 'logout' ],
mutationKey: ["logout"],
mutationFn: async () => { mutationFn: async () => {
setToken('')
return true
setToken("");
return true;
}, },
}))
export const currentStaticUserAtom = atomWithStorage<IUserInfo | null>('user', null)
}));
export const currentStaticUserAtom = atomWithStorage<IUserInfo | null>("user", null);
export const currentUserAtom = atomWithQuery<IApiResult<System.IUserInfo>, any, System.IUserInfo>((get) => { export const currentUserAtom = atomWithQuery<IApiResult<System.IUserInfo>, any, System.IUserInfo>((get) => {
return { return {
queryKey: [ 'user_info', get(appAtom).token ],
queryKey: ["user_info", get(appAtom).token],
queryFn: async () => { queryFn: async () => {
return await systemServ.user.current()
return await systemServ.user.current();
}, },
select: (data) => { select: (data) => {
// store.set(currentStaticUserAtom, data.data) // store.set(currentStaticUserAtom, data.data)
return data.data
return data.data;
}, },
}
})
};
});
export const userMenuDataAtom = atomWithQuery<IApiResult<IPageResult<System.IMenu[]>>, any, MenuItem[]>((get) => ({ export const userMenuDataAtom = atomWithQuery<IApiResult<IPageResult<System.IMenu[]>>, any, MenuItem[]>((get) => ({
enabled: false, enabled: false,
queryKey: [ 'user_menus', get(appAtom).token ],
queryKey: ["user_menus", get(appAtom).token],
queryFn: async () => { queryFn: async () => {
return await systemServ.user.menus()
return await systemServ.user.menus();
}, },
select: (data) => { select: (data) => {
return formatMenuData(data.data.rows as any ?? [], [])
return formatMenuData((data.data.rows as any) ?? [], []);
}, },
retry: false, retry: false,
}))
}));
export type UserSearch = { export type UserSearch = {
dept_id?: any,
key?: string
}
dept_id?: any;
key?: string;
};
export const userSearchAtom = atom<UserSearch>({} as UserSearch)
export const userSearchAtom = atom<UserSearch>({} as UserSearch);
//=======user page store====== //=======user page store======
export const userPageAtom = atom<IPage>({ export const userPageAtom = atom<IPage>({
pageSize: 10, pageSize: 10,
page: 1
})
page: 1,
});
// user list // user list
export const userListAtom = atomWithQuery((get) => { export const userListAtom = atomWithQuery((get) => {
return { return {
queryKey: [ 'user_list', get(userSearchAtom), get(userPageAtom) ],
queryKey: ["user_list", get(userSearchAtom), get(userPageAtom)],
queryFn: async ({ queryKey: [, params, page] }) => { queryFn: async ({ queryKey: [, params, page] }) => {
return await systemServ.user.list({ return await systemServ.user.list({
...params as any,
...page as any,
})
...(params as any),
...(page as any),
});
}, },
select: (data) => { select: (data) => {
return data.data
return data.data;
}, },
}
})
};
});
// user selected // user selected
export const userSelectedAtom = atom<System.IUser>({} as System.IUser)
export const userSelectedAtom = atom<System.IUser>({} as System.IUser);
export const defaultUserData = { export const defaultUserData = {
id: 0, id: 0,
dept_id: 0, dept_id: 0,
role_id: 0, role_id: 0,
} as System.IUser
} as System.IUser;
//save or update user //save or update user
export const saveOrUpdateUserAtom = atomWithMutation<IApiResult, System.IUser>((get) => ({ export const saveOrUpdateUserAtom = atomWithMutation<IApiResult, System.IUser>((get) => ({
mutationKey: [ 'save_user' ],
mutationKey: ["save_user"],
mutationFn: async (params) => { mutationFn: async (params) => {
params.status = params.status ? '1' : '0'
const isAdd = 0 === params.id
params.status = params.status ? "1" : "0";
const isAdd = 0 === params.id;
if (isAdd) { if (isAdd) {
return await systemServ.user.add(params)
return await systemServ.user.add(params);
} }
return await systemServ.user.update(params)
return await systemServ.user.update(params);
}, },
onSuccess: (res) => { onSuccess: (res) => {
const isAdd = !!res.data?.id
message.success(t(isAdd ? 'message.saveSuccess' : 'message.editSuccess', '保存成功'))
const isAdd = !!res.data?.id;
message.success(t(isAdd ? "message.saveSuccess" : "message.editSuccess", "保存成功"));
//刷新userList //刷新userList
get(queryClientAtom).invalidateQueries({ queryKey: [ 'user_list' ] })
get(queryClientAtom).invalidateQueries({ queryKey: ["user_list"] });
return res
return res;
}, },
}))
}));
//delete user //delete user
export const batchUserIdsAtom = atom<number[]>([])
export const batchUserIdsAtom = atom<number[]>([]);
export const deleteUserAtom = atomWithMutation<IApiResult, number[]>((get) => ({ export const deleteUserAtom = atomWithMutation<IApiResult, number[]>((get) => ({
mutationKey: [ 'delete_user' ],
mutationKey: ["delete_user"],
mutationFn: async (params) => { mutationFn: async (params) => {
return await systemServ.user.batchDelete(params)
return await systemServ.user.batchDelete(params);
}, },
onSuccess: () => { onSuccess: () => {
message.success(t('message.deleteSuccess', '删除成功'))
message.success(t("message.deleteSuccess", "删除成功"));
//刷新userList //刷新userList
get(queryClientAtom).invalidateQueries({ queryKey: [ 'user_list' ] })
return true
get(queryClientAtom).invalidateQueries({ queryKey: ["user_list"] });
return true;
}, },
}))
}));
//reset password //reset password
export const resetPasswordAtom = atomWithMutation<IApiResult, number>(() => ({ export const resetPasswordAtom = atomWithMutation<IApiResult, number>(() => ({
mutationKey: [ 'reset_password' ],
mutationKey: ["reset_password"],
mutationFn: async (id) => { mutationFn: async (id) => {
return await systemServ.user.resetPassword(id)
return await systemServ.user.resetPassword(id);
}, },
onSuccess: () => { onSuccess: () => {
message.success(t('message.resetSuccess', '重置成功'))
message.success(t("message.resetSuccess", "重置成功"));
//刷新userList //刷新userList
// get(queryClientAtom).invalidateQueries({ queryKey: [ 'user_list' ] }) // get(queryClientAtom).invalidateQueries({ queryKey: [ 'user_list' ] })
return true
return true;
}, },
onError: () => { onError: () => {
message.error(t('message.resetError', '重置失败'))
message.error(t("message.resetError", "重置失败"));
}, },
}))
}));

18
src/types/system/login.d.ts

@ -1,14 +1,12 @@
export interface LoginRequest {
'mfa_status': boolean;
'username': string;
'password': string;
'code': string;
export interface UPLoginRequest {
mfa_status: boolean;
account: string;
username: string;
password: string;
code: string;
} }
export interface LoginResponse { export interface LoginResponse {
'token': string;
'mfa_status': boolean;
token: string;
mfa_status: boolean;
} }

1
vite.config.ts

@ -13,6 +13,7 @@ const proxyMap = {
//'/api/v1/certold': 'http://192.168.31.41:8000', //'/api/v1/certold': 'http://192.168.31.41:8000',
"/api/v1/cert": "http://127.0.0.1:8000", "/api/v1/cert": "http://127.0.0.1:8000",
//'/api/v1/cert': 'http://192.168.31.41:8000', //'/api/v1/cert': 'http://192.168.31.41:8000',
"/api/v1/sys": "http://192.168.31.41:8686/",
} as Record<any, string>; } as Record<any, string>;
const proxyConfig = Object.keys(proxyMap).reduce( const proxyConfig = Object.keys(proxyMap).reduce(

Loading…
Cancel
Save