mirror of
https://github.com/didi/KnowStreaming.git
synced 2026-01-03 02:52:08 +08:00
feat: 图表支持存储拖拽排序 & 补点逻辑优化
This commit is contained in:
@@ -2,9 +2,10 @@ import React, { forwardRef, useEffect, useImperativeHandle, useMemo, useRef, use
|
|||||||
import { AppContainer, Drawer, Spin, Table, SingleChart, Utils, Tooltip } from 'knowdesign';
|
import { AppContainer, Drawer, Spin, Table, SingleChart, Utils, Tooltip } from 'knowdesign';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import api, { MetricType } from '@src/api';
|
import api, { MetricType } from '@src/api';
|
||||||
|
import { MetricDefaultChartDataType, MetricChartDataType, formatChartData } from '@src/constants/chartConfig';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
import { MetricDefaultChartDataType, MetricChartDataType, formatChartData, getDetailChartConfig } from './config';
|
import { getDetailChartConfig } from './config';
|
||||||
import { UNIT_MAP } from '@src/constants/chartConfig';
|
import { UNIT_MAP } from '@src/constants/chartConfig';
|
||||||
import RenderEmpty from '../RenderEmpty';
|
import RenderEmpty from '../RenderEmpty';
|
||||||
|
|
||||||
@@ -50,8 +51,6 @@ interface DataZoomEventProps {
|
|||||||
const DATA_ZOOM_DEFAULT_SCALE = 0.25;
|
const DATA_ZOOM_DEFAULT_SCALE = 0.25;
|
||||||
// 单次向服务器请求数据的范围(默认 6 小时,超过后采集频率间隔会变长),单位: ms
|
// 单次向服务器请求数据的范围(默认 6 小时,超过后采集频率间隔会变长),单位: ms
|
||||||
const DEFAULT_REQUEST_TIME_RANGE = 6 * 60 * 60 * 1000;
|
const DEFAULT_REQUEST_TIME_RANGE = 6 * 60 * 60 * 1000;
|
||||||
// 采样间隔,影响前端补点逻辑,单位: ms
|
|
||||||
const DEFAULT_POINT_INTERVAL = 60 * 1000;
|
|
||||||
// 向服务器每轮请求的数量
|
// 向服务器每轮请求的数量
|
||||||
const DEFAULT_REQUEST_COUNT = 6;
|
const DEFAULT_REQUEST_COUNT = 6;
|
||||||
// 进入详情页默认展示的时间范围
|
// 进入详情页默认展示的时间范围
|
||||||
@@ -376,8 +375,6 @@ const ChartDetail = (props: ChartDetailProps) => {
|
|||||||
global.getMetricDefine || {},
|
global.getMetricDefine || {},
|
||||||
metricType,
|
metricType,
|
||||||
timeRange,
|
timeRange,
|
||||||
DEFAULT_POINT_INTERVAL,
|
|
||||||
false,
|
|
||||||
chartInfo.current.transformUnit
|
chartInfo.current.transformUnit
|
||||||
) as MetricChartDataType[];
|
) as MetricChartDataType[];
|
||||||
// 增量填充图表数据
|
// 增量填充图表数据
|
||||||
@@ -540,14 +537,7 @@ const ChartDetail = (props: ChartDetailProps) => {
|
|||||||
if (res?.length) {
|
if (res?.length) {
|
||||||
// 格式化图表需要的数据
|
// 格式化图表需要的数据
|
||||||
const formattedMetricData = (
|
const formattedMetricData = (
|
||||||
formatChartData(
|
formatChartData(res, global.getMetricDefine || {}, metricType, curTimeRange) as MetricChartDataType[]
|
||||||
res,
|
|
||||||
global.getMetricDefine || {},
|
|
||||||
metricType,
|
|
||||||
curTimeRange,
|
|
||||||
DEFAULT_POINT_INTERVAL,
|
|
||||||
false
|
|
||||||
) as MetricChartDataType[]
|
|
||||||
)[0];
|
)[0];
|
||||||
// 填充图表数据
|
// 填充图表数据
|
||||||
let initFullTimeRange = curTimeRange;
|
let initFullTimeRange = curTimeRange;
|
||||||
|
|||||||
@@ -1,150 +1,4 @@
|
|||||||
import { getUnit, getDataNumberUnit, getBasicChartConfig, CHART_COLOR_LIST } from '@src/constants/chartConfig';
|
import { getBasicChartConfig, CHART_COLOR_LIST } from '@src/constants/chartConfig';
|
||||||
import { MetricType } from '@src/api';
|
|
||||||
import { MetricsDefine } from '@src/pages/CommonConfig';
|
|
||||||
|
|
||||||
export interface MetricInfo {
|
|
||||||
name: string;
|
|
||||||
desc: string;
|
|
||||||
type: number;
|
|
||||||
set: boolean;
|
|
||||||
support: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 接口返回图表原始数据类型
|
|
||||||
export interface MetricDefaultChartDataType {
|
|
||||||
metricName: string;
|
|
||||||
metricLines: {
|
|
||||||
name: string;
|
|
||||||
createTime: number;
|
|
||||||
updateTime: number;
|
|
||||||
metricPoints: {
|
|
||||||
aggType: string;
|
|
||||||
timeStamp: number;
|
|
||||||
value: number;
|
|
||||||
createTime: number;
|
|
||||||
updateTime: number;
|
|
||||||
}[];
|
|
||||||
}[];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 格式化后图表数据类型
|
|
||||||
export interface MetricChartDataType {
|
|
||||||
metricName: string;
|
|
||||||
metricUnit: string;
|
|
||||||
metricLines: {
|
|
||||||
name: string;
|
|
||||||
data: (string | number)[][];
|
|
||||||
}[];
|
|
||||||
dragKey?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 补点
|
|
||||||
export const supplementaryPoints = (
|
|
||||||
lines: MetricChartDataType['metricLines'],
|
|
||||||
timeRange: readonly [number, number],
|
|
||||||
interval: number,
|
|
||||||
extraCallback?: (point: [number, 0]) => any[]
|
|
||||||
) => {
|
|
||||||
lines.forEach(({ data }) => {
|
|
||||||
// 获取未补点前线条的点的个数
|
|
||||||
let len = data.length;
|
|
||||||
// 记录当前处理到的点的下标值
|
|
||||||
let i = 0;
|
|
||||||
|
|
||||||
for (; i < len; i++) {
|
|
||||||
if (i === 0) {
|
|
||||||
let firstPointTimestamp = data[0][0] as number;
|
|
||||||
while (firstPointTimestamp - interval > timeRange[0]) {
|
|
||||||
const prevPointTimestamp = firstPointTimestamp - interval;
|
|
||||||
data.unshift(extraCallback ? extraCallback([prevPointTimestamp, 0]) : [prevPointTimestamp, 0]);
|
|
||||||
firstPointTimestamp = prevPointTimestamp;
|
|
||||||
len++;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i === len - 1) {
|
|
||||||
let lastPointTimestamp = data[i][0] as number;
|
|
||||||
while (lastPointTimestamp + interval < timeRange[1]) {
|
|
||||||
const nextPointTimestamp = lastPointTimestamp + interval;
|
|
||||||
data.push(extraCallback ? extraCallback([nextPointTimestamp, 0]) : [nextPointTimestamp, 0]);
|
|
||||||
lastPointTimestamp = nextPointTimestamp;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
let timestamp = data[i][0] as number;
|
|
||||||
while (timestamp + interval < data[i + 1][0]) {
|
|
||||||
const nextPointTimestamp = timestamp + interval;
|
|
||||||
data.splice(i + 1, 0, extraCallback ? extraCallback([nextPointTimestamp, 0]) : [nextPointTimestamp, 0]);
|
|
||||||
timestamp = nextPointTimestamp;
|
|
||||||
len++;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// 格式化图表数据
|
|
||||||
export const formatChartData = (
|
|
||||||
metricData: MetricDefaultChartDataType[],
|
|
||||||
getMetricDefine: (type: MetricType, metric: string) => MetricsDefine[keyof MetricsDefine],
|
|
||||||
metricType: MetricType,
|
|
||||||
timeRange: readonly [number, number],
|
|
||||||
supplementaryInterval: number,
|
|
||||||
needDrag = false,
|
|
||||||
transformUnit: [string, number] = undefined
|
|
||||||
): MetricChartDataType[] => {
|
|
||||||
return metricData.map(({ metricName, metricLines }) => {
|
|
||||||
const curMetricInfo = (getMetricDefine && getMetricDefine(metricType, metricName)) || null;
|
|
||||||
const isByteUnit = curMetricInfo?.unit?.toLowerCase().includes('byte');
|
|
||||||
let maxValue = -1;
|
|
||||||
|
|
||||||
const PointsMapMethod = ({ timeStamp, value }: { timeStamp: number; value: string | number }) => {
|
|
||||||
let parsedValue: string | number = Number(value);
|
|
||||||
|
|
||||||
if (Number.isNaN(parsedValue)) {
|
|
||||||
parsedValue = value;
|
|
||||||
} else {
|
|
||||||
// 为避免出现过小的数字影响图表展示效果,图表值统一只保留到小数点后三位
|
|
||||||
parsedValue = parseFloat(parsedValue.toFixed(3));
|
|
||||||
if (maxValue < parsedValue) maxValue = parsedValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [timeStamp, parsedValue];
|
|
||||||
};
|
|
||||||
|
|
||||||
const chartData = Object.assign(
|
|
||||||
{
|
|
||||||
metricName,
|
|
||||||
metricUnit: curMetricInfo?.unit || '',
|
|
||||||
metricLines: metricLines
|
|
||||||
.sort((a, b) => Number(a.name < b.name) - 0.5)
|
|
||||||
.map(({ name, metricPoints }) => ({
|
|
||||||
name,
|
|
||||||
data: metricPoints.map(PointsMapMethod),
|
|
||||||
})),
|
|
||||||
},
|
|
||||||
needDrag ? { dragKey: 999 } : {}
|
|
||||||
);
|
|
||||||
|
|
||||||
chartData.metricLines.forEach(({ data }) => data.sort((a, b) => (a[0] as number) - (b[0] as number)));
|
|
||||||
supplementaryPoints(chartData.metricLines, timeRange, supplementaryInterval);
|
|
||||||
|
|
||||||
// 将所有图表点的值按单位进行转换
|
|
||||||
if (maxValue > 0) {
|
|
||||||
const [unitName, unitSize]: [string, number] = transformUnit || isByteUnit ? getUnit(maxValue) : getDataNumberUnit(maxValue);
|
|
||||||
chartData.metricUnit = isByteUnit
|
|
||||||
? chartData.metricUnit.toLowerCase().replace('byte', unitName)
|
|
||||||
: `${unitName}${chartData.metricUnit}`;
|
|
||||||
chartData.metricLines.forEach(({ data }) => data.forEach((point: any) => (point[1] /= unitSize)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return chartData;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const seriesCallback = (lines: { name: string; data: [number, string | number][] }[]) => {
|
const seriesCallback = (lines: { name: string; data: [number, string | number][] }[]) => {
|
||||||
const len = CHART_COLOR_LIST.length;
|
const len = CHART_COLOR_LIST.length;
|
||||||
|
|||||||
@@ -1,14 +1,21 @@
|
|||||||
import React, { useState, useEffect, useRef } from 'react';
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
import { arrayMoveImmutable } from 'array-move';
|
import { arrayMoveImmutable } from 'array-move';
|
||||||
import { Utils, Empty, IconFont, Spin, AppContainer, SingleChart, Tooltip } from 'knowdesign';
|
import { Utils, Empty, Spin, AppContainer, SingleChart, Tooltip } from 'knowdesign';
|
||||||
|
import { IconFont } from '@knowdesign/icons';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import api, { MetricType } from '@src/api';
|
import api, { MetricType } from '@src/api';
|
||||||
|
import {
|
||||||
|
MetricInfo,
|
||||||
|
MetricDefaultChartDataType,
|
||||||
|
MetricChartDataType,
|
||||||
|
formatChartData,
|
||||||
|
resolveMetricsRank,
|
||||||
|
} from '@src/constants/chartConfig';
|
||||||
import SingleChartHeader, { KsHeaderOptions } from '../SingleChartHeader';
|
import SingleChartHeader, { KsHeaderOptions } from '../SingleChartHeader';
|
||||||
import DragGroup from '../DragGroup';
|
import DragGroup from '../DragGroup';
|
||||||
import ChartDetail from './ChartDetail';
|
import ChartDetail from './ChartDetail';
|
||||||
import { MetricInfo, MetricDefaultChartDataType, MetricChartDataType, formatChartData, getChartConfig } from './config';
|
import { getChartConfig } from './config';
|
||||||
import './index.less';
|
import './index.less';
|
||||||
import { MAX_TIME_RANGE_WITH_SMALL_POINT_INTERVAL } from '@src/constants/common';
|
|
||||||
|
|
||||||
interface IcustomScope {
|
interface IcustomScope {
|
||||||
label: string;
|
label: string;
|
||||||
@@ -39,8 +46,8 @@ const DashboardDragChart = (props: PropsType): JSX.Element => {
|
|||||||
const [curHeaderOptions, setCurHeaderOptions] = useState<ChartFilterOptions>();
|
const [curHeaderOptions, setCurHeaderOptions] = useState<ChartFilterOptions>();
|
||||||
const [metricChartData, setMetricChartData] = useState<MetricChartDataType[]>([]); // 指标图表数据列表
|
const [metricChartData, setMetricChartData] = useState<MetricChartDataType[]>([]); // 指标图表数据列表
|
||||||
const [gridNum, setGridNum] = useState<number>(12); // 图表列布局
|
const [gridNum, setGridNum] = useState<number>(12); // 图表列布局
|
||||||
|
const metricRankList = useRef<string[]>([]);
|
||||||
const chartDetailRef = useRef(null);
|
const chartDetailRef = useRef(null);
|
||||||
const chartDragOrder = useRef([]);
|
|
||||||
const curFetchingTimestamp = useRef(0);
|
const curFetchingTimestamp = useRef(0);
|
||||||
|
|
||||||
// 获取节点范围列表
|
// 获取节点范围列表
|
||||||
@@ -60,23 +67,33 @@ const DashboardDragChart = (props: PropsType): JSX.Element => {
|
|||||||
setScopeList(list);
|
setScopeList(list);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 更新 rank
|
||||||
|
const updateRank = (metricList: MetricInfo[]) => {
|
||||||
|
const { list, listInfo, shouldUpdate } = resolveMetricsRank(metricList);
|
||||||
|
metricRankList.current = list;
|
||||||
|
if (shouldUpdate) {
|
||||||
|
setMetricList(listInfo);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 获取指标列表
|
// 获取指标列表
|
||||||
const getMetricList = () => {
|
const getMetricList = () => {
|
||||||
Utils.request(api.getDashboardMetricList(clusterId, dashboardType)).then((res: MetricInfo[] | null) => {
|
Utils.request(api.getDashboardMetricList(clusterId, dashboardType)).then((res: MetricInfo[] | null) => {
|
||||||
if (!res) return;
|
if (!res) return;
|
||||||
const showMetrics = res.filter((metric) => metric.support);
|
const supportMetrics = res.filter((metric) => metric.support);
|
||||||
const selectedMetrics = showMetrics.filter((metric) => metric.set).map((metric) => metric.name);
|
const selectedMetrics = supportMetrics.filter((metric) => metric.set).map((metric) => metric.name);
|
||||||
setMetricsList(showMetrics);
|
updateRank([...supportMetrics]);
|
||||||
|
setMetricsList(supportMetrics);
|
||||||
setSelectedMetricNames(selectedMetrics);
|
setSelectedMetricNames(selectedMetrics);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 更新指标
|
// 更新指标
|
||||||
const setMetricList = (metricsSet: { [name: string]: boolean }) => {
|
const setMetricList = (metricDetailDTOList: { metric: string; rank: number; set: boolean }[]) => {
|
||||||
return Utils.request(api.getDashboardMetricList(clusterId, dashboardType), {
|
return Utils.request(api.getDashboardMetricList(clusterId, dashboardType), {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: {
|
data: {
|
||||||
metricsSet,
|
metricDetailDTOList,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -84,10 +101,11 @@ const DashboardDragChart = (props: PropsType): JSX.Element => {
|
|||||||
// 根据筛选项获取图表信息
|
// 根据筛选项获取图表信息
|
||||||
const getMetricChartData = () => {
|
const getMetricChartData = () => {
|
||||||
!curHeaderOptions.isAutoReload && setLoading(true);
|
!curHeaderOptions.isAutoReload && setLoading(true);
|
||||||
const [startTime, endTime] = curHeaderOptions.rangeTime;
|
|
||||||
|
|
||||||
|
const [startTime, endTime] = curHeaderOptions.rangeTime;
|
||||||
const curTimestamp = Date.now();
|
const curTimestamp = Date.now();
|
||||||
curFetchingTimestamp.current = curTimestamp;
|
curFetchingTimestamp.current = curTimestamp;
|
||||||
|
|
||||||
Utils.post(api.getDashboardMetricChartData(clusterId, dashboardType), {
|
Utils.post(api.getDashboardMetricChartData(clusterId, dashboardType), {
|
||||||
startTime,
|
startTime,
|
||||||
endTime,
|
endTime,
|
||||||
@@ -108,36 +126,20 @@ const DashboardDragChart = (props: PropsType): JSX.Element => {
|
|||||||
setMetricChartData([]);
|
setMetricChartData([]);
|
||||||
} else {
|
} else {
|
||||||
// 格式化图表需要的数据
|
// 格式化图表需要的数据
|
||||||
const supplementaryInterval = (endTime - startTime > MAX_TIME_RANGE_WITH_SMALL_POINT_INTERVAL ? 10 : 1) * 60 * 1000;
|
|
||||||
const formattedMetricData = formatChartData(
|
const formattedMetricData = formatChartData(
|
||||||
res,
|
res,
|
||||||
global.getMetricDefine || {},
|
global.getMetricDefine || {},
|
||||||
dashboardType,
|
dashboardType,
|
||||||
curHeaderOptions.rangeTime,
|
curHeaderOptions.rangeTime
|
||||||
supplementaryInterval,
|
|
||||||
true
|
|
||||||
) as MetricChartDataType[];
|
) as MetricChartDataType[];
|
||||||
// 处理图表的拖拽顺序
|
// 指标排序
|
||||||
if (chartDragOrder.current && chartDragOrder.current.length) {
|
formattedMetricData.sort((a, b) => metricRankList.current.indexOf(a.metricName) - metricRankList.current.indexOf(b.metricName));
|
||||||
// 根据当前拖拽顺序排列图表数据
|
|
||||||
formattedMetricData.forEach((metric) => {
|
|
||||||
const i = chartDragOrder.current.indexOf(metric.metricName);
|
|
||||||
metric.dragKey = i === -1 ? 999 : i;
|
|
||||||
});
|
|
||||||
formattedMetricData.sort((a, b) => a.dragKey - b.dragKey);
|
|
||||||
}
|
|
||||||
// 更新当前拖拽顺序(处理新增或减少图表的情况)
|
|
||||||
chartDragOrder.current = formattedMetricData.map((data) => data.metricName);
|
|
||||||
|
|
||||||
setMetricChartData(formattedMetricData);
|
setMetricChartData(formattedMetricData);
|
||||||
}
|
}
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
},
|
},
|
||||||
() => {
|
() => curFetchingTimestamp.current === curTimestamp && setLoading(false)
|
||||||
if (curFetchingTimestamp.current === curTimestamp) {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -163,11 +165,19 @@ const DashboardDragChart = (props: PropsType): JSX.Element => {
|
|||||||
|
|
||||||
// 指标选中项更新回调
|
// 指标选中项更新回调
|
||||||
const indicatorChangeCallback = (newMetricNames: (string | number)[]) => {
|
const indicatorChangeCallback = (newMetricNames: (string | number)[]) => {
|
||||||
const updateMetrics: { [name: string]: boolean } = {};
|
const updateMetrics: { metric: string; set: boolean; rank: number }[] = [];
|
||||||
// 需要选中的指标
|
// 需要选中的指标
|
||||||
newMetricNames.forEach((name) => !selectedMetricNames.includes(name) && (updateMetrics[name] = true));
|
newMetricNames.forEach(
|
||||||
|
(name) =>
|
||||||
|
!selectedMetricNames.includes(name) &&
|
||||||
|
updateMetrics.push({ metric: name as string, set: true, rank: metricsList.find(({ name: metric }) => metric === name)?.rank })
|
||||||
|
);
|
||||||
// 取消选中的指标
|
// 取消选中的指标
|
||||||
selectedMetricNames.forEach((name) => !newMetricNames.includes(name) && (updateMetrics[name] = false));
|
selectedMetricNames.forEach(
|
||||||
|
(name) =>
|
||||||
|
!newMetricNames.includes(name) &&
|
||||||
|
updateMetrics.push({ metric: name as string, set: false, rank: metricsList.find(({ name: metric }) => metric === name)?.rank })
|
||||||
|
);
|
||||||
|
|
||||||
const requestPromise = Object.keys(updateMetrics).length ? setMetricList(updateMetrics) : Promise.resolve();
|
const requestPromise = Object.keys(updateMetrics).length ? setMetricList(updateMetrics) : Promise.resolve();
|
||||||
requestPromise.then(
|
requestPromise.then(
|
||||||
@@ -186,7 +196,11 @@ const DashboardDragChart = (props: PropsType): JSX.Element => {
|
|||||||
// 拖拽结束回调,更新图表顺序,并触发图表的 onDrag 事件( 设置为 false ),允许同步展示图表的 tooltip
|
// 拖拽结束回调,更新图表顺序,并触发图表的 onDrag 事件( 设置为 false ),允许同步展示图表的 tooltip
|
||||||
const dragEnd = ({ oldIndex, newIndex }: { oldIndex: number; newIndex: number }) => {
|
const dragEnd = ({ oldIndex, newIndex }: { oldIndex: number; newIndex: number }) => {
|
||||||
busInstance.emit('onDrag', false);
|
busInstance.emit('onDrag', false);
|
||||||
chartDragOrder.current = arrayMoveImmutable(chartDragOrder.current, oldIndex, newIndex);
|
const originFrom = metricRankList.current.indexOf(metricChartData[oldIndex].metricName);
|
||||||
|
const originTarget = metricRankList.current.indexOf(metricChartData[newIndex].metricName);
|
||||||
|
const newList = arrayMoveImmutable(metricRankList.current, originFrom, originTarget);
|
||||||
|
metricRankList.current = newList;
|
||||||
|
setMetricList(newList.map((metric, rank) => ({ metric, rank, set: metricsList.find(({ name }) => metric === name)?.set || false })));
|
||||||
setMetricChartData(arrayMoveImmutable(metricChartData, oldIndex, newIndex));
|
setMetricChartData(arrayMoveImmutable(metricChartData, oldIndex, newIndex));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,44 @@
|
|||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
import { MetricType } from '@src/api';
|
||||||
|
import { MetricsDefine } from '@src/pages/CommonConfig';
|
||||||
|
|
||||||
|
export interface MetricInfo {
|
||||||
|
name: string;
|
||||||
|
desc: string;
|
||||||
|
type: number;
|
||||||
|
set: boolean;
|
||||||
|
rank: number | null;
|
||||||
|
support: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 接口返回图表原始数据类型
|
||||||
|
export interface MetricDefaultChartDataType {
|
||||||
|
metricName: string;
|
||||||
|
metricLines: {
|
||||||
|
name: string;
|
||||||
|
createTime: number;
|
||||||
|
updateTime: number;
|
||||||
|
metricPoints: {
|
||||||
|
aggType: string;
|
||||||
|
timeStamp: number;
|
||||||
|
value: number;
|
||||||
|
createTime: number;
|
||||||
|
updateTime: number;
|
||||||
|
}[];
|
||||||
|
}[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化后图表数据类型
|
||||||
|
export interface MetricChartDataType {
|
||||||
|
metricName: string;
|
||||||
|
metricUnit: string;
|
||||||
|
metricLines: {
|
||||||
|
name: string;
|
||||||
|
data: (string | number)[][];
|
||||||
|
}[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 图表颜色库
|
||||||
export const CHART_COLOR_LIST = [
|
export const CHART_COLOR_LIST = [
|
||||||
'#556ee6',
|
'#556ee6',
|
||||||
'#94BEF2',
|
'#94BEF2',
|
||||||
@@ -16,27 +55,176 @@ export const CHART_COLOR_LIST = [
|
|||||||
'#C9E795',
|
'#C9E795',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// 图表存储单位换算
|
||||||
export const UNIT_MAP = {
|
export const UNIT_MAP = {
|
||||||
TB: Math.pow(1024, 4),
|
TB: Math.pow(1024, 4),
|
||||||
GB: Math.pow(1024, 3),
|
GB: Math.pow(1024, 3),
|
||||||
MB: Math.pow(1024, 2),
|
MB: Math.pow(1024, 2),
|
||||||
KB: 1024,
|
KB: 1024,
|
||||||
};
|
};
|
||||||
|
export const getUnit = (value: number) => Object.entries(UNIT_MAP).find(([, size]) => value / size >= 1) || ['Byte', 1];
|
||||||
|
|
||||||
|
// 图表数字单位换算
|
||||||
export const DATA_NUMBER_MAP = {
|
export const DATA_NUMBER_MAP = {
|
||||||
十亿: Math.pow(1000, 3),
|
十亿: Math.pow(1000, 3),
|
||||||
百万: Math.pow(1000, 2),
|
百万: Math.pow(1000, 2),
|
||||||
千: 1000,
|
千: 1000,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getUnit = (value: number) => Object.entries(UNIT_MAP).find(([, size]) => value / size >= 1) || ['Byte', 1];
|
|
||||||
|
|
||||||
export const getDataNumberUnit = (value: number) => Object.entries(DATA_NUMBER_MAP).find(([, size]) => value / size >= 1) || ['', 1];
|
export const getDataNumberUnit = (value: number) => Object.entries(DATA_NUMBER_MAP).find(([, size]) => value / size >= 1) || ['', 1];
|
||||||
|
|
||||||
|
// 图表补点间隔计算
|
||||||
|
export const SUPPLEMENTARY_INTERVAL_MAP = {
|
||||||
|
'0': 60 * 1000,
|
||||||
|
// 6 小时:10 分钟间隔
|
||||||
|
'21600000': 10 * 60 * 1000,
|
||||||
|
// 24 小时:1 小时间隔
|
||||||
|
'86400000': 60 * 60 * 1000,
|
||||||
|
};
|
||||||
|
export const getSupplementaryInterval = (range: number) => {
|
||||||
|
return Object.entries(SUPPLEMENTARY_INTERVAL_MAP)
|
||||||
|
.reverse()
|
||||||
|
.find(([curRange]) => range > Number(curRange))[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理图表排序
|
||||||
|
export const resolveMetricsRank = (metricList: MetricInfo[]) => {
|
||||||
|
const isRanked = metricList.some(({ rank }) => rank !== null);
|
||||||
|
let shouldUpdate = false;
|
||||||
|
let list: string[] = [];
|
||||||
|
if (isRanked) {
|
||||||
|
const rankedMetrics = metricList.filter(({ rank }) => rank !== null).sort((a, b) => a.rank - b.rank);
|
||||||
|
const unRankedMetrics = metricList.filter(({ rank }) => rank === null);
|
||||||
|
// 如果有新增/删除指标的情况,需要触发更新
|
||||||
|
if (unRankedMetrics.length || rankedMetrics.some(({ rank }, i) => rank !== i)) {
|
||||||
|
shouldUpdate = true;
|
||||||
|
}
|
||||||
|
list = [...rankedMetrics.map(({ name }) => name), ...unRankedMetrics.map(({ name }) => name).sort()];
|
||||||
|
} else {
|
||||||
|
shouldUpdate = true;
|
||||||
|
// 按字母先后顺序初始化指标排序
|
||||||
|
list = metricList.map(({ name }) => name).sort();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
list,
|
||||||
|
listInfo: list.map((metric, rank) => ({ metric, rank, set: metricList.find(({ name }) => metric === name)?.set || false })),
|
||||||
|
shouldUpdate,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// 补点
|
||||||
|
export const supplementaryPoints = (
|
||||||
|
lines: MetricChartDataType['metricLines'],
|
||||||
|
timeRange: readonly [number, number],
|
||||||
|
extraCallback?: (point: [number, 0]) => any[]
|
||||||
|
): void => {
|
||||||
|
const interval = getSupplementaryInterval(timeRange[1] - timeRange[0]);
|
||||||
|
|
||||||
|
lines.forEach(({ data }) => {
|
||||||
|
// 获取未补点前线条的点的个数
|
||||||
|
let len = data.length;
|
||||||
|
// 记录当前处理到的点的下标值
|
||||||
|
let i = 0;
|
||||||
|
|
||||||
|
for (; i < len; i++) {
|
||||||
|
if (i === 0) {
|
||||||
|
let firstPointTimestamp = data[0][0] as number;
|
||||||
|
while (firstPointTimestamp - interval > timeRange[0]) {
|
||||||
|
const prevPointTimestamp = firstPointTimestamp - interval;
|
||||||
|
data.unshift(extraCallback ? extraCallback([prevPointTimestamp, 0]) : [prevPointTimestamp, 0]);
|
||||||
|
firstPointTimestamp = prevPointTimestamp;
|
||||||
|
len++;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i === len - 1) {
|
||||||
|
let lastPointTimestamp = data[i][0] as number;
|
||||||
|
while (lastPointTimestamp + interval < timeRange[1]) {
|
||||||
|
const nextPointTimestamp = lastPointTimestamp + interval;
|
||||||
|
data.push(extraCallback ? extraCallback([nextPointTimestamp, 0]) : [nextPointTimestamp, 0]);
|
||||||
|
lastPointTimestamp = nextPointTimestamp;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let timestamp = data[i][0] as number;
|
||||||
|
while (timestamp + interval < data[i + 1][0]) {
|
||||||
|
const nextPointTimestamp = timestamp + interval;
|
||||||
|
data.splice(i + 1, 0, extraCallback ? extraCallback([nextPointTimestamp, 0]) : [nextPointTimestamp, 0]);
|
||||||
|
timestamp = nextPointTimestamp;
|
||||||
|
len++;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 格式化图表数据
|
||||||
|
export const formatChartData = (
|
||||||
|
// 图表源数据
|
||||||
|
metricData: MetricDefaultChartDataType[],
|
||||||
|
// 获取指标单位
|
||||||
|
getMetricDefine: (type: MetricType, metric: string) => MetricsDefine[keyof MetricsDefine],
|
||||||
|
// 指标类型
|
||||||
|
metricType: MetricType,
|
||||||
|
// 图表时间范围,用于补点
|
||||||
|
timeRange: readonly [number, number],
|
||||||
|
transformUnit: [string, number] = undefined
|
||||||
|
): MetricChartDataType[] => {
|
||||||
|
return metricData.map(({ metricName, metricLines }) => {
|
||||||
|
const curMetricInfo = (getMetricDefine && getMetricDefine(metricType, metricName)) || null;
|
||||||
|
const isByteUnit = curMetricInfo?.unit?.toLowerCase().includes('byte');
|
||||||
|
let maxValue = -1;
|
||||||
|
|
||||||
|
const PointsMapMethod = ({ timeStamp, value }: { timeStamp: number; value: string | number }) => {
|
||||||
|
let parsedValue: string | number = Number(value);
|
||||||
|
|
||||||
|
if (Number.isNaN(parsedValue)) {
|
||||||
|
parsedValue = value;
|
||||||
|
} else {
|
||||||
|
// 为避免出现过小的数字影响图表展示效果,图表值统一保留小数点后三位
|
||||||
|
parsedValue = parseFloat(parsedValue.toFixed(3));
|
||||||
|
if (maxValue < parsedValue) maxValue = parsedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [timeStamp, parsedValue];
|
||||||
|
};
|
||||||
|
|
||||||
|
// 初始化返回结构
|
||||||
|
const chartData = {
|
||||||
|
metricName,
|
||||||
|
metricUnit: curMetricInfo?.unit || '',
|
||||||
|
metricLines: metricLines
|
||||||
|
.sort((a, b) => Number(a.name < b.name) - 0.5)
|
||||||
|
.map(({ name, metricPoints }) => ({
|
||||||
|
name,
|
||||||
|
data: metricPoints.map(PointsMapMethod),
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
// 按时间先后进行对图表点排序
|
||||||
|
chartData.metricLines.forEach(({ data }) => data.sort((a, b) => (a[0] as number) - (b[0] as number)));
|
||||||
|
|
||||||
|
// 图表值单位转换
|
||||||
|
if (maxValue > 0) {
|
||||||
|
const [unitName, unitSize]: [string, number] = transformUnit || isByteUnit ? getUnit(maxValue) : getDataNumberUnit(maxValue);
|
||||||
|
chartData.metricUnit = isByteUnit
|
||||||
|
? chartData.metricUnit.toLowerCase().replace('byte', unitName)
|
||||||
|
: `${unitName}${chartData.metricUnit}`;
|
||||||
|
chartData.metricLines.forEach(({ data }) => data.forEach((point: any) => (point[1] /= unitSize)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 补点
|
||||||
|
supplementaryPoints(chartData.metricLines, timeRange);
|
||||||
|
|
||||||
|
return chartData;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// 图表 tooltip 基础展示样式
|
// 图表 tooltip 基础展示样式
|
||||||
const tooltipFormatter = (date: any, arr: any, tooltip: any) => {
|
const tooltipFormatter = (date: any, arr: any, tooltip: any) => {
|
||||||
// 从大到小排序
|
|
||||||
// arr = arr.sort((a: any, b: any) => b.value - a.value);
|
|
||||||
const str = arr
|
const str = arr
|
||||||
.map(
|
.map(
|
||||||
(item: any) => `<div style="margin: 3px 0;">
|
(item: any) => `<div style="margin: 3px 0;">
|
||||||
@@ -121,9 +309,6 @@ export const getBasicChartConfig = (props: any = {}) => {
|
|||||||
itemWidth: 8,
|
itemWidth: 8,
|
||||||
itemGap: 8,
|
itemGap: 8,
|
||||||
textStyle: {
|
textStyle: {
|
||||||
// width: 85,
|
|
||||||
// overflow: 'truncate',
|
|
||||||
// ellipsis: '...',
|
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
color: '#74788D',
|
color: '#74788D',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -37,9 +37,6 @@ export const SMALL_DRAWER_WIDTH = 480;
|
|||||||
export const MIDDLE_DRAWER_WIDTH = 728;
|
export const MIDDLE_DRAWER_WIDTH = 728;
|
||||||
export const LARGE_DRAWER_WIDTH = 1080;
|
export const LARGE_DRAWER_WIDTH = 1080;
|
||||||
|
|
||||||
// 小间隔(1 分钟)图表点的最大请求时间范围,单位: ms
|
|
||||||
export const MAX_TIME_RANGE_WITH_SMALL_POINT_INTERVAL = 6 * 60 * 60 * 1000;
|
|
||||||
|
|
||||||
export const primaryColor = '#556EE6';
|
export const primaryColor = '#556EE6';
|
||||||
|
|
||||||
export const numberToFixed = (value: number, num = 2) => {
|
export const numberToFixed = (value: number, num = 2) => {
|
||||||
|
|||||||
@@ -1,30 +1,26 @@
|
|||||||
import { Col, Row, SingleChart, IconFont, Utils, Modal, Spin, Empty, AppContainer, Tooltip } from 'knowdesign';
|
import { Col, Row, SingleChart, Utils, Modal, Spin, Empty, AppContainer, Tooltip } from 'knowdesign';
|
||||||
|
import { IconFont } from '@knowdesign/icons';
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
|
import { arrayMoveImmutable } from 'array-move';
|
||||||
import api from '@src/api';
|
import api from '@src/api';
|
||||||
import { getChartConfig } from './config';
|
|
||||||
import './index.less';
|
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
MetricDefaultChartDataType,
|
MetricDefaultChartDataType,
|
||||||
MetricChartDataType,
|
MetricChartDataType,
|
||||||
formatChartData,
|
formatChartData,
|
||||||
supplementaryPoints,
|
supplementaryPoints,
|
||||||
} from '@src/components/DashboardDragChart/config';
|
resolveMetricsRank,
|
||||||
|
MetricInfo,
|
||||||
|
} from '@src/constants/chartConfig';
|
||||||
import { MetricType } from '@src/api';
|
import { MetricType } from '@src/api';
|
||||||
import { getDataNumberUnit, getUnit } from '@src/constants/chartConfig';
|
import { getDataNumberUnit, getUnit } from '@src/constants/chartConfig';
|
||||||
import SingleChartHeader, { KsHeaderOptions } from '@src/components/SingleChartHeader';
|
import SingleChartHeader, { KsHeaderOptions } from '@src/components/SingleChartHeader';
|
||||||
import { MAX_TIME_RANGE_WITH_SMALL_POINT_INTERVAL } from '@src/constants/common';
|
|
||||||
import RenderEmpty from '@src/components/RenderEmpty';
|
import RenderEmpty from '@src/components/RenderEmpty';
|
||||||
import DragGroup from '@src/components/DragGroup';
|
import DragGroup from '@src/components/DragGroup';
|
||||||
|
import { getChartConfig } from './config';
|
||||||
|
import './index.less';
|
||||||
|
|
||||||
type ChartFilterOptions = Omit<KsHeaderOptions, 'gridNum'>;
|
type ChartFilterOptions = Omit<KsHeaderOptions, 'gridNum'>;
|
||||||
interface MetricInfo {
|
|
||||||
type: number;
|
|
||||||
name: string;
|
|
||||||
desc: string;
|
|
||||||
set: boolean;
|
|
||||||
support: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MessagesInDefaultData {
|
interface MessagesInDefaultData {
|
||||||
aggType: string | null;
|
aggType: string | null;
|
||||||
@@ -71,8 +67,7 @@ const DetailChart = (props: { children: JSX.Element }): JSX.Element => {
|
|||||||
const [curHeaderOptions, setCurHeaderOptions] = useState<ChartFilterOptions>();
|
const [curHeaderOptions, setCurHeaderOptions] = useState<ChartFilterOptions>();
|
||||||
const [defaultChartLoading, setDefaultChartLoading] = useState<boolean>(true);
|
const [defaultChartLoading, setDefaultChartLoading] = useState<boolean>(true);
|
||||||
const [chartLoading, setChartLoading] = useState<boolean>(true);
|
const [chartLoading, setChartLoading] = useState<boolean>(true);
|
||||||
const [showChartDetailModal, setShowChartDetailModal] = useState<boolean>(false);
|
const metricRankList = useRef<string[]>([]);
|
||||||
const [chartDetail, setChartDetail] = useState<any>();
|
|
||||||
const curFetchingTimestamp = useRef({
|
const curFetchingTimestamp = useRef({
|
||||||
messagesIn: 0,
|
messagesIn: 0,
|
||||||
other: 0,
|
other: 0,
|
||||||
@@ -91,36 +86,53 @@ const DetailChart = (props: { children: JSX.Element }): JSX.Element => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 更新 rank
|
||||||
|
const updateRank = (metricList: MetricInfo[]) => {
|
||||||
|
const { list, listInfo, shouldUpdate } = resolveMetricsRank(metricList);
|
||||||
|
metricRankList.current = list;
|
||||||
|
if (shouldUpdate) {
|
||||||
|
updateMetricList(listInfo);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 获取指标列表
|
// 获取指标列表
|
||||||
const getMetricList = () => {
|
const getMetricList = () => {
|
||||||
Utils.request(api.getDashboardMetricList(clusterId, MetricType.Cluster)).then((res: MetricInfo[] | null) => {
|
Utils.request(api.getDashboardMetricList(clusterId, MetricType.Cluster)).then((res: MetricInfo[] | null) => {
|
||||||
if (!res) return;
|
if (!res) return;
|
||||||
const showMetrics = res.filter((metric) => metric.support);
|
const supportMetrics = res.filter((metric) => metric.support);
|
||||||
const selectedMetrics = showMetrics.filter((metric) => metric.set).map((metric) => metric.name);
|
const selectedMetrics = supportMetrics.filter((metric) => metric.set).map((metric) => metric.name);
|
||||||
!selectedMetrics.includes(DEFAULT_METRIC) && selectedMetrics.push(DEFAULT_METRIC);
|
!selectedMetrics.includes(DEFAULT_METRIC) && selectedMetrics.push(DEFAULT_METRIC);
|
||||||
setMetricList(showMetrics);
|
updateRank([...supportMetrics]);
|
||||||
|
setMetricList(supportMetrics);
|
||||||
setSelectedMetricNames(selectedMetrics);
|
setSelectedMetricNames(selectedMetrics);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 更新指标
|
// 更新指标
|
||||||
const updateMetricList = (metricsSet: { [name: string]: boolean }) => {
|
const updateMetricList = (metricDetailDTOList: { metric: string; rank: number; set: boolean }[]) => {
|
||||||
return Utils.request(api.getDashboardMetricList(clusterId, MetricType.Cluster), {
|
return Utils.request(api.getDashboardMetricList(clusterId, MetricType.Cluster), {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: {
|
data: {
|
||||||
metricsSet,
|
metricDetailDTOList,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 指标选中项更新回调
|
// 指标选中项更新回调
|
||||||
const indicatorChangeCallback = (newMetricNames: (string | number)[]) => {
|
const indicatorChangeCallback = (newMetricNames: (string | number)[]) => {
|
||||||
const updateMetrics: { [name: string]: boolean } = {};
|
const updateMetrics: { metric: string; set: boolean; rank: number }[] = [];
|
||||||
// 需要选中的指标
|
// 需要选中的指标
|
||||||
newMetricNames.forEach((name) => !selectedMetricNames.includes(name) && (updateMetrics[name] = true));
|
newMetricNames.forEach(
|
||||||
|
(name) =>
|
||||||
|
!selectedMetricNames.includes(name) &&
|
||||||
|
updateMetrics.push({ metric: name as string, set: true, rank: metricList.find(({ name: metric }) => metric === name)?.rank })
|
||||||
|
);
|
||||||
// 取消选中的指标
|
// 取消选中的指标
|
||||||
selectedMetricNames.forEach((name) => !newMetricNames.includes(name) && (updateMetrics[name] = false));
|
selectedMetricNames.forEach(
|
||||||
|
(name) =>
|
||||||
|
!newMetricNames.includes(name) &&
|
||||||
|
updateMetrics.push({ metric: name as string, set: false, rank: metricList.find(({ name: metric }) => metric === name)?.rank })
|
||||||
|
);
|
||||||
const requestPromise = Object.keys(updateMetrics).length ? updateMetricList(updateMetrics) : Promise.resolve();
|
const requestPromise = Object.keys(updateMetrics).length ? updateMetricList(updateMetrics) : Promise.resolve();
|
||||||
requestPromise.then(
|
requestPromise.then(
|
||||||
() => getMetricList(),
|
() => getMetricList(),
|
||||||
@@ -156,15 +168,16 @@ const DetailChart = (props: { children: JSX.Element }): JSX.Element => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const supplementaryInterval = (endTime - startTime > MAX_TIME_RANGE_WITH_SMALL_POINT_INTERVAL ? 10 : 1) * 60 * 1000;
|
|
||||||
const formattedMetricData: MetricChartDataType[] = formatChartData(
|
const formattedMetricData: MetricChartDataType[] = formatChartData(
|
||||||
res,
|
res,
|
||||||
global.getMetricDefine || {},
|
global.getMetricDefine || {},
|
||||||
MetricType.Cluster,
|
MetricType.Cluster,
|
||||||
curHeaderOptions.rangeTime,
|
curHeaderOptions.rangeTime
|
||||||
supplementaryInterval
|
|
||||||
);
|
);
|
||||||
formattedMetricData.forEach((data) => (data.metricLines[0].name = data.metricName));
|
formattedMetricData.forEach((data) => (data.metricLines[0].name = data.metricName));
|
||||||
|
// 指标排序
|
||||||
|
formattedMetricData.sort((a, b) => metricRankList.current.indexOf(a.metricName) - metricRankList.current.indexOf(b.metricName));
|
||||||
|
|
||||||
setMetricDataList(formattedMetricData);
|
setMetricDataList(formattedMetricData);
|
||||||
setChartLoading(false);
|
setChartLoading(false);
|
||||||
},
|
},
|
||||||
@@ -242,9 +255,7 @@ const DetailChart = (props: { children: JSX.Element }): JSX.Element => {
|
|||||||
...info,
|
...info,
|
||||||
value: 0,
|
value: 0,
|
||||||
}));
|
}));
|
||||||
const supplementaryInterval =
|
supplementaryPoints([line], curHeaderOptions.rangeTime, (point) => {
|
||||||
(curHeaderOptions.rangeTime[1] - curHeaderOptions.rangeTime[0] > MAX_TIME_RANGE_WITH_SMALL_POINT_INTERVAL ? 10 : 1) * 60 * 1000;
|
|
||||||
supplementaryPoints([line], curHeaderOptions.rangeTime, supplementaryInterval, (point) => {
|
|
||||||
point.push(extraMetrics as any);
|
point.push(extraMetrics as any);
|
||||||
return point;
|
return point;
|
||||||
});
|
});
|
||||||
@@ -263,6 +274,22 @@ const DetailChart = (props: { children: JSX.Element }): JSX.Element => {
|
|||||||
targetNode && targetNode.addEventListener('click', () => busInstance.emit('chartResize'));
|
targetNode && targetNode.addEventListener('click', () => busInstance.emit('chartResize'));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 拖拽开始回调,触发图表的 onDrag 事件( 设置为 true ),禁止同步展示图表的 tooltip
|
||||||
|
const dragStart = () => {
|
||||||
|
busInstance.emit('onDrag', true);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 拖拽结束回调,更新图表顺序,并触发图表的 onDrag 事件( 设置为 false ),允许同步展示图表的 tooltip
|
||||||
|
const dragEnd = ({ oldIndex, newIndex }: { oldIndex: number; newIndex: number }) => {
|
||||||
|
busInstance.emit('onDrag', false);
|
||||||
|
const originFrom = metricRankList.current.indexOf(metricDataList[oldIndex].metricName);
|
||||||
|
const originTarget = metricRankList.current.indexOf(metricDataList[newIndex].metricName);
|
||||||
|
const newList = arrayMoveImmutable(metricRankList.current, originFrom, originTarget);
|
||||||
|
metricRankList.current = newList;
|
||||||
|
updateMetricList(newList.map((metric, rank) => ({ metric, rank, set: metricList.find(({ name }) => metric === name)?.set || false })));
|
||||||
|
setMetricDataList(arrayMoveImmutable(metricDataList, oldIndex, newIndex));
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getMetricData();
|
getMetricData();
|
||||||
}, [selectedMetricNames]);
|
}, [selectedMetricNames]);
|
||||||
@@ -359,10 +386,10 @@ const DetailChart = (props: { children: JSX.Element }): JSX.Element => {
|
|||||||
<div className="no-group-con">
|
<div className="no-group-con">
|
||||||
<DragGroup
|
<DragGroup
|
||||||
sortableContainerProps={{
|
sortableContainerProps={{
|
||||||
onSortStart: () => 0,
|
onSortStart: dragStart,
|
||||||
onSortEnd: () => 0,
|
onSortEnd: dragEnd,
|
||||||
axis: 'xy',
|
axis: 'xy',
|
||||||
useDragHandle: false,
|
useDragHandle: true,
|
||||||
}}
|
}}
|
||||||
gridProps={{
|
gridProps={{
|
||||||
span: 12,
|
span: 12,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { useParams } from 'react-router-dom';
|
|||||||
import { AppContainer, Button, Divider, Drawer, Form, InputNumber, notification, SingleChart, Space, Spin, Utils } from 'knowdesign';
|
import { AppContainer, Button, Divider, Drawer, Form, InputNumber, notification, SingleChart, Space, Spin, Utils } from 'knowdesign';
|
||||||
import Api, { MetricType } from '@src/api/index';
|
import Api, { MetricType } from '@src/api/index';
|
||||||
import { getBasicChartConfig, getUnit } from '@src/constants/chartConfig';
|
import { getBasicChartConfig, getUnit } from '@src/constants/chartConfig';
|
||||||
import { formatChartData, MetricDefaultChartDataType } from '@src/components/DashboardDragChart/config';
|
import { formatChartData, MetricDefaultChartDataType } from '@src/constants/chartConfig';
|
||||||
|
|
||||||
const ExpandPartition = (props: { record: any; onConfirm: () => void }) => {
|
const ExpandPartition = (props: { record: any; onConfirm: () => void }) => {
|
||||||
const [global] = AppContainer.useGlobalValue();
|
const [global] = AppContainer.useGlobalValue();
|
||||||
@@ -74,8 +74,7 @@ const ExpandPartition = (props: { record: any; onConfirm: () => void }) => {
|
|||||||
],
|
],
|
||||||
global?.getMetricDefine || {},
|
global?.getMetricDefine || {},
|
||||||
MetricType.Topic,
|
MetricType.Topic,
|
||||||
[startStamp, endStamp],
|
[startStamp, endStamp]
|
||||||
10 * 60 * 1000
|
|
||||||
);
|
);
|
||||||
|
|
||||||
setMinByteInOut(minByteInOut < empiricalMinValue ? empiricalMinValue : minByteInOut);
|
setMinByteInOut(minByteInOut < empiricalMinValue ? empiricalMinValue : minByteInOut);
|
||||||
|
|||||||
Reference in New Issue
Block a user