基于Geeker-Admin后台管理模版实现动态路由,权限管理

- 前言:最近公司在研发新的项目,由于vue2.x已经停止维护更新,作为这个这个决策者,决定采用vue3.x+vite+pinia+element-plus+ts的最新技术栈进行开发。
- 但是呢,自己搭开发模版费时费力,几番周折下来,决定采用坊间比较靠谱的Geeker-Admin来进行开发,苦于全网都没个像样的教程,故花了几天的时间研究了一下这个模版,发现确实还挺好用的,就基于这个模版实现了登录、获取个人信息、在不破坏主体架构的基础上,略微改造,实现了权限管理、动态路由。
- 之前也写过vue2.x基于vue-element-admin开发模版实现的权限管理,并且录制了视频教程,帮助了数以百计的学员吧,小赚了2万来块,哈哈,也算有点小经验,希望这篇教程能帮到你,让你少走点弯路。
- 废话不多说了,下面开始表演。
第一步:下载模版,打开gitee地址:https://gitee.com/HalseySpicy/Geeker-Admin
git clone https://gitee.com/HalseySpicy/Geeker-Admin.git
下面vscode打开代码
Geeker-Admin
├─ .husky                  # husky 配置文件
├─ .vscode                 # VSCode 推荐配置
├─ build                   # Vite 配置项
├─ public                  # 静态资源文件(该文件夹不会被打包)
├─ src
│  ├─ api                  # API 接口管理
│  ├─ assets               # 静态资源文件
│  ├─ components           # 全局组件
│  ├─ config               # 全局配置项
│  ├─ directives           # 全局指令文件
│  ├─ enums                # 项目常用枚举
│  ├─ hooks                # 常用 Hooks 封装
│  ├─ languages            # 语言国际化 i18n
│  ├─ layouts              # 框架布局模块
│  ├─ routers              # 路由管理
│  ├─ stores               # pinia store
│  ├─ styles               # 全局样式文件
│  ├─ typings              # 全局 ts 声明
│  ├─ utils                # 常用工具库
│  ├─ views                # 项目所有页面
│  ├─ App.vue              # 项目主组件
│  ├─ main.ts              # 项目入口文件
│  └─ vite-env.d.ts        # 指定 ts 识别 vue
├─ .editorconfig           # 统一不同编辑器的编码风格
├─ .env                    # vite 常用配置
├─ .env.development        # 开发环境配置
├─ .env.production         # 生产环境配置
├─ .env.test               # 测试环境配置
├─ .eslintignore           # 忽略 Eslint 校验
├─ .eslintrc.cjs           # Eslint 校验配置文件
├─ .gitignore              # 忽略 git 提交
├─ .prettierignore         # 忽略 Prettier 格式化
├─ .prettierrc.cjs         # Prettier 格式化配置
├─ .stylelintignore        # 忽略 stylelint 格式化
├─ .stylelintrc.cjs        # stylelint 样式格式化配置
├─ CHANGELOG.md            # 项目更新日志
├─ commitlint.config.cjs   # git 提交规范配置
├─ index.html              # 入口 html
├─ LICENSE                 # 开源协议文件
├─ lint-staged.config.cjs  # lint-staged 配置文件
├─ package-lock.json       # 依赖包包版本锁
├─ package.json            # 依赖包管理
├─ postcss.config.cjs      # postcss 配置
├─ README.md               # README 介绍
├─ tsconfig.json           # typescript 全局配置
└─ vite.config.ts          # vite 全局配置文件
以上是项目的工程目录
第二步:安装依赖,run起项目
pnpm install //安装依赖
pnpm dev  //启动项目
下面是启动后的登录页面和登录后的首页
登录页

首页
第三步:下面当然老规矩,先来分析下,登录逻辑!只有掌握了登录逻辑,才会熟悉整个项目。
  • 模版加载首先打开路径src/main.ts这个文件,vue项目打开项目首次加载的就是main.ts或者js,没有之一,别较真昂~
