关于vue-element-admin
前言:
最近在开发一个后台管理系统,是基于vue-element-admin进行开发的,在开发过程中感受到该框架的方便同时,也感受到了面对框架所带来的的一些局限性和依赖性。某些问题导致我在开发过程中耽误了很多时间,幸好最后都一一得到解决,所以希望写下这篇文章来进行记录,以便后来的使用。
介绍Vue-element-admin
这是一个集成大部分功能的后台管理系统框架,也就是说大多数的组件都帮我们写好了,他是基于vue-element进行开发布局的。
所以我们使用vue-element-admin的时候可以直接使用vue-element.。
因为工作时间的原因,导致一些问题可能遗漏,
下面将比较分散的述说这些框架的一些使用方法和一些在项目中遇到的坑点。
1、mock和请求
在过去,我使用过easy-mock来模拟数据,刚开始听说这个框架也有模拟数据的功能,而且是基于mock的。
其实他和easy-mock的使用差不多,不过这个框架可以直接在本地文件中模拟,不需要进行线上模拟。
例如:






本地的mock请求就是这样运行的。
它会结合我们在.env.development来判断我们是请求模拟的数据还是线上的数据。
那么如果想切换到线上请求?
刚接触这个框架,甚至有些不知道如何切换到线上请求,其实他这边是设计好的,只要我们在.env.development文件中设置我们要请求的API即可
他有两个环境,一个是开发环境和线上环境
例如:

我们可以在不同的环境设置不同的请求地址,因为dev是我们开发的时候使用的。
pro是我们打包的候使用的,所以是不会影响的。
走到这一步,也就是说VUE_APP_BASE_API保存的使我们请求的基础路径
接下来会说一下这个框架的请求封装。讲完了框架的请求机制之后,我将讲述请求具体接口的方法(和本地请求基本一致)
request.js
这个文件是该框架的请求封装的文件,也就是说我们所有的请求都是基于这个文件的。
首先,他的请求方法是使用axios.
其次,他每次的请求都会检测有没有token,如果有token就会携带token去请求,这样做会有一个很方便的点,那就是后台管理系统,本来就是需要验证身份的,也就是说我们后面的请求基本都会使用到token,这里他已经帮我们做好了,所以就不需要每次请求的时候手动添加一个token传到后台。
另外,
问题一 Status Code的问题
这个请求封装有一个问题,那就是Status Code的问题。(在Network的header里面)。它和res.code有本质的区别。

什么意思呢?
如果后台的status Code返回的不是200,那么他的请求会直接当成错误来使用,也就是说会直接跳到error中。他无法获取后台返回的信息。
因为用户可能输错信息了,我们需要提醒他,我们需要把后台的信息打印出来给用户去看,这种情况就没办法实现了,因为该框架会把不是200的status Code值当成报错
所以,我们要和后台沟通,不要返回status Code为其他的值,那如果是错误信息(例如res.code == 500),我们应该如何处理。
在response里面处理。
注意:这里指的错误信息,不是调用接口发生错误,而是返回的res.code不是正常的200.
如果调用接口失败,他还是会跳到error中的


如果只是用户输入不正确的信息,后台希望返回一些信息给用户看,我们还是要后台返回status Code为200,再去判断res.code是什么状态,再去给用户提供什么信息。

问题二 请求发送的参数格式问题
在初次使用该框架进行请求的时候,发现一个问题,就是请求后台的时候,参数没错了,接口没错的情况下,后台接受不到前端的数据。
这是我遇到的一大坑点。后来发现是格式不对,现在显示的是处理后的格式。

当时是引用了qs模块来处理了发送的格式。


补充:
我们可以不通过qs模块去处理格式。因为这样只能针对一种数据格式。假如后台需要其他格式,例如FormData,这样全局的写在请求上就会完蛋。
所以,我们可以通过在单独的请求中处理每一种不同的格式。
第一种:假如后台需要将参数以json格式写入
我们只需要将要传的参数写成对象就可以。但是接口要直接写成data


第二种:假如后台需要将参数以FromData格式写入
接口要写成params:data


第三种,文件下载类型的

