lodash#values TypeScript Examples
The following examples show how to use
lodash#values.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: utils.ts From prism-frontend with MIT License | 7 votes |
getActiveFeatureInfoLayers = (map: Map): WMSLayerProps[] => {
const matchStr = 'layer-';
const layerIds =
map
.getStyle()
.layers?.filter(l => l.id.startsWith(matchStr))
.map(l => l.id.split(matchStr)[1]) ?? [];
if (layerIds.length === 0) {
return [];
}
const featureInfoLayers = Object.values(LayerDefinitions).filter(
l => layerIds.includes(l.id) && l.type === 'wms' && l.featureInfoProps,
);
if (featureInfoLayers.length === 0) {
return [];
}
return featureInfoLayers as WMSLayerProps[];
}
Example #2
Source File: find-classes-matching-type.ts From ui5-language-assistant with Apache License 2.0 | 6 votes |
export function findClassesMatchingType({
type,
model,
}: {
type: UI5Class | UI5Interface;
model: UI5SemanticModel;
}): UI5Class[] {
const matchingClasses = pickBy(model.classes, (_) => classIsOfType(_, type));
return values(matchingClasses);
}
Example #3
Source File: mySql-builder.service.ts From EDA with GNU Affero General Public License v3.0 | 6 votes |
public generateInserts(queryData: any) {
let insert = `INSERT INTO ${queryData.tableName} VALUES\n`;
queryData.data.forEach((register) => {
let row = '('
Object.values(register).forEach((value: any, i) => {
const type = queryData.columns[i].type;
if (type === 'text') {
row += `'${value.replace(/'/g, "''")}',`;
} else if (type === 'timestamp') {
let date = value ? `STR_TO_DATE('${value}', '${this.getMysqlDateFormat( queryData.columns[i].format)}'),` : `${null},`
row += `${date}`;
} else {
value = queryData.columns[i].separator === ',' ? parseFloat(value.replace(".", "").replace(",", "."))
: parseFloat(value.replace(",", ""));
value = value ? value : null;
row += `${value},`;
}
});
row = row.slice(0, -1);
row += ')';
insert += `${row},`
});
insert = insert.slice(0, -1);
return insert;
}
Example #4
Source File: validation-failure.error.ts From relate with GNU General Public License v3.0 | 6 votes |
constructor(message: string, errors: ValidationError[] = []) {
super(
`${message}:\n\n${join(
map(
errors,
({property, constraints}) =>
`- Property "${property}" failed the following constraints: ${join(values(constraints), ', ')}`,
),
'\n',
)}`,
);
}
Example #5
Source File: commonUtils.ts From yearn-watch-legacy with GNU Affero General Public License v3.0 | 6 votes |
mapToStrategyAddressQueueIndex = (
vaultAddress: string,
network: Network,
strategiesHelperCallsResults?: ContractCallResults
): StrategyAddressQueueIndex[] => {
if (!strategiesHelperCallsResults) {
return [];
}
const strategiesHelperAddress = getStrategiesHelperAddress(network);
const strategiesHelperCallsReturnContext =
strategiesHelperCallsResults.results[strategiesHelperAddress]
.callsReturnContext;
const strategiesHelperCallsReturnContextList = values(
strategiesHelperCallsReturnContext
);
const strategiesQueuePosition = strategiesHelperCallsReturnContextList.find(
(item) =>
item.methodParameters[0].toLowerCase() ===
vaultAddress.toLowerCase()
);
let strategiesQueueIndexes: Array<StrategyAddressQueueIndex>;
if (strategiesQueuePosition === undefined) {
strategiesQueueIndexes = Array<{
queueIndex: number;
address: string;
}>();
} else {
strategiesQueueIndexes = strategiesQueuePosition?.returnValues.map(
(value: unknown, index: number) => {
return {
queueIndex: index,
address: (value as string).toLowerCase(),
};
}
);
}
return strategiesQueueIndexes;
}
Example #6
Source File: env.ts From yearn-watch-legacy with GNU Affero General Public License v3.0 | 6 votes |
sanitizeErrors = (
errorString: string,
environment?: Env
): string => {
const env = environment || getEnv();
const filteredEnv = omit(env, ['env', 'ethereumNetwork']);
let sanitizedError = errorString;
values(filteredEnv).forEach((val) => {
if (val) {
sanitizedError = sanitizedError.replace(val, 'redacted');
}
});
return sanitizedError;
}
Example #7
Source File: turn.ts From fishbowl with MIT License | 6 votes |
export function drawableCards(
turns: CurrentGameSubscription["games"][0]["turns"],
cards: CurrentGameSubscription["games"][0]["cards"]
) {
const allCompletedCardIds = flatMap(turns, (turn) => turn.completed_card_ids)
const maxCount = max(values(countBy(allCompletedCardIds)))
let completedCardIdsForRound = filter(
groupBy(allCompletedCardIds),
(arr) => arr.length === maxCount
).map((arr) => arr[0])
const remainingIdsForRound = difference(
cards.map((card) => card.id),
completedCardIdsForRound
)
if (remainingIdsForRound.length === 0) {
return cards
} else {
return filter(cards, (card) => remainingIdsForRound.includes(card.id))
}
}
Example #8
Source File: text.ts From S2 with MIT License | 6 votes |
measureTextWidth = memoize(
(text: number | string = '', font: unknown): number => {
if (!font) {
return 0;
}
const { fontSize, fontFamily, fontWeight, fontStyle, fontVariant } =
font as CSSStyleDeclaration;
// copy G 里面的处理逻辑
ctx.font = [fontStyle, fontVariant, fontWeight, `${fontSize}px`, fontFamily]
.join(' ')
.trim();
return ctx.measureText(`${text}`).width;
},
(text: any, font) => [text, ...values(font)].join(''),
)
Example #9
Source File: pivot-data-set.ts From S2 with MIT License | 6 votes |
getCustomData = (path: number[]) => {
let hadUndefined = false;
let currentData: DataType | DataType[] | DataType[][] = this.indexesData;
for (let i = 0; i < path.length; i++) {
const current = path[i];
if (hadUndefined) {
if (isUndefined(current)) {
currentData = customFlatten(currentData) as [];
} else {
currentData = values(currentData)?.map(
(d) => d && get(d, current),
) as [];
}
} else if (isUndefined(current)) {
hadUndefined = true;
} else {
currentData = currentData?.[current];
}
}
return currentData;
};
Example #10
Source File: pivot-data-set.ts From S2 with MIT License | 6 votes |
private getFieldFormatterForTotalValue(cellMeta?: ViewMeta) {
let valueField: string;
// 当数据置于行头时,小计总计列尝试去找对应的指标
if (!this.spreadsheet.isValueInCols() && cellMeta) {
valueField = get(cellMeta.rowQuery, EXTRA_FIELD);
}
// 如果没有找到对应指标,则默认取第一个维度
valueField = valueField ?? get(this.fields.values, 0);
return super.getFieldFormatter(valueField);
}
Example #11
Source File: utils.ts From prism-frontend with MIT License | 6 votes |
exportDataTableToCSV = (data: TableData) => {
const { rows } = data;
return rows.map(r => values(r)).join('\n');
}
Example #12
Source File: reports.ts From yearn-watch-legacy with GNU Affero General Public License v3.0 | 5 votes |
getReportsForStrategies = memoize(
_getReportsForStrategies,
(...args) => values(args).join('_')
)
Example #13
Source File: strategies.ts From yearn-watch-legacy with GNU Affero General Public License v3.0 | 5 votes |
getStrategies = memoize(innerGetStrategies, (...args) =>
values(args).join('_')
)
Example #14
Source File: strategies.ts From yearn-watch-legacy with GNU Affero General Public License v3.0 | 5 votes |
getGenLenderStrategy = memoize(_getGenLenderStrategy, (...args) =>
values(args).join('_')
)
Example #15
Source File: vaults.ts From yearn-watch-legacy with GNU Affero General Public License v3.0 | 5 votes |
getVaultStrategyMetadata = memoize(
_getVaultStrategyMetadata,
(...args) => values(args).join('_')
)
Example #16
Source File: apisRequest.ts From yearn-watch-legacy with GNU Affero General Public License v3.0 | 5 votes |
querySubgraphData = memoize(querySubgraph, (...args) =>
values(args).join('_')
)
Example #17
Source File: paginate.ts From nestjs-paginate with MIT License | 5 votes |
export function isOperator(value: unknown): value is FilterOperator {
return values(FilterOperator).includes(value as any)
}
Example #18
Source File: namespace.ts From ui5-language-assistant with Apache License 2.0 | 5 votes |
/**
* Suggests namespace value for namespace attribute
* In case value's prefix ends with dot we'll use the mode of exploration and provide
* only next level values of namespaces starting with the prefix
* In other cases we'll provide all values containing the prefix
**/
export function namespaceValueSuggestions(
opts: UI5AttributeValueCompletionOptions
): UI5NamespacesInXMLAttributeValueCompletion[] {
const xmlAttribute = opts.attribute;
const xmlAttributeName = xmlAttribute.key;
if (xmlAttributeName === null) {
return [];
}
const xmlnsPrefix = getXMLNamespaceKeyPrefix(xmlAttributeName);
if (xmlnsPrefix === undefined) {
return [];
}
const ui5Model = opts.context;
const attributeValue = opts.prefix ?? "";
let applicableNamespaces = values(ui5Model.namespaces);
if (attributeValue !== "") {
applicableNamespaces = filter(applicableNamespaces, (_) =>
ui5NodeToFQN(_).includes(attributeValue)
);
}
if (attributeValue.endsWith(".")) {
const applicableNamespacesForExploration = filter(
applicableNamespaces,
(_) => ui5NodeToFQN(_).startsWith(attributeValue)
);
if (applicableNamespacesForExploration.length > 0) {
applicableNamespaces = applicableNamespacesForExploration;
}
}
if (xmlnsPrefix !== "") {
const applicableNamespacesWithPrefix = filter(
applicableNamespaces,
(_) => _.name === xmlnsPrefix
);
if (applicableNamespacesWithPrefix.length > 0) {
applicableNamespaces = applicableNamespacesWithPrefix;
}
}
applicableNamespaces = filter(
applicableNamespaces,
(_) => find(_.classes, isElementSubClass) !== undefined
);
return map(applicableNamespaces, (_) => ({
type: "UI5NamespacesInXMLAttributeValue",
ui5Node: _,
astNode: opts.attribute as XMLAttribute,
}));
}
Example #19
Source File: data-loader.ts From erda-ui with GNU Affero General Public License v3.0 | 5 votes |
createLoadDataFn = (api: any, chartType: string) => async (payload?: any) => {
let extraQuery = payload;
if (typeof payload === 'string') {
try {
extraQuery = JSON.parse(payload);
} catch (error) {
// eslint-disable-next-line no-console
console.error(error);
}
}
const { data } = await getChartData(merge({}, api, { query: extraQuery }));
if (['chart:line', 'chart:area', 'chart:bar'].includes(chartType)) {
const { results, ...rest } = data;
if (results[0].data.length > 1) {
return {
...rest,
metricData: map(results[0].data, (item) => values(item)[0]),
};
} else {
return {
...rest,
metricData: results[0].data[0],
};
}
}
if (chartType === 'chart:pie') {
return {
metricData: !isEmpty(data.metricData)
? [
{
name: data.title || '',
data: map(data.metricData, ({ title, value, name }) => ({ name: title || name, value })),
},
]
: [],
};
}
// 新的统一返回结构
if (chartType === 'chart:map') {
const { cols, data: _data } = data;
const aliases = filter(
map(cols, (col) => col.key),
(alias) => alias !== 'map_name',
);
const metricData = map(aliases, (alias) => ({
name: alias,
data: map(_data, (item) => ({
name: item.map_name,
value: item[alias],
})),
}));
return { metricData };
}
return data;
}
Example #20
Source File: query-params-config.tsx From erda-ui with GNU Affero General Public License v3.0 | 5 votes |
QueryParamsConfig = (props: IProps) => {
const [{ paramsData }, updater] = useUpdate({
paramsData: {},
});
const { onChange, formData, paramIn, isEditMode, resourceKey } = props;
const { updateFormErrorNum } = apiDesignStore;
React.useEffect(() => {
const _paramList = formData.parameters || {};
const properties = {};
forEach(_paramList, (item) => {
if (item?.in === paramIn) {
properties[item[API_FORM_KEY] || item.name] = item?.schema || item;
}
});
const tempObj = {
type: 'object',
properties,
};
tempObj[API_FORM_KEY] = 'parameters';
updater.paramsData(tempObj);
}, [updater, formData, paramIn, resourceKey]);
const updateParamList = React.useCallback(
(formKey: string, _formData: any, extraProps?: Obj) => {
const { typeQuotePath, quoteTypeName } = extraProps || {};
const _extraProps = { quoteTypeName, typeQuotePath: [] } as IExtraProps;
if (formKey) {
const tempDetail = produce(formData, (draft) => {
const newParameters = map(values(_formData?.properties), (item) => {
const tempItem = {
in: paramIn,
name: item[API_FORM_KEY],
required: item[API_PROPERTY_REQUIRED],
schema: item,
};
tempItem[API_FORM_KEY] = item[API_FORM_KEY];
return tempItem;
});
const parametersWithoutChange = filter(formData?.parameters, (item) => item?.in !== paramIn) || [];
const allParameters = [...newParameters, ...parametersWithoutChange];
if (typeQuotePath && quoteTypeName) {
const targetParamName = typeQuotePath.split('.')[2];
const targetIndex = findIndex(allParameters, { [API_FORM_KEY]: targetParamName });
const newPath = ['parameters', targetIndex];
_extraProps.typeQuotePath = newPath;
}
set(draft, 'parameters', allParameters);
});
onChange(paramIn, tempDetail, _extraProps);
}
},
[formData, onChange, paramIn],
);
return (
<div className="basic-params-config">
{!isEmpty(paramsData) && (
<PropertyItemForm
onFormErrorNumChange={updateFormErrorNum}
formData={paramsData}
detailVisible
formType="Parameters"
onChange={updateParamList}
isEditMode={isEditMode}
/>
)}
{!isEditMode && isEmpty(paramsData?.properties) && <EmptyHolder relative />}
</div>
);
}
Example #21
Source File: chart.tsx From erda-ui with GNU Affero General Public License v3.0 | 5 votes |
Chart = ({ type, extraQuery = {} }: IProps) => {
const [{ layout, boardConfig, timeSpan, loading }, updater] = useUpdate<IState>({
layout: [],
boardConfig: [],
timeSpan: getTimeSpan(),
loading: false,
});
React.useEffect(() => {
updater.loading(true);
getDashboard({ type })
.then(({ data }: any) => updater.boardConfig(data.viewConfig || []))
.finally(() => {
updater.loading(false);
});
}, [type, updater]);
React.useEffect(() => {
updater.timeSpan(getTimeSpan());
}, [extraQuery, updater]);
const query = React.useMemo(() => {
return {
...extraQuery,
start: timeSpan.startTimeMs,
end: timeSpan.endTimeMs,
};
}, [extraQuery, timeSpan.endTimeMs, timeSpan.startTimeMs]);
React.useEffect(() => {
const { start, end, ...restQuery } = query;
const flag = !isEmpty(restQuery) && values(restQuery).every((t) => !!t);
if (!flag) {
return;
}
const _layout = boardConfig.map((viewItem) => {
const filters = get(viewItem, 'view.api.extraData.filters');
const _viewItem = merge({}, viewItem, {
view: {
api: {
query: {
start,
end,
...reduce(
filters,
(acc, { value, method, tag }) => {
const matchQuery = isString(value) ? getVariableStr(value) : undefined;
return {
...acc,
[`${method}_${tag}`]: matchQuery ? restQuery[matchQuery] : value.split(','),
};
},
{},
),
},
},
},
});
const { api, chartType } = _viewItem.view as any;
return merge({}, viewItem, { view: { loadData: createLoadDataFn(api, chartType) } });
});
updater.layout(_layout);
}, [boardConfig, query, updater]);
return (
<Spin spinning={loading}>
<CommonRangePicker
className="mb-3"
defaultTime={[timeSpan.startTimeMs, timeSpan.endTimeMs]}
onOk={(v) => updater.timeSpan(v)}
/>
<BoardGrid.Pure layout={layout} />
</Spin>
);
}
Example #22
Source File: monitor-chart-panel.tsx From erda-ui with GNU Affero General Public License v3.0 | 5 votes |
MonitorChartPanel = (props: IProps) => {
const [showNumber, setShowNumber] = React.useState(4);
const { resourceType, resourceId, metrics, commonChartQuery = {}, defaultTime } = props;
if (!resourceType || !resourceId) {
return null;
}
const displayMetrics = slice(values(metrics), 0, showNumber);
return (
<>
<div className="monitor-chart-time-container">
<TimeSelector defaultTime={defaultTime} />
</div>
<div className="monitor-chart-panel">
{map(displayMetrics, ({ parameters, name: metricKey, title, metricName, unit, unitType }) => {
const chartQuery = {
...commonChartQuery,
fetchMetricKey: metricKey,
};
if (!isEmpty(parameters)) {
map(Object.keys(parameters), (key) => {
chartQuery[key] = parameters[key];
});
}
return (
<div className="monitor-chart-cell spin-full-height" key={metricKey}>
<MonitorChart
{...{ resourceType, resourceId }}
title={title || metricName}
metricKey={metricKey}
metricUnitType={unitType}
metricUnit={unit}
chartQuery={chartQuery}
/>
</div>
);
})}
<IF check={keys(metrics).length > showNumber}>
<div className="show-all" onClick={() => setShowNumber((prevNumber) => prevNumber + 4)}>
{allWordsFirstLetterUpper(i18n.t('load more'))}
</div>
</IF>
</div>
</>
);
}
Example #23
Source File: pivot-data-set.ts From S2 with MIT License | 5 votes |
public getMultiData(
query: DataType,
isTotals?: boolean,
isRow?: boolean,
drillDownFields?: string[],
): DataType[] {
if (isEmpty(query)) {
return compact(customFlattenDeep(this.indexesData));
}
const { rows, columns, values: valueList } = this.fields;
const totalRows = !isEmpty(drillDownFields)
? rows.concat(drillDownFields)
: rows;
const rowDimensionValues = getQueryDimValues(totalRows, query);
const colDimensionValues = getQueryDimValues(columns, query);
const path = getDataPath({
rowDimensionValues,
colDimensionValues,
careUndefined: true,
isFirstCreate: true,
rowFields: rows,
colFields: columns,
rowPivotMeta: this.rowPivotMeta,
colPivotMeta: this.colPivotMeta,
});
const currentData = this.getCustomData(path);
let result = compact(customFlatten(currentData));
if (isTotals) {
// 总计/小计(行/列)
// need filter extra data
// grand total => {$$extra$$: 'price'}
// sub total => {$$extra$$: 'price', category: 'xxxx'}
// [undefined, undefined, "price"] => [category]
let fieldKeys = [];
const rowKeys = getFieldKeysByDimensionValues(rowDimensionValues, rows);
const colKeys = getFieldKeysByDimensionValues(
colDimensionValues,
columns,
);
if (isRow) {
// 行总计
fieldKeys = rowKeys;
} else {
// 只有一个值,此时为列总计
const isCol = keys(query)?.length === 1 && has(query, EXTRA_FIELD);
if (isCol) {
fieldKeys = colKeys;
} else {
const getTotalStatus = (dimensions: string[]) => {
return isEveryUndefined(
dimensions?.filter((item) => !valueList?.includes(item)),
);
};
const isRowTotal = getTotalStatus(colDimensionValues);
const isColTotal = getTotalStatus(rowDimensionValues);
if (isRowTotal) {
// 行小计
fieldKeys = rowKeys;
} else if (isColTotal) {
// 列小计
fieldKeys = colKeys;
} else {
// 行小计+列 or 列小计+行
fieldKeys = [...rowKeys, ...colKeys];
}
}
}
result = result.filter(
(r) =>
!fieldKeys?.find(
(item) => item !== EXTRA_FIELD && keys(r)?.includes(item),
),
);
}
return result || [];
}
Example #24
Source File: pivot-data-set.ts From S2 with MIT License | 5 votes |
public processDataCfg(dataCfg: S2DataConfig): S2DataConfig {
const { data, meta = [], fields, sortParams = [], totalData } = dataCfg;
const { columns, rows, values, valueInCols, customValueOrder } = fields;
let newColumns = columns;
let newRows = rows;
if (valueInCols) {
newColumns = this.isCustomMeasuresPosition(customValueOrder)
? this.handleCustomMeasuresOrder(customValueOrder, newColumns)
: uniq([...columns, EXTRA_FIELD]);
} else {
newRows = this.isCustomMeasuresPosition(customValueOrder)
? this.handleCustomMeasuresOrder(customValueOrder, newRows)
: uniq([...rows, EXTRA_FIELD]);
}
const valueFormatter = (value: string) => {
const currentMeta = find(meta, ({ field }: Meta) => field === value);
return get(currentMeta, 'name', value);
};
// 虚拟列字段,为文本分类字段
const extraFieldName =
this.spreadsheet?.options?.cornerExtraFieldText || i18n('数值');
const extraFieldMeta: Meta = {
field: EXTRA_FIELD,
name: extraFieldName,
formatter: (value: string) => valueFormatter(value),
};
const newMeta: Meta[] = [...meta, extraFieldMeta];
const newData = this.standardTransform(data, values);
const newTotalData = this.standardTransform(totalData, values);
return {
data: newData,
meta: newMeta,
fields: {
...fields,
rows: newRows,
columns: newColumns,
values,
},
totalData: newTotalData,
sortParams,
};
}
Example #25
Source File: pivot-data-set.ts From S2 with MIT License | 5 votes |
/**
* Provide a way to append some drill-down data in indexesData
* @param extraRowField
* @param drillDownData
* @param rowNode
*/
public transformDrillDownData(
extraRowField: string,
drillDownData: DataType[],
rowNode: Node,
) {
const { columns, values: dataValues } = this.fields;
const currentRowFields = Node.getFieldPath(rowNode, true);
const nextRowFields = [...currentRowFields, extraRowField];
const store = this.spreadsheet.store;
// 1、通过values在data中注入额外的维度信息,并分离`明细数据`&`汇总数据`
const transformedData = this.standardTransform(drillDownData, dataValues);
const totalData = splitTotal(transformedData, {
columns: this.fields.columns,
rows: nextRowFields,
});
const originData = difference(transformedData, totalData);
// 2. 检查该节点是否已经存在下钻维度
const rowNodeId = rowNode?.id;
const idPathMap = store.get('drillDownIdPathMap') ?? new Map();
if (idPathMap.has(rowNodeId)) {
// the current node has a drill-down field, clean it
forEach(idPathMap.get(rowNodeId), (path: number[]) => {
unset(this.indexesData, path);
});
deleteMetaById(this.rowPivotMeta, rowNodeId);
}
// 3、转换数据
const {
paths: drillDownDataPaths,
indexesData,
rowPivotMeta,
colPivotMeta,
sortedDimensionValues,
} = transformIndexesData({
rows: nextRowFields,
columns,
originData,
totalData,
indexesData: this.indexesData,
sortedDimensionValues: this.sortedDimensionValues,
rowPivotMeta: this.rowPivotMeta,
colPivotMeta: this.colPivotMeta,
});
this.indexesData = indexesData;
this.rowPivotMeta = rowPivotMeta;
this.colPivotMeta = colPivotMeta;
this.sortedDimensionValues = sortedDimensionValues;
// 4、record data paths by nodeId
// set new drill-down data path
idPathMap.set(rowNodeId, drillDownDataPaths);
store.set('drillDownIdPathMap', idPathMap);
}
Example #26
Source File: datatype-config.tsx From erda-ui with GNU Affero General Public License v3.0 | 4 votes |
DataTypeConfig = (props: IProps) => {
const [{ propertyFormData }, updater] = useUpdate({
propertyFormData: {} as Obj,
});
const { formData, onQuoteNameChange, quotePathMap, isEditMode, dataType, onDataTypeNameChange, dataTypeNameMap } =
props;
const { updateOpenApiDoc, updateFormErrorNum } = apiDesignStore;
const [openApiDoc] = apiDesignStore.useStore((s) => [s.openApiDoc]);
const openApiDocRef = useLatest(openApiDoc);
const quotePathMapRef = useLatest(quotePathMap);
const formRef = React.useRef<IFormExtendType>(null);
React.useEffect(() => {
const newFormData = { ...omit(formData, 'name') };
newFormData[API_FORM_KEY] = formData[API_FORM_KEY] || formData.name;
updater.propertyFormData(newFormData);
}, [updater, formData]);
const extraDataTypes = React.useMemo(() => {
const tempTypes = get(openApiDoc, 'components.schemas') || {};
const types = {};
const forbiddenTypes = get(tempTypes, [dataType, 'x-dice-forbidden-types']) || [dataType];
forEach(keys(tempTypes), (typeName) => {
if (!forbiddenTypes?.includes(typeName)) {
types[typeName] = tempTypes[typeName];
}
});
return types;
}, [dataType, openApiDoc]);
const allExtraDataTypes = React.useMemo(() => {
return get(openApiDoc, 'components.schemas') || {};
}, [openApiDoc]);
const getForbiddenTypes = React.useCallback((data: Obj, storage: string[]) => {
if (data?.properties) {
forEach(values(data.properties), (item) => {
const refTypePath = get(item, [QUOTE_PREFIX, 0, '$ref']) || data[QUOTE_PREFIX_NO_EXTENDED];
if (refTypePath) {
const tempQuoteName = refTypePath.split('#/components/schemas/')[1];
if (!storage.includes(tempQuoteName)) {
storage.push(tempQuoteName);
}
}
getForbiddenTypes(item, storage);
});
} else if (data?.items) {
const itemsData = data.items;
const refTypePath = get(itemsData, [QUOTE_PREFIX, 0, '$ref']) || itemsData[QUOTE_PREFIX_NO_EXTENDED];
if (refTypePath) {
const tempQuoteName = refTypePath.split('#/components/schemas/')[1];
if (!storage.includes(tempQuoteName)) {
storage.push(tempQuoteName);
}
getForbiddenTypes(itemsData, storage);
}
}
}, []);
const onFormChange = React.useCallback(
(_formKey: string, _formData: any, extraProps?: Obj) => {
const quotedType = extraProps?.quoteTypeName;
const nName: string = _formData[API_FORM_KEY]; // 当前正在编辑的类型的新name
const tempMap = {};
const originalDataTypeMap = get(openApiDocRef.current, ['components', 'schemas']);
const usedCustomTypes: string[] = []; // 获取所有引用当前类型的其他类型
const refTypePath = get(_formData, [QUOTE_PREFIX, 0, '$ref']) || _formData[QUOTE_PREFIX_NO_EXTENDED];
if (refTypePath) {
const tempQuoteName = refTypePath.split('#/components/schemas/')[1];
usedCustomTypes.push(tempQuoteName);
}
getForbiddenTypes(_formData, usedCustomTypes);
forEach(keys(originalDataTypeMap), (typeKey: string) => {
if (typeKey !== dataType) {
const isSelfUsed = usedCustomTypes.includes(typeKey); // 是否引用了当前正在编辑的类型
const _originalForbiddenTypes = filter(
get(originalDataTypeMap, [typeKey, 'x-dice-forbidden-types']) || [],
(item) => item !== dataType,
);
if (!_originalForbiddenTypes.includes(typeKey)) {
// 每个类型禁止引用自己
_originalForbiddenTypes.push(typeKey);
}
// 若当前编辑的类型引用了该类型,则禁该止引用当前正在编辑的类型
if (isSelfUsed || quotedType === typeKey) {
_originalForbiddenTypes.push(nName);
}
tempMap[typeKey] = {
...originalDataTypeMap[typeKey],
'x-dice-forbidden-types': _originalForbiddenTypes,
};
}
});
let originalForbiddenTypes: string[] = _formData['x-dice-forbidden-types'] || [dataType];
if (dataType !== nName) {
originalForbiddenTypes = originalForbiddenTypes.filter(
(item) => item !== dataType && originalDataTypeMap[item],
);
originalForbiddenTypes.push(nName);
}
tempMap[nName] = {
..._formData,
'x-dice-forbidden-types': originalForbiddenTypes,
};
if (dataType !== nName) {
onDataTypeNameChange(nName);
const newPathMap = produce(quotePathMapRef.current, (draft) => {
draft[nName] = filter(draft[dataType], (itemPath) => get(openApiDocRef.current, itemPath));
unset(draft, dataType);
});
const newHref =
_formData.type === 'object' ? [{ $ref: `#/components/schemas/${nName}` }] : `#/components/schemas/${nName}`;
const quotePrefix = _formData.type === 'object' ? QUOTE_PREFIX : QUOTE_PREFIX_NO_EXTENDED;
const _tempDocDetail = produce(openApiDocRef.current, (draft) => {
set(draft, 'components.schemas', tempMap);
});
const tempDocDetail = produce(_tempDocDetail, (draft) => {
forEach(quotePathMapRef.current[dataType], (curTypeQuotePath) => {
if (
get(draft, [...curTypeQuotePath, QUOTE_PREFIX]) ||
get(draft, [...curTypeQuotePath, QUOTE_PREFIX_NO_EXTENDED])
) {
unset(draft, [...curTypeQuotePath, QUOTE_PREFIX]);
unset(draft, [...curTypeQuotePath, QUOTE_PREFIX_NO_EXTENDED]);
set(draft, [...curTypeQuotePath, quotePrefix], newHref);
}
});
});
updateOpenApiDoc(tempDocDetail);
onQuoteNameChange(newPathMap);
} else {
if (quotedType && extraProps?.typeQuotePath) {
const prefixPath = `components.schemas.${extraProps.typeQuotePath}`;
const isQuotedBefore =
get(openApiDocRef.current, `${prefixPath}.${QUOTE_PREFIX}.0.$ref`) ||
get(openApiDocRef.current, `${prefixPath}.${QUOTE_PREFIX_NO_EXTENDED}`);
const oldQuotedType = isQuotedBefore ? isQuotedBefore.split('/').slice(-1)[0] : '';
const newPathMap = produce(quotePathMapRef.current, (draft) => {
draft[quotedType] = draft[quotedType] || [];
draft[quotedType].push(prefixPath.split('.'));
if (oldQuotedType) {
draft[oldQuotedType] = filter(draft[oldQuotedType], (item) => item.join('.') !== prefixPath);
}
});
onQuoteNameChange(newPathMap);
}
const tempDocDetail = produce(openApiDocRef.current, (draft) => {
set(draft, 'components.schemas', tempMap);
});
updateOpenApiDoc(tempDocDetail);
}
},
[
dataType,
getForbiddenTypes,
onDataTypeNameChange,
onQuoteNameChange,
openApiDocRef,
quotePathMapRef,
updateOpenApiDoc,
],
);
return (
<div className="basic-params-config">
<FormBuilder isMultiColumn className="param-config-form" ref={formRef}>
<PropertyItemForm
onChange={onFormChange}
onFormErrorNumChange={updateFormErrorNum}
detailVisible
formType="DataType"
formData={propertyFormData}
extraDataTypes={extraDataTypes}
allExtraDataTypes={allExtraDataTypes}
isEditMode={isEditMode}
allDataTypes={dataTypeNameMap}
/>
</FormBuilder>
</div>
);
}
Example #27
Source File: index.tsx From erda-ui with GNU Affero General Public License v3.0 | 4 votes |
ReportRecords = () => {
const [reportTaskRecords, reportTaskRecord, reportTaskRecordPaging] = alarmReportStore.useStore((s) => [
s.reportTaskRecords,
s.reportTaskRecord,
s.reportTaskRecordPaging,
]);
const { getReportTaskRecords, getReportTaskRecord, getReportTask } = alarmReportStore.effects;
const { clearReportTaskRecords, clearReportTaskRecord } = alarmReportStore.reducers;
const [getReportTaskRecordsLoading, getReportTaskRecordLoading] = useLoading(alarmReportStore, [
'getReportTaskRecords',
'getReportTaskRecord',
]);
const { pageNo, pageSize, total } = reportTaskRecordPaging;
const { query } = routeInfoStore.getState((s) => s);
const [{ activedRecord }, updater] = useUpdate({
activedRecord: query.recordId,
});
const layout = useMemo(
() =>
map(get(reportTaskRecord, 'dashboardBlock.viewConfig'), (item: any) => {
const _item = { ...item };
const chartType = get(_item, 'view.chartType');
const staticData = get(_item, 'view.staticData');
if (isEmpty(staticData)) {
set(_item, 'view.staticData', {
time: [],
metricData: [],
});
return _item;
}
if (['chart:line', 'chart:bar', 'chart:area'].includes(chartType)) {
const { time, results } = staticData;
if (results[0].data?.length > 1) {
set(_item, 'view.staticData', {
time,
metricData: map(results[0].data, (itemData) => values(itemData)[0]),
});
} else {
set(_item, 'view.staticData', {
time,
metricData: results[0].data?.[0],
});
}
}
if (chartType === 'chart:pie') {
set(_item, 'view.staticData', {
metricData: [
{
name: staticData.title || '',
data: map(staticData.metricData, ({ title, value }) => ({ name: title, value })),
},
],
});
set(_item, 'view.config.option.series', [
{
radius: ['30%', '50%'],
},
]);
}
return _item;
}),
[reportTaskRecord],
);
useMount(() => {
getReportTask();
getReportTaskRecords({ pageNo, pageSize });
});
useUnmount(() => {
clearReportTaskRecords();
clearReportTaskRecord();
});
useEffect(() => {
if (reportTaskRecords[0] && !activedRecord) {
updater.activedRecord(reportTaskRecords[0].id);
}
}, [activedRecord, reportTaskRecords, updater]);
useEffect(() => {
if (activedRecord) {
getReportTaskRecord(activedRecord);
} else {
clearReportTaskRecord();
}
}, [activedRecord, clearReportTaskRecord, getReportTaskRecord]);
const handleChange = (no: number, dates?: Array<undefined | Moment>) => {
let payload = { pageNo: no, pageSize } as any;
if (dates) {
payload = {
...payload,
start: dates[0] && dates[0].valueOf(),
end: dates[1] && dates[1].valueOf(),
};
}
clearReportTaskRecords();
updater.activedRecord(undefined);
getReportTaskRecords(payload);
};
const handleClick = (id: number) => {
updater.activedRecord(id);
};
return (
<div className="task-report-records flex items-start justify-between">
<div className="search-records pr-4 flex flex-col h-full">
<div className="mb-2">
<DatePicker.RangePicker
borderTime
className="w-full"
onChange={(dates) => handleChange(pageNo, dates)}
ranges={getTimeRanges()}
/>
</div>
<div className="flex-1 h-full overflow-auto">
<Spin spinning={getReportTaskRecordsLoading}>
<Holder when={isEmpty(reportTaskRecords)}>
<ul>
{map(reportTaskRecords, ({ id, start, end }) => (
<li
className={classnames({
'text-base': true,
'py-4': true,
'font-medium': true,
'text-center': true,
'hover-active-bg': true,
active: String(activedRecord) === String(id),
})}
key={id}
onClick={() => handleClick(id)}
>
<CustomIcon className="mr-2" type="rw" />
{end
? `${moment(start).format('YYYY/MM/DD')}-${moment(end).format('YYYY/MM/DD')}`
: moment(start).format('YYYY-MM-DD')}
</li>
))}
</ul>
{total && (
<Pagination
className="text-center mt-3"
simple
defaultCurrent={1}
total={total}
onChange={(no) => handleChange(no)}
/>
)}
</Holder>
</Spin>
</div>
</div>
<div className="flex-1 pl-4 overflow-auto h-full">
<Spin spinning={getReportTaskRecordLoading}>
<Holder when={isEmpty(layout)}>
<BoardGrid.Pure layout={layout} />
</Holder>
</Spin>
</div>
</div>
);
}
Example #28
Source File: index.tsx From erda-ui with GNU Affero General Public License v3.0 | 4 votes |
CloudSource = () => {
const [overviewData, ecsTrendingData] = cloudSourceStore.useStore((s) => [s.overviewData, s.ecsTrendingData]);
const { getOverview, getECSTrending } = cloudSourceStore.effects;
const [getECSTrendingLoading, getOverviewLoading] = useLoading(cloudSourceStore, ['getECSTrending', 'getOverview']);
const { getList } = cloudAccountStore.effects;
const accountsCount = cloudAccountStore.useStore((s) => s.list.length);
const getResourceInfo = () => {
getList({ pageNo: 1, pageSize: 15 }).then((accountList: IPagingResp<CLOUD_ACCOUNTS.Account>) => {
if (accountList.total > 0) {
getOverview();
getECSTrending();
}
});
};
useEffectOnce(getResourceInfo);
const parseExpireData = React.useCallback(() => {
const metricData: any[] = [];
const statusCountMap = {};
const xData: string[] = [];
let expireDays = 0;
let total = 0;
map(overviewData, (category) => {
map(category.resourceTypeData, (resource: any, type: string) => {
if (resource.expireDays) {
// 都一样,随便取一个
expireDays = resource.expireDays;
}
if (resource.statusCount) {
xData.push(type);
map(resource.statusCount, (v) => {
if (v.label === 'BeforeExpired') {
statusCountMap[v.status] = statusCountMap[v.status] || [];
statusCountMap[v.status].push(v.count);
total += v.count;
}
});
}
});
});
map(statusCountMap, (data, status) => {
metricData.push({
name: status,
barGap: 0,
data,
});
});
return {
xData,
metricData,
total,
expireDays,
};
}, [overviewData]);
const parseStatusData = React.useCallback(() => {
const metricData: any[] = [];
const statusCountMap = {};
const xData: string[] = [];
map(get(overviewData, 'CLOUD_SERVICE.resourceTypeData'), (resource: any, type: string) => {
if (resource.statusCount) {
xData.push(type);
map(resource.statusCount, (v) => {
statusCountMap[v.status] = statusCountMap[v.status] || [];
statusCountMap[v.status].push(v.count);
});
}
});
map(statusCountMap, (data, status) => {
metricData.push({
name: status,
barGap: 0,
data,
});
});
return {
xData,
metricData,
};
}, [overviewData]);
const getPieStaticData = (
list: Array<{ status?: string; chargeType?: string; count: number }>,
key: string,
formatter?: string,
) => {
let total = 0;
const metricData = [
{
name: i18n.t('publisher:proportion'),
radius: ['50%', '70%'],
center: ['50%', '42%'],
label: {
show: false,
formatter: formatter || '{b}\n{c}',
fontSize: '20',
position: 'center',
},
emphasis: {
label: {
show: true,
fontWeight: 'bold',
},
},
data: map(list, (item) => {
total += item.count;
return { name: item[key], value: item.count };
}),
},
];
const legendData = map(list, (item) => item[key]);
return {
metricData,
legendData,
total,
};
};
const getECSTrendingData = (data: any) => {
if (isEmpty(data) || isEmpty(data.results)) {
return {};
}
return {
time: data.time,
metricData: map(data.results[0].data, (item) => values(item)[0]),
};
};
const getLayout = React.useCallback(() => {
const commonConfig = {
option: {
tooltip: {
formatter: '{a}<br/>{b}: {d}%',
},
legend: {
orient: 'horizontal',
y: 'bottom',
x: 'center',
type: 'scroll',
},
},
};
const statusData = getPieStaticData(get(overviewData, 'COMPUTE.resourceTypeData.ECS.statusCount'), 'status');
const chargeData = getPieStaticData(
get(overviewData, 'COMPUTE.resourceTypeData.ECS.chargeTypeCount'),
'chargeType',
);
const labelData = getPieStaticData(get(overviewData, 'NETWORK.resourceTypeData.VPC.labelCount'), 'label', '{c}');
const expireData = parseExpireData();
return [
{
w: 8,
h: 8,
x: 0,
y: 0,
i: 'serverStatus',
moved: false,
static: false,
view: {
title: i18n.t('cmp:ECS status'),
chartType: 'chart:pie',
hideReload: true,
staticData: statusData.total ? statusData : [],
config: merge({}, commonConfig, {
option: {
color: [colorMap.appleGreen, colorMap.yellow, colorMap.pink, colorMap.gray],
},
}),
chartProps: {
onEvents: {
click: () => {
goTo(goTo.pages.cloudSourceEcs);
},
},
},
},
},
{
w: 8,
h: 8,
x: 8,
y: 0,
i: 'chargeType',
moved: false,
static: false,
view: {
title: i18n.t('cmp:ESC billing method'),
chartType: 'chart:pie',
hideReload: true,
staticData: chargeData.total ? chargeData : [],
config: merge({}, commonConfig, {
option: {
color: [colorMap.appleGreen, colorMap.darkGreen],
},
}),
chartProps: {
onEvents: {
click: () => {
goTo(goTo.pages.cloudSourceEcs);
},
},
},
},
},
{
w: 8,
h: 8,
x: 16,
y: 0,
i: 'vpcLabel',
moved: false,
static: false,
view: {
title: i18n.t('cmp:VPC label'),
chartType: 'chart:pie',
hideReload: true,
staticData: labelData.total ? labelData : [],
config: commonConfig,
chartProps: {
onEvents: {
click: () => {
goTo(goTo.pages.cloudSourceVpc);
},
},
},
},
},
{
w: 16,
h: 8,
x: 0,
y: 10,
i: 'will expire resource',
moved: false,
static: false,
view: {
title: i18n.t('cmp:Resources expire soon'),
hideReload: true,
chartType: 'chart:bar',
customRender: !expireData.total
? () => {
return (
<div className="no-expire-tip">
<img src={ts_svg} alt="no-will-expire-resource" />
<div className="text-sub">
{i18n.t('cmp:No service expire within {num} days.', {
num: expireData.expireDays,
})}
</div>
</div>
);
}
: undefined,
staticData: parseExpireData(),
chartProps: {
onEvents: {
click: (params: any) => {
switch (params.name || params.value) {
case 'REDIS':
goTo(goTo.pages.cloudSourceRedis);
break;
case 'RDS':
goTo(goTo.pages.cloudSourceRds);
break;
case 'ROCKET_MQ':
goTo(goTo.pages.cloudSourceMq);
break;
case 'ECS':
goTo(goTo.pages.cloudSourceEcs);
break;
default:
break;
}
},
},
},
config: {
option: {
color: [colorMap.appleGreen, colorMap.yellow, colorMap.pink, colorMap.gray],
xAxis: [
{
triggerEvent: true,
},
],
yAxis: [
{
name: i18n.t('cmp:Number'),
nameLocation: 'end',
nameGap: 15,
minInterval: 1,
nameTextStyle: {
padding: [0, 0, 0, 0],
},
},
],
},
},
},
},
{
w: 8,
h: 4,
x: 17,
y: 20,
i: 'OSS',
moved: false,
static: false,
view: {
title: i18n.t('cmp:OSS overview'),
hideReload: true,
customRender: () => {
const bucket = get(overviewData, 'STORAGE.resourceTypeData.OSS_BUCKET') || {};
return (
<div className="cloud-count-card">
<div
className="part hover-active"
onClick={() => {
goTo(goTo.pages.cloudSourceOss);
}}
>
<div className="count">{bucket.totalCount || 0}</div>
<div className="name">{firstCharToUpper(i18n.t('cmp:number of Bucket'))}</div>
</div>
{
// ref issue: 59066
}
{/* <div className='part hover-active' onClick={() => { goTo(goTo.pages.cloudSourceOss); }}>
<div className="count">
{getFormatter('STORAGE', 'B').format(bucket.storageUsage || 0)}
</div>
<div className="name">
{i18n.t('cmp:total storage capacity')}
</div>
</div> */}
</div>
);
},
},
},
{
w: 8,
h: 4,
x: 17,
y: 20,
i: 'Account',
moved: false,
static: false,
view: {
title: firstCharToUpper(i18n.t('cmp:Cloud Account').toLowerCase()),
hideReload: true,
customRender: () => {
return (
<div className="cloud-count-card">
<div
className="part hover-active"
onClick={() => {
goTo(goTo.pages.cloudAccounts);
}}
>
<div className="count">{accountsCount || 0}</div>
<div className="name">{i18n.t('cmp:Number of accounts')}</div>
</div>
</div>
);
},
},
},
{
w: 24,
h: 8,
x: 0,
y: 30,
i: 'cloud resource overview',
moved: false,
static: false,
view: {
title: i18n.t('cmp:Cloud service overview'),
chartType: 'chart:bar',
hideReload: true,
staticData: parseStatusData(),
chartProps: {
onEvents: {
click: (params: any) => {
switch (params.name || params.value) {
case 'REDIS':
goTo(goTo.pages.cloudSourceRedis);
break;
case 'RDS':
goTo(goTo.pages.cloudSourceRds);
break;
case 'ROCKET_MQ':
goTo(goTo.pages.cloudSourceMq);
break;
case 'ECS':
goTo(goTo.pages.cloudSourceEcs);
break;
default:
break;
}
},
},
},
config: {
option: {
color: [colorMap.appleGreen, colorMap.yellow, colorMap.pink, colorMap.gray],
xAxis: [
{
triggerEvent: true,
},
],
yAxis: [
{
name: i18n.t('cmp:Number'),
nameLocation: 'end',
nameGap: 15,
minInterval: 1,
nameTextStyle: {
padding: [0, 0, 0, 0],
},
},
],
},
},
},
},
{
w: 24,
h: 8,
x: 0,
y: 40,
i: 'ecs-add-trending',
moved: false,
static: false,
view: {
title: i18n.t('cmp:ECS add trending'),
chartType: 'chart:area',
hideReload: true,
staticData: getECSTrendingData(ecsTrendingData),
maskMsg: getECSTrendingLoading ? i18n.t('charts:loading') : undefined,
config: {
optionProps: {
isMoreThanOneDay: true,
moreThanOneDayFormat: 'MMM',
},
option: {
yAxis: [
{
name: i18n.t('cmp:Number'),
nameLocation: 'end',
nameGap: 15,
minInterval: 1,
nameTextStyle: {
padding: [0, 0, 0, 0],
},
},
],
},
},
},
},
];
}, [overviewData, parseExpireData, parseStatusData, ecsTrendingData, getECSTrendingLoading, accountsCount]);
if (accountsCount > 0) {
return (
<div className="cloud-source full-spin-height">
<Spin spinning={getOverviewLoading}>
<BoardGrid.Pure layout={getLayout()} />
</Spin>
</div>
);
}
return (
<Card className="h-full flex flex-wrap justify-center items-center">
<Guidance afterSubmit={getResourceInfo} />
</Card>
);
}
Example #29
Source File: connectChart.tsx From erda-ui with GNU Affero General Public License v3.0 | 4 votes |
ConnectChart = (props) => {
const {
data,
seriseName,
legendFormatter,
decimal = 2,
isBarChangeColor,
tooltipFormatter,
yAxisNames = [],
isLabel,
noAreaColor,
timeSpan,
opt,
} = props;
const groups = keys(data.results);
const [selectedGroup, setSelectedGroup] = React.useState();
const getOption = () => {
const moreThanOneDay = timeSpan ? timeSpan.seconds > 24 * 3600 : false;
const { results: originData, xAxis, time, lines } = data;
const results = sortBy(originData[selectedGroup] || values(originData)[0], 'axisIndex');
const legendData = [];
let yAxis = [];
const series = [];
const maxArr = [];
// 处理markLine
const markLines = lines || [];
let markLine = {};
if (markLines.length) {
markLine = {
silent: true,
label: {
normal: {
show: true,
position: 'middle',
formatter: (params) => {
const uType = results[0].unitType;
const { unit } = results[0];
const y = getFormatter(uType, unit).format(params.data.yAxis, decimal || 2);
return `${params.name}: ${y}`;
},
},
},
data: markLines.map(({ name, value }) => [
{ x: '7%', yAxis: value, name },
{ x: '93%', yAxis: value },
]),
};
}
map(results, (value, i) => {
const { axisIndex, name, tag, unit } = value;
(tag || name) && legendData.push({ name: tag || name });
const yAxisIndex = axisIndex || 0;
const areaColor = areaColors[i];
series.push({
type: value.chartType || 'line',
name: value.tag || seriseName || value.name || value.key,
yAxisIndex,
data: !isBarChangeColor
? value.data
: map(value.data, (item, j) => {
const sect = Math.ceil(value.data.length / CHANGE_COLORS.length);
return { ...item, itemStyle: { normal: { color: CHANGE_COLORS[Number.parseInt(j / sect, 10)] } } };
}),
label: {
normal: {
show: isLabel,
position: 'top',
formatter: (label) => label.data.label,
},
},
markLine: i === 0 ? markLine : {},
connectNulls: true,
symbol: 'emptyCircle',
symbolSize: 1,
barMaxWidth: 50,
areaStyle: {
normal: {
color: noAreaColor ? 'transparent' : areaColor,
},
},
});
const curMax = value.data ? calMax([value.data]) : [];
maxArr[yAxisIndex] = maxArr[yAxisIndex] && maxArr[yAxisIndex] > curMax ? maxArr[yAxisIndex] : curMax;
const curUnitType = value.unitType || ''; // y轴单位
const curUnit = value.unit || ''; // y轴单位
yAxis[yAxisIndex] = {
name: name || yAxisNames[yAxisIndex] || '',
nameTextStyle: {
padding: [0, 0, 0, 5],
},
position: yAxisIndex === 0 ? 'left' : 'right',
offset: 10,
min: 0,
splitLine: {
show: true,
},
axisTick: {
show: false,
},
axisLine: {
show: false,
},
unitType: curUnitType,
unit: curUnit,
axisLabel: {
margin: 0,
formatter: (val) => getFormatter(curUnitType, unit).format(val, decimal),
},
};
});
const formatTime = (timeStr) => moment(Number(timeStr)).format(moreThanOneDay ? 'D/M HH:mm' : 'HH:mm');
const getTTUnitType = (i) => {
const curYAxis = yAxis[i] || yAxis[yAxis.length - 1];
return [curYAxis.unitType, curYAxis.unit];
};
const genTTArray = (param) =>
param.map((unit, i) => {
return `<span style='color: ${unit.color}'>${cutStr(unit.seriesName, 20)} : ${getFormatter(
...getTTUnitType(i),
).format(unit.value, 2)}</span><br/>`;
});
let defaultTTFormatter = (param) => `${param[0].name}<br/>${genTTArray(param).join('')}`;
if (time) {
defaultTTFormatter = (param) => {
const endTime = time[param[0].dataIndex + 1];
if (!endTime) {
return `${formatTime(param[0].name)}<br />${genTTArray(param).join('')}`;
}
return `${formatTime(param[0].name)} - ${formatTime(endTime)}<br/>${genTTArray(param).join('')}`;
};
}
const lgFormatter = (name) => {
const defaultName = legendFormatter ? legendFormatter(name) : name;
return cutStr(defaultName, 20);
};
const haveTwoYAxis = yAxis.length > 1;
if (haveTwoYAxis) {
yAxis = yAxis.map((item, i) => {
// 有数据和无数据的显示有差异
const hasData = some(results[i].data || [], (_data) => Number(_data) !== 0);
let { name } = item;
if (!hasData) {
name =
i === 0
? `${' '.repeat(item.name.length + 1)}${item.name}`
: `${item.name}${' '.repeat(item.name.length)}`;
}
if (i > 1) {
// 右侧有超过两个Y轴
yAxis[i].offset = 80 * (i - 1);
}
const maxValue = item.max || maxArr[i];
return { ...item, name, max: maxValue, interval: maxValue / 5 };
// 如果有双y轴,刻度保持一致
});
} else {
yAxis[0].name = yAxisNames[0] || '';
}
const defaultOption = {
tooltip: {
trigger: 'axis',
transitionDuration: 0,
confine: true,
axisPointer: {
type: 'none',
},
formatter: tooltipFormatter || defaultTTFormatter,
},
legend: {
bottom: 10,
padding: [15, 5, 0, 5],
orient: 'horizontal',
align: 'left',
data: legendData,
formatter: lgFormatter,
type: 'scroll',
tooltip: {
show: true,
formatter: (t) => cutStr(t.name, 100),
},
},
grid: {
top: haveTwoYAxis ? 30 : 25,
left: 15,
right: haveTwoYAxis ? 30 : 5,
bottom: 40,
containLabel: true,
},
xAxis: [
{
type: 'category',
data: xAxis || time || [] /* X轴数据 */,
axisTick: {
show: false /* 坐标刻度 */,
},
axisLine: {
show: false,
},
axisLabel: {
formatter: xAxis
? (value) => value
: (value) => moment(Number(value)).format(moreThanOneDay ? 'D/M HH:mm' : 'HH:mm'),
},
splitLine: {
show: false,
},
},
],
yAxis,
textStyle: {
fontFamily: 'arial',
},
series,
};
return merge(defaultOption, opt);
};
const { xAxis, time, results } = data;
let hasData = size(results) > 0 && !isEmpty(xAxis || time);
if (time === undefined && xAxis === undefined) {
hasData = size(results) > 0;
}
const handleChange = (value) => {
setSelectedGroup(value);
};
return (
<>
<IF check={hasData}>
<div className="chart-selecter">
{i18n.t('msp:select instance')}:
<Select className="my-3" value={selectedGroup || groups[0]} style={{ width: 200 }} onChange={handleChange}>
{map(groups, (item) => (
<Option value={item} key={item}>
{item}
</Option>
))}
</Select>
</div>
</IF>
<ChartRender {...props} hasData={hasData} getOption={getOption} />
</>
);
}