image.png
  • 阴影部分是挂载一系列文件,可以先不管,最主要的就是import router from "@/routers";挂载路由,顺藤摸瓜,打开src/routers/index.ts文件
image.png
  • 找到路由拦截router.beforeEach,开始分析逻辑
  • 首先包含三个参数to, from, nextto代表去哪个页面,打印出来会报告到达的那个页面的路由信息;from代表从哪个页面来,打印出来会包含上一个页面的路由信息,next是放行的意思,如果不放行,说白了就是打不开下一个页面了。下面就是打印出来的to的路由信息
image.png
  • 可以很清晰的看到to的页面是/home/indexfrom的页面是/login,大白话就是跳转到的页面是【首页】,从【登录页】跳转过来的。
  • 好,下面继续往下看代码
const userStore = useUserStore();
const authStore = useAuthStore();
  • 按照指引,可以看出这是引入了 pinia状态管理
image.png
  • 模版作者注释的很详细
// 1.NProgress 开始
  NProgress.start();

  // 2.动态设置标题
  const title = import.meta.env.VITE_GLOB_APP_TITLE;
  document.title = to.meta.title ? `${to.meta.title} - ${title}` : title;

  // 3.判断是访问登陆页,有 Token 就在当前页面,没有 Token 重置路由到登陆页
  if (to.path.toLocaleLowerCase() === LOGIN_URL) {
    if (userStore.token) return next(from.fullPath);
    resetRouter();
    return next();
  }

  // 4.判断访问页面是否在路由白名单地址(静态路由)中,如果存在直接放行
  if (ROUTER_WHITE_LIST.includes(to.path)) return next();

  // 5.判断是否有 Token,没有重定向到 login 页面
  if (!userStore.token) return next({ path: LOGIN_URL, replace: true });

  // 6.如果没有菜单列表,就重新请求菜单列表并添加动态路由
  if (!authStore.authMenuListGet.length) {
    await initDynamicRouter();
    return next({ ...to, replace: true });
  }

  // 7.存储 routerName 做按钮权限筛选
  authStore.setRouteName(to.name as string);

  // 8.正常访问页面
  next();
  • 最重要的是3、4、5、6点,我们先来看3,4,5点,第6点待会看
  • 动态设置标题后,再判断访问的是否是登陆页,如果有 Token,也就是在登录后,在访问登录页,就不允许跳转登录页, 就停留在当前页面;如果没有 Token,也就是如果没有登录的状态下访问登录页,就重置路由直接放行到登陆页。
  • 第4点,判断访问页面是否在路由白名单地址(静态路由)中,如果存在直接放行,也就是说,如果访问的页面在白名单中,就直接放行。
  • 目前白名单就只有/500
image.png
  • 下面就在看第五点,判断是否有 Token,也就是说判断是否登录了,如果没有登录就重定向到 登录 /login 页面
  • 第6点,暂时不看,逻辑就执行完了。顺利跳转到了登录页面。
image.png
第四步:下面就开始分析,点击登录触发的方法,打开src/views/login/components/LoginForm.vue文件,找到login方法,
const login = (formEl: FormInstance | undefined) => {
  if (!formEl) return;
  formEl.validate(async valid => {
    if (!valid) return;
    loading.value = true;
    try {
      // 1.执行登录接口
      const { data } = await loginApi({ ...loginForm, password: md5(loginForm.password) });
      userStore.setToken(data.access_token);

      // 2.添加动态路由
      await initDynamicRouter();

      // 3.清空 tabs、keepAlive 数据
      tabsStore.setTabs([]);
      keepAliveStore.setKeepAliveName([]);

      // 4.跳转到首页
      router.push(HOME_URL);
      ElNotification({
        title: getTimeState(),
        message: "欢迎登录 Geeker-Admin",
        type: "success",
        duration: 3000
      });
    } finally {
      loading.value = false;
    }
  });
};
  • 这个方法首先校验了form表单的完整性,如果校验通过,就执行try里面的代码逻辑,首先请求loginApiapi方法,按照指引,可以看到开发模块封装好的loginApi方法,http是封装好的axios,含请求拦截和响应拦截,这里就不讲了,方法接收两个参数,loginForm里的usernamepassword
