fix Bug Features CHANGELOG.ms

This commit is contained in:
Ah jung
2021-07-19 16:42:11 +08:00
parent 46dc7eb69e
commit b689fabfdd
148 changed files with 5829 additions and 4268 deletions

View File

@@ -7,119 +7,119 @@ const DEFAULT_CACHE_TIME = 60 * 60 * 24 * 7
* @param {Object} [storage=localStorage] - sessionStorage | localStorage
*/
export const createStorage = ({ prefixKey = '', storage = localStorage } = {}) => {
/**
* 本地缓存类
* @class Storage
*/
const Storage = class {
private storage = storage
private prefixKey?: string = prefixKey
private getKey(key: string) {
return `${ this.prefixKey }${ key }`.toUpperCase()
}
/**
* @description 设置缓存
* @param {string} key 缓存键
* @param {*} value 缓存值
* @param expire
* 本地缓存
* @class Storage
*/
set(key: string, value: any, expire: number | null = DEFAULT_CACHE_TIME) {
const stringData = JSON.stringify({
value,
expire: expire !== null ? new Date().getTime() + expire * 1000 : null
})
this.storage.setItem(this.getKey(key), stringData)
}
const Storage = class {
private storage = storage
private prefixKey?: string = prefixKey
/**
* 读取缓存
* @param {string} key 缓存键
* @param {*=} def 默认值
*/
get(key: string, def: any = null) {
const item = this.storage.getItem(this.getKey(key))
if (item) {
try {
const data = JSON.parse(item)
const { value, expire } = data
// 在有效期内直接返回
if (expire === null || expire >= Date.now()) {
return value
}
this.remove(this.getKey(key))
} catch (e) {
return def
private getKey(key: string) {
return `${ this.prefixKey }${ key }`.toUpperCase()
}
}
return def
}
/**
* 从缓存删除某项
* @param {string} key
*/
remove(key: string) {
this.storage.removeItem(this.getKey(key))
}
/**
* 清空所有缓存
* @memberOf Cache
*/
clear(): void {
this.storage.clear()
}
/**
* 设置cookie
* @param {string} name cookie 名称
* @param {*} value cookie 值
* @param {number=} expire 过期时间
* 如果过期时间为设置,默认关闭浏览器自动删除
* @example
*/
setCookie(name: string, value: any, expire: number | null = DEFAULT_CACHE_TIME) {
document.cookie = `${ this.getKey(name) }=${ value }; Max-Age=${ expire }`
}
/**
* 根据名字获取cookie值
* @param name
*/
getCookie(name: string): string {
const cookieArr = document.cookie.split('; ')
for (let i = 0, length = cookieArr.length; i < length; i++) {
const kv = cookieArr[i].split('=')
if (kv[0] === this.getKey(name)) {
return kv[1]
/**
* @description 设置缓存
* @param {string} key 缓存键
* @param {*} value 缓存值
* @param expire
*/
set(key: string, value: any, expire: number | null = DEFAULT_CACHE_TIME) {
const stringData = JSON.stringify({
value,
expire: expire !== null ? new Date().getTime() + expire * 1000 : null
})
this.storage.setItem(this.getKey(key), stringData)
}
}
return ''
}
/**
* 根据名字删除指定的cookie
* @param {string} key
*/
removeCookie(key: string) {
this.setCookie(key, 1, -1)
}
/**
* 清空cookie使所有cookie失效
*/
clearCookie(): void {
const keys = document.cookie.match(/[^ =;]+(?==)/g)
if (keys) {
for (let i = keys.length; i--;) {
document.cookie = keys[i] + '=0;expire=' + new Date(0).toUTCString()
/**
* 读取缓存
* @param {string} key 缓存键
* @param {*=} def 默认值
*/
get(key: string, def: any = null) {
const item = this.storage.getItem(this.getKey(key))
if (item) {
try {
const data = JSON.parse(item)
const { value, expire } = data
// 在有效期内直接返回
if (expire === null || expire >= Date.now()) {
return value
}
this.remove(this.getKey(key))
} catch (e) {
return def
}
}
return def
}
/**
* 从缓存删除某项
* @param {string} key
*/
remove(key: string) {
this.storage.removeItem(this.getKey(key))
}
/**
* 清空所有缓存
* @memberOf Cache
*/
clear(): void {
this.storage.clear()
}
/**
* 设置cookie
* @param {string} name cookie 名称
* @param {*} value cookie 值
* @param {number=} expire 过期时间
* 如果过期时间为设置,默认关闭浏览器自动删除
* @example
*/
setCookie(name: string, value: any, expire: number | null = DEFAULT_CACHE_TIME) {
document.cookie = `${ this.getKey(name) }=${ value }; Max-Age=${ expire }`
}
/**
* 根据名字获取cookie值
* @param name
*/
getCookie(name: string): string {
const cookieArr = document.cookie.split('; ')
for (let i = 0, length = cookieArr.length; i < length; i++) {
const kv = cookieArr[i].split('=')
if (kv[0] === this.getKey(name)) {
return kv[1]
}
}
return ''
}
/**
* 根据名字删除指定的cookie
* @param {string} key
*/
removeCookie(key: string) {
this.setCookie(key, 1, -1)
}
/**
* 清空cookie使所有cookie失效
*/
clearCookie(): void {
const keys = document.cookie.match(/[^ =;]+(?==)/g)
if (keys) {
for (let i = keys.length; i--;) {
document.cookie = keys[i] + '=0;expire=' + new Date(0).toUTCString()
}
}
}
}
}
}
return new Storage()
return new Storage()
}
export const storage = createStorage()

View File

@@ -4,139 +4,139 @@
* @constructor
*/
export default function BrowserType(lang: 'zh-cn' | 'en' = 'en') {
// 权重:系统 + 系统版本 > 平台 > 内核 + 载体 + 内核版本 + 载体版本 > 外壳 + 外壳版本
const ua = navigator.userAgent.toLowerCase()
const testUa = (regexp) => regexp.test(ua)
const testVs = (regexp) =>
ua
.match(regexp)
?.toString()
.replace(/[^0-9|_.]/g, '')
.replace(/_/g, '.')
// 系统
const system =
new Map([
[testUa(/windows|win32|win64|wow32|wow64/g), 'windows'], // windows系统
[testUa(/macintosh|macintel/g), 'macos'], // macos系统
[testUa(/x11/g), 'linux'], // linux系统
[testUa(/android|adr/g), 'android'], // android系统
[testUa(/ios|iphone|ipad|ipod|iwatch/g), 'ios'] // ios系统
]).get(true) || 'unknow'
// 系统版本
const systemVs =
new Map([
[
'windows',
// 权重:系统 + 系统版本 > 平台 > 内核 + 载体 + 内核版本 + 载体版本 > 外壳 + 外壳版本
const ua = navigator.userAgent.toLowerCase()
const testUa = (regexp) => regexp.test(ua)
const testVs = (regexp) =>
ua
.match(regexp)
?.toString()
.replace(/[^0-9|_.]/g, '')
.replace(/_/g, '.')
// 系统
const system =
new Map([
[testUa(/windows nt 5.0|windows 2000/g), '2000'],
[testUa(/windows nt 5.1|windows xp/g), 'xp'],
[testUa(/windows nt 5.2|windows 2003/g), '2003'],
[testUa(/windows nt 6.0|windows vista/g), 'vista'],
[testUa(/windows nt 6.1|windows 7/g), '7'],
[testUa(/windows nt 6.2|windows 8/g), '8'],
[testUa(/windows nt 6.3|windows 8.1/g), '8.1'],
[testUa(/windows nt 10.0|windows 10/g), '10']
]).get(true)
],
['macos', testVs(/os x [\d._]+/g)],
['android', testVs(/android [\d._]+/g)],
['ios', testVs(/os [\d._]+/g)]
]).get(system) || 'unknow'
[testUa(/windows|win32|win64|wow32|wow64/g), 'windows'], // windows系统
[testUa(/macintosh|macintel/g), 'macos'], // macos系统
[testUa(/x11/g), 'linux'], // linux系统
[testUa(/android|adr/g), 'android'], // android系统
[testUa(/ios|iphone|ipad|ipod|iwatch/g), 'ios'] // ios系统
]).get(true) || 'unknow'
// 平台
let platform = 'unknow'
if (system === 'windows' || system === 'macos' || system === 'linux') {
platform = 'desktop' // 桌面端
} else if (system === 'android' || system === 'ios' || testUa(/mobile/g)) {
platform = 'mobile' // 移动端
}
// 内核和载体
const [engine = 'unknow', supporter = 'unknow'] = new Map([
[
testUa(/applewebkit/g),
[
'webkit',
// 系统版本
const systemVs =
new Map([
// webkit内核
[testUa(/safari/g), 'safari'], // safari浏览器
[testUa(/chrome/g), 'chrome'], // chrome浏览器
[testUa(/opr/g), 'opera'], // opera浏览器
[testUa(/edge/g), 'edge'] // edge浏览器
]).get(true)
] || 'unknow'
], // [webkit内核, xxx浏览器]
[testUa(/gecko/g) && testUa(/firefox/g), ['gecko', 'firefox']], // [gecko内核,firefox浏览器]
[testUa(/presto/g), ['presto', 'opera']], // [presto内核,opera浏览器]
[testUa(/trident|compatible|msie/g), ['trident', 'iexplore']] // [trident内核,iexplore浏览器]
]).get(true) || ['unknow', 'unknow']
[
'windows',
new Map([
[testUa(/windows nt 5.0|windows 2000/g), '2000'],
[testUa(/windows nt 5.1|windows xp/g), 'xp'],
[testUa(/windows nt 5.2|windows 2003/g), '2003'],
[testUa(/windows nt 6.0|windows vista/g), 'vista'],
[testUa(/windows nt 6.1|windows 7/g), '7'],
[testUa(/windows nt 6.2|windows 8/g), '8'],
[testUa(/windows nt 6.3|windows 8.1/g), '8.1'],
[testUa(/windows nt 10.0|windows 10/g), '10']
]).get(true)
],
['macos', testVs(/os x [\d._]+/g)],
['android', testVs(/android [\d._]+/g)],
['ios', testVs(/os [\d._]+/g)]
]).get(system) || 'unknow'
// 内核版本
const engineVs =
new Map([
['webkit', testVs(/applewebkit\/[\d._]+/g)],
['gecko', testVs(/gecko\/[\d._]+/g)],
['presto', testVs(/presto\/[\d._]+/g)],
['trident', testVs(/trident\/[\d._]+/g)]
]).get(engine) || 'unknow'
// 平台
let platform = 'unknow'
if (system === 'windows' || system === 'macos' || system === 'linux') {
platform = 'desktop' // 桌面端
} else if (system === 'android' || system === 'ios' || testUa(/mobile/g)) {
platform = 'mobile' // 移动端
}
// 内核和载体
const [engine = 'unknow', supporter = 'unknow'] = new Map([
[
testUa(/applewebkit/g),
[
'webkit',
new Map([
// webkit内核
[testUa(/safari/g), 'safari'], // safari浏览器
[testUa(/chrome/g), 'chrome'], // chrome浏览器
[testUa(/opr/g), 'opera'], // opera浏览器
[testUa(/edge/g), 'edge'] // edge浏览器
]).get(true)
] || 'unknow'
], // [webkit内核, xxx浏览器]
[testUa(/gecko/g) && testUa(/firefox/g), ['gecko', 'firefox']], // [gecko内核,firefox浏览器]
[testUa(/presto/g), ['presto', 'opera']], // [presto内核,opera浏览器]
[testUa(/trident|compatible|msie/g), ['trident', 'iexplore']] // [trident内核,iexplore浏览器]
]).get(true) || ['unknow', 'unknow']
// 载体版本
const supporterVs =
new Map([
['firefox', testVs(/firefox\/[\d._]+/g)],
['opera', testVs(/opr\/[\d._]+/g)],
['iexplore', testVs(/(msie [\d._]+)|(rv:[\d._]+)/g)],
['edge', testVs(/edge\/[\d._]+/g)],
['safari', testVs(/version\/[\d._]+/g)],
['chrome', testVs(/chrome\/[\d._]+/g)]
]).get(supporter) || 'unknow'
// 内核版本
const engineVs =
new Map([
['webkit', testVs(/applewebkit\/[\d._]+/g)],
['gecko', testVs(/gecko\/[\d._]+/g)],
['presto', testVs(/presto\/[\d._]+/g)],
['trident', testVs(/trident\/[\d._]+/g)]
]).get(engine) || 'unknow'
// 外壳和外壳版本
const [shell = 'none', shellVs = 'unknow'] = new Map([
[testUa(/micromessenger/g), ['wechat', testVs(/micromessenger\/[\d._]+/g)]], // [微信浏览器,]
[testUa(/qqbrowser/g), ['qq', testVs(/qqbrowser\/[\d._]+/g)]], // [QQ浏览器,]
[testUa(/ucbrowser/g), ['uc', testVs(/ucbrowser\/[\d._]+/g)]], // [UC浏览器,]
[testUa(/qihu 360se/g), ['360', 'unknow']], // [360浏览器(无版本),]
[testUa(/2345explorer/g), ['2345', testVs(/2345explorer\/[\d._]+/g)]], // [2345浏览器,]
[testUa(/metasr/g), ['sougou', 'unknow']], // [搜狗浏览器(无版本),]
[testUa(/lbbrowser/g), ['liebao', 'unknow']], // [猎豹浏览器(无版本),]
[testUa(/maxthon/g), ['maxthon', testVs(/maxthon\/[\d._]+/g)]] // [遨游浏览器,]
]).get(true) || ['none', 'unknow']
// 载体版本
const supporterVs =
new Map([
['firefox', testVs(/firefox\/[\d._]+/g)],
['opera', testVs(/opr\/[\d._]+/g)],
['iexplore', testVs(/(msie [\d._]+)|(rv:[\d._]+)/g)],
['edge', testVs(/edge\/[\d._]+/g)],
['safari', testVs(/version\/[\d._]+/g)],
['chrome', testVs(/chrome\/[\d._]+/g)]
]).get(supporter) || 'unknow'
return {
'zh-cn': Object.assign(
{
内核: engine, // 内核: webkit gecko presto trident
内核版本: engineVs, // 内核版本
平台: platform, // 平台: desktop mobile
载体: supporter, // 载体: chrome safari firefox opera iexplore edge
载体版本: supporterVs, // 载体版本
系统: system, // 系统: windows macos linux android ios
系统版本: systemVs // 系统版本
},
shell === 'none'
? {}
: {
外壳: shell, // 外壳: wechat qq uc 360 2345 sougou liebao maxthon
外壳版本: shellVs // 外壳版本
}
),
en: Object.assign(
{
engine, // 内核: webkit gecko presto trident
engineVs, // 内核版本
platform, // 平台: desktop mobile
supporter, // 载体: chrome safari firefox opera iexplore edge
supporterVs, // 载体版本
system, // 系统: windows macos linux android ios
systemVs // 系统版本
},
shell === 'none'
? {}
: {
shell, // 外壳: wechat qq uc 360 2345 sougou liebao maxthon
shellVs // 外壳版本
}
)
}[lang]
// 外壳和外壳版本
const [shell = 'none', shellVs = 'unknow'] = new Map([
[testUa(/micromessenger/g), ['wechat', testVs(/micromessenger\/[\d._]+/g)]], // [微信浏览器,]
[testUa(/qqbrowser/g), ['qq', testVs(/qqbrowser\/[\d._]+/g)]], // [QQ浏览器,]
[testUa(/ucbrowser/g), ['uc', testVs(/ucbrowser\/[\d._]+/g)]], // [UC浏览器,]
[testUa(/qihu 360se/g), ['360', 'unknow']], // [360浏览器(无版本),]
[testUa(/2345explorer/g), ['2345', testVs(/2345explorer\/[\d._]+/g)]], // [2345浏览器,]
[testUa(/metasr/g), ['sougou', 'unknow']], // [搜狗浏览器(无版本),]
[testUa(/lbbrowser/g), ['liebao', 'unknow']], // [猎豹浏览器(无版本),]
[testUa(/maxthon/g), ['maxthon', testVs(/maxthon\/[\d._]+/g)]] // [遨游浏览器,]
]).get(true) || ['none', 'unknow']
return {
'zh-cn': Object.assign(
{
内核: engine, // 内核: webkit gecko presto trident
内核版本: engineVs, // 内核版本
平台: platform, // 平台: desktop mobile
载体: supporter, // 载体: chrome safari firefox opera iexplore edge
载体版本: supporterVs, // 载体版本
系统: system, // 系统: windows macos linux android ios
系统版本: systemVs // 系统版本
},
shell === 'none'
? {}
: {
外壳: shell, // 外壳: wechat qq uc 360 2345 sougou liebao maxthon
外壳版本: shellVs // 外壳版本
}
),
en: Object.assign(
{
engine, // 内核: webkit gecko presto trident
engineVs, // 内核版本
platform, // 平台: desktop mobile
supporter, // 载体: chrome safari firefox opera iexplore edge
supporterVs, // 载体版本
system, // 系统: windows macos linux android ios
systemVs // 系统版本
},
shell === 'none'
? {}
: {
shell, // 外壳: wechat qq uc 360 2345 sougou liebao maxthon
shellVs // 外壳版本
}
)
}[lang]
}

