iview-admin 學習 01 實現調用後臺接口登錄

題外:
在使用axios時,注意params和data兩者的區別。
params:是添加到url的請求字符串中的,用於get請求
data:是添加到請求體(body)中的, 用於post請求

1.修改跨域:config/index.js

baseUrl: {
    dev: 'http://localhost:8081/standard',//開發環境
    pro: ''//生產環境
  },

2.修改登錄名密碼:scr/api/user.js

//登錄
export const login = ({ userName, password }) => {
  const data = {
    loginName:userName,//賬號與數據庫保持一致 loginName
    password:password //密碼與數據庫保持一致 password
  }
  return axios.request({
    url: 'login',// 後臺登錄url
    params: data, //參數
    method: 'post'
  })
}

3.註釋掉mock測試數據:src/main.js(不註釋請求的是mock的測試數據而不是修改後路徑的)

	if (process.env.NODE_ENV !== 'production') require('@/mock')

4.store文件 登錄 -退出登錄-獲取用戶信息-存token
根據後臺返回的json數據,將src/store//module/user.js下的data.token改成data.jwtToken。
src/store/module/user.js下的handleLogin方法中commit(‘setToken’, data.jwtToken)修改

import {
  login,
  logout,
  getUserInfo,
  getMessage,
  getContentByMsgId,
  hasRead,
  removeReaded,
  restoreTrash,
  getUnreadCount
} from '@/api/user'
import { setToken, getToken, clearToken } from '@/libs/util'

import { initRouter, clearMenu } from '@/libs/router-util' // ①引入動態菜單渲染

export default {
  state: {
    userName: '',
    userId: '',
    avatarImgPath: '',
    token: getToken(),
    access: '',
    hasGetInfo: false,
    unreadCount: 0,
    messageUnreadList: [],
    messageReadedList: [],
    messageTrashList: [],
    messageContentStore: {}
  },
  mutations: {
   	//...省略
    //
    setAccess (state, access) {
      state.access = access||[]//chenlf state.access必須有值可以是空數組但是不能是undefined
    },
    //
    setToken (state, token) {
      state.token = "Bearer "+token //chenlf setToken裏面加了前綴,因此需要加前綴
      setToken(token)
    },
   //...省略
  },
  getters: {
 	//...省略
  },
  actions: {
    // 登錄
    handleLogin ({ commit }, { userName, password }) {
      userName = userName.trim()
      return new Promise((resolve, reject) => {
        login({
          userName,
          password
        }).then(res => {
          const data = res.data
          //commit('setToken', data.token)
          commit('setToken', data.data)//chenlf 後臺傳回來的token
          initRouter() //chenlf 初始化路由
          resolve()
        }).catch(err => {
          reject(err)
        })
      })
    },
    // 退出登錄
    handleLogOut ({ state, commit }) {
      // chenlf 調用後臺方法開啓以下代碼
     /* return new Promise((resolve, reject) => {
        logout(state.token).then(() => {
            commit('setToken', '')
            commit('setAccess', [])
          resolve()
        }).catch(err => {
          reject(err)
        })*/
          return new Promise((resolve, reject) => {
              commit('setToken', '')
              commit('setAccess', [])
              clearToken();//chenlf 清除token
              clearMenu();//chenlf 清除菜單
              resolve()

        // 如果你的退出登錄無需請求接口,則可以直接使用下面三行代碼而無需使用logout調用接口
        // commit('setToken', '')
        // commit('setAccess', [])
        // resolve()
      })
    },
    // 獲取用戶相關信息
    getUserInfo ({ state, commit }) {
      return new Promise((resolve, reject) => {
        try {
          getUserInfo(state.token).then(res => {
            //chenlf 設置用戶信息
            const data = res.data.data
            commit('setAvatar', "")
            commit('setUserName', data.name)
            commit('setUserId', data.id)
            //commit('setAccess', data.access)
            commit('setAccess', [])
            commit('setHasGetInfo', true)
            resolve(data)
          }).catch(err => {
            reject(err)
          })
        } catch (error) {
          reject(error)
        }
      })
    },
   //...省略
  }
}