image.png
  • 请求成功后,返回{ data },并且userStore.setToken(data.access_token);把接口返回的token存储到pinia状态管理器里。
  • 下面就是执行添加动态路由的方法await initDynamicRouter();
  • 这个方法是异步的,所以在这个方法执行完之前,不会往下执行,所以按照指引,打开initDynamicRouter方法,打开src/routers/modules/dynamicRouter.ts文件,找到initDynamicRouter方法
/**
 * @description 初始化动态路由
 */
export const initDynamicRouter = async () => {
  const userStore = useUserStore();
  const authStore = useAuthStore();

  try {
    // 1.获取菜单列表 && 按钮权限列表
    await authStore.getAuthMenuList();
    await authStore.getAuthButtonList();

    // 2.判断当前用户有没有菜单权限
    if (!authStore.authMenuListGet.length) {
      ElNotification({
        title: "无权限访问",
        message: "当前账号无任何菜单权限,请联系系统管理员!",
        type: "warning",
        duration: 3000
      });
      userStore.setToken("");
      router.replace(LOGIN_URL);
      return Promise.reject("No permission");
    }

    // 3.添加动态路由
    authStore.flatMenuListGet.forEach(item => {
      item.children && delete item.children;
      if (item.component && typeof item.component == "string") {
        item.component = modules["/src/views" + item.component + ".vue"];
      }
      if (item.meta.isFull) {
        router.addRoute(item as unknown as RouteRecordRaw);
      } else {
        router.addRoute("layout", item as unknown as RouteRecordRaw);
      }
    });
  } catch (error) {
    // 当按钮 || 菜单请求出错时,重定向到登陆页
    userStore.setToken("");
    router.replace(LOGIN_URL);
    return Promise.reject(error);
  }
};
  • 首先引入pinia
 const userStore = useUserStore();
 const authStore = useAuthStore();
  • 在执行获取菜单、获取按钮权限的异步方法,这两个方法在pinia
 // 1.获取菜单列表 && 按钮权限列表
 await authStore.getAuthMenuList();
 await authStore.getAuthButtonList();
  • 打开 src/stores/modules/auth.ts文件
 // Get AuthButtonList
    async getAuthButtonList() {
      const { data } = await getAuthButtonListApi();
      this.authButtonList = data;
    },
    // Get AuthMenuList
    async getAuthMenuList() {
      const { data } = await getAuthMenuListApi();
      this.authMenuList = data;
    },
  • 顺藤摸瓜,在打开getAuthButtonListApigetAuthMenuListApi这两个api方法
// 获取菜单列表
export const getAuthMenuListApi = () => {
  return http.get<Menu.MenuOptions[]>(PORT1 + `/menu/list`, {}, { loading: false });
  // 如果想让菜单变为本地数据,注释上一行代码,并引入本地 authMenuList.json 数据
  return authMenuList;
};

// 获取按钮权限
export const getAuthButtonListApi = () => {
  return http.get<Login.ResAuthButtons>(PORT1 + `/auth/buttons`, {}, { loading: false });
  // 如果想让按钮权限变为本地数据,注释上一行代码,并引入本地 authButtonList.json 数据
  return authButtonList;
};
  • 这两个接口返回后,把authMenuListauthButtonList,存到了pinia状态管理器里,然后继续走下面的逻辑
// 2.判断当前用户有没有菜单权限
if (!authStore.authMenuListGet.length) {
      ElNotification({
        title: "无权限访问",
        message: "当前账号无任何菜单权限,请联系系统管理员!",
        type: "warning",
        duration: 3000
      });
      userStore.setToken("");
      router.replace(LOGIN_URL);
      return Promise.reject("No permission");
    }
  • 如果没有任何菜单权限,就直接拦截,并提示无权限访问,当前账号无任何菜单权限,请联系系统管理员!
  • 如果有菜单权限,就直接执行第3点,authStore.flatMenuListGet,这个【flatMenuListGet】数组是pinia里的getters里扁平化之后的一维数组菜单,主要用来添加动态路由,如果你精通vuex,这里你就很容易看得懂。