View File

@@ -4,13 +4,13 @@
* @param name 文件名
*/
function saveAs(data, name) {
const urlObject = window.URL || window.webkitURL || window
const export_blob = new Blob([data])
const save_link = document.createElement('a')
save_link.href = urlObject.createObjectURL(export_blob)
save_link.download = name
save_link.click()
urlObject.revokeObjectURL(save_link.href)
const urlObject = window.URL || window.webkitURL || window
const export_blob = new Blob([data])
const save_link = document.createElement('a')
save_link.href = urlObject.createObjectURL(export_blob)
save_link.download = name
save_link.click()
urlObject.revokeObjectURL(save_link.href)
}
/**
@@ -18,11 +18,11 @@ function saveAs(data, name) {
* @param url 文件url
*/
function getFileName(url) {
const num = url.lastIndexOf('/') + 1
let fileName = url.substring(num)
//把参数和文件名分割开
fileName = decodeURI(fileName.split('?')[0])
return fileName
const num = url.lastIndexOf('/') + 1
let fileName = url.substring(num)
//把参数和文件名分割开
fileName = decodeURI(fileName.split('?')[0])
return fileName
}
/**
@@ -30,57 +30,57 @@ function getFileName(url) {
* @param {*} sUrl
*/
export function downloadByUrl({ url, target = '_blank', fileName }: {
url: string
target?: '_self' | '_blank'
fileName?: string
url: string
target?: '_self' | '_blank'
fileName?: string
}): Promise<boolean> {
// 是否同源
const isSameHost = new URL(url).host == location.host
return new Promise<boolean>((resolve, reject) => {
if (isSameHost) {
const link = document.createElement('a')
link.href = url
link.target = target
// 是否同源
const isSameHost = new URL(url).host == location.host
return new Promise<boolean>((resolve, reject) => {
if (isSameHost) {
const link = document.createElement('a')
link.href = url
link.target = target
if (link.download !== undefined) {
link.download = fileName || getFileName(url)
}
if (link.download !== undefined) {
link.download = fileName || getFileName(url)
}
if (document.createEvent) {
const e = document.createEvent('MouseEvents')
e.initEvent('click', true, true)
link.dispatchEvent(e)
return resolve(true)
}
if (document.createEvent) {
const e = document.createEvent('MouseEvents')
e.initEvent('click', true, true)
link.dispatchEvent(e)
return resolve(true)
}
if (url.indexOf('?') === -1) {
url += '?download'
}
if (url.indexOf('?') === -1) {
url += '?download'
}
window.open(url, target)
return resolve(true)
} else {
const canvas = document.createElement('canvas')
const img = document.createElement('img')
img.setAttribute('crossOrigin', 'Anonymous')
img.src = url
img.onload = (e) => {
canvas.width = img.width
canvas.height = img.height
const context = canvas.getContext('2d')!
context.drawImage(img, 0, 0, img.width, img.height)
// window.navigator.msSaveBlob(canvas.msToBlob(),'image.jpg');
// saveAs(imageDataUrl, '附件');
canvas.toBlob((blob) => {
const link = document.createElement('a')
link.href = window.URL.createObjectURL(blob)
link.download = getFileName(url)
link.click()
URL.revokeObjectURL(link.href)
resolve(true)
}, 'image/jpeg')
}
img.onerror = (e) => reject(e)
}
})
window.open(url, target)
return resolve(true)
} else {
const canvas = document.createElement('canvas')
const img = document.createElement('img')
img.setAttribute('crossOrigin', 'Anonymous')
img.src = url
img.onload = (e) => {
canvas.width = img.width
canvas.height = img.height
const context = canvas.getContext('2d')!
context.drawImage(img, 0, 0, img.width, img.height)
// window.navigator.msSaveBlob(canvas.msToBlob(),'image.jpg');
// saveAs(imageDataUrl, '附件');
canvas.toBlob((blob) => {
const link = document.createElement('a')
link.href = window.URL.createObjectURL(blob)
link.download = getFileName(url)
link.click()
URL.revokeObjectURL(link.href)
resolve(true)
}, 'image/jpeg')
}
img.onerror = (e) => reject(e)
}
})
}