5.重點 state.access必須有值不能是undefined可以是空數組
(4中有代碼)

	commit('setAccess', data.access)
	setAccess (state, access) {
      state.access = access||[]
    },

6.libs文件ajax請求配置:src/libs/axios.js

import axios from 'axios'
import store from '@/store'

import {getToken, clearToken} from './util'
import {clearMenu} from './router-util'

// import { Spin } from 'view-design'
const addErrorLog = errorInfo => {
  const { statusText, status, request: { responseURL } } = errorInfo
  let info = {
    type: 'ajax',
    code: status,
    mes: statusText,
    url: responseURL
  }
  if (!responseURL.includes('save_error_logger')) store.dispatch('addErrorLog', info)
}

class HttpRequest {
  constructor (baseUrl = baseURL) {
    this.baseUrl = baseUrl
    this.queue = {}
  }
  getInsideConfig (url) {
    const config = {
      baseURL: this.baseUrl,
      headers: {
        //'X-Requested-With': 'XMLHttpRequest',
        //'Content-Type': 'application/x-www-form-urlencoded' // 必須設置,否則post請求的時候 參數會變成一個對象
      }
    }
    //chenlf 帶上請求頭
    if(url!== 'login'){
      config.headers['Authorization'] = getToken()
    }
    return config
  }
  destroy (url) {
    delete this.queue[url]
    if (!Object.keys(this.queue).length) {
      // Spin.hide()
    }
  }
  interceptors (instance, url) {
    // 請求攔截
    instance.interceptors.request.use(config => {
      // 添加全局的loading...
      if (!Object.keys(this.queue).length) {
        // Spin.show() // 不建議開啓,因爲界面不友好
      }
      this.queue[url] = true
      return config
    }, error => {
      return Promise.reject(error)
    })
    // 響應攔截
    instance.interceptors.response.use(res => {
      this.destroy(url)
      const { data, status } = res
      return { data, status }
    }, error => {
      this.destroy(url)
      let errorInfo = error.response
      if (!errorInfo) {
        const { request: { statusText, status }, config } = JSON.parse(JSON.stringify(error))
        errorInfo = {
          statusText,
          status,
          request: { responseURL: config.url }
        }
      }
      if (errorInfo.status === 401){
        clearToken()//清空token
        clearMenu();//chenlf 清除菜單

        router.push({ name: '/login'  })
      }
      addErrorLog(errorInfo)
      return Promise.reject(error)
    })
  }
  request (options) {
    const instance = axios.create()
    //chenlf getInsideConfig 傳參
    options = Object.assign(this.getInsideConfig(options.url), options)
    this.interceptors(instance, options.url)
    return instance(options)
  }
}
export default HttpRequest

7.src/libs/util.js

	import Cookies from 'js-cookie'
// cookie保存的天數
import config from '@/config'
import { forEach, hasOneOf, objEqual } from '@/libs/tools'
const { title, cookieExpires, useI18n } = config

//chenlf token存儲在localstorage 的key
export const TOKEN_KEY = 'jwtToken'

// 設置token
export const setToken = (token) => {
  //Cookies.set(TOKEN_KEY, token, { expires: cookieExpires || 1 })
  //chenlf 設置token+前綴
  if (token && token.indexOf('Bearer') === -1)
    token = 'Bearer ' + token
    storageSave(TOKEN_KEY, token)
}

// 獲取token
export const getToken = () => {
  /*chenlf 獲取token*/
  const token = storageRead(TOKEN_KEY)
  if (token) return token
  else return false

}
// chenlf 清除token
export const clearToken = () => {
  localStorage.removeItem(TOKEN_KEY)
  //sessionStorage.removeItem(TOKEN_KEY)
}

