后台管理项目

404 阅读6分钟

参考

Github高star项目:

如何在5天内学会Vue?聊聊我的学习方法!

极简的 vue admin 管理后台 README

手摸手,带你用vue撸后台 系列四(vueAdmin 一个极简的后台基础模板)

b站教学视频:

VUE项目,VUE项目实战,vue后台管理系统,前端面试,前端面试项目

我的Github项目和资料笔记

项目总结

应用场景、作用

页面展示

1.登录页,做了权限的控制,非管理员账号只能看到首页和商品管理;

2.登录进去后,是首页看板(包括左上角的用户名称、权限名称;还有几个table表格、echarts图标展示数据)

image.png

3.顶部header区域,左侧是一个折叠菜单的按钮和一个面包屑;右侧是用户头像,可以点击进行登出;

4.左侧是菜单栏,有一级菜单和二级菜单;菜单栏里包括商品管理页面和用户管理页面;

5.点击菜单栏,header下面会相应出现tab,点击tab会切换不同的页面

6.用户管理页面,可以新增用户、对用户数据进行编辑;用户数据是分页的列表;顶部有一个搜索功能,使用姓名做数据层面的过滤

image.png

技术栈

1. vue-router

现在的页面基本上都是单应用,对于vue项目都是vuejs和vue-router结合形成的单页面应用。vue-router是vue官方指定的路由管理器,通过vue.js组合组件形成应用程序,vue-router将组件映射到浏览器的路由上,然后告诉vue-router在哪里渲染它。

在根目录下创建router文件夹(index.js),专门管理项目路由,main.js中引入vue-router。

index.js中,引入vue和vue-router,将vue-router全局引入,进行初始配置;就可以给首页写一个路由;在App.vue中放置路由容器<router-view></router-view>

设置路由跳转,使用编程式导航$router.push();在Main主页中使用嵌套路由,显示主体区域;

2. vuex

store文件夹中引入vue和vuex,创造新的vuex实例;把引入的vuex实例注入到根组件中;这里的vuex使用modules模块,包括tab和user模块;

tab模块中,state包括isCollapse、tabList、currentMenu、menu。mutations包括collapseMenu(将控制侧边栏缩放状态的数据取反)、selectMenu(改变tabList内容,点击菜单栏就多一个tab(且当前点击高亮))、closeTag(点击tag的关闭按钮,数据源state也随之改变,只能通过mutation来改变)。使用$store.state.xxx取数据,$store.commit()调用方法

3. axios

axios是基于promise的http库,有几个好处

  • 支持promiseAPI,可以避免回调地狱;
  • 可以在请求和响应前进行拦截;
  • 支持防御XSRF的攻击

npm安装axios;全局引入,axios不是插件,想在全局中使用,需要绑定在Vue的prototype属性上

调用class类:将接口请求都写在data.js中

4. element-ui

分为全局引入和按需引入,全局引入会下载很多不必要的组件,影响性能,真实项目中应该用按需引入。

5. 二次封装axios--接口请求的二次封装

需要将项目的配置逻辑添加到axios的请求中,通过配置来改变接口请求的地址,同时对所有接口进行监听,在请求前和请求后进行对应的拦截(在请求前添加统一的header,请求后捕获全局的catch,同时对异常的http状态进行处理)

将axios生成一个工具类,在api文件夹下新建axios.js文件;配置文件config中定义相关配置,baseUrl定义开发环境和生产环境

首先对当前环境变量进行判断;再写axios的工具类,class语法;getInsideConfig() 定义axios的相关配置;interceptors(instance)拦截器;request(options) 后续接口请求时,会调用此函数

总结:这只是简单封装,最主要的功能是将配置文件与axios进行结合

6. mock--后台接口的模拟数据

可以拦截ajax请求,并且在回调函数中通过自定义数据模板,或者说在回调函数中直接返回接口的响应数据,用于模拟后台返回的接口。

mock.js下对ajax请求进行拦截,mockServeData文件夹存放所有数据

7. echarts--数据的可视化展示

官方文档

需要为echarts准备一个定义了宽高的DOM;在script中调用echarts的init方法,传如DOM节点;指定图表的配置项和数据;

echarts也有全局引入和按需引入

8. 其他

  • less:npm下载less、以及less解析器less-loader

项目搭建

1.项目架构分析

image.png image.png image.png

2.项目模块搭建

3.脚手架搭建配置