View File

@@ -1,50 +1,52 @@
import type { GlobEnvConfig } from '#/config';
import type { GlobEnvConfig } from '/#/config';
import { warn } from '@/utils/log';
import pkg from '../../package.json';
import { getConfigFileName } from '../../build/getConfigFileName';
export function getCommonStoragePrefix() {
const { VITE_GLOB_APP_SHORT_NAME } = getAppEnvConfig();
return `${ VITE_GLOB_APP_SHORT_NAME }__${ getEnv() }`.toUpperCase();
const { VITE_GLOB_APP_SHORT_NAME } = getAppEnvConfig();
return `${ VITE_GLOB_APP_SHORT_NAME }__${ getEnv() }`.toUpperCase();
}
// Generate cache key according to version
export function getStorageShortName() {
return `${ getCommonStoragePrefix() }${ `__${ pkg.version }` }__`.toUpperCase();
return `${ getCommonStoragePrefix() }${ `__${ pkg.version }` }__`.toUpperCase();
}
export function getAppEnvConfig() {
const ENV_NAME = getConfigFileName(import.meta.env);
const ENV_NAME = getConfigFileName(import.meta.env);
const ENV = (import.meta.env.DEV
? // Get the global configuration (the configuration will be extracted independently when packaging)
(import.meta.env as unknown as GlobEnvConfig)
: window[ENV_NAME as any]) as unknown as GlobEnvConfig;
const ENV = (import.meta.env.DEV
? // Get the global configuration (the configuration will be extracted independently when packaging)
(import.meta.env as unknown as GlobEnvConfig)
: window[ENV_NAME as any]) as unknown as GlobEnvConfig;
const {
VITE_GLOB_APP_TITLE,
VITE_GLOB_API_URL,
VITE_GLOB_APP_SHORT_NAME,
VITE_GLOB_API_URL_PREFIX,
VITE_GLOB_UPLOAD_URL,
VITE_GLOB_PROD_MOCK
} = ENV;
const {
VITE_GLOB_APP_TITLE,
VITE_GLOB_API_URL,
VITE_GLOB_APP_SHORT_NAME,
VITE_GLOB_API_URL_PREFIX,
VITE_GLOB_UPLOAD_URL,
VITE_GLOB_PROD_MOCK,
VITE_GLOB_IMG_URL
} = ENV;
if (!/^[a-zA-Z\_]*$/.test(VITE_GLOB_APP_SHORT_NAME)) {
warn(
`VITE_GLOB_APP_SHORT_NAME Variables can only be characters/underscores, please modify in the environment variables and re-running.`
);
}
if (!/^[a-zA-Z\_]*$/.test(VITE_GLOB_APP_SHORT_NAME)) {
warn(
`VITE_GLOB_APP_SHORT_NAME Variables can only be characters/underscores, please modify in the environment variables and re-running.`
);
}
return {
VITE_GLOB_APP_TITLE,
VITE_GLOB_API_URL,
VITE_GLOB_APP_SHORT_NAME,
VITE_GLOB_API_URL_PREFIX,
VITE_GLOB_UPLOAD_URL,
VITE_GLOB_PROD_MOCK
};
return {
VITE_GLOB_APP_TITLE,
VITE_GLOB_API_URL,
VITE_GLOB_APP_SHORT_NAME,
VITE_GLOB_API_URL_PREFIX,
VITE_GLOB_UPLOAD_URL,
VITE_GLOB_PROD_MOCK,
VITE_GLOB_IMG_URL
};
}
/**
@@ -63,7 +65,7 @@ export const prodMode = 'production';
* @example:
*/
export function getEnv(): string {
return import.meta.env.MODE;
return import.meta.env.MODE;
}
/**
@@ -72,7 +74,7 @@ export function getEnv(): string {
* @example:
*/
export function isDevMode(): boolean {
return import.meta.env.DEV;
return import.meta.env.DEV;
}
/**
@@ -81,5 +83,5 @@ export function isDevMode(): boolean {
* @example:
*/
export function isProdMode(): boolean {
return import.meta.env.PROD;
return import.meta.env.PROD;
}