//chenlf 儲存localStorage
export const storageSave = (key, value) => {
  localStorage.setItem(key, value)
  //sessionStorage.setItem(key, value)
}
//chenlf 讀取localStorage
export const storageRead = (key) => {
  return localStorage.getItem(key) || ''
  //return sessionStorage.getItem(key) || ''
}

export const hasChild = (item) => {
  return item.children && item.children.length !== 0
}

const showThisMenuEle = (item, access) => {
  if (item.meta && item.meta.access && item.meta.access.length) {
    if (hasOneOf(item.meta.access, access)) return true
    else return false
  } else return true
}
/**
 * @param {Array} list 通過路由列表得到菜單列表
 * @returns {Array}
 */
export const getMenuByRouter = (list, access) => {
  let res = []
  forEach(list, item => {
    if (!item.meta || (item.meta && !item.meta.hideInMenu)) {
      let obj = {
        icon: (item.meta && item.meta.icon) || '',
        name: item.name,
        meta: item.meta
      }
      if ((hasChild(item) || (item.meta && item.meta.showAlways)) && showThisMenuEle(item, access)) {
        obj.children = getMenuByRouter(item.children, access)
      }
      if (item.meta && item.meta.href) obj.href = item.meta.href
      if (showThisMenuEle(item, access)) res.push(obj)
    }
  })
  return res
}

/**
 * @param {Array} routeMetched 當前路由metched
 * @returns {Array}
 */
export const getBreadCrumbList = (route, homeRoute) => {
  let homeItem = { ...homeRoute, icon: homeRoute.meta.icon }
  let routeMetched = route.matched
  if (routeMetched.some(item => item.name === homeRoute.name)) return [homeItem]
  let res = routeMetched.filter(item => {
    return item.meta === undefined || !item.meta.hideInBread
  }).map(item => {
    let meta = { ...item.meta }
    if (meta.title && typeof meta.title === 'function') {
      meta.__titleIsFunction__ = true
      meta.title = meta.title(route)
    }
    let obj = {
      icon: (item.meta && item.meta.icon) || '',
      name: item.name,
      meta: meta
    }
    return obj
  })
  res = res.filter(item => {
    return !item.meta.hideInMenu
  })
  return [{ ...homeItem, to: homeRoute.path }, ...res]
}

export const getRouteTitleHandled = (route) => {
  let router = { ...route }
  let meta = { ...route.meta }
  let title = ''
  if (meta.title) {
    if (typeof meta.title === 'function') {
      meta.__titleIsFunction__ = true
      title = meta.title(router)
    } else title = meta.title
  }
  meta.title = title
  router.meta = meta
  return router
}

export const showTitle = (item, vm) => {
  let { title, __titleIsFunction__ } = item.meta
  if (!title) return
  if (useI18n) {
    if (title.includes('{{') && title.includes('}}') && useI18n) title = title.replace(/({{[\s\S]+?}})/, (m, str) => str.replace(/{{([\s\S]*)}}/, (m, _) => vm.$t(_.trim())))
    else if (__titleIsFunction__) title = item.meta.title
    else title = vm.$t(item.name)
  } else title = (item.meta && item.meta.title) || item.name
  return title
}

/**
 * @description 本地存儲和獲取標籤導航列表
 */
export const setTagNavListInLocalstorage = list => {
  localStorage.tagNaveList = JSON.stringify(list)
}
/**
 * @returns {Array} 其中的每個元素只包含路由原信息中的name, path, meta三項
 */
export const getTagNavListFromLocalstorage = () => {
  const list = localStorage.tagNaveList
  return list ? JSON.parse(list) : []
}

/**
 * @param {Array} routers 路由列表數組
 * @description 用於找到路由列表中name爲home的對象
 */
export const getHomeRoute = (routers, homeName = 'home') => {
  let i = -1
  let len = routers.length
  let homeRoute = {}
  while (++i < len) {
    let item = routers[i]
    if (item.children && item.children.length) {
      let res = getHomeRoute(item.children, homeName)
      if (res.name) return res
    } else {
      if (item.name === homeName) homeRoute = item
    }
  }
  return homeRoute
}

