mirror of
https://github.com/didi/KnowStreaming.git
synced 2026-01-07 15:12:14 +08:00
308 lines
11 KiB
TypeScript
308 lines
11 KiB
TypeScript
import React, { useState, useEffect, useRef } from 'react';
|
|
import { Utils, AppContainer } from 'knowdesign';
|
|
import api, { MetricType } from '@src/api';
|
|
import DBreadcrumb from 'knowdesign/es/extend/d-breadcrumb';
|
|
import ConnectCard from '@src/components/CardBar/ConnectCard';
|
|
import { useParams } from 'react-router-dom';
|
|
import { FormattedMetricData, formatChartData, MetricInfo } from '@src/constants/chartConfig';
|
|
import ChartOperateBar, { KsHeaderOptions } from '@src/components/ChartOperateBar';
|
|
import ChartDetail from '@src/components/DraggableCharts/Detail';
|
|
import ChartList from '@src/components/DraggableCharts/ChartList';
|
|
import MetricsFilter from './MetricsFilter';
|
|
import SelectContent, { Connector } from './SelectContent';
|
|
import './index.less';
|
|
import { ConnectCluster } from '../Connect/AddConnector';
|
|
import HasConnector from '../Connect/HasConnector';
|
|
|
|
type ChartFilterOptions = Omit<KsHeaderOptions, 'gridNum'>;
|
|
|
|
const { EventBus } = Utils;
|
|
const busInstance = new EventBus();
|
|
|
|
const DraggableCharts = (): JSX.Element => {
|
|
const [global] = AppContainer.useGlobalValue();
|
|
const { clusterId } = useParams<{
|
|
clusterId: string;
|
|
}>();
|
|
const [loading, setLoading] = useState<boolean>(true);
|
|
// const [scopeList, setScopeList] = useState<IcustomScope[]>([]); // 节点范围列表
|
|
const [curHeaderOptions, setCurHeaderOptions] = useState<ChartFilterOptions>();
|
|
const [metricList, setMetricList] = useState<{ [key: string]: (string | number)[] }>({});
|
|
const [metricChartData, setMetricChartData] = useState<FormattedMetricData[]>([]); // 指标图表数据列表
|
|
const [gridNum, setGridNum] = useState<number>(12); // 图表列布局
|
|
const [scopeList, setScopeList] = useState<{
|
|
connectClusters: ConnectCluster[];
|
|
connectors: Connector[];
|
|
}>({
|
|
connectClusters: [],
|
|
connectors: [],
|
|
});
|
|
const [screenType, setScreenType] = useState('all');
|
|
const curFetchingTimestamp = useRef(0);
|
|
const metricRankList = useRef<string[]>([]);
|
|
const metricFilterRef = useRef(null);
|
|
const chartDetailRef = useRef(null);
|
|
|
|
// 根据筛选项获取图表信息
|
|
const getMetricChartData = () => {
|
|
!curHeaderOptions.isAutoReload && setLoading(true);
|
|
const curTimestamp = Date.now();
|
|
curFetchingTimestamp.current = curTimestamp;
|
|
|
|
const [startTime, endTime] = curHeaderOptions.rangeTime;
|
|
const { connectClusters, connectors } = curHeaderOptions.scopeData.data;
|
|
const isTop = curHeaderOptions?.scopeData?.isTop;
|
|
const reqBasicBody = {
|
|
startTime,
|
|
endTime,
|
|
topNu: isTop ? curHeaderOptions.scopeData.data : null,
|
|
};
|
|
|
|
const getConnectClusterMetrics =
|
|
metricList[MetricType.Connect] && (isTop || connectClusters?.length)
|
|
? Utils.post(
|
|
api.getConnectClusterMetrics(clusterId),
|
|
Object.assign(
|
|
{
|
|
...reqBasicBody,
|
|
metricsNames: metricList[MetricType.Connect],
|
|
},
|
|
isTop
|
|
? {}
|
|
: {
|
|
connectClusterIdList: connectClusters,
|
|
}
|
|
)
|
|
)
|
|
: Promise.resolve([]);
|
|
const getConnectorMetrics =
|
|
metricList[MetricType.Connectors] && (isTop || connectors?.length)
|
|
? Utils.post(
|
|
api.getConnectorMetrics(clusterId),
|
|
Object.assign(
|
|
{
|
|
...reqBasicBody,
|
|
metricsNames: metricList[MetricType.Connectors],
|
|
},
|
|
isTop ? {} : { connectorNameList: connectors }
|
|
)
|
|
)
|
|
: Promise.resolve([]);
|
|
|
|
Promise.all([getConnectClusterMetrics, getConnectorMetrics]).then(
|
|
(res: any) => {
|
|
// 如果当前请求不是最新请求,则不做任何操作
|
|
if (curFetchingTimestamp.current !== curTimestamp) {
|
|
return;
|
|
}
|
|
|
|
// 为保证指标排序结果正确,当指标全部返回后才展示图表
|
|
if (res.length === 1 || (res.length === 2 && res[0] && res[1])) {
|
|
const connectClusterData = formatChartData(
|
|
res[0],
|
|
global.getMetricDefine || {},
|
|
MetricType.Connect,
|
|
curHeaderOptions.rangeTime
|
|
) as FormattedMetricData[];
|
|
// todo 将指标筛选选中但是没有返回的指标插入chartData中
|
|
const newConnectClusterData: any = [];
|
|
metricList[MetricType.Connect]?.forEach((item) => {
|
|
if (connectClusterData && connectClusterData.some((key) => item === key.metricName)) {
|
|
newConnectClusterData.push(null);
|
|
} else {
|
|
const chartData: any = {
|
|
metricName: item,
|
|
metricType: MetricType.Connect,
|
|
metricUnit: global.getMetricDefine(MetricType.Connect, item)?.unit || '',
|
|
metricLines: [],
|
|
showLegend: false,
|
|
targetUnit: undefined,
|
|
};
|
|
newConnectClusterData.push(chartData);
|
|
}
|
|
});
|
|
const connectorData = formatChartData(
|
|
res[1],
|
|
global.getMetricDefine || {},
|
|
MetricType.Connectors,
|
|
curHeaderOptions.rangeTime
|
|
) as FormattedMetricData[];
|
|
// todo 将指标筛选选中但是没有返回的指标插入chartData中
|
|
const newConnectorData: any = [];
|
|
|
|
metricList[MetricType.Connectors]?.forEach((item) => {
|
|
if (connectorData && connectorData.some((key) => item === key.metricName)) {
|
|
newConnectorData.push(null);
|
|
} else {
|
|
const chartData: any = {
|
|
metricName: item,
|
|
metricType: MetricType.Connectors,
|
|
metricUnit: global.getMetricDefine(MetricType.Connectors, item)?.unit || '',
|
|
metricLines: [],
|
|
showLegend: false,
|
|
targetUnit: undefined,
|
|
};
|
|
newConnectorData.push(chartData);
|
|
}
|
|
});
|
|
// 指标排序
|
|
const formattedMetricData = [...connectClusterData, ...connectorData];
|
|
const nullDataMetricData = [...newConnectClusterData, ...newConnectorData].filter((item) => item !== null);
|
|
formattedMetricData.sort((a, b) => metricRankList.current.indexOf(a.metricName) - metricRankList.current.indexOf(b.metricName));
|
|
nullDataMetricData.sort((a, b) => metricRankList.current.indexOf(a.metricName) - metricRankList.current.indexOf(b.metricName));
|
|
const filterMetricData = [...formattedMetricData, ...nullDataMetricData];
|
|
setMetricChartData(
|
|
screenType === 'Connect'
|
|
? filterMetricData.filter((item) => item.metricType === MetricType.Connect)
|
|
: screenType === 'Connector'
|
|
? filterMetricData.filter((item) => item.metricType === MetricType.Connectors)
|
|
: filterMetricData
|
|
);
|
|
} else {
|
|
setMetricChartData([]);
|
|
}
|
|
setLoading(false);
|
|
},
|
|
() => curFetchingTimestamp.current === curTimestamp && setLoading(false)
|
|
);
|
|
};
|
|
|
|
const getScopeList = () => {
|
|
const getConnectClusters = Utils.request(api.getConnectClusters(clusterId));
|
|
const getConnectors = Utils.request(api.getConnectors(clusterId));
|
|
Promise.all([getConnectClusters, getConnectors]).then(([connectClusters, connectors]: [ConnectCluster[], Connector[]]) => {
|
|
setScopeList({
|
|
connectClusters,
|
|
connectors,
|
|
});
|
|
});
|
|
};
|
|
|
|
// 筛选项变化或者点击刷新按钮
|
|
const ksHeaderChange = (ksOptions: KsHeaderOptions) => {
|
|
const { isAutoReload, gridNum: newGridNum, isRelativeRangeTime, rangeTime, scopeData } = ksOptions;
|
|
let newRangeTime = rangeTime;
|
|
// 重新渲染图表
|
|
if (newGridNum !== gridNum) {
|
|
setGridNum(newGridNum || 12);
|
|
busInstance.emit('chartResize');
|
|
} else {
|
|
// 如果为相对时间,则当前时间减去 1 分钟,避免最近一分钟的数据还没采集到时前端多补一个点
|
|
if (isRelativeRangeTime) {
|
|
newRangeTime = rangeTime.map((timestamp) => timestamp - 60 * 1000) as [number, number];
|
|
}
|
|
setCurHeaderOptions({
|
|
isRelativeRangeTime: isRelativeRangeTime,
|
|
isAutoReload: isAutoReload,
|
|
rangeTime: newRangeTime,
|
|
scopeData: scopeData,
|
|
});
|
|
}
|
|
};
|
|
|
|
// 图表拖拽
|
|
const dragCallback = (oldIndex: number, newIndex: number) => {
|
|
const originFrom = metricRankList.current.indexOf(metricChartData[oldIndex].metricName);
|
|
const originTarget = metricRankList.current.indexOf(metricChartData[newIndex].metricName);
|
|
metricFilterRef.current?.rankChange(originFrom, originTarget);
|
|
};
|
|
|
|
// 展开图表详情
|
|
const onExpand = (metricName: string, metricType: MetricType) => {
|
|
const linesName =
|
|
metricType === MetricType.Connect
|
|
? scopeList.connectClusters.map((cluster) => cluster.id)
|
|
: scopeList.connectors.map((connector) => ({
|
|
connectClusterId: connector.connectClusterId,
|
|
connectorName: connector.connectorName,
|
|
}));
|
|
chartDetailRef.current.onOpen(metricType, metricName, linesName);
|
|
};
|
|
|
|
// 获取图表指标
|
|
useEffect(() => {
|
|
if (Object.values(metricList).some((list) => list.length) && curHeaderOptions) {
|
|
getMetricChartData();
|
|
}
|
|
}, [curHeaderOptions, screenType]);
|
|
|
|
useEffect(() => {
|
|
if (Object.values(metricList).some((list) => list.length) && curHeaderOptions) {
|
|
setLoading(true);
|
|
getMetricChartData();
|
|
} else {
|
|
setMetricChartData([]);
|
|
setLoading(false);
|
|
}
|
|
}, [metricList]);
|
|
|
|
useEffect(() => {
|
|
getScopeList();
|
|
}, []);
|
|
|
|
return (
|
|
<div id="dashboard-drag-chart" className="topic-dashboard">
|
|
<ChartOperateBar
|
|
onChange={ksHeaderChange}
|
|
hideNodeScope={false}
|
|
openMetricFilter={() => metricFilterRef.current?.open()}
|
|
nodeSelect={{
|
|
name: 'Connect',
|
|
customContent: <SelectContent scopeList={scopeList} title="请选择 Connect 范围" />,
|
|
}}
|
|
setScreenType={setScreenType}
|
|
/>
|
|
<MetricsFilter
|
|
ref={metricFilterRef}
|
|
metricType={[MetricType.Connect, MetricType.Connectors]}
|
|
onSelectChange={(list) => {
|
|
const res: { [key: string]: (string | number)[] } = {};
|
|
list.forEach(({ type, name, set }) => {
|
|
set && (res[type] ? res[type].push(name) : (res[type] = [name]));
|
|
});
|
|
setMetricList(res);
|
|
}}
|
|
onRankChange={(rankList) => {
|
|
metricRankList.current = rankList;
|
|
}}
|
|
/>
|
|
<ChartList
|
|
busInstance={busInstance}
|
|
loading={loading}
|
|
gridNum={gridNum}
|
|
data={metricChartData}
|
|
autoReload={curHeaderOptions?.isAutoReload}
|
|
dragCallback={dragCallback}
|
|
onExpand={onExpand}
|
|
/>
|
|
{/* 图表详情 */}
|
|
<ChartDetail ref={chartDetailRef} />
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const ConnectDashboard = (): JSX.Element => {
|
|
const [global] = AppContainer.useGlobalValue();
|
|
return (
|
|
<>
|
|
<div className="breadcrumb" style={{ marginBottom: '10px' }}>
|
|
<DBreadcrumb
|
|
breadcrumbs={[
|
|
{ label: '多集群管理', aHref: '/' },
|
|
{ label: global?.clusterInfo?.name, aHref: `/cluster/${global?.clusterInfo?.id}` },
|
|
{ label: 'Connect', aHref: `` },
|
|
]}
|
|
/>
|
|
</div>
|
|
<HasConnector>
|
|
<>
|
|
<ConnectCard />
|
|
<DraggableCharts />
|
|
</>
|
|
</HasConnector>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default ConnectDashboard;
|