yarn和npm的对比:

  • npm的坑:npm i时慢;包的版本号无法保持一致;安装时,所有包同时下载安装,导致包抛出错误时在终端发现不了
  • yarn的优点:速度快(并行安装,npm是按队列执行每个package);离线模式(有缓存);安装版本统一;更简洁的输出;多注册来源处理;语义化

基于上,选择用yarn包管理工具(实际上,就开始的时候用了,后面也没用啊?)

vue-cli4脚手架搭建

  • 首先需要nodejs环境(通过安装包安装nodejs环境时,会自带npm环境)
  • 安装cnpm和yarn:来替代npm进行包管理
  • 安装vue-cli脚手架:用cnpm来安装
  • 创建项目:vue create 项目名称;选择自己想要的插件(这里选默认的vue2+babel+eslint),此时项目基本文件和依赖项都生成和下载好了
  • 启动项目:npm run serve

4.组件初始化

5.路由初始化

6.vuex初始化

模块分配

1登录页

路由中定义登录页的路由;使用element-ui的form表单,对表单进行校验

2.后台首页

使用element-ui的Container布局;

侧边栏使用element-ui的NavMenu组件。因为每个页面都要用到侧边栏,故将其抽离成一个公共组件,定义在components文件夹下CommonAside.vue;添加数据,后面是用mock模拟的,对有子项目的菜单和无子项目的菜单分别使用计算属性;设置路由跳转,使用编程式导航,并且,在Main主页中使用嵌套路由,显示主体区域;

header部分也抽离成一个公共组件。左侧按钮的样式用element-ui的icon来实现,右侧下拉菜单用element-ui的dropdown。左侧按钮点击实现侧边栏收缩,而侧边栏收缩是通过iscollapse数据控制,用vuex来实现兄弟组件间的通信;

主体区域home页面 使用element-ui的Layout布局,card卡片显示登录信息(这里数据是写死的,要做优化),table组件;图表数据用mock模拟(getData()),这里echarts还封装了公共组件,我没有做

面包屑和tag区域:面包屑的数据用vuex存储;header区域使用element-ui的breadcrumb组件,在computed中将vuex属性注入;tag区域用element-ui的tag标签组件,将tag抽离成公共组件,对tag删除时,vuex中的数据源也要相应改变

3.用户管理页

顶部form表单区域,使用element-ui的form表单组件,封装CommonForm组件;当表单内容填写完后点击确认按钮,需要将数据上传,这里使用mock模拟拦截数据(createUser、updateUser分别处理数据);

table表格区域,封装成CommonTable组件,其中使用element-ui的table组件;注意父子组件之间的通信,用props和this.$emit;

4.分页处理

table下面使用element-ui的pagination组件

5.用户crud

addUser、editUser、delUser、getList、confirm(comfirm、delUser都会调用getList更新用户列表,tabledata存请求返回的数据)

6.路由守卫

什么时候校验token是否存在?在路由跳转的时候,可以用导航守卫来监听token是否存在,

7.权限管理

分为两个部分,第一个是登陆权限(在未登录的状态下通过路由不能访问到页面),第二个是菜单权限(在登陆权限的基础上加角色权限的验证,不同的用户看到不同的菜单)

登录机制:填写用户名和密码后,点击登录将用户名和密码传给后端,后端与数据库进行匹配,匹配后返回一个登录的凭证(token),前端将这个凭证缓存起来(cookie),登陆的时候将token传给后端验证,这样便建立起了登陆权限体系。

  • store中管理token,将token缓存的时候依赖这个js-cookie库;

  • 使用vue-router的动态添加路由的方式,将之前写死的路由全部替换;permission中用mock模拟了接口数据

  • click事件触发登录操作,调用写好的接口(getMenu),服务器返回token(Mock.Random生成的)(返回code=2000、data(menu、token、message:“获取成功”))