/**
 * @param {*} list 現有標籤導航列表
 * @param {*} newRoute 新添加的路由原信息對象
 * @description 如果該newRoute已經存在則不再添加
 */
export const getNewTagList = (list, newRoute) => {
  const { name, path, meta } = newRoute
  let newList = [...list]
  if (newList.findIndex(item => item.name === name) >= 0) return newList
  else newList.push({ name, path, meta })
  return newList
}

/**
 * @param {*} access 用戶權限數組,如 ['super_admin', 'admin']
 * @param {*} route 路由列表
 */
const hasAccess = (access, route) => {
  if (route.meta && route.meta.access) return hasOneOf(access, route.meta.access)
  else return true
}

/**
 * 權鑑
 * @param {*} name 即將跳轉的路由name
 * @param {*} access 用戶權限數組
 * @param {*} routes 路由列表
 * @description 用戶是否可跳轉到該頁
 */
export const canTurnTo = (name, access, routes) => {
  const routePermissionJudge = (list) => {
    return list.some(item => {
      if (item.children && item.children.length) {
        return routePermissionJudge(item.children)
      } else if (item.name === name) {
        return hasAccess(access, item)
      }
    })
  }

  return routePermissionJudge(routes)
}

/**
 * @param {String} url
 * @description 從URL中解析參數
 */
export const getParams = url => {
  const keyValueArr = url.split('?')[1].split('&')
  let paramObj = {}
  keyValueArr.forEach(item => {
    const keyValue = item.split('=')
    paramObj[keyValue[0]] = keyValue[1]
  })
  return paramObj
}

/**
 * @param {Array} list 標籤列表
 * @param {String} name 當前關閉的標籤的name
 */
export const getNextRoute = (list, route) => {
  let res = {}
  if (list.length === 2) {
    res = getHomeRoute(list)
  } else {
    const index = list.findIndex(item => routeEqual(item, route))
    if (index === list.length - 1) res = list[list.length - 2]
    else res = list[index + 1]
  }
  return res
}

/**
 * @param {Number} times 回調函數需要執行的次數
 * @param {Function} callback 回調函數
 */
export const doCustomTimes = (times, callback) => {
  let i = -1
  while (++i < times) {
    callback(i)
  }
}

/**
 * @param {Object} file 從上傳組件得到的文件對象
 * @returns {Promise} resolve參數是解析後的二維數組
 * @description 從Csv文件中解析出表格,解析成二維數組
 */
export const getArrayFromFile = (file) => {
  let nameSplit = file.name.split('.')
  let format = nameSplit[nameSplit.length - 1]
  return new Promise((resolve, reject) => {
    let reader = new FileReader()
    reader.readAsText(file) // 以文本格式讀取
    let arr = []
    reader.onload = function (evt) {
      let data = evt.target.result // 讀到的數據
      let pasteData = data.trim()
      arr = pasteData.split((/[\n\u0085\u2028\u2029]|\r\n?/g)).map(row => {
        return row.split('\t')
      }).map(item => {
        return item[0].split(',')
      })
      if (format === 'csv') resolve(arr)
      else reject(new Error('[Format Error]:你上傳的不是Csv文件'))
    }
  })
}

/**
 * @param {Array} array 表格數據二維數組
 * @returns {Object} { columns, tableData }
 * @description 從二維數組中獲取表頭和表格數據,將第一行作爲表頭,用於在iView的表格中展示數據
 */
export const getTableDataFromArray = (array) => {
  let columns = []
  let tableData = []
  if (array.length > 1) {
    let titles = array.shift()
    columns = titles.map(item => {
      return {
        title: item,
        key: item
      }
    })
    tableData = array.map(item => {
      let res = {}
      item.forEach((col, i) => {
        res[titles[i]] = col
      })
      return res
    })
  }
  return {
    columns,
    tableData
  }
}

