feat: loginOut
This commit is contained in:
15
src/app.tsx
15
src/app.tsx
@@ -12,11 +12,12 @@ import {
|
|||||||
SelectLang,
|
SelectLang,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { getInfo } from "@/services/login";
|
import { getInfo } from "@/services/login";
|
||||||
import type { UserVO, TokenType } from "@/services/login/types";
|
import type { UserVO, TokenType, UserInfoVO } from "@/services/login/types";
|
||||||
import defaultSettings from "../config/defaultSettings";
|
import defaultSettings from "../config/defaultSettings";
|
||||||
import { errorConfig } from "./requestErrorConfig";
|
import { errorConfig } from "./requestErrorConfig";
|
||||||
import "@ant-design/v5-patch-for-react-19";
|
import "@ant-design/v5-patch-for-react-19";
|
||||||
import { getAccessToken, getRefreshToken, getTenantId } from "./utils/auth";
|
import { getAccessToken, getRefreshToken, getTenantId } from "./utils/auth";
|
||||||
|
import { CACHE_KEY, useCache } from "./hooks/web/useCache";
|
||||||
|
|
||||||
const isDev = process.env.NODE_ENV === "development";
|
const isDev = process.env.NODE_ENV === "development";
|
||||||
const isDevOrTest = isDev || process.env.CI;
|
const isDevOrTest = isDev || process.env.CI;
|
||||||
@@ -27,10 +28,11 @@ const loginPath = "/user/login";
|
|||||||
* */
|
* */
|
||||||
export async function getInitialState(): Promise<{
|
export async function getInitialState(): Promise<{
|
||||||
settings?: Partial<LayoutSettings>;
|
settings?: Partial<LayoutSettings>;
|
||||||
currentUser?: { user: UserVO };
|
currentUser?: UserInfoVO;
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
fetchUserInfo?: () => Promise<{ user: UserVO } | undefined>;
|
fetchUserInfo?: () => Promise<UserInfoVO | undefined>;
|
||||||
}> {
|
}> {
|
||||||
|
const { wsCache } = useCache();
|
||||||
const fetchUserInfo = async () => {
|
const fetchUserInfo = async () => {
|
||||||
// history.push(loginPath);
|
// history.push(loginPath);
|
||||||
try {
|
try {
|
||||||
@@ -38,8 +40,10 @@ export async function getInitialState(): Promise<{
|
|||||||
if (!token) {
|
if (!token) {
|
||||||
throw new Error("No token found");
|
throw new Error("No token found");
|
||||||
}
|
}
|
||||||
const msg = await getInfo();
|
const { data } = await getInfo();
|
||||||
return msg.data;
|
wsCache.set(CACHE_KEY.USER, data);
|
||||||
|
wsCache.set(CACHE_KEY.ROLE_ROUTERS, data.menus);
|
||||||
|
return data;
|
||||||
} catch (_error) {
|
} catch (_error) {
|
||||||
history.push(loginPath);
|
history.push(loginPath);
|
||||||
}
|
}
|
||||||
@@ -89,7 +93,6 @@ export const layout: RunTimeLayoutConfig = ({
|
|||||||
onPageChange: () => {
|
onPageChange: () => {
|
||||||
const { location } = history;
|
const { location } = history;
|
||||||
// 如果没有登录,重定向到 login
|
// 如果没有登录,重定向到 login
|
||||||
console.log(initialState);
|
|
||||||
if (!initialState?.currentUser && location.pathname !== loginPath) {
|
if (!initialState?.currentUser && location.pathname !== loginPath) {
|
||||||
history.push(loginPath);
|
history.push(loginPath);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import React from "react";
|
|||||||
import { flushSync } from "react-dom";
|
import { flushSync } from "react-dom";
|
||||||
import { loginOut } from "@/services/login";
|
import { loginOut } from "@/services/login";
|
||||||
import HeaderDropdown from "../HeaderDropdown";
|
import HeaderDropdown from "../HeaderDropdown";
|
||||||
|
import { removeToken } from "@/utils/auth";
|
||||||
|
import { deleteUserCache } from "@/hooks/web/useCache";
|
||||||
|
|
||||||
export type GlobalHeaderRightProps = {
|
export type GlobalHeaderRightProps = {
|
||||||
menu?: boolean;
|
menu?: boolean;
|
||||||
@@ -48,8 +50,10 @@ export const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({
|
|||||||
/**
|
/**
|
||||||
* 退出登录,并且将当前的 url 保存
|
* 退出登录,并且将当前的 url 保存
|
||||||
*/
|
*/
|
||||||
const loginOut = async () => {
|
const onLoginOut = async () => {
|
||||||
await loginOut();
|
await loginOut();
|
||||||
|
removeToken();
|
||||||
|
deleteUserCache(); // 删除用户缓存
|
||||||
const { search, pathname } = window.location;
|
const { search, pathname } = window.location;
|
||||||
const urlParams = new URL(window.location.href).searchParams;
|
const urlParams = new URL(window.location.href).searchParams;
|
||||||
const searchParams = new URLSearchParams({
|
const searchParams = new URLSearchParams({
|
||||||
@@ -57,7 +61,6 @@ export const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({
|
|||||||
});
|
});
|
||||||
/** 此方法会跳转到 redirect 参数所在的位置 */
|
/** 此方法会跳转到 redirect 参数所在的位置 */
|
||||||
const redirect = urlParams.get("redirect");
|
const redirect = urlParams.get("redirect");
|
||||||
// Note: There may be security issues, please note
|
|
||||||
if (window.location.pathname !== "/user/login" && !redirect) {
|
if (window.location.pathname !== "/user/login" && !redirect) {
|
||||||
history.replace({
|
history.replace({
|
||||||
pathname: "/user/login",
|
pathname: "/user/login",
|
||||||
@@ -75,7 +78,7 @@ export const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({
|
|||||||
flushSync(() => {
|
flushSync(() => {
|
||||||
setInitialState((s) => ({ ...s, currentUser: undefined }));
|
setInitialState((s) => ({ ...s, currentUser: undefined }));
|
||||||
});
|
});
|
||||||
loginOut();
|
onLoginOut();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
history.push(`/account/${key}`);
|
history.push(`/account/${key}`);
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import {
|
|||||||
ProFormCheckbox,
|
ProFormCheckbox,
|
||||||
ProFormText,
|
ProFormText,
|
||||||
} from "@ant-design/pro-components";
|
} from "@ant-design/pro-components";
|
||||||
import { history, useModel } from "@umijs/max";
|
import { history, useModel, useNavigate } from "@umijs/max";
|
||||||
import { Button, Divider, Space, Tabs, theme, message } from "antd";
|
import { Button, Divider, Space, Tabs, theme, message } from "antd";
|
||||||
import type { CSSProperties } from "react";
|
import type { CSSProperties } from "react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
@@ -34,6 +34,7 @@ const Page = () => {
|
|||||||
const { token } = theme.useToken();
|
const { token } = theme.useToken();
|
||||||
const [messageApi, contextHolder] = message.useMessage();
|
const [messageApi, contextHolder] = message.useMessage();
|
||||||
const { initialState, setInitialState } = useModel("@@initialState");
|
const { initialState, setInitialState } = useModel("@@initialState");
|
||||||
|
const navigate = useNavigate();
|
||||||
// 获取租户 ID
|
// 获取租户 ID
|
||||||
const getTenantId = async (name: string) => {
|
const getTenantId = async (name: string) => {
|
||||||
const res = await getTenantIdByName({ name });
|
const res = await getTenantIdByName({ name });
|
||||||
@@ -72,12 +73,11 @@ const Page = () => {
|
|||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
// 登录成功
|
// 登录成功
|
||||||
messageApi.success("登录成功!");
|
messageApi.success("登录成功!");
|
||||||
|
|
||||||
// 跳转到首页
|
// 跳转到首页
|
||||||
authUtil.setToken(res.data);
|
authUtil.setToken(res.data);
|
||||||
await fetchUserInfo();
|
await fetchUserInfo();
|
||||||
const urlParams = new URL(window.location.href).searchParams;
|
const urlParams = new URL(window.location.href).searchParams;
|
||||||
window.location.href = urlParams.get("redirect") || "/";
|
navigate(urlParams.get("redirect") || "/");
|
||||||
} else {
|
} else {
|
||||||
// 登录失败
|
// 登录失败
|
||||||
messageApi.error(res.msg || "登录失败,请重试!");
|
messageApi.error(res.msg || "登录失败,请重试!");
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import type { RequestOptions } from '@@/plugin-request/request';
|
import type { RequestOptions } from "@@/plugin-request/request";
|
||||||
import type { RequestConfig } from '@umijs/max';
|
import type { RequestConfig } from "@umijs/max";
|
||||||
import { message, notification } from 'antd';
|
import { message, notification } from "antd";
|
||||||
|
import { deleteUserCache } from "@/hooks/web/useCache";
|
||||||
|
const tenantEnable = process.env.VITE_APP_TENANT_ENABLE;
|
||||||
|
// const { result_code, base_url, request_timeout } = config;
|
||||||
// 错误处理方案: 错误类型
|
// 错误处理方案: 错误类型
|
||||||
enum ErrorShowType {
|
enum ErrorShowType {
|
||||||
SILENT = 0,
|
SILENT = 0,
|
||||||
@@ -33,7 +35,7 @@ export const errorConfig: RequestConfig = {
|
|||||||
res as unknown as ResponseStructure;
|
res as unknown as ResponseStructure;
|
||||||
if (!success) {
|
if (!success) {
|
||||||
const error: any = new Error(errorMessage);
|
const error: any = new Error(errorMessage);
|
||||||
error.name = 'BizError';
|
error.name = "BizError";
|
||||||
error.info = { errorCode, errorMessage, showType, data };
|
error.info = { errorCode, errorMessage, showType, data };
|
||||||
throw error; // 抛出自制的错误
|
throw error; // 抛出自制的错误
|
||||||
}
|
}
|
||||||
@@ -42,7 +44,7 @@ export const errorConfig: RequestConfig = {
|
|||||||
errorHandler: (error: any, opts: any) => {
|
errorHandler: (error: any, opts: any) => {
|
||||||
if (opts?.skipErrorHandler) throw error;
|
if (opts?.skipErrorHandler) throw error;
|
||||||
// 我们的 errorThrower 抛出的错误。
|
// 我们的 errorThrower 抛出的错误。
|
||||||
if (error.name === 'BizError') {
|
if (error.name === "BizError") {
|
||||||
const errorInfo: ResponseStructure | undefined = error.info;
|
const errorInfo: ResponseStructure | undefined = error.info;
|
||||||
if (errorInfo) {
|
if (errorInfo) {
|
||||||
const { errorMessage, errorCode } = errorInfo;
|
const { errorMessage, errorCode } = errorInfo;
|
||||||
@@ -77,10 +79,10 @@ export const errorConfig: RequestConfig = {
|
|||||||
// 请求已经成功发起,但没有收到响应
|
// 请求已经成功发起,但没有收到响应
|
||||||
// \`error.request\` 在浏览器中是 XMLHttpRequest 的实例,
|
// \`error.request\` 在浏览器中是 XMLHttpRequest 的实例,
|
||||||
// 而在node.js中是 http.ClientRequest 的实例
|
// 而在node.js中是 http.ClientRequest 的实例
|
||||||
message.error('None response! Please retry.');
|
message.error("None response! Please retry.");
|
||||||
} else {
|
} else {
|
||||||
// 发送请求时出了点问题
|
// 发送请求时出了点问题
|
||||||
message.error('Request error, please retry.');
|
message.error("Request error, please retry.");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -89,8 +91,8 @@ export const errorConfig: RequestConfig = {
|
|||||||
requestInterceptors: [
|
requestInterceptors: [
|
||||||
(config: RequestOptions) => {
|
(config: RequestOptions) => {
|
||||||
// 拦截请求配置,进行个性化处理。
|
// 拦截请求配置,进行个性化处理。
|
||||||
const url = config?.url?.concat('?token=123');
|
|
||||||
return { ...config, url };
|
return { ...config };
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
@@ -101,7 +103,7 @@ export const errorConfig: RequestConfig = {
|
|||||||
const { data } = response as unknown as ResponseStructure;
|
const { data } = response as unknown as ResponseStructure;
|
||||||
|
|
||||||
if (data?.success === false) {
|
if (data?.success === false) {
|
||||||
message.error('请求失败!');
|
message.error("请求失败!");
|
||||||
}
|
}
|
||||||
return response;
|
return response;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -71,21 +71,13 @@ export async function getTenantByWebsite(
|
|||||||
...(options || {}),
|
...(options || {}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// 登出
|
|
||||||
// export const loginOut = () => {
|
export async function loginOut() {
|
||||||
// return request.post({ url: "/system/auth/logout" });
|
|
||||||
// };
|
|
||||||
export async function loginOut(
|
|
||||||
body: API.UserLoginVO,
|
|
||||||
options?: { [key: string]: any }
|
|
||||||
) {
|
|
||||||
return request("/system/auth/logout", {
|
return request("/system/auth/logout", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
data: body,
|
|
||||||
...(options || {}),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// 获取用户权限信息
|
// 获取用户权限信息
|
||||||
@@ -93,7 +85,7 @@ export async function loginOut(
|
|||||||
// return request.get({ url: "/system/auth/get-permission-info" });
|
// return request.get({ url: "/system/auth/get-permission-info" });
|
||||||
// };
|
// };
|
||||||
export async function getInfo(options?: { [key: string]: any }) {
|
export async function getInfo(options?: { [key: string]: any }) {
|
||||||
return request<IResponse<{ user: API.UserVO }>>(
|
return request<IResponse<API.UserInfoVO>>(
|
||||||
"/system/auth/get-permission-info",
|
"/system/auth/get-permission-info",
|
||||||
{
|
{
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
|||||||
@@ -1,38 +1,49 @@
|
|||||||
|
import { MenuVO } from "../system/menu";
|
||||||
|
|
||||||
export type UserLoginVO = {
|
export type UserLoginVO = {
|
||||||
username: string
|
username: string;
|
||||||
password: string
|
password: string;
|
||||||
captchaVerification: string
|
captchaVerification: string;
|
||||||
socialType?: string
|
socialType?: string;
|
||||||
socialCode?: string
|
socialCode?: string;
|
||||||
socialState?: string
|
socialState?: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type TokenType = {
|
export type TokenType = {
|
||||||
id: number // 编号
|
id: number; // 编号
|
||||||
accessToken: string // 访问令牌
|
accessToken: string; // 访问令牌
|
||||||
refreshToken: string // 刷新令牌
|
refreshToken: string; // 刷新令牌
|
||||||
userId: number // 用户编号
|
userId: number; // 用户编号
|
||||||
userType: number //用户类型
|
userType: number; //用户类型
|
||||||
clientId: string //客户端编号
|
clientId: string; //客户端编号
|
||||||
expiresTime: number //过期时间
|
expiresTime: number; //过期时间
|
||||||
}
|
};
|
||||||
|
|
||||||
export type UserVO = {
|
export type UserVO = {
|
||||||
id: number
|
id: number;
|
||||||
username: string
|
username: string;
|
||||||
nickname: string
|
nickname: string;
|
||||||
deptId: number
|
deptId: number;
|
||||||
email: string
|
email: string;
|
||||||
mobile: string
|
mobile: string;
|
||||||
sex: number
|
sex: number;
|
||||||
avatar: string
|
avatar: string;
|
||||||
loginIp: string
|
loginIp: string;
|
||||||
loginDate: string
|
loginDate: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type RegisterVO = {
|
export type RegisterVO = {
|
||||||
tenantName: string
|
tenantName: string;
|
||||||
username: string
|
username: string;
|
||||||
password: string
|
password: string;
|
||||||
captchaVerification: string
|
captchaVerification: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface UserInfoVO {
|
||||||
|
// USER 缓存
|
||||||
|
permissions: Set<string>;
|
||||||
|
roles: string[];
|
||||||
|
isSetUser: boolean;
|
||||||
|
user: UserVO;
|
||||||
|
menus: MenuVO[];
|
||||||
}
|
}
|
||||||
|
|||||||
49
src/services/system/menu/index.ts
Normal file
49
src/services/system/menu/index.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import { request } from "@umijs/max";
|
||||||
|
|
||||||
|
export interface MenuVO {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
permission: string;
|
||||||
|
type: number;
|
||||||
|
sort: number;
|
||||||
|
parentId: number;
|
||||||
|
path: string;
|
||||||
|
icon: string;
|
||||||
|
component: string;
|
||||||
|
componentName?: string;
|
||||||
|
status: number;
|
||||||
|
visible: boolean;
|
||||||
|
keepAlive: boolean;
|
||||||
|
alwaysShow?: boolean;
|
||||||
|
createTime: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询菜单(精简)列表
|
||||||
|
// export const getSimpleMenusList = () => {
|
||||||
|
// return request.get({ url: '/system/menu/simple-list' })
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // 查询菜单列表
|
||||||
|
// export const getMenuList = (params) => {
|
||||||
|
// return request.get({ url: '/system/menu/list', params })
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // 获取菜单详情
|
||||||
|
// export const getMenu = (id: number) => {
|
||||||
|
// return request.get({ url: '/system/menu/get?id=' + id })
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // 新增菜单
|
||||||
|
// export const createMenu = (data: MenuVO) => {
|
||||||
|
// return request.post({ url: '/system/menu/create', data })
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // 修改菜单
|
||||||
|
// export const updateMenu = (data: MenuVO) => {
|
||||||
|
// return request.put({ url: '/system/menu/update', data })
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // 删除菜单
|
||||||
|
// export const deleteMenu = (id: number) => {
|
||||||
|
// return request.delete({ url: '/system/menu/delete?id=' + id })
|
||||||
|
// }
|
||||||
Reference in New Issue
Block a user