image.png
// 3.添加动态路由
    authStore.flatMenuListGet.forEach(item => {
      item.children && delete item.children;
      if (item.component && typeof item.component == "string") {
        item.component = modules["/src/views" + item.component + ".vue"];
      }
      if (item.meta.isFull) {
        router.addRoute(item as unknown as RouteRecordRaw);
      } else {
        router.addRoute("layout", item as unknown as RouteRecordRaw);
      }
    });
  • 扁平化后的一维数组,经过处理,使用路由的addRoute方法,将处理后的路由挂载到router实例中。
  • 这里执行完后,才会执行LoginForm.vue文件中 login方法里的第3、4点
// 3.清空 tabs、keepAlive 数据
      tabsStore.setTabs([]);
      keepAliveStore.setKeepAliveName([]);

      // 4.跳转到首页
      router.push(HOME_URL);
      ElNotification({
        title: getTimeState(),
        message: "欢迎登录 Geeker-Admin",
        type: "success",
        duration: 3000
      });
  • 清空 tabs、keepAlive 数据,并且跳转到首页,然后弹出engulf成功的提示.【欢迎登录 Geeker-Admin】
image.png

继续往下!

第五步:既然要开发项目嘛,先把无用的多余的页面删了。
  • 要删页面呢,肯定要先看路由文件routers,但是呢但是呢,这个项目是请求接口获取的动态路由,路由文件里,只配置了基础的staticRouter (静态路由)errorRouter (错误页面路由),那咋整???我们的原则是还不能破坏模版的大框架结构(人家的很规范,改起来能麻烦死,很容易弃坑哈哈哈哈哈)
  • 别急,跟我来,打开src/api/modules/login.ts文件,找到getAuthMenuListApi方法,你可以看到// 如果想让菜单变为本地数据,注释上一行代码,并引入本地 authMenuList.json 数据,你要听话,让你注释就注释,别钻牛角尖,注释了,在看页面的变化,如下:[人家的写法真的很规范,在进入正式项目开发之前,我们在来讲怎么写vue3+ts照葫芦画瓢 你也能会]
image.png
  • 好吧,好像没啥变化,啊哈哈,但是我们可以控制渲染不渲染哪个路由了,打开authMenuList.json,路径src/assets/json/authMenuList.json
image.png
  • 看嘛。是不是对上了,下面就开始删除除了首页之外的所有页面,只剩下面这段代码
{
  "code": 200,
  "data": [
    {
      "path": "/home/index",
      "name": "home",
      "component": "/home/index",
      "meta": {
        "icon": "HomeFilled",
        "title": "首页",
        "isLink": "",
        "isHide": false,
        "isFull": false,
        "isAffix": true,
        "isKeepAlive": true
      }
    }
  ],
  "msg": "成功"
}
  • 项目节目变成这样
image.png
  • 接下来,再把删掉的路由对应的文件删除。
image.png
  • 蓝框里的都可以删除,留下homelogin!!!!
第六步:开始分析这个authMenuList.json,为啥return这个json文件就可以渲染出来页面呢?
  • 不讲了,9.00了先上班,下班在讲!
  • 中午了,继续,上回说到要分析下这个authMenuList.json,先打开src/api/modules/login.ts文件,找到getAuthMenuListApi方法,复制它,全局搜索,找到调用它的地方,如下图:路径src/stores/modules/auth.ts
image.png
  • 这里做了一个事情,就是把authMenuList.json中的data数据,赋值给了 state中的authMenuList //菜单权限列表。
  • 同时,getters触发,代码如下:
// 菜单权限列表 ==> 扁平化之后的一维数组菜单,主要用来添加动态路由
    flatMenuListGet: state => getFlatMenuList(state.authMenuList),
