目录
一、后台管理
1.1、elmentui 安装与配置
Element UI 组件库 :
Element - The world's most popular Vue UI framework
elementui 组件库,此组件库只针对于 pc 端 ,一般用于 后台管理系统
npm i element-ui -S
"element-ui" : "^2.15.6"
npm install babel-plugin-component -D
"babel-plugin-component" : "^1.1.1"
将我们 根目录 下的 babel.config.js 修改如下 :
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
],
plugins: [
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]
]
}
然后在 main.js 文件内 导入我们刚刚创建好的 组件文件 element.js
配置 OK => 启动服务器 ( npm run serve )
下面让我们先来简单上手测试一下 :
先用简单的按钮来进行测试一下看是否成功 ,
1.2、后台登录
一个简易的登录页面 :
<template>
<div class="login">
<el-form
:model="ruleForm"
status-icon
:rules="rules"
ref="ruleForm"
label-width="100px"
class="demo-ruleForm"
>
<el-form-item label="账号" prop="username">
<el-input
type="text"
v-model="ruleForm.username"
autocomplete="off"
></el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input
type="text"
v-model="ruleForm.password"
autocomplete="off"
></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('ruleForm')"
>提交</el-button
>
<el-button @click="resetForm('ruleForm')">重置</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
data() {
return {
loading: false,
ruleForm: {
username: "",
password: "",
// checkPass: "",
},
rules: {
username: [
{ required: true, message: "账号不能为空" },
// 自定义验证规则实现规则定义
{ validator: this.validateUsername },
// {type:'string',regexp://}
],
password: [
{ required: true, message: "密码不能为空" },
{ validator: this.validatePassword },
],
},
};
},
methods: {
// 自定义验证规则 ( 最终要拆分出去 : 利用混入 )
validateUsername(rule, value, callback) {
if (/^\s+$/.test(value)) {
callback(new Error("空格不能当用户名"));
} else {
callback();
}
},
validatePassword(rule, value, callback) {
if (/^\s+$/.test(value)) {
callback(new Error("空格不能当密码"));
} else {
callback();
}
},
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
alert("submit!");
} else {
console.log("error submit!!");
return false;
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
},
},
};
</script>
<style lang="scss" scoped>
.login {
width: 500px;
margin: 100px auto;
border: 10px solid skyblue;
border-radius: 10px;
padding: 20px 30px 0 0;
}
</style>
1.2.1、全局混入实现自定义验证提取
1.2.2、局部混入实现自定义验证规则
1.2.3、登录按钮实现防抖处理
不断点击登陆按钮时 , 会一直触发提交功能 , 消耗浏览器性能 , 需进行防抖优化处理 :
利用 elementUI 组件库内的 : Message 消息提示 ( onClose 参数 )
1.3、后台登录接口编写
写后端接口所需下载的 第三方包 :
测试后端接口使用的工具插件 : Apizza
1.3.1、jwt
在之前的项目中,大部分网页是通过 session 来判断用户是否有权限来访问个别网页的,但是如果是 SPA 项目的话,前后端 分离,客户请求的是 前端 ,所以并不会保存数据到 session ,那该怎么办呢?针对这一问题,可以使用 jwt 来解决!
JWT 也就是 JSON Web Token( JWT ),是目前最流行的 跨域身份验证 解决方案 。
JWT 的组成 :一个 JWT 实际上就是一个 字符串 ,它由 三部分 组成 :头 部 ( Header ) 、载 荷 ( Payload ) 与 签名 ( signature ) 。生成一个 加密后的字符串 ,发送给 客户端 ,客户端保存在浏览器中 ( cookie / localstorage / sessionstorage ) , 在客户端请求敏感数据时,需要必须让客户端在 头部 ( Header ) 携带 加密 的字符串 ( token ) 给服务器,服务器获取到 token 值后,进行 解密 ,进行信息的比对,如果 比对成功则返回数据,不对则不返回数据 。
1.3.2、生成 jwt 凭证
nodejs 中 jsonwebtoken 提供了一套( jwt )加密 和 解码的 算法
- 安装
npm i -S jsonwebtoken
- 加密 解密
var token = require("jsonwebtoken");
// 加密 生成密钥 key
var key = token.sign( 数据 , 加密串 );
// 解密 通过加密串将密钥解密还原
var source = token.verify( key, 加密串 );
正式测试一下自己所写的后端请求端口 :
1.3.3、网络请求封装
下载 axios 第三方
npm i -S axios
"axios": "^0.24.0",
src / utils / request.js
封装 sessionStorage 会话存储 的 Api 方法 :
操作方法 Api :
import store from './sessionStore' const key = 'token' export const setToken = token => store.set(key, token) export const getToken = () => store.get(key) export const delToken = () => store.del(key) // 判断是否有 token export const hasToken = () => store.get(key) === '' ? false : true
loginAction 是一个 Promise
登录成功跳转页面之后 在 sessionStorage 内为什么能够成功写入数据呢 ?
是因为首先你一定要有一个 返回值 (boolean)
如果你不写返回值的话 , 因为这个写入是异步的 , 而跳转又是同步的 ,
你可能会出现的异常情况就是 : 你写入数据写了一半时 , 它跳转走了 , 导致你的数据一会有一会又没有了
所以你每次登录的时候每次都是第一次登录不成功 , 第二次登录就成功了的原因所在
(第一次你写了一半的时候它跳转走了 , 第二次在写的时候又把第一次的写进去了 , 第二次用的其实就是第一次的了 , 但是给人的感觉就像是第二次才登录成功了)
但是你有了返回值 ( boolean )就没有问题了 , Promise 得到了你成功 ( true ) 了之后才会再执行下一步的操作
src / config / api / loginConfig.js
src / api / loginApi.js
src / store / module / user.js
// 导入请求的方法 import { doLoginApi } from '@/api/loginApi' import { setUid } from '@/utils/uid' import { setUserName } from '@/utils/username' import { setToken } from '@/utils/token' // import { setPassWord } from '@/utils/password' export default { namespaced: true, state: { uid: 0, token: '', username: '' }, mutations: { addUid(state, uid) { setUid(uid) state.uid = uid }, addUserName(state, username) { setUserName(username) state.username = username }, addToken(state, token) { setToken(token) state.token = token }, }, actions: { /* loginAction 是一个 Promise 登录成功跳转页面之后 在 sessionStorage 内为什么能够成功写入数据呢 ? 是因为首先你一定要有一个 返回值 (boolean) 如果你不写返回值的话 , 因为这个写入是异步的 , 而跳转又是同步的 , 你可能会出现的异常情况就是 : 你写入数据写了一半时 , 它跳转走了 , 导致你的数据一会有一会又没有了 所以你每次登录的时候每次都是第一次登录不成功 , 第二次登录就成功了的原因所在 (第一次你写了一半的时候它跳转走了 , 第二次在写的时候又把第一次的写进去了 , 第二次用的其实就是第一次的了 , 但是给人的感觉就像是第二次才登录成功了) 但是你有了返回值就没有问题了 , Promise 得到了你成功了之后才会再执行下一步的操作 */ async loginAction({ commit }, userData) { let ret = await doLoginApi(userData) if (ret.code === 0) { /* 分开最大的好处就是你的代码结构比较完整(函数式编程) 在这里呢 , 我们其实可以只写一个的 , 但是为什么我们写多个呢 ? 因为假如某一天我们不要哪一个了的话 , 我们就直接在这里注释掉就可以了 你的方法关闭和开合就会很方便了 */ commit('addUid', ret.data.uid) commit('addUserName', ret.data.username) commit('addToken', ret.data.token) } return ret.code === 0 ? true : false } } }
1.2.6、后台登录实现
1.2.7、后台用户访问验证
1.2.8、后台界面展示
1.2.9、后台页面子路由设置显示
1.2.10、菜单动态管理
1.2.11、通过动态路由添加来完成路由权限控制
1.2.12、动态添加路由来完成路由权限
1.4、权限控制
1.4.1、登录权限
检查用户是已经登录
// 路由全局前置守卫
router.beforeEach((to, form, next) => {
if (to.path != '/login') { // 如果不是登录页面, 怎进行向下验证
console.log('to.path 不是 login');
if (true) { // 这里只需要判断是否有 token 令牌
console.log('没有 token 则为假 , 跳登录页面');
next({
path: '/login',
replace: true // 不能进行返回操作
})
} else {
next() // 向下执行
}
} else {
next() // 向下执行
}
})
1.4.2、菜单权限
基于角色的权限控制 rbac
在公司中都是在登录时后台通过用户名来完成对于此用户属于哪一个角色,根据此角色来完成对应菜单数据返回
1.4.3、路由权限
如果当前用户的角色没有对应的路由权限则直接访问也无法获获取对应的页面展示
router.addRoute( ) api
vue-router3
router.addRoutes( [ ] )
router.addroute( { } )
vue-router4
router.addroute( { } )
1.4.4、按钮
富文本编辑器
可以简单的理解为 在线 Word , 有图有文字 , 实现如下操作步骤 :
"wangeditor": "^4.7.9"
使用 :
module.exports = {
lintOnsave: false
}
<template>
<div id="div1">
<p>请输入内容</p>
</div>
</template>
<script>
// Web 富文本编辑器
import E from "wangeditor";
export default {
data() {
return {
editor: null,
userinfo: {
introduce: "",
},
};
},
mounted() {
this.editor = new E("#div1");
// 或者 const editor = new E( document.getElementById('div1') )
// 监听编辑器的变化
this.editor.config.onchange = (newHtml) => {
this.userinfo.introduce = newHtml;
// console.log(newHtml);
};
this.editor.create();
},
beforeDestroy() {
this.editor = null;
},
};
</script>
<style lang="scss" scoped>
@import "./scss/index.scss";
</style>
附例 : Vue 中的 @ 符 提示 文件配置
jsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
}
}
自动化 导入 路由 配置 文件 :
// vue-cli 中脚手架工具(webpack工具)提供一个api方法完成对于模块的自动加载
// 参数1:要去检索的目录
// 参数2:是否递归检索,true是,false否
// 参数3:指定要检索的文件类型 用正则来完成
// 返回值:是一个函数,且此函数有一个方法为keys返回一个检索到的文件列表(数组)
// 正则的 i不区分大小写 g全局 u支持中文
// const moduleFn = require.context('./routes', false, /\.(js|txt)$/i)
const moduleFn = require.context('./routes', false, /\.js$/i)
/* const routes = []
moduleFn.keys().forEach(file => {
// 动态得到当前导入的对象,可能为对象也可能为数组
let module = moduleFn(file).default
// es6提供一个api
if (Array.isArray(module)) {
routes.push(...module)
} else {
routes.push(module)
}
}) */
const routes = moduleFn.keys().reduce((prev, curr) => {
// 动态得到当前导入的对象,可能为对象也可能为数组
let module = moduleFn(curr).default
// es6提供一个api
if (Array.isArray(module)) {
prev.push(...module)
} else {
prev.push(module)
}
return prev
}, [])
自动化 导入 Vuex 模块化配置 文件 :
let moduleFn = require.context('./module', false, /\.js$/i)
let modules = moduleFn.keys().reduce((prev, curr) => {
let value = { ...moduleFn(curr).default, namespaced: true }
// 字符串方法和正则表达式中的分组
let key = curr.match(/\.\/(\w+)\.js/i)[1]
prev[key] = value
return prev
}, {})