export const findNodeUpper = (ele, tag) => {
  if (ele.parentNode) {
    if (ele.parentNode.tagName === tag.toUpperCase()) {
      return ele.parentNode
    } else {
      return findNodeUpper(ele.parentNode, tag)
    }
  }
}

export const findNodeUpperByClasses = (ele, classes) => {
  let parentNode = ele.parentNode
  if (parentNode) {
    let classList = parentNode.classList
    if (classList && classes.every(className => classList.contains(className))) {
      return parentNode
    } else {
      return findNodeUpperByClasses(parentNode, classes)
    }
  }
}

export const findNodeDownward = (ele, tag) => {
  const tagName = tag.toUpperCase()
  if (ele.childNodes.length) {
    let i = -1
    let len = ele.childNodes.length
    while (++i < len) {
      let child = ele.childNodes[i]
      if (child.tagName === tagName) return child
      else return findNodeDownward(child, tag)
    }
  }
}

export const showByAccess = (access, canViewAccess) => {
  return hasOneOf(canViewAccess, access)
}

/**
 * @description 根據name/params/query判斷兩個路由對象是否相等
 * @param {*} route1 路由對象
 * @param {*} route2 路由對象
 */
export const routeEqual = (route1, route2) => {
  const params1 = route1.params || {}
  const params2 = route2.params || {}
  const query1 = route1.query || {}
  const query2 = route2.query || {}
  return (route1.name === route2.name) && objEqual(params1, params2) && objEqual(query1, query2)
}

/**
 * 判斷打開的標籤列表裏是否已存在這個新添加的路由對象
 */
export const routeHasExist = (tagNavList, routeItem) => {
  let len = tagNavList.length
  let res = false
  doCustomTimes(len, (index) => {
    if (routeEqual(tagNavList[index], routeItem)) res = true
  })
  return res
}

export const localSave = (key, value) => {
  localStorage.setItem(key, value)
}

export const localRead = (key) => {
  return localStorage.getItem(key) || ''
}

// scrollTop animation
export const scrollTop = (el, from = 0, to, duration = 500, endCallback) => {
  if (!window.requestAnimationFrame) {
    window.requestAnimationFrame = (
      window.webkitRequestAnimationFrame ||
      window.mozRequestAnimationFrame ||
      window.msRequestAnimationFrame ||
      function (callback) {
        return window.setTimeout(callback, 1000 / 60)
      }
    )
  }
  const difference = Math.abs(from - to)
  const step = Math.ceil(difference / duration * 50)

  const scroll = (start, end, step) => {
    if (start === end) {
      endCallback && endCallback()
      return
    }

    let d = (start + step > end) ? end : start + step
    if (start > end) {
      d = (start - step < end) ? end : start - step
    }

    if (el === window) {
      window.scrollTo(d, d)
    } else {
      el.scrollTop = d
    }
    window.requestAnimationFrame(() => scroll(d, end, step))
  }
  scroll(from, to, step)
}

/**
 * @description 根據當前跳轉的路由設置顯示在瀏覽器標籤的title
 * @param {Object} routeItem 路由對象
 * @param {Object} vm Vue實例
 */
export const setTitle = (routeItem, vm) => {
  const handledRoute = getRouteTitleHandled(routeItem)
  const pageTitle = showTitle(handledRoute, vm)
  const resTitle = pageTitle ? `${title} - ${pageTitle}` : title
  window.document.title = resTitle
}

8.src/libs/axios.js 修改

	//引入
	import { setToken, getToken, clearToken } from '@/libs/util'
	// 響應攔截
    instance.interceptors.response.use(res => {
      this.destroy(url)
      const { data, status } = res
      return { data, status }
    }, error => {
      this.destroy(url)
      let { data, status } = error.response
      if (status === 401) {
        clearToken()
        router.push({
          name: 'login'
        })
      }
      return Promise.reject(error)
    })
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章