image.png
  • 可以看到,这个方法做了一件事,就是把authMenuList.json中的data数组扁平化,转成一维数组,方便用啦添加动态路由,可能很多同学不知道什么是数组扁平化,建议去百度下,这里我就把扁平化的数组,截图出来给大家看下,为了方便演示,我在authMenuList.json中,再新建一个嵌套路由,如下:
{
  "code": 200,
  "data": [
    {
      "path": "/home/index",
      "name": "home",
      "component": "/home/index",
      "meta": {
        "icon": "HomeFilled",
        "title": "首页",
        "isLink": "",
        "isHide": false,
        "isFull": false,
        "isAffix": true,
        "isKeepAlive": true
      }
    },
    {
      "path": "/assembly",
      "name": "assembly",
      "redirect": "/assembly/guide",
      "meta": {
        "icon": "Briefcase",
        "title": "测试菜单",
        "isLink": "",
        "isHide": false,
        "isFull": false,
        "isAffix": false,
        "isKeepAlive": true
      },
      "children": [
        {
          "path": "/assembly/guide",
          "name": "guide",
          "component": "/home/index",
          "meta": {
            "icon": "Menu",
            "title": "测试页面",
            "isLink": "",
            "isHide": false,
            "isFull": false,
            "isAffix": false,
            "isKeepAlive": true
          }
        }
      ]
    }
  ],
  "msg": "成功"
}
  • 现在页面变成了这样
image.png
  • 然后我们在来看flatMenuListGet数组,利用vue-devtool插件可以看pinia内部数据。
image.png
  • 这样的[1,2,3,4,5]就是一维数组,这样就是二维数组[[1,2],[3,4],5],数组扁平化就是把二维或者多维数组转成一维数组,明白了吧
  • 好了,跑题了。
  • 继续
  • src/stores/modules/auth.ts找到getAuthMenuList方法,在按照同样的方法,全局检索下,哪里调用了这个方法,路径src/routers/modules/dynamicRouter.ts
image.png
  • 原来是initDynamicRouter初始化动态路由这个方法调用的,getAuthMenuList方法,getAuthButtonList方法是一样的哈,逻辑一模一样,不要问我为啥不讲,因为我后面还要删了它呢,按钮权限我会有另外一种思路。
  • getAuthMenuList方法是个异步方法,执行完了以后,继续往下执行,2.判断当前用户有没有菜单权限这一步走不到,因为我们有权限,直接到第3步,添加动态路由,就用到了扁平化的一维数组:flatMenuListGet数组
 // 3.添加动态路由
    authStore.flatMenuListGet.forEach(item => {
      item.children && delete item.children;
      if (item.component && typeof item.component == "string") {
        item.component = modules["/src/views" + item.component + ".vue"];
      }
      if (item.meta.isFull) {
        router.addRoute(item as unknown as RouteRecordRaw);
      } else {
        router.addRoute("layout", item as unknown as RouteRecordRaw);
      }
    });
  • 文档中就不多解释了,你不理解的话也不影响你开发项目,解释起来打字太麻烦了,回头我会录制视频课程。
  • initDynamicRouter这个方法执行完了以后,已经把路由添加到router路由实例里去了,这个方法执行完了。
  • 然后,别忘了这个方法是在哪里执行的,复制initDynamicRouter全局检索,在src/views/login/components/LoginForm.vue页面中,点击login方法后,触发的第2点 // 2.添加动态路由,在这里调用了initDynamicRouter方法,这个方法执行完毕后,继续往下执行,router.push(HOME_URL);就跳转到首页了。
  • 至此,登录逻辑剖析完毕,不懂的在加我v交流沟通
1115009958
  • 未完待续...

接上篇 基于Geeker-Admin后台管理模版实现动态路由,权限管理01
  • 上篇我们学完了对整个登录逻辑的剖析,这一篇我们来细细讲下,vue3+ts如何开发项目的,我们既然是使用这个模版来开发,就要熟悉它,使用它,然后在试着改造它,使其适配你们自己的项目,这才是正确的学习步骤,不要上来就想剖析源码,很容易放弃自己的,哈哈。
  • 一个项目,拿到手之后,第一步一定少不开反向代理,因为请求接口,如果后端没有放开跨域,前端直接请求,肯定会跨域,那么开发环境解决跨域的方式就是配置反响代理。
  • 之前vue2.x+js的项目配置反向代理的位置在vue.config.js文件的proxy里,那么vue3.x+vite+ts的项目,一定在vite.config.ts文件的proxy中,那么,打开vite.config.ts找到proxy
