正確姿式開發vue後臺管理系統

項目地址 vue-admin-webapp 歡迎star,forkcss

前言

相信許多人和我同樣剛接觸 vue 時看文檔都很枯燥,看完 vue,還有 vueRouter 、vuex 、vue-cli、es6 (學不動了。。。 ) 對於看完教程以後又遲遲不能上手實際項目,只能寫一些簡單的小demo,這確定和實際生產工做是有出入的,因而乎我就打算本身從零開始使用最新的技術棧搭建一個vue後臺管理系統,依此加深對理論知識的學習,並加強本身的項目能力,因此但願本系列教程對你開發vue項目有所幫助。html

1.項目基本簡介

vue-admin-webapp 是一個後臺管理 spa 頁面,它基於 vueelement-ui 採用了最新的前端技術棧,實現了登陸權限驗證,動態路由生成,並使用 easy-mock 來模擬請求數據,實現了典型的業務模型案例,它能夠幫你快速搭建後臺管理系統模板,並根據實際的業務需求添加路由來實現企業級管理頁面,相信本項目必定能幫助到你。前端

- 在線預覽-githubvue

- 在線預覽-gitee (推薦國內用戶)node

目前版本基於 webpack 4.0+vue-cli 3.x 版本構建,須要 Node.js 8.9或更高版本(推薦8.11.0+),相關知識能夠自行進官網進行了解android

功能

- 登陸 / 註銷
  - 登陸仿GeeTest-極驗安全策略
  
- 頁面
  - 初次進入引導用戶
  - sideBar收縮和展開
  - 全屏控制
  
- 側邊欄
  - 根據不一樣用戶權限展現相應的動態左側菜單
  
- 權限驗證
  - 管理員頁面
  - 權限設置
  
- 表格操做
  - 涉及日常業務遇到的相關表格操做(參考)
  
- Excel
 - Excel導出
 - Excel導入
 - 多級表頭導出
 
- Echarts
 - 滑動顯示更多數據
 - 動態切換charts
 - map地圖使用
 
- Icons
 - element-icon
 - 阿里iconfont
 
複製代碼

準備工做

在開始以前,請確保在本地安裝 node 和 webpack 及 git。 本項目涉及的技術棧主要有 ES6vuevuexvue-routervue-cliaxioswebpackelement-uieasyMock ,因此你最好提早熟悉瞭解這些知識,這將對你認識學習該項目有很大幫助webpack

目錄結構

下面是整個項目的目錄結構ios

├── public                     # 靜態資源
│   ├── favicon.ico            # favicon圖標
│   └── index.html             # html模板
├── src                        # 源代碼
│   ├── api                    # 全部請求
│   ├── assets                 # 圖片、字體等靜態資源
│   ├── components             # 全局公用組件
│   ├── layout                 # 頁面總體佈局盒子
│   ├── mixins                 # 全局混入模塊
│   ├── plugins                # 全局插件部分
│   ├── router                 # 路由
│   ├── store                  # 全局store管理
│   ├── style                  # 全局樣式
│   ├── utils                  # 全局公用方法
│   ├── vendor                 # 公用vendor(excel導入導出)
│   ├── views                  # views全部頁面
│   ├── App.vue                # 入口頁面
│   ├── main.js                # 入口文件 加載組件 初始化等
├── .borwserslistrc            # 瀏覽器兼容相關
├── .env.xxx                   # 環境變量配置 
├── .eslintrc.js               # eslint 配置項
├── .gitignore.js              # git忽略文件設置
├── .babelrc.config.js         # babel-loader 配置
├── package.json               # package.json
├── postcss.config.js          # postcss 配置
└── vue.config.js              # vue-cli 配置

複製代碼

安裝

# 克隆項目
git clone git@github.com:gcddblue/vue-admin-webapp.git

# 進入項目目錄
cd vue-admin-webapp

# 安裝依賴
npm install

# 啓動服務
npm run serve
複製代碼

啓動完成後將打開瀏覽器訪問 http://localhost:8080,接下來你就能夠根據本身的實際需求,能夠添加或修改路由,編寫本身的業務代碼。git