把这些请求的坑点都几乎过一遍之后,下面就来说说我们请求的步骤:
1、创建好一个js文件,设置好请求的参数,url是请求的地址,他会自动拼接到基础路径上,method是请求方式,params是携带的参数
有三种写法:params:data我们可以携带大量的参数 params:{id} 我们传过去的就是id这个参数,第三种就是不写params,直接写data,这样的话他会把数据直接放到{}中,后台有时候需要这样的x-www格式

2、在页面中引入文件,使用在util/require 中定义好的请求函数名

3、直接使用,这里我们使用了async,其实可以不使用,就像普通函数一样使用userList即可
这里getList只是一个普通函数,userList才是请求函数

2、登录问题
关于登录,我也遇到了不少的坑
先说说这个框架的一个登录流程
他进行登录的时候,首先会调用登录接口获取到token值,只是获取token值。
然后再根据token去获取用户的信息。
他会从用户信息中获取一个角色值,来判定这个角色拥有哪些路由权限,也就是这个用户可以看到什么页面是有这个角色值来做决定的。
问题一:后台没有返回role(角色值),遭到路由拦截
我当初调用后台给我的登录接口,发现无论如何都无法登录,因为后台只返回了我一个token值。
该框架做了一个路由拦截,每当他从登录页想跳到首页的时候,他都会实施一个路由拦截。
判断你有没有token值,

如果有那么就会判断你有没有角色role值,如果没有,他就会调用请求用户的接口。去获取用户的信息,其中role值应该返回在这个接口里面。如果有了角色值,他才可以根据这个角色来帮你配置路由和跳转页面。

所以从登录页跳转到首页,必须有角色值
因为我们后台并不会返回角色值给我,所以之前我只能把角色值写死。当然,后面直接获取后台菜单就改了这里的判断
我说明一下这种情况是出自什么原因。
该框架是基于前端来控制权限的,也就是前端根据后台返回的角色值是什么来为用户显示怎样的侧边栏(路由),前端就是根据这个role和动态路由对象中的meta中的role来进行匹配。如果有这个角色则显示。这是该框架对于路由的做法。

然而我的需求有些不一样,后台是不会返回角色值给我的,我们的路由也不能写死,后台会把整个路由发送给我,我进行动态添加,也就是说后台已经处理了角色权限的问题,他返回的是该用户可以看到的路由,我只需要添加就可以。

但是,问题来了,怎么添加,而且这种格式和我们合法的路由格式不太一样。所以就要做一些另类的判断。

我们会发现在登录的时候,其实这个框架也做了动态添加路由的操作,router.addRoutes。
所以我们只要改造modules permission.js文件就可以得到我们的路由,
obj,是我们获取用户信息的时候,后台返回给我们的数据
通过整理obj来获取合法的路由格式



获取正确的路由格式的判断
我在判断那里datas.componet===的都是我在前端设置好的父级菜单,因为这个框架的父级菜单的component都为layout组件,也就是就算点击也不会跳页面的,他是这个框架的总体框架,真正显示内容的是子菜单。



最终成功动态添加路由。
但是还是遇到不少坑,就是相同方法的情况下,返回错误。原因是component是需要和我们前端这边配置好的,如果前端没有这个组件,后台那边添加了一个数据里面的组件是我们没有的,那么就会出现报错。
第二个坑就是,其实我们把后台的返回的路由转为我们前端可用的路由的难点就在于componet对象这里。compoent后台返回的是一个字符串,我们要转成一个compoent对象,要用到_import.js文件。
3、请求时间的问题
该框架在每次请求的时候都做了一个时间的限制,也就是说超过了这个时间,无论接口返回什么信息都会以请求时长过久而取消请求。

当然,这里的初始时长只有5000,也就是5秒,我这里设置了2分钟。
但是还有一个问题,就是这个请求是全局的,也就是说所有请求的请求时长限制都是一样的,现在有这样一种情况,
我们希望普通的请求的请求时长限制短一点。
某些特殊的请求请求时长限制长一点。(例如:上传图片,上传视频,生成,这种非常不确定性的请求)
解决方法:
第一,另外写一个request.js 在 请求数据的时候引用这个request.js
第二,直接使用axios调用,不走这个框架封装的request.js
4、固钉的缓存

