refactor: 登陆&获取菜单&登出
This commit is contained in:
parent
9888af431e
commit
7a8b8677bd
|
@ -15,7 +15,7 @@ export default mergeConfig(
|
||||||
'/front': {
|
'/front': {
|
||||||
target: 'http://101.43.186.75:8081/',
|
target: 'http://101.43.186.75:8081/',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite: (path) => path.replace(/^\/front/, ''),
|
rewrite: (path: string) => path.replace(/^\/front/, ''),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,23 +1,22 @@
|
||||||
import { Message, Modal } from '@arco-design/web-vue';
|
import { Message, Modal } from '@arco-design/web-vue';
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import useUser from '@/store/modules/user';
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
import type { ErrorMessageMode } from '#/axios';
|
import type { ErrorMessageMode } from '#/axios';
|
||||||
|
|
||||||
export default function checkStatus(status: number, msg: string, errorMessageMode: ErrorMessageMode = 'message'): void {
|
export default function checkStatus(status: number, msg: string, errorMessageMode: ErrorMessageMode = 'message'): void {
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const userStore = useUser();
|
|
||||||
let errMessage = '';
|
let errMessage = '';
|
||||||
|
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 400:
|
case 400:
|
||||||
errMessage = `${msg}`;
|
errMessage = `${msg}`;
|
||||||
break;
|
break;
|
||||||
// 401: Not logged in
|
case 401: {
|
||||||
case 401:
|
|
||||||
errMessage = msg || t('api.errMsg401');
|
errMessage = msg || t('api.errMsg401');
|
||||||
userStore.logout();
|
const router = useRouter();
|
||||||
|
router.push('/login');
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case 403:
|
case 403:
|
||||||
errMessage = t('api.errMsg403');
|
errMessage = t('api.errMsg403');
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -107,14 +107,12 @@ const transform: AxiosTransform = {
|
||||||
/**
|
/**
|
||||||
* @description: 请求拦截器处理
|
* @description: 请求拦截器处理
|
||||||
*/
|
*/
|
||||||
requestInterceptors: (config, options) => {
|
requestInterceptors: (config) => {
|
||||||
// 请求之前处理config
|
// 请求之前处理config
|
||||||
const token = getToken();
|
const token = getToken();
|
||||||
if (token && (config as Recordable)?.requestOptions?.withToken !== false) {
|
if (token && (config as Recordable)?.requestOptions?.withToken !== false) {
|
||||||
// jwt token
|
const { sessionId, csrfToken } = token;
|
||||||
(config as Recordable).headers.Authorization = options.authenticationScheme
|
(config as Recordable).headers = { 'X-AUTH-TOKEN': sessionId, 'CSRF-TOKEN': csrfToken };
|
||||||
? `${options.authenticationScheme} ${token}`
|
|
||||||
: token;
|
|
||||||
}
|
}
|
||||||
return config;
|
return config;
|
||||||
},
|
},
|
||||||
|
@ -162,8 +160,6 @@ const transform: AxiosTransform = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const { sessionId, csrfToken } = getToken();
|
|
||||||
|
|
||||||
function createAxios(opt?: Partial<CreateAxiosOptions>) {
|
function createAxios(opt?: Partial<CreateAxiosOptions>) {
|
||||||
return new MSAxios(
|
return new MSAxios(
|
||||||
deepMerge(
|
deepMerge(
|
||||||
|
@ -174,7 +170,7 @@ function createAxios(opt?: Partial<CreateAxiosOptions>) {
|
||||||
authenticationScheme: '',
|
authenticationScheme: '',
|
||||||
baseURL: `${window.location.origin}/${import.meta.env.VITE_API_BASE_URL as string}`,
|
baseURL: `${window.location.origin}/${import.meta.env.VITE_API_BASE_URL as string}`,
|
||||||
timeout: 30 * 1000,
|
timeout: 30 * 1000,
|
||||||
headers: { 'Content-Type': ContentTypeEnum.JSON, 'X-AUTH-TOKEN': sessionId, 'CSRF-TOKEN': csrfToken },
|
headers: { 'Content-Type': ContentTypeEnum.JSON },
|
||||||
// 如果是form-data格式
|
// 如果是form-data格式
|
||||||
// headers: { 'Content-Type': ContentTypeEnum.FORM_URLENCODED },
|
// headers: { 'Content-Type': ContentTypeEnum.FORM_URLENCODED },
|
||||||
// 数据处理方式
|
// 数据处理方式
|
||||||
|
|
|
@ -1,19 +1,18 @@
|
||||||
import MSR from '@/api/http/index';
|
import MSR from '@/api/http/index';
|
||||||
import { LoginUrl, LogoutUrl, GetUserInfoUrl, GetMenuListUrl } from '@/api/requrls/user';
|
import { LoginUrl, LogoutUrl, GetMenuListUrl, isLoginUrl } from '@/api/requrls/user';
|
||||||
import type { RouteRecordNormalized } from 'vue-router';
|
import type { RouteRecordNormalized } from 'vue-router';
|
||||||
import type { LoginData, LoginRes } from '@/models/user';
|
import type { LoginData, LoginRes } from '@/models/user';
|
||||||
import type { UserState } from '@/store/modules/user/types';
|
|
||||||
|
|
||||||
export function login(data: LoginData) {
|
export function login(data: LoginData) {
|
||||||
return MSR.post<LoginRes>({ url: LoginUrl, data });
|
return MSR.post<LoginRes>({ url: LoginUrl, data });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function logout() {
|
export function isLogin() {
|
||||||
return MSR.post<LoginRes>({ url: LogoutUrl });
|
return MSR.get<LoginRes>({ url: isLoginUrl });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getUserInfo() {
|
export function logout() {
|
||||||
return MSR.post<UserState>({ url: GetUserInfoUrl });
|
return MSR.get<LoginRes>({ url: LogoutUrl });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getMenuList() {
|
export function getMenuList() {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
export const LoginUrl = '/login';
|
export const LoginUrl = '/login';
|
||||||
export const LogoutUrl = '/api/user/logout';
|
export const isLoginUrl = '/is-login';
|
||||||
export const GetUserInfoUrl = '/api/user/info';
|
export const LogoutUrl = '/signout';
|
||||||
export const GetMenuListUrl = '/api/user/menu';
|
export const GetMenuListUrl = '/api/user/menu';
|
||||||
|
|
|
@ -17,9 +17,9 @@
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
const { logout } = useUser();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const { logout } = useUser();
|
|
||||||
const { menuTree } = useMenuTree();
|
const { menuTree } = useMenuTree();
|
||||||
const collapsed = computed({
|
const collapsed = computed({
|
||||||
get() {
|
get() {
|
||||||
|
@ -121,7 +121,6 @@
|
||||||
{
|
{
|
||||||
label: t('personal.switchOrg'),
|
label: t('personal.switchOrg'),
|
||||||
icon: <icon-swap />,
|
icon: <icon-swap />,
|
||||||
event: () => {},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
divider: <a-divider class="ms-dropdown-divider" />,
|
divider: <a-divider class="ms-dropdown-divider" />,
|
||||||
|
@ -129,7 +128,7 @@
|
||||||
{
|
{
|
||||||
label: t('personal.exit'),
|
label: t('personal.exit'),
|
||||||
icon: <icon-export />,
|
icon: <icon-export />,
|
||||||
event: logout,
|
event: () => logout(),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -154,6 +154,7 @@ export default function useTableProps(
|
||||||
setPagination({ current: data.current, total: data.total });
|
setPagination({ current: data.current, total: data.total });
|
||||||
return data;
|
return data;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
console.log(err);
|
console.log(err);
|
||||||
// TODO 表格异常放到solt的empty
|
// TODO 表格异常放到solt的empty
|
||||||
} finally {
|
} finally {
|
||||||
|
|
|
@ -11,17 +11,15 @@ export default function useUser() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const logout = async (logoutTo?: string) => {
|
/**
|
||||||
|
* 登出
|
||||||
|
* @param logoutTo 登出后跳转的页面
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const logout = async () => {
|
||||||
await userStore.logout();
|
await userStore.logout();
|
||||||
const currentRoute = router.currentRoute.value;
|
|
||||||
Message.success(t('message.logoutSuccess'));
|
Message.success(t('message.logoutSuccess'));
|
||||||
router.push({
|
router.push({ name: 'login' });
|
||||||
name: logoutTo && typeof logoutTo === 'string' ? logoutTo : 'login',
|
|
||||||
query: {
|
|
||||||
...router.currentRoute.value.query,
|
|
||||||
redirect: currentRoute.name as string,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
logout,
|
logout,
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
import Mock from 'mockjs';
|
import Mock from 'mockjs';
|
||||||
import setupMock, { successResponseWrap, failResponseWrap, makeMockUrl } from '@/utils/setup-mock';
|
import setupMock, { successResponseWrap, failResponseWrap, makeMockUrl } from '@/utils/setup-mock';
|
||||||
|
|
||||||
import { GetMenuListUrl, LogoutUrl, GetUserInfoUrl, LoginUrl } from '@/api/requrls/user';
|
import { GetMenuListUrl, LogoutUrl, LoginUrl } from '@/api/requrls/user';
|
||||||
import { isLogin } from '@/utils/auth';
|
import { isLogin } from '@/utils/auth';
|
||||||
|
|
||||||
setupMock({
|
setupMock({
|
||||||
|
mock: false,
|
||||||
setup() {
|
setup() {
|
||||||
// 用户信息
|
// 用户信息
|
||||||
Mock.mock(makeMockUrl(GetUserInfoUrl), () => {
|
Mock.mock(makeMockUrl('/api/userinfo'), async () => {
|
||||||
if (isLogin()) {
|
if (await isLogin()) {
|
||||||
const role = window.localStorage.getItem('userRole') || 'admin';
|
const role = window.localStorage.getItem('userRole') || 'admin';
|
||||||
return successResponseWrap({
|
return successResponseWrap({
|
||||||
name: '王立群',
|
name: '王立群',
|
||||||
|
@ -32,7 +33,7 @@ setupMock({
|
||||||
return failResponseWrap(null, '未登录', 50008);
|
return failResponseWrap(null, '未登录', 50008);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 登出
|
// 登陆
|
||||||
Mock.mock(makeMockUrl(LoginUrl), () => {
|
Mock.mock(makeMockUrl(LoginUrl), () => {
|
||||||
return successResponseWrap({});
|
return successResponseWrap({});
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,8 +5,35 @@ export interface LoginData {
|
||||||
authenticate: string;
|
authenticate: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface UserRole {
|
||||||
|
id: string;
|
||||||
|
createTime: number;
|
||||||
|
createUser: string;
|
||||||
|
roleId: string;
|
||||||
|
sourceId: string;
|
||||||
|
userId: string;
|
||||||
|
}
|
||||||
|
|
||||||
// 登录返回
|
// 登录返回
|
||||||
export interface LoginRes {
|
export interface LoginRes {
|
||||||
sessionId: string;
|
|
||||||
csrfToken: string;
|
csrfToken: string;
|
||||||
|
createTime: number;
|
||||||
|
createUser: string;
|
||||||
|
email: string;
|
||||||
|
enabled: boolean;
|
||||||
|
id: string;
|
||||||
|
language: string;
|
||||||
|
lastOrganizationId: string;
|
||||||
|
lastProjectId: string;
|
||||||
|
name: string;
|
||||||
|
phone: string;
|
||||||
|
platformInfo: string;
|
||||||
|
seleniumServer: string;
|
||||||
|
sessionId: string;
|
||||||
|
source: string;
|
||||||
|
updateTime: number;
|
||||||
|
updateUser: string;
|
||||||
|
userRolePermissions: UserRole[];
|
||||||
|
userRoleRelations: UserRole[];
|
||||||
|
userRoles: UserRole[];
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +1,17 @@
|
||||||
import type { Router, RouteRecordNormalized } from 'vue-router';
|
import type { Router } from 'vue-router';
|
||||||
import NProgress from 'nprogress'; // progress bar
|
import NProgress from 'nprogress'; // progress bar
|
||||||
|
|
||||||
import usePermission from '@/hooks/usePermission';
|
import usePermission from '@/hooks/usePermission';
|
||||||
import { useAppStore } from '@/store';
|
|
||||||
import { WHITE_LIST, NOT_FOUND } from '../constants';
|
import { WHITE_LIST, NOT_FOUND } from '../constants';
|
||||||
|
|
||||||
export default function setupPermissionGuard(router: Router) {
|
export default function setupPermissionGuard(router: Router) {
|
||||||
router.beforeEach(async (to, from, next) => {
|
router.beforeEach(async (to, from, next) => {
|
||||||
const appStore = useAppStore();
|
|
||||||
const Permission = usePermission();
|
const Permission = usePermission();
|
||||||
const permissionsAllow = Permission.accessRouter(to);
|
const permissionsAllow = Permission.accessRouter(to);
|
||||||
// 针对来自服务端的菜单配置进行处理
|
|
||||||
if (!appStore.appAsyncMenus.length && !WHITE_LIST.find((el) => el.name === to.name)) {
|
|
||||||
await appStore.fetchServerMenuConfig();
|
|
||||||
}
|
|
||||||
const serverMenuConfig = [...appStore.appAsyncMenus, ...WHITE_LIST];
|
|
||||||
|
|
||||||
let exist = false;
|
const exist = WHITE_LIST.find((el) => el.name === to.name);
|
||||||
while (serverMenuConfig.length && !exist) {
|
|
||||||
const element = serverMenuConfig.shift();
|
|
||||||
if (element?.name === to.name) exist = true;
|
|
||||||
|
|
||||||
if (element?.children) {
|
if (exist || permissionsAllow) {
|
||||||
serverMenuConfig.push(...(element.children as unknown as RouteRecordNormalized[]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (exist && permissionsAllow) {
|
|
||||||
next();
|
next();
|
||||||
} else next(NOT_FOUND);
|
} else next(NOT_FOUND);
|
||||||
NProgress.done();
|
NProgress.done();
|
||||||
|
|
|
@ -1,32 +1,12 @@
|
||||||
import type { Router, LocationQueryRaw } from 'vue-router';
|
import type { Router, LocationQueryRaw } from 'vue-router';
|
||||||
import NProgress from 'nprogress'; // progress bar
|
import NProgress from 'nprogress'; // progress bar
|
||||||
|
|
||||||
import { useUserStore } from '@/store';
|
|
||||||
import { isLogin } from '@/utils/auth';
|
import { isLogin } from '@/utils/auth';
|
||||||
|
|
||||||
export default function setupUserLoginInfoGuard(router: Router) {
|
export default function setupUserLoginInfoGuard(router: Router) {
|
||||||
router.beforeEach(async (to, from, next) => {
|
router.beforeEach(async (to, from, next) => {
|
||||||
NProgress.start();
|
NProgress.start();
|
||||||
const userStore = useUserStore();
|
if (to.name !== 'login' && (await isLogin())) {
|
||||||
if (isLogin()) {
|
next();
|
||||||
if (userStore.role) {
|
|
||||||
next();
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
await userStore.info();
|
|
||||||
next();
|
|
||||||
} catch (error) {
|
|
||||||
// 获取用户信息错误则先退出,重定向到登录页
|
|
||||||
await userStore.logout();
|
|
||||||
next({
|
|
||||||
name: 'login',
|
|
||||||
query: {
|
|
||||||
redirect: to.name,
|
|
||||||
...to.query,
|
|
||||||
} as LocationQueryRaw,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// 未登录的都直接跳转至登录页,访问的页面地址缓存到 query 上
|
// 未登录的都直接跳转至登录页,访问的页面地址缓存到 query 上
|
||||||
if (to.name === 'login') {
|
if (to.name === 'login') {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { AppRouteRecordRaw } from '../types';
|
||||||
const ApiTest: AppRouteRecordRaw = {
|
const ApiTest: AppRouteRecordRaw = {
|
||||||
path: '/api-test',
|
path: '/api-test',
|
||||||
name: 'apiTest',
|
name: 'apiTest',
|
||||||
|
redirect: '/api-test/list',
|
||||||
component: DEFAULT_LAYOUT,
|
component: DEFAULT_LAYOUT,
|
||||||
meta: {
|
meta: {
|
||||||
locale: 'menu.apiTest',
|
locale: 'menu.apiTest',
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import { login as userLogin, logout as userLogout, getUserInfo } from '@/api/modules/user';
|
import { login as userLogin, logout as userLogout } from '@/api/modules/user';
|
||||||
import { setToken, clearToken } from '@/utils/auth';
|
import { setToken, clearToken } from '@/utils/auth';
|
||||||
import { removeRouteListener } from '@/utils/route-listener';
|
import { removeRouteListener } from '@/utils/route-listener';
|
||||||
import useAppStore from '../app';
|
import useAppStore from '../app';
|
||||||
|
@ -51,22 +51,17 @@ const useUserStore = defineStore('user', {
|
||||||
this.$reset();
|
this.$reset();
|
||||||
},
|
},
|
||||||
|
|
||||||
// 获取用户信息
|
|
||||||
async info() {
|
|
||||||
const res = await getUserInfo();
|
|
||||||
const appStore = useAppStore();
|
|
||||||
if (appStore.currentOrgId === '') {
|
|
||||||
// 第一次进系统才设置组织 ID,后续已经持久化存储了
|
|
||||||
appStore.setCurrentOrgId(res.organization || '');
|
|
||||||
}
|
|
||||||
this.setInfo(res);
|
|
||||||
},
|
|
||||||
|
|
||||||
// 登录
|
// 登录
|
||||||
async login(loginForm: LoginData) {
|
async login(loginForm: LoginData) {
|
||||||
try {
|
try {
|
||||||
const res = await userLogin(loginForm);
|
const res = await userLogin(loginForm);
|
||||||
setToken(res.sessionId, res.csrfToken);
|
setToken(res.sessionId, res.csrfToken);
|
||||||
|
const appStore = useAppStore();
|
||||||
|
if (appStore.currentOrgId === '') {
|
||||||
|
// 第一次进系统才设置组织 ID,后续已经持久化存储了
|
||||||
|
appStore.setCurrentOrgId(res.lastOrganizationId || '');
|
||||||
|
}
|
||||||
|
this.setInfo(res);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
clearToken();
|
clearToken();
|
||||||
throw err;
|
throw err;
|
||||||
|
|
|
@ -1,8 +1,15 @@
|
||||||
|
import { isLogin as isLoginFun } from '@/api/modules/user';
|
||||||
|
|
||||||
const SESSION_ID = 'sessionId';
|
const SESSION_ID = 'sessionId';
|
||||||
const CSRF_TOKEN = 'csrfToken';
|
const CSRF_TOKEN = 'csrfToken';
|
||||||
|
|
||||||
const isLogin = () => {
|
const isLogin = async () => {
|
||||||
return !!localStorage.getItem(SESSION_ID);
|
try {
|
||||||
|
await isLoginFun();
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
// 获取token
|
// 获取token
|
||||||
const getToken = () => {
|
const getToken = () => {
|
||||||
|
|
|
@ -101,13 +101,6 @@
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
await userStore.login(values as LoginData);
|
await userStore.login(values as LoginData);
|
||||||
const { redirect, ...othersQuery } = router.currentRoute.value.query;
|
|
||||||
router.push({
|
|
||||||
name: (redirect as string) || 'apiTest',
|
|
||||||
query: {
|
|
||||||
...othersQuery,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
Message.success(t('login.form.login.success'));
|
Message.success(t('login.form.login.success'));
|
||||||
const { rememberPassword } = loginConfig.value;
|
const { rememberPassword } = loginConfig.value;
|
||||||
const { username, password } = values;
|
const { username, password } = values;
|
||||||
|
@ -115,6 +108,13 @@
|
||||||
// The actual production environment requires encrypted storage.
|
// The actual production environment requires encrypted storage.
|
||||||
loginConfig.value.username = rememberPassword ? username : '';
|
loginConfig.value.username = rememberPassword ? username : '';
|
||||||
loginConfig.value.password = rememberPassword ? password : '';
|
loginConfig.value.password = rememberPassword ? password : '';
|
||||||
|
const { redirect, ...othersQuery } = router.currentRoute.value.query;
|
||||||
|
router.push({
|
||||||
|
name: (redirect as string) || 'apiTest',
|
||||||
|
query: {
|
||||||
|
...othersQuery,
|
||||||
|
},
|
||||||
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
errorMessage.value = (err as Error).message;
|
errorMessage.value = (err as Error).message;
|
||||||
} finally {
|
} finally {
|
||||||
|
|
Loading…
Reference in New Issue