vue-element-admin擼後臺實踐(一)

wangle_style發表於2019-02-28

一個專案首先要解決的問題,同時也是核心的問題就是登陸和許可權的問題,vue-element-admin將路由和左邊側欄進行了繫結,所以我們要解決的就是根據不同的role,生成不同的路由,然後動態渲染不同的側邊欄,同時還要解決在後臺可以通過tree控制元件動態設定許可權的效果,所以,先擼一下整體實現思路:

首先要說一下,vue-element-admin是怎麼與後端進行互動的:使用者端有互動提交---->>呼叫api下的.js請求函式(有些請求函式我單獨放到service裡了)--->>呼叫untils下的request.js(封裝好的請求axiso)--->>獲取到後端返回的response--->>進行客戶端data渲染(後面我會細說一下request.js裡封裝的axiso)

1.登陸:當使用者完成登陸後,要實現兩個功能:一是:會向後端進行user_info的獲取,並把相關資訊寫入cookie中,以實現整個系統的一個角色繫結。二是進行頁面的跳轉

2.許可權:登陸後在permission.js中router.beforeEach方法中利用寫在請求頭裡的cookie向後端獲取menuList,然後利用vuex進行路由的拼接,最後通過router.addRouter掛載所計算好的路由並實現側邊欄的動態渲染

ok,下面擼一下具體的實現操作:

1,login頁面的click事件中寫:

handleLogin() {
      this.$refs.loginForm.validate(async valid => { //進行登陸驗證
        if (valid) { //驗證通過
          this.loading = true
          this.logins='登入中...'
          const { username, password } = this.loginForm
          try { //像後端獲取user_info並儲存到cookie中
            const res = await login({ user_name: username, passwd: password })
            setUserInfo(res.data) //user_info儲存到cookie中
            this.loading = false
            this.logins='登入'
            this.$router.push({ path: '/' }) //跳轉到首頁
          } catch (e) {
            this.loading = false
            this.logins='登入'  
          }
        } else {
          console.log('error submit!!')
          return false
        }
      })
    }

其中setUserInfo()方法在utils下的auth.js,其中這裡面可以寫一些cookie的相關函式,並export出去,以供複用


// 設定Role_id
export function setRoleId(roleId) {
  return Cookies.set(RoleKey, roleId)
}  //這裡只寫一個,其他的set方法都類似於此

// 儲存使用者資訊
export function setUserInfo(user) {
  const { role_id, agent_id, user_name,
    agent_name, province_id, level, price, city_name,
    city_ip
  } = user
  setRoleId(role_id)
  setAgentId(agent_id)
  setUserName(user_name)
  setAgentName(agent_name)
  setProvinceId(province_id)
  setLevel(level)
  setPrice(price / 100)
  setCityName(city_name)
  setCityIp(city_ip)
}

// 清除使用者資訊
export function removeUserInfo() {
  removeRoleId()
  removeAgentId()
  removeUserName()
  removeAgentName()
  removeProvinceId()
  removeLevel()
  removePrice()
  removeCityName()
  removeCityIp()
}

2.然後在在permission.js中router.beforeEach,一個是實現渲染側邊欄之前的一個token攔截,二是實現從後端獲取menuList以供拼接動態路由

router.beforeEach(async(to, from, next) => {
  NProgress.start() // start progress bar
  if (getToken()) { // determine if there has token
    if (to.path === '/login') {
      next({ path: '/' })
      NProgress.done() // if current page is dashboard will not trigger	afterEach hook, so manually handle it
    } else {
      if (!menuList) { //根據之前繫結的角色,通過cookie中的role向後端獲取menulist
        const res = await fetchMenuList()
        menuList = res.data
        store.dispatch('GenerateRoutes', menuList)  //利用vuex,在store中的premission.js中進行路由的拼接
      }
      next()
    }
  } else {
    /* has no token*/
    if (whiteList.indexOf(to.path) !== -1) { // 在免登入白名單,直接進入
      next()
    } else {
      next('/login') // 否則全部重定向到登入頁
      NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it
    }
  }
})

3.在store中的premission.js中進行路由的拼接,思路是在後端根據role獲取到id,然後通過獲取到的id與寫在route.index裡的動態路由表進行比對拼接,如果包含獲取到的id的話就將路由表中的hidden設定為false,否則設為true來控制側邊欄是否顯示這個menu

