const log = console.log.bind(console, "%c[Tools]:", "font-weight:bold;background-color:#00A0D8;color:#fff;padding: 3px 3px;margin: 5px 0px;border-radius:2px;");
const toastStyle = `.link-toast{position:fixed !important;padding:12px 24px;font-size:14px;border-radius:8px;white-space:nowrap;color:#fff;-webkit-transition:-webkit-transform 0.4s cubic-bezier(0.22,0.58,0.12,0.98);transition:-webkit-transform 0.4s cubic-bezier(0.22,0.58,0.12,0.98);transition:transform 0.4s cubic-bezier(0.22,0.58,0.12,0.98);transition:transform 0.4s cubic-bezier(0.22,0.58,0.12,0.98),-webkit-transform 0.4s cubic-bezier(0.22,0.58,0.12,0.98);-webkit-animation:link-msg-move-in-top cubic-bezier(0.22,0.58,0.12,0.98) 0.4s;animation:link-msg-move-in-top cubic-bezier(0.22,0.58,0.12,0.98) 0.4s;z-index:10100;text-align:center;height:auto;line-height:auto}.link-toast.mobile{text-align:center;-webkit-box-shadow:none !important;box-shadow:none !important;width:auto;background-color:transparent !important;position:fixed;top:50vh;left:0;right:0}.link-toast.mobile .toast-text{background-color:rgba(0,0,0,0.8);padding:8px;border-radius:8px;display:inline-block;min-width:50px;word-break:break-all;white-space:pre-wrap}.link-toast.fixed{position:fixed}.link-toast.success{background-color:#47d279;-webkit-box-shadow:0 0.2em 0.1em 0.1em rgba(71,210,121,0.2);box-shadow:0 0.2em 0.1em 0.1em rgba(71,210,121,0.2)}.link-toast.caution{background-color:#ffb243;-webkit-box-shadow:0 0.2em 0.1em 0.1em rgba(255,190,68,0.2);box-shadow:0 0.2em 0.1em 0.1em rgba(255,190,68,0.2)}.link-toast.error{background-color:#ff6464;-webkit-box-shadow:0 0.2em 1em 0.1em rgba(255,100,100,0.2);box-shadow:0 0.2em 1em 0.1em rgba(255,100,100,0.2)}.link-toast.info{background-color:#48bbf8;-webkit-box-shadow:0 0.2em 0.1em 0.1em rgba(72,187,248,0.2);box-shadow:0 0.2em 0.1em 0.1em rgba(72,187,248,0.2)}.link-toast.out-fade{-webkit-animation:link-msg-fade-out cubic-bezier(0.22,0.58,0.12,0.98) 0.4s;animation:link-msg-fade-out cubic-bezier(0.22,0.58,0.12,0.98) 0.4s}@-webkit-keyframes link-msg-move-in-top{from{opacity:0;-webkit-transform:translate(0,5em);transform:translate(0,5em)}to{opacity:1;-webkit-transform:translate(0,0);transform:translate(0,0)}}@keyframes link-msg-move-in-top{from{opacity:0;-webkit-transform:translate(0,5em);transform:translate(0,5em)}to{opacity:1;-webkit-transform:translate(0,0);transform:translate(0,0)}}@-webkit-keyframes link-msg-fade-out{from{opacity:1}to{opacity:0}}@keyframes link-msg-fade-out{from{opacity:1}to{opacity:0}}.el-fade-enter,.el-fade-leave{opacity:0;animation-timing-function:linear;animation-duration:.2s;animation-fill-mode:both;animation-play-state:paused}.el-fade-enter.el-fade-enter-active{animation-name:el-fade-in;animation-play-state:running}.el-fade-leave.el-fade-leave-active{animation-name:el-fade-out;animation-play-state:running;pointer-events:none}.el-fade-enter-active,.el-fade-leave-active{transition:opacity .3s}@keyframes el-fade-in{0%{opacity:0}to{opacity:1}}@-webkit-keyframes el-fade-in{0%{opacity:0}to{opacity:1}}@keyframes el-fade-out{0%{opacity:1}to{opacity:0}}@-webkit-keyframes el-fade-out{0%{opacity:1}to{opacity:0}}`;
const aniStyle = `@keyframes aniLeftToRight{0%{transform:translateX(-32px);opacity:0.2}20%{opacity:0.5}30%{opacity:0.8}100%{opacity:1}}@keyframes aniBottomToTop{0%{transform:translateY(32px);opacity:0.2}20%{opacity:0.5}30%{opacity:0.8}100%{opacity:1}}@-webkit-keyframes aniTopToBottom{0%{transform:translateY(-32px);opacity:0.2}20%{opacity:0.5}30%{opacity:0.8}100%{opacity:1}}@-webkit-keyframes aniHideToShow{0%{display:none;opacity:0.2}20%{opacity:0.5}30%{opacity:0.8}100%{opacity:1}}@-webkit-keyframes aniShowToHide{0%{display:none;opacity:1}20%{opacity:0.8}30%{opacity:0.5}100%{opacity:0.3}}.aniDelete{transition:all 0.15s cubic-bezier(0.4,0,1,1);opacity:0.1}@keyframes spin{0%{transform:rotate(0deg);}100%{transform:rotate(360deg);}}`;
/**
* ContentType 映射表
* @type {{download: string, form: string, json: string}}
*/
const ContentType = {
json: 'application/json;charset=UTF-8', // json数据格式
form: 'application/x-www-form-urlencoded; charset=UTF-8', // 表单数据格式
download: 'application/octet-stream' // 二进制文件流格式,用于download
};
/**
* 消息提示框类型
* @type {{warn: string, success: string, error: string, info: string}}
*/
const ToastType = {
success: 'success', error: 'error', info: 'info', warn: 'caution'
};
/**
* 消息提示弹窗
* @param message
* @param type
* @param wait
* @param target
*/
function toast(message = '成功', type = ToastType.info, wait = 3000, target = document.body) { // 签到提示
const toast = document.createElement("div"); // 创建标签
toast.className = `link-toast ${type}`;
toast.style.cssText = "top: 70px; right: 25px;";
toast.innerHTML = `<span class="toast-text">${message}</span>`;
target.append(toast); // 添加提示到页面上
setTimeout(() => {
fadeOutRemove(toast);
}, wait);
}
/**
* 解析boolean
* @param val
* @returns { boolean }
*/
function parseBoolean(val) {
if (["yes", "y", "true", "1", "on"].indexOf(String(val).toLowerCase()) !== -1) return true;
if (["no", "n", "false", "0", "off"].indexOf(String(val).toLowerCase()) !== -1) return false;
return Boolean(val);
}
/**
* 淡入显示
* @param element
*/
function fadeInShow(element) {
element.classList.remove("el-hidden");
element.classList.add("el-fade-enter", "el-fade-enter-active");
const f = () => {
element.classList.remove("el-fade-enter", "el-fade-enter-active");
element.removeEventListener("animationend", f);
element.removeEventListener("webkitAnimationEnd", f);
};
element.addEventListener("animationend", f);
element.addEventListener("webkitAnimationEnd", f);
}
/**
* 淡出隐藏
* @param element
*/
function fadeOutHide(element) {
element.classList.add("el-fade-leave", "el-fade-leave-active");
const f = () => {
element.classList.remove("el-fade-leave", "el-fade-leave-active");
element.classList.add("el-hidden");
element.removeEventListener("animationend", f);
element.removeEventListener("webkitAnimationEnd", f);
};
element.addEventListener("animationend", f);
element.addEventListener("webkitAnimationEnd", f);
}
/**
* 淡出删除
* @param element
*/
function fadeOutRemove(element) {
element.classList.add("el-fade-leave", "el-fade-leave-active");
const f = () => {
aniRemove(element, false, 0);
element.removeEventListener("animationend", f);
element.removeEventListener("webkitAnimationEnd", f);
};
element.addEventListener("animationend", f);
element.addEventListener("webkitAnimationEnd", f);
}
/**
* 单选选择器
* @param selector
* @param { Document } element
* @returns { * }
*/
function $one(selector, element = document) {
return element.querySelector(selector);
}
/**
* 多选选择器
* @param selector
* @param element
* @returns { NodeListOf<*> }
*/
function $all(selector, element = document) {
return element.querySelectorAll(selector);
}
/**
* 安全删除
* @param { string } selector
* @param { boolean } withAni
* @param { number } wait
* @returns { void }
*/
function safeRemove(selector, withAni = false, wait = 200) {
safeFunction(() => {
let removeNodes = document.querySelectorAll(selector);
for (let i = 0; i < removeNodes.length; i++) {
aniRemove(removeNodes[i], withAni, wait);
}
});
}
/**
* 动画删除element
* @param { Element } node
* @param { boolean } withAni
* @param { number } wait
* @returns { void }
*/
function aniRemove(node, withAni = false, wait = 200) {
if (withAni) {
node.classList.add('aniDelete');
setTimeout(() => {
node.remove();
}, wait);
} else {
node.remove();
}
}
/**
* 安全运行方法
* @param func
* @param failCb
* @returns { void }
*/
function safeFunction(func, failCb) {
try {
func();
} catch (e) {
failCb && failCb(e);
}
}
/**
*
* @param callback 回调函数, 需要返回是否, True: 结束、False: 相当于定时器
* callback return:
* true = 倒计时
* false = 计时器
* @param { number } period 周期,如: 200ms
* @param { boolean } now 立即执行
* @param { number } count 次数, -1: Infinity
* @returns { void }
*/
function retryInterval(callback, period = 50, now = false, count = -1) {
if (now && count-- !== 0) {
if (callback()) return;
}
const inter = setInterval(() => {
if (count-- === 0) {
return clearInterval(inter);
}
callback() && clearInterval(inter);
}, period);
}
/**
* 等待选择器运行
* @param selector
* @param callbackFunc
* @param time
* @param notClear
* @returns { void }
*/
function safeWaitFunc(selector, callbackFunc, time = 60, notClear = false) {
notClear = notClear || false;
let doClear = !notClear;
retryInterval(() => {
if ((typeof (selector) === "string" && document.querySelector(selector) != null)) {
callbackFunc(document.querySelector(selector));
if (doClear) return true;
} else if (typeof (selector) === "function" && (selector() != null || (selector() || []).length > 0)) {
callbackFunc(selector()[0]);
if (doClear) return true;
}
}, time, true);
}
/**
* 生成n位数字字母混合字符串
* @param n
* @returns { string }
*/
function generateMixed(n) {
const chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
let res = "";
for (let i = 0; i < n; i++) {
const id = Math.floor(Math.random() * chars.length);
res += chars[id];
}
return res;
}
/**
* 睡眠
* @param milliseconds
* @returns { Promise<void> }
*/
function sleep(milliseconds) {
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, milliseconds);
});
}
/**
* 是否是数组
* @param arr
* @returns { boolean }
*/
function isArray(arr) {
const toString = Object.prototype.toString;
const isArray = Array.isArray || function (arg) {
return toString.call(arg) === '[object Array]';
};
return isArray(arr);
}
/**
* 是否是对象
* @param val
* @returns { boolean }
*/
function isObject(val) {
return typeof val === 'object' && val !== null && Array.isArray(val) === false;
}
/**
* 过滤空字段
* @param obj
* @returns { {} }
*/
function filterNullUndefined(obj) {
if (!isObject(obj)) {
return {};
}
return Object.entries(obj).reduce((acc, [key, value]) => {
if (value !== null && value !== undefined && value !== '') {
acc[key] = value;
}
return acc;
}, {});
}
/**
* 递归获取
* @param key
* @param source
* @returns { {} | * }
*/
function acquireChain(key = [], source = {}) {
if (!isArray(key)) {
return {};
}
return key.reduce((res, k) => {
if (!res) return {};
return res[k];
}, source);
}
/**
* 是否时期已过
* @param timestamp
* @param time
* @returns { boolean }
*/
function isExpired(timestamp, time) {
const now = Date.now();
return now - timestamp * 1000 > time;
}
/**
* 请求
* @param { string } path
* @param { string } method
* @param { {} } data
* @param { {} } headers
* @returns { Promise<any> }
*/
function request(path, method = 'GET', data = {}, headers = {}) {
/**
* 转化请求参数
* @param data
* @returns { string }
*/
function transform(data = {}) {
const esc = encodeURIComponent;
// 转换参数
return Object.keys(data)
.map(k => `${esc(k)}=${esc(data[k])}`)
.join('&');
}
method = method.toLocaleUpperCase();
const config = {
method, mode: 'cors', // 跨域
credentials: 'include', // 允许携带cookie
headers: Object.assign({
'Content-type': ContentType.form
}, headers)
};
// 如果是get请求
if (method === 'GET') {
// 转换拼接get参数
let param = transform(data);
if (param) path += `?${param}`;
} else if (config.headers["Content-type"] === ContentType.form) {
config.body = transform(data);
} else {
// 其他请求 放入body里面
config.body = JSON.stringify(data);
}
return fetch(path, config).then(response => response.json());
}
/**
* 开关按钮切换
* @param e
*/
function arcoSwitchChange(e) {
const $ev = e || window.event;
const target = $ev.target || $ev.srcElement;
const btn = target.closest(".arco-switch");
if (!btn) return;
if (btn.classList.contains("arco-switch-checked")) {
btn.classList.remove("arco-switch-checked");
btn.setAttribute("aria-checked", false);
} else {
btn.classList.add("arco-switch-checked");
btn.setAttribute("aria-checked", true);
}
btn.dispatchEvent(new Event('change'));
}
/**
* 内部检查
* @param e
* @param selector
* @returns { * }
*/
function insideCheck(e, selector) {
const $ev = e || window.event;
const target = $ev.target || $ev.srcElement;
return target.closest(selector);
}