fix Bug or esLink formatting

This commit is contained in:
Ah jung
2021-07-22 13:47:44 +08:00
parent f6be8f521e
commit 7f81152793
172 changed files with 10553 additions and 9031 deletions

View File

@@ -1,5 +1,5 @@
// 默认缓存期限为7天
const DEFAULT_CACHE_TIME = 60 * 60 * 24 * 7
const DEFAULT_CACHE_TIME = 60 * 60 * 24 * 7;
/**
* 创建本地缓存对象
@@ -7,121 +7,121 @@ 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
/**
* 本地缓存类
* @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
*/
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)
}
/**
* 读取缓存
* @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()
}
}
}
private getKey(key: string) {
return `${this.prefixKey}${key}`.toUpperCase();
}
return new Storage()
}
export const storage = createStorage()
/**
* @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);
}
export default Storage
/**
* 读取缓存
* @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();
};
export const storage = createStorage();
export default Storage;

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 =
// 权重:系统 + 系统版本 > 平台 > 内核 + 载体 + 内核版本 + 载体版本 > 外壳 + 外壳版本
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',
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'
[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 systemVs =
// 平台
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([
[
'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'
// 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'];
// 平台
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 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 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 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 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 [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 [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]
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

@@ -1,86 +1,75 @@
/**
* URL方式保存文件到本地
* @param data 文件的blob数据
* @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)
}
/**
* 根据文件url获取文件名
* @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;
}
/**
* 根据文件地址下载文件
* @param {*} sUrl
*/
export function downloadByUrl({ url, target = '_blank', fileName }: {
url: string
target?: '_self' | '_blank'
fileName?: string
export function downloadByUrl({
url,
target = '_blank',
fileName,
}: {
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 = () => {
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

@@ -5,48 +5,48 @@ 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,
VITE_GLOB_IMG_URL
} = 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,
VITE_GLOB_IMG_URL
};
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,
};
}
/**
@@ -65,7 +65,7 @@ export const prodMode = 'production';
* @example:
*/
export function getEnv(): string {
return import.meta.env.MODE;
return import.meta.env.MODE;
}
/**
@@ -74,7 +74,7 @@ export function getEnv(): string {
* @example:
*/
export function isDevMode(): boolean {
return import.meta.env.DEV;
return import.meta.env.DEV;
}
/**
@@ -83,5 +83,5 @@ export function isDevMode(): boolean {
* @example:
*/
export function isProdMode(): boolean {
return import.meta.env.PROD;
return import.meta.env.PROD;
}

View File

@@ -1,166 +1,166 @@
import type { AxiosRequestConfig, AxiosInstance, AxiosResponse } from 'axios'
import type { AxiosRequestConfig, AxiosInstance, AxiosResponse } from 'axios';
import axios from 'axios'
import { AxiosCanceler } from './axiosCancel'
import { isFunction } from '@/utils/is'
import { cloneDeep } from 'lodash-es'
import axios from 'axios';
import { AxiosCanceler } from './axiosCancel';
import { isFunction } from '@/utils/is';
import { cloneDeep } from 'lodash-es';
import type { RequestOptions, CreateAxiosOptions, Result } from './types'
import type { RequestOptions, CreateAxiosOptions, Result } from './types';
// import { ContentTypeEnum } from '/@/enums/httpEnum';
export * from './axiosTransform'
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()
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;
}
this.createAxios(config);
}
/**
* @description: 创建axios实例
*/
private createAxios(config: CreateAxiosOptions): void {
this.axiosInstance = axios.create(config)
/**
* @description: 设置通用header
*/
setHeader(headers: any): void {
if (!this.axiosInstance) {
return;
}
Object.assign(this.axiosInstance.defaults.headers, headers);
}
private getTransform() {
const { transform } = this.options
return transform
/**
* @description: 拦截器配置
*/
private setupInterceptors() {
const transform = this.getTransform();
if (!transform) {
return;
}
const {
requestInterceptors,
requestInterceptorsCatch,
responseInterceptors,
responseInterceptorsCatch,
} = transform;
getAxios(): AxiosInstance {
return this.axiosInstance
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);
}
/**
* @description: 重新配置axios
*/
configAxios(config: CreateAxiosOptions) {
if (!this.axiosInstance) {
return
}
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)
})
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

@@ -1,61 +1,61 @@
import axios, { AxiosRequestConfig, Canceler } from 'axios'
import qs from 'qs'
import axios, { AxiosRequestConfig, Canceler } from 'axios';
import qs from 'qs';
import { isFunction } from '@/utils/is/index'
import { isFunction } from '@/utils/is/index';
// 声明一个 Map 用于存储每个请求的标识 和 取消函数
let pendingMap = new Map<string, Canceler>()
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

