題外:
在使用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)
})