1
0
mirror of https://github.com/zclzone/vue-naive-admin.git synced 2026-01-22 15:40:21 +08:00
This commit is contained in:
zclzone
2023-12-07 21:55:23 +08:00
commit cfeb813b62
401 changed files with 11125 additions and 0 deletions

107
src/utils/common.js Normal file
View File

@@ -0,0 +1,107 @@
/**********************************
* @FilePath: common.js
* @Author: Ronnie Zhang
* @LastEditor: Ronnie Zhang
* @LastEditTime: 2023/12/04 22:45:46
* @Email: zclzone@outlook.com
* Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
**********************************/
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)
}
}
}
/**
* @desc 睡一会儿,让子弹暂停一下
* @param {number} time 毫秒数
* @returns
*/
export function sleep(time) {
return new Promise((resolve) => setTimeout(resolve, time))
}
/**
* @param {HTMLElement} el
* @param {Function} cb
* @return {ResizeObserver}
*/
export function useResize(el, cb) {
const observer = new ResizeObserver((entries) => {
cb(entries[0].contentRect)
})
observer.observe(el)
return observer
}

64
src/utils/http/helpers.js Normal file
View File

@@ -0,0 +1,64 @@
/**********************************
* @FilePath: helpers.js
* @Author: Ronnie Zhang
* @LastEditor: Ronnie Zhang
* @LastEditTime: 2023/12/04 22:46:22
* @Email: zclzone@outlook.com
* Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
**********************************/
import { useAuthStore } from '@/store'
let isConfirming = false
export function resolveResError(code, message) {
switch (code) {
case 401:
if (isConfirming) return
isConfirming = true
$dialog.confirm({
title: '提示',
type: 'info',
content: '登录已过期,是否重新登录?',
confirm() {
useAuthStore().logout()
window.$message?.success('已退出登录')
isConfirming = false
},
cancel() {
isConfirming = false
},
})
return false
case 11007:
case 11008:
if (isConfirming) return
isConfirming = true
$dialog.confirm({
title: '提示',
type: 'info',
content: `${message},是否重新登录?`,
confirm() {
useAuthStore().logout()
window.$message?.success('已退出登录')
isConfirming = false
},
cancel() {
isConfirming = false
},
})
return false
case 403:
message = '请求被拒绝'
break
case 404:
message = '请求资源或接口不存在'
break
case 500:
message = '服务器发生异常'
break
default:
message = message ?? `${code}】: 未知异常!`
break
}
return message
}

30
src/utils/http/index.js Normal file
View File

@@ -0,0 +1,30 @@
/**********************************
* @FilePath: index.js
* @Author: Ronnie Zhang
* @LastEditor: Ronnie Zhang
* @LastEditTime: 2023/12/04 22:46:28
* @Email: zclzone@outlook.com
* Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
**********************************/
import axios from 'axios'
import { setupInterceptors } from './interceptors'
export function createAxios(options = {}) {
const defaultOptions = {
baseURL: '/api',
timeout: 12000,
}
const service = axios.create({
...defaultOptions,
...options,
})
setupInterceptors(service)
return service
}
export const request = createAxios()
export const mockRequest = createAxios({
baseURL: '/mock-api',
})

View File

@@ -0,0 +1,72 @@
/**********************************
* @FilePath: interceptors.js
* @Author: Ronnie Zhang
* @LastEditor: Ronnie Zhang
* @LastEditTime: 2023/12/04 22:46:40
* @Email: zclzone@outlook.com
* Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
**********************************/
import { resolveResError } from './helpers'
import { useAuthStore } from '@/store'
export function setupInterceptors(axiosInstance) {
function reqResolve(config) {
// 处理不需要token的请求
if (config.noNeedToken) {
return config
}
const { accessToken } = useAuthStore()
if (accessToken) {
// token: Bearer + xxx
config.headers.Authorization = 'Bearer ' + accessToken
}
return config
}
function reqReject(error) {
return Promise.reject(error)
}
const SUCCESS_CODES = [0, 200]
function resResolve(response) {
const { data, status, config, statusText, headers } = response
if (headers['content-type']?.includes('json')) {
if (SUCCESS_CODES.includes(data?.code)) {
return Promise.resolve(data)
}
const code = data?.code ?? status
// 根据code处理对应的操作并返回处理后的message
const message = resolveResError(code, data?.message ?? statusText)
//需要错误提醒
!config.noNeedTip && window.$message?.error(message)
return Promise.reject({ code, message, error: data ?? response })
}
return Promise.resolve(data ?? response)
}
async function resReject(error) {
if (!error || !error.response) {
const code = error?.code
/** 根据code处理对应的操作并返回处理后的message */
const message = resolveResError(code, error.message)
window.$message?.error(message)
return Promise.reject({ code, message, error })
}
const { data, status, config } = error.response
const code = data?.code ?? status
const message = resolveResError(code, data?.message ?? error.message)
/** 需要错误提醒 */
!config?.noNeedTip && message && window.$message?.error(message)
return Promise.reject({ code, message, error: error.response?.data || error.response })
}
axiosInstance.interceptors.request.use(reqResolve, reqReject)
axiosInstance.interceptors.response.use(resResolve, resReject)
}

14
src/utils/index.js Normal file
View File

@@ -0,0 +1,14 @@
/**********************************
* @FilePath: index.js
* @Author: Ronnie Zhang
* @LastEditor: Ronnie Zhang
* @LastEditTime: 2023/12/04 22:45:53
* @Email: zclzone@outlook.com
* Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
**********************************/
export * from './common'
export * from './http'
export * from './is'
export * from './naiveTools'
export * from './storage'

