Files
KnowStreaming/km-console/packages/layout-clusters-fe/src/pages/ConnectDashboard/index.tsx

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;