2.頁面架構

除去登陸頁外,整個頁面架構由三個部分組成 頭部 側邊欄 右側內容頁 在項目@/layout/index.js文件中對對這三個組件進行封裝,經過點擊左側菜單切換右側router-view 的路由更替,對應的項目文件以下es6

3.axios封裝

在vue項目中,和後臺進行請求交互這塊,咱們一般都會選擇axios庫,它是基於promise的http庫,可運行在瀏覽器端和node.js中。在本項目中主要實現了請求和響應攔截,get,post請求封裝。

配置不一樣環境

經過在項目中建立不一樣環境的文件,我這裏只建立了開發和生產環境的,固然,你也能夠建立基於測試的.env.test 等文件,以.env.production 爲例:

ENV = 'production'
# base api
VUE_APP_BASE_API = 'https://www.easy-mock.com/mock/5cee951f11690b5261b75566/admin'
複製代碼

只要以 VUE_APP_ 開頭的變量都會被 webpack.DefinePlugin 靜態嵌入到客戶端的包中。你能夠在應用的代碼中這樣訪問它們,例如我在@/api/index.js中初始化axios:

const $axios = axios.create({
  timeout: 30000,
  // 基礎url,會在請求url中自動添加前置連接
  baseURL: process.env.VUE_APP_BASE_API
})
複製代碼

經過建立api文件夾將全部接口都集中在這個文件夾中,根據不一樣的業務建立不一樣js文件,來更好的劃分接口的功能,其中index.js中代碼以下:

import axios from 'axios'
import Qs from 'qs' // 處理post請求數據格式
import store from '@/store'
import router from '@/router'
import Vue from 'vue'
import { Loading, Message } from 'element-ui' // 引用element-ui的加載和消息提示組件

const $axios = axios.create({
  // 設置超時時間
  timeout: 30000,
  // 基礎url,會在請求url中自動添加前置連接
  baseURL: process.env.VUE_APP_BASE_API
})
Vue.prototype.$http = axios // 這裏併發請求以便在組件使用this.$http.all(),具體看dashborad頁面

// 在全局請求和響應攔截器中添加請求狀態
let loading = null

/**
 * 請求攔截器
 * 用於處理請求前添加loading、判斷是否已保存token,並在每次請求頭部添加token
 */
$axios.interceptors.request.use(
  config => {
    loading = Loading.service({ text: '拼命加載中' })
    const token = store.getters.token
    if (token) {
      config.headers.Authorization = token // 請求頭部添加token
    }
    return config
  },
  error => {
    return Promise.reject(error)
  }
)
/**
 * 響應攔截器
 * 用於處理loading狀態關閉、請求成功回調、響應錯誤處理
 */
$axios.interceptors.response.use(
  response => {
    if (loading) {
      loading.close()
    }
    const code = response.status
    // 請求成功返回response.data
    if ((code >= 200 && code < 300) || code === 304) {
      return Promise.resolve(response.data)
    } else {
      return Promise.reject(response)
    }
  },
  error => {
    if (loading) {
      loading.close()
    }
    console.log(error)
    if (error.response) {
      switch (error.response.status) {
        case 401:
          // 返回401 清除token信息並跳轉到登錄頁面
          store.commit('DEL_TOKEN')
          router.replace({
            path: '/login',
            query: {
              redirect: router.currentRoute.fullPath
            }
          })
          break
        case 404:
          Message.error('網絡請求不存在')
          break
        default:
          Message.error(error.response.data.message)
      }
    } else {
      // 請求超時或者網絡有問題
      if (error.message.includes('timeout')) {
        Message.error('請求超時!請檢查網絡是否正常')
      } else {
        Message.error('請求失敗,請檢查網絡是否已鏈接')
      }
    }
    return Promise.reject(error)
  }
)

// get,post請求方法
export default {
  post(url, data) {
    return $axios({
      method: 'post',
      url,
      data: Qs.stringify(data),
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
      }
    })
  },
  get(url, params) {
    return $axios({
      method: 'get',
      url,
      params
    })
  }
}

