Files
supersonic/webapp/packages/supersonic-fe/src/utils/utils.ts
tristanliu 157c2999dc [improvement][semantic-fe] Dictionary import for dimension values supported in Q&A visibility (#89)
* [improvement][semantic-fe] Add model alias setting & Add view permission restrictions to the model permission management tab.
[improvement][semantic-fe] Add permission control to the action buttons for the main domain; apply high sensitivity filtering to the authorization of metrics/dimensions.
[improvement][semantic-fe] Optimize the editing mode in the dimension/metric/datasource components to use the modelId stored in the database for data, instead of relying on the data from the state manager.

* [improvement][semantic-fe] Add time granularity setting in the data source configuration.

* [improvement][semantic-fe] Dictionary import for dimension values supported in Q&A visibility
2023-09-14 12:14:02 +08:00

427 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { AUTH_TOKEN_KEY, NumericUnit } from '@/common/constants';
import { message } from 'antd';
import numeral from 'numeral';
import copy from 'copy-to-clipboard';
import { isString } from 'lodash';
/* eslint no-useless-escape:0 */
const reg =
/(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/;
export const isUrl = (path: string): boolean => reg.test(path);
export function copyText(str: string) {
copy(str);
return message.success('复制成功');
}
export function mapToOptions(map: Map<string | number, string>) {
return [...map].map((item) => ({
value: item[0],
label: item[1],
}));
}
export function objToList(obj: any) {
return Object.keys(obj).map((key: string) => {
return {
value: key,
label: obj[key],
};
});
}
// list 转成树形json
export function listToTree(list: any[], parentId: number) {
const ret: any[] = [];
list.forEach((item) => {
if (item.parentId === parentId) {
const data = { ...item };
const leftList = list.filter((l) => l.id !== data.id);
data.children = listToTree(leftList, data.id);
ret.push(data);
}
});
return ret;
}
/**
* 返回格式化后的url params
* @param {string} originUrl 地址例如http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled不传则默认是window.location.href
* @return {*} 针对以上URL返回以下对象
* {
user: 'anonymous',
id: [123, 456], // 重复出现的 key 要组装成数组,能被转成数字的就转成数字类型
city: '北京', // 中文
enabled: true, // 未指定值的 key 约定值为 true
}
* */
export function getUrlParams(originUrl: string = '') {
let url: string;
if (!originUrl) {
url = decodeURIComponent(window.location.href);
} else {
url = decodeURIComponent(originUrl);
}
const index = url.indexOf('?');
if (index === -1) {
return {};
}
const paramString = url.substr(index + 1);
const paramArr = paramString.split('&');
const paramObj = {};
paramArr.forEach((item) => {
const itemArr = item.split('=');
const key = itemArr[0];
const value = itemArr[1];
if (Array.isArray(paramObj[key])) {
paramObj[key].push(value);
} else if (paramObj[key]) {
paramObj[key] = [paramObj[key], value];
} else {
paramObj[key] = value || true;
}
});
return paramObj;
}
/**
* 删除url中的某个参数
* @param {String} URL 地址例如http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled
* @param {String} key 指定的key
* @return {String} URL 删除指定参数后的URL
*/
export function deleteUrlQuery(url = '', key = '') {
const regExp = new RegExp(`[\\&\\?]${key}=([^&#]+)`, 'g');
return url.replace(regExp, '');
}
/**
* 获取权限判断后的树数据(项目树选择等组件会用到)
* @param {Array} treeData
* @param {Function} authFn 权限过滤函数如果不传则默认根据数结构的authCodes字段进行权限识别
* @return {Array} 处理后的树结构数据
*/
type AuthCodes = ('VIEW' | 'EDIT')[];
type AuthFn = {
(authArr: AuthCodes): boolean;
};
type TreeNode = {
disabled?: boolean;
children?: TreeNode[];
authCodes?: AuthCodes;
};
export function getAuthTreeData(treeData: TreeNode[] = [], authFn?: AuthFn) {
const EDIT_KEY = 'EDIT';
return treeData.map((treeNode: any) => {
const item = { ...treeNode };
if (typeof authFn === 'function') {
item.disabled = authFn(item.authCodes);
} else if (!(item.authCodes ?? []).includes(EDIT_KEY)) {
item.disabled = true;
}
if (item.children) {
item.children = getAuthTreeData(item.children, authFn);
}
return item;
});
}
export function changeTreeDataTolongId(treeData: TreeNode[] = []) {
return treeData.map((treeNode: any) => {
const item = { ...treeNode };
item.value = item.projectIncreId;
if (item.children) {
item.children = changeTreeDataTolongId(item.children);
}
return item;
});
}
export type RegisterBdPostMessageData = { from: string; type: string; payload: any };
export function formatNumber(number: number, formatter = '0,0') {
return numeral(number).format(formatter);
}
export function getToken() {
return localStorage.getItem(AUTH_TOKEN_KEY);
}
export function findFirstLeaf(tree: any): any {
if (tree.children.length === 0) {
return tree;
}
for (const child of tree.children) {
if (child.children.length === 0) {
return child;
} else {
const data = findFirstLeaf(child);
if (data) {
return data;
}
}
}
return null;
}
export function jsonParse(config: any, defaultReturn?: any) {
if (!isString(config)) {
return config;
}
if (!config) {
return defaultReturn;
}
try {
return JSON.parse(config);
} catch (error) {
console.log(error);
return defaultReturn;
}
}
/**
* UUID generator
* @param len length number
* @param radix random base number
* @returns {string}
*/
export const uuid = (len: number = 8, radix: number = 62) => {
const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
const uuid = [];
let i;
if (len) {
// Compact form
for (i = 0; i < len; i++) {
uuid[i] = chars[Math.floor(Math.random() * radix)];
}
} else {
// rfc4122, version 4 form
let r;
// rfc4122 requires these characters
uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
uuid[14] = '4';
// Fill in random data. At i==19 set the high bits of clock sequence as
// per rfc4122, sec. 4.1.5
for (i = 0; i < 36; i++) {
if (!uuid[i]) {
r = Math.floor(Math.random() * 16);
uuid[i] = chars[i === 19 ? ((r % 4) % 8) + 8 : r];
}
}
}
return uuid.join('');
};
export const isMobile = window.navigator.userAgent.match(/(iPhone|iPod|Android|ios)/i);
export const updateMessageContainerScroll = (step?: boolean, gap?: number) => {
setTimeout(() => {
const ele: any = document.getElementById('messageContainer');
if (ele && ele.scrollHeight > ele.clientHeight) {
ele.scrollTop = step ? ele.scrollTop + ele.clientHeight - (gap || 130) : ele.scrollHeight;
}
}, 100);
};
export const groupByColumn = (data: any[], column: string) => {
return data.reduce((result, item) => {
const resultData = { ...result };
const key = item[column];
if (!resultData[key]) {
resultData[key] = [];
}
resultData[key].push(item);
return resultData;
}, {});
};
let utilCanvas: any = null;
export const getTextWidth = (
text: string,
fontSize: string = '16px',
fontWeight: string = 'normal',
fontFamily: string = 'DINPro Medium',
): number => {
const canvas = utilCanvas || (utilCanvas = document.createElement('canvas'));
const context = canvas.getContext('2d');
context.font = `${fontWeight} ${fontSize} ${fontFamily}`;
const metrics = context.measureText(text);
return Math.ceil(metrics.width);
};
export function formatByDecimalPlaces(value: number | string, decimalPlaces: number) {
if (isNaN(+value)) {
return value;
}
if (decimalPlaces < 0 || decimalPlaces > 100) {
return value;
}
let str = (+value).toFixed(decimalPlaces);
if (!/^[0-9.]+$/g.test(str)) {
return '0';
}
while (str.includes('.') && (str.endsWith('.') || str.endsWith('0'))) {
str = str.slice(0, -1);
}
return str;
}
export function formatByThousandSeperator(value: number | string) {
if (isNaN(+value)) {
return value;
}
const parts = value.toString().split('.');
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
const formatted = parts.join('.');
return formatted;
}
export function formatByUnit(value: number | string, unit: NumericUnit) {
const numericValue = +value;
if (isNaN(numericValue) || unit === NumericUnit.None) {
return value;
}
let exponent = 0;
switch (unit) {
case NumericUnit.TenThousand:
case NumericUnit.EnTenThousand:
exponent = 4;
break;
case NumericUnit.OneHundredMillion:
exponent = 8;
break;
case NumericUnit.Thousand:
exponent = 3;
break;
case NumericUnit.Million:
exponent = 6;
break;
case NumericUnit.Giga:
exponent = 9;
break;
}
return numericValue / Math.pow(10, exponent);
}
export const getFormattedValueData = (value: number | string, remainZero?: boolean) => {
if (remainZero && (value === undefined || +value === 0)) {
return 0;
}
if (value === undefined) {
return '-';
}
if (!isFinite(+value)) {
return value;
}
const unit =
value >= 100000000
? NumericUnit.OneHundredMillion
: value >= 10000
? NumericUnit.EnTenThousand
: NumericUnit.None;
let formattedValue = formatByUnit(value, unit);
formattedValue = formatByDecimalPlaces(
formattedValue,
unit === NumericUnit.OneHundredMillion ? 2 : value < 1 ? 3 : 1,
);
formattedValue = formatByThousandSeperator(formattedValue);
if ((typeof formattedValue === 'number' && isNaN(formattedValue)) || +formattedValue === 0) {
return '-';
}
return `${formattedValue}${unit === NumericUnit.None ? '' : unit}`;
};
function getLeafNodes(treeNodes: any[]): any[] {
const leafNodes: any[] = [];
function traverse(node: any) {
if (!node.children || node.children.length === 0) {
leafNodes.push(node);
} else {
node.children.forEach((child: any) => traverse(child));
}
}
treeNodes.forEach((node) => traverse(node));
return leafNodes;
}
function buildTree(nodes: any[]): any[] {
const map: Record<number, any> = {};
const roots: any[] = [];
nodes.forEach((node) => {
map[node.id] = node;
node.children = [];
});
nodes.forEach((node) => {
if (node.parentId) {
const parent = map[node.parentId];
if (parent) {
parent.children.push(node);
}
} else {
roots.push(node);
}
});
return roots;
}
export function getLeafList(flatNodes: any[]): any[] {
const treeNodes = buildTree(flatNodes);
const leafNodes = getLeafNodes(treeNodes);
return leafNodes;
}
export function traverseRoutes(routes, env: string, result: any[] = []) {
if (!Array.isArray(routes)) {
return result;
}
for (let i = 0; i < routes.length; i++) {
const route = routes[i];
if (route.envRedirect) {
route.redirect = route.envRedirect[env];
}
if (route.routes) {
const filteredRoutes = traverseRoutes(route.routes, env);
if (Array.isArray(filteredRoutes) && filteredRoutes.length > 0) {
result.push({
...route,
routes: filteredRoutes,
});
}
} else if (
(route.envEnableList &&
(route.envEnableList.includes(env) || route.envEnableList.length === 0)) ||
!route.envEnableList
) {
result.push(route);
}
}
return result;
}
export function isProd() {
return process.env.NODE_ENV === 'production';
}