Vue2+elementUi后台管理项目总结
前言该项目是一款对公司员工及商品管理的后台系统,主要实现功能:公司角色的增删改查,和商品的增删改查,项目的主要模块有,登录,主页,员工管理,权限管理,商品管理,该项目的亮点是权限管理,不同角色登录进入首页,看到的菜单和可操作的按钮是不一样的,如系统管理员可查看和操作所有模块。1.初始化项目涉及的前端技术栈:VueVue是一个用于创建用户界面的开源JavaScript框架Vue-routervue-
前言
该项目是一款对公司员工及商品管理的后台系统,主要实现功能:公司角色的增删改查,和商品的增删改查,项目的主要模块有,登录,主页,员工管理,权限管理,商品管理,该项目的亮点是权限管理,不同角色登录进入首页,看到的菜单和可操作的按钮是不一样的,如系统管理员可查看和操作所有模块。
1.初始化项目
涉及的前端技术栈:
Vue
Vue是一个用于创建用户界面的开源JavaScript框架
Vue-router
vue-router是Vue官方推出的路由管理器
elementUi
element是基于VUE的一套UI组件库
Axios
Axios,是一个基于promise网络请求库,作用于node.js和浏览器中
Echarts
“ECharts是一款基于JavaScript的数据可视化图表库
Vuex
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库
2.项目功能模块划分
- 登录/退出功能
- 主页布局
- 用户管理模块
- 权限管理模块
- 分类管理模块
- 商品列表模块
- 订单管理模块
- 数据统计模块
3.各模块功能实现
3.1封装axios请求
import axios from 'axios'
//因为我们将tokne存入的vuex,所以这里需要引入vuex
import vuex from '../store'
var api = axios.create({
baseURL: 'http://127.0.0.1:8888/api/private/v1/',
timeout: 5000
})
api.interceptors.request.use((config) => {
if (vuex.state.token) {
//这里是进行判断有没有token,如果有token的话通过请求头将token携带过去请求参数
config.headers.Authorization = vuex.state.token
}
return config
})
api.interceptors.response.use((res) => {
//这里是为了token过期之后是否回退到登陆页面
let code = res.data.meta.status
//获取返回的状态码
if (res.data.meta.msg == "无效token") {
//判断返回的msg如果为无效token的话,就说明token过期了,此时弹出一个提示框。
MessageBox.confirm('token过期, 是否跳转到登录页面?', '提示', {
confirmButtonText: '重新登录',
//若用户点击重新登录则回到登录页
cancelButtonText: '取消',
//若用户点击取消,则留在当前页面,但什么也做不了
type: 'warning'
}).then(() => {
//通过原生js的方法跳转到登录页
location.href = '/login'
}).catch((err) => {
err
})
}
return res.data
})
//最后导出出去
export default api
3.1.1二次封装
这里拿权限的接口为例
// 权限列表
export const menus = () => api.get("/menus")
3.1.2调用
在需要的页面就可以通过.then
获取数据
getData() {
menus().then((res) => {
this.menusList = res.data;
});
},
3.2登录/退出功能
对于后台管理这个项目来说呢,所有的功能和页面要求用户必须登录之后才可以查看,再后面的各个模块中,需要发送大量请求来获取数据,再进行页面的渲染,那么这个时候就需要进行哦按段用户的登录状态,登录成功后,才可以拿到相应的数据
3.2.1实现登录功能
这个很简单,只需将用户填写的用户名和密码通过接口发送到服务器验证即可,在服务器返回数据后将token存入Vuex
3.2.2
基于elementUi的表单验证
通过
:rules
绑定表单验证规则的对象,在表单上通过ref绑定,再通过$refs
拿到对应的表单对象,然后调用validate
方法进行验证
ruleValidate: {
username: [
{ required: true, message: "用户名不能为空", trigger: "blur" },
{ min: 3, max: 10, message: "用户名长度在3到10个字符",trigger:'blur'},
],
password: [
{ required: true, message: "密码不能为空", trigger: "blur" },
{ min: 6, max: 10, message: "用户名长度在6到10个字符",trigger:'blur'},
],
},
登录校验并将token存入本地
handleSubmit(name) {
let tant = this;
this.$refs[name].validate((valid) => {
if (valid) {
login(this.formValidate).then((res) => {
//这里是将token存入vuex
this.$store.commit("token", res.data.token);
this.$Notice.success({
title: "登录成功",
duration: 1,
});
this.$router.push("/");
});
} else {
this.$Message.error("表单验证失败!");
}
});
},
3.2.3路由守卫控制访问权限
router.beforeEach((to, from, next) => {
//to,from,next分别代表要去的地方,从哪里来,和放行或者重定向
let token = vuex.state.token
//先判断有没有token
if (token) {
//有的话就一路畅通
next()
} else {
//没有的话判断是不是去登录页
if (to.path == '/login') {
//是的话放行
next()
} else {
//不是的话就强行让他回到登录页
next('/login')
}
}
})
3.2.4退出功能
因为是基于token实现的登录,所以退出只需将存在vuex中的token销毁并跳转到登录页即可
3.3主页布局
这里是使用elementUi的菜单栏组件实现的,只需替换数据即可。
菜单栏的原本数据是树状结构,这里可以在router文件内挨个添加并引入本地路径,但稍显繁琐且代码量增多,那么这里呢就可以使用动态路由模式。
因为我们获取的数据是树状的,所以需要先通过递归获取最下层的结构并处理成列表结构,代码如下
export function fn(data) {
//先声明一个空数组,用来存放最终的列表结构
let arr = [];
//递归函数
function deep(data) {
//循环每一项
data.forEach((item) => {
//判断有没有children
if (item.children.length) {
//如果有的话,说明不是底层数据,开始递归
deep(item.children);
} else {
//如果没有的话则证明已经是底层数据了
arr.push({
//路径名
path: "/" + item.path,
//name名
name: item.authName,
//引入,注意 这里的文件还是需要手动配置的
component: () => import("@/views/homeList/" + item.path[0].toUpperCase() + item.path.substring(1) + ".vue"),
});
}
});
}
deep(data);
//最终return底层数据的数组
return arr;
}
这里是之后的列表数据
最后再使用this.$router.addRoute
添加到首页下的children
里边就可以了,代码如下
function loadRoute() {
let token = tant.$store.state.token;
let menus = JSON.parse(localStorage.getItem("menus"));
if (token && menus) {
let newList = fn(menus);
newList.forEach((item) => {
tant.$router.addRoute("Home", item);
});
}
}
loadRoute();
3.4用户管理模块
3.4.1
这个用户管理模块,就是由最基本的增删改查来实现的,首先通过接口请求用户列表的数据,然后通过elementUi的table表格渲染就可以了,大部分功能都是操作接口完成的,如根据ID搜索用户,就是传一个id值给后端,后端进行筛选之后返回符合id的用户,我们拿到数据之后进行渲染就可以。还有状态,也是通过接口像后端发送需要修改状态的用户id和修改后的布尔值,即可修改状态。这里值得说一下的有,添加用户和编辑用户可以公用一个模态框,代码如下。
//点击添加将判断的布尔值改为true,点击编辑则改为false
handleSubmit(name) {
//这里根据布尔值判断此次执行函数为编辑还是添加
if (this.deilOrAdd) {
//如果是添加的话
this.$refs[name].validate((valid) => {
if (valid) {
//通过表单验证
this.isShow = false;
//通过接口传递参数给后端
usersAdd(this.formValidate).then((res) => {
if (res.meta.status == 201) {
//创建成功后重新获取数据
this.getData();
this.$Notice.success({
title: "创建成功",
});
}
});
} else {
//表单验证失败
this.$Message.error("表单验证失败!");
}
});
} else {
//如果为编辑的话
let obj = {
id: this.userId,
obj1: this.formValidate,
};
//先回填当前编辑用户的数据
usersDeil(obj).then((res) => {
//通过接口传递参数给后端进行修改
if (res.meta.status == 200) {
this.$Notice.success({
title: "更新成功",
});
//修改完成重新刷新
this.getData();
//将模态框隐藏
this.isShow = false;
}
});
}
},
3.4.2分页
值得一说的还有分页的逻辑了,分页所有的功能都是围绕这current
,pagesize
,token
来实现的,我这里自己封装了一个分页的组件,代码如下
<template>
<div class="myPage">
<span class="total">共{{ total }}条</span>
<div class="before">
<div @click="sub" class="arrows beforeArrows"></div>
</div>
<span
@click="item == '...' ? false : $emit('change-current', item)"
:class="current == item ? 'pageItem pageItemACur' : 'pageItem'"
v-for="(item, index) in pagenum"
:key="index"
>{{ item }}</span
>
<div class="aftter">
<div @click="add" class="arrows aftterArrows"></div>
</div>
<p class="toPage">
前往<input
class="inp"
type="text"
v-model.number="toPage"
@keyup.enter="toPageNum"
/>页
</p>
</div>
</template>
<script>
export default {
name: "demo",
props: {
//接收父组件传递过来的总条数
total: {
type: Number,
default: 50,
},
//接收父组件传递过来的每页条数
pagesize: {
type: Number,
default: 5,
},
//接收父组件传递过来的当前页
current: {
type: Number,
default: 1,
},
},
components: {},
data() {
return {
num: 0,
toPage: this.current,
};
},
created() {},
computed: {
//这里使用计算属性完成
pagenum() {
//先声明一个存放按钮内容的空数组
let pagenumArr = [];
//遍历token每页条数就可以获得
for (var i = 0; i < Math.ceil(this.total / this.pagesize); i++) {
//push到数组
pagenumArr.push(i + 1);
}
//此时需要判断按钮的数量
//以这里为例,我希望按钮最大数量为9,所以加了个>9的if判断
if (pagenumArr.length > 9) {
//再进行判断 当前值是否为5以下
if (this.current <= 5) {
//为5以下的显示前7个按钮,中间用...代替,最后一个值为数组的长度
pagenumArr = [1, 2, 3, 4, 5, 6, 7, "...", pagenumArr.length];
} else if (this.current >= pagenumArr[pagenumArr.length - 1] - 4) {
//反之判断是否当前值是否是在数组的末尾
pagenumArr = [
1,
"...",
pagenumArr.length - 6,
pagenumArr.length - 5,
pagenumArr.length - 4,
pagenumArr.length - 3,
pagenumArr.length - 2,
pagenumArr.length - 1,
pagenumArr.length,
];
// 如果是在数组的末尾则将1后边的省略为...,最后的值为length--就能得出后边的值
} else {
//再进行判断是否是在中间点,如果是在中间,则两边以...显示
pagenumArr = [
1,
"...",
this.current - 2,
this.current - 1,
this.current,
this.current + 1,
this.current + 2,
"...",
pagenumArr.length,
];
}
}
//return出去
return pagenumArr;
},
},
methods: {
//去第几页的函数
toPageNum() {
//如果跳转的页码大于总页数,则跳到最后一页
if (this.toPage > this.pagenum[this.pagenum.length - 1]) {
this.$emit("change-current", this.pagenum[this.pagenum.length - 1]);
} else {
//反之则跳转到相应页面
this.$emit("change-current", this.toPage);
}
//跳转完input失焦
document.querySelector(".inp").blur();
},
add() {
//点击判断是否是最后一位
if (this.current >= this.pagenum[this.pagenum.length - 1]) {
//是的话return
return;
} else {
//不是则跳转
this.$emit("change-current", this.current + 1);
}
},
sub() {
//判断是不是第一位
if (this.current <= 1) {
//是就return
return;
} else {
//不是则跳转
this.$emit("change-current", this.current - 1);
}
},
},
mounted() {}
};
</script>
3.4.3面包屑导航
我这里的面包屑导航是使用了自己封装的一个函数,代码如下;
goItem(e) {
//在点击路由跳转时触发
this.menusList.forEach((item) => {
//遍历menus
item.children.forEach((ele) => {
//遍历menus里边的每一个children
if (ele.path == e) {
//如果children的path等于路由要跳转的路径的时候
this.bread = { Fname: item.authName, Cname: ele.authName };
//将data里边的面包屑对象更换,第一个值是父亲的name,第二个值是儿子的name,这样就可以拿这个对象去渲染面包屑了
}
});
});
//同时将当前路径存储到data中
this.name = e;
//进行跳转,这里catch是vue重复调换报错的问题,捕获报错但不发不出来,蛮有趣的。
this.$router.push("/" + e).catch((err) => {
err;
});
},
breadShow() {
//判断刚刚存入的路径名称
this.name = this.name + "";
//是不是等于欢迎页
if (window.location.hash != "#/welcome") {
//是的话不显示面包屑
return true;
} else {
//不是则显示面包屑
return false;
}
},
因为后台管理项目只有两层,所以我这里没用递归,只使用了双层for循环即可完成面包屑效果。
3.4.3还有给用户分配角色
这一步需要在模态框回填该角色的数据,并获取所有角色列表,渲染到下拉菜单里边,选中之后发送请求,携带当前用户id和角色ID就可以给用户分配角色
3.5权限管理模块
权限管理模块,顾名思义,就是给不同的用户分配不同的操作和查阅的权限,而这个权限与用户中间的,又添加了角色这个概念,即给用户分配角色,给角色分配权限,这样会使得进权限分配更加的方便且灵活
3.5.1角色列表
首先需要在点击添加弹出的模态框添加角色,只需填写角色名和角色描述即可通过接口提交就可以了,如图
添加完成之后就可以给这个角色分配相应的权限
我这里也封装了一个tree树状组件,代码如下
<div class="myTree">
<div class="treeP">
<b v-if="listFlag" @click="change">{{ isShow ? "-" : "+" }}</b>
<h4>{{ treeList.label }}</h4>
</div>
<div class="treeBox" v-show="isShow">
<my-tree
v-for="(item, index) in treeList.children"
:key="index"
:treeList="item"
></my-tree>
</div>
</div>
这个主要是使用的递归组件的思路,在组件里再使用一次当前的组件,第二次使用组件时就不需要再引入了,然后通过父传子一层一层的向下传递,即可实现递归显示所有树形数据,通过props接收的时候可以定义两种数据类型为Array,Object
props: {
treeList: {
type: [Object, Array],
required: true,
},
},
而树形组件是否显示呢则是通过计算属性实现的
listFlag() {
return this.treeList.children && this.treeList.children.length;
},
判断他是否有children且children有没有长度,这里的返回值就是判断是否隐藏的布尔值。
3.5.2权限列表
这个权限列表只需渲染即可,唯一要注意的点呢就是tag标签通过v-if或者v-show判断以下显示某个颜色的标签即可。
3.6商品管理
3.6.1商品列表
商品列表的增删改查的功能同用户管理的增删改查,都是操作接口即可,而添加商品呢,需要跳转到另外的页面,这里说一下,如果使用的是动态路由的话,那么像welcom欢迎页,还有这个添加商品是需要自己另外配置路由的,因为请求menus列表的时候返回的路径是不包含这两个页面的路径的,所以需要手动配置。
那么现在开始添加商品的操作。
点击添加按钮跳转到添加页,如图所示
这个选择商品分类呢,是一个级联菜单,这里需要将第二级的id通过接口发送给商品添加分类
3.6.2上传图片
然后在商品图片中呢,使用的是elementUi的上传图片的组件。
<el-upload
class="upload-demo"
:action="actionUrl"
:headers="headers"
:on-preview="handlePreview"
:on-remove="handleRemove"
:on-success="handleSuccess"
list-type="picture"
>
<el-button size="small" type="primary">点击上传</el-button>
</el-upload>
:action图片上传的地址
:headers请求头的配置
:on-success上传成功的钩子函数
:on-remove删除的钩子函数
:on-preview——点击列表中已上传的图片的钩子
上传成功后,需要把上传的图片的信息追加到数组中
3.6.3最后还有富文本编辑器
<quill-editor v-model="ruleForm.goods_introduce"> </quill-editor>
3.6.4分类参数
分类参数首先需要选择商品的分类,也是一个级联菜单,选择完之后可以给他们配置参数和属性,也是通过接口进行一些增删改查。
3.6.5商品分类
同用户管理的增删改查,这里不再过多赘述。
3.7数据报表
数据报表我们是使用echarts实现的,echarts的使用方法呢,和elementUi差不多,只需引入之后替换数据即可,需要注意的是呢,这个echarts的模板呢是需要通过ui给的图来选择的。像我刚开始自己学习echarts的时候是哪个好看用哪个,结果跟后台 返回的数据根本对不上,所以写了好久都没写出来,而当我替换了与效果图类似的模板,效果便直接出来了。还有一点,如果你的项目需要打包的话,不建议使用5.0以上的echarts,因为这个版本以上打包时需要在名称前添加* as
在打包抽离时不好根据名字去匹配,所以建议使用4.9.0左右的版本。下面是效果
至此,后台管理项目结束。
更多推荐
所有评论(0)