一、项目技术
1、 技术栈
基于vue-admin-template 后台管理系统
二次开发
ES6, vue, vuex, vue-router, vue-cli, axios, element-ui
2、技术解决方案
- RBAC 权限设计(RBAC: 基于角色的访问控制权限的基本模型)
- 多语言
- Excel 导入导出
- 反向代理
- 请求模块封装
3、页面截图
二、环境搭建
1、 从远程仓库克隆项目到本地
// 从码云拉取代码
git clone https://gitee.com/panjiachen/vue-admin-template.git
克隆之后,首先查看项目文件夹,了解各个文件的作用
2、启动项目
每个项目文件夹下,有个package.json里的scripts,记录了只有当前文件夹下才能使用的npm run 自定义命令
我们的是dev
npm run dev
注意:① 一定要看好终端要在package.json所在文件夹(项目根目录文件)
② 启动项目的时候注意是否有第三方依赖包(node_modules)
如果没有就npm i 先下载(npm i 的意思是:根据当前命令所在的文件夹下的package.json中记录的包名和版本,帮助我们全部下载到node_mudules内支撑我们整个项目的正常运行)
// 建议不要用 cnpm 安装 会有各种诡异的bug 可以通过如下操作解决 npm 下载速度慢的问题
npm install --registry=https://registry.npm.taobao.org
启动成功截图
3、解读各个文件代码作用
(看一个文件找相关联的文件,不要看的太深,最多看两层)
文件名 | 作用 |
---|---|
main.js | 入口文件,挂载路由,挂载vue-store、全局注册Element、App.vue根组件 |
api 文件 | 将所有的网络请求方法放在 api 目录下统一管理,按照模块来划分对应的文件 |
assets 文件 | 存放项目的静态图片 |
components 文件 | 封装的全局组件,可以等到用的时候再去分析 |
Icons 文件 | 字体图标 |
layout | 是项目登录以后, 见到的布局的框, 左侧导航, 上部导航和下部的内容部分 |
router/index.js | 路由相关的配置, 以及路由规则对象数组 |
store | vuex结构 |
styles | 样式文件 |
utils | 整个项目用到的工具, 封装起来集中管理, 逻辑页面需要的时候进行调用 |
permission.js | 控制路由权限判断、登录和非登录的相关设置 |
settings.js | 封装了全局的一些变量, 方便一键修改 |
4、公共资源图片和统一样式
将common文件夹的两个文件放到到styles目录下,然后在**index.scss
**中引入该样式
5、建立远程Git仓库并完成页面初始提交
详细步骤:https://blog.csdn.net/Vest_er/article/details/127508844
三、登录模块
登录界面实现效果如图
1、设置固定的本地访问端口和网站名称
在正式开发业务之前,先将项目的本地端口和网站名称进行一下调整
详细步骤:https://blog.csdn.net/Vest_er/article/details/127518687
2、登录页面的基础布局
① 设置头部背景
<div class="title-container">
<h3 class="title">
<img src="@/assets/common/login-logo.png" alt="">
</h3>
</div>
② 设置背景图片
如需要在样式表中使用@
别名的时候,需要在@前面加上一个~
符号,否则不识别
/* reset element-ui css */
.login-container {
background-image: url('~@/assets/common/login.jpg'); // 设置背景图片
background-position: center; // 将图片位置设置为充满整个屏幕
}
③ 设置手机号和密码的字体颜色
$light_gray: #68b0fe; // 将输入框颜色改成蓝色
④ 设置输入表单整体背景色
.el-form-item {
border: 1px solid rgba(255, 255, 255, 0.1);
background: rgba(255, 255, 255, 0.7); // 输入登录表单的背景色
border-radius: 5px;
color: #454545;
}
⑤ 设置错误信息的颜色
.el-form-item__error {
color: #fff
}
⑥ 设置登录按钮的样式
.loginBtn {
background: #407ffe;
height: 64px;
line-height: 32px;
font-size: 24px;
}
⑦ 修改显示的提示文本和登录文本
<div class="tips">
<span style="margin-right:20px;">账号: 13800000002</span>
<span> 密码: 123456</span>
</div>
3、登录表单的校验
目标:对登录表单进行规则校验
① 表单校验的先决条件
3个条件缺一不可(loginRules:trigger校验的触发方式 ,validator自定义函数)
② 手机号和密码的校验
字段名对应
将username改为mobile,实际接口中采用的是mobile
的字段,为了更方便的写代码,所以我们将username
改成mobile
校验手机号和校验密码
新规则:手机号必填,并且进行格式校验,密码必填,长度6-16位之间
data() {
const validateMobile = (rule, value, callback) => {
// 校验成功 callback()
validMobile(value) ? callback() : callback(new Error('请输入正确的手机号'))
}
const validatePassword = (rule, value, callback) => {
if (value.length < 6) {
callback(new Error('密码长度为6-16位'))
} else {
callback()
}
}
return {
loginForm: {
mobile: '13800000002',
password: '123456'
},
loginRules: {
// trigger校验的触发方式 ,validator自定义函数
mobile: [{ required: true, trigger: 'blur', message: '手机号不能为空' }, { validator: validateMobile, trigger: 'blur' }],
password: [{ required: true, trigger: 'blur', message: '密码长度为6-16位' }, { validator: validatePassword, trigger: 'blur', min: 6, max: 16 }]
},
loading: false,
passwordType: 'password',
redirect: undefined
}
},
我们在utils/validate.js
方法中增加了一个校验手机号的方法
/**
* 校验手机号
* **/
export function validMobile(str) {
return /^1[3-9]\d{9}$/.test(str) // 校验手机号
}
utils/validate.js
是一个专门存放校验工具方法的文件
关于修饰符
@keyup.enter.native
表示监听组件的原生事件,比如 keyup就是于input的原生事件,这里写native表示keyup是一个原生事件
③ Vue-Cli脚手架配置跨域代理
目的:解决浏览器跨域问题
详细讲解:vue脚手架配置代理
devServer: {
Proxy: {
'/api': { // 匹配所有以'/api'开头的请求路径
target: 'http://ihrm-java.itheima.net', // 代理目标的基本路径
changeOrigin: true // 伪装路径
}
},
}
④ 封装单独的登录接口
export const login = (data) => request({ url: "/sys/login", method: "POST",data })
⑤ 封装Vuex的登录Action并处理token
vuex详解:Vuex安装使用详解及案例练习(彻底搞懂vuex)
在utils/auth.js
中,基础模板已经为我们提供了获取token
,设置token
,删除token
的方法,可以直接使用
token不能每次都通过登录获取,我们可以将token放置到本地的缓存中
import { getToken, setToken, removeToken } from '@/utils/auth'
// 状态
// 初始化的时候从缓存中读取状态 并赋值到初始化的状态上
// Vuex的持久化 如何实现 ? Vuex和前端缓存相结合
const state = {
token: getToken() // 设置token初始状态 token持久化 => 放到缓存中
}
// 修改状态
const mutations = {
// 设置token
setToken(state, token) {
state.token = token // 设置token 只是修改state的数据 123 =》 1234
// vuex变化 => 缓存数据
setToken(token) // vuex和 缓存数据的同步
},
// 删除缓存
removeToken(state) {
state.token = null // 删除vuex的token
removeToken() // 先清除 vuex 再清除缓存 vuex和 缓存数据的同步
}
}
const state = {
token: getToken()
}
⑥ request中环境变量和异常的处理
环境变量
在request中设置baseUrl
const service = axios.create({
// 如果执行 npm run dev 值为 /api 正确 /api 这个代理只是给开发环境配置的代理
// 如果执行 npm run build 值为 /prod-api 没关系 运维应该在上线的时候 给你配置上 /prod-api的代理
baseURL: "/api", // 设置axios请求的基础的基础地址
timeout: 5000 // 定义5秒超时
}) // 创建一个axios的实例
处理axios的响应拦截器
// 响应拦截器
service.interceptors.response.use(response => {
// axios默认加了一层data
const { success, message, data } = response.data
// 要根据success的成功与否决定下面的操作
if (success) {
return data
} else {
// 业务已经错误了 还能进then ? 不能 ! 应该进catch
Message.error(message) // 提示错误消息
return Promise.reject(new Error(message))
}
}, error => {
Message.error(error.message) // 提示错误信息
return Promise.reject(error) // 返回执行错误 让当前的执行链跳出成功 直接进入 catch
})
⑦ 登录页面调用登录action,处理异常
引入actions辅助函数
import { mapActions } from 'vuex' // 引入vuex的辅助函数
引入action方法
我们调用的是Vuex中子模块的action,该模块我们进行了namespaced: true,所以引用aciton时需要带上user/
, 并且在使用该方法时,直接使用 this['user/login']
, 使用this.user/login 语法是错误的
methods: {
...mapActions(['user/login'])
}
调用登录
this.$refs.loginForm.validate(async isOK => {
if (isOK) {
try {
this.loading = true
// 只有校验通过了 我们才去调用action
await this['user/login'](this.loginForm)
// 应该登录成功之后
// async标记的函数实际上一个promise对象
// await下面的代码 都是成功执行的代码
this.$router.push('/')
} catch (error) {
console.log(error)
} finally {
// 不论执行try 还是catch 都去关闭转圈
this.loading = false
}
}
})
四、主页模块
1、主页的token拦截处理
① 权限拦截的流程图
② 流程图转化代码
src/permission.js
是专门处理路由权限的
// 权限拦截 导航守卫 路由守卫 router
import router from "@/router"; // 引入路由实例
import store from "@/store"; // 引入vuex store实例
import NProgress from "nprogress"; // 引入一份进度条插件
import "nprogress/nprogress.css"; // 引入进度条样式
router.beforeEach((to, from, next) => {
NProgress.start(); // 开启进度条
const whileName = ["/login", "/404"];
if (store.getters.token) {
if (to.path == "/login") {
next("/");
} else {
next();
}
} else {
if (whileName.indexOf(to.path) > -1) {
next();
} else {
next();
}
}
NProgress.done();
});
router.afterEach(function () {
NProgress.done(); // 关闭进度条
});
2、主页的左侧导航样式
① 主页布局架构
② 最终效果
3、获取用户资料接口和token注入
① 获取用户资料接口
// 获取信息
export const getInfo = (data) => {
request({ url: "/sys/profile", method: "POST", data });
};
② 统一注入token`src/utils/request.js
service.interceptors.request.use(config => {
// 在这个位置需要统一的去注入token
if (store.getters.token) {
// 如果token存在 注入token
config.headers['Authorization'] = `Bearer ${store.getters.token}`
}
return config // 必须返回配置
}, error => {
return Promise.reject(error)
})
4、封装获取用户资料的action并共享用户状态
目标: 在用户的vuex模块中封装获取用户资料的action,并设置相关状态
userInfo为什么我们不设置为null,而是设置为 {}
因为我们会在**getters
**中引用userinfo的变量,如果设置为null,则会引起异常和报错
5、权限拦截处调用获取资料action
① 权限拦截器调用action
用户资料有个硬性要求,**
必须有token
**才可以获取,那么我们就可以在确定有token的位置去获取用户资料
② 获取头像接口合并数据