128
src/utils/is.js Normal file
View File

@@ -0,0 +1,128 @@
/**********************************
* @FilePath: is.js
* @Author: Ronnie Zhang
* @LastEditor: Ronnie Zhang
* @LastEditTime: 2023/12/04 22:45:32
* @Email: zclzone@outlook.com
* Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
**********************************/
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 isWhitespace(val) {
return val === ''
}
export function isObject(val) {
return !isNull(val) && 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 isNullOrWhitespace(val) {
return isNullOrUndef(val) || isWhitespace(val)
}
/** 空数组 | 空字符串 | 空对象 | 空Map | 空Set */
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
}
/**
* * 类似mysql的IFNULL函数
* * 第一个参数为null/undefined/'' 则返回第二个参数作为备用值,否则返回第一个参数
* @param {Number|Boolean|String} val
* @param {Number|Boolean|String} def
* @returns
*/
export function ifNull(val, def = '') {
return isNullOrWhitespace(val) ? def : val
}
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)
}
/**
* @param {string} path
* @returns {Boolean}
*/
export function isExternal(path) {
return /^(https?:|mailto:|tel:)/.test(path)
}
export const isServer = typeof window === 'undefined'
export const isClient = !isServer

121
src/utils/naiveTools.js Normal file
View File

@@ -0,0 +1,121 @@
/**********************************
* @FilePath: naiveTools.js
* @Author: Ronnie Zhang
* @LastEditor: Ronnie Zhang
* @LastEditTime: 2023/12/04 22:45:20
* @Email: zclzone@outlook.com
* Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
**********************************/
import * as NaiveUI from 'naive-ui'
import { isNullOrUndef } from '@/utils'
import settings from '@/settings'
import { useAppStore } from '@/store/modules/app'
export function setupMessage(NMessage) {
class Message {
static instance
constructor() {
// 单例模式
if (Message.instance) return Message.instance
Message.instance = this
this.message = {}
this.removeTimer = {}
}
removeMessage(key, duration = 5000) {
this.removeTimer[key] && clearTimeout(this.removeTimer[key])
this.removeTimer[key] = setTimeout(() => {
this.message[key]?.destroy()
}, duration)
}
destroy(key, duration = 200) {
setTimeout(() => {
this.message[key]?.destroy()
}, duration)
}
showMessage(type, content, option = {}) {
if (Array.isArray(content)) {
return content.forEach((msg) => NMessage[type](msg, option))
}
if (!option.key) {
return NMessage[type](content, option)
}
const currentMessage = this.message[option.key]
if (currentMessage) {
currentMessage.type = type
currentMessage.content = content
} else {
this.message[option.key] = NMessage[type](content, {
...option,
duration: 0,
onAfterLeave: () => {
delete this.message[option.key]
},
})
}
this.removeMessage(option.key, option.duration)
}
loading(content, option) {
this.showMessage('loading', content, option)
}
success(content, option) {
this.showMessage('success', content, option)
}
error(content, option) {
this.showMessage('error', content, option)
}
info(content, option) {
this.showMessage('info', content, option)
}
warning(content, option) {
this.showMessage('warning', content, option)
}
}
return new Message()
}
export function setupDialog(NDialog) {
NDialog.confirm = function (option = {}) {
const showIcon = !isNullOrUndef(option.title)
return NDialog[option.type || 'warning']({
showIcon,
positiveText: '确定',
negativeText: '取消',
onPositiveClick: option.confirm,
onNegativeClick: option.cancel,
onMaskClick: option.cancel,
...option,
})
}
return NDialog
}
export function setupNaiveDiscreteApi() {
const appStore = useAppStore()
const { naiveThemeOverrides: themeOverrides } = settings
const configProviderProps = computed(() => ({
theme: appStore.isDark ? NaiveUI.darkTheme : undefined,
themeOverrides,
}))
const { message, dialog, notification, loadingBar } = NaiveUI.createDiscreteApi(
['message', 'dialog', 'notification', 'loadingBar'],
{ configProviderProps }
)
window.$loadingBar = loadingBar
window.$notification = notification
window.$message = setupMessage(message)
window.$dialog = setupDialog(dialog)
}

View File

@@ -0,0 +1,30 @@
/**********************************
* @FilePath: index.js
* @Author: Ronnie Zhang
* @LastEditor: Ronnie Zhang
* @LastEditTime: 2023/12/04 22:46:07
* @Email: zclzone@outlook.com
* Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
**********************************/
import { createStorage } from './storage'
const prefixKey = 'vue-naive-admin_'
export const createLocalStorage = function (option = {}) {
return createStorage({
prefixKey: option.prefixKey || '',
storage: localStorage,
})
}
export const createSessionStorage = function (option = {}) {
return createStorage({
prefixKey: option.prefixKey || '',
storage: sessionStorage,
})
}
export const lStorage = createLocalStorage({ prefixKey })
export const sStorage = createSessionStorage({ prefixKey })

View File

@@ -0,0 +1,64 @@
/**********************************
* @FilePath: storage.js
* @Author: Ronnie Zhang
* @LastEditor: Ronnie Zhang
* @LastEditTime: 2023/12/04 22:46:13
* @Email: zclzone@outlook.com
* Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
**********************************/
import { isNullOrUndef } from '@/utils'
class Storage {
constructor(option) {
this.storage = option.storage
this.prefixKey = option.prefixKey
}
getKey(key) {
return `${this.prefixKey}${key}`.toLowerCase()
}
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 createStorage({ prefixKey = '', storage = sessionStorage }) {
return new Storage({ prefixKey, storage })
}