複製代碼

如上,你們能夠看個人註釋說明,axios配置的封裝是整個項目中很重要的模塊,其實在不一樣的項目中,axios封裝都大同小異,因此,只要掌握了一種技巧,下次開發新項目也就很容易完成封裝這塊。

4.權限驗證及側邊欄

路由

路由是組織一個vue項目的關鍵,在對項目原型分析後,接下來的第一步就是編寫路由,本項目中,主要分爲兩種路由,currencyRoutesasyncRoutes

currencyRoutes:表明通用路由,意思就是不須要權限判斷,不一樣角色用戶都顯示的頁面,如:登錄頁、404等

asyncRoutes: 表明動態路由,須要經過判斷權限動態分配的頁面,有關的權限判斷的方法接下來會介紹。

路由相關配置說明:

/**
 * 路由相關屬性說明
 * hidden: 當設置hidden爲true時,意思不在sideBars側邊欄中顯示
 * mete{
 * title: xxx,  設置sideBars側邊欄名稱
 * icon: xxx,  設置ideBars側邊欄圖標
 * noCache: true  當設置爲true時不緩存該路由頁面
 * }
 */
複製代碼

權限驗證動態添加側邊欄

本項目經過路由聯動更新側邊欄,全部側邊欄配置都是在前端完成的,經過訪問接口,後端會返回一個權限相關的list數組,其中數組值爲路由的name屬性值,前端經過遞歸遍歷asyncRoutes判斷權限list中是否包含有對應的name路由,最終會返回包含該用戶角色全部權限路由頁面的addRoutes的數組對象。

具體實現是在路由index.js中設置一個全局前置導航守衛,具體判斷流程以下:

// 導航守衛
router.beforeEach(async (to, from, next) => {
  document.title = getTitle(to.meta.title)
  if (to.path === '/login') {
    next()
  } else {
    if (store.getters.token) {
      const hasRoles = store.getters.roles.length > 0
      if (hasRoles) {
        next()
      } else {
        try {
          const { roles } = await store.dispatch('user/_getInfo')
          const addRoutes = await store.dispatch(
            'permission/getAsyncRoutes',
            roles
          )
          router.addRoutes(addRoutes)
          // hack method to ensure that addRoutes is complete
          // set the replace: true, so the navigation will not leave a history record
          next({ ...to, replace: true })
        } catch (error) {
          Message.error(error)
        }
      }
    } else {
      next({
        path: '/login',
        query: {
          redirect: to.fullPath
        }
      })
    }
  }
})
複製代碼

這裏我在經過addRoutes添加路由時,遇到一個bug,當切換角色時,並不能刪除以前添加動態路由,因此這邊從新初始化router.matcher的屬性方式實現:

const creatRouter = () => {
  return new Router({
    routes: currencyRoutes,
    scrollBehavior() {
      return { x: 0, y: 0 }
    }
  })
}
const router = creatRouter()

// 解決addRoute不能刪除動態路由問題
export function resetRouter() {
  const reset = creatRouter()
  router.matcher = reset.matcher
}
複製代碼

當我每次退出登陸的時候執行resetRouter方法來初始化router對象,實現刪除以前動態添加的路由。

最後經過element-ui的el-menu組件來遞歸遍歷路由對象加載側邊欄。

5.Mock數據

身爲前端開發人員,相信你們都知道Mock數據吧,它的做用主要就是僞造假數據使團隊能夠並行開發,本項目使用了 easy-mock 來實現接口數據的請求,你們能夠去官網看下簡單教程,easy-mock 它的好處就是不用像傳統mock數據那樣須要在項目中建立mock文件夾並攔截ajax來實現假數據請求,它是真真實實的api請求,並容許任何跨域請求,下面是本項目全部接口

其中全部接口經過建立 _res 字段來判斷請求是否含有Authorzation頭部字段是否含有token來判斷用戶是不是登錄狀態,以下 getCardsData接口的配置:

