feature: Add monitor overview

add monitor overview

Change-Id: I1e66ffde80ae2c162c1a68411cdd2cfa98ec8931
This commit is contained in:
zhuyue 2021-11-29 14:52:35 +08:00 committed by zhu yue
parent 986891f430
commit a210d80f5c
13 changed files with 1077 additions and 1 deletions

View File

@ -18,7 +18,7 @@ import {
DatabaseOutlined,
CreditCardOutlined,
GlobalOutlined,
// ToolOutlined,
MonitorOutlined,
SettingOutlined,
HomeOutlined,
AppstoreOutlined,
@ -587,6 +587,22 @@ const renderMenu = (t) => {
},
],
},
{
path: '/monitor-center',
name: t('Monitor Center'),
key: '/monitorCenterAdmin',
icon: <MonitorOutlined />,
children: [
{
path: '/monitor-center/overview-admin',
name: t('Monitor Overview'),
key: 'monitorOverviewAdmin',
level: 1,
children: [],
hasBreadcrumb: true,
},
],
},
{
path: '/configuration-admin',
name: t('Global Setting'),

View File

@ -41,6 +41,9 @@ const Heat = lazy(() =>
const UserCenter = lazy(() =>
import(/* webpackChunkName: "user-center" */ 'pages/user-center/App')
);
const MonitorCenter = lazy(() =>
import(/* webpackChunkName: "monitor-center" */ 'pages/monitor/App')
);
const E404 = lazy(() =>
import(/* webpackChunkName: "E404" */ 'pages/base/containers/404')
);
@ -77,6 +80,10 @@ export default [
path: `/heat`,
component: Heat,
},
{
path: `/monitor-center`,
component: MonitorCenter,
},
{
path: `/user`,
component: UserCenter,

21
src/pages/monitor/App.jsx Normal file
View File

@ -0,0 +1,21 @@
// Copyright 2021 99cloud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import renderRoutes from 'utils/RouterConfig';
import routes from './routes';
const App = (props) => renderRoutes(routes, props);
export default App;

View File

@ -0,0 +1,55 @@
// Copyright 2021 99cloud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import React from 'react';
import { Empty, Row } from 'antd';
import { Chart, Line, Tooltip } from 'bizcharts';
import { observer } from 'mobx-react';
import styles from '../../../index.less';
export default observer(({ data }) => {
return (
<div className={styles.card}>
<Row justify="space-between">
<span>{t('Last week alarm trend')}</span>
<span>{t('time / 24h')}</span>
</Row>
<Row
justify="center"
align="middle"
style={{ height: 272, paddingTop: 10 }}
>
{data.length === 0 ? <Empty /> : <Charts data={data} />}
</Row>
</div>
);
});
function Charts({ data }) {
return (
<Chart
padding={[10, 20, 50, 50]}
autoFit
data={data}
scale={{
count: {
nice: true,
},
}}
>
<Line position="date*count" />
<Tooltip showCrosshairs lock />
</Chart>
);
}

View File

@ -0,0 +1,112 @@
// Copyright 2021 99cloud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import React, { useState, useEffect } from 'react';
import { Col, Row, Spin } from 'antd';
import styles from 'pages/monitor/containers/Overview/index.less';
import FetchPrometheusStore from 'components/PrometheusChart/store/FetchPrometheusStore';
import { handleResponse } from 'components/PrometheusChart/utils/dataHandler';
import moment from 'moment';
import AlertChart from './components/AlertChart';
const STEP = 15;
const Index = function () {
const store = new FetchPrometheusStore({
requestType: 'range',
metricKey: 'monitorOverview.alertInfo',
formatDataFn: (responses, typeKey, deviceKey, modifyKeys) => {
const ret = [];
responses.forEach((response, idx) => {
ret.push(handleResponse(response, typeKey, deviceKey, modifyKeys[idx]));
});
return ret;
},
modifyKeys: ['cpu', 'memory'],
});
const [isLoading, setIsLoading] = useState(true);
const [cpuCount, setCpuCount] = useState(0);
const [memCount, setMemCount] = useState(0);
const [weekData, setWeekData] = useState(build7DaysData());
useEffect(() => {
const end = moment();
const start = moment().startOf('day');
setIsLoading(true);
store
.fetchData({
interval: STEP,
currentRange: [start, end],
})
.then((d) => {
const [cpuData, memoryData] = d;
const newCpuCount = cpuData.reduce(
(pre, cur, idx) =>
idx > 0 && cur.x - cpuData[idx - 1].x > STEP ? pre + 1 : pre,
0
);
const newMemoryCount = memoryData.reduce(
(pre, cur, idx) =>
idx > 0 && cur.x - memoryData[idx - 1].x > STEP ? pre + 1 : pre,
0
);
setCpuCount(newCpuCount);
setMemCount(newMemoryCount);
weekData[6].count = newCpuCount + newMemoryCount;
setWeekData([...weekData]);
})
.finally(() => {
setIsLoading(false);
});
}, []);
return isLoading ? (
<Spin />
) : (
<Row gutter={[16, 16]}>
<Col flex="1 1">
<div className={styles.card}>
<Row style={{ height: '100%' }}>
<Col span={12} className={styles.alertCardLine}>
<div className={styles.number}>{cpuCount}</div>
<div>{t('Today CPU usage > 80% alert')}</div>
</Col>
<Col span={12} className={styles.alertCardLine}>
<div className={styles.number}>{memCount}</div>
<div>{t('Today Memory usage > 80% alert')}</div>
</Col>
</Row>
</div>
</Col>
<Col flex="0 1 440px">
<AlertChart data={weekData} />
</Col>
</Row>
);
};
function build7DaysData() {
const today = moment().startOf('day');
const ret = [];
for (let index = 6; index >= 0; index--) {
ret.push({
date: today.clone().subtract(index, 'day').format('MM-DD'),
count: 0,
});
}
return ret;
}
export default Index;

View File

@ -0,0 +1,171 @@
// Copyright 2021 99cloud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import React from 'react';
import { Col, Progress, Row } from 'antd';
import { handleResponses } from 'components/PrometheusChart/utils/dataHandler';
import { get, merge } from 'lodash';
import { getSuitableValue } from 'resources/monitoring';
import { computePercentage } from 'utils/index';
import BaseCard from 'components/PrometheusChart/BaseCard';
import styles from '../../index.less';
const ClusterCard = (props) => {
const { store: propStore } = props;
const baseConfig = {
span: 12,
constructorParams: {
requestType: 'current',
formatDataFn: handleResponses,
},
visibleHeight: 55,
renderContent: (store) => (
<div className={styles.topContent}>{store.data}</div>
),
};
const chartLists = [
{
title: t('Physical CPU Usage'),
span: 12,
constructorParams: {
metricKey: 'monitorOverview.physicalCPUUsage',
},
renderContent: (store) => {
const { data } = store;
const used = get(data[0], 'y', 0);
const total = get(data[1], 'y', 0);
return (
<div className={styles.topContent} style={{ height: 55 }}>
<div>
<Row style={{ alignItems: 'baseline', justifyContent: 'center' }}>
<span style={{ fontSize: 28, fontWeight: 600 }}>
{computePercentage(used, total)}
</span>
%
</Row>
<Row
style={{
alignItems: 'baseline',
justifyContent: 'center',
fontSize: 12,
}}
>
{`${used} / ${total}`}
</Row>
</div>
</div>
);
},
},
{
title: t('Total Ram'),
span: 12,
constructorParams: {
metricKey: 'monitorOverview.physicalMemoryUsage',
},
renderContent: (store) => {
const { data } = store;
const usedValue = get(data[0], 'y', 0);
const totalValue = get(data[1], 'y', 0);
const used = getSuitableValue(usedValue, 'memory');
const total = getSuitableValue(totalValue, 'memory');
return (
<div className={styles.topContent} style={{ height: 55 }}>
<div>
<Row style={{ alignItems: 'baseline', justifyContent: 'center' }}>
<span style={{ fontSize: 28, fontWeight: 600 }}>
{computePercentage(usedValue, totalValue)}
</span>
%
</Row>
<Row
style={{
alignItems: 'baseline',
justifyContent: 'center',
fontSize: 12,
}}
>
{`${used} / ${total}`}
</Row>
</div>
</div>
);
},
},
{
title: t('Physical Storage Usage'),
span: 24,
constructorParams: {
metricKey: 'monitorOverview.physicalStorageUsage',
},
renderContent: (store) => {
const { data } = store;
const usedValue = get(data[0], 'y', 0);
const totalValue = get(data[1], 'y', 0);
const used = getSuitableValue(usedValue, 'disk');
const total = getSuitableValue(totalValue, 'disk');
const progressPercentage = computePercentage(usedValue, totalValue);
return (
<div className={styles.topContent} style={{ height: 55 }}>
<div
style={{
width: '100%',
height: '100%',
}}
>
<Row style={{ justifyContent: 'flex-end', height: '50%' }}>
<span
style={{
fontSize: 12,
marginRight: 32,
}}
>
{`${t('Used')} ${used} / ${t('Total')} ${total}`}
</span>
</Row>
<Row style={{ height: '50%' }}>
<Progress
style={{ width: '95%' }}
percent={progressPercentage}
strokeColor={progressPercentage > 80 ? '#FAAD14' : '#1890FF'}
showInfo={progressPercentage !== 100}
/>
</Row>
</div>
</div>
);
},
},
];
return (
<Row gutter={[16, 16]}>
{chartLists.map((chartProps) => {
const config = merge({}, baseConfig, chartProps);
const { span, ...rest } = config;
return (
<Col span={span} key={chartProps.constructorParams.metricKey}>
<BaseCard
{...rest}
currentRange={propStore.currentRange}
interval={propStore.interval}
/>
</Col>
);
})}
</Row>
);
};
export default ClusterCard;

View File

@ -0,0 +1,68 @@
// Copyright 2021 99cloud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import React from 'react';
import styles from 'pages/monitor/containers/Overview/index.less';
import { observer } from 'mobx-react';
import CircleChart from 'components/PrometheusChart/CircleWithRightLegend';
import { get } from 'lodash';
import BaseCard from 'components/PrometheusChart/BaseCard';
const ClusterChart = observer((props) => {
const { store: propStore } = props;
const config = {
title: t('Compute Node status'),
constructorParams: {
requestType: 'current',
formatDataFn: (responses) => {
const status = [
{
type: 'up',
value: 0,
},
{
type: 'down',
value: 0,
},
];
const result = get(responses[0], 'data.result', []);
result.forEach((sta) => {
const idx = sta.metric.services_state === 'up' ? 0 : 1;
status[idx].value += parseInt(sta.value[1], 10);
});
return status;
},
metricKey: 'monitorOverview.computeNodeStatus',
},
renderContent: (store) => (
<div className={styles.topContent}>
<div style={{ height: 218 }}>
<CircleChart data={store.data} />
</div>
</div>
),
visibleHeight: 230,
};
return (
<BaseCard
{...config}
currentRange={propStore.currentRange}
interval={propStore.interval}
/>
);
});
export default ClusterChart;

View File

@ -0,0 +1,155 @@
// Copyright 2021 99cloud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import React from 'react';
import { Col, Row } from 'antd';
import { computePercentage } from 'utils/index';
import {
cephStatusColorMap,
cephStatusMap,
getSuitableValue,
} from 'resources/monitoring';
import { handleResponses } from 'components/PrometheusChart/utils/dataHandler';
import { get, merge } from 'lodash';
import BaseCard from 'components/PrometheusChart/BaseCard';
import styles from '../../index.less';
const StorageClusterCard = (props) => {
const { store: propStore } = props;
const baseConfig = {
span: 12,
constructorParams: {
requestType: 'current',
formatDataFn: handleResponses,
},
visibleHeight: 65,
renderContent: (store) => (
<div className={styles.topContent}>{JSON.stringify(store.data)}</div>
),
};
const chartLists = [
{
title: t('Storage Cluster Status'),
span: 24,
constructorParams: {
metricKey: 'monitorOverview.cephHealthStatus',
},
renderContent: (store) => {
const data = get(store.data, 'y', 0);
return (
<div
className={styles.topContent}
style={{
fontSize: 28,
fontWeight: 600,
color: cephStatusColorMap[data],
height: 65,
}}
>
{cephStatusMap[data]}
</div>
);
},
},
{
title: t('Storage Cluster Usage'),
span: 12,
constructorParams: {
metricKey: 'monitorOverview.cephStorageUsage',
},
renderContent: (store) => {
const { data } = store;
const usedValue = get(data[0], 'y', 0);
const totalValue = get(data[1], 'y', 0);
const used = getSuitableValue(usedValue, 'disk');
const total = getSuitableValue(totalValue, 'disk');
return (
<div className={styles.topContent} style={{ height: 55 }}>
<div>
<Row style={{ alignItems: 'baseline', justifyContent: 'center' }}>
<span style={{ fontSize: 28, fontWeight: 600 }}>
{computePercentage(usedValue, totalValue)}
</span>
%
</Row>
<Row
style={{
alignItems: 'baseline',
justifyContent: 'center',
fontSize: 12,
}}
>
{`${used} / ${total}`}
</Row>
</div>
</div>
);
},
},
{
title: t('Disk allocation (GB)'),
span: 12,
constructorParams: {
metricKey: 'monitorOverview.cephStorageAllocate',
},
renderContent: (store) => {
const { data } = store;
const totalValue = parseFloat(get(data[1], 'y', 0).toFixed(2));
const usedValue = parseFloat(
(totalValue - get(data[0], 'y', 0)).toFixed(2)
);
return (
<div className={styles.topContent} style={{ height: 55 }}>
<div>
<Row style={{ alignItems: 'baseline', justifyContent: 'center' }}>
<span style={{ fontSize: 28, fontWeight: 600 }}>
{computePercentage(usedValue, totalValue)}
</span>
%
</Row>
<Row
style={{
alignItems: 'baseline',
justifyContent: 'center',
fontSize: 12,
}}
>
{`${usedValue} GB / ${totalValue} GB`}
</Row>
</div>
</div>
);
},
},
];
return (
<Row gutter={[16, 16]}>
{chartLists.map((chartProps) => {
const config = merge({}, baseConfig, chartProps);
const { span, ...rest } = config;
return (
<Col span={span} key={chartProps.constructorParams.metricKey}>
<BaseCard
{...rest}
currentRange={propStore.currentRange}
interval={propStore.interval}
/>
</Col>
);
})}
</Row>
);
};
export default StorageClusterCard;

View File

@ -0,0 +1,57 @@
// Copyright 2021 99cloud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import React from 'react';
import { observer } from 'mobx-react';
import ChartCard from 'components/PrometheusChart/ChartCard';
import { handleResponses } from 'components/PrometheusChart/utils/dataHandler';
import { ChartType } from 'components/PrometheusChart/utils/utils';
const StorageClusterChart = observer((props) => {
const { store: propStore } = props;
const config = {
title: t('Storage Cluster IOPS'),
constructorParams: {
requestType: 'range',
metricKey: 'monitorOverview.cephStorageClusterIOPS',
formatDataFn: handleResponses,
modifyKeys: [t('read'), t('write')],
},
chartProps: {
chartType: ChartType.MULTILINE,
height: 250,
scale: {
y: {
nice: true,
},
},
},
visibleHeight: 230,
};
return (
<ChartCard
{...config}
currentRange={propStore.currentRange}
interval={propStore.interval}
BaseContentConfig={{
...props.BaseContentConfig,
renderTimeRangeSelect: true,
}}
/>
);
});
export default StorageClusterChart;

View File

@ -0,0 +1,187 @@
// Copyright 2021 99cloud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import React from 'react';
import { Col, Progress, Row, Tabs } from 'antd';
import { handleResponses } from 'components/PrometheusChart/utils/dataHandler';
import { get, merge } from 'lodash';
import BaseCard from 'components/PrometheusChart/BaseCard';
import { Chart, Interval } from 'bizcharts';
import { getSuitableValue } from 'resources/monitoring';
import styles from '../../index.less';
const renderTopProgress = (store) => {
const { data } = store;
return (
<Row style={{ height: '100%' }}>
{data.map((d) => {
const percentage = get(d, 'y', 0);
const percentageColor = percentage > 80 ? '#FAAD14' : '#1890FF';
return (
<Col span={24} key={d.type}>
<div>{d.type}</div>
<Progress
strokeColor={percentageColor}
percent={percentage}
style={{ marginBottom: 4 }}
showInfo={percentage !== 100}
/>
</Col>
);
})}
</Row>
);
};
const renderTopColumnExtra = (store) => {
const { modifyKeys } = store;
return (
<Tabs
className={styles.tabs}
defaultActiveKey={modifyKeys[0]}
onChange={(key) => store.handleDeviceChange(key)}
>
{modifyKeys.map((k) => (
<Tabs.TabPane tab={k} key={k} />
))}
</Tabs>
);
};
const renderTopColumnChart = (store, scale = { y: {} }) => {
const { data, modifyKeys } = store;
return (
<div>
<Chart
autoFit
data={data.filter((d) => d.type === (store.device || modifyKeys[0]))}
height={198}
scale={{
y: {
nice: true,
...scale.y,
},
}}
>
<Interval position="x*y" size={20} />
</Chart>
</div>
);
};
const TopCard = (props) => {
const { store: propStore } = props;
const baseConfig = {
span: 12,
constructorParams: {
requestType: 'current',
formatDataFn: handleResponses,
},
visibleHeight: 200,
renderContent: (store) => (
<div className={styles.topContent}>{store.data}</div>
),
};
const chartLists = [
{
title: t('Host CPU Usage'),
span: 12,
constructorParams: {
metricKey: 'monitorOverview.topHostCPUUsage',
typeKey: 'instance',
},
renderContent: renderTopProgress,
},
{
title: t('Host Disk Average IOPS'),
span: 12,
constructorParams: {
metricKey: 'monitorOverview.topHostDiskIOPS',
formatDataFn: (reps, tk, dk, mk) => {
const data = [];
reps.forEach((ret, resIdx) => {
(ret.data.result || []).forEach((d) => {
data.push({
x: d.metric.instance,
y: parseFloat(get(d, 'value[1]', 0)),
type: mk[resIdx],
});
});
});
return data;
},
modifyKeys: [t('read'), t('write')],
},
extra: renderTopColumnExtra,
renderContent: renderTopColumnChart,
},
{
title: t('Host Memory Usage'),
span: 12,
constructorParams: {
metricKey: 'monitorOverview.topHostMemoryUsage',
typeKey: 'instance',
},
renderContent: renderTopProgress,
},
{
title: t('Host Average Network IO'),
span: 12,
constructorParams: {
metricKey: 'monitorOverview.topHostInterface',
formatDataFn: (reps, tk, dk, mk) => {
const data = [];
reps.forEach((ret, resIdx) => {
(ret.data.result || []).forEach((d) => {
data.push({
x: d.metric.instance,
y: parseFloat(get(d, 'value[1]', 0)),
type: mk[resIdx],
});
});
});
return data;
},
modifyKeys: [t('receive'), t('transmit')],
},
extra: renderTopColumnExtra,
renderContent: (store) =>
renderTopColumnChart(store, {
y: {
formatter: (d) => getSuitableValue(d, 'traffic', 0),
},
}),
},
];
return (
<Row gutter={[16, 16]}>
{chartLists.map((chartProps) => {
const config = merge({}, baseConfig, chartProps);
const { span, title, ...rest } = config;
return (
<Col span={span} key={chartProps.constructorParams.metricKey}>
<BaseCard
{...rest}
title={`${title} TOP5`}
currentRange={propStore.currentRange}
interval={propStore.interval}
/>
</Col>
);
})}
</Row>
);
};
export default TopCard;

View File

@ -0,0 +1,72 @@
// Copyright 2021 99cloud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import React from 'react';
import BaseContent from 'components/PrometheusChart/component/BaseContent';
import { Col, Row } from 'antd';
import styles from './index.less';
import AlertInfo from './components/AlertInfo';
import ClusterCard from './components/ClusterMonitor/ClusterCard';
import ClusterChart from './components/ClusterMonitor/ClusterChart';
import TopCard from './components/Tops/TopCard';
import StorageClusterCard from './components/StorageClusterMonitor/StorageClusterCard';
import StorageClusterChart from './components/StorageClusterMonitor/StorageClusterChart';
const BaseContentConfig = {
renderNodeSelect: false,
renderTimeRangeSelect: false,
};
const Index = () => {
return (
<BaseContent
{...BaseContentConfig}
renderChartCards={(store) => (
<Row gutter={[16, 16]} className={styles.container}>
<Col span={24}>
<AlertInfo />
</Col>
<Col span={24}>
<Row gutter={[16, 16]}>
<Col span={12}>
<ClusterCard store={store} />
</Col>
<Col span={12}>
<ClusterChart store={store} />
</Col>
</Row>
</Col>
<Col span={24}>
<TopCard store={store} />
</Col>
<Col span={24}>
<Row gutter={[16, 16]}>
<Col span={12}>
<StorageClusterCard store={store} />
</Col>
<Col span={12}>
<StorageClusterChart
store={store}
BaseContentConfig={BaseContentConfig}
/>
</Col>
</Row>
</Col>
</Row>
)}
/>
);
};
export default Index;

View File

@ -0,0 +1,126 @@
.container {
.card {
padding: 16px;
background-color: #ffffff;
box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.05),
0px 0px 10px 0px rgba(0, 0, 0, 0.05);
color: #000000;
height: 100%;
.tabs {
:global {
.ant-tabs-tab {
margin-right: 20px;
border-bottom: 1px solid #f0f0f0;
}
.ant-tabs-nav::before {
border-bottom: none;
}
}
}
}
.alertCardLine {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.number {
font-size: 36px;
color: rgb(232, 104, 74);
}
}
.alertCardTitle {
font-size: 14px;
font-weight: 400;
color: #000000;
}
.alertInfo {
margin-top: 6px;
.alertNumber {
font-size: 40px;
font-weight: 600;
}
.alertLevel {
font-size: 16px;
font-weight: 400;
color: rgba(0, 0, 0, 0.65);
margin-top: 12px;
}
}
.alertCardBottom {
padding: 0 26px;
.buttonTitle {
font-size: 12px;
font-weight: 400;
color: #000000;
margin-bottom: 15px;
height: 20px;
line-height: 20px;
}
.alertLineInfo {
font-size: 12px;
font-weight: 400;
height: 100%;
color: rgba(0, 0, 0, 0.65);
}
}
}
.outer {
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
font-size: 14px;
.inner {
width: 100%;
height: 100%;
position: absolute;
left: 0;
overflow-x: hidden;
overflow-y: scroll;
}
.inner::-webkit-scrollbar {
display: none;
}
}
.flexColumnAround {
display: flex;
flex-direction: column;
justify-content: space-between;
}
.topContent {
font-size: 24px;
font-weight: 500;
height: 100px;
display: flex;
justify-content: center;
align-items: center;
}
.tabs {
:global {
.ant-tabs-tab {
margin-right: 20px;
border-bottom: 1px solid #f0f0f0;
}
.ant-tabs-nav::before {
border-bottom: none;
}
}
}

View File

@ -0,0 +1,29 @@
// Copyright 2021 99cloud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import BaseLayout from 'layouts/Basic';
import E404 from 'pages/base/containers/404';
import Overview from '../containers/Overview';
const PATH = '/monitor-center';
export default [
{
path: PATH,
component: BaseLayout,
routes: [
{ path: `${PATH}/overview-admin`, component: Overview, exact: true },
{ path: '*', component: E404 },
],
},
];