项目简介:
该项目为电商后台的管理系统。设计了登录页面。
管理人员需要通过输入正确的用户名和密码才能登录。登陆成功之后进入管理页面:
管理页面由五个子模块组成:用户管理,权限管理,商品管理,订单管理,数据统计;
每个子模块有若干子模块组成,用户管理下->用户列表,权限管理->角色列表,权限管理,商品管理->商品列表,分类参数,商品分配,订单管理->订单列表,数据统计->数据报表
登录页面
登录页面中对用户输入的内容进行预校验,如果不符合要求则,则不向后端发送请求,同事挂载路由守卫,防止强制跳转。同时设置令牌校验,避免重复登录。如果用户输入格式正确的用户名以及密码时,向后端发送请求,请求通过则跳转到管理页面,否则返回登录页面。
路由导航守卫:
// 挂载路由导航守卫router.beforeEach((to, from, next) => { // to 将要访问的路径 // from 代表从哪个路径跳转而来 // next 是一个函数,表示放行 // next() 放行 next('/login') 强制跳转 if (to.path === '/login') return next() // 获取token const tokenStr = window.sessionStorage.getItem('token') if (!tokenStr) return next('/login') next()})
登录页面核心代码:
登录 重置 export default { data() { return { // 这是登录表单的数据绑定对象 loginForm: { username: 'admin', password: '123456' }, // 这是表单的验证规则对象 loginFormRules: { // 验证用户名是否合法 username: [ { required: true, message: '请输入登录名称', trigger: 'blur' }, { min: 3, max: 10, message: '长度在 3 到 10 个字符', trigger: 'blur' } ], // 验证密码是否合法 password: [ { required: true, message: '请输入登录密码', trigger: 'blur' }, { min: 6, max: 15, message: '长度在 6 到 15 个字符', trigger: 'blur' } ] } } }, methods: { // 点击重置按钮,重置登录表单 resetLoginForm() { // console.log(this); this.$refs.loginFormRef.resetFields() }, login() { this.$refs.loginFormRef.validate(async valid => { if (!valid) return const { data: res } = await this.$http.post('login', this.loginForm) if (res.meta.status !== 200) return this.$message.error('登录失败!') this.$message.success('登录成功') // 1. 将登录成功之后的 token,保存到客户端的 sessionStorage 中 // 1.1 项目中出了登录之外的其他API接口,必须在登录之后才能访问 // 1.2 token 只应在当前网站打开期间生效,所以将 token 保存在 sessionStorage 中 window.sessionStorage.setItem('token', res.data.token) // 2. 通过编程式导航跳转到后台主页,路由地址是 /home this.$router.push('/home') }) } }}.login_container { background-color: #2b4b6b; height: 100%;}.login_box { width: 450px; height: 300px; background-color: #fff; border-radius: 3px; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); .avatar_box { height: 130px; width: 130px; border: 1px solid #eee; border-radius: 50%; padding: 10px; box-shadow: 0 0 10px #ddd; position: absolute; left: 50%; transform: translate(-50%, -50%); background-color: #fff; img { width: 100%; height: 100%; border-radius: 50%; background-color: #eee; } }}.login_form { position: absolute; bottom: 0; width: 100%; padding: 0 20px; box-sizing: border-box;}.btns { display: flex; justify-content: flex-end;}
菜单实现
管理页面有一个侧面的两级菜单,菜单的数据来自于后端,点击二级菜单会跳转到相应的子页面中。在el-menu中设置router属性,即可通过index添加到路由上进行跳转。
电商后台管理系统 退出 ||| {{item.authName}} {{subItem.authName}} export default { data() { return { // 左侧菜单数据 menulist: [], iconsObj: { '125': 'iconfont icon-user', '103': 'iconfont icon-tijikongjian', '101': 'iconfont icon-shangpin', '102': 'iconfont icon-danju', '145': 'iconfont icon-baobiao' }, // 是否折叠 isCollapse: false, // 被激活的链接地址 activePath: '' } }, created() { this.getMenuList() this.activePath = window.sessionStorage.getItem('activePath') }, methods: { logout() { window.sessionStorage.clear() this.$router.push('/login') }, // 获取所有的菜单 async getMenuList() { const { data: res } = await this.$http.get('menus') if (res.meta.status !== 200) return this.$message.error(res.meta.msg) this.menulist = res.data console.log(res) }, // 点击按钮,切换菜单的折叠与展开 toggleCollapse() { this.isCollapse = !this.isCollapse }, // 保存链接的激活状态 saveNavState(activePath) { window.sessionStorage.setItem('activePath', activePath) this.activePath = activePath } }}.home-container { height: 100%;}.el-header { background-color: #373d41; display: flex; justify-content: space-between; padding-left: 0; align-items: center; color: #fff; font-size: 20px; > div { display: flex; align-items: center; span { margin-left: 15px; } }}.el-aside { background-color: #333744; .el-menu { border-right: none; }}.el-main { background-color: #eaedf1;}.iconfont { margin-right: 10px;}.toggle-button { background-color: #4a5064; font-size: 10px; line-height: 24px; color: #fff; text-align: center; letter-spacing: 0.2em; cursor: pointer;}
用户管理
用户列表
用户管理下有用户列表,这里渲染了后端的用户列表,可以编辑用户信息,删除用户,为用户分配角色,还可以对用户是否禁用进行管理;除此之外,还添加了查询用户,添加用户,和分页功能。
核心代码:
首页 用户管理 用户列表 添加用户 当前的用户:{{userInfo.username}}
当前的角色:{{userInfo.role_name}}
分配新角色:
export default { data() { // 验证邮箱的规则 var checkEmail = (rule, value, cb) => { // 验证邮箱的正则表达式 const regEmail = /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(\.[a-zA-Z0-9_-])+/ if (regEmail.test(value)) { // 合法的邮箱 return cb() } cb(new Error('请输入合法的邮箱')) } // 验证手机号的规则 var checkMobile = (rule, value, cb) => { // 验证手机号的正则表达式 const regMobile = /^(0|86|17951)" />
权限管理
角色列表
角色列表中可以创建新的角色,创建的新的角色可以在用户管理中赋予用户,同时可以为已有的角色赋予权限
首页 权限管理 角色列表 添加角色 {{item1.authName}} {{item2.authName}} {{item3.authName}} <!--
{{scope.row}}--> 编辑 删除 分配权限
export default { data() { return { // 所有角色列表数据 rolelist: [], // 控制分配权限对话框的显示与隐藏 setRightDialogVisible: false, // 所有权限的数据 rightslist: [], // 树形控件的属性绑定对象 treeProps: { label: 'authName', children: 'children' }, // 默认选中的节点Id值数组 defKeys: [], // 当前即将分配权限的角色id roleId: '' } }, created() { this.getRolesList() }, methods: { // 获取所有角色的列表 async getRolesList() { const { data: res } = await this.$http.get('roles') if (res.meta.status !== 200) { return this.$message.error('获取角色列表失败!') } this.rolelist = res.data console.log(this.rolelist) }, // 根据Id删除对应的权限 async removeRightById(role, rightId) { // 弹框提示用户是否要删除 const confirmResult = await this.$confirm( '此操作将永久删除该文件, 是否继续?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' } ).catch(err => err) if (confirmResult !== 'confirm') { return this.$message.info('取消了删除!') } const { data: res } = await this.$http.delete( `roles/${role.id}/rights/${rightId}` ) if (res.meta.status !== 200) { return this.$message.error('删除权限失败!') } // this.getRolesList() role.children = res.data }, // 展示分配权限的对话框 async showSetRightDialog(role) { this.roleId = role.id // 获取所有权限的数据 const { data: res } = await this.$http.get('rights/tree') if (res.meta.status !== 200) { return this.$message.error('获取权限数据失败!') } // 把获取到的权限数据保存到 data 中 this.rightslist = res.data console.log(this.rightslist) // 递归获取三级节点的Id this.getLeafKeys(role, this.defKeys) this.setRightDialogVisible = true }, // 通过递归的形式,获取角色下所有三级权限的id,并保存到 defKeys 数组中 getLeafKeys(node, arr) { // 如果当前 node 节点不包含 children 属性,则是三级节点 if (!node.children) { return arr.push(node.id) } node.children.forEach(item => this.getLeafKeys(item, arr)) }, // 监听分配权限对话框的关闭事件 setRightDialogClosed() { this.defKeys = [] }, // 点击为角色分配权限 async allotRights() { const keys = [ ...this.$refs.treeRef.getCheckedKeys(), ...this.$refs.treeRef.getHalfCheckedKeys() ] const idStr = keys.join(',') const { data: res } = await this.$http.post( `roles/${this.roleId}/rights`, { rids: idStr } ) if (res.meta.status !== 200) { return this.$message.error('分配权限失败!') } this.$message.success('分配权限成功!') this.getRolesList() this.setRightDialogVisible = false } }}.el-tag { margin: 7px;}.bdtop { border-top: 1px solid #eee;}.bdbottom { border-bottom: 1px solid #eee;}.vcenter { display: flex; align-items: center;}权限列表
权限列表对不同的权限做出展示,只发送一个请求即可获取所有需要的数据
首页 权限管理 权限列表 一级 二级 三级 export default { data() { return { // 权限列表 rightsList: [] } }, created() { // 获取所有的权限 this.getRightsList() }, methods: { // 获取权限列表 async getRightsList() { const { data: res } = await this.$http.get('rights/list') if (res.meta.status !== 200) { return this.$message.error('获取权限列表失败!') } this.rightsList = res.data console.log(this.rightsList) } }}
商品管理
商品分类
首页 商品管理 商品分类 添加分类 一级 二级 三级 编辑 删除
export default { data() { return { // 查询条件 querInfo: { type: 3, pagenum: 1, pagesize: 5 }, // 商品分类的数据列表,默认为空 catelist: [], // 总数据条数 total: 0, // 为table指定列的定义 columns: [ { label: '分类名称', prop: 'cat_name' }, { label: '是否有效', // 表示,将当前列定义为模板列 type: 'template', // 表示当前这一列使用模板名称 template: 'isok' }, { label: '排序', // 表示,将当前列定义为模板列 type: 'template', // 表示当前这一列使用模板名称 template: 'order' }, { label: '操作', // 表示,将当前列定义为模板列 type: 'template', // 表示当前这一列使用模板名称 template: 'opt' } ], // 控制添加分类对话框的显示与隐藏 addCateDialogVisible: false, // 添加分类的表单数据对象 addCateForm: { // 将要添加的分类的名称 cat_name: '', // 父级分类的Id cat_pid: 0, // 分类的等级,默认要添加的是1级分类 cat_level: 0 }, // 添加分类表单的验证规则对象 addCateFormRules: { cat_name: [{ required: true, message: '请输入分类名称', trigger: 'blur' }] }, // 父级分类的列表 parentCateList: [], // 指定级联选择器的配置对象 cascaderProps: { value: 'cat_id', label: 'cat_name', children: 'children' }, // 选中的父级分类的Id数组 selectedKeys: [] } }, created() { this.getCateList() }, methods: { // 获取商品分类数据 async getCateList() { const { data: res } = await this.$http.get('categories', { params: this.querInfo }) if (res.meta.status !== 200) { return this.$message.error('获取商品分类失败!') } console.log(res.data) // 把数据列表,赋值给 catelist this.catelist = res.data.result // 为总数据条数赋值 this.total = res.data.total }, // 监听 pagesize 改变 handleSizeChange(newSize) { this.querInfo.pagesize = newSize this.getCateList() }, // 监听 pagenum 改变 handleCurrentChange(newPage) { this.querInfo.pagenum = newPage this.getCateList() }, // 点击按钮,展示添加分类的对话框 showAddCateDialog() { // 先获取父级分类的数据列表 this.getParentCateList() // 再展示出对话框 this.addCateDialogVisible = true }, // 获取父级分类的数据列表 async getParentCateList() { const { data: res } = await this.$http.get('categories', { params: { type: 2 } }) if (res.meta.status !== 200) { return this.$message.error('获取父级分类数据失败!') } console.log(res.data) this.parentCateList = res.data }, // 选择项发生变化触发这个函数 parentCateChanged() { console.log(this.selectedKeys) // 如果 selectedKeys 数组中的 length 大于0,证明选中的父级分类 // 反之,就说明没有选中任何父级分类 if (this.selectedKeys.length > 0) { // 父级分类的Id this.addCateForm.cat_pid = this.selectedKeys[this.selectedKeys.length - 1] // 为当前分类的等级赋值 this.addCateForm.cat_level = this.selectedKeys.length } else { // 父级分类的Id this.addCateForm.cat_pid = 0 // 为当前分类的等级赋值 this.addCateForm.cat_level = 0 } }, // 点击按钮,添加新的分类 addCate() { this.$refs.addCateFormRef.validate(async valid => { if (!valid) return const { data: res } = await this.$http.post('categories', this.addCateForm) if (res.meta.status !== 201) { return this.$message.error('添加分类失败!') } this.$message.success('添加分类成功!') this.getCateList() this.addCateDialogVisible = false }) }, // 监听对话框的关闭事件,重置表单数据 addCateDialogClosed() { this.$refs.addCateFormRef.resetFields() this.selectedKeys = [] this.addCateForm.cat_level = 0 this.addCateForm.cat_pid = 0 } }}.treeTable { margin-top: 15px;}.el-cascader { width: 100%;}商品列表
首页 商品管理 商品列表 添加商品 {{scope.row.add_time | dateFormat}} export default { data() { return { // 查询参数对象 queryInfo: { query: '', pagenum: 1, pagesize: 10 }, // 商品列表 goodslist: [], // 总数据条数 total: 0 } }, created() { this.getGoodsList() }, methods: { // 根据分页获取对应的商品列表 async getGoodsList() { const { data: res } = await this.$http.get('goods', { params: this.queryInfo }) if (res.meta.status !== 200) { return this.$message.error('获取商品列表失败!') } this.$message.success('获取商品列表成功!') console.log(res.data) this.goodslist = res.data.goods this.total = res.data.total }, handleSizeChange(newSize) { this.queryInfo.pagesize = newSize this.getGoodsList() }, handleCurrentChange(newPage) { this.queryInfo.pagenum = newPage this.getGoodsList() }, async removeById(id) { const confirmResult = await this.$confirm( '此操作将永久删除该商品, 是否继续?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' } ).catch(err => err) if (confirmResult !== 'confirm') { return this.$message.info('已经取消删除!') } const { data: res } = await this.$http.delete(`goods/${id}`) if (res.meta.status !== 200) { return this.$message.error('删除失败!') } this.$message.success('删除成功!') this.getGoodsList() }, goAddpage() { this.$router.push('/goods/add') } }}
增加商品
在商品分类中点击新增商品,则跳转到新增商品窗口
首页 商品管理 添加商品 点击上传 添加商品 import _ from 'lodash'export default { data() { return { activeIndex: '0', // 添加商品的表单数据对象 addForm: { goods_name: '', goods_price: 0, goods_weight: 0, goods_number: 0, // 商品所属的分类数组 goods_cat: [], // 图片的数组 pics: [], // 商品的详情描述 goods_introduce: '', attrs: [] }, addFormRules: { goods_name: [ { required: true, message: '请输入商品名称', trigger: 'blur' } ], goods_price: [ { required: true, message: '请输入商品价格', trigger: 'blur' } ], goods_weight: [ { required: true, message: '请输入商品重量', trigger: 'blur' } ], goods_number: [ { required: true, message: '请输入商品数量', trigger: 'blur' } ], goods_cat: [ { required: true, message: '请选择商品分类', trigger: 'blur' } ] }, // 商品分类列表 catelist: [], cateProps: { label: 'cat_name', value: 'cat_id', children: 'children' }, // 动态参数列表数据 manyTableData: [], // 静态属性列表数据 onlyTableData: [], // 上传图片的URL地址 uploadURL: 'http://127.0.0.1:8888/api/private/v1/upload', // 图片上传组件的headers请求头对象 headerObj: { Authorization: window.sessionStorage.getItem('token') }, previewPath: '', previewVisible: false } }, created() { this.getCateList() }, methods: { // 获取所有商品分类数据 async getCateList() { const { data: res } = await this.$http.get('categories') if (res.meta.status !== 200) { return this.$message.error('获取商品分类数据失败!') } this.catelist = res.data console.log(this.catelist) }, // 级联选择器选中项变化,会触发这个函数 handleChange() { console.log(this.addForm.goods_cat) if (this.addForm.goods_cat.length !== 3) { this.addForm.goods_cat = [] } }, beforeTabLeave(activeName, oldActiveName) { // console.log('即将离开的标签页名字是:' + oldActiveName) // console.log('即将进入的标签页名字是:' + activeName) // return false if (oldActiveName === '0' && this.addForm.goods_cat.length !== 3) { this.$message.error('请先选择商品分类!') return false } }, async tabClicked() { // console.log(this.activeIndex) // 证明访问的是动态参数面板 if (this.activeIndex === '1') { const { data: res } = await this.$http.get( `categories/${this.cateId}/attributes`, { params: { sel: 'many' } } ) if (res.meta.status !== 200) { return this.$message.error('获取动态参数列表失败!') } console.log(res.data) res.data.forEach(item => { item.attr_vals = item.attr_vals.length === 0 " />.el-checkbox { margin: 0 10px 0 0 !important;}.previewImg { width: 100%;}.btnAdd { margin-top: 15px;}
分类参数
在分类参数中选择一件商品,可以为其添加静态或者动态参数,这个参数可以展示在移动端商品的属性中。
首页 商品管理 参数列表 选择商品分类: 添加参数 {{item}} + New Tag 编辑 删除 添加属性 {{item}} + New Tag 编辑 删除 export default { data() { return { // 商品分类列表 catelist: [], // 级联选择框的配置对象 cateProps: { value: 'cat_id', label: 'cat_name', children: 'children' }, // 级联选择框双向绑定到的数组 selectedCateKeys: [], // 被激活的页签的名称 activeName: 'many', // 动态参数的数据 manyTableData: [], // 静态属性的数据 onlyTableData: [], // 控制添加对话框的显示与隐藏 addDialogVisible: false, // 添加参数的表单数据对象 addForm: { attr_name: '' }, // 添加表单的验证规则对象 addFormRules: { attr_name: [ { required: true, message: '请输入参数名称', trigger: 'blur' } ] }, // 控制修改对话框的显示与隐藏 editDialogVisible: false, // 修改的表单数据对象 editForm: {}, // 修改表单的验证规则对象 editFormRules: { attr_name: [ { required: true, message: '请输入参数名称', trigger: 'blur' } ] } } }, created() { this.getCateList() }, methods: { // 获取所有的商品分类列表 async getCateList() { const { data: res } = await this.$http.get('categories') if (res.meta.status !== 200) { return this.$message.error('获取商品分类失败!') } this.catelist = res.data console.log(this.catelist) }, // 级联选择框选中项变化,会触发这个函数 handleChange() { this.getParamsData() }, // tab 页签点击事件的处理函数 handleTabClick() { console.log(this.activeName) this.getParamsData() }, // 获取参数的列表数据 async getParamsData() { // 证明选中的不是三级分类 if (this.selectedCateKeys.length !== 3) { this.selectedCateKeys = [] this.manyTableData = [] this.onlyTableData = [] return } // 证明选中的是三级分类 console.log(this.selectedCateKeys) // 根据所选分类的Id,和当前所处的面板,获取对应的参数 const { data: res } = await this.$http.get( `categories/${this.cateId}/attributes`, { params: { sel: this.activeName } } ) if (res.meta.status !== 200) { return this.$message.error('获取参数列表失败!') } res.data.forEach(item => { item.attr_vals = item.attr_vals " />.cat_opt { margin: 15px 0;}.el-tag { margin: 10px;}.input-new-tag { width: 120px;}
订单管理
订单列表
订单管理的实现和用户管理有很多类似的地方,都是向后端发送请求然后渲染到页面上
首页 订单管理 订单列表 已付款 未付款 {{scope.row.is_send}} {{scope.row.create_time | dateFormat}}
{{activity.context}} import cityData from './citydata.js'export default { data() { return { queryInfo: { query: '', pagenum: 1, pagesize: 10 }, total: 0, orderlist: [], addressVisible: false, addressForm: { address1: [], address2: '' }, addressFormRules: { address1: [ { required: true, message: '请选择省市区县', trigger: 'blur' } ], address2: [ { required: true, message: '请填写详细地址', trigger: 'blur' } ] }, cityData, progressVisible: false, progressInfo: [] } }, created() { this.getOrderList() }, methods: { async getOrderList() { const { data: res } = await this.$http.get('orders', { params: this.queryInfo }) if (res.meta.status !== 200) { return this.$message.error('获取订单列表失败!') } console.log(res) this.total = res.data.total this.orderlist = res.data.goods }, handleSizeChange(newSize) { this.queryInfo.pagesize = newSize this.getOrderList() }, handleCurrentChange(newPage) { this.queryInfo.pagenum = newPage this.getOrderList() }, // 展示修改地址的对话框 showBox() { this.addressVisible = true }, addressDialogClosed() { this.$refs.addressFormRef.resetFields() }, async showProgressBox() { const { data: res } = await this.$http.get('/kuaidi/804909574412544580') if (res.meta.status !== 200) { return this.$message.error('获取物流进度失败!') } this.progressInfo = res.data this.progressVisible = true console.log(this.progressInfo) } }}@import '../../plugins/timeline/timeline.css';@import '../../plugins/timeline-item/timeline-item.css';.el-cascader { width: 100%;}数据统计
数据报表
数据统计部分用到了echarts,从后端获得数据后通过_.merge()将数据组合在一起,最后渲染在页面上
首页 数据统计 数据报表 // 1. 导入 echartsimport echarts from 'echarts'import _ from 'lodash'export default { data() { return { // 需要合并的数据 options: { title: { text: '用户来源' }, tooltip: { trigger: 'axis', axisPointer: { type: 'cross', label: { backgroundColor: '#E9EEF3' } } }, grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true }, xAxis: [ { boundaryGap: false } ], yAxis: [ { type: 'value' } ] } } }, created() {}, // 此时,页面上的元素,已经被渲染完毕了! async mounted() { // 3. 基于准备好的dom,初始化echarts实例 var myChart = echarts.init(document.getElementById('main')) const { data: res } = await this.$http.get('reports/type/1') if (res.meta.status !== 200) { return this.$message.error('获取折线图数据失败!') } // 4. 准备数据和配置项 const result = _.merge(res.data, this.options) // 5. 展示数据 myChart.setOption(result) }, methods: {}}
项目git地址
目录中有后端以及sql文件,按照说明运行即可
电商系统前端: 电商管理系统前端
学习资源
黑马程序员前端