const checkedId = [] //設定一個陣列,用來儲存許可權獲取到的route的id
function getCheckedId(menulist) { //將根據角色從後端獲取到的id進行foreach後儲存到checkedId中
  menulist.forEach(item => {
    checkedId.push(item.id)
    if (item.childs.length) {
      getCheckedId(item.childs)
    }
  })
}

function findIdRoute(asyncRouterMap, id) { //通過比對checkedId和寫在route中的路由表的id來進行hidden引數的true/false編寫
  asyncRouterMap.forEach(item => {
    if (id === item.id) {
      item.hidden = false
    }
    if (item.children) {
      findIdRoute(item.children, id)
    }
  })
}

function getRoutes(asyncRouterMap) {  //呼叫findidroute函式
  checkedId.forEach(id => {
    findIdRoute(asyncRouterMap, +id)
  })
}

const permission = {  //vuex
  state: {
    routers: constantRouterMap,
    addRouters: []
  },
  mutations: {
    SET_ROUTERS: (state, routers) => { //將動態路由與靜態路由進行concat拼接,形成完整路由表
      state.addRouters = routers
      state.routers = constantRouterMap.concat(routers)
    }
  },
//路由拼接
actions: {
    GenerateRoutes({ commit }, menulist) {
      getCheckedId(menulist)
      getRoutes(asyncRouterMap)
      return new Promise(resolve => {
        const accessedRouters = asyncRouterMap
        commit('SET_ROUTERS', accessedRouters)
        resolve()
      })
    }
  }

ok至此,路由已經根據role拼接完畢,下面介紹下側邊欄如何去渲染出來,其原理就是遍歷之前算出來的permission_routers,然後通過vuex拿到之後動態v-for渲染而已

1:在sliderba的index中書寫如下

<sidebar-item v-for="route in permission_routers" :key="route.name" :item="route" :base-path="route.path"/>
<script>
import { mapGetters } from 'vuex'
import SidebarItem from './SidebarItem'

export default {
  components: { SidebarItem },
  computed: {
    ...mapGetters([
      'permission_routers',
      'sidebar'
    ]),
    isCollapse() {
      return !this.sidebar.opened
    }
  }
}
</script>

2.在sliderba的sidebaritem中書寫如下

hasOneShowingChild(children) {
      const showingChildren = children.filter(item => {
        if (item.hidden) {
          return false
        } else {
          // temp set(will be used if only has one showing child )
          this.onlyOneChild = item
          return true
        }
      })
      if (showingChildren.length === 1) {
        return true
      }
      return false
    },
    resolvePath(...paths) {
      return path.resolve(this.basePath, ...paths)
    }

下面講一下前端登出:登出需要做兩件事,一是清除token,一是將vuex管理的使用者資訊state清空

1.在view層面設定點選函式

//登出
logout() {
      this.$store.dispatch('FedLogOut').then(() => {
        location.reload()// In order to re-instantiate the vue-router object to avoid bugs
      })
    }

2.在store的user.js中配置user相關的state和書寫登出actions

import { getToken, removeToken, removeUserInfo } from '@/utils/auth' //用於獲取token和userinfo
import { logout } from '../../service/common' //用於後端登出請求

const user = {
  state: {
    token: getToken(),
    agent_id: '',
    agent_name: '',
    role_id: '', // 角色id
    role_name: '',
    user_name: '',
    user_id: ''
  },

  mutations: {
    SET_USERINFO: (state, user) => {
      state = {
        ...state,
        ...user
      }
      console.log(state)
    },
    CLEAR_USERINFO: (state) => {
      state.agent_id = ''
      state.agent_name = ''
      state.role_id = ''
      state.role_name = ''
      state.user_id = ''
      state.user_name = ''
    }
  },

  actions: {
    saveUserinfo({ commit, state }, user) {
      commit('SET_USERINFO', user)
    },
    // 後端登出
    LogOut({ commit, state }) {
      return new Promise((resolve, reject) => {
        logout().then(() => {
          commit('CLEAR_USERINFO')
          removeToken()
          resolve()
        }).catch(error => {
          reject(error)
        })
      })
    },
    // 前端 登出
    FedLogOut({ commit }) {
      return new Promise(resolve => {
        commit('CLEAR_USERINFO') //請求mutations
        removeToken() //清除token
        removeUserInfo() //清除cookes
        resolve()
      })
    }
  }
}

export default user

好了,這樣登陸,退出和許可權,以及動態路由已經實現,會繼續下更,如tree元件時間動態更改許可權,以及其他一些vue-element-admind自帶的元件的使用!

相關文章