vue-admin-template后臺管理之權限篇

前言:

在開發(fā)后臺管理項目時珍策,多用戶多角色不同權限的場景可以說是非常普遍的。從零開始手寫一個后臺沮协,要考慮的東西很多谦铃,這里直接拿網上大家比較熟悉的vue-admin-template后臺模版來進行改造。
也可以看比較完整的前端開發(fā)http://www.reibang.com/p/12ef029e3ab2
對應視頻教程
先來看下vue-admin-template這個模版的代碼目錄結構

├── build                      # 構建相關
├── mock                       # 項目mock 模擬數(shù)據(jù)
├── public                     # 靜態(tài)資源
│   │── favicon.ico            # favicon圖標
│   └── index.html             # html模板
├── src                        # 源代碼
│   ├── api                    # 所有請求
│   ├── assets                 # 主題 字體等靜態(tài)資源
│   ├── components             # 全局公用組件
│   ├── icons                  # 項目所有 svg icons
│   ├── layout                 # 全局 layout
│   ├── router                 # 路由
│   ├── store                  # 全局 store管理
│   ├── styles                 # 全局樣式
│   ├── utils                  # 全局公用方法
│   ├── views                  # views 所有頁面
│   ├── App.vue                # 入口頁面
│   ├── main.js                # 入口文件 加載組件 初始化等
│   └── permission.js          # 權限管理
├── tests                      # 測試
├── .env.xxx                   # 環(huán)境變量配置
├── .eslintrc.js               # eslint 配置項
├── .babelrc                   # babel-loader 配置
├── .travis.yml                # 自動化CI配置
├── vue.config.js              # vue-cli 配置
├── postcss.config.js          # postcss 配置
└── package.json               # package.json

該模版使用了mock數(shù)據(jù)赞厕,可能對于不會mock的人有點生澀艳狐,比如作者本人
所以,我打算把mock刪了皿桑,自己模擬數(shù)據(jù)毫目,就是那種自己寫json數(shù)據(jù)蔬啡,寫死,后端接口好了镀虐,直接請求接口把這些假數(shù)據(jù)替換下來就ok了箱蟆。接下來的教程可以比較啰嗦,各位挑著看吧刮便,不喜勿噴空猜,畢竟不是專業(yè)做教程的。

第一步恨旱,我們先來看看最后的成果展示


login.png

image.png

image.png

image.png
image.png

image.png

image.png

image.png

image.png

比如我們用財務這個用戶去登錄一下后臺辈毯,(先給財務這個角色分配一下權限)

image.png

比如給他分配培訓認證全部的權限
image.png

然后財務管理員登錄后臺,只渲染所擁有的權限動態(tài)渲染了側邊欄。
image.png

補充一下:這里是按鈕權限的設置搜贤,全局配置一個指令谆沃,到時候
v-permission='['add']'就可以實現(xiàn)按鈕是否顯示。
image.png

基本上就是這么個東西仪芒,先添加菜單唁影,在新建一個角色,再給這個角色分配一下權限桌硫,然后在新建個用戶夭咬,賬號密碼配置一下,再給這個用戶分配一個角色铆隘,最后這個用戶登錄后臺卓舵,就展示所擁有的權限動態(tài)去渲染側邊欄

第二步 : 我們先來啟動一下vue-admin-template這個模版膀钠,安裝依賴 cnpm install啟動:npm run dev

image.png

image.png

可以看到掏湾,全是英文的,我是受不了肿嘲,(英文爛的一逼融击,看不懂),側邊欄是手寫的英文雳窟,刪掉就好了尊浪,element-ui 是英文的,這個要改一下配置封救。

修改路徑  src/main.js
// Vue.use(ElementUI, { locale })
// 如果想要中文版 element-ui拇涤,按如下方式聲明
Vue.use(ElementUI) 

這樣就好了
image.png

當然要先實現(xiàn)正常登錄,我這里用下真實的登錄接口誉结,修改一下src/api/user.js

image.png

.env.development
image.png

vue.config.js配置反向代理鹅士,然后重啟項目

image.png

箭頭標注的根據(jù)實際情況修改
image.png

這樣實際是觸發(fā)登錄接口了,為啥沒登錄進去呢惩坑,因為登錄成功后掉盅,會立即觸發(fā)getinfo 這個接口也拜,這個接口請求出錯,就會清除token且又回到登錄頁了趾痘。
所以接下來要完善getinfo這個接口慢哈,先來看下邏輯
1.執(zhí)行完登錄請求后,會走 permission.js中的邏輯

image.png

可以看到getinfo扼脐,所以就要完善getinfo這個接口岸军,同樣換成真實的。
image.png