{
  code: 0,
  data: {
    vistors: '@integer(10000, 100000)',
    message: '@integer(100, 1000)',
    order: '@integer(0, 1000)',
    profit: '@integer(1000, 100000)'
  },
  _res: function({
    _req,
    Mock
  }) {
    if (!_req.header.authorization) {
      return {
        status: 401,
        data: {
          msg: '未受權'
        }
      }
    } else {
      return {
        status: 200
      }
    }
  }
}
複製代碼

mock數據在項目開發中可以起到推動項目進度的功效,你們能夠預先和後端人員商量好,並先拿到假數據字段,而後mock本身的假數據,這樣你就能夠不用等後端人員開發接口而使項目卡住。通常在項目中,建立.env.development.env.production 文件,表明了開發和生產環境,在文件裏能夠定義不一樣環境接口的請求url

# base api
VUE_APP_BASE_API = 'https://www.easy-mock.com/mock/5cee951f11690b5261b75566/admin'
複製代碼

在封裝axios這樣初始化

const $axios = axios.create({
  // 設置超時時間
  timeout: 30000,
  // 基礎url,會在請求url中自動添加前置連接
  baseURL: process.env.VUE_APP_BASE_API
})
複製代碼

這樣就能夠自動根據不一樣的環境切換請求地址,不用咱們一個一個的修改每個請求接口

6.登陸

經過將登陸函數封裝在store中,當點擊登錄時,調用this.$store.dispatch('user/_login', this.ruleForm) 這個action方法,當後臺接口驗證成功時,會返回 token 字段,前端會調用 localStroage 接口將這個 token 保存在本地,之後每次請求前經過攔截器將這個token保存在 Authorization 這個頭部字段中,後臺只要驗證這個token就知道這個用戶的信息了。還不僅token的同窗,能夠 瘋狂點擊token說明 裏面對http爲何要添加toekn及token介紹的都很詳細。

這裏我還採用了仿 geetest 行爲驗證,經過滑動圖片來驗證真人操做,其中原理利用 h5 canves繪製功能,繪製底部圖片和滑塊圖片,而後監聽mouseMove事件,當滑動block摳出的圖片和初始化圖片的y座標差小於10時觸發驗證成功函數。

七、優化及小技巧

巧用Mixins

若是你的多個組件都用到一個或多個方法,咱們能夠不用每次都粘貼複製,這樣豈不是很low,咱們能夠將這些方法封裝在一個js文件中,當個人某個組件須要調用這個方法時

import aMixin from '@/mixins/a-mixin'
export default {
  name: 'page1',
  mixins: [newsMixin]  //調用mixins屬性,將aMixin這個模塊的數據及方法等都添加進這個組建吧
}
複製代碼

mixins的使用使用規則

Object.freeze方法

這個方法有什麼用呢,它主要是能夠將一個對象凍結,防止對象被修改,那這個對vue項目有什麼優化做用呢,你們都知道vue採用了數據劫持的方式遍歷數據對象,把這些屬性轉爲getter、settter方法來監聽並通知數據的變化,因此當你遇到一個巨大的數組或者對象,而且肯定數據不會修改,這時就可使用 Object.freeze() 方法來組織vue對這個巨大數據的轉化,,這可讓性能獲得很大的提高,舉個例子:

new Vue({
    data: {
        // vue不會對list裏的object作getter、setter綁定
        list: Object.freeze([
            { value: 1 },
            { value: 2 }
        ])
    },
    mounted () {
        // 界面不會有響應
        this.list[0].value = 100;

        // 下面兩種作法,界面都會響應
        this.list = [
            { value: 100 },
            { value: 200 }
        ];
        this.list = Object.freeze([
            { value: 100 },
            { value: 200 }
        ]);
    }
})
複製代碼

自動化導入模塊

當咱們某個組件或js文件須要引入多個模塊時,通常作法就是,import每一個模塊,這樣顯然是至關繁瑣的,這時 require.context 函數將派上用場,那個這個函數到底怎麼用呢,這裏官法介紹是 主要用來實現自動化導入模塊,在前端工程中,若是遇到從一個文件夾引入不少模塊的狀況,可使用這個api,它會遍歷文件夾中的指定文件,而後自動導入,使得不須要每次顯式的調用import導入模塊