server: {
      host: "0.0.0.0",
      port: viteEnv.VITE_PORT,
      open: viteEnv.VITE_OPEN,
      cors: true,
      // Load proxy configuration from .env.development
      proxy: createProxy(viteEnv.VITE_PROXY)
    },
  • 可以看到,proxy反向代理,这个模版全部封装起来了,createProxy是个封装好的方法,我们不要管,都配置好了,直接用,如何vue2.x项目里是你自己配置的,你应该很熟悉下面这段代码
image.png
  • createProxy(viteEnv.VITE_PROXY)传入的一个实参,顺藤摸瓜,找到本地环境配置文件.env.development里的VITE_PROXY
image.png
  • 全局搜索VITE_API_URL,打开src/api/index.ts,这个文件是模版封装好的axios请求插件,代码不用动,就修改下该修改的地方,比如:
config.headers.set("x-access-token", userStore.token);
image.png
  • 这里都x-access-token修改成你们后端所接收的token key
  • data.code == ResultEnum.OVERDUEdata.code && data.code !== ResultEnum.SUCCESS这里的code值,修改成你们项目实际返回的对应的code,模版封装统一管理起来了,很方便,路径是src/enums/httpEnum.ts
image.png
  • 这里我就不动了。
  • 接下来,就打开src/api/modules/login.ts文件,我们来看下,登录接口是如何封装的。
image.png
  • 首先引入了import { Login } from "@/api/interface/index";接口,导入了Login接口,里面规定了某些字段,多传或者不传或者少传,都会报错提醒,这就是TS,严格校验规范了开发规范,故而开发周期可能会久一点(相比vue+js项目来说)。
  • Login.ReqLoginForm规范了传入参数字段,必须是usernamepassword,而且比如是string类型,否则报错!
  • Login.ResLogin规范了,接口返回数据字段,比如有access_token,且必须是string类型
image.png
  • 换句话说,loginApi = (params: Login.ReqLoginForm)括弧里的是规范传入参数的
  • http.post<Login.ResLogin>请求方式后面的尖括号是规范返回字段的。
  • import { PORT1 } from "@/api/config/servicePort";这个是引入了一个请求接口前缀
image.png
  • 没啥好说的,就是个拼接接口地址用的,根据实际情况使用
import authMenuList from "@/assets/json/authMenuList.json";
import authButtonList from "@/assets/json/authButtonList.json";
  • 这俩就是引入的本地json文件。
  • import http from "@/api";这个导入的文件,就至关重要了,这是接口请求工作。
  • 这个请求工具,我们已经配置过了,直接就可以用了。
/**
 * @name 登录模块
 */
// 用户登录
export const loginApi = (params: Login.ReqLoginForm) => {
  return http.post<Login.ResLogin>(PORT1 + `/login`, params, { loading: false }); // 正常 post json 请求  ==>  application/json
};
  • 如果你在开发中,想要新建api方法,就直接复制它,稍做修改请求方式和传参接口,以及返回接口字段规范。就可以了。
  • *后面在切换真实接口的时候,在演示如何新建方法和和接口*

下篇再讲对接真实登录接口,累了,想睡觉了。

接上篇 基于Geeker-Admin后台管理模版实现动态路由,权限管理02
  • 上篇讲了模版中的反向代理以及api方法是如何定义的。
  • 下面来讲下,如何着手开发项目,我们就拿对接真实登录接口来讲吧
  • 登录接口地址:https://demo.it98k.cn/admin/api/login
  • 要求传入参数:loginamepassword
  • 接口返回的格式为:
{
    "code": 200,
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpX5cCI6IkpX5cCI6IkpX",
    "message": "登录成功"
}
  • 接口接收的参数和返回的数据字段,和模版是不匹配的,我们就需要改下模版了。
  • 现在先不改,等遇到报错了,我们根据报错再来改造,这叫对症下药
