first commit
This commit is contained in:
9
src/utils/cache/index.js
vendored
Normal file
9
src/utils/cache/index.js
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
import { createWebStorage } from './webStorage'
|
||||
|
||||
export const createLocalStorage = function (option = {}) {
|
||||
return createWebStorage({ prefixKey: option.prefixKey || '', storage: localStorage })
|
||||
}
|
||||
|
||||
export const createSessionStorage = function (option = {}) {
|
||||
return createWebStorage({ prefixKey: option.prefixKey || '', storage: localStorage })
|
||||
}
|
55
src/utils/cache/webStorage.js
vendored
Normal file
55
src/utils/cache/webStorage.js
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
import { isNullOrUndef } from '@/utils/is'
|
||||
|
||||
class WebStorage {
|
||||
constructor(option) {
|
||||
this.storage = option.storage
|
||||
this.prefixKey = option.prefixKey
|
||||
}
|
||||
|
||||
getKey(key) {
|
||||
return `${this.prefixKey}${key}`.toUpperCase()
|
||||
}
|
||||
|
||||
set(key, value, expire) {
|
||||
const stringData = JSON.stringify({
|
||||
value,
|
||||
time: Date.now(),
|
||||
expire: !isNullOrUndef(expire) ? new Date().getTime() + expire * 1000 : null,
|
||||
})
|
||||
this.storage.setItem(this.getKey(key), stringData)
|
||||
}
|
||||
|
||||
get(key) {
|
||||
const { value } = this.getItem(key, {})
|
||||
return value
|
||||
}
|
||||
|
||||
getItem(key, def = null) {
|
||||
const val = this.storage.getItem(this.getKey(key))
|
||||
if (!val) return def
|
||||
try {
|
||||
const data = JSON.parse(val)
|
||||
const { value, time, expire } = data
|
||||
if (isNullOrUndef(expire) || expire > new Date().getTime()) {
|
||||
return { value, time }
|
||||
}
|
||||
this.remove(key)
|
||||
return def
|
||||
} catch (error) {
|
||||
this.remove(key)
|
||||
return def
|
||||
}
|
||||
}
|
||||
|
||||
remove(key) {
|
||||
this.storage.removeItem(this.getKey(key))
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.storage.clear()
|
||||
}
|
||||
}
|
||||
|
||||
export function createWebStorage({ prefixKey = '', storage = sessionStorage }) {
|
||||
return new WebStorage({ prefixKey, storage })
|
||||
}
|
13
src/utils/http/help.js
Normal file
13
src/utils/http/help.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
|
||||
const WITHOUT_TOKEN_API = [{ url: '/auth/login', method: 'POST' }]
|
||||
|
||||
export function isWithoutToken({ url, method = '' }) {
|
||||
return WITHOUT_TOKEN_API.some((item) => item.url === url && item.method === method.toUpperCase())
|
||||
}
|
||||
|
||||
export function addBaseParams(params) {
|
||||
if (!params.userId) {
|
||||
params.userId = useUserStore().userId
|
||||
}
|
||||
}
|
18
src/utils/http/index.js
Normal file
18
src/utils/http/index.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import axios from 'axios'
|
||||
import { setupInterceptor } from './interceptors'
|
||||
|
||||
function createAxios(option = {}) {
|
||||
const defBaseURL = window.__APP__GLOB__CONF__?.VITE_APP_GLOB_BASE_API || import.meta.env.VITE_APP_GLOB_BASE_API
|
||||
const service = axios.create({
|
||||
timeout: option.timeout || 120000,
|
||||
baseURL: option.baseURL || defBaseURL,
|
||||
})
|
||||
setupInterceptor(service)
|
||||
return service
|
||||
}
|
||||
|
||||
export const defAxios = createAxios()
|
||||
|
||||
export const mockAxios = createAxios({
|
||||
baseURL: window.__APP__GLOB__CONF__?.VITE_APP_GLOB_BASE_API_TEST || import.meta.env.VITE_APP_GLOB_BASE_API_TEST,
|
||||
})
|
82
src/utils/http/interceptors.js
Normal file
82
src/utils/http/interceptors.js
Normal file
@@ -0,0 +1,82 @@
|
||||
import { router } from '@/router'
|
||||
import { getToken, removeToken } from '@/utils/token'
|
||||
import { isWithoutToken } from './help'
|
||||
|
||||
export function setupInterceptor(service) {
|
||||
service.interceptors.request.use(
|
||||
async (config) => {
|
||||
// 防止缓存,给get请求加上时间戳
|
||||
if (config.method === 'get') {
|
||||
config.params = { ...config.params, t: new Date().getTime() }
|
||||
}
|
||||
|
||||
// 处理不需要token的请求
|
||||
if (isWithoutToken(config)) {
|
||||
return config
|
||||
}
|
||||
|
||||
const token = getToken()
|
||||
if (token) {
|
||||
/**
|
||||
* * jwt token
|
||||
* ! 认证方案: Bearer
|
||||
*/
|
||||
config.headers.Authorization = 'Bearer ' + token
|
||||
|
||||
return config
|
||||
}
|
||||
/**
|
||||
* * 未登录或者token过期的情况下
|
||||
* * 跳转登录页重新登录,携带当前路由及参数,登录成功会回到原来的页面
|
||||
*/
|
||||
const { currentRoute } = router
|
||||
router.replace({
|
||||
path: '/login',
|
||||
query: { ...currentRoute.query, redirect: currentRoute.path },
|
||||
})
|
||||
return Promise.reject({ code: '-1', message: '未登录' })
|
||||
},
|
||||
(error) => Promise.reject(error)
|
||||
)
|
||||
|
||||
service.interceptors.response.use(
|
||||
(response) => response?.data,
|
||||
(error) => {
|
||||
let { code, message } = error.response?.data
|
||||
return Promise.reject({ code, message })
|
||||
|
||||
/**
|
||||
* TODO 此处可以根据后端返回的错误码自定义框架层面的错误处理
|
||||
*/
|
||||
switch (code) {
|
||||
case 401:
|
||||
// 未登录(可能是token过期或者无效了)
|
||||
console.error(message)
|
||||
removeToken()
|
||||
const { currentRoute } = router
|
||||
router.replace({
|
||||
path: '/login',
|
||||
query: { ...currentRoute.query, redirect: currentRoute.path },
|
||||
})
|
||||
break
|
||||
case 403:
|
||||
// 没有权限
|
||||
console.error(message)
|
||||
break
|
||||
case 404:
|
||||
// 资源不存在
|
||||
console.error(message)
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
// 已知错误resolve,在业务代码中作提醒,未知错误reject,捕获错误统一提示接口异常(9000以上为业务类型错误,需要跟后端确定好)
|
||||
if ([401, 403, 404].includes(code) || code >= 9000) {
|
||||
return Promise.resolve({ code, message })
|
||||
} else {
|
||||
console.error('【err】' + error)
|
||||
return Promise.reject({ message: '接口异常,请稍后重试!' })
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
76
src/utils/index.js
Normal file
76
src/utils/index.js
Normal file
@@ -0,0 +1,76 @@
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
/**
|
||||
* @desc 格式化时间
|
||||
* @param {(Object|string|number)} time
|
||||
* @param {string} format
|
||||
* @returns {string | null}
|
||||
*/
|
||||
export function formatDateTime(time = undefined, format = 'YYYY-MM-DD HH:mm:ss') {
|
||||
return dayjs(time).format(format)
|
||||
}
|
||||
|
||||
export function formatDate(date = undefined, format = 'YYYY-MM-DD') {
|
||||
return formatDateTime(date, format)
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc 函数节流
|
||||
* @param {Function} fn
|
||||
* @param {Number} wait
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function throttle(fn, wait) {
|
||||
var context, args
|
||||
var previous = 0
|
||||
|
||||
return function () {
|
||||
var now = +new Date()
|
||||
context = this
|
||||
args = arguments
|
||||
if (now - previous > wait) {
|
||||
fn.apply(context, args)
|
||||
previous = now
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc 函数防抖
|
||||
* @param {Function} func
|
||||
* @param {number} wait
|
||||
* @param {boolean} immediate
|
||||
* @return {*}
|
||||
*/
|
||||
export function debounce(method, wait, immediate) {
|
||||
let timeout
|
||||
return function (...args) {
|
||||
let context = this
|
||||
if (timeout) {
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
// 立即执行需要两个条件,一是immediate为true,二是timeout未被赋值或被置为null
|
||||
if (immediate) {
|
||||
/**
|
||||
* 如果定时器不存在,则立即执行,并设置一个定时器,wait毫秒后将定时器置为null
|
||||
* 这样确保立即执行后wait毫秒内不会被再次触发
|
||||
*/
|
||||
let callNow = !timeout
|
||||
timeout = setTimeout(() => {
|
||||
timeout = null
|
||||
}, wait)
|
||||
if (callNow) {
|
||||
method.apply(context, args)
|
||||
}
|
||||
} else {
|
||||
// 如果immediate为false,则函数wait毫秒后执行
|
||||
timeout = setTimeout(() => {
|
||||
/**
|
||||
* args是一个类数组对象,所以使用fn.apply
|
||||
* 也可写作method.call(context, ...args)
|
||||
*/
|
||||
method.apply(context, args)
|
||||
}, wait)
|
||||
}
|
||||
}
|
||||
}
|
91
src/utils/is.js
Normal file
91
src/utils/is.js
Normal file
@@ -0,0 +1,91 @@
|
||||
const toString = Object.prototype.toString
|
||||
|
||||
export function is(val, type) {
|
||||
return toString.call(val) === `[object ${type}]`
|
||||
}
|
||||
|
||||
export function isDef(val) {
|
||||
return typeof val !== 'undefined'
|
||||
}
|
||||
|
||||
export function isUndef(val) {
|
||||
return typeof val === 'undefined'
|
||||
}
|
||||
|
||||
export function isNull(val) {
|
||||
return val === null
|
||||
}
|
||||
|
||||
export function isObject(val) {
|
||||
return !isNull(isNull) && is(val, 'Object')
|
||||
}
|
||||
|
||||
export function isArray(val) {
|
||||
return val && Array.isArray(val)
|
||||
}
|
||||
|
||||
export function isString(val) {
|
||||
return is(val, 'String')
|
||||
}
|
||||
|
||||
export function isNumber(val) {
|
||||
return is(val, 'Number')
|
||||
}
|
||||
|
||||
export function isBoolean(val) {
|
||||
return is(val, 'Boolean')
|
||||
}
|
||||
|
||||
export function isDate(val) {
|
||||
return is(val, 'Date')
|
||||
}
|
||||
|
||||
export function isRegExp(val) {
|
||||
return is(val, 'RegExp')
|
||||
}
|
||||
|
||||
export function isFunction(val) {
|
||||
return typeof val === 'function'
|
||||
}
|
||||
|
||||
export function isPromise(val) {
|
||||
return is(val, 'Promise') && isObject(val) && isFunction(val.then) && isFunction(val.catch)
|
||||
}
|
||||
|
||||
export function isElement(val) {
|
||||
return isObject(val) && !!val.tagName
|
||||
}
|
||||
|
||||
export function isWindow(val) {
|
||||
return typeof window !== 'undefined' && isDef(window) && is(val, 'Window')
|
||||
}
|
||||
|
||||
export function isNullOrUndef(val) {
|
||||
return isNull(val) || isUndef(val)
|
||||
}
|
||||
|
||||
export function isEmpty(val) {
|
||||
if (isArray(val) || isString(val)) {
|
||||
return val.length === 0
|
||||
}
|
||||
|
||||
if (val instanceof Map || val instanceof Set) {
|
||||
return val.size === 0
|
||||
}
|
||||
|
||||
if (isObject(val)) {
|
||||
return Object.keys(val).length === 0
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
export function isUrl(path) {
|
||||
const reg =
|
||||
/(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/
|
||||
return reg.test(path)
|
||||
}
|
||||
|
||||
export const isServer = typeof window === 'undefined'
|
||||
|
||||
export const isClient = !isServer
|
37
src/utils/token.js
Normal file
37
src/utils/token.js
Normal file
@@ -0,0 +1,37 @@
|
||||
import { createLocalStorage } from './cache'
|
||||
import { refreshToken } from '@/api/auth'
|
||||
|
||||
const TOKEN_CODE = 'access_token'
|
||||
const DURATION = 6 * 60 * 60
|
||||
|
||||
export const lsToken = createLocalStorage()
|
||||
|
||||
export function getToken() {
|
||||
return lsToken.get(TOKEN_CODE)
|
||||
}
|
||||
|
||||
export function setToken(token) {
|
||||
lsToken.set(TOKEN_CODE, token, DURATION)
|
||||
}
|
||||
|
||||
export function removeToken() {
|
||||
lsToken.remove(TOKEN_CODE)
|
||||
}
|
||||
|
||||
export async function refreshAccessToken() {
|
||||
const tokenItem = lsToken.getItem(TOKEN_CODE)
|
||||
if (!tokenItem) {
|
||||
return
|
||||
}
|
||||
const { time } = tokenItem
|
||||
if (new Date().getTime() - time > 1000 * 60 * 30) {
|
||||
try {
|
||||
const res = await refreshToken()
|
||||
if (res.code === 0) {
|
||||
setToken(res.data.token)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user