// 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); } }; } 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 = ( } color="gold"> {vs} {vs_diff} ); } else if (parseInt(vs) > 0) { tag = ( } color="lime"> {vs} {vs_diff} ); } return (
{data1} vs {data2}
{tag}
); } // 数组去掉重复 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); }; /** * 合并Object, 递归地 */ export function merge(...objects) { const isDeep = objects.some(obj => obj !== null && typeof obj === 'object'); const result = 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] = [...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; }, {}); } /** * 深拷贝 */ 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); /** * 映射 * @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; }