第一步:既然知道接口地址了,首先先来配置项目反向代理。
  • 打开文件.env.development本地配置文件
  • 修改VITE_PROXY = [["/api","https://mock.mengxuegu.com/mock/629d727e6163854a32e8307e"]]
  • 修改为:
VITE_PROXY = [["/api","https://demo.it98k.cn"]]
  • 然后,不用启动项目,实时生效,vite还是很方便!!!
第二步:打开src/api/modules/login.ts文件,找到loginApi,修改下接口地址
  • 修改为:
export const loginApi = (params: Login.ReqLoginForm) => {
  return http.post<Login.ResLogin>(PORT1 + `/api/login`, params, { loading: false }); // 正常 post json 请求  ==>  application/json
  // return http.post<Login.ResLogin>(PORT1 + `/login`, params, { loading: false }); // 控制当前请求不显示 loading
  // return http.post<Login.ResLogin>(PORT1 + `/login`, {}, { params }); // post 请求携带 query 参数  ==>  ?username=admin&password=123456
  // return http.post<Login.ResLogin>(PORT1 + `/login`, qs.stringify(params)); // post 请求携带表单参数  ==>  application/x-www-form-urlencoded
  // return http.get<Login.ResLogin>(PORT1 + `/login?${qs.stringify(params, { arrayFormat: "repeat" })}`); // get 请求可以携带数组等复杂参数
};
  • 别忘了,还有一个/admin忘记拼接了,打开src/api/config/servicePort.ts文件,把export const PORT1 = "/geeker";改为export const PORT1 = "/admin";
  • 当然,你也可以选择不拼接,看你项目需求,如果不拼接就直接把loginApi方法内的PORT1去掉就行了
  • 接下来,需要修改传入参数,打开src/views/login/components/LoginForm.vue文件
  • 找到下面这段代码
<el-input v-model="loginForm.username" placeholder="用户名:admin / user">
const loginRules = reactive({
  username: [{ required: true, message: "请输入用户名", trigger: "blur" }],
  password: [{ required: true, message: "请输入密码", trigger: "blur" }]
});
const loginForm = reactive<Login.ReqLoginForm>({
  username: "",
  password: ""
});
  • 修改为:
<el-input v-model="loginForm.loginame" placeholder="用户名:admin / user">
const loginRules = reactive({
  loginame: [{ required: true, message: "请输入用户名", trigger: "blur" }],
  password: [{ required: true, message: "请输入密码", trigger: "blur" }]
});
const loginForm = reactive<Login.ReqLoginForm>({
  loginame: "",
  password: ""
});
  • 然后,代码飘红了,这就是TS的强类型校验,在对应的ReqLoginForm接口里并不存在loginame字段
image.png
  • 报错信息提示的很到位,那就去改下
  • 打开src/api/interface/index.ts文件,找到ReqLoginForm
  export interface ReqLoginForm {
    username: string;
    password: string;
  }
  • 修改为:
  export interface ReqLoginForm {
    loginame: string;
    password: string;
  }
image.png
  • 这种飘蓝的,不用管也行,没有任何影响,就是单词不规范。如果你不舒服,就点击快速修复
image.png
  • 就不飘蓝了
image.png
  • 接下来,输入账号密码先试试
  • 输入账号admin,密码123456,当然,我是随便输入的,目的就是先检测下,有没有报错,方便再去修改别的,不出意外,果然报错了
image.png
  • 第一点,就是错误信息,没弹出来,第2点就是密码后端接收没要求加密,需要去掉加密
  • 先去掉加密
image.png
  • 把md5干掉就行了
  • 保存项目,会报错,记住 vue3+TS项目,文件中不允许存在未使用的方法或者插件,直接去注释掉这一行就行了,或者删掉,就没报错了
image.png
  • 再来处理下,报错信息没显示出来的问题。
