build / build (push) Successful in 1m57s
- 推送/操作/开放接口日志列表各加"大小"列(payload_size/response_size,Utils.getFileSize 格式化)
- 详情弹窗:下载按钮常驻;Utils.canPreview(≤50KB) 才显示"在线预览",点击 fetch 复用各自 /Download/{id} 取原文渲染
- oapilog 详情从无条件内联改为同一套门槛逻辑
- utils 新增 PREVIEW_MAX_BYTES 与 canPreview
367 lines
11 KiB
TypeScript
367 lines
11 KiB
TypeScript
import { IObj, UseTableSetting } from "@/types/Basics";
|
|
import { resolveComponent } from "vue";
|
|
export default class Utils {
|
|
/**
|
|
* 格式化日期
|
|
*
|
|
* @param {(Date | string)} 日期
|
|
* @param {string} fmt 格式化字符串
|
|
* @returns {string} 日期字符串
|
|
* @memberof Utils
|
|
*/
|
|
public static dateFormat(date: Date | string, fmt: string): string {
|
|
if (typeof date === "string") date = date.replace(/\-/g, "/");
|
|
let dt = new Date(date);
|
|
let o: IObj = {
|
|
"M+": dt.getMonth() + 1, //月份
|
|
"d+": dt.getDate(), //日
|
|
"H+": dt.getHours(), //小时
|
|
"m+": dt.getMinutes(), //分
|
|
"s+": dt.getSeconds(), //秒
|
|
"q+": Math.floor((dt.getMonth() + 3) / 3), //季度
|
|
S: dt.getMilliseconds(), //毫秒
|
|
};
|
|
if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (dt.getFullYear() + "").substr(4 - RegExp.$1.length));
|
|
|
|
for (let k in o)
|
|
if (new RegExp("(" + k + ")").test(fmt))
|
|
fmt = fmt.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length));
|
|
return fmt;
|
|
}
|
|
|
|
/**
|
|
* 获取地址栏指定参数
|
|
*/
|
|
public static getUrlParam(name: string): string | null {
|
|
let reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
|
|
let r = window.location.search.substr(1).match(reg);
|
|
if (r != null) return unescape(r[2]);
|
|
else return null;
|
|
}
|
|
|
|
/**
|
|
* 生成uuid
|
|
*/
|
|
public static uuid(): string {
|
|
return "xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx".replace(/[xy]/g, function (c) {
|
|
let r = (Math.random() * 16) | 0,
|
|
v = c == "x" ? r : (r & 0x3) | 0x8;
|
|
return v.toString(16);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 返回文件大小
|
|
*/
|
|
public static getFileSize(size: number): string {
|
|
if (!size) return "";
|
|
if (size / 1024 < 1024) {
|
|
size = Math.ceil(size / 1024);
|
|
return size + "KB";
|
|
} else if (size / (1024 * 1024) < 1024) {
|
|
size = Math.ceil((size / (1024 * 1024)) * 100) / 100;
|
|
return size + "MB";
|
|
} else if (size / (1024 * 1024 * 1024) < 1024) {
|
|
size = Math.ceil((size / (1024 * 1024 * 1024)) * 100) / 100;
|
|
return size + "GB";
|
|
} else return "";
|
|
}
|
|
|
|
/**
|
|
* 在线预览大小上限(字节):日志大字段(请求体/响应)超过此值时隐藏"在线预览"、只保留"下载"。
|
|
* 浏览器 JSON 美化 + <pre> 渲染在数百 KB 起会卡顿,50KB 为体验无损的保守阈值。
|
|
*/
|
|
public static readonly PREVIEW_MAX_BYTES = 50 * 1024;
|
|
|
|
/**
|
|
* 是否允许在线预览:有内容(size > 0)且字节数不超过阈值。
|
|
* size 为 null/undefined/0(未记录请求体、saveResponse=false、响应为空)→ 不可预览,仅下载。
|
|
*/
|
|
public static canPreview(size?: number | null): boolean {
|
|
return size != null && size > 0 && size <= Utils.PREVIEW_MAX_BYTES;
|
|
}
|
|
|
|
/**
|
|
* 删除指定键
|
|
*/
|
|
public static delKey(obj: IObj, ...args: string[]) {
|
|
args.forEach(v => delete obj[v]);
|
|
return obj;
|
|
}
|
|
|
|
/**
|
|
* iview Tooltip封装
|
|
*/
|
|
public static operatingHandle(
|
|
h: any,
|
|
info: string,
|
|
iconClass: string,
|
|
callback: Function,
|
|
disabled: boolean = false,
|
|
) {
|
|
if (disabled) iconClass += " disabled";
|
|
|
|
return h(
|
|
resolveComponent("Tooltip"),
|
|
{
|
|
content: info,
|
|
placement: "bottom",
|
|
transfer: true,
|
|
},
|
|
{
|
|
default: () => [
|
|
h(
|
|
"div",
|
|
{
|
|
class: `operating-btn iconfont ${iconClass}`,
|
|
onClick: callback,
|
|
},
|
|
null,
|
|
),
|
|
],
|
|
},
|
|
);
|
|
}
|
|
|
|
public static operatingTextHandle(
|
|
h: any,
|
|
info: string,
|
|
className: string,
|
|
callback: Function,
|
|
disabled: boolean = false,
|
|
) {
|
|
if (disabled) className += " disabled";
|
|
return h(
|
|
"div",
|
|
{
|
|
class: `operating-link ${className}`,
|
|
onClick: disabled ? null : callback,
|
|
},
|
|
info,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 计算表格高度
|
|
*/
|
|
public static useTableHeight({
|
|
isSearch = false,
|
|
isPage = false,
|
|
isTabs = false,
|
|
isOperat = false,
|
|
otherH = 0,
|
|
} = {}) {
|
|
let bodyH = document.body.clientHeight,
|
|
headerH = 112,
|
|
searchH = 52,
|
|
operatH = 48,
|
|
tabsH = 56,
|
|
pageH = 72,
|
|
paddingH = 8;
|
|
|
|
//@ts-ignore
|
|
const bschUtil = BschLoadUtil.getInstance();
|
|
if (!bschUtil.getIsSelf()) headerH = 56;
|
|
let tableH = bodyH - headerH - paddingH;
|
|
isOperat && (tableH -= operatH);
|
|
isTabs && (tableH -= tabsH);
|
|
isSearch && (tableH -= searchH);
|
|
isPage && (tableH -= pageH);
|
|
otherH > 0 && (tableH -= otherH);
|
|
return tableH;
|
|
}
|
|
/**
|
|
* 数字转大写汉字
|
|
* @param num
|
|
* @returns
|
|
*/
|
|
public static convertToChinaNum(num: number) {
|
|
var arr1 = new Array("零", "一", "二", "三", "四", "五", "六", "七", "八", "九");
|
|
var arr2 = new Array(
|
|
"",
|
|
"十",
|
|
"百",
|
|
"千",
|
|
"万",
|
|
"十",
|
|
"百",
|
|
"千",
|
|
"亿",
|
|
"十",
|
|
"百",
|
|
"千",
|
|
"万",
|
|
"十",
|
|
"百",
|
|
"千",
|
|
"亿",
|
|
); //可继续追加更高位转换值
|
|
if (!num || isNaN(num)) {
|
|
return "零";
|
|
}
|
|
var english = num.toString().split("");
|
|
var result = "";
|
|
for (var i = 0; i < english.length; i++) {
|
|
var des_i = english.length - 1 - i; //倒序排列设值
|
|
result = arr2[i] + result;
|
|
var arr1_index: any = english[des_i];
|
|
result = arr1[arr1_index] + result;
|
|
}
|
|
//将【零千、零百】换成【零】 【十零】换成【十】
|
|
result = result.replace(/零(千|百|十)/g, "零").replace(/十零/g, "十");
|
|
//合并中间多个零为一个零
|
|
result = result.replace(/零+/g, "零");
|
|
//将【零亿】换成【亿】【零万】换成【万】
|
|
result = result.replace(/零亿/g, "亿").replace(/零万/g, "万");
|
|
//将【亿万】换成【亿】
|
|
result = result.replace(/亿万/g, "亿");
|
|
//移除末尾的零
|
|
result = result.replace(/零+$/, "");
|
|
//将【零一十】换成【零十】
|
|
//result = result.replace(/零一十/g, '零十');//貌似正规读法是零一十
|
|
//将【一十】换成【十】
|
|
result = result.replace(/^一十/g, "十");
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* 对象转url参数
|
|
* @param num
|
|
* @returns
|
|
*/
|
|
public static createUrl(url: string, query: any) {
|
|
var temp: any[] = [];
|
|
for (var k in query) {
|
|
if (query[k] != null) temp.push(k + "=" + query[k]);
|
|
}
|
|
return url + "?" + temp.join("&");
|
|
}
|
|
/**
|
|
* 获取当前角色信息
|
|
* @returns
|
|
*/
|
|
public static getCurrentRoleInfo<T>() {
|
|
let currentRole = localStorage.getItem("currentRole");
|
|
if (!currentRole) return null;
|
|
let { schId, roleId, childId, userId } = JSON.parse(decodeURI(currentRole ?? ""));
|
|
return { schId, roleId, childId, userId } as T;
|
|
}
|
|
|
|
/**
|
|
* tree对象转化为数组
|
|
* @param tree
|
|
* @returns
|
|
*/
|
|
public static treeToArray(tree: any[]): any[] {
|
|
return tree.reduce((res, item) => {
|
|
const { children, ...i } = item;
|
|
return res.concat(i, children && children.length ? this.treeToArray(children) : []);
|
|
}, []);
|
|
}
|
|
|
|
/**
|
|
* 数组转化为tree对象
|
|
* @param tree
|
|
* @returns
|
|
*/
|
|
public static arrayToTree(arr: any[], pid: number): any[] {
|
|
return arr
|
|
.filter(item => item.pid === pid)
|
|
.map(item => ({ ...item, children: this.arrayToTree(arr, item.id) }));
|
|
}
|
|
|
|
//字符串的16进制表达
|
|
public static strToHexCharCode(_str: string) {
|
|
if (_str === "") return "";
|
|
var hexCharCode: string[] = [];
|
|
for (var i = 0; i < _str.length; i++) {
|
|
var str = _str.charCodeAt(i).toString(16);
|
|
if (_str.length == 1) str += "0" + _str;
|
|
hexCharCode.push(str);
|
|
}
|
|
return hexCharCode.join("");
|
|
}
|
|
|
|
public static hexToString(hex: string) {
|
|
if (hex === "") return "";
|
|
// 处理奇数长度,补前导零
|
|
if (hex.length % 2 !== 0) {
|
|
hex = "0" + hex;
|
|
[[3]];
|
|
}
|
|
let str = "";
|
|
for (let i = 0; i < hex.length; i += 2) {
|
|
// 每两位解析为一个字符的Unicode值
|
|
const hexPair = hex.substr(i, 2);
|
|
const charCode = parseInt(hexPair, 16);
|
|
str += String.fromCharCode(charCode);
|
|
[[14, 16]];
|
|
}
|
|
return str;
|
|
}
|
|
|
|
/**
|
|
* 将颜色值HEX格式转换为rgb的格式
|
|
* @param {hex} hex 需要转换的rgb字符串
|
|
* @param {number} opacity 透明度
|
|
* @return {string} ;
|
|
*/
|
|
public static hexToRgb(hex: string, opacity: number = 1) {
|
|
let str = hex.replace("#", "");
|
|
if (str.length % 3) {
|
|
return "hex格式不正确!";
|
|
}
|
|
//获取截取的字符长度
|
|
let count = str.length / 3;
|
|
//根据字符串的长度判断是否需要 进行幂次方
|
|
let power = 6 / str.length;
|
|
let r = parseInt("0x" + str.substring(0 * count, 1 * count)) ** power;
|
|
let g = parseInt("0x" + str.substring(1 * count, 2 * count)) ** power;
|
|
let b = parseInt("0x" + str.substring(2 * count)) ** power;
|
|
return `rgb(${r}, ${g}, ${b},${opacity})`;
|
|
}
|
|
|
|
public static removeHtmlTag(str: string) {
|
|
str = str.replace(/<\/?[^>]*>/g, ""); //去除HTML tag
|
|
str = str.replace(/[ | ]*\n/g, "\n"); //去除行尾空白
|
|
str = str.replace(/\n[\s| | ]*\r/g, "\n"); //去除多余空行
|
|
str = str.replace(/ /gi, ""); //去掉
|
|
return str;
|
|
}
|
|
|
|
public static unpackPerms = (bits: number) => [
|
|
!!(bits & 1), // create
|
|
!!(bits & 2), // update
|
|
!!(bits & 4), // move
|
|
!!(bits & 8), // delete
|
|
];
|
|
|
|
public static copyToClipboard(text: string) {
|
|
const input = document.createElement("input");
|
|
input.setAttribute("value", text);
|
|
document.body.appendChild(input);
|
|
input.select();
|
|
try {
|
|
const successful = document.execCommand("copy");
|
|
if (successful) {
|
|
return Promise.resolve();
|
|
} else {
|
|
return Promise.reject();
|
|
}
|
|
} catch (err) {
|
|
return Promise.reject(err);
|
|
} finally {
|
|
document.body.removeChild(input);
|
|
}
|
|
}
|
|
|
|
public static sleep(ms: number, callback?: () => void): Promise<void> {
|
|
return new Promise<void>(resolve => {
|
|
setTimeout(() => {
|
|
callback?.();
|
|
resolve();
|
|
}, ms);
|
|
});
|
|
}
|
|
}
|