require.context 函數接受三個參數:

  1. directory {String} -讀取文件的路徑
  2. useSubdirectories {Boolean} -是否遍歷文件的子目錄
  3. gExp {RegExp} -匹配文件的正則

require.context('./test', false, /.test.js$/)
#上面的代碼遍歷當前目錄下的test文件夾的全部.test.js結尾的文件,不遍歷子目錄
複製代碼

require.context 函數執行後返回一個函數,而且這個函數包含了三個屬性:

  1. resolve {Function} -接受一個參數request,request爲test文件夾下面匹配文件的相對路徑,返回這個匹配文件相對於整個工程的相對路徑
  2. keys {Function} -返回匹配成功模塊的名字組成的數組
  3. id {String} -執行環境的id,返回的是一個字符串

咱們常會遍歷keys返回的數組來對路徑進行處理,這是至關方便的,最後 require.context 返回的函數接受keys放回數組中的路徑成員做爲參數,並返回這個路徑文件的模塊

下面是我使用 require.context 函數動態生成moudles對象

import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'
const path = require('path')

Vue.use(Vuex)

const files = require.context('./modules', false, /\.js$/)
let modules = {}
files.keys().forEach(key => {
  let name = path.basename(key, '.js')
  modules[name] = files(key).default || files(key)
})
const store = new Vuex.Store({
  modules,
  getters
})
export default store
複製代碼

cdn引入

對於一些不常改動的模塊庫,例如: vue vueRouter vuex echarts element-ui 等, 咱們讓 webpack 不將他們進行打包,而是經過 cdn 引入,這樣就能夠減小代碼大小,減小服務器帶寬,並經過cdn將它們緩存起來,提升網站性能 。

具體實現就是修改 vue.config.js ,爲對象模塊添加 externals 完整配置以下:

const cdn = {
	css: [
		// element-ui css
   'https://unpkg.com/element-ui/lib/theme-chalk/index.css'
	],
	js: [
		// vue
    'https://unpkg.com/vue/2.5.22/vue.min.js',
    // element-ui
    'https://unpkg.com/element-ui/lib/index.js',
    // vue
    'https://unpkg.com/vuex/3.1.0/vuex.min.js'
	]
}
# 不打包vue、element-ui、vuex
module.exports = {
	externals: {
		vue: 'Vue',
  	'element-ui':'ELEMENT',
  	vuex: 'Vuex'
  },
  chainWebpack: config => {
  	config.plugin('html')
        .tap(args => {
          args[0].cdn = cdn
          return args
        })
  }
}
複製代碼

接下來修改 index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <% if (process.env.NODE_ENV === 'production') { %>
    	<!-- 引入樣式 -->
      <% for(var css of htmlWebpackPlugin.options.cdn.css) { %>
        <link rel="stylesheet" href="<%=css%>">
      <% } %>
      <!-- 引入js -->
      <% for(var js of htmlWebpackPlugin.options.cdn.js) { %>
        <script src="<%=js%>"></script>
      <% } %>      
    <% } %>
    <title>vue-admin-webapp</title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but vue-admin-webapp doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>
複製代碼

好了,大公告成

vue cli3多頁面配置

能夠關注個人另外一篇文章 正確姿式使用vue cli3配置多頁項目

vue cli3初始化項目

能夠關注個人另外一篇文章 正確姿式使用vue cli3建立項目

總結

這個項目是我在上班之餘斷斷續續開發的,沒太寫過技術貼,文筆和邏輯組織能力仍是至關差的,你們見諒。起初在沒開始作以前以爲應該至關的順利的,沒想到真正一步一步實現時,仍是和本身最初設想是有出入的,期間遇到了很多的bug,大多都是由於細節不注意,也讓我更加體會 好記性不如爛筆頭 這句話,實踐纔是真理啊,多動手,多探索。最後我會考慮使用 uni-app 這個框架來開發多平臺(小程序、android、ios、h5)移動版vue後臺管理系統,期待吧...

相關文章
相關標籤/搜索