antd vue pro-vip登录鉴权部分

目录结构

├── public
│ └── logo.png # LOGO
| └── index.html # Vue 入口模板
├── src
│ ├── api # Api ajax 等
│ ├── assets # 本地静态资源
│ ├── config # 项目基础配置,包含路由,全局设置
│ ├── components # 业务通用组件
│ ├── core # 项目引导, 全局配置初始化,依赖包引入等
│ ├── router # Vue-Router
│ ├── store # Vuex
│ ├── utils # 工具库
│ ├── locales # 国际化资源
│ ├── views # 业务页面入口和常用模板
│ ├── App.vue # Vue 模板入口
│ └── main.js # Vue 入口 JS
│ └── permission.js # 路由守卫(路由权限控制)
│ └── global.less # 全局样式
├── tests # 测试工具
├── README.md
└── package.json

路线 app -> router-view -> Layout -> basic-layout -> router-view -> RouteView

App.vue

组件

  1. ConfigProvider 全局化配置 国际化切换语言入口
  2. router-view 全局页面入口

做了哪些事

  1. menuState 取缓存的菜单状态并初始化

  2. lang 语言初始化(默认英语)

  3. theme 主题初始化

    • 通过给 html 标签添加属性data-pro-theme
  4. isMobilerealDarkmenuState向下传递

router 入口

文件

  1. staticRoutes 静态路由(不需要权限)
  2. AsyncWorkplace 登录后首页(仪表盘)
  3. createRouter 创建 router 实例并设置 history 模式

做了哪些事

  1. defineAsyncComponent 同步渲染仪表盘组件, 其余组件均为异步加载

  2. staticRoutes 登录白名单

    • login 登录页
    • register 注册页
    • register-result 注册结果页
    • 404 404 页
  3. routes 需要权限处理的页面

Layout

组件

  1. WaterMark 水印
  2. LeftMenuLayout 一二级分栏展示
  3. BasicLayout 一二同栏展示

做了哪些事

  1. <water-mark :disabled="true"></water-mark> 控制是否禁止显示水印
  2. <left-menu-layout></left-menu-layout> 显示一二级分栏
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x5RUXSMg-1628669727112)(9C7F378C627548B2AB20C3797EB0CEF8)]
  3. <basic-layout ></basic-layout> 显示一二级同栏 目前一般使用的基础布局

basic-layout

组件

  1. pro-provider pro 容器
  2. sider-menu 左侧菜单组件
  3. a-drawer 左侧菜单放在抽屉里
  4. HeaderView 头部组件
  5. multi-tab 快捷 tab
  6. router-view 视图
  7. wrap-content 主内容容器

做了哪些事

  1. pro-provider 布局宽度/语言

  2. sider-menu 左侧菜单

  3. a-drawer 移动端

  4. header-view 头部组件

    • notice-icon 消息通知组件
    • avatar-dropdown 个人信息操作
    • select-lang 切换语言组件
  5. wrap-content 添加 class 类名

route-view

组件

  1. transition 动画
  2. multi-tab-store-consumer 创建消费端

做了哪些事

  1. multi-tab-store-consumer
    • 取缓存路由
    • 保存路由组件
    • 合并页面组件

登录鉴权

main.ts

  1. 引入了 import './router/router-guards';

/router/router-guards

  1. router.beforeEach 全局前置守卫
  • 是否登录 --> 已登录 --> 除了免权限白名单都跳登录页
  • 是否登录 --> 未登录 --> 查看是否已经生成权限表
    • 已生成直接跳转
    • 未生成采用静态路由或动态路由
  1. 登录 -> access token -> localstorage -> 通过 token 获取 user 信息包括 user 权限 -> user 权限和本地路由通过对比生成权限树或服务端返回完整的权限树 -> token 过期 -> 重新登录

登录

  1. handleSubmit 登录确定

    • 验证对应的登录方式
    • 调用 store.dispatch(user/${LOGIN}, { …values})
    • 跳转到 /
  2. 获取 token

    store/user/actions 登录函数

[LOGIN]({ commit }, info) {
  return new Promise((resolve, reject) => {
    // call ajax
    postAccountLogin(info)
    .then(res => {
      // 将token储存到state里并且缓存到local-storage
      commit(SET_TOKEN, res.token);
      resolve(res);
    })
    .catch(error => {
      reject(error);
    });
  });
}
  1. 路由守卫拦截

跳转登录成功后跳转 ‘/’, /router/router-guards 会进行拦截

// 获取用户信息(包含权限)
const info = await store.dispatch(`user/${GET_INFO}`);
const allowRouters = await store.dispatch(`user/${GENERATE_ROUTES}`, info);
// const allowRouters = store.dispatch(`user/${GENERATE_ROUTES_DYNAMIC}`, info);
  1. 获取用户信息并存储用户信息

    store/user/actions