每次用户登录打开页面,如果有使用固钉的样式,那么页面将会显示在固钉上。但是有个很尴尬的情况,那就是当用户退出再登录的时候,固钉还存在,这本来是挺好的,但是如果同一个电脑,不同的用户登录呢,我们会发现第二个用户登录的时候依然可以看到第一个用户留下来的页面固钉。幸好如果第二个用户没有第一个用户的页面权限的时候,点击固钉会跳到404.
但是这种情况对用户的体验非常不好,而且一定程度上会带来一些安全性的问题,虽然问题不是很大。
一开始,我以为固钉的缓存是放到localStorger中,或者其他浏览器缓存上面,但后面发现并不是。他只是放到了VUEX里面,所以其实我们只要刷新页面,数据就会消失掉。
我们只要在退出登录的时候,把VUEX里面的数据清除掉就可以了。
补充:我们退出登录的时候,其实必要的话可以把重定向去掉
this.$router.push('/login)

固钉的用法还有一个:
当我们切换固钉时,会发现每一次切换固钉切换页面时,都会重新刷新页面,这个时候客户可能希望你缓存前面页面的数据,别人新增数据时可能希望切换到其他页面看一下数据,你给他刷新了,这样的体验效果不是那么好。这个框架规定一个缓存的法则,就是我们的页面可以设置一个name属性,他必须和我们路由的name一直,那么页面就会得到缓存。

必须和router.js中该页面路由的name属性一直
5、element的样式
在前面我们讲过,该框架是基于element开发的。其实他有全局设置了部分element的样式,例如按钮,固钉,菜单等。只要我们修改了他的全局样式就可以全局改变。

我们的按钮tyle的样式可以在这里修改,当然不局限按钮的样式

比较难修改的是菜单栏被选中时的背景颜色,因为element没有封装选中菜单栏时的背景颜色。
所以我们可以使用active来设置。因为菜单栏还有搜索后的样式,所以也要设置。
以下,只是部分呈现(不完全):

6、坑爹的Tree组件
在项目中大多数的页面都有一个Tree组件


但是这些数据一般都是通过后台返回给我们的。
而后台通常返回的数据不会这样的格式,而是一个扁平化的数据,我们要通过id,和pid来得到一个结构化的格式。

可以使用JSON-Tree插件来将他结构化。
https://github.com/aximario/json-tree

但是,在数据转化的时候,可能会我们发现根本不行,这个时候可以把最父级的数据的pid去掉,让后台设置。
第二个问题。
如果我们设置了这种多选的。

我在项目中碰到了一个需求,就是用户可以选择多个选项。但是这里非常的坑,假设,用户选了一级1,那么一级1的所有子项都会被选中。发送的时候,会把一级1和一级1下面的子项id全部发送给后台。
但是,如果我们没有选一级1,而只是选一级1下面的某一个子项,那么发送的时候其实只会把选中的子项id传过去。并不会把父级的id传过去。但是我们的接口通常是需要我们把父级id也传过去的,所以这就是一个坑点。幸好有了解决的方法。

坑点二
用户选了那些选项,后台通常都是以什么什么为true或者false来返回的,例如,一级1选中就返回true没有选中就会返回false. 但是这个组件非常坑,就是只要一级1有一个子项被选中了,他的父级都会以true的形式返回给后台,也就是说当我们回显的时候会出现一个问题,就算用户在一级1里面只选中了一个子项,他回显的时候也会把一级的所有项选中。
后面我做了一个判断,就是把这些选中的子项的父级都选出来去掉再显示。
因为tree组件有一个属性,可以控制默认显示的值,而且去掉父级也不会有关系,因为没有改变数据,只是改变默认显示的数据而已,更重要的是,这个tree组件会在子集全选的时候自动选中父级,所以这就有了思路。

7、按钮权限控制
在本次的项目中,我没有用框架的方式。
这次权限的控制实现非常简单。
首先,后台会返回一个列表,记录该用户所有的按钮权限,其实就是一个个字符串。
我们把他存到vueX里面。
我们在全局中定义一个方法。

把这个方法挂载到全局

然后在每个页面中为那些需要权限的按钮添加上v-if

虽然这样做很麻烦,但是要设置按钮级别的权限,我认为必须是每个按钮都要单独设置的。
以下是官方的写法。其实和我们的写法差不多,只不过他是使用角色值去判断的。
