前言(很重要哦)
后台管理系统
,一般都是内部人员使用,所以对界面要求不高,一般都使用模板开发
。而前台系统
,是给游客使用,要凸显个性,所以一般自己开发,不使用模板
如果你想从头到尾写,可以参考我的前台系统,每个细节都会讲到(看完完全可以自己写个模板)
:@TODO 日后再写 这里是后台管理系统,我们使用模板"PanJiaChen的模板简洁版"
简洁版:https://github.com/PanJiaChen/vue-admin-template 加强版:https://github.com/PanJiaChen/vue-element-admin
如果你GitHub进不去
,可以到这里下载(码云)
:https://gitee.com/panjiachen/vue-admin-template.git
下载完成后,我们用它来当做项目,执行npm install --save 来下载相关依赖,然后通过npm run dev启动项目,最后浏览器查看项目
一、概念和技术总结(重点,一定要搞懂)
大家最起码需要混个眼熟,后面代码中用到,不至于不知道这是干什么的(现在好多人,会用,但不知道为什么这么用,那你怎么优化和调优,调错呢?
) 如果这里有你不会或没见过的东西(看了我的介绍不懂的),可以参考官方文档(说明你压根没学过Vue的相关知识,直接百度Vue官网就行)
1. vue
props :适用于父子组件通信的场景,有一个容易混淆的点,如下:
如果父组件利用props给子组件传递数据时,传递的是一个函数,其实就是子组件给父组件传递数据(因为调用的是父组件的函数)。 如果传递的不是函数,那么才是真正的父组件给子组件传递了数据(子组件使用传来的数据). 可以指定父组件传输的类型,{type:Array},也可以指定默认值{type:Array,default:[]},也可以什么都不指定,[‘todos’]
自定义事件 :适用于子组件给父组件传递数据, $ on、和$ emit全局事件总线$bus :适用于任何场景,Vue.prototype. $bus = this;pubsub-js发布和订阅 :适用于任何场景,但是Vue用的不多,一般React框架经常使用Vuex :适用于任何场景,用的非常多solt插槽 :使用于针对结构(标签)的父子组件通信,常用的有默认插槽,具名插槽和作用域插槽
2. vue-admin-template模板文件解析
文件目录解析(注意:以下所有文件夹和文件,你自己写效果也一样,并不是必须在这个模板文件中才能用)
build
mock
node_modules
p ublic
src
App. vue
main. js
permission. js
settings
. editorconfig
. env. development
. env. production
. env. staging
. eslintignore
. eslintrc. js
. gitignore
. travis. yml
babel. config. js
jest. config. js
LICENSE
jsconfig. json
package. json
postcss. config. js
vue. config. js
首先,将路由模块挂载到Vue,然后将所有路由配置封装到常量constantRoutes中,然后回调函数创建路由,挂载了常量constantRoutes,并将函数放置到常量createRouter,随后,将createRouter()这个函数常量,换了个简单的名字router。最后对外暴露router和一个重置路由的函数resetRouter() 这样做,我们可以直接通过$router操作和获取路由相关信息
jsconfig.json文件配置src别名为@解析
3个环境配置文件,可以通过webpack对外暴露的函数process获取,注意配置文件中内容,要以VUE_APP_*****开头(就和,java类名首字母要大写一样,大家要共同遵守)
我们看下开发环境的配置文件的配置内容,测试是否能正确获取 main.js文件中输出process和process.env,查看结果
console. log ( process)
console. log ( process. env)
这里封装了对于权限的功能函数
index.js文件,主要是掌控Vuex的大局,挂载到Vue getters.js文件,封装getters操作,这里统一使用lambda表达式(箭头函数),使用了上面配置的modules中的app和user中内容,例如state.app.*****
a => return a;
user.js,主要提供登录的state,setter(action和mutations),并暴露他们.另外app.js、settings.js文件和user.js差不多
3. 异步相关,前后端交互
4. 导航守卫
此文件主要控制路由跳转的业务,像一个导航一样,可以理解为路由的拦截器 此文件主要完成的功能是,拦截一个路由跳转,判断用户是否有token,如果有,判断是否跳转路由为/login(登录路径),如果是,不允许跳转,从定向到"/"首页。如果不是登录路径,尝试获取用户信息,获取不到,尝试重新获取,重新获取失败,删除token,让用户进入登录页面重新登录
5. 路由解析
记住一句话,所有路由都在找< router-view> ,所有子路由,都在找父路由组件中的< router-view>
整个路由进入首页后,为Layout组件,此组件分为3部分,sidebar导航栏,navbar顶部菜单栏,app-main内部二级路由,我们实际编写页面的地方
路由分析,当我们访问’/'它加载的是Layout组件,然后重定向到/dashboard,也就是二级路由,显示首页(显示到< router-view> 标签),其它的也是一样 Layout组件分析,一共有3个子组件 scrollbar navbar AppMain,可见< router-view> 标签在这个组件中,子路由会渲染到这里
二、单点登录实现
登录页面直接使用模板提供的即可,我们对其样式进行一些修改,以让你知道如何对其自定义
更换背景图片,也可以使用@
符号,只是需要加一个~
前缀
assets文件加放一张back.jpg图片 修改css样式
1. 后端接口和响应结果分析
验证码:请求captcha这个url返回一张图片 登录,Post请求,传输username,password,code3个参数,全部正确,登录成功,返回tokenHead和token,最终token规定格式为,tokenHead + “空格” + token,也就是Bearer eyJh…、成功状态码20000,失败状态码20001 当前登录用户信息,需要携带token,注意,token格式,以及header的name为authorization
2. 解决跨域,以及请求头.几乎所有项目都需要的配置,背下来吧。
1. 首先,我们知道环境配置文件中,有VUE_APP_BASE_API这个东西,我们通过这个来规定请求路径,我们可以规定它.改完记得重启项目(我们是开发环境,所以修改.env.development文件)
2. 然后我们通过修改webpack的配置文件vue.config.js,配置跨域代理,主要位置,别写错位置,同样,配置完记得重启项目
proxy: {
[ process. env. VUE_APP_BASE_API] : {
target: `http: / / localhost: 8001 `,
pathRewrite: {
[ '^' + process. env. VUE_APP_BASE_API] : ''
}
}
} ,
3. token请求头这种东西,直接配置到请求拦截器中就好了,request.js,注意token请求头的key必须为authorization(因为这是我们后端规定的,不是这个,后端没法正常解析)
service. interceptors. request. use (
config = > {
if ( store. getters. token) {
config. headers[ 'authorization' ] = getToken ( )
}
return config
} ,
error = > {
console. log ( error)
return Promise . reject ( error)
}
)
3. 验证码展示
后端直接返回一张图片给我们,我们可以通过window.URL.createObjectURL(response)来获取图片的一个url地址 window.URL.createObjectURL用于创建一个新的对象URL,该对象URL可以代表某一个指定的File对象或Blob对象
响应类型必须是blob或file两种格式(responseType:‘blob’ 或者 responseType:‘file’)
1. 首先,我们后端的验证码功能没有响应码,而我们使用的模板,在request.js中封装了请求拦截器和响应拦截器,致命的是,响应拦截器直接拦截所有响应码不是20000的响应
这样做的好处是,所有请求如果出错(响应码不是20000),统一进行错误逻辑,但这也同样是缺点。因为针对不同错误,我们希望有不同的反馈,而不是统一的一种 所以我们要丰富其功能(一个if就搞定),对于不需要code状态码的响应,如果有响应体,表示响应成功,否则表示响应失败
2. 封装api请求,注意一定要将响应类型设置为’blob’(responseType:‘blob’)
api/user.js添加验证码接口
export function captcha ( ) {
return request ( {
url: 'captcha' ,
method: 'get' ,
responseType: 'blob'
} )
}
3. login页面,login/index.js文件引入api,然后展示验证码
编写函数getImgCode,通过window.URL.createObjectURL(response)封装图片为url
async getImgCode ( ) {
await captcha ( ) . then ( response => {
this . codesrc = window. URL . createObjectURL ( response)
} )
} ,
添加组件和样式,指定函数回调(单击事件),以实现单击图片,重新生成验证码
< el- form- item prop= "code" class = "code" >
< span class = "svg-container" >
< ! -- < svg- icon icon- class = "el-icon-s-flag" / > -- >
< i class = "el-icon-edit" > < / i>
< / span>
< el- input
class = "code-input"
ref= "code"
type= "text"
v- model= "loginForm.code"
placeholder= "验证码"
name= "code"
tabindex= "2"
auto- complete= "on"
@keyup. enter. native= "handleLogin"
/ >
< / el- form- item>
< img : src= "codesrc" class = "code-img" @click= "getImgCode" >
. code{
width: 70 % ;
float: left;
}
. code- img{
float: right;
width: 29.6 % ;
}
4. 单点登录,退出登录,根据后端API实现
登录后,查看token 退出登录,查看token
1. api接口
api文件夹user.js文件,修改login,getInfo和logout三个接口
2. 路由组件编写登录函数逻辑
我们发现,此模板所有登录逻辑都在view/login/index.vue文件中的handleLogin方法中,对其进行修改
handleLogin ( ) {
this . $refs. loginForm. validate ( valid = > {
if ( valid) {
this . loading = true
this . $store. dispatch ( 'user/login' , this . loginForm) . then ( ( ) = > {
this . $router. push ( { path: this . redirect || '/' } )
this . loading = false
} ) . catch ( ( ) = > {
this . loading = false
this . getImgCode ( )
} )
} else {
console. log ( 'error submit!!' )
this . getImgCode ( )
return false
}
} )
}
3. 修改store(vuex)异步逻辑
所有操作都在store/user.js中,我们需要修改login和getInfo以及logout
async login ( { commit } , userInfo) {
const { username, password, code} = userInfo;
let result = await login ( { username: username. trim ( ) , password: password, code: code} ) ;
if ( result. code== 20000 ) {
const token = result. data. tokenHead+ ' ' + result. data. token;
commit ( "SET_TOKEN" , token) ;
setToken ( token)
} else {
return Promise . reject ( new Error ( result. message) ) ;
}
} ,
async getInfo ( { commit, state } ) {
let result = await getInfo ( state. token) ;
if ( result. code == = 20000 ) {
const { loginInfo } = result. data
if ( ! loginInfo) {
return reject ( '验证失败,请重新登录.' ) ;
}
const { username} = loginInfo;
commit ( 'SET_NAME' , username) ;
commit ( 'SET_AVATAR' , 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif' )
} else {
return Promise . reject ( new Error ( result. message) ) ;
}
} ,
async logout ( { commit, state } ) {
let result = await logout ( state. token) ;
if ( result. code == = 20000 ) {
removeToken ( )
resetRouter ( )
commit ( 'RESET_STATE' )
} else {
return Promise . reject ( new Error ( result. message) ) ;
}
} ,
5. 流程总结
this.$store.dispatch(‘user/login’, this.loginForm),调用login this.$router.push({ path: this.redirect || ‘/’ })//成功跳转路由,触发permission.js导航守卫 导航守卫,根据token会调用getInfo获取用户信息,获取不到,会删除token,重载路由到登录页面
三、后台管理系统实现