import { ElLoading, ElMessage } from 'element-plus';
import type { Column } from 'element-plus';
import { i18n } from '@/i18n/index';
const t = i18n.global.t;
* Parse the time to string
* @param {(Object|string|number)} time
* @param {string} cFormat
* @returns {string | null}
*/
export function parseTime(time, cFormat) {
if (arguments.length === 0 || !time) {
return null;
}
const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}';
let date;
if (typeof time === 'object') {
date = time;
} else {
if (typeof time === 'string') {
if (/^[0-9]+$/.test(time)) {
time = parseInt(time);
} else {
time = time.replace(new RegExp(/-/gm), '/');
}
}
if (typeof time === 'number' && time.toString().length === 10) {
time = time * 1000;
}
date = new Date(time);
}
const formatObj = {
y: date.getFullYear(),
m: date.getMonth() + 1,
d: date.getDate(),
h: date.getHours(),
i: date.getMinutes(),
s: date.getSeconds(),
a: date.getDay(),
};
const time_str = format.replace(/{([ymdhisa])+}/g, (result, key) => {
const value = formatObj[key];
if (key === 'a') {
return [
t('week.Sunday'),
t('week.Monday'),
t('week.Tuesday'),
t('week.Wednesday'),
t('week.Thursday'),
t('week.Friday'),
t('week.Saturday'),
][value];
}
return value.toString().padStart(2, '0');
});
return time_str;
}
* @param {string} url
* @returns {Object}
*/
export function getQueryObject(url) {
url = url == null ? window.location.href : url;
const search = url.substring(url.lastIndexOf('?') + 1);
const obj = {};
const reg = /([^?&=]+)=([^?&=]*)/g;
search.replace(reg, (rs, $1, $2) => {
const name = decodeURIComponent($1);
let val = decodeURIComponent($2);
val = String(val);
obj[name] = val;
return rs;
});
return obj;
}
* @param {string} input value
* @returns {number} output value
*/
export function byteLength(str) {
let s = str.length;
for (let i = str.length - 1; i >= 0; i--) {
const code = str.charCodeAt(i);
if (code > 0x7f && code <= 0x7ff) s++;
else if (code > 0x7ff && code <= 0xffff) s += 2;
if (code >= 0xdc00 && code <= 0xdfff) i--;
}
return s;
}
* @param {Array} actual
* @returns {Array}
*/
export function cleanArray(actual) {
const newArray = [];
for (let i = 0; i < actual.length; i++) {
if (actual[i]) {
newArray.push(actual[i]);
}
}
return newArray;
}
* @param {Object} json
* @returns {Array}
*/
export function param(json) {
if (!json) return '';
return cleanArray(
Object.keys(json).map((key) => {
if (json[key] === undefined) return '';
return encodeURIComponent(key) + '=' + encodeURIComponent(json[key]);
}),
).join('&');
}
* @param {string} url
* @returns {Object}
*/
export function param2Obj(url) {
const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ');
if (!search) {
return {};
}
const obj = {};
const searchArr = search.split('&');
searchArr.forEach((v) => {
const index = v.indexOf('=');
if (index !== -1) {
const name = v.substring(0, index);
const val = v.substring(index + 1, v.length);
obj[name] = val;
}
});
return obj;
}
* @param {string} val
* @returns {string}
*/
export function html2Text(val) {
const div = document.createElement('div');
div.innerHTML = val;
return div.textContent || div.innerText;
}
* Merges two objects, giving the last one precedence
* @param {Object} target
* @param {(Object|Array)} source
* @returns {Object}
*/
export function objectMerge(target, source) {
if (typeof target !== 'object') {
target = {};
}
if (Array.isArray(source)) {
return source.slice();
}
Object.keys(source).forEach((property) => {
const sourceProperty = source[property];
if (typeof sourceProperty === 'object') {
target[property] = objectMerge(target[property], sourceProperty);
} else {
target[property] = sourceProperty;
}
});
return target;
}
* @param {HTMLElement} element
* @param {string} className
*/
export function toggleClass(element, className) {
if (!element || !className) {
return;
}
let classString = element.className;
const nameIndex = classString.indexOf(className);
if (nameIndex === -1) {
classString += '' + className;
} else {
classString =
classString.substr(0, nameIndex) + classString.substr(nameIndex + className.length);
}
element.className = classString;
}
* @param {string} type
* @returns {Date}
*/
export function getTime(type) {
if (type === 'start') {
return new Date().getTime() - 3600 * 1000 * 24 * 90;
} else {
return new Date(new Date().toDateString());
}
}
* @param {Function} func
* @param {number} wait
* @param {boolean} immediate
* @return {*}
*/
export function debounce(func, wait, immediate) {
let timeout, args, that, timestamp, result;
const later = function () {
const last = +new Date() - timestamp;
if (last < wait && last > 0) {
timeout = setTimeout(later, wait - last);
} else {
timeout = null;
if (!immediate) {
result = func.apply(that, args);
if (!timeout) that = args = null;
}
}
};
return function (...args) {
that = this;
timestamp = +new Date();
const callNow = immediate && !timeout;
if (!timeout) timeout = setTimeout(later, wait);
if (callNow) {
result = func.apply(that, args);
that = null;
}
return result;
};
}
* This is just a simple version of deep copy
* Has a lot of edge cases bug
* If you want to use a perfect deep copy, use lodash's _.cloneDeep
* @param {Object} source
* @returns {Object}
*/
export function deepClone(source) {
if (!source && typeof source !== 'object') {
throw new Error('deepClone: error arguments');
}
const targetObj = source.constructor === Array ? [] : {};
Object.keys(source).forEach((keys) => {
if (source[keys] && typeof source[keys] === 'object') {
targetObj[keys] = deepClone(source[keys]);
} else {
targetObj[keys] = source[keys];
}
});
return targetObj;
}
* @param {Array} arr
* @returns {Array}
*/
export function uniqueArr(arr) {
return Array.from(new Set(arr));
}
* @returns {string}
*/
export function createUniqueString() {
const timestamp = +new Date() + '';
const randomNum = parseInt((1 + Math.random()) * 65536 + '') + '';
return (+(randomNum + timestamp)).toString(32);
}
* Check if an element has a class
* @param {HTMLElement} elm
* @param {string} cls
* @returns {boolean}
*/
export function hasClass(ele, cls) {
return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'));
}
* Add class to element
* @param {HTMLElement} elm
* @param {string} cls
*/
export function addClass(ele, cls) {
if (!hasClass(ele, cls)) ele.className += ' ' + cls;
}
* Remove class from element
* @param {HTMLElement} elm
* @param {string} cls
*/
export function removeClass(ele, cls) {
if (hasClass(ele, cls)) {
const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)');
ele.className = ele.className.replace(reg, ' ');
}
}
export function getColor() {
let str = '#';
const arr = ['1', '2', '3', '4', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
for (let i = 0; i < 6; i++) {
const num = parseInt('' + Math.random() * 16);
str += arr[num];
}
return str;
}
export const isArray = function (value) {
return objToString.call(value) === '[object Array]';
};
const funProto = Function.prototype;
const objProto = Object.prototype;
const getPrototypeOf = Object.getPrototypeOf;
const objToString = objProto.toString;
const hasOwnProperty = objProto.hasOwnProperty;
const funToString = funProto.toString;
export const isString = function (value) {
return objToString.call(value) === '[object String]';
};
export const isPlainObject = function (value) {
if (!value || objToString.call(value) !== '[object Object]') {
return false;
}
const prototype = getPrototypeOf(value);
if (prototype === null) {
return true;
}
const constructor = hasOwnProperty.call(prototype, 'constructor') && prototype.constructor;
return (
typeof constructor === 'function' && funToString.call(constructor) === funToString.call(Object)
);
};
export const deepObjClone = function (obj) {
const weakMap = new WeakMap();
function clone(obj) {
if (obj == null) {
return obj;
}
if (obj instanceof Date) {
return new Date(obj);
}
if (obj instanceof RegExp) {
return new RegExp(obj);
}
if (typeof obj !== 'object') return obj;
if (weakMap.get(obj)) {
return weakMap.get(obj);
}
const copy = new obj.constructor();
weakMap.set(obj, copy);
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
const value = obj[key];
copy[key] = clone(value);
}
}
return copy;
}
return clone(obj);
};
* color: hexToRgb
* @param str color string
* @returns Processed color value
*/
export function hexToRgb(str: any) {
let hexs: any = '';
const reg = /^#?[0-9A-Fa-f]{6}$/;
if (!reg.test(str)) return ElMessage.warning(t('utils.hexError'));
str = str.replace('#', '');
hexs = str.match(/../g);
for (let i = 0; i < 3; i++) hexs[i] = parseInt(hexs[i], 16);
return hexs;
}
* color: rgbToHex
* @param r red
* @param g green
* @param b blue
* @returns Processed color value
*/
export function rgbToHex(r: any, g: any, b: any) {
const reg = /^\d{1,3}$/;
if (!reg.test(r) || !reg.test(g) || !reg.test(b))
return ElMessage.warning(t('utils.colorError', 'rgb'));
const hexs = [r.toString(16), g.toString(16), b.toString(16)];
for (let i = 0; i < 3; i++) if (hexs[i].length == 1) hexs[i] = `0${hexs[i]}`;
return `#${hexs.join('')}`;
}
* Deepen Color Value
* @param color color string
* @param level deepening-level: 0~1
* @returns Processed color value
*/
export function getDarkColor(color: string, level: number) {
const reg = /^#?[0-9A-Fa-f]{6}$/;
if (!reg.test(color)) return ElMessage.warning(t('utils.colorError', 'hex'));
const rgb = hexToRgb(color);
for (let i = 0; i < 3; i++) rgb[i] = Math.floor(rgb[i] * (1 - level));
return rgbToHex(rgb[0], rgb[1], rgb[2]);
}
* Lighten Color Value
* @param color color string
* @param level deepening-level: 0~1
* @returns Processed color value
*/
export function getLightColor(color: string, level: number) {
const reg = /^#?[0-9A-Fa-f]{6}$/;
if (!reg.test(color)) return ElMessage.warning(t('utils.colorError', 'hex'));
const rgb = hexToRgb(color);
for (let i = 0; i < 3; i++) rgb[i] = Math.floor((255 - rgb[i]) * level + rgb[i]);
return rgbToHex(rgb[0], rgb[1], rgb[2]);
}
export const uuid = () => Math.random().toString(36).slice(2);
export const loadingInstance = () => {
const loading = ElLoading.service({
lock: true,
text: t('common.loadData'),
background: 'rgba(255, 255, 255, 0.7)',
});
return loading;
};
* dateFormat
* @param date
* @param fmt format, example: 'yyyy-MM-dd hh:mm:ss'
* @returns Processed date
*/
export const dateFormat = (date, fmt) => {
date = new Date(date);
const o = {
'M+': date.getMonth() + 1,
'd+': date.getDate(),
'h+': date.getHours(),
'm+': date.getMinutes(),
's+': date.getSeconds(),
'q+': Math.floor((date.getMonth() + 3) / 3),
S: date.getMilliseconds(),
w: date.getDay(),
T: 'T',
};
if (/(y+)/.test(fmt)) {
fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
}
for (const 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;
};
* format el-table-v2 data
* @param columns example: ['col1', 'col2', 'col3']
* @param data example: [[1,2,3],[4,5,6]] => 2row,3col
* @param config example: { showIndex: true, indexName: 'No.', }
* @returns Processed: columns,data
* columns: [{title: 'col1', dataKey: 'col1', width: 150}...]
* data: [{col1: 1, col2: 2, col3: 3}]
*/
interface FormatTableV2Options {
showIndex?: boolean;
indexName?: string;
columnWidth?: number;
}
export const formatTableV2Data = (
columns: string[],
data: (string | number)[][],
config?: FormatTableV2Options,
) => {
const defaultConfig = {
showIndex: false,
indexName: '',
};
config = { ...defaultConfig, ...config };
let columnWidth;
if (config.columnWidth) {
columnWidth = config.columnWidth;
} else if (columns.length == 1) {
columnWidth = 300;
} else if (columns.length == 2) {
columnWidth = 230;
} else if (columns.length == 3) {
columnWidth = 180;
} else {
columnWidth = 150;
}
let colKeyMap = {};
const myColumns: Column<any>[] = columns.map((col) => {
* The query result may contain the same column with different values.
* In this case, the column key needs to distinguish between them
* e.g. columns = ['CREATED_AT', '?column?', '?column?', '?column?', '?column?', '?column?']
* myColumns = [{title: 'CREATED_AT', key: 'CREATED_AT', dataKey: 'CREATED_AT', width: xxx}
{title: '?column?', key: '?column?', dataKey: '?column?', width: xxx}
{title: '?column?', key: '?column?1', dataKey: '?column?', width: xxx}
{title: '?column?', key: '?column?2', dataKey: '?column?', width: xxx}
{title: '?column?', key: '?column?3', dataKey: '?column?', width: xxx}
{title: '?column?', key: '?column?4', dataKey: '?column?', width: xxx}]
*/
let colKey = col;
if (colKeyMap[col]) {
colKey += colKeyMap[col];
colKeyMap[col] ++;
} else {
colKeyMap[col] = 1;
}
return {
title: col,
key: colKey,
dataKey: col,
width: columnWidth,
};
});
const myData = data.map((rowData) => {
const obj = {};
myColumns.forEach((col, colIndex) => {
obj[col.key] = rowData[colIndex];
});
return obj;
});
if (config.showIndex) {
myColumns.unshift({
title: config.indexName || '',
key: 'index',
width: 50,
cellRenderer: ({ rowIndex }) => `${rowIndex + 1}` as unknown as VNode,
});
}
return {
columns: myColumns,
data: myData,
};
};
* format el-table data
* @param columns example: ['col1', 'col2', 'col3']
* @param data example: [[1,2,3],[4,5,6]] => 2row,3col
* @returns Processed data:[{col1: 1, col2: 2}...]
*/
export const formatTableData = (columns: string[], data: (string | number)[][]) => {
return data.map((rowData, rowIndex) => {
const obj = {
id: rowIndex + 1,
};
columns.forEach((col, colIndex) => {
obj[col] = rowData[colIndex];
});
return obj;
});
};
* format el-table data & width of columns
* @param columns example: ['col1', 'col2', 'col3']
* @param data example: [[1,2,3],[4,5,6]] => 2row,3col
* @returns Processed { columns: [{label: 'col1', columnKey: 'col1', minWidth: 150}...], data:[{col1: 1, col2: 2}...]}
*/
interface FormatTableData2Option {
indexName?: string;
columnWidth?: number;
}
export const formatTableDataAndColumns = (
columns: string[],
data: (string | number)[][],
config: FormatTableData2Option = {},
) => {
let columnWidth;
if (config.columnWidth) {
columnWidth = config.columnWidth;
} else if (columns.length == 1) {
columnWidth = 300;
} else if (columns.length == 2) {
columnWidth = 230;
} else if (columns.length == 3) {
columnWidth = 180;
} else {
columnWidth = 150;
}
const myColumns = columns.map((col) => {
return {
label: col,
prop: col,
columnKey: col,
minWidth: columnWidth,
};
});
const myData = data.map((rowData, rowIndex) => {
const obj = {};
columns.forEach((col, colIndex) => {
config.indexName && (obj[config.indexName] = rowIndex + 1);
obj[col] = rowData[colIndex];
});
return obj;
});
return {
columns: myColumns,
data: myData,
};
};
export const formatEditTableData = (config: {
columns: any[];
data: (string | number)[][];
idKey?: string;
rowStatusKey?: string;
editingSuffix?: string;
editedSuffix?: string;
}) => {
const options = {
editingSuffix: '_isEditing',
editedSuffix: '_edited',
...config,
};
return options.data.map((rowData, rowIndex) => {
const obj = {};
options.columns.forEach((col, colIndex) => {
obj[col.name] = rowData[colIndex];
obj[col.name + options.editingSuffix] = false;
obj[col.name + options.editedSuffix] = false;
if (options.idKey) obj[options.idKey] = rowIndex;
if (options.rowStatusKey) obj[options.rowStatusKey] = '';
});
return obj;
});
};
export const getFlexColumnWidth = (
data: Record<string, any>[],
key: string,
isCalculateCharWidth?: boolean,
) => {
const minWidth = 90;
const maxWidth = 400;
const maxCharLength = 50 * 3;
key = key + '';
if (!data || !data.length || data.length === 0 || data === undefined) {
return minWidth;
}
if (!key || !key.length || key.length === 0 || key === undefined || typeof key == 'object') {
return minWidth;
}
let index = 0;
for (let i = 0; i < data.length; i++) {
if ([null, undefined, ''].includes(data[i][key])) {
continue;
}
const now_temp = data[i][key] + '';
const max_temp = data[index][key] + '';
if (now_temp.length > max_temp.length) {
index = i;
}
if ((data[index][key] + '').length >= maxCharLength) {
return maxWidth;
}
}
let maxColumnContent = data[index][key] ?? '';
maxColumnContent += '';
let flexWidth = 0;
if (isCalculateCharWidth) {
for (const char of maxColumnContent) {
if ((char >= 'A' && char <= 'Z') || (char >= 'a' && char <= 'z')) {
flexWidth += 8;
} else if (char >= '\u4e00' && char <= '\u9fa5') {
flexWidth += 15;
} else {
flexWidth += 8;
}
}
} else {
flexWidth = maxColumnContent.length * 8;
}
if (flexWidth <= minWidth) {
flexWidth = minWidth;
} else if (flexWidth <= 200) {
flexWidth = 100;
} else if (flexWidth <= 300) {
flexWidth = 200;
} else if (flexWidth <= 400) {
flexWidth = 300;
} else {
flexWidth = maxWidth;
}
return flexWidth;
};
export const dispatchEventStorage = () => {
const setItemSign = localStorage.setItem;
localStorage.setItem = function (...arg: string[]) {
const setEvent = new Event('setItemEvent') as any;
setEvent.key = arg[0];
setEvent.value = arg[1];
setItemSign.apply(this, arg);
window.dispatchEvent(setEvent);
};
};
export const manualStringify = (value) => {
let cache = [];
const str = JSON.stringify(value, function (key, value) {
if (typeof value === 'object' && value !== null) {
if (cache.indexOf(value) !== -1) {
return;
}
cache.push(value);
}
return value;
});
cache = null;
return str;
};
* Update only the first letter of the first word in the sentence to uppercase
* @example 'hello world' => 'Hello world'
*/
export const upperSentenceFirstLetter = (str: string): string => {
return str.slice(0, 1).toUpperCase() + str.slice(1).toLowerCase();
};
* Capitalize the first letter of each word
* @example 'hello world' => 'Hello World'
*/
export const upperWordFirstLetter = (str: string): string => {
const strArr = str.split(' ');
for (let i = 0; i < strArr.length; i++) {
strArr[i] = strArr[i].substring(0, 1).toUpperCase() + strArr[i].toLowerCase().substring(1);
}
return strArr.join(' ');
};
export const isBodyElement = (node: Element): boolean => {
return node && node.nodeType == 1 && node.tagName.toLowerCase() == 'body';
};
export const findParentElement = (el: Element | null, parentClassName: string): Element | null => {
if (!el) return null;
if (isBodyElement(el)) return null;
if (el.classList.contains(parentClassName)) return el;
return findParentElement(el.parentElement, parentClassName);
};
* @param str string
* @returns string
* @example 'abcDef' => 'Abc Def'
* @example ' abc Def' => 'Abc Def'
*/
export const toSpacePascalCase = (str: string): string => {
const toUppercase = (str: string) => str.toUpperCase();
return str
.replace(/\B[A-Z]/g, ' $&')
.trim()
.replace(/^./, toUppercase)
.replace(/\b\w/g, toUppercase);
};
export const downloadHtml = (htmlContent: BlobPart, fileName?: string): void => {
const blob = new Blob([htmlContent], { type: 'text/html' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = fileName || 'download.html';
link.click();
URL.revokeObjectURL(url);
};
export const simpleDownloadUrl = async (url: string, fileName?: string): Promise<void> => {
const link = document.createElement('a');
link.href = url;
link.download = fileName || '';
link.click();
};
export const downLoadURL = (url: string | Blob, fileName: string): void => {
const elink = document.createElement('a');
elink.download = fileName;
elink.style.display = 'none';
if (url instanceof Blob) {
elink.href = URL.createObjectURL(url);
} else {
elink.href = url;
}
document.body.appendChild(elink);
elink.click();
URL.revokeObjectURL(elink.href);
document.body.removeChild(elink);
};
export const downLoadURLBlob = async (url: string, fileName?: string): Promise<void> => {
if (!fileName) {
const arr = url.split('?')[0].split('/');
fileName = arr[arr.length - 1];
}
const resp = await fetch(url);
if (!resp) return;
const eleA = document.createElement('a');
const blob = await resp.blob();
if (!blob) return;
eleA.href = URL.createObjectURL(blob);
eleA.download = fileName;
eleA.click();
URL.revokeObjectURL(eleA.href);
};
export const downLoadMyBlobType = (fileName: string, data: BlobPart): void => {
const blob = new Blob([data]);
const tag = document.createElement('a');
tag.href = window.URL.createObjectURL(blob);
tag.download = fileName;
tag.click();
URL.revokeObjectURL(tag.href);
};
export const copyToClickBoard = (text: string): void => {
const el = document.createElement('textarea');
el.value = text;
document.body.appendChild(el);
el.select();
document.execCommand('copy');
document.body.removeChild(el);
};