手撸一个vue后台管理系统框架(一)路由+Tabs篇

本章技术栈:Vue-Router + VueX + Tabs + 动态菜单渲染

背景:上周写了一篇基于 Iframe实现单页面多tab切换界面无刷新 的功能,现在SPA盛行的时代,感觉Iframe实现SPA有点Low了(不过基于传统多页面实现SPA也是无奈之举),所以最近想着基于VUE实现多tab功能,随便也实现了菜单栏动态渲染、路由管理、状态管理等项目框架基础功能。这样看来,这都可以用作一般中小型web后台管理系统的框架基本骨架了。基于这套骨架后面我会持续增加登录(Token)、HTTP请求(axios)、用户、角色、权限管理等。还会封装一些分页、上传等组建,打造一款真正的开箱即用的后台管理系统框架,让开发者更多的关注项目需求,提高开发效率,节省开发成本。
先上效果图

读完这篇文章你能收获什么?
  1. 可以基于这套骨架搭建属于自己的后台管理系统框架
  2. 可以在自己的Vue项目中加入Tabs功能
  3. 了解Vue-Router、VueX基本用法
  4. 了解Element-ui中Menu、Tabs等组建基本用法
  5. 浏览器前进后退时监听路由变化,更新当前tabs(10月25日晚新增)
  6. 利用动态缓存指定组件(10月25日晚新增)

源码注释很详细,详情请戳:https://gitbook.cn/gitchat/activity/5db1a2469fc75b4c0af61ee4

核心代码:
  • 左侧菜单栏渲染:





  • 右侧Tabs组件:



  • VueX状态管理:
import Store from './store'
// 菜单列表,可通过后台返回,返回格式类似就行,还可增加icon图标等字段
const menumap = [
  { name: '首页', hasChilder: false, index: 'index', children: [] },
  { name: '菜单一', hasChilder: false, index: 'one', children: [] },
  { name: '菜单二', hasChilder: false, index: 'two', children: [] },
  {
    name: '菜单三',
    hasChilder: true,
    index: 'three',
    children: [
      { name: '子菜单3-1', hasChilder: false, index: 'three3-1' },
      { name: '子菜单3-2', hasChilder: false, index: 'three3-2' }
    ]
  },
  {
    name: '菜单四',
    hasChilder: true,
    index: 'four',
    children: [
      { name: '子菜单4-1', hasChilder: false, index: 'four4-1' },
      { name: '子菜单4-2', hasChilder: false, index: 'four4-2' }
    ]
  }
]

Store.registerModule('menu', {
  namespaced: true,
  state: {
    menu: [],
    // 默认tabs里面有‘首页’,且没有closable属性,不能删除
    tabs: [
      {
        label: '首页',
        index: 'index'
      }
    ],
    activeItem: 'index' // 默认选中首页
  },
  getters: {
  },
  mutations: {
    initMenu (state, menu) {
      state.menu = menu
    },
    initTabs (state, tabs) {
      state.tabs = tabs
    },
    addTab (state, tab) {
      state.tabs.push(tab)
    },
    switchTab (state, nowIndex) {
      state.activeItem = nowIndex
    }
  },
  actions: {
    getMenu (context) {
      context.commit('initMenu', menumap)
    },
    clickMenuItem (context, index) {
      if (index !== 'index') {
        var tab = context.state.tabs.find(f => f.index === index)
        if (!tab) {
          let menu = {}
          menu = context.state.menu.find(f => f.index === index)
          if (!menu) {
            menu = context.state.menu.map(a => a.children).flat().find(f => f.index === index)
          }
          let newTab = {
            label: menu.name,
            index: menu.index,
            closable: true
          }
          context.commit('addTab', newTab)
        }
      }
      context.commit('switchTab', index)
    },
    closeTab (context, index) {
      let indexNum = context.state.tabs.findIndex(f => f.index === index)
      let activeItem = context.state.activeItem
      let newTabs = context.state.tabs.filter(f => f.index !== index)
      context.commit('initTabs', newTabs)
      if (activeItem === index) {
        context.commit('switchTab', indexNum === 0 ? 'index' : newTabs[indexNum - 1].index)
      }
    }
  }
})
  • Vue-Router路由管理:
import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'home',
    redirect: 'index',
    component: () => import('../views/Home.vue'),
    children: [
      {
        path: 'index',
        name: 'index',
        component: () => import('../views/Index.vue')
      },
      {
        path: 'one',
        name: 'one',
        component: () => import('../views/One.vue')
      },
      {
        path: 'two',
        name: 'two',
        component: () => import('../views/Two.vue')
      },
      {
        path: 'three3-1',
        name: 'three3-1',
        component: () => import('../views/three/Three3-1.vue')
      },
      {
        path: 'three3-2',
        name: 'three3-2',
        component: () => import('../views/three/Three3-2.vue')
      },
      {
        path: 'four4-1',
        name: 'four4-1',
        component: () => import('../views/four/Four4-1.vue')
      },
      {
        path: 'four4-2',
        name: 'four4-2',
        component: () => import('../views/four/Four4-2.vue')
      }
    ]
  }
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push (location) {
  return originalPush.call(this, location).catch(err => err)
}
export default router
10月29日晚,更新以下内容:
  1. 增加登录界面,路由守卫,储存 token;
  2. 封装 Axios,实现http请求拦截和响应拦截;
  3. 加入highcharts和拖拽组件,实现推拽图表功能;
  4. 增加样式,美化界面;

整体效果图

目前整体功能包括:

1.登录(路由守卫);
2.封装 Axios 请求;
3.Tabs 动态管理,切换界面;
4.动态渲染菜单;
5.利用 ; 动态缓存指定组件;
6.Vue-Router 路由管理;
7.VueX 状态管理;
8.图表拖拽效果;

特点:结构简单,通用性强,适合用作中小型 Web 后台管理系统框架,开箱即用。

  • Axios封装
import axios from 'axios'
import { Message, Loading } from 'element-ui'
import router from '../router/index'

let loading // 定义loading变量

function startLoading () { // 使用Element loading-start 方法
  loading = Loading.service({
    lock: true,
    text: '加载中...',
    background: 'rgba(0, 0, 0, 0.7)'
  })
}
function endLoading () { // 使用Element loading-close 方法
  loading.close()
}

// 请求拦截  设置统一header
axios.interceptors.request.use(config => {
  // 加载
  startLoading()
  if (localStorage.eleToken) { config.headers.Authorization = localStorage.eleToken }
  return config
}, error => {
  return Promise.reject(error)
})

// 响应拦截  token过期处理
axios.interceptors.response.use(response => {
  endLoading()
  if (response.data.success) {
    return response.data.message
  } else {
    Message.error(response.data.message)
    return Promise.reject(response.data.message)
  }
}, error => {
  // 错误提醒
  endLoading()
  // Message.error(error.response.message)
  const { status } = error.response
  if (status === '401') {  //状态吗根据后台返回而定
    Message.error('token值无效,请重新登录')
    // 清除token
    localStorage.removeItem('eleToken')
    // 页面跳转
    router.push('/login')
  } else {
    Message.error('系统错误')
  }
  return Promise.reject(error)
})

export default axios

源码注释很详细,详情请戳:https://gitbook.cn/gitchat/activity/5db1a2469fc75b4c0af61ee4

未完待续......

欢迎交流,欢迎 Star (^_^)
经验总结,代码加工厂!

你可能感兴趣的:(tab,element-ui,vue.js,vuex,vue-router)