store/modules/user.js根據(jù)實際情況修改下getinfo這個方法瓦侮,這里看自己公司要求艰赞,我們只是取到昵稱和頭像存起來,也可以在這個方法直接把該用戶所擁有的權限拿到并保存到vuex肚吏,建議在起一個接口方妖,模擬的話我先在這個方法里把路由信息寫死,然后在定一個存儲到vuex的方法

const getDefaultState = () => {
  return {
    token: getToken(),
    name: '',
    avatar: '',
    menus: "",//新增
  }
}

const mutations = {
  RESET_STATE: (state) => {
    Object.assign(state, getDefaultState())
  },
  SET_TOKEN: (state, token) => {
    state.token = token
  },
  SET_NAME: (state, name) => {
    state.name = name
  },
  SET_AVATAR: (state, avatar) => {
    state.avatar = avatar
  },
  // 新增
  SET_MENUS: (state, menus) => {
    state.menus = menus
  }
}


// get user info
  getInfo({ commit, state }) {
    return new Promise((resolve, reject) => {
      getInfo().then(response => {
        //用戶信息是根據(jù)token返回的罚攀,
        //我們把token放到header里自動帶過去了党觅,這里就把state.token刪掉了
        const { data } = response

        if (!data) {
          return reject('Verification failed, please Login again.')
        }

        const { nickname, avatar } = data
        // 模擬請求數(shù)據(jù)
        const menus = [
          {
            "path": "/system",
            "redirect": "/menu",
            "component": "Layout",
            "meta": {
              "title": "系統(tǒng)管理",
              "icon": "form"
            },
            "children": [{
              "path": "/menu",
              "name": "menu",
              "component": "menu/index",
              "meta": {
                "title": "菜單管理",
                "icon": "table",
              }
            },
            {
              "path": "/roles",
              "name": "roles",
              "component": "roles/index",
              "meta": {
                "title": "角色管理",
                "icon": "table",
              }
            },
            {
              "path": "/administrator",
              "name": "administrator",
              "component": "dashboard/index",
              "meta": {
                "title": "用戶管理",
                "icon": "table"
              }
            }
            ]
          }

        ]
        //如果需要404 頁面,請在此處添加
        menus.push({
          path: "/404",
          component: "404",
          hidden: true
        }, {
          path: "*",
          redirect: "/404",
          hidden: true
        })
        commit('SET_NAME', nickname)
        commit('SET_AVATAR', avatar)
        commit("SET_MENUS", menus) // 觸發(fā)vuex SET_MENUS 保存路由表到vuex
        resolve(data)
      }).catch(error => {
        reject(error)
      })
    })
  }

getters.js

const getters = {
  sidebar: state => state.app.sidebar,
  device: state => state.app.device,
  token: state => state.user.token,
  avatar: state => state.user.avatar,
  name: state => state.user.name,
  menus: state => state.user.menus //新增
}
export default getters

登錄成功后斋泄,可以看到vuex里已經把昵稱和頭像和路由表信息存進去了杯瞻。

image.png

下面把 router.js中的路由先刪掉,保留必要的路由炫掐。

export const constantRoutes = [
  {
    path: '/login',
    component: () => import('@/views/login/index'),
    hidden: true
  },
  {
    path: '/',
    component: Layout,
    redirect: '/dashboard',
    children: [{
      path: 'dashboard',
      name: 'Dashboard',
      component: () => import('@/views/dashboard/index'),
      meta: { title: 'Dashboard', icon: 'dashboard' }
    }]
  }
]
image.png

繼續(xù)魁莉,可以看到,后端返給我們的是 "component": "Layout",是一個字符串募胃,我們要把過濾下這段路由表信息旗唁,并且addrouter到路由里去,并全局掛載一個global.antRouter 痹束,渲染的時候會用到

先在router這個目錄下新建兩個js文件检疫,開發(fā)環(huán)境和生產環(huán)境導入組件的方式略有不同

_import_development.js

// 開發(fā)環(huán)境導入組件
module.exports = file => require('@/views/' + file + '.vue').default // vue-loader at least v13.0.0+

_import_production.js

// 生產環(huán)境導入組件
module.exports = file => () => import('@/views/' + file + '.vue')

然后在permission.js中引入剛創(chuàng)建的js文件

const _import = require('./router/_import_' + process.env.NODE_ENV) // 獲取組件的方法

permission.js改造成下面這樣,不明白的地方看注釋

import router from './router'
import store from './store'
import {
  Message
} from 'element-ui'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
import {
  getToken
} from '@/utils/auth' // get token from cookie
import getPageTitle from '@/utils/get-page-title'
import Layout from '@/layout'
const _import = require('./router/_import_' + process.env.NODE_ENV) // 獲取組件的方法

NProgress.configure({
  showSpinner: false
}) // NProgress Configuration

const whiteList = ['/login'] // no redirect whitelist

