- 前言:最近公司在研发新的项目,由于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,没有之一,别较真昂~
- 阴影部分是挂载一系列文件,可以先不管,最主要的就是
import router from "@/routers";
挂载路由,顺藤摸瓜,打开src/routers/index.ts
文件
- 找到路由拦截
router.beforeEach
,开始分析逻辑
- 首先包含三个参数
to, from, next
,to
代表去哪个页面,打印出来会报告到达的那个页面的路由信息;from
代表从哪个页面来,打印出来会包含上一个页面的路由信息,next
是放行的意思,如果不放行,说白了就是打不开下一个页面了。下面就是打印出来的to
的路由信息
- 可以很清晰的看到to的页面是
/home/index
,from
的页面是/login
,大白话就是跳转到的页面是【首页】,从【登录页】跳转过来的。
- 好,下面继续往下看代码
const userStore = useUserStore();
const authStore = useAuthStore();
// 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
- 下面就在看第五点,判断是否有 Token,也就是说判断是否登录了,如果没有登录就重定向到 登录
/login
页面
- 第6点,暂时不看,逻辑就执行完了。顺利跳转到了登录页面。
第四步:下面就开始分析,点击登录触发的方法,打开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
里面的代码逻辑,首先请求loginApi
api方法,按照指引,可以看到开发模块封装好的loginApi
方法,http
是封装好的axios
,含请求拦截和响应拦截,这里就不讲了,方法接收两个参数,loginForm
里的username
和password
。
- 请求成功后,返回{ 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);
}
};
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;
},
- 顺藤摸瓜,在打开
getAuthButtonListApi
、getAuthMenuListApi
这两个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;
};
- 这两个接口返回后,把
authMenuList
和authButtonList
,存到了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,这里你就很容易看得懂。
// 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】
继续往下!
第五步:既然要开发项目嘛,先把无用的多余的页面删了。
- 要删页面呢,肯定要先看路由文件
routers
,但是呢但是呢,这个项目是请求接口获取的动态路由,路由文件里,只配置了基础的staticRouter (静态路由)
和errorRouter (错误页面路由)
,那咋整???我们的原则是还不能破坏模版的大框架结构(人家的很规范,改起来能麻烦死,很容易弃坑哈哈哈哈哈)
- 别急,跟我来,打开
src/api/modules/login.ts
文件,找到getAuthMenuListApi
方法,你可以看到// 如果想让菜单变为本地数据,注释上一行代码,并引入本地 authMenuList.json 数据
,你要听话,让你注释就注释,别钻牛角尖,注释了,在看页面的变化,如下:[人家的写法真的很规范,在进入正式项目开发之前,我们在来讲怎么写vue3+ts
,照葫芦画瓢 你也能会
]
- 好吧,好像没啥变化,啊哈哈,但是我们可以控制渲染不渲染哪个路由了,打开
authMenuList.json
,路径src/assets/json/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
}
}
],
"msg": "成功"
}
- 蓝框里的都可以删除,留下
home
和login
!!!!
第六步:开始分析这个authMenuList.json
,为啥return
这个json文件
就可以渲染出来页面呢?
- 不讲了,9.00了先上班,下班在讲!
- 中午了,继续,上回说到要分析下这个
authMenuList.json
,先打开src/api/modules/login.ts
文件,找到getAuthMenuListApi
方法,复制它,全局搜索,找到调用它的地方,如下图:路径src/stores/modules/auth.ts
- 这里做了一个事情,就是把
authMenuList.json
中的data数据,赋值给了 state
中的authMenuList
//菜单权限列表。
- 同时,
getters
触发,代码如下:
// 菜单权限列表 ==> 扁平化之后的一维数组菜单,主要用来添加动态路由
flatMenuListGet: state => getFlatMenuList(state.authMenuList),
- 可以看到,这个方法做了一件事,就是把
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": "成功"
}
- 然后我们在来看
flatMenuListGet
数组,利用vue-devtool插件可以看pinia
内部数据。
- 这样的
[1,2,3,4,5]
就是一维数组,这样就是二维数组[[1,2],[3,4],5]
,数组扁平化就是把二维或者多维数组转成一维数组,明白了吧
- 好了,跑题了。
- 继续
- 在
src/stores/modules/auth.ts
找到getAuthMenuList
方法,在按照同样的方法,全局检索下,哪里调用了这个方法,路径src/routers/modules/dynamicRouter.ts
,
- 原来是
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
-
上篇我们学完了对整个登录逻辑
的剖析,这一篇我们来细细讲下,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项目里是你自己配置的,你应该很熟悉下面这段代码
-
createProxy(viteEnv.VITE_PROXY)
传入的一个实参,顺藤摸瓜,找到本地环境配置文件.env.development
里的VITE_PROXY
- 全局搜索
VITE_API_URL
,打开src/api/index.ts
,这个文件是模版封装好的axios请求插件,代码不用动,就修改下该修改的地方,比如:
config.headers.set("x-access-token", userStore.token);
- 这里都
x-access-token
修改成你们后端所接收的token key
-
data.code == ResultEnum.OVERDUE
和data.code && data.code !== ResultEnum.SUCCESS
这里的code值,修改成你们项目实际返回的对应的code,模版封装统一管理起来了,很方便,路径是src/enums/httpEnum.ts
- 这里我就不动了。
-
接下来,就打开src/api/modules/login.ts
文件,我们来看下,登录接口是如何封装的。
- 首先引入了
import { Login } from "@/api/interface/index";
接口,导入了Login
接口,里面规定了某些字段,多传或者不传或者少传,都会报错提醒,这就是TS,严格校验规范了开发规范,故而开发周期可能会久一点(相比vue+js项目来说)。
-
Login.ReqLoginForm
规范了传入参数字段,必须是username
和password
,而且比如是string
类型,否则报错!
-
Login.ResLogin
规范了,接口返回数据字段,比如有access_token
,且必须是string
类型
- 换句话说,
loginApi = (params: Login.ReqLoginForm)
括弧里的是规范传入参数的
-
http.post<Login.ResLogin>
请求方式后面的尖括号是规范返回字段的。
-
import { PORT1 } from "@/api/config/servicePort";
这个是引入了一个请求接口前缀
- 没啥好说的,就是个拼接接口地址用的,根据实际情况使用
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
方法,就直接复制它,稍做修改请求方式和传参接口,以及返回接口字段规范。就可以了。
-
*后面在切换真实接口的时候,在演示如何新建方法和和接口*
。
下篇再讲对接真实登录接口,累了,想睡觉了。
- 上篇讲了模版中的反向代理以及api方法是如何定义的。
- 下面来讲下,如何着手开发项目,我们就拿
对接真实登录接口
来讲吧
- 登录接口地址:
https://demo.it98k.cn/admin/api/login
- 要求传入参数:
loginame
和password
- 接口返回的格式为:
{
"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
字段
- 报错信息提示的很到位,那就去改下
- 打开
src/api/interface/index.ts
文件,找到ReqLoginForm
export interface ReqLoginForm {
username: string;
password: string;
}
export interface ReqLoginForm {
loginame: string;
password: string;
}
- 这种飘蓝的,不用管也行,没有任何影响,就是单词不规范。如果你不舒服,就点击快速修复
- 接下来,输入账号密码先试试
- 输入账号
admin
,密码123456
,当然,我是随便输入的,目的就是先检测下,有没有报错,方便再去修改别的,不出意外,果然报错了
- 第一点,就是错误信息,没弹出来,第2点就是密码后端接收没要求加密,需要去掉加密
- 先去掉加密
- 把md5干掉就行了
- 保存项目,会报错,记住 vue3+TS项目,文件中
不允许存在未使用的方法或者插件
,直接去注释掉这一行就行了,或者删掉,就没报错了
-
message
应该出现在箭头所指的位置的,【没出现就是因为可能字段没取对
】
- 打开文件去排查
src/api/index.ts
【你要问我为啥我知道是打开这个文件,因为,这个文件内封装了接口响应拦截,全局拦截了错误信息,所以要在这个文件里找到响应拦截器咯】
- 一看就明白了,代码里取的是
msg
!,接口返回的是message
,改下就可以了
- 那接下来,输入正确的账号密码
admin
和14551
,接口请求成功但是控制台报错了
- 原因就是
无法读取未定义的属性(正在读取“access_token”)
,说白了,就是后端接口压根没返回access_token
这个字段,所以时时刻刻要根据你的项目接口来改造模版
- 这里可以点进去,直接到报错位置
- 再看接口返回,就知道了,接口返回数据结构里,没有data,解构不出来,但是可以解构出来
token
// 1.执行登录接口
const { token } = await loginApi({ ...loginForm, password: loginForm.password });
userStore.setToken(token);
- 还是TS强类型校验哈哈,别嫌烦哦,烦就别用TS!
- 你要是想问,在哪里规范了接口返回字段的
- 在这里
export interface ResLogin {
token: string;
}
- 但是,还报错,是因为这里进行了
双重校验
,说到底,还是我接口数据接口返回不规范,接口都要包含data
的,如果你们的接口都有返data,你就不会遇到我这种问题
- 针对我这个不规范的接口,改一下吧
- 全局搜
ResultData
,打开src/api/index.ts
- 在模版自己封装的常用请求方法,规范了
ResultData
,点过去可以看到
- 请求响应参数(包含data)
- 要么一刀切,不校验这个了,要么就修改接口返回数据结构,我这里选择一刀切
- 去掉
ResultData
,但是这样会违反使用TS开发的初衷,如果你也存在这种情况,建议去找后端修改数据结构
- 这样就不飘红了
- 可以看到,接口请求成功了,代码也往下执行了,说明我们的登录接口对接成功了,下面的报错是接口404,不存在
- 上篇讲完了如何对接真实的登录接口,并且已经调试成功,并且继续往下执行,调用了一个404的接口
- 接下来再继续往下讲
- 如果你不知道在哪调用这个方法,就直接复制接口地址
auth/buttons
,全局搜索一下
- 然后再全局检索一下
getAuthButtonListApi
这个方法是在哪里调用的
- 然后再全局检索
getAuthButtonList
方法
- 我们一层一层的找,终于是找到了在哪里调用的,当然这是最笨的方法,如果再第一课,你已经掌握了,你就会知道,这个方法在哪里调用,直接就可以打开了,不用这么麻烦。
- 其实我们这个项目实现按钮权限不用这个方式,这个方法可以注释掉或者删除掉。
- 嘿,进去了~
- 但是,要想实现动态路由、权限管理,还需要在多请求一个接口
- 如果你之前学过我的vue2实现的动态路由权限管理,就会知道
- 我们在登录接口后,立马执行了一个
getInfo
获取用户信息的接口
- 而这个项目没有,所以要补上他
- 下面我们就要简单学下
pinia
状态管理器怎么用了,getInfo
这个方法就是要写在pinia
里的