View File

@@ -14,153 +14,153 @@ export * from './axiosTransform'
* @description: axios模块
*/
export class VAxios {
private axiosInstance: AxiosInstance
private options: CreateAxiosOptions
private axiosInstance: AxiosInstance
private options: CreateAxiosOptions
constructor(options: CreateAxiosOptions) {
this.options = options
this.axiosInstance = axios.create(options)
this.setupInterceptors()
}
/**
* @description: 创建axios实例
*/
private createAxios(config: CreateAxiosOptions): void {
this.axiosInstance = axios.create(config)
}
private getTransform() {
const { transform } = this.options
return transform
}
getAxios(): AxiosInstance {
return this.axiosInstance
}
/**
* @description: 重新配置axios
*/
configAxios(config: CreateAxiosOptions) {
if (!this.axiosInstance) {
return
constructor(options: CreateAxiosOptions) {
this.options = options
this.axiosInstance = axios.create(options)
this.setupInterceptors()
}
this.createAxios(config)
}
/**
* @description: 设置通用header
*/
setHeader(headers: any): void {
if (!this.axiosInstance) {
return
/**
* @description: 创建axios实例
*/
private createAxios(config: CreateAxiosOptions): void {
this.axiosInstance = axios.create(config)
}
Object.assign(this.axiosInstance.defaults.headers, headers)
}
/**
* @description: 拦截器配置
*/
private setupInterceptors() {
const transform = this.getTransform()
if (!transform) {
return
private getTransform() {
const { transform } = this.options
return transform
}
const {
requestInterceptors,
requestInterceptorsCatch,
responseInterceptors,
responseInterceptorsCatch
} = transform
const axiosCanceler = new AxiosCanceler()
// 请求拦截器配置处理
this.axiosInstance.interceptors.request.use((config: AxiosRequestConfig) => {
const { headers: { ignoreCancelToken } = { ignoreCancelToken: false } } = config
!ignoreCancelToken && axiosCanceler.addPending(config)
if (requestInterceptors && isFunction(requestInterceptors)) {
config = requestInterceptors(config)
}
return config
}, undefined)
// 请求拦截器错误捕获
requestInterceptorsCatch &&
isFunction(requestInterceptorsCatch) &&
this.axiosInstance.interceptors.request.use(undefined, requestInterceptorsCatch)
// 响应结果拦截器处理
this.axiosInstance.interceptors.response.use((res: AxiosResponse<any>) => {
res && axiosCanceler.removePending(res.config)
if (responseInterceptors && isFunction(responseInterceptors)) {
res = responseInterceptors(res)
}
return res
}, undefined)
// 响应结果拦截器错误捕获
responseInterceptorsCatch &&
isFunction(responseInterceptorsCatch) &&
this.axiosInstance.interceptors.response.use(undefined, responseInterceptorsCatch)
}
// /**
// * @description: 文件上传
// */
// uploadFiles(config: AxiosRequestConfig, params: File[]) {
// const formData = new FormData();
// Object.keys(params).forEach((key) => {
// formData.append(key, params[key as any]);
// });
// return this.request({
// ...config,
// method: 'POST',
// data: formData,
// headers: {
// 'Content-type': ContentTypeEnum.FORM_DATA,
// },
// });
// }
/**
* @description: 请求方法
*/
request<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
let conf: AxiosRequestConfig = cloneDeep(config)
const transform = this.getTransform()
const { requestOptions } = this.options
const opt: RequestOptions = Object.assign({}, requestOptions, options)
const { beforeRequestHook, requestCatch, transformRequestData } = transform || {}
if (beforeRequestHook && isFunction(beforeRequestHook)) {
conf = beforeRequestHook(conf, opt)
getAxios(): AxiosInstance {
return this.axiosInstance
}
return new Promise((resolve, reject) => {
this.axiosInstance
.request<any, AxiosResponse<Result>>(conf)
.then((res: AxiosResponse<Result>) => {
// 请求是否被取消
const isCancel = axios.isCancel(res)
if (transformRequestData && isFunction(transformRequestData) && !isCancel) {
const ret = transformRequestData(res, opt)
// ret !== undefined ? resolve(ret) : reject(new Error('request error!'));
return resolve(ret)
}
reject((res as unknown) as Promise<T>)
})
.catch((e: Error) => {
if (requestCatch && isFunction(requestCatch)) {
reject(requestCatch(e))
/**
* @description: 重新配置axios
*/
configAxios(config: CreateAxiosOptions) {
if (!this.axiosInstance) {
return
}
reject(e)
}
this.createAxios(config)
}
/**
* @description: 设置通用header
*/
setHeader(headers: any): void {
if (!this.axiosInstance) {
return
}
Object.assign(this.axiosInstance.defaults.headers, headers)
}
/**
* @description: 拦截器配置
*/
private setupInterceptors() {
const transform = this.getTransform()
if (!transform) {
return
}
const {
requestInterceptors,
requestInterceptorsCatch,
responseInterceptors,
responseInterceptorsCatch
} = transform
const axiosCanceler = new AxiosCanceler()
// 请求拦截器配置处理
this.axiosInstance.interceptors.request.use((config: AxiosRequestConfig) => {
const { headers: { ignoreCancelToken } = { ignoreCancelToken: false } } = config
!ignoreCancelToken && axiosCanceler.addPending(config)
if (requestInterceptors && isFunction(requestInterceptors)) {
config = requestInterceptors(config)
}
return config
}, undefined)
// 请求拦截器错误捕获
requestInterceptorsCatch &&
isFunction(requestInterceptorsCatch) &&
this.axiosInstance.interceptors.request.use(undefined, requestInterceptorsCatch)
// 响应结果拦截器处理
this.axiosInstance.interceptors.response.use((res: AxiosResponse<any>) => {
res && axiosCanceler.removePending(res.config)
if (responseInterceptors && isFunction(responseInterceptors)) {
res = responseInterceptors(res)
}
return res
}, undefined)
// 响应结果拦截器错误捕获
responseInterceptorsCatch &&
isFunction(responseInterceptorsCatch) &&
this.axiosInstance.interceptors.response.use(undefined, responseInterceptorsCatch)
}
// /**
// * @description: 文件上传
// */
// uploadFiles(config: AxiosRequestConfig, params: File[]) {
// const formData = new FormData();
// Object.keys(params).forEach((key) => {
// formData.append(key, params[key as any]);
// });
// return this.request({
// ...config,
// method: 'POST',
// data: formData,
// headers: {
// 'Content-type': ContentTypeEnum.FORM_DATA,
// },
// });
// }
/**
* @description: 请求方法
*/
request<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
let conf: AxiosRequestConfig = cloneDeep(config)
const transform = this.getTransform()
const { requestOptions } = this.options
const opt: RequestOptions = Object.assign({}, requestOptions, options)
const { beforeRequestHook, requestCatch, transformRequestData } = transform || {}
if (beforeRequestHook && isFunction(beforeRequestHook)) {
conf = beforeRequestHook(conf, opt)
}
return new Promise((resolve, reject) => {
this.axiosInstance
.request<any, AxiosResponse<Result>>(conf)
.then((res: AxiosResponse<Result>) => {
// 请求是否被取消
const isCancel = axios.isCancel(res)
if (transformRequestData && isFunction(transformRequestData) && !isCancel) {
const ret = transformRequestData(res, opt)
// ret !== undefined ? resolve(ret) : reject(new Error('request error!'));
return resolve(ret)
}
reject((res as unknown) as Promise<T>)
})
.catch((e: Error) => {
if (requestCatch && isFunction(requestCatch)) {
reject(requestCatch(e))
return
}
reject(e)
})
})
})
}
}
}

View File

@@ -7,55 +7,55 @@ import { isFunction } from '@/utils/is/index'
let pendingMap = new Map<string, Canceler>()
export const getPendingUrl = (config: AxiosRequestConfig) =>
[config.method, config.url, qs.stringify(config.data), qs.stringify(config.params)].join('&')
[config.method, config.url, qs.stringify(config.data), qs.stringify(config.params)].join('&')
export class AxiosCanceler {
/**
* 添加请求
* @param {Object} config
*/
addPending(config: AxiosRequestConfig) {
this.removePending(config)
const url = getPendingUrl(config)
config.cancelToken =
config.cancelToken ||
new axios.CancelToken((cancel) => {
if (!pendingMap.has(url)) {
// 如果 pending 中不存在当前请求,则添加进去
pendingMap.set(url, cancel)
}
})
}
/**
* @description: 清空所有pending
*/
removeAllPending() {
pendingMap.forEach((cancel) => {
cancel && isFunction(cancel) && cancel()
})
pendingMap.clear()
}
/**
* 移除请求
* @param {Object} config
*/
removePending(config: AxiosRequestConfig) {
const url = getPendingUrl(config)
if (pendingMap.has(url)) {
// 如果在 pending 中存在当前请求标识,需要取消当前请求,并且移除
const cancel = pendingMap.get(url)
cancel && cancel(url)
pendingMap.delete(url)
/**
* 添加请求
* @param {Object} config
*/
addPending(config: AxiosRequestConfig) {
this.removePending(config)
const url = getPendingUrl(config)
config.cancelToken =
config.cancelToken ||
new axios.CancelToken((cancel) => {
if (!pendingMap.has(url)) {
// 如果 pending 中不存在当前请求,则添加进去
pendingMap.set(url, cancel)
}
})
}
}
/**
* @description: 重置
*/
reset(): void {
pendingMap = new Map<string, Canceler>()
}
/**
* @description: 清空所有pending
*/
removeAllPending() {
pendingMap.forEach((cancel) => {
cancel && isFunction(cancel) && cancel()
})
pendingMap.clear()
}
/**
* 移除请求
* @param {Object} config
*/
removePending(config: AxiosRequestConfig) {
const url = getPendingUrl(config)
if (pendingMap.has(url)) {
// 如果在 pending 中存在当前请求标识,需要取消当前请求,并且移除
const cancel = pendingMap.get(url)
cancel && cancel(url)
pendingMap.delete(url)
}
}
/**
* @description: 重置
*/
reset(): void {
pendingMap = new Map<string, Canceler>()
}
}

View File

@@ -5,39 +5,39 @@ import type { AxiosRequestConfig, AxiosResponse } from 'axios'
import type { RequestOptions, Result } from './types'
export abstract class AxiosTransform {
/**
* @description: 请求之前处理配置
* @description: Process configuration before request
*/
beforeRequestHook?: (config: AxiosRequestConfig, options: RequestOptions) => AxiosRequestConfig
/**
* @description: 请求之前处理配置
* @description: Process configuration before request
*/
beforeRequestHook?: (config: AxiosRequestConfig, options: RequestOptions) => AxiosRequestConfig
/**
* @description: 请求成功处理
*/
transformRequestData?: (res: AxiosResponse<Result>, options: RequestOptions) => any
/**
* @description: 请求成功处理
*/
transformRequestData?: (res: AxiosResponse<Result>, options: RequestOptions) => any
/**
* @description: 请求失败处理
*/
requestCatch?: (e: Error) => Promise<any>
/**
* @description: 请求失败处理
*/
requestCatch?: (e: Error) => Promise<any>
/**
* @description: 请求之前的拦截器
*/
requestInterceptors?: (config: AxiosRequestConfig) => AxiosRequestConfig
/**
* @description: 请求之前的拦截器
*/
requestInterceptors?: (config: AxiosRequestConfig) => AxiosRequestConfig
/**
* @description: 请求之后的拦截器
*/
responseInterceptors?: (res: AxiosResponse<any>) => AxiosResponse<any>
/**
* @description: 请求之后的拦截器
*/
responseInterceptors?: (res: AxiosResponse<any>) => AxiosResponse<any>
/**
* @description: 请求之前的拦截器错误处理
*/
requestInterceptorsCatch?: (error: Error) => void
/**
* @description: 请求之前的拦截器错误处理
*/
requestInterceptorsCatch?: (error: Error) => void
/**
* @description: 请求之后的拦截器错误处理
*/
responseInterceptorsCatch?: (error: Error) => void
/**
* @description: 请求之后的拦截器错误处理
*/
responseInterceptorsCatch?: (error: Error) => void
}

View File

@@ -1,50 +1,50 @@
import { createStorage } from '@/utils/Storage'
export function checkStatus(status: number, msg: string): void {
const message = window.$message
const storage = createStorage()
switch (status) {
case 400:
message.error(`${ msg }`)
break
// 401: 未登录
// 未登录则跳转登录页面,并携带当前页面的路径
// 在登录成功后返回当前页面,这一步需要在登录页操作。
case 401:
message.error('用户没有权限(令牌、用户名、密码错误)!')
break
case 403:
message.error('用户得到授权,但是访问是被禁止的。!')
break
// 404请求不存在
case 404:
message.error('网络请求错误,未找到该资源!')
break
case 405:
message.error('网络请求错误,请求方法未允许!')
break
case 408:
message.error('网络请求超时!')
break
case 500:
message.error('服务器错误,请联系管理员!')
break
case 501:
message.error('网络未实现!')
break
case 502:
message.error('网络错误!')
break
case 503:
message.error('服务不可用,服务器暂时过载或维护!')
break
case 504:
message.error('网络超时!')
break
case 505:
message.error('http版本不支持该请求!')
break
default:
message.error(msg)
}
const message = window.$message
const storage = createStorage()
switch (status) {
case 400:
message.error(`${ msg }`)
break
// 401: 未登录
// 未登录则跳转登录页面,并携带当前页面的路径
// 在登录成功后返回当前页面,这一步需要在登录页操作。
case 401:
message.error('用户没有权限(令牌、用户名、密码错误)!')
break
case 403:
message.error('用户得到授权,但是访问是被禁止的。!')
break
// 404请求不存在
case 404:
message.error('网络请求错误,未找到该资源!')
break
case 405:
message.error('网络请求错误,请求方法未允许!')
break
case 408:
message.error('网络请求超时!')
break
case 500:
message.error('服务器错误,请联系管理员!')
break
case 501:
message.error('网络未实现!')
break
case 502:
message.error('网络错误!')
break
case 503:
message.error('服务不可用,服务器暂时过载或维护!')
break
case 504:
message.error('网络超时!')
break
case 505:
message.error('http版本不支持该请求!')
break
default:
message.error(msg)
}
}

View File

@@ -3,45 +3,45 @@ import { isObject, isString } from '@/utils/is';
const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm';
export function joinTimestamp<T extends boolean>(
join: boolean,
restful: T
join: boolean,
restful: T
): T extends true ? string : object;
export function joinTimestamp(join: boolean, restful = false): string | object {
if (!join) {
return restful ? '' : {};
}
const now = new Date().getTime();
if (restful) {
return `?_t=${ now }`;
}
return { _t: now };
if (!join) {
return restful ? '' : {};
}
const now = new Date().getTime();
if (restful) {
return `?_t=${ now }`;
}
return { _t: now };
}
/**
* @description: Format request parameter time
*/
export function formatRequestDate(params: Recordable) {
if (Object.prototype.toString.call(params) !== '[object Object]') {
return;
}
if (Object.prototype.toString.call(params) !== '[object Object]') {
return;
}
for (const key in params) {
if (params[key] && params[key]._isAMomentObject) {
params[key] = params[key].format(DATE_TIME_FORMAT);
}
if (isString(key)) {
const value = params[key];
if (value) {
try {
params[key] = isString(value) ? value.trim() : value;
} catch (error) {
throw new Error(error);
for (const key in params) {
if (params[key] && params[key]._isAMomentObject) {
params[key] = params[key].format(DATE_TIME_FORMAT);
}
if (isString(key)) {
const value = params[key];
if (value) {
try {
params[key] = isString(value) ? value.trim() : value;
} catch (error) {
throw new Error(error);
}
}
}
if (isObject(params[key])) {
formatRequestDate(params[key]);
}
}
}
if (isObject(params[key])) {
formatRequestDate(params[key]);
}
}
}

View File

@@ -18,9 +18,8 @@ import { useUserStoreWidthOut } from '@/store/modules/user'
const globSetting = useGlobSetting()
const urlPrefix = globSetting.urlPrefix || '';
const Message = window.$message
const Modal = window.$dialog
// @ts-ignore
const { $message: Message, $dialog: Modal } = window
import router from '@/router'
import { storage } from '@/utils/Storage'
@@ -29,208 +28,208 @@ import { storage } from '@/utils/Storage'
* @description: 数据处理,方便区分多种处理方式
*/
const transform: AxiosTransform = {
/**
* @description: 处理请求数据
*/
transformRequestData: (res: AxiosResponse<Result>, options: RequestOptions) => {
const {
isTransformRequestResult,
isShowMessage = true,
isShowErrorMessage,
isShowSuccessMessage,
successMessageText,
errorMessageText
} = options
/**
* @description: 处理请求数据
*/
transformRequestData: (res: AxiosResponse<Result>, options: RequestOptions) => {
const {
isTransformRequestResult,
isShowMessage = true,
isShowErrorMessage,
isShowSuccessMessage,
successMessageText,
errorMessageText
} = options
const reject = Promise.reject
const reject = Promise.reject
const { data } = res
// 这里 coderesultmessage为 后台统一的字段,需要在 types.ts内修改为项目自己的接口返回格式
const { code, result, message } = data
// 请求成功
const hasSuccess = data && Reflect.has(data, 'code') && code === ResultEnum.SUCCESS
// 是否显示提示信息
if (isShowMessage) {
if (hasSuccess && (successMessageText || isShowSuccessMessage)) {
// 是否显示自定义信息提示
Message.success(successMessageText || message || '操作成功!')
} else if (!hasSuccess && (errorMessageText || isShowErrorMessage)) {
// 是否显示自定义信息提示
Message.error(message || errorMessageText || '操作失败!')
} else if (!hasSuccess && options.errorMessageMode === 'modal') {
// errorMessageMode=custom-modal的时候会显示modal错误弹窗而不是消息提示用于一些比较重要的错误
Modal.confirm({ title: '错误提示', content: message })
}
}
// 不进行任何处理,直接返回
// 用于页面代码可能需要直接获取codedatamessage这些信息时开启
if (!isTransformRequestResult) {
return res.data
}
if (!data) {
// return '[HTTP] Request has no return value';
return reject(data)
}
// 接口请求成功,直接返回结果
if (code === ResultEnum.SUCCESS) {
return result
}
// 接口请求错误,统一提示错误信息
if (code === ResultEnum.ERROR) {
if (message) {
Message.error(data.message)
Promise.reject(new Error(message))
} else {
const msg = '操作失败,系统异常!'
Message.error(msg)
Promise.reject(new Error(msg))
}
return reject()
}
// 登录超时
if (code === ResultEnum.TIMEOUT) {
if (router.currentRoute.value.name == 'login') return
// 到登录页
const timeoutMsg = '登录超时,请重新登录!'
Modal.destroyAll()
Modal.warning({
title: '提示',
content: '登录身份已失效,请重新登录!',
onOk: () => {
router.replace({
name: 'login',
query: {
redirect: router.currentRoute.value.fullPath
const { data } = res
// 这里 coderesultmessage为 后台统一的字段,需要在 types.ts内修改为项目自己的接口返回格式
const { code, result, message } = data
// 请求成功
const hasSuccess = data && Reflect.has(data, 'code') && code === ResultEnum.SUCCESS
// 是否显示提示信息
if (isShowMessage) {
if (hasSuccess && (successMessageText || isShowSuccessMessage)) {
// 是否显示自定义信息提示
Message.success(successMessageText || message || '操作成功!')
} else if (!hasSuccess && (errorMessageText || isShowErrorMessage)) {
// 是否显示自定义信息提示
Message.error(message || errorMessageText || '操作失败!')
} else if (!hasSuccess && options.errorMessageMode === 'modal') {
// errorMessageMode=custom-modal的时候会显示modal错误弹窗而不是消息提示用于一些比较重要的错误
Modal.confirm({ title: '错误提示', content: message })
}
})
storage.clear()
}
})
return reject(new Error(timeoutMsg))
}
// 这里逻辑可以根据项目进行修改
if (!hasSuccess) {
return reject(new Error(message))
}
return data
},
// 请求之前处理config
beforeRequestHook: (config, options) => {
const { apiUrl, joinPrefix, joinParamsToUrl, formatDate, joinTime = true } = options;
if (joinPrefix) {
config.url = `${ urlPrefix }${ config.url }`;
}
if (apiUrl && isString(apiUrl)) {
config.url = `${ apiUrl }${ config.url }`;
}
const params = config.params || {};
if (config.method?.toUpperCase() === RequestEnum.GET) {
if (!isString(params)) {
// 给 get 请求加上时间戳参数,避免从缓存中拿数据。
config.params = Object.assign(params || {}, joinTimestamp(joinTime, false));
} else {
// 兼容restful风格
config.url = config.url + params + `${ joinTimestamp(joinTime, true) }`;
config.params = undefined;
}
} else {
if (!isString(params)) {
formatDate && formatRequestDate(params);
config.data = params;
config.params = undefined;
if (joinParamsToUrl) {
config.url = setObjToUrlParams(config.url as string, config.data);
// 不进行任何处理,直接返回
// 用于页面代码可能需要直接获取codedatamessage这些信息时开启
if (!isTransformRequestResult) {
return res.data
}
} else {
// 兼容restful风格
config.url = config.url + params;
config.params = undefined;
}
}
return config;
},
/**
* @description: 请求拦截器处理
*/
requestInterceptors: (config) => {
if (!data) {
// return '[HTTP] Request has no return value';
return reject(data)
}
// 接口请求成功,直接返回结果
if (code === ResultEnum.SUCCESS) {
return result
}
// 接口请求错误,统一提示错误信息
if (code === ResultEnum.ERROR) {
if (message) {
Message.error(data.message)
Promise.reject(new Error(message))
} else {
const msg = '操作失败,系统异常!'
Message.error(msg)
Promise.reject(new Error(msg))
}
return reject()
}
// 登录超时
if (code === ResultEnum.TIMEOUT) {
if (router.currentRoute.value.name == 'login') return
// 到登录页
const timeoutMsg = '登录超时,请重新登录!'
Modal.destroyAll()
Modal.warning({
title: '提示',
content: '登录身份已失效,请重新登录!',
onOk: () => {
router.replace({
name: 'login',
query: {
redirect: router.currentRoute.value.fullPath
}
})
storage.clear()
}
})
return reject(new Error(timeoutMsg))
}
// 这里逻辑可以根据项目进行修改
if (!hasSuccess) {
return reject(new Error(message))
}
return data
},
// 请求之前处理config
const userStore = useUserStoreWidthOut();
const token = userStore.getToken
if (token) {
// jwt token
config.headers.token = token
}
return config
},
beforeRequestHook: (config, options) => {
const { apiUrl, joinPrefix, joinParamsToUrl, formatDate, joinTime = true } = options;
/**
* @description: 响应错误处理
*/
responseInterceptorsCatch: (error: any) => {
const { response, code, message } = error || {}
const msg: string =
response && response.data && response.data.error ? response.data.error.message : ''
const err: string = error.toString()
try {
if (code === 'ECONNABORTED' && message.indexOf('timeout') !== -1) {
Message.error('接口请求超时,请刷新页面重试!')
return
}
if (err && err.includes('Network Error')) {
Modal.confirm({
title: '网络异常',
content: '请检查您的网络连接是否正常!'
})
return
}
} catch (error) {
throw new Error(error)
if (joinPrefix) {
config.url = `${ urlPrefix }${ config.url }`;
}
if (apiUrl && isString(apiUrl)) {
config.url = `${ apiUrl }${ config.url }`;
}
const params = config.params || {};
if (config.method?.toUpperCase() === RequestEnum.GET) {
if (!isString(params)) {
// 给 get 请求加上时间戳参数,避免从缓存中拿数据。
config.params = Object.assign(params || {}, joinTimestamp(joinTime, false));
} else {
// 兼容restful风格
config.url = config.url + params + `${ joinTimestamp(joinTime, true) }`;
config.params = undefined;
}
} else {
if (!isString(params)) {
formatDate && formatRequestDate(params);
config.data = params;
config.params = undefined;
if (joinParamsToUrl) {
config.url = setObjToUrlParams(config.url as string, config.data);
}
} else {
// 兼容restful风格
config.url = config.url + params;
config.params = undefined;
}
}
return config;
},
/**
* @description: 请求拦截器处理
*/
requestInterceptors: (config) => {
// 请求之前处理config
const userStore = useUserStoreWidthOut();
const token = userStore.getToken
if (token) {
// jwt token
config.headers.token = token
}
return config
},
/**
* @description: 响应错误处理
*/
responseInterceptorsCatch: (error: any) => {
const { response, code, message } = error || {}
const msg: string =
response && response.data && response.data.error ? response.data.error.message : ''
const err: string = error.toString()
try {
if (code === 'ECONNABORTED' && message.indexOf('timeout') !== -1) {
Message.error('接口请求超时,请刷新页面重试!')
return
}
if (err && err.includes('Network Error')) {
Modal.confirm({
title: '网络异常',
content: '请检查您的网络连接是否正常!'
})
return
}
} catch (error) {
throw new Error(error)
}
// 请求是否被取消
const isCancel = axios.isCancel(error)
if (!isCancel) {
checkStatus(error.response && error.response.status, msg)
} else {
console.warn(error, '请求被取消!')
}
return error
}
// 请求是否被取消
const isCancel = axios.isCancel(error)
if (!isCancel) {
checkStatus(error.response && error.response.status, msg)
} else {
console.warn(error, '请求被取消!')
}
return error
}
}
const Axios = new VAxios({
timeout: 15 * 1000,
// 基础接口地址
// baseURL: globSetting.apiUrl,
// 接口可能会有通用的地址部分,可以统一抽取出来
// prefixUrl: prefix,
headers: { 'Content-Type': ContentTypeEnum.FORM_URLENCODED },
// 数据处理方式
transform,
// 配置项,下面的选项都可以在独立的接口请求中覆盖
requestOptions: {
// 默认将prefix 添加到url
joinPrefix: true,
// 需要对返回数据进行处理
isTransformRequestResult: true,
// post请求的时候添加参数到url
joinParamsToUrl: false,
// 格式化提交参数时间
formatDate: true,
// 消息提示类型
errorMessageMode: 'none',
// 接口地址
apiUrl: globSetting.apiUrl as string
},
withCredentials: false
timeout: 15 * 1000,
// 基础接口地址
// baseURL: globSetting.apiUrl,
// 接口可能会有通用的地址部分,可以统一抽取出来
// prefixUrl: prefix,
headers: { 'Content-Type': ContentTypeEnum.FORM_URLENCODED },
// 数据处理方式
transform,
// 配置项,下面的选项都可以在独立的接口请求中覆盖
requestOptions: {
// 默认将prefix 添加到url
joinPrefix: true,
// 需要对返回数据进行处理
isTransformRequestResult: true,
// post请求的时候添加参数到url
joinParamsToUrl: false,
// 格式化提交参数时间
formatDate: true,
// 消息提示类型
errorMessageMode: 'none',
// 接口地址
apiUrl: globSetting.apiUrl as string
},
withCredentials: false
})
export default Axios

View File

@@ -2,41 +2,43 @@ import { AxiosRequestConfig } from 'axios'
import { AxiosTransform } from './axiosTransform'
export interface CreateAxiosOptions extends AxiosRequestConfig {
prefixUrl?: string
transform?: AxiosTransform
requestOptions?: RequestOptions
prefixUrl?: string
transform?: AxiosTransform
requestOptions?: RequestOptions
}
export interface RequestOptions {
// 请求参数拼接到url
joinParamsToUrl?: boolean
// 格式化请求参数时间
formatDate?: boolean
// 是否处理请求结果
isTransformRequestResult?: boolean
// 是否显示提示信息
isShowMessage?: boolean
// 是否解析成JSON
isParseToJson?: boolean
// 成功的文本信息
successMessageText?: string
// 是否显示成功信息
isShowSuccessMessage?: boolean
// 是否显示失败信息
isShowErrorMessage?: boolean
// 错误的文本信息
errorMessageText?: string
// 是否加入url
joinPrefix?: boolean
// 接口地址, 不填则使用默认apiUrl
apiUrl?: string
// 错误消息提示类型
errorMessageMode?: 'none' | 'modal'
// 请求参数拼接到url
joinParamsToUrl?: boolean
// 格式化请求参数时间
formatDate?: boolean
// 是否处理请求结果
isTransformRequestResult?: boolean
// 是否显示提示信息
isShowMessage?: boolean
// 是否解析成JSON
isParseToJson?: boolean
// 成功的文本信息
successMessageText?: string
// 是否显示成功信息
isShowSuccessMessage?: boolean
// 是否显示失败信息
isShowErrorMessage?: boolean
// 错误的文本信息
errorMessageText?: string
// 是否加入url
joinPrefix?: boolean
// 接口地址, 不填则使用默认apiUrl
apiUrl?: string
// 错误消息提示类型
errorMessageMode?: 'none' | 'modal'
// 是否添加时间戳
joinTime?: boolean;
}
export interface Result<T = any> {
code: number
type?: 'success' | 'error' | 'warning'
message: string
result?: T
code: number
type?: 'success' | 'error' | 'warning'
message: string
result?: T
}

View File

@@ -1,43 +1,44 @@
import { h } from 'vue';
import type { App, Plugin } from 'vue';
import { NIcon } from 'naive-ui'
import { PageEnum } from "@/enums/pageEnum";
/**
* render 图标
* */
export function renderIcon(icon) {
return () => h(NIcon, null, { default: () => h(icon) })
return () => h(NIcon, null, { default: () => h(icon) })
}
/**
* 递归组装菜单格式
*/
export function generatorMenu(routerMap: Array<any>) {
return routerMap.filter(item => {
return item.meta.hidden != true && !['/:path(.*)*', '/', '/redirect', '/login'].includes(item.path)
}).map(item => {
const currentMenu = {
...item,
...item.meta,
label: item.meta.title,
key: item.name
}
// 是否有子菜单,并递归处理
if (item.children && item.children.length > 0) {
// Recursion
currentMenu.children = generatorMenu(item.children)
}
return currentMenu
})
return routerMap.filter(item => {
return item.meta.hidden != true && !['/:path(.*)*', '/', PageEnum.REDIRECT, PageEnum.BASE_LOGIN].includes(item.path)
}).map(item => {
const currentMenu = {
...item,
...item.meta,
label: item.meta.title,
key: item.name
}
// 是否有子菜单,并递归处理
if (item.children && item.children.length > 0) {
// Recursion
currentMenu.children = generatorMenu(item.children)
}
return currentMenu
})
}
export const withInstall = <T>(component: T, alias?: string) => {
const comp = component as any;
comp.install = (app: App) => {
app.component(comp.name || comp.displayName, component);
if (alias) {
app.config.globalProperties[alias] = component;
}
};
return component as T & Plugin;
const comp = component as any;
comp.install = (app: App) => {
app.component(comp.name || comp.displayName, component);
if (alias) {
app.config.globalProperties[alias] = component;
}
};
return component as T & Plugin;
};

View File

@@ -4,111 +4,111 @@ const toString = Object.prototype.toString
* @description: 判断值是否未某个类型
*/
export function is(val: unknown, type: string) {
return toString.call(val) === `[object ${ type }]`
return toString.call(val) === `[object ${ type }]`
}
/**
* @description: 是否为函数
*/
export function isFunction<T = Function>(val: unknown): val is T {
return is(val, 'Function')
return is(val, 'Function')
}
/**
* @description: 是否已定义
*/
export const isDef = <T = unknown>(val?: T): val is T => {
return typeof val !== 'undefined'
return typeof val !== 'undefined'
}
export const isUnDef = <T = unknown>(val?: T): val is T => {
return !isDef(val)
return !isDef(val)
}
/**
* @description: 是否为对象
*/
export const isObject = (val: any): val is Record<any, any> => {
return val !== null && is(val, 'Object')
return val !== null && is(val, 'Object')
}
/**
* @description: 是否为时间
*/
export function isDate(val: unknown): val is Date {
return is(val, 'Date')
return is(val, 'Date')
}
/**
* @description: 是否为数值
*/
export function isNumber(val: unknown): val is number {
return is(val, 'Number')
return is(val, 'Number')
}
/**
* @description: 是否为AsyncFunction
*/
export function isAsyncFunction<T = any>(val: unknown): val is Promise<T> {
return is(val, 'AsyncFunction')
return is(val, 'AsyncFunction')
}
/**
* @description: 是否为promise
*/
export function isPromise<T = any>(val: unknown): val is Promise<T> {
return is(val, 'Promise') && isObject(val) && isFunction(val.then) && isFunction(val.catch)
return is(val, 'Promise') && isObject(val) && isFunction(val.then) && isFunction(val.catch)
}
/**
* @description: 是否为字符串
*/
export function isString(val: unknown): val is string {
return is(val, 'String')
return is(val, 'String')
}
/**
* @description: 是否为boolean类型
*/
export function isBoolean(val: unknown): val is boolean {
return is(val, 'Boolean')
return is(val, 'Boolean')
}
/**
* @description: 是否为数组
*/
export function isArray(val: any): val is Array<any> {
return val && Array.isArray(val)
return val && Array.isArray(val)
}
/**
* @description: 是否客户端
*/
export const isClient = () => {
return typeof window !== 'undefined'
return typeof window !== 'undefined'
}
/**
* @description: 是否为浏览器
*/
export const isWindow = (val: any): val is Window => {
return typeof window !== 'undefined' && is(val, 'Window')
return typeof window !== 'undefined' && is(val, 'Window')
}
export const isElement = (val: unknown): val is Element => {
return isObject(val) && !!val.tagName
return isObject(val) && !!val.tagName
}
export const isServer = typeof window === 'undefined'
// 是否为图片节点
export function isImageDom(o: Element) {
return o && ['IMAGE', 'IMG'].includes(o.tagName)
return o && ['IMAGE', 'IMG'].includes(o.tagName)
}
export function isNull(val: unknown): val is null {
return val === null;
return val === null;
}
export function isNullAndUnDef(val: unknown): val is null | undefined {
return isUnDef(val) && isNull(val);
return isUnDef(val) && isNull(val);
}

View File

@@ -1,53 +1,53 @@
import * as echarts from 'echarts/core';
import {
BarChart,
LineChart,
PieChart,
MapChart,
PictorialBarChart,
RadarChart,
BarChart,
LineChart,
PieChart,
MapChart,
PictorialBarChart,
RadarChart,
} from 'echarts/charts';
import {
TitleComponent,
TooltipComponent,
GridComponent,
PolarComponent,
AriaComponent,
ParallelComponent,
LegendComponent,
RadarComponent,
ToolboxComponent,
DataZoomComponent,
VisualMapComponent,
TimelineComponent,
CalendarComponent,
TitleComponent,
TooltipComponent,
GridComponent,
PolarComponent,
AriaComponent,
ParallelComponent,
LegendComponent,
RadarComponent,
ToolboxComponent,
DataZoomComponent,
VisualMapComponent,
TimelineComponent,
CalendarComponent,
} from 'echarts/components';
import { SVGRenderer } from 'echarts/renderers';
echarts.use([
LegendComponent,
TitleComponent,
TooltipComponent,
GridComponent,
PolarComponent,
AriaComponent,
ParallelComponent,
BarChart,
LineChart,
PieChart,
MapChart,
RadarChart,
SVGRenderer,
PictorialBarChart,
RadarComponent,
ToolboxComponent,
DataZoomComponent,
VisualMapComponent,
TimelineComponent,
CalendarComponent,
LegendComponent,
TitleComponent,
TooltipComponent,
GridComponent,
PolarComponent,
AriaComponent,
ParallelComponent,
BarChart,
LineChart,
PieChart,
MapChart,
RadarChart,
SVGRenderer,
PictorialBarChart,
RadarComponent,
ToolboxComponent,
DataZoomComponent,
VisualMapComponent,
TimelineComponent,
CalendarComponent,
]);
export default echarts;

View File

@@ -1,9 +1,9 @@
const projectName = import.meta.env.VITE_GLOB_APP_TITLE;
export function warn(message: string) {
console.warn(`[${ projectName } warn]:${ message }`);
console.warn(`[${ projectName } warn]:${ message }`);
}
export function error(message: string) {
throw new Error(`[${ projectName } error]:${ message }`);
throw new Error(`[${ projectName } error]:${ message }`);
}

View File

@@ -4,30 +4,30 @@ import { createTypes, VueTypeValidableDef, VueTypesInterface } from 'vue-types';
export type VueNode = VNodeChild | JSX.Element;
type PropTypes = VueTypesInterface & {
readonly style: VueTypeValidableDef<CSSProperties>;
readonly VNodeChild: VueTypeValidableDef<VueNode>;
readonly style: VueTypeValidableDef<CSSProperties>;
readonly VNodeChild: VueTypeValidableDef<VueNode>;
};
const propTypes = createTypes({
func: undefined,
bool: undefined,
string: undefined,
number: undefined,
object: undefined,
integer: undefined,
func: undefined,
bool: undefined,
string: undefined,
number: undefined,
object: undefined,
integer: undefined,
}) as PropTypes;
propTypes.extend([
{
name: 'style',
getter: true,
type: [String, Object],
default: undefined,
},
{
name: 'VNodeChild',
getter: true,
type: undefined,
},
{
name: 'style',
getter: true,
type: [String, Object],
default: undefined,
},
{
name: 'VNodeChild',
getter: true,
type: undefined,
},
]);
export { propTypes };

View File

@@ -9,16 +9,16 @@
* ==>www.baidu.com?a=3&b=4
*/
export function setObjToUrlParams(baseUrl: string, obj: object): string {
let parameters = ''
let url = ''
for (const key in obj) {
parameters += key + '=' + encodeURIComponent(obj[key]) + '&'
}
parameters = parameters.replace(/&$/, '')
if (/\?$/.test(baseUrl)) {
url = baseUrl + parameters
} else {
url = baseUrl.replace(/\/?$/, '?') + parameters
}
return url
let parameters = ''
let url = ''
for (const key in obj) {
parameters += key + '=' + encodeURIComponent(obj[key]) + '&'
}
parameters = parameters.replace(/&$/, '')
if (/\?$/.test(baseUrl)) {
url = baseUrl + parameters
} else {
url = baseUrl.replace(/\/?$/, '?') + parameters
}
return url
}