router.beforeEach(async (to, from, next) => {
  // start progress bar
  NProgress.start()

  // set page title
  document.title = getPageTitle(to.meta.title)

  // determine whether the user has logged in
  const hasToken = getToken()

  if (hasToken) {
    if (to.path === '/login') {
      // if is logged in, redirect to the home page
      next({
        path: '/'
      })
      NProgress.done()
    } else {
      const hasGetUserInfo = store.getters.name
      if (hasGetUserInfo) {
        next()
      } else {
        try {
          // get user info
          await store.dispatch('user/getInfo') // 請求獲取用戶信息
          if (store.getters.menus.length < 1) {
            global.antRouter = []
            next()
          }
          const menus = filterAsyncRouter(store.getters.menus) // 1.過濾路由
          console.log(menus);
          
          router.addRoutes(menus) // 2.動態(tài)添加路由
          global.antRouter = menus // 3.將路由數(shù)據(jù)傳遞給全局變量祷嘶,做側邊欄菜單渲染工作
          next({
            ...to,
            replace: true
          })
          // next()
        } catch (error) {
          // remove token and go to login page to re-login
          console.log(error);
          await store.dispatch('user/resetToken')
          Message.error(error || 'Has Error')
          next(`/login?redirect=${to.path}`)
          NProgress.done()
        }
      }
    }
  } else {
    /* has no token*/

    if (whiteList.indexOf(to.path) !== -1) {
      // in the free login whitelist, go directly
      next()
    } else {
      // other pages that do not have permission to access are redirected to the login page.
      next(`/login?redirect=${to.path}`)
      NProgress.done()
    }
  }
})

router.afterEach(() => {
  // finish progress bar
  NProgress.done()
})
// // 遍歷后臺傳來的路由字符串屎媳,轉換為組件對象
function filterAsyncRouter(asyncRouterMap) {
  const accessedRouters = asyncRouterMap.filter(route => {
    if (route.component) {
      if (route.component === 'Layout') {
        route.component = Layout
      } else {
        route.component = _import(route.component) // 導入組件
      }
    }
    if (route.children && route.children.length) {
      route.children = filterAsyncRouter(route.children)
    }
    return true
  })
  
  return accessedRouters
}

下面在改造下layout/components/Sidebar/index.vue

computed: {
    ...mapGetters([
      'sidebar'
    ]),
    routes() {
      //return this.$router.options.routes
      return this.$router.options.routes.concat(global.antRouter) //把路由concat進去
    },

這時候登錄成功會報錯


image.png

原因就是 沒有提前建好頁面。新建一下路由表內的幾個頁面就行了
然后就成功了 有木有B畚 剿牺!


image.png

下面我打算換成真實的接口數(shù)據(jù),真正實現(xiàn)開篇的成果展示环壤,下面的打算錄制成視頻,對應視頻教程钞诡,點我去看視頻

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
禁止轉載郑现,如需轉載請通過簡信或評論聯(lián)系作者湃崩。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市接箫,隨后出現(xiàn)的幾起案子攒读,更是在濱河造成了極大的恐慌,老刑警劉巖辛友,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件薄扁,死亡現(xiàn)場離奇詭異,居然都是意外死亡庆猫,警方通過查閱死者的電腦和手機十饥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門蚀之,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人日缨,你說我怎么就攤上這事∫纯矗” “怎么了匣距?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長哎壳。 經常有香客問我毅待,道長,這世上最難降的妖魔是什么归榕? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任尸红,我火速辦了婚禮,結果婚禮上蹲坷,老公的妹妹穿的比我還像新娘驶乾。我一直安慰自己,他們只是感情好循签,可當我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布级乐。 她就那樣靜靜地躺著,像睡著了一般县匠。 火紅的嫁衣襯著肌膚如雪风科。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天乞旦,我揣著相機與錄音贼穆,去河邊找鬼。 笑死兰粉,一個胖子當著我的面吹牛故痊,可吹牛的內容都是我干的。 我是一名探鬼主播玖姑,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼愕秫,長吁一口氣:“原來是場噩夢啊……” “哼慨菱!你這毒婦竟也來了?” 一聲冷哼從身側響起戴甩,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤符喝,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后甜孤,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體协饲,經...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年缴川,在試婚紗的時候發(fā)現(xiàn)自己被綠了茉稠。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡二跋,死狀恐怖战惊,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情扎即,我是刑警寧澤吞获,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站谚鄙,受9級特大地震影響各拷,放射性物質發(fā)生泄漏。R本人自食惡果不足惜闷营,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一烤黍、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧傻盟,春花似錦速蕊、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至诽表,卻和暖如春唉锌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背竿奏。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工袄简, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人泛啸。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓绿语,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子汞舱,可洞房花燭夜當晚...
    茶點故事閱讀 42,722評論 2 345