Commit 6fdb8357 authored by 张毅辰's avatar 张毅辰

路由更改提交

parent 4f7ec01e
...@@ -37,8 +37,8 @@ export function loginApi({ ...@@ -37,8 +37,8 @@ export function loginApi({
*/ */
export function logoutApi() { export function logoutApi() {
return request({ return request({
url: "/api/v1/auth/logout", url: "/logout",
method: "delete", method: "post",
}); });
} }
......
import request from "@/utils/request"; import request from "@/utils/request";
import { AxiosPromise } from "axios"; import { AxiosPromise } from "axios";
import { UserForm, UserInfo, UserPageVO, UserQuery } from "./types"; import { User, UserForm, UserInfo, UserPageVO, UserQuery } from "./types";
/** /**
* 登录成功后获取用户信息(昵称、头像、权限集合和角色集合) * 登录成功后获取用户信息(昵称、头像、权限集合和角色集合)
*/ */
export function getUserInfoApi(): AxiosPromise<UserInfo> { export function getUserInfoApi() {
return request({ return request({
url: "/api/v1/users/me", url: "/system/user/getInfo",
method: "get", method: "get",
}); });
} }
......
export interface User {
/**
* 用户名称
*/
username?: string;
/**
* 用户特殊标志
*/
specialTag?: string;
/**
* 用户头像地址
*/
avatar?: any;
}
/** /**
* 登录用户信息 * 登录用户信息
*/ */
export interface UserInfo { export interface UserInfo {
/**
* 用户id
*/
userId?: number; userId?: number;
/**
* 用户名称
*/
username?: string; username?: string;
/**
* 用户昵称
*/
nickname?: string; nickname?: string;
/**
* 用户头像地址
*/
avatar?: string; avatar?: string;
/**
* token
*/
token: string | null;
/**
* 用户角色
*/
roles: string[]; roles: string[];
perms: string[]; /**
* 用户权限
*/
permissions: string[];
/**
* 用户特殊标志
*/
specialTag?: string;
/**
* 用户数据
*/
user?: User | undefined;
menus?: [];
} }
/** /**
......
...@@ -7,7 +7,7 @@ import { Directive, DirectiveBinding } from "vue"; ...@@ -7,7 +7,7 @@ import { Directive, DirectiveBinding } from "vue";
export const hasPerm: Directive = { export const hasPerm: Directive = {
mounted(el: HTMLElement, binding: DirectiveBinding) { mounted(el: HTMLElement, binding: DirectiveBinding) {
// 「超级管理员」拥有所有的按钮权限 // 「超级管理员」拥有所有的按钮权限
const { roles, perms } = useUserStoreHook().user; const { roles, permissions } = useUserStoreHook().user;
if (roles.includes("ROOT")) { if (roles.includes("ROOT")) {
return true; return true;
} }
...@@ -16,7 +16,7 @@ export const hasPerm: Directive = { ...@@ -16,7 +16,7 @@ export const hasPerm: Directive = {
if (value) { if (value) {
const requiredPerms = value; // DOM绑定需要的按钮权限标识 const requiredPerms = value; // DOM绑定需要的按钮权限标识
const hasPerm = perms?.some((perm) => { const hasPerm = permissions?.some((perm) => {
return requiredPerms.includes(perm); return requiredPerms.includes(perm);
}); });
......
import router from "@/router"; import router from "@/router";
import { useUserStoreHook } from "@/store/modules/user"; import { useUserStoreHook } from "@/store/modules/user";
import { usePermissionStoreHook } from "@/store/modules/permission"; import { usePermissionStoreHook } from "@/store/modules/permission";
import { getToken } from "@/utils/auth";
import NProgress from "nprogress"; import NProgress from "nprogress";
import "nprogress/nprogress.css"; import "nprogress/nprogress.css";
NProgress.configure({ showSpinner: false }); // 进度条 NProgress.configure({ showSpinner: false }); // 进度条
const userStore = useUserStoreHook();
const permissionStore = usePermissionStoreHook(); const permissionStore = usePermissionStoreHook();
// 白名单路由 // 白名单路由
...@@ -14,14 +16,12 @@ const whiteList = ["/login"]; ...@@ -14,14 +16,12 @@ const whiteList = ["/login"];
router.beforeEach(async (to, from, next) => { router.beforeEach(async (to, from, next) => {
NProgress.start(); NProgress.start();
const hasToken = localStorage.getItem("accessToken"); if (getToken()) {
if (hasToken) {
if (to.path === "/login") { if (to.path === "/login") {
// 如果已登录,跳转首页 // 如果已登录,跳转首页
next({ path: "/" }); next();
NProgress.done(); NProgress.done();
} else { } else {
const userStore = useUserStoreHook();
const hasRoles = userStore.user.roles && userStore.user.roles.length > 0; const hasRoles = userStore.user.roles && userStore.user.roles.length > 0;
if (hasRoles) { if (hasRoles) {
// 未匹配到任何路由,跳转404 // 未匹配到任何路由,跳转404
...@@ -32,12 +32,24 @@ router.beforeEach(async (to, from, next) => { ...@@ -32,12 +32,24 @@ router.beforeEach(async (to, from, next) => {
} }
} else { } else {
try { try {
const { roles } = await userStore.getUserInfo(); const { roles, permissions, menus, user } =
const accessRoutes = await permissionStore.generateRoutes(roles); await userStore.getUserInfo();
accessRoutes.forEach((route) => { if (permissions && permissions.length > 0) {
router.addRoute(route); const accessRoutes = await userStore.generateRoutes();
}); // const accessRoutes = await permissionStore.generateRoutes(roles);
next({ ...to, replace: true }); console.log(accessRoutes);
accessRoutes.forEach((route) => {
console.log(route);
router.addRoute(route);
});
next({ ...to, replace: true });
} else {
userStore.logout().then((_) => {
console.log("用户无权限2");
alert("用户无权限");
next(`/login`);
});
}
} catch (error) { } catch (error) {
// 移除 token 并跳转登录页 // 移除 token 并跳转登录页
await userStore.resetToken(); await userStore.resetToken();
......
...@@ -42,12 +42,12 @@ export const constantRoutes: RouteRecordRaw[] = [ ...@@ -42,12 +42,12 @@ export const constantRoutes: RouteRecordRaw[] = [
}, },
}, },
{ {
path: "401", path: "/401",
component: () => import("@/views/error-page/401.vue"), component: () => import("@/views/error-page/401.vue"),
meta: { hidden: true }, meta: { hidden: true },
}, },
{ {
path: "404", path: "/404",
component: () => import("@/views/error-page/404.vue"), component: () => import("@/views/error-page/404.vue"),
meta: { hidden: true }, meta: { hidden: true },
}, },
...@@ -58,7 +58,8 @@ export const constantRoutes: RouteRecordRaw[] = [ ...@@ -58,7 +58,8 @@ export const constantRoutes: RouteRecordRaw[] = [
// { // {
// path: "/external-link", // path: "/external-link",
// component: Layout, // component: Layout,
// children: [ { // children: [
// {
// component: () => import("@/views/external-link/index.vue"), // component: () => import("@/views/external-link/index.vue"),
// path: "https://www.cnblogs.com/haoxianrui/", // path: "https://www.cnblogs.com/haoxianrui/",
// meta: { title: "外部链接", icon: "link" }, // meta: { title: "外部链接", icon: "link" },
......
import { RouteRecordRaw } from "vue-router";
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import { loginApi, logoutApi } from "@/api/auth"; import { loginApi, logoutApi } from "@/api/auth";
import { getUserInfoApi } from "@/api/user"; import { getUserInfoApi } from "@/api/user";
import { resetRouter } from "@/router"; import { constantRoutes, resetRouter } from "@/router";
import { store } from "@/store"; import { store } from "@/store";
import { LoginData } from "@/api/auth/types"; import { LoginData } from "@/api/auth/types";
import { UserInfo } from "@/api/user/types"; import { UserInfo } from "@/api/user/types";
import { useStorage } from "@vueuse/core"; import { useStorage } from "@vueuse/core";
import { setToken } from "@/utils/auth"; import { getToken, setToken } from "@/utils/auth";
import { ref } from "vue";
import { listRoutes } from "@/api/menu";
const Layout = () => import("@/layout/index.vue");
const modules = import.meta.glob("../../views/**/**.vue");
export const useUserStore = defineStore("user", () => { export const useUserStore = defineStore("user", () => {
const user: UserInfo = { const user = reactive<UserInfo>({
token: getToken(),
username: "",
avatar: "",
roles: [], roles: [],
perms: [], permissions: [],
}; specialTag: "",
});
const token = useStorage("accessToken", ""); const menus = ref<RouteRecordRaw[]>([]);
function setRoutes(newRoutes: RouteRecordRaw[]) {
menus.value = constantRoutes.concat(newRoutes);
}
/** /**
* 登录 * 登录
* *
...@@ -30,7 +41,7 @@ export const useUserStore = defineStore("user", () => { ...@@ -30,7 +41,7 @@ export const useUserStore = defineStore("user", () => {
loginApi(loginData) loginApi(loginData)
.then((response) => { .then((response) => {
setToken(response.data); setToken(response.data);
token.value = response.data; // Bearer eyJhbGciOiJIUzI1NiJ9.xxx.xxx user.token = response.data; // Bearer eyJhbGciOiJIUzI1NiJ9.xxx.xxx
resolve(); resolve();
}) })
.catch((error) => { .catch((error) => {
...@@ -44,15 +55,17 @@ export const useUserStore = defineStore("user", () => { ...@@ -44,15 +55,17 @@ export const useUserStore = defineStore("user", () => {
return new Promise<UserInfo>((resolve, reject) => { return new Promise<UserInfo>((resolve, reject) => {
getUserInfoApi() getUserInfoApi()
.then(({ data }) => { .then(({ data }) => {
if (!data) { if (data.roles.length > 0) {
reject("Verification failed, please Login again."); user.roles = data.roles;
return; } else {
user.roles = ["ROLE_DEFAULT"];
} }
if (!data.roles || data.roles.length <= 0) { user.roles = data.roles;
reject("getUserInfo: roles must be a non-null array!"); user.permissions = data.permissions;
return; user.username = data.user?.username;
} user.specialTag = data.user?.specialTag;
Object.assign(user, { ...data }); user.avatar = data.user?.avatar;
menus.value = data.menus;
resolve(data); resolve(data);
}) })
.catch((error) => { .catch((error) => {
...@@ -66,7 +79,7 @@ export const useUserStore = defineStore("user", () => { ...@@ -66,7 +79,7 @@ export const useUserStore = defineStore("user", () => {
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
logoutApi() logoutApi()
.then(() => { .then(() => {
token.value = ""; user.token = "";
location.reload(); // 清空路由 location.reload(); // 清空路由
resolve(); resolve();
}) })
...@@ -79,19 +92,68 @@ export const useUserStore = defineStore("user", () => { ...@@ -79,19 +92,68 @@ export const useUserStore = defineStore("user", () => {
// remove token // remove token
function resetToken() { function resetToken() {
return new Promise<void>((resolve) => { return new Promise<void>((resolve) => {
token.value = ""; user.token = "";
resetRouter(); resetRouter();
resolve(); resolve();
}); });
} }
/**
* 生成动态路由
*
* @returns
*/
function generateRoutes() {
return new Promise<RouteRecordRaw[]>((resolve, reject) => {
// 接口获取所有路由
if (menus.value.length > 0) {
// 根据角色获取有访问权限的路由
const accessedRoutes = filterAsyncRoutes(menus.value);
console.log(accessedRoutes);
setRoutes(accessedRoutes);
resolve(accessedRoutes);
} else {
reject();
}
});
}
function loadView(view: any) {
// return () => import(`@/views/${view}.vue`);
const path = modules[`../../views/${view}.vue`];
return path;
}
/**
* 递归过滤有权限的异步(动态)路由
*
* @param routes 接口返回的异步(动态)路由
* @param roles 用户角色集合
* @returns 返回用户有权限的异步(动态)路由
*/
function filterAsyncRoutes(routes: RouteRecordRaw[]) {
return routes.filter((route) => {
if (route.component) {
// Layout组件特殊处理
if (route.component?.toString() === "Layout") {
route.component = Layout;
} else {
route.component = loadView(route.component);
}
}
if (route.children != null && route.children && route.children.length) {
route.children = filterAsyncRoutes(route.children);
}
return true;
});
}
return { return {
token,
user, user,
login, login,
getUserInfo, getUserInfo,
logout, logout,
resetToken, resetToken,
generateRoutes,
}; };
}); });
......
const TokenKey = "token"; const TokenKey = "token";
const emailKey = "email"; const emailKey = "email";
export function getToken() { export function getToken(): string | null {
return localStorage.getItem(TokenKey) || sessionStorage.getItem(TokenKey); return localStorage.getItem(TokenKey) || sessionStorage.getItem(TokenKey);
} }
......
...@@ -57,7 +57,7 @@ function connectWebSocket() { ...@@ -57,7 +57,7 @@ function connectWebSocket() {
stompClient = Stomp.over(socket); stompClient = Stomp.over(socket);
stompClient.connect( stompClient.connect(
{ Authorization: userStore.token }, { Authorization: userStore.user.token },
() => { () => {
isConnected.value = true; isConnected.value = true;
messages.value.push({ messages.value.push({
......
...@@ -345,6 +345,7 @@ function successLogin() { ...@@ -345,6 +345,7 @@ function successLogin() {
.then(() => { .then(() => {
const query: LocationQuery = route.query; const query: LocationQuery = route.query;
const redirect = (query.redirect as LocationQueryValue) ?? "/"; const redirect = (query.redirect as LocationQueryValue) ?? "/";
//
const otherQueryParams = Object.keys(query).reduce( const otherQueryParams = Object.keys(query).reduce(
(acc: any, cur: string) => { (acc: any, cur: string) => {
if (cur !== "redirect") { if (cur !== "redirect") {
...@@ -354,7 +355,7 @@ function successLogin() { ...@@ -354,7 +355,7 @@ function successLogin() {
}, },
{} {}
); );
router.push({ path: redirect, query: otherQueryParams }); router.push({ path: "/system/user", query: otherQueryParams });
}) })
.catch(() => { .catch(() => {
// 验证失败,重新生成验证码 // 验证失败,重新生成验证码
...@@ -444,9 +445,6 @@ onMounted(() => { ...@@ -444,9 +445,6 @@ onMounted(() => {
} }
} }
.my_btn { .my_btn {
position: relative; position: relative;
width: 100%; width: 100%;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment