mirror of
https://github.com/didi/KnowStreaming.git
synced 2026-01-12 19:12:48 +08:00
feat: 健康状态展示优化
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
/* eslint-disable react/display-name */
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useHistory, useLocation, useParams } from 'react-router-dom';
|
||||
import { useLocation, useParams } from 'react-router-dom';
|
||||
import CardBar from '@src/components/CardBar';
|
||||
import { healthDataProps } from '.';
|
||||
import { Tag, Utils } from 'knowdesign';
|
||||
import { Utils } from 'knowdesign';
|
||||
import Api from '@src/api';
|
||||
import { hashDataParse } from '@src/constants/common';
|
||||
import { HealthStateEnum } from '../HealthState';
|
||||
|
||||
export default (props: { record: any }) => {
|
||||
const { record } = props;
|
||||
@@ -14,22 +15,20 @@ export default (props: { record: any }) => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [cardData, setCardData] = useState([]);
|
||||
const [healthData, setHealthData] = useState<healthDataProps>({
|
||||
score: 0,
|
||||
state: HealthStateEnum.UNKNOWN,
|
||||
passed: 0,
|
||||
total: 0,
|
||||
alive: 0,
|
||||
});
|
||||
const healthItems = ['HealthScore_Topics', 'HealthCheckPassed_Topics', 'HealthCheckTotal_Topics', 'live'];
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
Utils.post(Api.getBrokerDetailMetricPoints(hashDataParse(urlLocation.hash)?.brokerId, urlParams?.clusterId), [
|
||||
'Partitions',
|
||||
'Leaders',
|
||||
'PartitionURP',
|
||||
'HealthScore',
|
||||
'HealthCheckPassed',
|
||||
'HealthCheckTotal',
|
||||
'Alive',
|
||||
'HealthState',
|
||||
]).then((data: any) => {
|
||||
setLoading(false);
|
||||
const rightData = JSON.parse(JSON.stringify(data.metrics));
|
||||
@@ -47,14 +46,12 @@ export default (props: { record: any }) => {
|
||||
value: rightData['PartitionURP'] || '-',
|
||||
},
|
||||
];
|
||||
const healthResData: any = {};
|
||||
healthResData.score = data?.metrics?.['HealthScore'] || 0;
|
||||
healthResData.passed = data?.metrics?.['HealthCheckPassed'] || 0;
|
||||
healthResData.total = data?.metrics?.['HealthCheckTotal'] || 0;
|
||||
healthResData.alive = data?.metrics?.['Alive'] || 0;
|
||||
setCardData(cordRightMap);
|
||||
setHealthData(healthResData);
|
||||
// setCardData(data.metrics)
|
||||
setHealthData({
|
||||
state: data?.metrics?.['HealthState'],
|
||||
passed: data?.metrics?.['HealthCheckPassed'] || 0,
|
||||
total: data?.metrics?.['HealthCheckTotal'] || 0,
|
||||
});
|
||||
});
|
||||
}, []);
|
||||
return (
|
||||
|
||||
@@ -6,6 +6,7 @@ import { healthDataProps } from '.';
|
||||
import { Tag, Tooltip, Utils } from 'knowdesign';
|
||||
import api from '@src/api';
|
||||
import { QuestionCircleOutlined } from '@ant-design/icons';
|
||||
import { HealthStateEnum } from '../HealthState';
|
||||
|
||||
export default () => {
|
||||
const routeParams = useParams<{
|
||||
@@ -14,26 +15,21 @@ export default () => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [cardData, setCardData] = useState([]);
|
||||
const [healthData, setHealthData] = useState<healthDataProps>({
|
||||
score: 0,
|
||||
state: HealthStateEnum.UNKNOWN,
|
||||
passed: 0,
|
||||
total: 0,
|
||||
alive: 0,
|
||||
});
|
||||
const cardItems = ['Partitions', 'PartitionsSkew', 'Leaders', 'LeadersSkew', 'LogSize'];
|
||||
const healthItems = ['HealthScore_Brokers', 'HealthCheckPassed_Brokers', 'HealthCheckTotal_Brokers', 'Alive'];
|
||||
const healthItems = ['HealthCheckPassed_Brokers', 'HealthCheckTotal_Brokers', 'HealthState'];
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
// 获取左侧健康度
|
||||
const brokerMetric = Utils.post(api.getBrokerMetricPoints(Number(routeParams.clusterId)), healthItems).then((data: any) => {
|
||||
const healthResData: any = {};
|
||||
// healthResData.score = data?.find((item:any) => item.metricName === 'HealthScore_Brokers')?.value || 0;
|
||||
// healthResData.passed = data?.find((item:any) => item.metricName === 'HealthCheckPassed_Brokers')?.value || 0;
|
||||
// healthResData.total = data?.find((item:any) => item.metricName === 'HealthCheckTotal_Brokers')?.value || 0;
|
||||
healthResData.score = data?.metrics?.['HealthScore_Brokers'] || 0;
|
||||
healthResData.passed = data?.metrics?.['HealthCheckPassed_Brokers'] || 0;
|
||||
healthResData.total = data?.metrics?.['HealthCheckTotal_Brokers'] || 0;
|
||||
healthResData.alive = data?.metrics?.['Alive'] || 0;
|
||||
setHealthData(healthResData);
|
||||
setHealthData({
|
||||
state: data?.metrics?.['HealthState'],
|
||||
passed: data?.metrics?.['HealthCheckPassed_Brokers'] || 0,
|
||||
total: data?.metrics?.['HealthCheckTotal_Brokers'] || 0,
|
||||
});
|
||||
});
|
||||
// 获取右侧状态
|
||||
const brokersState = Utils.request(api.getBrokersState(routeParams?.clusterId)).then((data) => {
|
||||
@@ -115,6 +111,6 @@ export default () => {
|
||||
setLoading(false);
|
||||
});
|
||||
}, [routeParams.clusterId]);
|
||||
// console.log('cardData', cardData, healthData);
|
||||
|
||||
return <CardBar scene="broker" healthData={healthData} cardColumns={cardData} loading={loading}></CardBar>;
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@ import CardBar from '@src/components/CardBar';
|
||||
import { healthDataProps } from '.';
|
||||
import { Utils } from 'knowdesign';
|
||||
import api from '@src/api';
|
||||
import { HealthStateEnum } from '../HealthState';
|
||||
|
||||
export default () => {
|
||||
const routeParams = useParams<{
|
||||
@@ -12,22 +13,17 @@ export default () => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [cardData, setCardData] = useState([]);
|
||||
const [healthData, setHealthData] = useState<healthDataProps>({
|
||||
score: 0,
|
||||
state: HealthStateEnum.UNKNOWN,
|
||||
passed: 0,
|
||||
total: 0,
|
||||
alive: 0,
|
||||
});
|
||||
const [healthDetail, setHealthDetail] = useState([]);
|
||||
const cardItems = ['Groups', 'GroupActives', 'GroupEmptys', 'GroupRebalances', 'GroupDeads'];
|
||||
const healthItems = ['HealthScore_Groups', 'HealthCheckPassed_Groups', 'HealthCheckTotal_Groups', 'Alive'];
|
||||
const healthItems = ['HealthCheckPassed_Groups', 'HealthCheckTotal_Groups', 'HealthState'];
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
Utils.post(api.getMetricPointsLatest(Number(routeParams.clusterId)), cardItems.concat(healthItems)).then((data: any) => {
|
||||
setLoading(false);
|
||||
// setCardData(data
|
||||
// .filter((item: any) => cardItems.indexOf(item.metricName) >= 0)
|
||||
// .map((item: any) => ({ title: item.metricName, value: item.value }))
|
||||
// )
|
||||
setCardData(
|
||||
cardItems.map((item) => {
|
||||
if (item === 'GroupDeads') {
|
||||
@@ -36,12 +32,11 @@ export default () => {
|
||||
return { title: item, value: data.metrics[item] };
|
||||
})
|
||||
);
|
||||
const healthResData: any = {};
|
||||
healthResData.score = data.metrics['HealthScore_Groups'] || 0;
|
||||
healthResData.passed = data.metrics['HealthCheckPassed_Groups'] || 0;
|
||||
healthResData.total = data.metrics['HealthCheckTotal_Groups'] || 0;
|
||||
healthResData.alive = data.metrics['Alive'] || 0;
|
||||
setHealthData(healthResData);
|
||||
setHealthData({
|
||||
state: data?.metrics?.['HealthState'],
|
||||
passed: data?.metrics?.['HealthCheckPassed_Groups'] || 0,
|
||||
total: data?.metrics?.['HealthCheckTotal_Groups'] || 0,
|
||||
});
|
||||
});
|
||||
}, []);
|
||||
return <CardBar scene="group" healthData={healthData} cardColumns={cardData} loading={loading}></CardBar>;
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import CardBar from '@src/components/CardBar';
|
||||
import { healthDataProps } from '.';
|
||||
import { Tag, Utils } from 'knowdesign';
|
||||
import { Utils } from 'knowdesign';
|
||||
import Api from '@src/api';
|
||||
|
||||
export default () => {
|
||||
@@ -12,14 +11,7 @@ export default () => {
|
||||
}>();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [cardData, setCardData] = useState([]);
|
||||
const [healthData, setHealthData] = useState<healthDataProps>({
|
||||
score: 0,
|
||||
passed: 0,
|
||||
total: 0,
|
||||
alive: 0,
|
||||
});
|
||||
const cardItems = ['Partitions', 'PartitionsSkew', 'Leaders', 'LeadersSkew', 'LogSize'];
|
||||
const healthItems = ['HealthScore_Brokers', 'HealthCheckPassed_Brokers', 'HealthCheckTotal_Brokers', 'alive'];
|
||||
|
||||
const getCordRightMap = (data: any) => {
|
||||
const cordRightMap = [
|
||||
{
|
||||
@@ -49,6 +41,7 @@ export default () => {
|
||||
];
|
||||
return cordRightMap;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
// 获取状态
|
||||
|
||||
@@ -32,7 +32,6 @@ const LoadRebalanceCardBar = (props: any) => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [cardData, setCardData] = useState([]);
|
||||
const [normsVisible, setNormsVisible] = useState(null);
|
||||
const cardItems = ['AclEnable', 'Acls', 'AclUsers', 'AclTopics', 'AclGroups'];
|
||||
const onClose = () => {
|
||||
setNormsVisible(false);
|
||||
};
|
||||
@@ -45,11 +44,9 @@ const LoadRebalanceCardBar = (props: any) => {
|
||||
// 获取右侧状态
|
||||
getCartInfo()
|
||||
.then((res: any) => {
|
||||
// const { AclEnable, Acls, AclUsers, AclTopics, AclGroups } = res.metrics;
|
||||
const { next, sub, status } = res;
|
||||
const { cpu, disk, bytesIn, bytesOut } = sub;
|
||||
const newNextDate: any = transUnitTimePro(moment(next).valueOf() - moment().valueOf());
|
||||
// const newNextDate = parseInt(`${transUnitTimePro(moment(next).valueOf() - moment().valueOf())}`);
|
||||
const cardMap = [
|
||||
{
|
||||
title() {
|
||||
@@ -80,20 +77,15 @@ const LoadRebalanceCardBar = (props: any) => {
|
||||
>
|
||||
{!status ? '已均衡' : '未均衡'}
|
||||
</Tag>
|
||||
{/* <Tag style={{ padding: '2px 4px', backgroundColor: 'rgba(85,110,230,0.10)', color: '#556EE6' }}>已均衡</Tag> */}
|
||||
</div>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<span>
|
||||
周期均衡 <IconFont className="cutomIcon" type={`${!status ? 'icon-zhengchang' : 'icon-warning'}`} />
|
||||
</span>
|
||||
{/* <span>
|
||||
周期均衡 <IconFont className="cutomIcon" type="icon-zhengchang" />
|
||||
</span> */}
|
||||
<span>
|
||||
距下次均衡还剩{newNextDate?.value || 0}
|
||||
{newNextDate?.unit || '分钟'}
|
||||
</span>
|
||||
{/* {<span>距下次均衡还剩{1}小时</span>} */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -106,73 +98,6 @@ const LoadRebalanceCardBar = (props: any) => {
|
||||
padding: '12px 12px 8px 12px',
|
||||
},
|
||||
},
|
||||
// {
|
||||
// // title: 'CPU avg',
|
||||
// title() {
|
||||
// return (
|
||||
// <div>
|
||||
// <span style={{ display: 'inline-block', marginRight: '8px' }}>CPU AVG</span>
|
||||
// {!cpu?.interval && cpu?.interval !== 0 && (
|
||||
// <Tooltip overlayClassName="rebalance-tooltip" title="未设置均衡策略">
|
||||
// <QuestionCircleOutlined />
|
||||
// </Tooltip>
|
||||
// )}
|
||||
// {/* <IconFont className="cutomIcon" onClick={() => setNormsVisible(true)} type="icon-shezhi"></IconFont> */}
|
||||
// </div>
|
||||
// );
|
||||
// },
|
||||
// value(visibleType: boolean) {
|
||||
// return (
|
||||
// <div id="CPU" style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end' }}>
|
||||
// <div style={{ display: 'inline-block' }}>
|
||||
// <div style={{ margin: '5px 0', fontFamily: 'DIDIFD-Medium' }}>
|
||||
// <span style={{ fontSize: '24px' }}>{cpu?.avg || 0}</span>
|
||||
// <span style={{ fontSize: '14px', display: 'inline-block', marginLeft: '4px' }}>%</span>
|
||||
// </div>
|
||||
// <div style={{ marginTop: '-4px', display: 'flex', justifyContent: 'space-between' }}>
|
||||
// <span>均衡区间: ±{cpu?.interval || 0}%</span>
|
||||
// </div>
|
||||
// </div>
|
||||
// <Popover
|
||||
// // visible={visibleType} // 修改为hover柱状图
|
||||
// overlayClassName="custom-popover"
|
||||
// content={
|
||||
// <div style={{ color: '#495057' }}>
|
||||
// <div>
|
||||
// <IconFont className="cutomIcon cutomIcon-red" type="icon-chaoguo" />
|
||||
// 超过均衡区间的有: {cpu?.bigNu || 0}
|
||||
// </div>
|
||||
// <div style={{ margin: '6px 0' }}>
|
||||
// <IconFont className="cutomIcon cutomIcon-green" type="icon-qujian" />
|
||||
// 在均衡区间内的有: {cpu?.betweenNu || 0}
|
||||
// </div>
|
||||
// <div>
|
||||
// <IconFont className="cutomIcon cutomIcon-red" type="icon-diyu" />
|
||||
// 低于均衡区间的有: {cpu?.smallNu || 0}
|
||||
// </div>
|
||||
// </div>
|
||||
// }
|
||||
// getPopupContainer={(triggerNode: any) => {
|
||||
// return triggerNode;
|
||||
// }}
|
||||
// color="#ffffff"
|
||||
// >
|
||||
// <div style={{ width: '44px', height: '30px' }}>
|
||||
// <StateChart
|
||||
// data={[
|
||||
// { name: 'bigNu', value: cpu?.bigNu || 0 },
|
||||
// { name: 'betweenNu', value: cpu?.betweenNu || 0 },
|
||||
// { name: 'smallNu', value: cpu?.smallNu || 0 },
|
||||
// ]}
|
||||
// />
|
||||
// </div>
|
||||
// </Popover>
|
||||
// </div>
|
||||
// );
|
||||
// },
|
||||
// className: 'custom-card-bar',
|
||||
// valueClassName: 'custom-card-bar-value',
|
||||
// },
|
||||
{
|
||||
title() {
|
||||
return (
|
||||
|
||||
@@ -5,8 +5,10 @@ import { healthDataProps } from '.';
|
||||
import { Utils } from 'knowdesign';
|
||||
import { IconFont } from '@knowdesign/icons';
|
||||
import api from '@src/api';
|
||||
import { healthScoreCondition } from './const';
|
||||
import { hashDataParse } from '@src/constants/common';
|
||||
import { HealthStateEnum } from '../HealthState';
|
||||
|
||||
const healthItems = ['HealthCheckPassed', 'HealthCheckTotal', 'HealthState'];
|
||||
|
||||
const renderValue = (v: string | number | ((visibleType?: boolean) => JSX.Element), visibleType?: boolean) => {
|
||||
return typeof v === 'function' ? v(visibleType) : v;
|
||||
@@ -19,14 +21,12 @@ export default (props: { record: any }) => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [cardData, setCardData] = useState([]);
|
||||
const [healthData, setHealthData] = useState<healthDataProps>({
|
||||
score: 0,
|
||||
state: HealthStateEnum.UNKNOWN,
|
||||
passed: 0,
|
||||
total: 0,
|
||||
alive: 0,
|
||||
});
|
||||
const [healthDetail, setHealthDetail] = useState([]);
|
||||
const [clusterAlive, setClusterAlive] = useState(0);
|
||||
const healthItems = ['HealthScore', 'HealthCheckPassed', 'HealthCheckTotal', 'alive'];
|
||||
|
||||
const getNumAndSubTitles = (cardColumnsItemData: any) => {
|
||||
return (
|
||||
<div style={{ width: '100%', display: 'flex', alignItems: 'end' }}>
|
||||
@@ -40,21 +40,21 @@ export default (props: { record: any }) => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
const topicName = hashDataParse(location.hash)['topicName'];
|
||||
let detailHealthPromise = Utils.post(api.getTopicMetricPointsLatest(Number(routeParams.clusterId), topicName), healthItems).then(
|
||||
const detailHealthPromise = Utils.post(api.getTopicMetricPointsLatest(Number(routeParams.clusterId), topicName), healthItems).then(
|
||||
(data: any) => {
|
||||
let healthResData: any = {};
|
||||
healthResData.score = data.metrics['HealthScore'] || 0;
|
||||
healthResData.passed = data.metrics['HealthCheckPassed'] || 0;
|
||||
healthResData.total = data.metrics['HealthCheckTotal'] || 0;
|
||||
// healthResData.alive = data.metrics['alive'] || 0
|
||||
setHealthData(healthResData);
|
||||
setHealthData({
|
||||
state: data.metrics['HealthState'],
|
||||
passed: data.metrics['HealthCheckPassed'] || 0,
|
||||
total: data.metrics['HealthCheckTotal'] || 0,
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
let detailStatePromise = Utils.request(api.getTopicState(Number(routeParams.clusterId), topicName)).then((topicHealthState: any) => {
|
||||
const detailStatePromise = Utils.request(api.getTopicState(Number(routeParams.clusterId), topicName)).then((topicHealthState: any) => {
|
||||
setCardData([
|
||||
{
|
||||
title: 'Partitions',
|
||||
@@ -87,13 +87,12 @@ export default (props: { record: any }) => {
|
||||
]);
|
||||
});
|
||||
// 获取集群维度的指标信息
|
||||
let clusterStatePromise = Utils.post(api.getMetricPointsLatest(Number(routeParams.clusterId)), ['Alive']).then(
|
||||
const clusterStatePromise = Utils.post(api.getMetricPointsLatest(Number(routeParams.clusterId)), ['Alive']).then(
|
||||
(clusterHealthState: any) => {
|
||||
let clusterAlive = clusterHealthState?.metrics?.Alive || 0;
|
||||
setClusterAlive(clusterAlive);
|
||||
setClusterAlive(clusterHealthState?.metrics?.Alive || 0);
|
||||
}
|
||||
);
|
||||
Promise.all([detailHealthPromise, detailStatePromise, clusterStatePromise]).then((res) => {
|
||||
Promise.all([detailHealthPromise, detailStatePromise, clusterStatePromise]).then(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
}, []);
|
||||
@@ -101,7 +100,7 @@ export default (props: { record: any }) => {
|
||||
<CardBar
|
||||
record={record}
|
||||
scene="topic"
|
||||
healthData={{ ...healthData, alive: clusterAlive }}
|
||||
healthData={{ ...healthData, state: clusterAlive ? healthData.state : HealthStateEnum.DOWN }}
|
||||
cardColumns={cardData}
|
||||
showCardBg={false}
|
||||
loading={loading}
|
||||
|
||||
@@ -4,6 +4,7 @@ import CardBar from '@src/components/CardBar';
|
||||
import { healthDataProps } from '.';
|
||||
import { Utils } from 'knowdesign';
|
||||
import api from '@src/api';
|
||||
import { HealthStateEnum } from '../HealthState';
|
||||
|
||||
export default () => {
|
||||
const routeParams = useParams<{
|
||||
@@ -12,14 +13,12 @@ export default () => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [cardData, setCardData] = useState([]);
|
||||
const [healthData, setHealthData] = useState<healthDataProps>({
|
||||
score: 0,
|
||||
state: HealthStateEnum.UNKNOWN,
|
||||
passed: 0,
|
||||
total: 0,
|
||||
alive: 0,
|
||||
});
|
||||
const [healthDetail, setHealthDetail] = useState([]);
|
||||
const cardItems = ['Topics', 'Partitions', 'PartitionNoLeader', 'PartitionMinISR_S', 'PartitionMinISR_E', 'PartitionURP'];
|
||||
const healthItems = ['HealthScore_Topics', 'HealthCheckPassed_Topics', 'HealthCheckTotal_Topics', 'Alive'];
|
||||
const healthItems = ['HealthCheckPassed_Topics', 'HealthCheckTotal_Topics', 'HealthState'];
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
Utils.post(api.getMetricPointsLatest(Number(routeParams.clusterId)), cardItems.concat(healthItems)).then((data: any) => {
|
||||
@@ -42,12 +41,6 @@ export default () => {
|
||||
PartitionURP: 'URP',
|
||||
PartitionNoLeader: 'No Leader',
|
||||
};
|
||||
// setCardData(data
|
||||
// .filter(item => cardItems.indexOf(item.name) >= 0)
|
||||
// .map(item => {
|
||||
// return { title: metricElmMap[item.name] || item.name, value: item.value }
|
||||
// })
|
||||
// )
|
||||
setCardData(
|
||||
cardItems.map((item) => {
|
||||
let title = item;
|
||||
@@ -66,12 +59,11 @@ export default () => {
|
||||
return { title, value: data.metrics[item] };
|
||||
})
|
||||
);
|
||||
const healthResData: any = {};
|
||||
healthResData.score = data.metrics['HealthScore_Topics'] || 0;
|
||||
healthResData.passed = data.metrics['HealthCheckPassed_Topics'] || 0;
|
||||
healthResData.total = data.metrics['HealthCheckTotal_Topics'] || 0;
|
||||
healthResData.alive = data.metrics['Alive'] || 0;
|
||||
setHealthData(healthResData);
|
||||
setHealthData({
|
||||
state: data.metrics['HealthState'],
|
||||
passed: data.metrics['HealthCheckPassed_Topics'] || 0,
|
||||
total: data.metrics['HealthCheckTotal_Topics'] || 0,
|
||||
});
|
||||
});
|
||||
}, []);
|
||||
return <CardBar scene="topic" healthData={healthData} cardColumns={cardData} loading={loading}></CardBar>;
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import CardBar, { healthDataProps } from './index';
|
||||
import { Utils } from 'knowdesign';
|
||||
import api from '@src/api';
|
||||
import { HealthStateEnum } from '../HealthState';
|
||||
|
||||
interface ZookeeperState {
|
||||
aliveFollowerCount: number;
|
||||
aliveObserverCount: number;
|
||||
aliveServerCount: number;
|
||||
healthCheckPassed: number;
|
||||
healthCheckTotal: number;
|
||||
healthState: number;
|
||||
leaderNode: string;
|
||||
totalFollowerCount: number;
|
||||
totalObserverCount: number;
|
||||
totalServerCount: number;
|
||||
watchCount: number;
|
||||
}
|
||||
|
||||
const getVal = (val: string | number | undefined | null) => {
|
||||
return val === undefined || val === null || val === '' ? '-' : val;
|
||||
};
|
||||
|
||||
const ZookeeperCard = () => {
|
||||
const { clusterId } = useParams<{
|
||||
clusterId: string;
|
||||
}>();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [cardData, setCardData] = useState([]);
|
||||
const [healthData, setHealthData] = useState<healthDataProps>({
|
||||
state: HealthStateEnum.UNKNOWN,
|
||||
passed: 0,
|
||||
total: 0,
|
||||
});
|
||||
|
||||
const getHealthData = () => {
|
||||
return Utils.post(api.getZookeeperMetricsInfo(Number(clusterId)), ['HealthCheckPassed', 'HealthCheckTotal', 'HealthState']).then(
|
||||
(data: any) => {
|
||||
setHealthData({
|
||||
state: data?.metrics?.['HealthState'],
|
||||
passed: data?.metrics?.['HealthCheckPassed'] || 0,
|
||||
total: data?.metrics?.['HealthCheckTotal'] || 0,
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const getCardInfo = () => {
|
||||
return Utils.request(api.getZookeeperState(clusterId)).then((res: ZookeeperState) => {
|
||||
const {
|
||||
aliveFollowerCount,
|
||||
aliveObserverCount,
|
||||
aliveServerCount,
|
||||
totalFollowerCount,
|
||||
totalObserverCount,
|
||||
totalServerCount,
|
||||
watchCount,
|
||||
leaderNode,
|
||||
} = res || {};
|
||||
const cardMap = [
|
||||
{
|
||||
title: 'Node Count',
|
||||
value() {
|
||||
return (
|
||||
<span>
|
||||
{aliveServerCount || '-'}/{totalServerCount || '-'}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
customStyle: {
|
||||
// 自定义cardbar样式
|
||||
marginLeft: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Watch Count',
|
||||
value: getVal(watchCount),
|
||||
},
|
||||
{
|
||||
title: 'Leader',
|
||||
value() {
|
||||
return <span style={{ fontSize: 24 }}>{leaderNode || '-'}</span>;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Follower',
|
||||
value() {
|
||||
return (
|
||||
<span>
|
||||
{getVal(aliveFollowerCount)}/{getVal(totalFollowerCount)}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Observer',
|
||||
value() {
|
||||
return (
|
||||
<span>
|
||||
{getVal(aliveObserverCount)}/{getVal(totalObserverCount)}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
setCardData(cardMap);
|
||||
});
|
||||
};
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
Promise.all([getHealthData(), getCardInfo()]).finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
}, [clusterId]);
|
||||
return <CardBar scene="zookeeper" healthData={healthData} cardColumns={cardData} loading={loading}></CardBar>;
|
||||
};
|
||||
|
||||
export default ZookeeperCard;
|
||||
@@ -10,48 +10,15 @@
|
||||
height: 88px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
// justify-content: space-between;
|
||||
align-items: center;
|
||||
.card-bar-health {
|
||||
width: 240px;
|
||||
height: 70px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
// justify-content: space-between;
|
||||
.card-bar-health-process {
|
||||
height: 100%;
|
||||
margin-right: 24px;
|
||||
.dcloud-progress-inner {
|
||||
border-radius: 50%;
|
||||
}
|
||||
.dcloud-progress-status-normal {
|
||||
.dcloud-progress-inner {
|
||||
background: rgba(85, 110, 230, 0.03);
|
||||
}
|
||||
.dcloud-progress-inner:not(.dcloud-progress-circle-gradient) .dcloud-progress-circle-path {
|
||||
stroke: rgb(85, 110, 230);
|
||||
}
|
||||
}
|
||||
.dcloud-progress-status-success {
|
||||
.dcloud-progress-inner {
|
||||
background: rgba(0, 192, 162, 0.03);
|
||||
}
|
||||
.dcloud-progress-inner:not(.dcloud-progress-circle-gradient) .dcloud-progress-circle-path {
|
||||
stroke: rgb(0, 192, 162);
|
||||
}
|
||||
}
|
||||
.dcloud-progress-status-exception {
|
||||
.dcloud-progress-inner {
|
||||
background: rgba(255, 112, 102, 0.03);
|
||||
}
|
||||
.dcloud-progress-inner:not(.dcloud-progress-circle-gradient) .dcloud-progress-circle-path {
|
||||
stroke: rgb(255, 112, 102);
|
||||
}
|
||||
}
|
||||
.dcloud-progress-inner {
|
||||
font-family: DIDIFD-Regular;
|
||||
font-size: 40px !important;
|
||||
}
|
||||
padding-top: 30px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
.state {
|
||||
font-size: 13px;
|
||||
@@ -61,20 +28,6 @@
|
||||
line-height: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.health-status-image {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
background-size: cover;
|
||||
}
|
||||
.health-status-image-success {
|
||||
background-image: url('../../assets/health-status-success.png');
|
||||
}
|
||||
.health-status-image-exception {
|
||||
background-image: url('../../assets/health-status-exception.png');
|
||||
}
|
||||
.health-status-image-normal {
|
||||
background-image: url('../../assets/health-status-normal.png');
|
||||
}
|
||||
}
|
||||
.value-bar {
|
||||
display: flex;
|
||||
|
||||
@@ -1,25 +1,24 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { Drawer, Select, Spin, Table } from 'knowdesign';
|
||||
import { Drawer, Spin, Table, Utils } from 'knowdesign';
|
||||
import { IconFont } from '@knowdesign/icons';
|
||||
import { Utils, Progress } from 'knowdesign';
|
||||
import './index.less';
|
||||
import api from '@src/api';
|
||||
import moment from 'moment';
|
||||
import TagsWithHide from '../TagsWithHide/index';
|
||||
import { getHealthProcessColor } from '@src/pages/SingleClusterDetail/config';
|
||||
import HealthState, { getHealthStateDesc, getHealthStateEmoji, HealthStateEnum } from '../HealthState';
|
||||
import { getConfigItemDetailDesc } from '@src/pages/SingleClusterDetail/config';
|
||||
|
||||
export interface healthDataProps {
|
||||
score: number;
|
||||
state: HealthStateEnum;
|
||||
passed: number;
|
||||
total: number;
|
||||
alive: number;
|
||||
}
|
||||
export interface CardBarProps {
|
||||
cardColumns?: any[];
|
||||
healthData?: healthDataProps;
|
||||
showCardBg?: boolean;
|
||||
scene: 'topic' | 'broker' | 'group';
|
||||
scene: 'topic' | 'broker' | 'group' | 'zookeeper';
|
||||
record?: any;
|
||||
loading?: boolean;
|
||||
needProgress?: boolean;
|
||||
@@ -27,36 +26,27 @@ export interface CardBarProps {
|
||||
const renderValue = (v: string | number | ((visibleType?: boolean) => JSX.Element), visibleType?: boolean) => {
|
||||
return typeof v === 'function' ? v(visibleType) : v;
|
||||
};
|
||||
const statusTxtEmojiMap = {
|
||||
success: {
|
||||
emoji: '👍',
|
||||
txt: '优异',
|
||||
},
|
||||
normal: {
|
||||
emoji: '😊',
|
||||
txt: '正常',
|
||||
},
|
||||
exception: {
|
||||
emoji: '👻',
|
||||
txt: '异常',
|
||||
},
|
||||
};
|
||||
const sceneCodeMap = {
|
||||
topic: {
|
||||
code: 2,
|
||||
fieldName: 'topicName',
|
||||
alias: 'Topics',
|
||||
},
|
||||
broker: {
|
||||
code: 1,
|
||||
fieldName: 'brokerId',
|
||||
alias: 'Brokers',
|
||||
},
|
||||
topic: {
|
||||
code: 2,
|
||||
fieldName: 'topicName',
|
||||
alias: 'Topics',
|
||||
},
|
||||
group: {
|
||||
code: 3,
|
||||
fieldName: 'groupName',
|
||||
alias: 'Consumers',
|
||||
},
|
||||
zookeeper: {
|
||||
code: 4,
|
||||
fieldName: 'zookeeperId',
|
||||
alias: 'Zookeeper',
|
||||
},
|
||||
};
|
||||
const CardColumnsItem: any = (cardItem: any) => {
|
||||
const { cardColumnsItemData, showCardBg } = cardItem;
|
||||
@@ -92,16 +82,7 @@ const CardBar = (props: CardBarProps) => {
|
||||
}>();
|
||||
const { healthData, cardColumns, showCardBg = true, scene, record, loading, needProgress = true } = props;
|
||||
const [detailDrawerVisible, setDetailDrawerVisible] = useState(false);
|
||||
const [progressStatus, setProgressStatus] = useState<'success' | 'exception' | 'normal'>('success');
|
||||
const [healthCheckDetailList, setHealthCheckDetailList] = useState([]);
|
||||
const [isAlive, setIsAlive] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
if (healthData) {
|
||||
setProgressStatus(!isAlive ? 'exception' : healthData.score >= 90 ? 'success' : 'normal');
|
||||
setIsAlive(healthData.alive === 1);
|
||||
}
|
||||
}, [healthData, isAlive]);
|
||||
|
||||
useEffect(() => {
|
||||
const sceneObj = sceneCodeMap[scene];
|
||||
@@ -120,23 +101,24 @@ const CardBar = (props: CardBarProps) => {
|
||||
const columns = [
|
||||
{
|
||||
title: '检查项',
|
||||
dataIndex: 'configDesc',
|
||||
key: 'configDesc',
|
||||
},
|
||||
{
|
||||
title: '权重',
|
||||
dataIndex: 'weightPercent',
|
||||
key: 'weightPercent',
|
||||
},
|
||||
{
|
||||
title: '得分',
|
||||
dataIndex: 'score',
|
||||
key: 'score',
|
||||
dataIndex: 'checkConfig',
|
||||
render(config: any, record: any) {
|
||||
let valueGroup = {};
|
||||
try {
|
||||
valueGroup = JSON.parse(config.value);
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
return getConfigItemDetailDesc(record.configItem, valueGroup) || record.configDesc || '-';
|
||||
},
|
||||
},
|
||||
// {
|
||||
// title: '得分',
|
||||
// dataIndex: 'score',
|
||||
// },
|
||||
{
|
||||
title: '检查时间',
|
||||
dataIndex: 'updateTime',
|
||||
key: 'updateTime',
|
||||
render: (value: number) => {
|
||||
return moment(value).format('YYYY-MM-DD HH:mm:ss');
|
||||
},
|
||||
@@ -144,8 +126,6 @@ const CardBar = (props: CardBarProps) => {
|
||||
{
|
||||
title: '检查结果',
|
||||
dataIndex: 'passed',
|
||||
key: 'passed',
|
||||
width: 280,
|
||||
render(value: boolean, record: any) {
|
||||
const icon = value ? <IconFont type="icon-zhengchang"></IconFont> : <IconFont type="icon-yichang"></IconFont>;
|
||||
const txt = value ? '已通过' : '未通过';
|
||||
@@ -168,40 +148,13 @@ const CardBar = (props: CardBarProps) => {
|
||||
{!loading && healthData && needProgress && (
|
||||
<div className="card-bar-health">
|
||||
<div className="card-bar-health-process">
|
||||
<Progress
|
||||
width={70}
|
||||
type="circle"
|
||||
percent={!isAlive ? 100 : healthData.score}
|
||||
status={progressStatus}
|
||||
format={(percent, successPercent) => {
|
||||
return !isAlive ? (
|
||||
<div
|
||||
style={{
|
||||
fontFamily: 'HelveticaNeue-Medium',
|
||||
fontSize: 22,
|
||||
color: getHealthProcessColor(healthData.score, healthData.alive),
|
||||
}}
|
||||
>
|
||||
Down
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
textIndent: Math.round(percent) >= 100 ? '-4px' : '',
|
||||
color: getHealthProcessColor(healthData.score, healthData.alive),
|
||||
}}
|
||||
>
|
||||
{Math.round(percent)}
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
strokeWidth={3}
|
||||
/>
|
||||
<HealthState state={healthData?.state} width={74} height={74} />
|
||||
</div>
|
||||
<div>
|
||||
<div className="state">
|
||||
<div className={`health-status-image health-status-image-${progressStatus}`}></div>
|
||||
{sceneCodeMap[scene].alias}状态{statusTxtEmojiMap[progressStatus].txt}
|
||||
{getHealthStateEmoji(healthData?.state)}
|
||||
{sceneCodeMap[scene].alias}
|
||||
{getHealthStateDesc(healthData?.state)}
|
||||
</div>
|
||||
<div className="value-bar">
|
||||
<div className="value">{`${healthData?.passed}/${healthData?.total}`}</div>
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
.health-state {
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
import React from 'react';
|
||||
import GoodState from '@src/assets/health-good.png';
|
||||
import MediumState from '@src/assets/health-medium.png';
|
||||
import PoorState from '@src/assets/health-poor.png';
|
||||
import DownState from '@src/assets/health-down.png';
|
||||
import UnknownState from '@src/assets/health-unknown.png';
|
||||
import GoodStateEmoji from '@src/assets/health-good-emoji.png';
|
||||
import MediumStateEmoji from '@src/assets/health-medium-emoji.png';
|
||||
import PoorStateEmoji from '@src/assets/health-poor-emoji.png';
|
||||
import DownStateEmoji from '@src/assets/health-down-emoji.png';
|
||||
import './index.less';
|
||||
|
||||
export enum HealthStateEnum {
|
||||
UNKNOWN = -1,
|
||||
GOOD,
|
||||
MEDIUM,
|
||||
POOR,
|
||||
DOWN,
|
||||
}
|
||||
|
||||
interface HealthStateProps {
|
||||
state: HealthStateEnum;
|
||||
width: string | number;
|
||||
height: string | number;
|
||||
}
|
||||
|
||||
const HEALTH_STATE_MAP = {
|
||||
[HealthStateEnum.GOOD]: GoodState,
|
||||
[HealthStateEnum.MEDIUM]: MediumState,
|
||||
[HealthStateEnum.POOR]: PoorState,
|
||||
[HealthStateEnum.DOWN]: DownState,
|
||||
[HealthStateEnum.UNKNOWN]: UnknownState,
|
||||
};
|
||||
|
||||
const HEALTH_STATE_EMOJI_MAP = {
|
||||
[HealthStateEnum.GOOD]: GoodStateEmoji,
|
||||
[HealthStateEnum.MEDIUM]: MediumStateEmoji,
|
||||
[HealthStateEnum.POOR]: PoorStateEmoji,
|
||||
[HealthStateEnum.DOWN]: DownStateEmoji,
|
||||
[HealthStateEnum.UNKNOWN]: DownStateEmoji,
|
||||
};
|
||||
|
||||
const HEALTH_STATE_DESC_MAP = {
|
||||
[HealthStateEnum.GOOD]: '状态优异',
|
||||
[HealthStateEnum.MEDIUM]: '状态良好',
|
||||
[HealthStateEnum.POOR]: '状态较差',
|
||||
[HealthStateEnum.DOWN]: '状态异常',
|
||||
[HealthStateEnum.UNKNOWN]: '状态异常',
|
||||
};
|
||||
|
||||
export const getHealthStateEmoji = (state: HealthStateEnum, width = 16, height = 16) => {
|
||||
return (
|
||||
<img
|
||||
width={width}
|
||||
height={height}
|
||||
style={{ marginTop: -3 }}
|
||||
src={HEALTH_STATE_EMOJI_MAP[state] || HEALTH_STATE_EMOJI_MAP[HealthStateEnum.UNKNOWN]}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const getHealthStateDesc = (state: HealthStateEnum) => {
|
||||
return HEALTH_STATE_DESC_MAP[state] || HEALTH_STATE_DESC_MAP[HealthStateEnum.UNKNOWN];
|
||||
};
|
||||
|
||||
const HealthState = (props: HealthStateProps) => {
|
||||
const { state, width, height } = props;
|
||||
|
||||
return (
|
||||
<div className="health-state" style={{ width, height }}>
|
||||
<img src={HEALTH_STATE_MAP[state] || UnknownState} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default HealthState;
|
||||
Reference in New Issue
Block a user