You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
dashboard/src/utils/commons.js

563 lines
13 KiB
JavaScript

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// https://github.com/uxitten/polyfill/blob/master/string.polyfill.js
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart
import { Tag } from "antd";
import { CaretUpOutlined, CaretDownOutlined } from "@ant-design/icons";
import moment from "moment";
if (!String.prototype.padStart) {
String.prototype.padStart = function padStart(targetLength, padString) {
targetLength = targetLength >> 0; // floor if number or convert non-number to 0;
padString = String(typeof padString !== "undefined" ? padString : " ");
if (this.length > targetLength) {
return String(this);
} else {
targetLength = targetLength - this.length;
if (targetLength > padString.length) {
padString += padString.repeat(targetLength / padString.length); // append to original to ensure we are longer than needed
}
return padString.slice(0, targetLength) + String(this);
}
};
}
if (!Object.fromEntries) {
Object.fromEntries = function(entries) {
const obj = {};
for (let i = 0; i < entries.length; ++i) {
const entry = entries[i];
if (entry && entry.length === 2) {
obj[entry[0]] = entry[1];
}
}
return obj;
};
}
export function copy(obj) {
return JSON.parse(JSON.stringify(obj));
}
export function named(value) {
return function (target) {
target.definedName = value;
};
}
export function formatCurrency(name) {
if (name === "USD") {
return "$";
} else if (name === "RMB") {
return "¥";
} else if (name === "EUR") {
return "€";
} else if (name === "GBP") {
return "£";
} else {
return name + " ";
}
}
export function formatPrice(price) {
return Math.ceil(price).toLocaleString();
}
// 千分符的金额转成数字默认为0
export function price_to_number(price) {
const num_string = (price + "").replace(/,/g, "");
const number = parseFloat(num_string);
return isNaN(number) ? 0 : number;
}
export function formatPercent(number) {
return Math.round(number * 100) + "%";
}
export function formatDate(date) {
if (isEmpty(date)) {
return "NaN";
}
const year = date.getFullYear();
const month = date.getMonth() + 1;
const day = date.getDate();
const monthStr = ("" + month).padStart(2, 0);
const dayStr = ("" + day).padStart(2, 0);
const formatted = year + "-" + monthStr + "-" + dayStr;
return formatted;
}
export function formatTime(date) {
const hours = date.getHours();
const minutes = date.getMinutes();
const hoursStr = ("" + hours).padStart(2, 0);
const minutesStr = ("" + minutes).padStart(2, 0);
const formatted = hoursStr + ":" + minutesStr;
return formatted;
}
export function formatDatetime(date) {
const year = date.getFullYear();
const month = date.getMonth() + 1;
const day = date.getDate();
const monthStr = ("" + month).padStart(2, 0);
const dayStr = ("" + day).padStart(2, 0);
const hours = date.getHours();
const minutes = date.getMinutes();
const hoursStr = ("" + hours).padStart(2, 0);
const minutesStr = ("" + minutes).padStart(2, 0);
const formatted = year + "-" + monthStr + "-" + dayStr + " " + hoursStr + ":" + minutesStr;
return formatted;
}
export function mixins(...list) {
return function (target) {
list.forEach(val => {
const mixinObj = Object.create(val.prototype, {});
const name = Object.getPrototypeOf(mixinObj).constructor.name;
const camelCase = name.substr(0, 1).toLowerCase() + name.substr(1);
Object.assign(target.prototype, { [camelCase]: mixinObj });
});
};
}
export function camelCase(name) {
return name.substr(0, 1).toLowerCase() + name.substr(1);
}
export class UrlBuilder {
constructor(url) {
this.url = url;
this.paramList = [];
}
append(name, value) {
if (isNotEmpty(value)) {
this.paramList.push({ name, value });
}
return this;
}
build() {
this.paramList.forEach((e, i, a) => {
if (i === 0) {
this.url += "?";
} else {
this.url += "&";
}
this.url += e.name + "=" + e.value;
});
return this.url;
}
}
export function isNotEmpty(val) {
return val !== undefined && val !== null && val !== "";
}
/**
* ! 不支持计算 Set 或 Map
* @param {*} val
* @example
* true if: 0, [], {}, null, '', undefined
* false if: 'false', 'undefined'
*/
export function isEmpty(val) {
// return val === undefined || val === null || val === "";
return [Object, Array].includes((val || {}).constructor) && !Object.entries((val || {})).length;
}
/**
* @example
* empty(0) => false
*/
export function empty(a) {
if (a === "") return true; // 检验空字符串
if (a === "null") return true; // 检验字符串类型的null
if (a === "undefined") return true; // 检验字符串类型的 undefined
if (!a && a !== 0 && a !== "") return true; // 检验 undefined 和 null
if (Array.prototype.isPrototypeOf(a) && a.length === 0) return true; // 检验空数组
if (Object.prototype.isPrototypeOf(a) && Object.keys(a).length === 0) return true; // 检验空对象
return false;
}
export function prepareUrl(url) {
return new UrlBuilder(url);
}
export function debounce(fn, delay = 500) {
let timer;
return e => {
e.persist();
clearTimeout(timer);
timer = setTimeout(() => {
fn(e);
}, delay);
};
}
export function throttle(fn, delay, atleast) {
let timeout = null;
let startTime = new Date();
return function () {
const curTime = new Date();
clearTimeout(timeout);
if (curTime - startTime >= atleast) {
fn();
startTime = curTime;
} else {
timeout = setTimeout(fn, delay);
}
};
}
export function clickUrl(url) {
const httpLink = document.createElement("a");
httpLink.href = url;
httpLink.target = "_blank";
httpLink.click();
}
export function show_vs_tag(vs, vs_diff, data1, data2) {
let tag = "-";
if (parseInt(vs) < 0) {
tag = (
<Tag icon={<CaretDownOutlined />} color="gold">
{vs} {vs_diff}
</Tag>
);
} else if (parseInt(vs) > 0) {
tag = (
<Tag icon={<CaretUpOutlined />} color="lime">
{vs} {vs_diff}
</Tag>
);
}
return (
<span>
<div>
{data1} vs {data2}
</div>
{tag}
</span>
);
}
// 数组去掉重复
export function unique(arr) {
const x = new Set(arr);
return [...x];
}
export function getWeek(date) {
// 参数时间戳
const week = moment(date).day();
switch (week) {
case 1:
return "周一";
case 2:
return "周二";
case 3:
return "周三";
case 4:
return "周四";
case 5:
return "周五";
case 6:
return "周六";
case 0:
return "周日";
}
}
// 把非数字下标的数组设置下标因为非数字数组的length为0导致读取失败
export function set_array_index(result) {
const result_array = [];
const result_keys = Object.keys(result);
result_keys.sort(); // 必须做一次排序用for in循环会导致顺序错误
for (const key of result_keys) {
result_array.push(result[key]);
}
return result_array;
}
/**
* 排序
*/
export const sortBy = (key) => {
return (a, b) => (a[key] > b[key]) ? 1 : ((b[key] > a[key]) ? -1 : 0);
};
export const sortKeys = (obj) =>
Object.keys(obj)
.sort()
.reduce((a, k2) => ({...a, [k2]: obj[k2]}), {});
/**
* 合并Object, 递归地
*/
export function merge(...objects) {
const isDeep = objects.some(obj => obj !== null && typeof obj === 'object');
const result = objects[0] || (isDeep ? {} : objects[0]);
for (let i = 1; i < objects.length; i++) {
const obj = objects[i];
if (!obj) continue;
Object.keys(obj).forEach(key => {
const val = obj[key];
if (isDeep) {
if (Array.isArray(val)) {
result[key] = [].concat(Array.isArray(result[key]) ? result[key] : [result[key]], val);
} else if (typeof val === 'object') {
result[key] = merge(result[key], val);
} else {
result[key] = val;
}
} else {
result[key] = typeof val === 'boolean' ? val : result[key];
}
});
}
return result;
}
/**
* 数组分组
* - 相当于 lodash 的 _.groupBy
* @see https://www.lodashjs.com/docs/lodash.groupBy#_groupbycollection-iteratee_identity
*/
export function groupBy(array, callback) {
return array.reduce((groups, item) => {
const key = callback(item);
if (!groups[key]) {
groups[key] = [];
}
groups[key].push(item);
return groups;
}, {});
}
/**
* 创建一个从 object 中选中的属性的对象。
* @param {*} object
* @param {array} keys
*/
export function pick(object, keys) {
return keys.reduce((obj, key) => {
if (object && Object.prototype.hasOwnProperty.call(object, key)) {
obj[key] = object[key];
}
return obj;
}, {});
}
/**
* 返回对象的副本,经过筛选以省略指定的键。
* @param {*} object
* @param {string[]} keysToOmit
* @returns
*/
export function omit(object, keysToOmit) {
return Object.fromEntries(
Object.entries(object).filter(
([key]) => !keysToOmit.includes(key)
)
);
}
/**
* 深拷贝
*/
export function cloneDeep(value) {
if (typeof value !== 'object' || value === null) {
return value;
}
const result = Array.isArray(value) ? [] : {};
for (const key in value) {
if (Object.prototype.hasOwnProperty.call(value, key)) {
result[key] = cloneDeep(value[key]);
}
}
return result;
}
/**
* 向零四舍五入, 固定精度设置
*/
function curriedFix(precision = 0) {
return function(number) {
// Shift number by precision places
const shift = Math.pow(10, precision);
const shiftedNumber = number * shift;
// Round to nearest integer
const roundedNumber = Math.round(shiftedNumber);
// Shift back decimal place
return roundedNumber / shift;
};
}
/**
* 向零四舍五入, 保留2位小数
*/
export const fixTo2Decimals = curriedFix(2);
/**
* 向零四舍五入, 保留4位小数
*/
export const fixTo4Decimals = curriedFix(4);
export const fixTo1Decimals = curriedFix(1);
export const fixToInt = curriedFix(0);
/**
* 映射
* @example
* const keyMap = {
a: [{key: 'a1'}, {key: 'a2', transform: v => v * 2}],
b: {key: 'b1'}
};
const result = objectMapper({a: 1, b: 3}, keyMap);
// result = {a1: 1, a2: 2, b1: 3}
*
*/
export function objectMapper(input, keyMap) {
// Loop through array mapping
if (Array.isArray(input)) {
return input.map((obj) => objectMapper(obj, keyMap));
}
if (typeof input === 'object') {
const mappedObj = {};
Object.keys(input).forEach((key) => {
// Keep original keys not in keyMap
if (!keyMap[key]) {
mappedObj[key] = input[key];
}
// Handle array of maps
if (Array.isArray(keyMap[key])) {
keyMap[key].forEach((map) => {
let value = input[key];
if (map.transform) value = map.transform(value);
mappedObj[map.key] = value;
});
// Handle single map
} else {
const map = keyMap[key];
if (map) {
let value = input[key];
if (map.transform) value = map.transform(value);
mappedObj[map.key || key] = value;
}
}
});
return mappedObj;
}
return input;
}
/**
* 创建一个对应于对象路径的值数组
*/
export function at(obj, path) {
let result;
if (Array.isArray(obj)) {
// array case
const indexes = path.split('.').map((i) => parseInt(i));
result = [];
for (let i = 0; i < indexes.length; i++) {
result.push(obj[indexes[i]]);
}
} else {
// object case
const indexes = path.split('.').map((i) => i);
result = [obj];
for (let i = 0; i < indexes.length; i++) {
result = [result[0][indexes[i]]];
}
}
return result;
}
/**
* 删除 null/undefined
*/
export function flush(collection) {
let result, len, i;
if (!collection) {
return undefined;
}
if (Array.isArray(collection)) {
result = [];
len = collection.length;
for (i = 0; i < len; i++) {
const elem = collection[i];
if (elem != null) {
result.push(elem);
}
}
return result;
}
if (typeof collection === 'object') {
result = {};
const keys = Object.keys(collection);
len = keys.length;
for (i = 0; i < len; i++) {
const key = keys[i];
const value = collection[key];
if (value != null) {
result[key] = value;
}
}
return result;
}
return undefined;
}
/**
* 千分位 格式化数字
*/
export const numberFormatter = (number) => {
return new Intl.NumberFormat().format(number);
};
export const getNestedValue = (obj, keyArr) => {
return keyArr.reduce((acc, curr) => {
return acc && acc[curr];
}, obj);
};
/**
* 计算笛卡尔积
*/
export const cartesianProductArray = (arr, sep = '_', index = 0, prefix = '') => {
let result = [];
if(index === arr.length){
return [prefix];
}
arr[index].forEach(item => {
result = result.concat(cartesianProductArray(arr, sep, index+1, prefix ? `${prefix}${sep}${item}` : `${item}`));
});
return result;
};