getMenu(this.form).then(({ data: res }) => {
  console.log(res, 'res')
  //根据返回的数据,来做对应的处理
  if (res.code === 20000) {
      //清空Menu,cookie也移除menu
      this.$store.commit('clearMenu')
      //将服务器返回的menu数据传入vuex管理的Menu,并将menu存入cookie
      this.$store.commit('setMenu', res.data.menu)
      //将服务器返回的token值存入cookie
      this.$store.commit('setToken', res.data.token)
      //路由的动态添加
      this.$store.commit('addMenu', this.$router)
      //进行页面跳转,跳到首页
      this.$router.push({ name: 'home' })
  } else {
      this.$message.warning(res.data.message)
  }
})
  • login设置权限,不同的用户对应不同的Menu(接口准备好了),vuex管理menu,侧边栏拿到menu的数据进行动态切换
  1. 登录权限
  • 导航守卫:在路由进行跳转的时候,通过导航守卫来实现路由的监听。此部分即实现登陆权限验证。
        router.beforeEach((to, from, next) => {
          store.commit('getToken')
          //拿到token
          const token = store.state.user.token
          //如果token不存在且要跳转的不是登录页,则让其返回登录页
          if (!token && to.name !== 'login') {
            next({ name: 'login' })
            //如果token存在且要跳转的是login,则直接进首页
          } else if (token && to.name === 'login') {
            next({ name: 'home' })
          }else {
            next()
          }
        })
  1. 菜单权限
  • 动态添加路由:addRoute()给main添加children(不同权限的用户拿到不同的children(也就是menu))
            if (!Cookie.get('menu')) {
                return
            }
            const menu = JSON.parse(Cookie.get('menu'))
            state.menu = menu
            const menuArray = []
            //相当于把侧边栏对应的页面都设置一个路由,添加完component属性。那么menuArray里面存入的就是路由的相关数据
            menu.forEach(item => {
                //有children的再进行一次遍历
                if (item.children) {
                    item.children = item.children.map(item => {
                        item.component = () => import(`../views/${item.url}`)
                        return item
                    })
                    menuArray.push(...item.children)
                } else {
                    item.component = () => import(`../views/${item.url}`)
                    menuArray.push(item)
                }
            });
            // 路由的动态添加。即给layout(也就是main页面)添加children。
            menuArray.forEach(item => {
                router.addRoute('Main', item)
            })
        }

后端路由:后端中,采用的是后端渲染模式,就是在他的router中接收到了哪一项就会去寻找当前项对应的controller层,去渲染相应的界面。这个过程要经过http请求

前端路由:接收不同的路由,然后匹配当前路由对应的组件。前端路由的使用可以构建单页面,就是匹配不同的路由去显示不同的组件,不用经http请求,也不用重新加载资源,大大减少了服务区的压力。

8.顶部导航菜单与左侧导航联动的面包屑

vuex实现组件间的相互通信,selectMenu(改变tabList内容,点击菜单栏就多一个tab(且当前点击高亮))、closeTag(点击tag的关闭按钮,数据源state也随之改变,只能通过mutation来改变)。

header组件中使用element-ui的面包屑,需要拿到vuex中的tabList数据(在computed属性中使用mapState将数据拿过来);

Aside组件中点击菜单就会触发selectMenu方法,改变tabList内容;

tag组件也需要拿到vuex中的tabList数据,跟header组件中一样,点击tag会触发使路由跳转的方法,点击tag的删除按钮会触发closeTag(随之改变tabList)且改变路由。

9.路由懒加载

//非懒加载的情况
import List from '@/components/list.vue'
const router = new VueRouter({
  routes: [
    { path: '/list', component: List }
  ]
})
//使用懒加载的情况。另外还有require等方法实现懒加载
const List = () => import('@/components/list.vue')
const router = new VueRouter({
  routes: [
    { path: '/list', component: List }
  ]
})
//可以再简写
component: () => import('@/components/list.vue')

实现原理(参考):

  • 前提:ES6动态地加载模块——import()。通过import()引用的子模块会被单独分离出来,打包成一个单独的文件Chunk
  • 无论使用函数声明还是函数表达式创建函数,函数被创建后并不会立即执行函数内部的代码,只有等到函数被调用之后,才执行内部的代码。只要将需要进行懒加载的子模块文件(children chunk)的引入语句(import())放到一个函数内部。然后在需要加载的时候再执行该函数。这样就可以实现懒加载(按需加载)。

注意:

component  (和 components)配置接收一个返回 Promise 组件的函数,Vue Router 只会在第一次进入页面时才会获取这个函数,然后使用缓存数据。”—— 这说明更好的利用了浏览器的缓存

10.axios二次封装

将项目的配置逻辑添加到axios的请求中,通过配置来改变接口请求的地址,可以在本地调试线上环境。

同时对所有接口进行监听,在请求前和请求后进行对应的拦截。比如说,在请求前添加统一的header,在请求后捕获全局的cache,对异常的http状态进行监听和处理。

# vue 线上环境和本地环境 对于axios中api的切换处理