// store/user/actions [GET_INFO] 获取用户信息
[GET_INFO]({ commit }) {
  return new Promise((resolve, reject) => {
    getCurrentUser()
      .then((res: UserInfo) => {
        // 将用户信息存储到state里
        commit(SET_INFO, res);
        resolve(res);
      })
      .catch(err => {
        // 获取登录用户信息后,直接清理掉当前 token 并强制让流程走到登录页
        commit(SET_TOKEN, null);
        reject(err);
      });
  });
},

// store/user/mutations [SET_INFO] 设置用户信息
[SET_INFO]: (state, info) => {
  if (info.role) {
    state.role = info.role;
  }
  if (info.userid) {
    state.username = info.userid;
    state.nickname = info.userid;
  }
  if (info.name) {
    state.nickname = info.name;
  }
  if (info.avatar) {
    state.avatar = info.avatar;
  }
  state.extra = { ...info };
},

静态构建路由权限管理

  1. 处理用户权限
import { hasAuthority, filterChildRoute } from '@/utils/authority';

// store/user/actions [GENERATE_ROUTES] 从路由表构建路由
// 从路由表构建路由(前端对比后端权限字段过滤静态路由表)
[GENERATE_ROUTES]({ commit }, info: UserInfo) {
  return new Promise<RouteRecordRaw[]>(resolve => {
    // 修改这里可以进行接口返回的对象结构进行改变
    // 亦或是去掉 info.role 使用别的属性替代
    // 任何方案都可以,只需要将最后拼接构建好的路由数组使用
    // router.addRoute() 添加到当前运行时的路由中即可
    const { permissions } = (info.role || {}) as Role;
    // 返回index下children路由
    const allRoutes = filterMenu(routes);
    // permissionsKey = ['home','form']
    const permissionsKey = permissions?.map(permission => permission.name);

    /**
     * 先处理一层
     * 如果这层有children的话,就要递归处理对应权限,返回用户权限对应的路由
     */
    const allowRoutes = !permissionsKey
      ? allRoutes
      : allRoutes.filter(route => {
        // parnent route filter
        const hasAllow = hasAuthority(route as MenuDataItem, permissionsKey!);
        if (hasAllow && route.children && route.children.length > 0) {
          // current route children filter
          route.children = filterChildRoute(route as MenuDataItem, permissionsKey!);
        }
        return hasAllow;
      });
      // 解构路由表 _代表的是index下的children, mainRoute是index路由
    const {
      // eslint-disable-next-line
      children: _,
      ...mainRoute
    } = routes[0];
    const route = {
      ...mainRoute,
      children: allowRoutes,
    };
    // 当采用动态路由时,/workplace可能不是重定向首页,解决原pro-vip框架跳转错误问题
    route.redirect = allowRoutes[0]?.path;
    // 添加动态路由
    router.addRoute(route);
    // 存储用户权限匹配出来的路由
    commit(SET_ROUTERS, allowRoutes);
    resolve(allowRoutes);
  });
},
  1. 获取 index 下 children 全部路由

    @/utils/menu-util filterMenu

export const filterMenu = (routes: MenuDataItem[]): RouteRecordRaw[] => {
	return routes.find((item) => item.name === "index")?.children || [];
};
  1. 具体处理权限函数
// @/utils/authority
import { MenuDataItem } from '@/router/typing';

export const filterChildRoute = (route: MenuDataItem, permissions: string[]) =>
  route.children
    ?.filter(item => {
      // 递归遍历返回与权限相符的路由
      const hasAllow = hasAuthority(item, permissions);
      if (hasAllow && item.children && item.children.length > 0) {
        item.children = filterChildRoute(item, permissions!);
      }
      return hasAllow;
    })
    .filter(item => item);

// permissions: Permission[]
export const hasAuthority = (route: MenuDataItem, permissions: string[]) => {
  if (route.meta?.authority) {
    return permissions.some(value => {
      return route.meta?.authority?.includes(value);
    });
  }
  // 如true 返回: 没有authority属性的路由 + 符合权限的路由, false 返回 有authority属性的路由并且符合权限的路由
  return true;
};
  1. 缓存路由
[SET_ROUTERS]: (state, allowRoutes) => {
  state.allowRouters = allowRoutes;
},

动态构建路由权限管理

  1. 获取用户信息

  2. 获取接口数据构建前端路由

// store/user/actions [GENERATE_ROUTES_DYNAMIC] 从路由表构建路由
  // 从后端获取路由表结构体,并构建前端路由
  [GENERATE_ROUTES_DYNAMIC]({ commit }) {
    return new Promise<RouteRecordRaw>(resolve => {
      generatorDynamicRouter()
        .then((routes: RouteRecordRaw) => {
          const allowRoutes = routes.children || [];
          // 添加到路由表
          router.addRoute(routes);

          commit(SET_ROUTERS, allowRoutes);
          resolve(routes);
        })
        .catch(err => {
          console.error('generatorDynamicRouter', err);
        });
    });
  },
  1. 缓存路由
[SET_ROUTERS]: (state, allowRoutes) => {
  state.allowRouters = allowRoutes;
},
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值