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