image.png
  • message应该出现在箭头所指的位置的,【没出现就是因为可能字段没取对
  • 打开文件去排查src/api/index.ts【你要问我为啥我知道是打开这个文件,因为,这个文件内封装了接口响应拦截,全局拦截了错误信息,所以要在这个文件里找到响应拦截器咯】
image.png
  • 一看就明白了,代码里取的是msg!,接口返回的是message,改下就可以了
image.png
  • 那接下来,输入正确的账号密码admin14551,接口请求成功但是控制台报错了
image.png

image.png
  • 原因就是无法读取未定义的属性(正在读取“access_token”),说白了,就是后端接口压根没返回access_token这个字段,所以时时刻刻要根据你的项目接口来改造模版
  • 这里可以点进去,直接到报错位置
image.png

image.png
  • 你可以打印出来data看看,就明白了
image.png
  • 打印出来是undefined
image.png
  • 再看接口返回,就知道了,接口返回数据结构里,没有data,解构不出来,但是可以解构出来token
image.png
  • 所以这么改
// 1.执行登录接口
const { token } = await loginApi({ ...loginForm, password: loginForm.password });

userStore.setToken(token);
  • 但是飘红了
image.png
  • 还是TS强类型校验哈哈,别嫌烦哦,烦就别用TS!
  • 你要是想问,在哪里规范了接口返回字段的
  • 在这里
image.png
  • 所以,找到ResLogin类型接口
image.png
  • 修改ResLogin为这样
 export interface ResLogin {
    token: string;
  }
image.png
  • 但是,还报错,是因为这里进行了双重校验,说到底,还是我接口数据接口返回不规范,接口都要包含data的,如果你们的接口都有返data,你就不会遇到我这种问题
  • 针对我这个不规范的接口,改一下吧
  • 全局搜ResultData,打开src/api/index.ts
image.png
  • 在模版自己封装的常用请求方法,规范了ResultData,点过去可以看到
image.png
  • 请求响应参数(包含data)
  • 要么一刀切,不校验这个了,要么就修改接口返回数据结构,我这里选择一刀切
image.png
  • 去掉ResultData,但是这样会违反使用TS开发的初衷,如果你也存在这种情况,建议去找后端修改数据结构
  • 这样就不飘红了
image.png
  • 在输入正确的账号密码,点击登录试试看
image.png
  • 可以看到,接口请求成功了,代码也往下执行了,说明我们的登录接口对接成功了,下面的报错是接口404,不存在
  • 下篇再继续讲!!!!
接上篇:基于Geeker-Admin后台管理模版实现动态路由,权限管理03
  • 上篇讲完了如何对接真实的登录接口,并且已经调试成功,并且继续往下执行,调用了一个404的接口
  • 接下来再继续往下讲
  • 如果你不知道在哪调用这个方法,就直接复制接口地址auth/buttons,全局搜索一下
image.png
  • 然后再全局检索一下getAuthButtonListApi这个方法是在哪里调用的
  • image.png
  • 然后再全局检索getAuthButtonList方法
image.png
  • 我们一层一层的找,终于是找到了在哪里调用的,当然这是最笨的方法,如果再第一课,你已经掌握了,你就会知道,这个方法在哪里调用,直接就可以打开了,不用这么麻烦。
  • 其实我们这个项目实现按钮权限不用这个方式,这个方法可以注释掉或者删除掉。
image.png
  • 下面在输入账号密码,点击登录试试,会发生什么
image.png
  • 嘿,进去了~
  • 但是,要想实现动态路由、权限管理,还需要在多请求一个接口
  • 如果你之前学过我的vue2实现的动态路由权限管理,就会知道
  • 我们在登录接口后,立马执行了一个getInfo获取用户信息的接口
  • 而这个项目没有,所以要补上他
  • 下面我们就要简单学下pinia状态管理器怎么用了,getInfo这个方法就是要写在pinia里的
还有 34% 的精彩内容
©著作权归作者所有,转载或内容合作请联系作者
支付 ¥15.00 继续阅读
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容