@@ -1,43 +1,43 @@
/**
* 数据处理类,可以根据项目自行配置
*/
import type { AxiosRequestConfig, AxiosResponse } from 'axios'
import type { RequestOptions, Result } from './types'
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,47 @@
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'];
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);
}
}
}
if (isObject(params[key])) {
formatRequestDate(params[key]);
}
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]);
}
}
}

View File

@@ -1,235 +1,235 @@
// axios配置 可自行根据项目进行更改,只需更改该文件即可,其他文件可以不动
import { VAxios } from './Axios'
import { AxiosTransform } from './axiosTransform'
import axios, { AxiosResponse } from 'axios'
import { checkStatus } from './checkStatus'
import { joinTimestamp, formatRequestDate } from './helper'
import { RequestEnum, ResultEnum, ContentTypeEnum } from '@/enums/httpEnum'
import { VAxios } from './Axios';
import { AxiosTransform } from './axiosTransform';
import axios, { AxiosResponse } from 'axios';
import { checkStatus } from './checkStatus';
import { joinTimestamp, formatRequestDate } from './helper';
import { RequestEnum, ResultEnum, ContentTypeEnum } from '@/enums/httpEnum';
import { useGlobSetting } from '@/hooks/setting'
import { useGlobSetting } from '@/hooks/setting';
import { isString } from '@/utils/is/'
import { setObjToUrlParams } from '@/utils/urlUtils'
import { isString } from '@/utils/is/';
import { setObjToUrlParams } from '@/utils/urlUtils';
import { RequestOptions, Result } from './types'
import { RequestOptions, Result } from './types';
import { useUserStoreWidthOut } from '@/store/modules/user'
import { useUserStoreWidthOut } from '@/store/modules/user';
const globSetting = useGlobSetting()
const globSetting = useGlobSetting();
const urlPrefix = globSetting.urlPrefix || '';
// @ts-ignore
const { $message: Message, $dialog: Modal } = window
const { $message: Message, $dialog: Modal } = window;
import router from '@/router'
import { storage } from '@/utils/Storage'
import router from '@/router';
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
}
})
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);
}
} 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 { 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,
},
});
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);
}
} 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 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
export default Axios;

View File

@@ -1,44 +1,44 @@
import { AxiosRequestConfig } from 'axios'
import { AxiosTransform } from './axiosTransform'
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'
// 是否添加时间戳
joinTime?: boolean;
// 请求参数拼接到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,75 +1,80 @@
import { h } from 'vue';
import type { App, Plugin } from 'vue';
import { NIcon } from 'naive-ui'
import { PageEnum } from "@/enums/pageEnum";
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(.*)*', '/', 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
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;
};
/**
* 找到对应的节点
* */
let result = null
export function getTreeItem(data: any[], key?: string | number) {
data.map(item => {
if (item.key === key) {
result = item;
} else {
if (item.children && item.children.length) {
getTreeItem(item.children, key);
}
}
});
return result
let result = null;
export function getTreeItem(data: any[], key?: string | number): any {
data.map((item) => {
if (item.key === key) {
result = item;
} else {
if (item.children && item.children.length) {
getTreeItem(item.children, key);
}
}
});
return result;
}
/**
* 找到所有节点
* */
let treeAll = []
export function getTreeAll(data: any[]) {
data.map(item => {
treeAll.push(item.key)
if (item.children && item.children.length) {
getTreeAll(item.children);
}
});
return treeAll
const treeAll: any[] = [];
export function getTreeAll(data: any[]): any[] {
data.map((item) => {
treeAll.push(item.key);
if (item.children && item.children.length) {
getTreeAll(item.children);
}
});
return treeAll;
}

View File

@@ -1,114 +1,114 @@
const toString = Object.prototype.toString
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 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

@@ -4,9 +4,9 @@
// export {default as xxx} from 'lodash/xxx'
export { default as cloneDeep } from 'lodash/cloneDeep'
export { default as intersection } from 'lodash/intersection'
export { default as get } from 'lodash/get'
export { default as upperFirst } from 'lodash/upperFirst'
export { default as omit } from 'lodash/omit'
export { default as debounce } from 'lodash/debounce'
export { default as cloneDeep } from 'lodash/cloneDeep';
export { default as intersection } from 'lodash/intersection';
export { default as get } from 'lodash/get';
export { default as upperFirst } from 'lodash/upperFirst';
export { default as omit } from 'lodash/omit';
export { default as debounce } from 'lodash/debounce';

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;
}