一、基础环境配置
1.vue-element-admin后台集成方案
vue-element-admin 是一个后台集成方案, 集成了PC项目中很多的业务场景和功能
2.搭建项目前的一些基本准备
(1)nodejs环境
查看node 和 npm的版本
$ node -v #查看node版本
$ npm -v #查看npm版本
(2)git版本控制
查看git安装版本
$ git --version #查看git安装版本
(3)npm淘宝镜像
$ npm config set registry https://registry.npm.taobao.org/ #设置淘宝镜像地址
$ npm config get registry #查看镜像地址
(4)vscode编辑器
vscode编辑器插件 + vetur + eslint
3.项目模板启动
(1)git拉取基础项目模板
$ git clone https://github.com/PanJiaChen/vue-admin-template.git hrsaas #拉取基础模板到hrsaas目录
(2)安装项目依赖
$ npm install #安装依赖
4.目录结构
├── build # 构建相关
├── mock # 项目mock 模拟数据
├── public # 静态资源
│ │── favicon.ico # favicon图标
│ └── index.html # html模板
├── src # 源代码
│ ├── api # 所有请求
│ ├── assets # 主题 字体等静态资源
│ ├── components # 全局公用组件
│ ├── icons # 项目所有 svg icons
│ ├── layout # 全局 layout
│ ├── router # 路由
│ ├── store # 全局 store管理
│ ├── styles # 全局样式
│ ├── utils # 全局公用方法
│ ├── vendor # 公用vendor
│ ├── views # views 所有页面
│ ├── App.vue # 入口页面
│ ├── main.js # 入口文件 加载组件 初始化等
│ └── permission.js # 权限管理
│ └── settings.js # 配置文件
├── tests # 测试
├── .env.xxx # 环境变量配置
├── .eslintrc.js # eslint 配置项
├── .babelrc # babel-loader 配置
├── .travis.yml # 自动化CI配置
├── vue.config.js # vue-cli 配置
├── postcss.config.js # postcss 配置
└── package.json # package.json
5.项目运行机制
(1)main.js
(2)App.vue
(3)Vuex结构
(4)scss
该项目使用了scss作为css的扩展语言
(5)icon
6.API模块和请求封装模块介绍
(1)Axios的拦截器介绍
拦截器的主要原理如下
7.建立远程Git仓库并完成初始提交
$ git init #初始化项目
$ git add . #将修改添加到暂存
$ git commit -m '人资项目初始化' #将暂存提到本地仓库
$ git log #查看版本日志
$ git remote add origin <远程仓库地址> #添加远程仓库地址
$ git remote -v #查看本地仓库的远程仓库地址映射
$ git push -u origin master #将master分支推送到origin所代表的远程仓库地址
二、登录模块
1.表单校验
2.Vue-Cli配置跨域代理
(1)开发环境的跨域
开发环境的跨域,也就是在vue-cli脚手架环境下开发启动服务时,我们访问接口所遇到的跨域问题,vue-cli为我们在本地**开启了一个服务
,可以通过这个服务帮我们代理请求
**,解决跨域问题
这就是vue-cli配置webpack的反向代理
vue-cli的配置文件即**vue.config.js
**
(2)配置反向代理
proxy: {
'/api': {
// vue-cli反向代理配置
target: 'http://ihrm-java.itheima.net/',
changeOrigin: true
}
}
3.封装单独的登录接口
export function login(data) {
return request({
url: '/sys/login',
method: 'POST',
data
})
}
4.封装Vuex的登录Action并处理token
import { getToken, setToken } from '@/utils/auth'
import { login } from '@/api/user'
// 状态
const state = {
token: getToken()// 设置token初始状态 token持久化=》放入缓存中
}
// 修改状态
const mutations = {
// 设置token
setToken(state, token) {
state.token = token // 设置token
setToken(token)
}
}
// 执行异步
const actions = {
// 定义login action 也需要参数 调用action时 传递过来的参数
async login(context, data) {
const result = await login(data)
// 登录成功 用户名和密码验证通过,响应拦截器中已经做了判断
// 把登录成功的用户token存储到vuex
context.commit('setToken', result)
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
5.处理axios响应拦截器
import axios from 'axios'// 引入axios
import { Message } from 'element-ui'
const service = axios.create({
// 如果执行 npm run dev 值为 /api正确 /api 这个代理只是给开发环境配置的代理
// 如果执行 npm run build 值为 /prod-api 生产环境
baseURL: process.env.VUE_APP_BASE_API, // 设置axios请求的基地址
timeout: 5000// 设置超时时间
})// 创建axios实例
// 请求拦截器
service.interceptors.request.use()
// 响应拦截器
service.interceptors.response.use(response => {
const { success, message, data } = response.data
// 要根据success的成功与否决定下面操作
if (success) {
// 成功,返回data
return data
} else {
// 走到这里业务已经错误了
Message.error(message)// 提示错误信息
return Promise.reject(message)
}
}, error => {
Message.error(error.message)// 提示错误信息
})
export default service// 导出axios实例
6.登录页面调用登录action
handleLogin() {
this.$refs.loginForm.validate(async isOk => {
if (isOk) {
try {
this.loading = true
await this['user/login'](this.loginForm)
this.$router.push('/')
} catch (error) {
console.log(error)
} finally {
this.loading = false
}
}
})
7.主页的token拦截处理
我们已经完成了登录的过程,并且存储了token,但是此时主页并没有因为token的有无而被控制访问权限
(1)权限拦截处理流程图
(2)流程图转化代码
在**src/permission.js
**下做权限拦截处理
// 配置路由守卫页面
import router from '@/router' // 引入路由实例
import store from '@/store' // 引入vuex store实例
import nprogress from 'nprogress' // 引入进度条插件
import 'nprogress/nprogress.css' // 引入进度条样式
const whiteList = ['/login', '/404'] // 定义白名单 不受权限控制的页面
// 路由的前置守卫
router.beforeEach((to, from, next) => {
nprogress.start()// 开启进度条
// 首先判断有没有token
if (store.getters.token) {
//是否是登录页
if (to.path === '/login') {
next('/')// 是||跳到主页
} else {
next() // 不是||放行
}
} else {
if (whiteList.indexOf(to.path) > -1) {
//没有token查看是否属于我们定义的白名单
// 找到说明在白名单
next()//放行
} else {
next('/login') // 跳到登录页
}
}
nprogress.done()//代码执行完毕手动关闭一次进度条
})
// 后置守卫
router.afterEach(() => {
nprogress.done() // 关闭进度条
})
三、主页模块
1.访问权限拦截
在用户点击登录操作后,利用路由守卫对用户的访问做拦截,判断处理后执行后面的操作,在**src/permission.js
**中处理权限拦截操作,具体操作流程图如下:
相应的代码如下:
**src/permission.js**
// 配置路由守卫页面
import router from '@/router' // 引入路由实例
import store from '@/store' // 引入vuex store实例
import nprogress from 'nprogress' // 引入进度条插件
import 'nprogress/nprogress.css' // 引入进度条样式
const whiteList = ['/login', '/404'] // 定义白名单 不受权限控制的页面
// 路由的前置守卫
router.beforeEach(async(to, from, next) => {
nprogress.start()// 开启进度条
// 首先判断有没有token
if (store.getters.token) {
// 有token,判断是否在登录页
if (to.path === '/login') {
next('/')// 是||跳到主页
} else {
// 获取用户资料
// 首先应该判断之前是否获取过资料了
if (!store.state.user.userInfo.userId) {
// 如果没有获取过||获取资料
await store.dispatch('user/getUserInfo')// 这里加await是要获取完资料才能放行
}
// 已经获取过资料,放行
next()
}
} else {
// 没有token,判断是否在不受权限控制的白名单
if (whiteList.indexOf(to.path) > -1) {
// 找到说明在白名单,放行
next()
} else {
next('/login') // 跳到登录页
}
}
nprogress.done()
})
// 后置守卫
router.afterEach(() => {
nprogress.done() // 关闭进度条
})
2.token注入和token失效的主动介入
项目中大多数请求都需要token,请求headers中的Authorization相当于我们开门(调用接口)时**钥匙(token)
,我们在打开任何带安全权限的门的时候都需要钥匙(token)
,每次在接口中携带钥匙(token)
**很麻烦,所以我们可以在axios请求拦截器中统一注入token ,这样每次请求都会携带token。开门的钥匙不是一直有效的,如果一直有效,会有安全风险,所以我们尝试在客户端进行一下token的时间检查
首先在登录action中存入时间戳
在请求拦截器中注入token
// 请求拦截器
service.interceptors.request.use(config => {
// 先判断vuex中有没有token,没有说明在登录页面,注入token会导致登录请求错误
if (store.getters.token) {
// 为token设置一个时间戳,用于判断token超时
if (isCheckTimeOut()) {
store.dispatch('user/logout')// 退出登录
router.push('/login')// 跳转到登录页面
return Promise.reject(new Error('登录信息超时,请重新登录'))
} else {
// 有token,把token注入到该拦截器中
config.headers.Authorization = `Bearer ${store.getters.token}`
}
}
return config // 无论拦截成功还是失败,必须返回config
}, error => {
// error信息 里面response对象
if (error.response && error.response.data && error.response.data.code === 10002) {
// 当等于10002时表示后端告诉我token超时了
store.dispatch('user/logout') // 退出登录
router.push('/login')
}
return Promise.reject(error)
})