马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
有了菜单及脚色管理后,我们还须要根据用户访问的token,去获取用户信息,根据用户的脚色信息,拉取全部的菜单权限,进而生成左侧菜单树数据。
1 增长获取用户信息 api
在 src/api/user.ts 中,添加获取用户信息的 api(getUserInfo),代码如下:
- //src/api/user.ts
- import request from "@/api/config/request";
- // 从 "./type" 模块中导入 ApiResponse 类型,用于定义接口响应数据的结构
- import type { ApiResponse } from "./type";
- import type { IRole } from "./role";
- /**
- * 定义用户登录所需的数据结构
- * @interface IUserLoginData
- * @property {string} username - 用户登录使用的用户名
- * @property {string} password - 用户登录使用的密码
- */
- export interface IUserLoginData {
- username: string;
- password: string;
- }
- /**
- * 定义登录接口响应的数据结构
- * @interface ILoginResponseData
- * @property {string} token - 登录成功后返回的令牌,用于后续请求的身份验证
- */
- export interface ILoginResponseData {
- token: string;
- }
- /**
- * 登录接口
- * @param {IUserLoginData} data - 用户登录所需的数据,包含用户名和密码
- * @returns {Promise<ApiResponse<ILoginResponseData>>} - 返回一个 Promise 对象,该对象解析为包含登录响应数据的 ApiResponse 类型
- */
- export const login = (
- data: IUserLoginData
- ): Promise<ApiResponse<ILoginResponseData>> => {
- return request.post("/4642164-4292760-default/287017559", data);
- };
- //test
- export const logout_error = (): Promise<ApiResponse<ILoginResponseData>> => {
- return request.post("/4642164-4292760-default/auth/401");
- };
- //个人中心接口
- export interface Profile {
- id: number;
- username: string;
- email: string;
- mobile: string;
- isSuper: boolean;
- status: boolean;
- avatar: string;
- description: string;
- roles: IRole[];
- roleIds?: number[]; // 修改用户的时候,后端接受只要id
- }
- export interface IUsers {
- users: Profile[];
- count: number;
- }
- // 查询参数
- export interface IUserQuery {
- pageNum?: number;
- pageSize?: number;
- mobile?: string;
- status?: boolean;
- username?: string;
- flag: number;
- }
- // 获取用户列表的接口
- export const getUsers = (params: IUserQuery): Promise<ApiResponse<IUsers>> => {
- const {
- pageNum = 0,
- pageSize = 10,
- username = "",
- status,
- mobile = "",
- flag
- } = params;
- return request.get("http://127.0.0.1:4523/m1/4642164-4292760-default/user", {
- params: {
- pageNum,
- pageSize,
- username,
- status,
- mobile,
- flag
- }
- });
- };
- // 删除用户
- export const removeUser = (id: number): Promise<ApiResponse> => {
- return request.delete(
- `http://127.0.0.1:4523/m1/4642164-4292760-default/user/`
- );
- };
- // 添加用户
- export const addUser = (data: Profile): Promise<ApiResponse> => {
- return request.post(
- "http://127.0.0.1:4523/m1/4642164-4292760-default/user",
- data
- );
- };
- // 编辑用户
- export const updateUser = (id: number, data: Profile): Promise<ApiResponse> => {
- return request.put(
- `http://127.0.0.1:4523/m1/4642164-4292760-default/user`,
- data
- );
- };
- // 获取用户信息
- export const getUserInfo = (): Promise<ApiResponse<Profile>> => {
- return request.post("/auth/info");
- };
复制代码 2 修改用户 Store
在 src/stores/user.ts 中,增长获取当前用户信息的方法 getUserInfo,代码如下:
- //src/stores/user.ts
- import type { IUserLoginData, IUserQuery, IUsers, Profile } from "@/api/user";
- import {
- login as loginApi,
- getUsers as getUsersApi, // 获取用户
- addUser as addUserApi,
- removeUser as removeUserApi,
- updateUser as updateUserApi,
- getUserInfo as getUserInfoApi
- } from "@/api/user";
- import { setToken, removeToken } from "@/utils/auth";
- import { useTagsView } from "./tagsView";
- import type { IRole } from "@/api/role";
- /**
- * 用户信息查询参数类型,继承自Profile并添加分页参数
- */
- export type IProfileQuery = Profile & {
- pageNum?: number;
- pageSize?: number;
- };
- /**
- * 用户状态管理
- */
- export const useUserStore = defineStore("user", () => {
- // 状态管理
- const state = reactive({
- token: "", // 用户令牌
- users: [] as IUsers["users"], // 用户列表
- count: 0, // 用户总数
- roles: [] as IRole[], // 用户角色列表
- userInfo: {} as Profile // 当前用户信息
- });
- // 引用标签视图模块
- const tagsViewStore = useTagsView();
- /**
- * 获取当前用户信息
- */
- const getUserInfo = async () => {
- const res = await getUserInfoApi();
- if (res.code === 0) {
- // 解构响应数据,分离角色和用户信息
- const { roles, ...info } = res.data;
- state.roles = roles; // 存储角色信息
- state.userInfo = info as Profile; // 存储用户信息
- }
- };
- /**
- * 用户登录
- * @param userInfo - 包含用户名和密码的登录信息
- */
- const login = async (userInfo: IUserLoginData) => {
- try {
- const { username, password } = userInfo;
- // 调用登录API,用户名去除首尾空格
- const response = await loginApi({ username: username.trim(), password });
- const { data } = response;
- state.token = data.token; // 存储令牌
- setToken(data.token); // 保存令牌到本地存储
- } catch (e) {
- return Promise.reject(e); // 登录失败时返回错误
- }
- };
- /**
- * 用户注销
- */
- const logout = () => {
- state.token = ""; // 清空令牌
- removeToken(); // 移除本地存储的令牌
- tagsViewStore.delAllView(); // 清除所有标签视图
- };
- /**
- * 获取全部用户列表
- * @param params - 查询参数,包含分页和筛选条件
- */
- const getAllUsers = async (params: IUserQuery) => {
- const res = await getUsersApi(params);
- const { data } = res;
- state.users = data.users; // 更新用户列表
- state.count = data.count; // 更新用户总数
- };
- /**
- * 添加新用户
- * @param data - 用户信息,包含分页参数(用于添加成功后刷新列表)
- */
- const addUser = async (data: IProfileQuery) => {
- // 分离分页参数和用户信息
- const { pageSize, pageNum, ...params } = data;
- const res = await addUserApi(params);
- if (res.code === 0) {
- // 添加成功后刷新用户列表
- getAllUsers({
- pageSize,
- pageNum
- });
- }
- };
- /**
- * 删除用户
- * @param data - 包含用户ID和分页信息(用于删除成功后刷新列表)
- */
- const removeUser = async (data: IProfileQuery) => {
- const { pageSize, pageNum, id } = data;
- const res = await removeUserApi(id);
- if (res.code === 0) {
- // 删除成功后刷新用户列表
- getAllUsers({
- pageSize,
- pageNum
- });
- }
- };
- /**
- * 编辑用户信息
- * @param data - 用户信息,包含分页参数(用于编辑成功后刷新列表)
- */
- const editUser = async (data: IProfileQuery) => {
- // 分离分页参数和用户信息
- const { pageSize, pageNum, ...params } = data;
- const res = await updateUserApi(params.id, params);
- if (res.code === 0) {
- // 编辑成功后刷新用户列表
- getAllUsers({
- pageSize,
- pageNum
- });
- }
- };
- // 导出可访问的方法和状态
- return {
- login,
- state,
- logout,
- getAllUsers,
- editUser,
- removeUser,
- addUser,
- getUserInfo
- };
- });
复制代码 3 新增 permission Store
新增 src/stores/permission.ts,代码如下:
4 修改 permission.ts
修改 src/permission.ts,代码如下:
- //src/permission.ts
- // 路由鉴权配置 - 控制用户访问权限和页面导航逻辑
- import router from "@/router";
- import NProgress from "nprogress"; // 进度条插件
- import "nprogress/nprogress.css"; // 进度条样式
- import { getToken } from "./utils/auth"; // 获取token工具函数
- import { useUserStore } from "./stores/user"; // 用户状态管理
- import { usePermissionStore } from "./stores/permission"; // 权限状态管理
- // 配置进度条选项,不显示旋转加载图标
- NProgress.configure({ showSpinner: false });
- // 白名单路由 - 无需登录即可访问的页面
- const whiteList = ["/login"];
- /**
- * 全局前置守卫 - 路由切换前的权限校验
- * 1. 检查Token有效性
- * 2. 判断用户权限
- * 3. 动态生成路由
- */
- router.beforeEach(async (to, from) => {
- // 开始进度条
- NProgress.start();
- // 获取本地Token
- const hasToken = getToken();
- const userStore = useUserStore();
- const permissionStore = usePermissionStore();
- // 情况1:已登录状态
- if (hasToken) {
- // 已登录但访问登录页,重定向到首页
- if (to.path === "/login") {
- NProgress.done(); // 结束进度条
- return {
- path: "/",
- replace: true // 替换历史记录,禁止回退
- };
- } else {
- // 已登录且访问非登录页,校验用户权限(有可能token是伪造的,无效的)
- try {
- // 检查是否已有用户角色信息
- const hasRoles = userStore.state.roles.length > 0;
- // 已有角色信息,直接放行
- if (hasRoles) {
- NProgress.done();
- return true;
- }
- // 没有角色信息,重新获取用户信息
- await userStore.getUserInfo();
- // 根据用户角色动态生成可访问的路由配置
- const routes = await permissionStore.generateRoutes();
- // 动态添加路由到路由器
- routes.forEach((route) => router.addRoute(route));
- // 确保路由添加完成,重新访问目标路径
- return router.push({ path: to.path, replace: true });
- } catch (error) {
- // 获取用户信息失败,可能Token过期或无效
- console.error("获取用户信息失败:", error);
- // 清除用户状态并跳转到登录页
- userStore.logout();
- NProgress.done();
- // 携带当前路径作为重定向参数
- return `/login?redirect=${to.path}`;
- }
- }
- }
- // 情况2:未登录状态
- else {
- // 访问白名单页面,直接放行
- if (whiteList.includes(to.path)) {
- NProgress.done();
- return true;
- }
- // 非白名单页面,重定向到登录页并记录原始路径
- return {
- path: "/login",
- query: {
- redirect: to.path,
- ...to.query // 保留原路径的查询参数
- }
- };
- }
- });
复制代码 5 修改路由配置
修改 src/router/index.ts,代码如下:
- //src/router/index.ts
- import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router";
- import Layout from "@/layout/index.vue";
- export const constantRoutes: RouteRecordRaw[] = [
- {
- path: "/",
- component: Layout,
- redirect: "/dashboard",
- children: [
- {
- path: "dashboard",
- name: "dashboard",
- component: () => import("@/views/dashboard/index.vue"),
- meta: {
- icon: "ant-design:bank-outlined",
- title: "dashboard",
- affix: true, // 固定在tagsViews中
- noCache: true // 不需要缓存
- }
- }
- ]
- },
- {
- path: "/redirect",
- component: Layout,
- meta: {
- hidden: true
- },
- // 当跳转到 /redirect/a/b/c/d?query=1
- children: [
- {
- path: "/redirect/:path(.*)",
- component: () => import("@/views/redirect/index.vue")
- }
- ]
- },
- {
- path: "/login",
- name: "Login",
- meta: {
- hidden: true
- },
- component: () => import("@/views/login/index.vue")
- }
- ];
- export const asyncRoutes: RouteRecordRaw[] = [
- {
- path: "/documentation",
- component: Layout,
- redirect: "/documentation/index",
- children: [
- {
- path: "index",
- name: "documentation",
- component: () => import("@/views/documentation/index.vue"),
- meta: {
- icon: "ant-design:database-filled",
- title: "documentation"
- }
- }
- ]
- },
- {
- path: "/guide",
- component: Layout,
- redirect: "/guide/index",
- children: [
- {
- path: "index",
- name: "guide",
- component: () => import("@/views/guide/index.vue"),
- meta: {
- icon: "ant-design:car-twotone",
- title: "guide"
- }
- }
- ]
- },
- {
- path: "/system",
- component: Layout,
- redirect: "/system/menu",
- meta: {
- icon: "ant-design:unlock-filled",
- title: "system",
- alwaysShow: true
- // breadcrumb: false
- // 作为父文件夹一直显示
- },
- children: [
- {
- path: "menu",
- name: "menu",
- component: () => import("@/views/system/menu/index.vue"),
- meta: {
- icon: "ant-design:unlock-filled",
- title: "menu"
- }
- },
- {
- path: "role",
- name: "role",
- component: () => import("@/views/system/role/index.vue"),
- meta: {
- icon: "ant-design:unlock-filled",
- title: "role"
- }
- },
- {
- path: "user",
- name: "user",
- component: () => import("@/views/system/user/index.vue"),
- meta: {
- icon: "ant-design:unlock-filled",
- title: "user"
- }
- }
- ]
- },
- {
- path: "/external-link",
- component: Layout,
- children: [
- {
- path: "http://www.baidu.com",
- redirect: "/",
- meta: {
- icon: "ant-design:link-outlined",
- title: "link Baidu"
- }
- }
- ]
- }
- ];
- // 需要根据用户赋予的权限来动态添加异步路由
- export const routes = [...constantRoutes];
- export default createRouter({
- routes, // 路由表
- history: createWebHistory() // 路由模式
- });
复制代码 以上就是菜单权限相关内容。
下一篇将继承探讨 动态菜单的实现,敬请期待~
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |