fix: 图表逻辑 & 展示优化

This commit is contained in:
GraceWalk
2022-10-21 11:42:34 +08:00
parent 26e60d8a64
commit 62f7d3f72f
20 changed files with 328 additions and 260 deletions

View File

@@ -14,6 +14,7 @@ export enum MetricType {
Broker = 103, Broker = 103,
Partition = 104, Partition = 104,
Replication = 105, Replication = 105,
Zookeeper = 110,
Controls = 901, Controls = 901,
} }
@@ -61,6 +62,8 @@ const api = {
phyClusterState: getApi(`/physical-clusters/state`), phyClusterState: getApi(`/physical-clusters/state`),
getOperatingStateList: (clusterPhyId: number) => getApi(`/clusters/${clusterPhyId}/groups-overview`), getOperatingStateList: (clusterPhyId: number) => getApi(`/clusters/${clusterPhyId}/groups-overview`),
getGroupTopicList: (clusterPhyId: number, groupName: string) => getApi(`/clusters/${clusterPhyId}/groups/${groupName}/topics-overview`),
// 物理集群接口 // 物理集群接口
phyCluster: getApi(`/physical-clusters`), phyCluster: getApi(`/physical-clusters`),
getPhyClusterBasic: (clusterPhyId: number) => getApi(`/physical-clusters/${clusterPhyId}/basic`), getPhyClusterBasic: (clusterPhyId: number) => getApi(`/physical-clusters/${clusterPhyId}/basic`),
@@ -127,6 +130,7 @@ const api = {
getApi(`/clusters/${clusterPhyId}/topics/${topicName}/brokers-partitions-summary`), getApi(`/clusters/${clusterPhyId}/topics/${topicName}/brokers-partitions-summary`),
getTopicPartitionsDetail: (clusterPhyId: string, topicName: string) => getApi(`/clusters/${clusterPhyId}/topics/${topicName}/partitions`), getTopicPartitionsDetail: (clusterPhyId: string, topicName: string) => getApi(`/clusters/${clusterPhyId}/topics/${topicName}/partitions`),
getTopicMessagesList: (topicName: string, clusterPhyId: number) => getApi(`/clusters/${clusterPhyId}/topics/${topicName}/records`), // Messages列表 getTopicMessagesList: (topicName: string, clusterPhyId: number) => getApi(`/clusters/${clusterPhyId}/topics/${topicName}/records`), // Messages列表
getTopicGroupList: (topicName: string, clusterPhyId: number) => getApi(`/clusters/${clusterPhyId}/topics/${topicName}/groups-overview`), // Consumers列表
getTopicMessagesMetadata: (topicName: string, clusterPhyId: number) => getApi(`/clusters//${clusterPhyId}/topics/${topicName}/metadata`), // Messages列表 getTopicMessagesMetadata: (topicName: string, clusterPhyId: number) => getApi(`/clusters//${clusterPhyId}/topics/${topicName}/metadata`), // Messages列表
getTopicACLsList: (topicName: string, clusterPhyId: number) => getApi(`/clusters/${clusterPhyId}/topics/${topicName}/acl-Bindings`), // ACLs列表 getTopicACLsList: (topicName: string, clusterPhyId: number) => getApi(`/clusters/${clusterPhyId}/topics/${topicName}/acl-Bindings`), // ACLs列表
getTopicConfigs: (topicName: string, clusterPhyId: number) => getApi(`/clusters/${clusterPhyId}/config-topics/${topicName}/configs`), // Configuration列表 getTopicConfigs: (topicName: string, clusterPhyId: number) => getApi(`/clusters/${clusterPhyId}/config-topics/${topicName}/configs`), // Configuration列表

View File

@@ -1,15 +1,12 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect, forwardRef, useImperativeHandle } from 'react';
import { Drawer, Button, Space, Divider, AppContainer, ProTable } from 'knowdesign'; import { Drawer, Button, Space, Divider, AppContainer, ProTable, Utils } from 'knowdesign';
import { IconFont } from '@knowdesign/icons'; import { IconFont } from '@knowdesign/icons';
import { IindicatorSelectModule } from './index'; import { MetricSelect } from './index';
import './style/indicator-drawer.less'; import './style/indicator-drawer.less';
import { useLocation } from 'react-router-dom'; import { useLocation } from 'react-router-dom';
interface PropsType extends React.HTMLAttributes<HTMLDivElement> { interface PropsType extends React.HTMLAttributes<HTMLDivElement> {
onClose: () => void; metricSelect: MetricSelect;
visible: boolean;
isGroup?: boolean; // 是否分组
indicatorSelectModule: IindicatorSelectModule;
} }
interface MetricInfo { interface MetricInfo {
@@ -27,25 +24,25 @@ type CategoryData = {
metrics: MetricInfo[]; metrics: MetricInfo[];
}; };
const ExpandedRow = ({ metrics, category, selectedMetrics, selectedMetricChange }: any) => { const expandedRowColumns = [
const innerColumns = [ {
{ title: '指标名称',
title: '指标名称', dataIndex: 'name',
dataIndex: 'name', key: 'name',
key: 'name', },
}, {
{ title: '单位',
title: '单位', dataIndex: 'unit',
dataIndex: 'unit', key: 'unit',
key: 'unit', },
}, {
{ title: '描述',
title: '描述', dataIndex: 'desc',
dataIndex: 'desc', key: 'desc',
key: 'desc', },
}, ];
];
const ExpandedRow = ({ metrics, category, selectedMetrics, selectedMetricChange }: any) => {
return ( return (
<div <div
style={{ style={{
@@ -62,7 +59,7 @@ const ExpandedRow = ({ metrics, category, selectedMetrics, selectedMetricChange
showHeader: false, showHeader: false,
noPagination: true, noPagination: true,
rowKey: 'name', rowKey: 'name',
columns: innerColumns, columns: expandedRowColumns,
dataSource: metrics, dataSource: metrics,
attrs: { attrs: {
rowSelection: { rowSelection: {
@@ -79,13 +76,14 @@ const ExpandedRow = ({ metrics, category, selectedMetrics, selectedMetricChange
); );
}; };
const IndicatorDrawer = ({ onClose, visible, indicatorSelectModule }: PropsType) => { const MetricSelect = forwardRef(({ metricSelect }: PropsType, ref) => {
const [global] = AppContainer.useGlobalValue(); const [global] = AppContainer.useGlobalValue();
const { pathname } = useLocation(); const { pathname } = useLocation();
const [confirmLoading, setConfirmLoading] = useState<boolean>(false); const [confirmLoading, setConfirmLoading] = useState<boolean>(false);
const [categoryData, setCategoryData] = useState<CategoryData[]>([]); const [categoryData, setCategoryData] = useState<CategoryData[]>([]);
const [selectedCategories, setSelectedCategories] = useState<string[]>([]); const [selectedCategories, setSelectedCategories] = useState<string[]>([]);
const [childrenSelectedRowKeys, setChildrenSelectedRowKeys] = useState<SelectedMetrics>({}); const [childrenSelectedRowKeys, setChildrenSelectedRowKeys] = useState<SelectedMetrics>({});
const [visible, setVisible] = useState<boolean>(false);
const columns = [ const columns = [
{ {
@@ -96,13 +94,13 @@ const IndicatorDrawer = ({ onClose, visible, indicatorSelectModule }: PropsType)
]; ];
const formateTableData = () => { const formateTableData = () => {
const tableData = indicatorSelectModule.tableData; const tableData = metricSelect.tableData;
const categoryData: { const categoryData: {
[category: string]: MetricInfo[]; [category: string]: MetricInfo[];
} = {}; } = {};
tableData.forEach(({ name, desc }) => { tableData.forEach(({ name, desc }) => {
const metricDefine = global.getMetricDefine(indicatorSelectModule?.metricType, name); const metricDefine = global.getMetricDefine(metricSelect?.metricType, name);
const returnData = { const returnData = {
name, name,
desc, desc,
@@ -125,12 +123,12 @@ const IndicatorDrawer = ({ onClose, visible, indicatorSelectModule }: PropsType)
}; };
const formateSelectedKeys = () => { const formateSelectedKeys = () => {
const newKeys = indicatorSelectModule.selectedRows; const newKeys = metricSelect.selectedRows;
const result: SelectedMetrics = {}; const result: SelectedMetrics = {};
const selectedCategories: string[] = []; const selectedCategories: string[] = [];
newKeys.forEach((name: string) => { newKeys.forEach((name: string) => {
const metricDefine = global.getMetricDefine(indicatorSelectModule?.metricType, name); const metricDefine = global.getMetricDefine(metricSelect?.metricType, name);
if (metricDefine) { if (metricDefine) {
if (!result[metricDefine.category]) { if (!result[metricDefine.category]) {
result[metricDefine.category] = [name]; result[metricDefine.category] = [name];
@@ -217,10 +215,10 @@ const IndicatorDrawer = ({ onClose, visible, indicatorSelectModule }: PropsType)
const allRowKeys: string[] = []; const allRowKeys: string[] = [];
Object.entries(childrenSelectedRowKeys).forEach(([, arr]) => allRowKeys.push(...arr)); Object.entries(childrenSelectedRowKeys).forEach(([, arr]) => allRowKeys.push(...arr));
indicatorSelectModule.submitCallback(allRowKeys).then( metricSelect.submitCallback(allRowKeys).then(
() => { () => {
setConfirmLoading(false); setConfirmLoading(false);
onClose(); setVisible(false);
}, },
() => { () => {
setConfirmLoading(false); setConfirmLoading(false);
@@ -231,7 +229,7 @@ const IndicatorDrawer = ({ onClose, visible, indicatorSelectModule }: PropsType)
const rowSelection = { const rowSelection = {
selectedRowKeys: selectedCategories, selectedRowKeys: selectedCategories,
onChange: rowChange, onChange: rowChange,
// getCheckboxProps: (record: any) => indicatorSelectModule.checkboxProps && indicatorSelectModule.checkboxProps(record), // getCheckboxProps: (record: any) => metricSelect.checkboxProps && metricSelect.checkboxProps(record),
getCheckboxProps: (record: CategoryData) => { getCheckboxProps: (record: CategoryData) => {
const isAllSelected = record.metrics.length === childrenSelectedRowKeys[record.category]?.length; const isAllSelected = record.metrics.length === childrenSelectedRowKeys[record.category]?.length;
const isNotCheck = !childrenSelectedRowKeys[record.category] || childrenSelectedRowKeys[record.category]?.length === 0; const isNotCheck = !childrenSelectedRowKeys[record.category] || childrenSelectedRowKeys[record.category]?.length === 0;
@@ -241,25 +239,33 @@ const IndicatorDrawer = ({ onClose, visible, indicatorSelectModule }: PropsType)
}, },
}; };
useEffect(formateTableData, [indicatorSelectModule.tableData]); useEffect(formateTableData, [metricSelect.tableData]);
useEffect(() => { useEffect(() => {
visible && formateSelectedKeys(); visible && formateSelectedKeys();
}, [visible, indicatorSelectModule.selectedRows]); }, [visible, metricSelect.selectedRows]);
useImperativeHandle(
ref,
() => ({
open: () => setVisible(true),
}),
[]
);
return ( return (
<> <>
<Drawer <Drawer
className="indicator-drawer" className="indicator-drawer"
title={indicatorSelectModule.drawerTitle || '指标筛选'} title={metricSelect.drawerTitle || '指标筛选'}
width="868px" width="868px"
forceRender={true} forceRender={true}
onClose={onClose} onClose={() => setVisible(false)}
visible={visible} visible={visible}
maskClosable={false} maskClosable={false}
extra={ extra={
<Space> <Space>
<Button size="small" onClick={onClose}> <Button size="small" onClick={() => setVisible(false)}>
</Button> </Button>
<Button <Button
@@ -281,6 +287,7 @@ const IndicatorDrawer = ({ onClose, visible, indicatorSelectModule }: PropsType)
rowKey: 'category', rowKey: 'category',
columns: columns, columns: columns,
dataSource: categoryData, dataSource: categoryData,
noPagination: true,
attrs: { attrs: {
rowSelection: rowSelection, rowSelection: rowSelection,
expandable: { expandable: {
@@ -319,6 +326,6 @@ const IndicatorDrawer = ({ onClose, visible, indicatorSelectModule }: PropsType)
</Drawer> </Drawer>
</> </>
); );
}; });
export default IndicatorDrawer; export default MetricSelect;

View File

@@ -26,6 +26,7 @@ const OptionsDefault = [
const NodeScope = ({ nodeScopeModule, change }: propsType) => { const NodeScope = ({ nodeScopeModule, change }: propsType) => {
const { const {
hasCustomScope,
customScopeList: customList, customScopeList: customList,
scopeName = '', scopeName = '',
scopeLabel = '自定义范围', scopeLabel = '自定义范围',
@@ -128,51 +129,53 @@ const NodeScope = ({ nodeScopeModule, change }: propsType) => {
</Space> </Space>
</Radio.Group> </Radio.Group>
</div> </div>
<div className="flx_r"> {hasCustomScope && (
<h6 className="time_title">{scopeLabel}</h6> <div className="flx_r">
<div className="custom-scope"> <h6 className="time_title">{scopeLabel}</h6>
<div className="check-row"> <div className="custom-scope">
<Checkbox className="check-all" indeterminate={indeterminate} onChange={onCheckAllChange} checked={checkAll}> <div className="check-row">
<Checkbox className="check-all" indeterminate={indeterminate} onChange={onCheckAllChange} checked={checkAll}>
</Checkbox>
<Input </Checkbox>
className="search-input" <Input
suffix={<IconFont type="icon-fangdajing" style={{ fontSize: '16px' }} />} className="search-input"
size="small" suffix={<IconFont type="icon-fangdajing" style={{ fontSize: '16px' }} />}
placeholder={searchPlaceholder} size="small"
onChange={(e) => setScopeSearchValue(e.target.value)} placeholder={searchPlaceholder}
/> onChange={(e) => setScopeSearchValue(e.target.value)}
</div> />
<div className="fixed-height"> </div>
<Checkbox.Group style={{ width: '100%' }} onChange={checkChange} value={checkedListTemp}> <div className="fixed-height">
<Row gutter={[10, 12]}> <Checkbox.Group style={{ width: '100%' }} onChange={checkChange} value={checkedListTemp}>
{customList <Row gutter={[10, 12]}>
.filter((item) => item.label.includes(scopeSearchValue)) {customList
.map((item) => ( .filter((item) => item.label.includes(scopeSearchValue))
<Col span={12} key={item.value}> .map((item) => (
<Checkbox value={item.value}>{item.label}</Checkbox> <Col span={12} key={item.value}>
</Col> <Checkbox value={item.value}>{item.label}</Checkbox>
))} </Col>
</Row> ))}
</Checkbox.Group> </Row>
</div> </Checkbox.Group>
</div>
<div className="btn-con"> <div className="btn-con">
<Button <Button
type="primary" type="primary"
size="small" size="small"
className="btn-sure" className="btn-sure"
onClick={customSure} onClick={customSure}
disabled={checkedListTemp?.length > 0 ? false : true} disabled={checkedListTemp?.length > 0 ? false : true}
> >
</Button> </Button>
<Button size="small" onClick={customCancel}> <Button size="small" onClick={customCancel}>
</Button> </Button>
</div>
</div> </div>
</div> </div>
</div> )}
</div> </div>
</div> </div>
); );
@@ -185,7 +188,7 @@ const NodeScope = ({ nodeScopeModule, change }: propsType) => {
visible={popVisible} visible={popVisible}
content={clickContent} content={clickContent}
placement="bottomRight" placement="bottomRight"
overlayClassName="d-node-scope-popover" overlayClassName={`d-node-scope-popover ${hasCustomScope ? 'large-size' : ''}`}
onVisibleChange={visibleChange} onVisibleChange={visibleChange}
> >
<span className="input-span"> <span className="input-span">

View File

@@ -1,9 +1,9 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useRef, useState } from 'react';
import { Tooltip, Select, Utils, Divider, Button } from 'knowdesign'; import { Select, Divider, Button } from 'knowdesign';
import { IconFont } from '@knowdesign/icons'; import { IconFont } from '@knowdesign/icons';
import moment from 'moment'; import moment from 'moment';
import { DRangeTime } from 'knowdesign'; import { DRangeTime } from 'knowdesign';
import IndicatorDrawer from './IndicatorDrawer'; import MetricSelect from './MetricSelect';
import NodeScope from './NodeScope'; import NodeScope from './NodeScope';
import './style/index.less'; import './style/index.less';
@@ -24,8 +24,8 @@ export interface KsHeaderOptions {
data: number | number[]; data: number | number[];
}; };
} }
export interface IindicatorSelectModule { export interface MetricSelect {
metricType?: MetricType; metricType: MetricType;
hide?: boolean; hide?: boolean;
drawerTitle?: string; drawerTitle?: string;
selectedRows: (string | number)[]; selectedRows: (string | number)[];
@@ -47,20 +47,27 @@ export interface IcustomScope {
} }
export interface InodeScopeModule { export interface InodeScopeModule {
hasCustomScope: boolean;
customScopeList: IcustomScope[]; customScopeList: IcustomScope[];
scopeName?: string; scopeName?: string;
scopeLabel?: string; scopeLabel?: string;
searchPlaceholder?: string; searchPlaceholder?: string;
change?: () => void; change?: () => void;
} }
interface PropsType { interface PropsType {
indicatorSelectModule?: IindicatorSelectModule; metricSelect?: MetricSelect;
hideNodeScope?: boolean; hideNodeScope?: boolean;
hideGridSelect?: boolean; hideGridSelect?: boolean;
nodeScopeModule?: InodeScopeModule; nodeScopeModule?: InodeScopeModule;
onChange: (options: KsHeaderOptions) => void; onChange: (options: KsHeaderOptions) => void;
} }
interface ScopeData {
isTop: boolean;
data: any;
}
// 列布局选项 // 列布局选项
const GRID_SIZE_OPTIONS = [ const GRID_SIZE_OPTIONS = [
{ {
@@ -77,15 +84,17 @@ const GRID_SIZE_OPTIONS = [
}, },
]; ];
const SingleChartHeader = ({ const MetricOperateBar = ({
indicatorSelectModule, metricSelect,
nodeScopeModule = { nodeScopeModule = {
hasCustomScope: false,
customScopeList: [], customScopeList: [],
}, },
hideNodeScope = false, hideNodeScope = false,
hideGridSelect = false, hideGridSelect = false,
onChange: onChangeCallback, onChange: onChangeCallback,
}: PropsType): JSX.Element => { }: PropsType): JSX.Element => {
const metricSelectRef = useRef(null);
const [gridNum, setGridNum] = useState<number>(GRID_SIZE_OPTIONS[1].value); const [gridNum, setGridNum] = useState<number>(GRID_SIZE_OPTIONS[1].value);
const [rangeTime, setRangeTime] = useState<[number, number]>(() => { const [rangeTime, setRangeTime] = useState<[number, number]>(() => {
const curTimeStamp = moment().valueOf(); const curTimeStamp = moment().valueOf();
@@ -93,16 +102,35 @@ const SingleChartHeader = ({
}); });
const [isRelativeRangeTime, setIsRelativeRangeTime] = useState(true); const [isRelativeRangeTime, setIsRelativeRangeTime] = useState(true);
const [isAutoReload, setIsAutoReload] = useState(false); const [isAutoReload, setIsAutoReload] = useState(false);
const [indicatorDrawerVisible, setIndicatorDrawerVisible] = useState(false); const [scopeData, setScopeData] = useState<ScopeData>({
const [scopeData, setScopeData] = useState<{
isTop: boolean;
data: any;
}>({
isTop: true, isTop: true,
data: 5, data: 5,
}); });
const sizeChange = (value: number) => setGridNum(value);
const timeChange = (curRangeTime: [number, number], isRelative: boolean) => {
setRangeTime([...curRangeTime]);
setIsRelativeRangeTime(isRelative);
};
const reloadRangeTime = () => {
if (isRelativeRangeTime) {
const timeLen = rangeTime[1] - rangeTime[0] || 0;
const curTimeStamp = moment().valueOf();
setRangeTime([curTimeStamp - timeLen, curTimeStamp]);
} else {
setRangeTime([...rangeTime]);
}
};
const nodeScopeChange = (data: any, isTop?: any) => {
setScopeData({
isTop,
data,
});
};
useEffect(() => { useEffect(() => {
onChangeCallback({ onChangeCallback({
rangeTime, rangeTime,
@@ -129,68 +157,37 @@ const SingleChartHeader = ({
}; };
}, [isRelativeRangeTime, rangeTime]); }, [isRelativeRangeTime, rangeTime]);
const sizeChange = (value: number) => {
setGridNum(value);
};
const timeChange = (curRangeTime: [number, number], isRelative: boolean) => {
setRangeTime([...curRangeTime]);
setIsRelativeRangeTime(isRelative);
};
const reloadRangeTime = () => {
if (isRelativeRangeTime) {
const timeLen = rangeTime[1] - rangeTime[0] || 0;
const curTimeStamp = moment().valueOf();
setRangeTime([curTimeStamp - timeLen, curTimeStamp]);
} else {
setRangeTime([...rangeTime]);
}
};
const openIndicatorDrawer = () => {
setIndicatorDrawerVisible(true);
};
const closeIndicatorDrawer = () => {
setIndicatorDrawerVisible(false);
};
const nodeScopeChange = (data: any, isTop?: any) => {
setScopeData({
isTop,
data,
});
};
return ( return (
<> <>
<div className="ks-chart-container"> <div className="ks-chart-container">
<div className="ks-chart-container-header"> <div className="ks-chart-container-header">
<div className="header-left"> <div className="header-left">
{/* 刷新 */}
<div className="icon-box" onClick={reloadRangeTime}> <div className="icon-box" onClick={reloadRangeTime}>
<IconFont className="icon" type="icon-shuaxin1" /> <IconFont className="icon" type="icon-shuaxin1" />
</div> </div>
<Divider type="vertical" style={{ height: 20, top: 0 }} /> <Divider type="vertical" style={{ height: 20, top: 0 }} />
{/* 时间选择 */}
<DRangeTime timeChange={timeChange} rangeTimeArr={rangeTime} /> <DRangeTime timeChange={timeChange} rangeTimeArr={rangeTime} />
</div> </div>
<div className="header-right"> <div className="header-right">
{/* 节点范围 */}
{!hideNodeScope && <NodeScope nodeScopeModule={nodeScopeModule} change={nodeScopeChange} />} {!hideNodeScope && <NodeScope nodeScopeModule={nodeScopeModule} change={nodeScopeChange} />}
{/* 分栏 */}
{!hideGridSelect && ( {!hideGridSelect && (
<Select className="grid-select" style={{ width: 70 }} value={gridNum} options={GRID_SIZE_OPTIONS} onChange={sizeChange} /> <Select className="grid-select" style={{ width: 70 }} value={gridNum} options={GRID_SIZE_OPTIONS} onChange={sizeChange} />
)} )}
{(!hideNodeScope || !hideGridSelect) && <Divider type="vertical" style={{ height: 20, top: 0 }} />} {(!hideNodeScope || !hideGridSelect) && <Divider type="vertical" style={{ height: 20, top: 0 }} />}
<Button type="primary" onClick={openIndicatorDrawer}> <Button type="primary" onClick={() => metricSelectRef.current.open()}>
</Button> </Button>
</div> </div>
</div> </div>
</div> </div>
{!indicatorSelectModule?.hide && ( {/* 指标筛选 */}
<IndicatorDrawer visible={indicatorDrawerVisible} onClose={closeIndicatorDrawer} indicatorSelectModule={indicatorSelectModule} /> {!metricSelect?.hide && <MetricSelect ref={metricSelectRef} metricSelect={metricSelect} />}
)}
</> </>
); );
}; };
export default SingleChartHeader; export default MetricOperateBar;

View File

@@ -1,13 +1,8 @@
@root-entry-name: 'default'; .indicator-drawer {
@import '~knowdesign/es/basic/style/themes/index'; .dcloud-drawer-body {
@import '~knowdesign/es/basic/style/mixins/index'; padding-top: 2px !important;
.indicator-drawer{
.dcloud-drawer-body{
padding-top: 2px !important;
}
} }
}
// .dd-indicator-drawer { // .dd-indicator-drawer {
// @drawerItemH: 27px; // @drawerItemH: 27px;
// @primary-color: #556ee6; // @primary-color: #556ee6;

View File

@@ -63,9 +63,16 @@
} }
.@{ant-prefix}-popover-inner-content { .@{ant-prefix}-popover-inner-content {
padding: 16px 24px; padding: 16px 24px;
width: 479px; width: 200px;
box-sizing: border-box; box-sizing: border-box;
} }
&.large-size {
.@{ant-prefix}-popover-inner-content {
padding: 16px 24px;
width: 479px;
box-sizing: border-box;
}
}
&.@{ant-prefix}-popover-placement-bottomRight { &.@{ant-prefix}-popover-placement-bottomRight {
// padding-top: 0; // padding-top: 0;
} }

View File

@@ -2,11 +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 { OriginMetricData, FormattedMetricData, 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 { getDetailChartConfig } from './config'; import { getDetailChartConfig } from './config';
import { UNIT_MAP } from '@src/constants/chartConfig';
import RenderEmpty from '../RenderEmpty'; import RenderEmpty from '../RenderEmpty';
interface ChartDetailProps { interface ChartDetailProps {
@@ -35,7 +34,7 @@ interface ChartInfo {
curTimeRange?: readonly [number, number]; curTimeRange?: readonly [number, number];
sliderPos?: readonly [number, number]; sliderPos?: readonly [number, number];
transformUnit?: [string, number]; transformUnit?: [string, number];
fullMetricData?: MetricChartDataType; fullMetricData?: FormattedMetricData;
oldDataZoomOption?: any; oldDataZoomOption?: any;
} }
@@ -79,7 +78,7 @@ const ChartDetail = (props: ChartDetailProps) => {
isLoadingAdditionData: false, isLoadingAdditionData: false,
isLoadedFullData: false, isLoadedFullData: false,
fullTimeRange: curTimeRange, fullTimeRange: curTimeRange,
fullMetricData: {} as MetricChartDataType, fullMetricData: {} as FormattedMetricData,
curTimeRange, curTimeRange,
oldDataZoomOption: {}, oldDataZoomOption: {},
sliderPos: [0, 0], sliderPos: [0, 0],
@@ -90,7 +89,7 @@ const ChartDetail = (props: ChartDetailProps) => {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
// 当前展示的图表数据 // 当前展示的图表数据
const [curMetricData, setCurMetricData] = useState<MetricChartDataType>(); const [curMetricData, setCurMetricData] = useState<FormattedMetricData>();
// 图表数据的各项计算指标 // 图表数据的各项计算指标
const [tableInfo, setTableInfo] = useState<MetricTableInfo[]>([]); const [tableInfo, setTableInfo] = useState<MetricTableInfo[]>([]);
const [linesStatus, setLinesStatus] = useState<{ const [linesStatus, setLinesStatus] = useState<{
@@ -329,7 +328,7 @@ const ChartDetail = (props: ChartDetailProps) => {
if (i === DEFAULT_REQUEST_COUNT - 1) { if (i === DEFAULT_REQUEST_COUNT - 1) {
Promise.all(requestArr).then((resList) => { Promise.all(requestArr).then((resList) => {
// 填充增量的图表数据 // 填充增量的图表数据
resList.forEach((res: MetricDefaultChartDataType[], i) => { resList.forEach((res: OriginMetricData[], i) => {
// 最后一个请求返回数据为空时,认为已获取到全部图表数据 // 最后一个请求返回数据为空时,认为已获取到全部图表数据
if (!res?.length) { if (!res?.length) {
// 标记数据已经全部加载完毕 // 标记数据已经全部加载完毕
@@ -368,7 +367,7 @@ const ChartDetail = (props: ChartDetailProps) => {
}; };
// 处理增量图表数据 // 处理增量图表数据
const resolveAdditionChartData = (res: MetricDefaultChartDataType[], timeRange: [number, number]) => { const resolveAdditionChartData = (res: OriginMetricData[], timeRange: [number, number]) => {
// 格式化图表需要的数据 // 格式化图表需要的数据
const formattedMetricData = formatChartData( const formattedMetricData = formatChartData(
res, res,
@@ -376,7 +375,7 @@ const ChartDetail = (props: ChartDetailProps) => {
metricType, metricType,
timeRange, timeRange,
chartInfo.current.transformUnit chartInfo.current.transformUnit
) as MetricChartDataType[]; ) as FormattedMetricData[];
// 增量填充图表数据 // 增量填充图表数据
const additionMetricPoints = formattedMetricData[0].metricLines; const additionMetricPoints = formattedMetricData[0].metricLines;
Object.values(additionMetricPoints).forEach((additionLine) => { Object.values(additionMetricPoints).forEach((additionLine) => {
@@ -536,9 +535,7 @@ const ChartDetail = (props: ChartDetailProps) => {
// 如果图表返回数据 // 如果图表返回数据
if (res?.length) { if (res?.length) {
// 格式化图表需要的数据 // 格式化图表需要的数据
const formattedMetricData = ( const formattedMetricData = formatChartData(res, global.getMetricDefine || {}, metricType, curTimeRange)[0];
formatChartData(res, global.getMetricDefine || {}, metricType, curTimeRange) as MetricChartDataType[]
)[0];
// 填充图表数据 // 填充图表数据
let initFullTimeRange = curTimeRange; let initFullTimeRange = curTimeRange;
const pointsOfFirstLine = formattedMetricData.metricLines.find((line) => line.data.length).data; const pointsOfFirstLine = formattedMetricData.metricLines.find((line) => line.data.length).data;
@@ -549,14 +546,6 @@ const ChartDetail = (props: ChartDetailProps) => {
] as const; ] as const;
} }
// 获取单位保存起来
let transformUnit = undefined;
Object.entries(UNIT_MAP).forEach((unit) => {
if (formattedMetricData.metricUnit.includes(unit[0])) {
transformUnit = unit;
}
});
updateChartInfo({ updateChartInfo({
fullMetricData: formattedMetricData, fullMetricData: formattedMetricData,
fullTimeRange: [...initFullTimeRange], fullTimeRange: [...initFullTimeRange],
@@ -565,7 +554,7 @@ const ChartDetail = (props: ChartDetailProps) => {
initFullTimeRange[1] - (initFullTimeRange[1] - initFullTimeRange[0]) * DATA_ZOOM_DEFAULT_SCALE, initFullTimeRange[1] - (initFullTimeRange[1] - initFullTimeRange[0]) * DATA_ZOOM_DEFAULT_SCALE,
initFullTimeRange[1], initFullTimeRange[1],
], ],
transformUnit, transformUnit: formattedMetricData.targetUnit,
}); });
setCurMetricData(formattedMetricData); setCurMetricData(formattedMetricData);
const newLinesStatus: { [lineName: string]: boolean } = {}; const newLinesStatus: { [lineName: string]: boolean } = {};

View File

@@ -1,11 +1,22 @@
import api, { MetricType } from '@src/api';
import { getBasicChartConfig, CHART_COLOR_LIST } from '@src/constants/chartConfig'; import { getBasicChartConfig, CHART_COLOR_LIST } from '@src/constants/chartConfig';
const METRIC_DASHBOARD_REQ_MAP = {
[MetricType.Broker]: (clusterId: string) => api.getDashboardMetricChartData(clusterId, MetricType.Broker),
[MetricType.Topic]: (clusterId: string) => api.getDashboardMetricChartData(clusterId, MetricType.Topic),
[MetricType.Zookeeper]: (clusterId: string) => '',
};
export const getMetricDashboardReq = (clusterId: string, type: MetricType.Broker | MetricType.Topic | MetricType.Zookeeper) =>
METRIC_DASHBOARD_REQ_MAP[type](clusterId);
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;
// series 配置 // series 配置
return lines.map((line, i) => { return lines.map((line, i) => {
return { return {
...line, ...line,
z: len - i,
lineStyle: { lineStyle: {
width: 1.5, width: 1.5,
}, },
@@ -13,6 +24,7 @@ const seriesCallback = (lines: { name: string; data: [number, string | number][]
symbol: 'emptyCircle', symbol: 'emptyCircle',
symbolSize: 4, symbolSize: 4,
smooth: 0.25, smooth: 0.25,
color: CHART_COLOR_LIST[i % len],
areaStyle: { areaStyle: {
color: { color: {
type: 'linear', type: 'linear',
@@ -38,17 +50,13 @@ const seriesCallback = (lines: { name: string; data: [number, string | number][]
}; };
// 返回图表配置 // 返回图表配置
export const getChartConfig = (title: string, metricLength: number) => { export const getChartConfig = (title: string, metricLength: number, showLegend = true) => {
return { return {
option: getBasicChartConfig({ option: getBasicChartConfig({
title: { show: false }, title: { show: false },
grid: { top: 24 }, grid: { top: 24, bottom: showLegend ? 40 : 20 },
tooltip: { enterable: metricLength > 9, legendContextMaxHeight: 192 }, tooltip: { enterable: metricLength > 9, legendContextMaxHeight: 192 },
color: CHART_COLOR_LIST, legend: { show: showLegend },
// xAxis: {
// type: 'time',
// boundaryGap: ['5%', '5%'],
// },
}), }),
seriesCallback, seriesCallback,
}; };
@@ -67,7 +75,6 @@ export const getDetailChartConfig = (title: string, sliderPos: readonly [number,
legend: { legend: {
show: false, show: false,
}, },
color: CHART_COLOR_LIST,
dataZoom: [ dataZoom: [
{ {
type: 'inside', type: 'inside',

View File

@@ -1,5 +1,5 @@
.topic-dashboard { .topic-dashboard {
height: calc(100% - 160px); height: calc(100% - 162px);
padding-bottom: 10px; padding-bottom: 10px;
.ks-chart-container-header { .ks-chart-container-header {
margin-top: 12px; margin-top: 12px;

View File

@@ -4,17 +4,11 @@ import { Utils, Empty, Spin, AppContainer, SingleChart, Tooltip } from 'knowdesi
import { IconFont } from '@knowdesign/icons'; 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 { import { MetricInfo, OriginMetricData, FormattedMetricData, formatChartData, resolveMetricsRank } from '@src/constants/chartConfig';
MetricInfo, import ChartOperateBar, { KsHeaderOptions } from '../ChartOperateBar';
MetricDefaultChartDataType,
MetricChartDataType,
formatChartData,
resolveMetricsRank,
} from '@src/constants/chartConfig';
import SingleChartHeader, { KsHeaderOptions } from '../SingleChartHeader';
import DragGroup from '../DragGroup'; import DragGroup from '../DragGroup';
import ChartDetail from './ChartDetail'; import ChartDetail from './Detail';
import { getChartConfig } from './config'; import { getChartConfig, getMetricDashboardReq } from './config';
import './index.less'; import './index.less';
interface IcustomScope { interface IcustomScope {
@@ -33,7 +27,7 @@ const busInstance = new EventBus();
const DRAG_GROUP_GUTTER_NUM: [number, number] = [16, 16]; const DRAG_GROUP_GUTTER_NUM: [number, number] = [16, 16];
const DashboardDragChart = (props: PropsType): JSX.Element => { const DraggableCharts = (props: PropsType): JSX.Element => {
const [global] = AppContainer.useGlobalValue(); const [global] = AppContainer.useGlobalValue();
const { type: dashboardType } = props; const { type: dashboardType } = props;
const { clusterId } = useParams<{ const { clusterId } = useParams<{
@@ -44,7 +38,7 @@ const DashboardDragChart = (props: PropsType): JSX.Element => {
const [metricsList, setMetricsList] = useState<MetricInfo[]>([]); // 指标列表 const [metricsList, setMetricsList] = useState<MetricInfo[]>([]); // 指标列表
const [selectedMetricNames, setSelectedMetricNames] = useState<(string | number)[]>([]); // 默认选中的指标的列表 const [selectedMetricNames, setSelectedMetricNames] = useState<(string | number)[]>([]); // 默认选中的指标的列表
const [curHeaderOptions, setCurHeaderOptions] = useState<ChartFilterOptions>(); const [curHeaderOptions, setCurHeaderOptions] = useState<ChartFilterOptions>();
const [metricChartData, setMetricChartData] = useState<MetricChartDataType[]>([]); // 指标图表数据列表 const [metricChartData, setMetricChartData] = useState<FormattedMetricData[]>([]); // 指标图表数据列表
const [gridNum, setGridNum] = useState<number>(12); // 图表列布局 const [gridNum, setGridNum] = useState<number>(12); // 图表列布局
const metricRankList = useRef<string[]>([]); const metricRankList = useRef<string[]>([]);
const chartDetailRef = useRef(null); const chartDetailRef = useRef(null);
@@ -85,6 +79,9 @@ const DashboardDragChart = (props: PropsType): JSX.Element => {
updateRank([...supportMetrics]); updateRank([...supportMetrics]);
setMetricsList(supportMetrics); setMetricsList(supportMetrics);
setSelectedMetricNames(selectedMetrics); setSelectedMetricNames(selectedMetrics);
if (!selectedMetrics.length) {
setLoading(false);
}
}); });
}; };
@@ -106,16 +103,24 @@ const DashboardDragChart = (props: PropsType): JSX.Element => {
const curTimestamp = Date.now(); const curTimestamp = Date.now();
curFetchingTimestamp.current = curTimestamp; curFetchingTimestamp.current = curTimestamp;
Utils.post(api.getDashboardMetricChartData(clusterId, dashboardType), { const reqBody = Object.assign(
startTime, {
endTime, startTime,
metricsNames: selectedMetricNames, endTime,
topNu: curHeaderOptions?.scopeData?.isTop ? curHeaderOptions.scopeData.data : null, metricsNames: selectedMetricNames,
[dashboardType === MetricType.Broker ? 'brokerIds' : 'topics']: curHeaderOptions?.scopeData?.isTop topNu: curHeaderOptions?.scopeData?.isTop ? curHeaderOptions.scopeData.data : null,
? null },
: curHeaderOptions.scopeData.data, dashboardType === MetricType.Broker || dashboardType === MetricType.Topic
}).then( ? {
(res: MetricDefaultChartDataType[] | null) => { [dashboardType === MetricType.Broker ? 'brokerIds' : 'topics']: curHeaderOptions?.scopeData?.isTop
? null
: curHeaderOptions.scopeData.data,
}
: {}
);
Utils.post(getMetricDashboardReq(clusterId, dashboardType as any), reqBody).then(
(res: OriginMetricData[] | null) => {
// 如果当前请求不是最新请求,则不做任何操作 // 如果当前请求不是最新请求,则不做任何操作
if (curFetchingTimestamp.current !== curTimestamp) { if (curFetchingTimestamp.current !== curTimestamp) {
return; return;
@@ -131,7 +136,7 @@ const DashboardDragChart = (props: PropsType): JSX.Element => {
global.getMetricDefine || {}, global.getMetricDefine || {},
dashboardType, dashboardType,
curHeaderOptions.rangeTime curHeaderOptions.rangeTime
) as MetricChartDataType[]; ) as FormattedMetricData[];
// 指标排序 // 指标排序
formattedMetricData.sort((a, b) => metricRankList.current.indexOf(a.metricName) - metricRankList.current.indexOf(b.metricName)); formattedMetricData.sort((a, b) => metricRankList.current.indexOf(a.metricName) - metricRankList.current.indexOf(b.metricName));
@@ -164,7 +169,7 @@ const DashboardDragChart = (props: PropsType): JSX.Element => {
}; };
// 指标选中项更新回调 // 指标选中项更新回调
const indicatorChangeCallback = (newMetricNames: (string | number)[]) => { const metricSelectCallback = (newMetricNames: (string | number)[]) => {
const updateMetrics: { metric: string; set: boolean; rank: number }[] = []; const updateMetrics: { metric: string; set: boolean; rank: number }[] = [];
// 需要选中的指标 // 需要选中的指标
newMetricNames.forEach( newMetricNames.forEach(
@@ -218,7 +223,7 @@ const DashboardDragChart = (props: PropsType): JSX.Element => {
useEffect(() => { useEffect(() => {
// 初始化页面,获取 scope 和 metric 信息 // 初始化页面,获取 scope 和 metric 信息
getScopeList(); (dashboardType === MetricType.Broker || dashboardType === MetricType.Topic) && getScopeList();
getMetricList(); getMetricList();
setTimeout(() => observeDashboardWidthChange()); setTimeout(() => observeDashboardWidthChange());
@@ -226,19 +231,22 @@ const DashboardDragChart = (props: PropsType): JSX.Element => {
return ( return (
<div id="dashboard-drag-chart" className="topic-dashboard"> <div id="dashboard-drag-chart" className="topic-dashboard">
<SingleChartHeader <ChartOperateBar
onChange={ksHeaderChange} onChange={ksHeaderChange}
nodeScopeModule={{ nodeScopeModule={{
hasCustomScope: !(dashboardType === MetricType.Zookeeper),
customScopeList: scopeList, customScopeList: scopeList,
scopeName: dashboardType === MetricType.Broker ? 'Broker' : 'Topic', scopeName: dashboardType === MetricType.Broker ? 'Broker' : dashboardType === MetricType.Topic ? 'Topic' : 'Zookeeper',
scopeLabel: `自定义 ${dashboardType === MetricType.Broker ? 'Broker' : 'Topic'} 范围`, scopeLabel: `自定义 ${
dashboardType === MetricType.Broker ? 'Broker' : dashboardType === MetricType.Topic ? 'Topic' : 'Zookeeper'
} `,
}} }}
indicatorSelectModule={{ metricSelect={{
hide: false, hide: false,
metricType: dashboardType, metricType: dashboardType,
tableData: metricsList, tableData: metricsList,
selectedRows: selectedMetricNames, selectedRows: selectedMetricNames,
submitCallback: indicatorChangeCallback, submitCallback: metricSelectCallback,
}} }}
/> />
<div className="topic-dashboard-container"> <div className="topic-dashboard-container">
@@ -258,7 +266,7 @@ const DashboardDragChart = (props: PropsType): JSX.Element => {
}} }}
> >
{metricChartData.map((data) => { {metricChartData.map((data) => {
const { metricName, metricUnit, metricLines } = data; const { metricName, metricUnit, metricLines, showLegend } = data;
return ( return (
<div key={metricName} className="dashboard-drag-item-box"> <div key={metricName} className="dashboard-drag-item-box">
@@ -301,7 +309,7 @@ const DashboardDragChart = (props: PropsType): JSX.Element => {
eventBus={busInstance} eventBus={busInstance}
propChartData={metricLines} propChartData={metricLines}
optionMergeProps={{ replaceMerge: curHeaderOptions.isAutoReload ? ['xAxis'] : ['series'] }} optionMergeProps={{ replaceMerge: curHeaderOptions.isAutoReload ? ['xAxis'] : ['series'] }}
{...getChartConfig(`${metricName}{unit|${metricUnit}}`, metricLines.length)} {...getChartConfig(`${metricName}{unit|${metricUnit}}`, metricLines.length, showLegend)}
/> />
</div> </div>
); );
@@ -321,4 +329,4 @@ const DashboardDragChart = (props: PropsType): JSX.Element => {
); );
}; };
export default DashboardDragChart; export default DraggableCharts;

View File

@@ -12,30 +12,38 @@ export interface MetricInfo {
} }
// 接口返回图表原始数据类型 // 接口返回图表原始数据类型
export interface MetricDefaultChartDataType { export interface OriginMetricData {
metricName: string; metricName: string;
metricLines: { metricLines?: {
name: string; name: string;
createTime: number; createTime: number;
updateTime: number; updateTime: number;
metricPoints: { metricPoints: {
aggType: string; aggType: string;
timeStamp: number; timeStamp: number;
value: number; value: string;
createTime: number; createTime: number;
updateTime: number; updateTime: number;
}[]; }[];
}[]; }[];
metricPoints?: {
aggType: string;
name: string;
timeStamp: number;
value: string;
}[];
} }
// 格式化后图表数据类型 // 格式化后图表数据类型
export interface MetricChartDataType { export interface FormattedMetricData {
metricName: string; metricName: string;
metricUnit: string; metricUnit: string;
metricLines: { metricLines: {
name: string; name: string;
data: (string | number)[][]; data: (string | number)[][];
}[]; }[];
showLegend: boolean;
targetUnit: [string, number] | undefined;
} }
// 图表颜色库 // 图表颜色库
@@ -55,22 +63,38 @@ export const CHART_COLOR_LIST = [
'#C9E795', '#C9E795',
]; ];
// 图表存储单位换算 // 图表存储单位
export const UNIT_MAP = { export const MEMORY_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 TIME_MAP = {
// 图表数字单位换算 h: 1000 * 60 * 60,
export const DATA_NUMBER_MAP = { min: 1000 * 60,
s: 1000,
};
// 图表数字单位
export const NUM_MAP = {
十亿: Math.pow(1000, 3), 十亿: Math.pow(1000, 3),
百万: Math.pow(1000, 2), 百万: Math.pow(1000, 2),
: 1000, : 1000,
}; };
export const getDataNumberUnit = (value: number) => Object.entries(DATA_NUMBER_MAP).find(([, size]) => value / size >= 1) || ['', 1]; const calculateUnit = (map: { [unit: string]: number }, targetValue: number) => {
return Object.entries(map).find(([, size]) => targetValue / size >= 1);
};
const getMemoryUnit = (value: number) => calculateUnit(MEMORY_MAP, value) || ['Byte', 1];
const getTimeUnit = (value: number) => calculateUnit(TIME_MAP, value) || ['ms', 1];
const getNumUnit = (value: number) => calculateUnit(NUM_MAP, value) || ['', 1];
export const getDataUnit = {
Memory: getMemoryUnit,
Time: getTimeUnit,
Num: getNumUnit,
};
export type DataUnitType = keyof typeof getDataUnit;
// 图表补点间隔计算 // 图表补点间隔计算
export const SUPPLEMENTARY_INTERVAL_MAP = { export const SUPPLEMENTARY_INTERVAL_MAP = {
@@ -112,9 +136,9 @@ export const resolveMetricsRank = (metricList: MetricInfo[]) => {
}; };
}; };
// 补点 // 图表补点
export const supplementaryPoints = ( export const supplementaryPoints = (
lines: MetricChartDataType['metricLines'], lines: FormattedMetricData['metricLines'],
timeRange: readonly [number, number], timeRange: readonly [number, number],
extraCallback?: (point: [number, 0]) => any[] extraCallback?: (point: [number, 0]) => any[]
): void => { ): void => {
@@ -165,19 +189,39 @@ export const supplementaryPoints = (
// 格式化图表数据 // 格式化图表数据
export const formatChartData = ( export const formatChartData = (
// 图表源数据 // 图表源数据
metricData: MetricDefaultChartDataType[], metricsData: OriginMetricData[],
// 获取指标单位 // 获取指标单位
getMetricDefine: (type: MetricType, metric: string) => MetricsDefine[keyof MetricsDefine], getMetricDefine: (type: MetricType, metric: string) => MetricsDefine[keyof MetricsDefine],
// 指标类型 // 指标类型
metricType: MetricType, metricType: MetricType,
// 图表时间范围,用于补点 // 图表时间范围,用于补点
timeRange: readonly [number, number], timeRange: readonly [number, number],
transformUnit: [string, number] = undefined targetUnit: [string, number] = undefined
): MetricChartDataType[] => { ): FormattedMetricData[] => {
return metricData.map(({ metricName, metricLines }) => { return metricsData.map((originData) => {
const { metricName } = originData;
const curMetricInfo = (getMetricDefine && getMetricDefine(metricType, metricName)) || null; const curMetricInfo = (getMetricDefine && getMetricDefine(metricType, metricName)) || null;
const isByteUnit = curMetricInfo?.unit?.toLowerCase().includes('byte'); let showLegend = true;
let metricLines = [];
let maxValue = -1; let maxValue = -1;
let unitType: DataUnitType;
if (originData?.metricLines && originData?.metricLines !== null) {
metricLines = originData.metricLines;
} else {
showLegend = false;
metricLines = [
{
name: metricName,
metricPoints: originData.metricPoints,
},
];
}
{
const originUnit = curMetricInfo?.unit?.toLowerCase();
unitType = originUnit.includes('byte') ? 'Memory' : originUnit.includes('ms') ? 'Time' : 'Num';
}
const PointsMapMethod = ({ timeStamp, value }: { timeStamp: number; value: string | number }) => { const PointsMapMethod = ({ timeStamp, value }: { timeStamp: number; value: string | number }) => {
let parsedValue: string | number = Number(value); let parsedValue: string | number = Number(value);
@@ -194,7 +238,7 @@ export const formatChartData = (
}; };
// 初始化返回结构 // 初始化返回结构
const chartData = { const chartData: FormattedMetricData = {
metricName, metricName,
metricUnit: curMetricInfo?.unit || '', metricUnit: curMetricInfo?.unit || '',
metricLines: metricLines metricLines: metricLines
@@ -203,17 +247,21 @@ export const formatChartData = (
name, name,
data: metricPoints.map(PointsMapMethod), data: metricPoints.map(PointsMapMethod),
})), })),
showLegend,
targetUnit: undefined,
}; };
// 按时间先后进行对图表点排序 // 按时间先后进行对图表点排序
chartData.metricLines.forEach(({ data }) => data.sort((a, b) => (a[0] as number) - (b[0] as number))); chartData.metricLines.forEach(({ data }) => data.sort((a, b) => (a[0] as number) - (b[0] as number)));
// 图表值单位转换 // 图表值单位转换
if (maxValue > 0) { if (maxValue > 0) {
const [unitName, unitSize]: [string, number] = transformUnit || isByteUnit ? getUnit(maxValue) : getDataNumberUnit(maxValue); const [unitName, unitSize]: [string, number] = targetUnit || getDataUnit[unitType](maxValue);
chartData.metricUnit = isByteUnit chartData.targetUnit = [unitName, unitSize];
? chartData.metricUnit.toLowerCase().replace('byte', unitName) chartData.metricUnit =
: `${unitName}${chartData.metricUnit}`; unitType !== 'Num'
chartData.metricLines.forEach(({ data }) => data.forEach((point: any) => (point[1] /= unitSize))); ? chartData.metricUnit.toLowerCase().replace(unitType === 'Memory' ? 'byte' : 'ms', unitName)
: `${unitName}${chartData.metricUnit}`;
chartData.metricLines.forEach(({ data }) => data.forEach((point: any) => parseFloat((point[1] /= unitSize).toFixed(3))));
} }
// 补点 // 补点
@@ -231,7 +279,7 @@ const tooltipFormatter = (date: any, arr: any, tooltip: any) => {
<div style="display:flex;align-items:center;"> <div style="display:flex;align-items:center;">
<div style="margin-right:4px;width:8px;height:2px;background-color:${item.color};"></div> <div style="margin-right:4px;width:8px;height:2px;background-color:${item.color};"></div>
<div style="flex:1;display:flex;justify-content:space-between;align-items:center;overflow: hidden;"> <div style="flex:1;display:flex;justify-content:space-between;align-items:center;overflow: hidden;">
<span style="font-size:12px;color:#74788D;pointer-events:auto;margin-left:2px;line-height: 18px;font-family: HelveticaNeue;overflow: hidden; text-overflow: ellipsis; white-space: no-wrap;"> <span style="font-size:12px;color:#74788D;pointer-events:auto;margin-left:2px;line-height: 18px;font-family: HelveticaNeue;overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
${item.seriesName} ${item.seriesName}
</span> </span>
<span style="font-size:12px;color:#212529;line-height:18px;font-family:HelveticaNeue-Medium; padding-left: 6px;"> <span style="font-size:12px;color:#212529;line-height:18px;font-family:HelveticaNeue-Medium; padding-left: 6px;">
@@ -324,6 +372,7 @@ export const getBasicChartConfig = (props: any = {}) => {
tooltip: false, tooltip: false,
...legend, ...legend,
}, },
color: CHART_COLOR_LIST,
// 横坐标配置 // 横坐标配置
xAxis: { xAxis: {
type: 'category', type: 'category',

View File

@@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { MetricType } from '@src/api'; import { MetricType } from '@src/api';
import BrokerHealthCheck from '@src/components/CardBar/BrokerHealthCheck'; import BrokerHealthCheck from '@src/components/CardBar/BrokerHealthCheck';
import DashboardDragChart from '@src/components/DashboardDragChart'; import DraggableCharts from '@src/components/DraggableCharts';
import DBreadcrumb from 'knowdesign/es/extend/d-breadcrumb'; import DBreadcrumb from 'knowdesign/es/extend/d-breadcrumb';
import { AppContainer } from 'knowdesign'; import { AppContainer } from 'knowdesign';
@@ -19,7 +19,7 @@ const BrokerDashboard = (): JSX.Element => {
/> />
</div> </div>
<BrokerHealthCheck /> <BrokerHealthCheck />
<DashboardDragChart type={MetricType.Broker} /> <DraggableCharts type={MetricType.Broker} />
</> </>
); );
}; };

View File

@@ -1,4 +1,5 @@
import { AppContainer, Divider, Form, Input, List, message, Modal, Progress, Spin, Tooltip, Utils } from 'knowdesign'; import { AppContainer, Divider, Form, Input, List, Modal, Progress, Spin, Tooltip, Utils } from 'knowdesign';
import message from '@src/components/Message';
import { IconFont } from '@knowdesign/icons'; import { IconFont } from '@knowdesign/icons';
import moment from 'moment'; import moment from 'moment';
import API from '@src/api'; import API from '@src/api';
@@ -11,7 +12,7 @@ import { useIntl } from 'react-intl';
import api, { MetricType } from '@src/api'; import api, { MetricType } from '@src/api';
import { getHealthClassName, getHealthProcessColor, getHealthText } from '../SingleClusterDetail/config'; import { getHealthClassName, getHealthProcessColor, getHealthText } from '../SingleClusterDetail/config';
import { ClustersPermissionMap } from '../CommonConfig'; import { ClustersPermissionMap } from '../CommonConfig';
import { getUnit, getDataNumberUnit } from '@src/constants/chartConfig'; import { getDataUnit } from '@src/constants/chartConfig';
import SmallChart from '@src/components/SmallChart'; import SmallChart from '@src/components/SmallChart';
import { SearchParams } from './HomePage'; import { SearchParams } from './HomePage';
@@ -235,14 +236,14 @@ const ClusterList = (props: { searchParams: SearchParams; showAccessCluster: any
// 如果单位是 字节 ,进行单位换算 // 如果单位是 字节 ,进行单位换算
if (line.unit.toLowerCase().includes('byte')) { if (line.unit.toLowerCase().includes('byte')) {
const [unit, size] = getUnit(line.value); const [unit, size] = getDataUnit['Memory'](line.value);
line.value = Number((line.value / size).toFixed(2)); line.value = Number((line.value / size).toFixed(2));
line.unit = line.unit.toLowerCase().replace('byte', unit); line.unit = line.unit.toLowerCase().replace('byte', unit);
} }
// Messages 指标值特殊处理 // Messages 指标值特殊处理
if (line.metricName === 'LeaderMessages') { if (line.metricName === 'LeaderMessages') {
const [unit, size] = getDataNumberUnit(line.value); const [unit, size] = getDataUnit['Num'](line.value);
line.value = Number((line.value / size).toFixed(2)); line.value = Number((line.value / size).toFixed(2));
line.unit = unit + line.unit; line.unit = unit + line.unit;
} }

View File

@@ -14,7 +14,7 @@ const messagesInTooltipFormatter = (date: any, arr: any) => {
<div style="display:flex;align-items:center;"> <div style="display:flex;align-items:center;">
<div style="margin-right:4px;width:8px;height:2px;background-color:${params.color};"></div> <div style="margin-right:4px;width:8px;height:2px;background-color:${params.color};"></div>
<div style="flex:1;display:flex;justify-content:space-between;align-items:center;overflow: hidden;"> <div style="flex:1;display:flex;justify-content:space-between;align-items:center;overflow: hidden;">
<span style="flex: 1;font-size:12px;color:#74788D;pointer-events:auto;margin-left:2px;line-height: 18px;font-family: HelveticaNeue;overflow: hidden; text-overflow: ellipsis; white-space: no-wrap;"> <span style="flex: 1;font-size:12px;color:#74788D;pointer-events:auto;margin-left:2px;line-height: 18px;font-family: HelveticaNeue;overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
${params.seriesName} ${params.seriesName}
</span> </span>
<span style="font-size:12px;color:#212529;line-height:18px;font-family:HelveticaNeue-Medium;margin-left: 10px;"> <span style="font-size:12px;color:#212529;line-height:18px;font-family:HelveticaNeue-Medium;margin-left: 10px;">
@@ -55,7 +55,7 @@ const messagesInTooltipFormatter = (date: any, arr: any) => {
}; };
export const getChartConfig = (props: any) => { export const getChartConfig = (props: any) => {
const { metricName, lineColor, isDefaultMetric = false } = props; const { lineColor, isDefaultMetric = false } = props;
return { return {
option: getBasicChartConfig({ option: getBasicChartConfig({
// TODO: time 轴图表联动有问题,先切换为 category // TODO: time 轴图表联动有问题,先切换为 category
@@ -63,7 +63,6 @@ export const getChartConfig = (props: any) => {
title: { show: false }, title: { show: false },
legend: { show: false }, legend: { show: false },
grid: { top: 24, bottom: 12 }, grid: { top: 24, bottom: 12 },
lineColor: [lineColor],
tooltip: isDefaultMetric tooltip: isDefaultMetric
? { ? {
formatter: function (params: any) { formatter: function (params: any) {
@@ -87,6 +86,7 @@ export const getChartConfig = (props: any) => {
smooth: 0.25, smooth: 0.25,
symbol: 'emptyCircle', symbol: 'emptyCircle',
symbolSize: 4, symbolSize: 4,
color: '#556ee6',
// 面积图样式 // 面积图样式
areaStyle: { areaStyle: {
color: lineColor, color: lineColor,

View File

@@ -5,16 +5,16 @@ import { arrayMoveImmutable } from 'array-move';
import api from '@src/api'; import api from '@src/api';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { import {
MetricDefaultChartDataType, OriginMetricData,
MetricChartDataType, FormattedMetricData,
formatChartData, formatChartData,
supplementaryPoints, supplementaryPoints,
resolveMetricsRank, resolveMetricsRank,
MetricInfo, MetricInfo,
} from '@src/constants/chartConfig'; } from '@src/constants/chartConfig';
import { MetricType } from '@src/api'; import { MetricType } from '@src/api';
import { getDataNumberUnit, getUnit } from '@src/constants/chartConfig'; import { getDataUnit } from '@src/constants/chartConfig';
import SingleChartHeader, { KsHeaderOptions } from '@src/components/SingleChartHeader'; import ChartOperateBar, { KsHeaderOptions } from '@src/components/ChartOperateBar';
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 { getChartConfig } from './config';
@@ -162,13 +162,13 @@ const DetailChart = (props: { children: JSX.Element }): JSX.Element => {
metricsNames: selectedMetricNames.filter((name) => name !== DEFAULT_METRIC), metricsNames: selectedMetricNames.filter((name) => name !== DEFAULT_METRIC),
}, },
}).then( }).then(
(res: MetricDefaultChartDataType[]) => { (res: OriginMetricData[]) => {
// 如果当前请求不是最新请求,则不做任何操作 // 如果当前请求不是最新请求,则不做任何操作
if (curFetchingTimestamp.current.messagesIn !== curTimestamp) { if (curFetchingTimestamp.current.messagesIn !== curTimestamp) {
return; return;
} }
const formattedMetricData: MetricChartDataType[] = formatChartData( const formattedMetricData: FormattedMetricData[] = formatChartData(
res, res,
global.getMetricDefine || {}, global.getMetricDefine || {},
MetricType.Cluster, MetricType.Cluster,
@@ -224,7 +224,7 @@ const DetailChart = (props: { children: JSX.Element }): JSX.Element => {
let valueWithUnit = Number(value); let valueWithUnit = Number(value);
let unit = ((global.getMetricDefine && global.getMetricDefine(MetricType.Cluster, key)?.unit) || '') as string; let unit = ((global.getMetricDefine && global.getMetricDefine(MetricType.Cluster, key)?.unit) || '') as string;
if (unit.toLowerCase().includes('byte')) { if (unit.toLowerCase().includes('byte')) {
const [unitName, unitSize]: [string, number] = getUnit(Number(value)); const [unitName, unitSize]: [string, number] = getDataUnit['Memory'](Number(value));
unit = unit.toLowerCase().replace('byte', unitName); unit = unit.toLowerCase().replace('byte', unitName);
valueWithUnit /= unitSize; valueWithUnit /= unitSize;
} }
@@ -235,7 +235,7 @@ const DetailChart = (props: { children: JSX.Element }): JSX.Element => {
}; };
return returnValue; return returnValue;
}); });
return [timeStamp, values.MessagesIn || '0', valuesWithUnit] as [number, number | string, typeof valuesWithUnit]; return [timeStamp, parsedValue || '0', valuesWithUnit] as [number, number | string, typeof valuesWithUnit];
}); });
result.sort((a, b) => (a[0] as number) - (b[0] as number)); result.sort((a, b) => (a[0] as number) - (b[0] as number));
const line = { const line = {
@@ -244,9 +244,9 @@ const DetailChart = (props: { children: JSX.Element }): JSX.Element => {
data: result as any, data: result as any,
}; };
if (maxValue > 0) { if (maxValue > 0) {
const [unitName, unitSize]: [string, number] = getDataNumberUnit(maxValue); const [unitName, unitSize]: [string, number] = getDataUnit['Num'](maxValue);
line.unit = `${unitName}${line.unit}`; line.unit = `${unitName}${line.unit}`;
result.forEach((point) => ((point[1] as number) /= unitSize)); result.forEach((point) => parseFloat(((point[1] as number) /= unitSize).toFixed(3)));
} }
if (result.length) { if (result.length) {
@@ -308,11 +308,11 @@ const DetailChart = (props: { children: JSX.Element }): JSX.Element => {
return ( return (
<div className="chart-panel cluster-detail-container"> <div className="chart-panel cluster-detail-container">
<SingleChartHeader <ChartOperateBar
onChange={ksHeaderChange} onChange={ksHeaderChange}
hideNodeScope={true} hideNodeScope={true}
hideGridSelect={true} hideGridSelect={true}
indicatorSelectModule={{ metricSelect={{
hide: false, hide: false,
metricType: MetricType.Cluster, metricType: MetricType.Cluster,
tableData: metricList, tableData: metricList,

View File

@@ -1,5 +1,6 @@
/* eslint-disable react/display-name */ /* eslint-disable react/display-name */
import { Button, Divider, Drawer, Form, message, ProTable, Table, Utils } from 'knowdesign'; import { Button, Divider, Drawer, Form, ProTable, Table, Utils } from 'knowdesign';
import message from '@src/components/Message';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { useIntl } from 'react-intl'; import { useIntl } from 'react-intl';
import { getHealthySettingColumn } from './config'; import { getHealthySettingColumn } from './config';
@@ -34,8 +35,8 @@ const HealthySetting = React.forwardRef((props: any, ref): JSX.Element => {
item.configItem.indexOf('Group Re-Balance') > -1 item.configItem.indexOf('Group Re-Balance') > -1
? 'ReBalance' ? 'ReBalance'
: item.configItem.includes('副本未同步') : item.configItem.includes('副本未同步')
? 'UNDER_REPLICA' ? 'UNDER_REPLICA'
: item.configItem; : item.configItem;
values[`weight_${item.configItemName}`] = itemValue?.weight; values[`weight_${item.configItemName}`] = itemValue?.weight;
values[`value_${item.configItemName}`] = itemValue?.value; values[`value_${item.configItemName}`] = itemValue?.value;

View File

@@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { MetricType } from '@src/api'; import { MetricType } from '@src/api';
import TopicHealthCheck from '@src/components/CardBar/TopicHealthCheck'; import TopicHealthCheck from '@src/components/CardBar/TopicHealthCheck';
import DashboardDragChart from '@src/components/DashboardDragChart'; import DraggableCharts from '@src/components/DraggableCharts';
import { AppContainer } from 'knowdesign'; import { AppContainer } from 'knowdesign';
import DBreadcrumb from 'knowdesign/es/extend/d-breadcrumb'; import DBreadcrumb from 'knowdesign/es/extend/d-breadcrumb';
@@ -19,7 +19,7 @@ const TopicDashboard = () => {
/> />
</div> </div>
<TopicHealthCheck /> <TopicHealthCheck />
<DashboardDragChart type={MetricType.Topic} /> <DraggableCharts type={MetricType.Topic} />
</> </>
); );
}; };