fix: 图表展示 bugifx & 优化

This commit is contained in:
GraceWalk
2022-09-13 14:09:03 +08:00
parent 863b765e0d
commit 12b82c1395
9 changed files with 182 additions and 142 deletions

View File

@@ -46,30 +46,42 @@ export const supplementaryPoints = (
extraCallback?: (point: [number, 0]) => any[]
) => {
lines.forEach(({ data }) => {
// 获取未补点前线条的点的个数
let len = data.length;
for (let i = 0; i < len; i++) {
const timestamp = data[i][0] as number;
// 数组第一个点和最后一个点单独处理
// 记录当前处理到的点的下标值
let i = 0;
for (; i < len; i++) {
if (i === 0) {
let firstPointTimestamp = data[0][0] as number;
while (firstPointTimestamp - interval > timeRange[0]) {
const prePointTimestamp = firstPointTimestamp - interval;
data.unshift(extraCallback ? extraCallback([prePointTimestamp, 0]) : [prePointTimestamp, 0]);
const prevPointTimestamp = firstPointTimestamp - interval;
data.unshift(extraCallback ? extraCallback([prevPointTimestamp, 0]) : [prevPointTimestamp, 0]);
firstPointTimestamp = prevPointTimestamp;
len++;
i++;
firstPointTimestamp = prePointTimestamp;
}
}
if (i === len - 1) {
let lastPointTimestamp = data[len - 1][0] as number;
let lastPointTimestamp = data[i][0] as number;
while (lastPointTimestamp + interval < timeRange[1]) {
const next = lastPointTimestamp + interval;
data.push(extraCallback ? extraCallback([next, 0]) : [next, 0]);
lastPointTimestamp = next;
const nextPointTimestamp = lastPointTimestamp + interval;
data.push(extraCallback ? extraCallback([nextPointTimestamp, 0]) : [nextPointTimestamp, 0]);
lastPointTimestamp = nextPointTimestamp;
}
break;
}
{
let timestamp = data[i][0] as number;
while (timestamp + interval < data[i + 1][0]) {
const nextPointTimestamp = timestamp + interval;
data.splice(i + 1, 0, extraCallback ? extraCallback([nextPointTimestamp, 0]) : [nextPointTimestamp, 0]);
timestamp = nextPointTimestamp;
len++;
i++;
}
} else if (timestamp + interval < data[i + 1][0]) {
data.splice(i + 1, 0, extraCallback ? extraCallback([timestamp + interval, 0]) : [timestamp + interval, 0]);
len++;
}
}
});
@@ -135,18 +147,37 @@ export const formatChartData = (
};
const seriesCallback = (lines: { name: string; data: [number, string | number][] }[]) => {
const len = CHART_COLOR_LIST.length;
// series 配置
return lines.map((line) => {
return lines.map((line, i) => {
return {
...line,
lineStyle: {
width: 1.5,
},
connectNulls: false,
symbol: 'emptyCircle',
symbolSize: 4,
smooth: 0.25,
areaStyle: {
opacity: 0.02,
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: CHART_COLOR_LIST[i % len] + '10',
},
{
offset: 1,
color: 'rgba(255,255,255,0)', // 100% 处的颜色
},
],
global: false, // 缺省为 false
},
},
};
});
@@ -189,6 +220,7 @@ export const getDetailChartConfig = (title: string, sliderPos: readonly [number,
startValue: sliderPos[0],
endValue: sliderPos[1],
zoomOnMouseWheel: false,
minValueSpan: 10 * 60 * 1000,
},
{
start: 0,

View File

@@ -63,56 +63,63 @@
}
}
}
.chart-detail-modal-container {
position: relative;
.expand-icon-box {
position: absolute;
z-index: 1000;
top: 14px;
right: 44px;
width: 24px;
height: 24px;
cursor: pointer;
font-size: 16px;
text-align: center;
border-radius: 50%;
transition: background-color 0.3s ease;
.expand-icon {
color: #adb5bc;
line-height: 24px;
}
&:hover {
background: rgba(33, 37, 41, 0.04);
.expand-icon {
color: #74788d;
.overview-chart-detail-drawer {
.dcloud-spin-nested-loading > div > .dcloud-spin.dcloud-spin-spinning {
height: 300px;
}
&.dcloud-drawer .dcloud-drawer-body {
padding: 0 20px;
}
.detail-header {
display: flex;
align-items: flex-end;
font-weight: normal;
.title {
font-family: @font-family-bold;
font-size: 18px;
color: #495057;
letter-spacing: 0;
.unit {
font-family: @font-family-bold;
font-size: 14px;
letter-spacing: 0.5px;
}
}
.slider-info {
margin-left: 10px;
font-size: 12px;
font-family: @font-family;
color: #303a51;
}
}
.detail-title {
display: flex;
justify-content: space-between;
align-items: center;
.left {
display: flex;
align-items: flex-end;
.title {
font-family: @font-family-bold;
font-size: 18px;
color: #495057;
letter-spacing: 0;
.unit {
font-family: @font-family-bold;
font-size: 14px;
letter-spacing: 0.5px;
.chart-detail-modal-container {
position: relative;
overflow: hidden;
.expand-icon-box {
position: absolute;
z-index: 1000;
top: 14px;
right: 44px;
width: 24px;
height: 24px;
cursor: pointer;
font-size: 16px;
text-align: center;
border-radius: 50%;
transition: background-color 0.3s ease;
.expand-icon {
color: #adb5bc;
line-height: 24px;
}
&:hover {
background: rgba(33, 37, 41, 0.04);
.expand-icon {
color: #74788d;
}
}
.info {
margin-left: 10px;
}
}
.detail-table {
margin-top: 16px;
}
}
.detail-table {
margin-top: 16px;
}
}

View File

@@ -216,8 +216,8 @@ const DashboardDragChart = (props: PropsType): JSX.Element => {
onChange={ksHeaderChange}
nodeScopeModule={{
customScopeList: scopeList,
scopeName: `自定义 ${dashboardType === MetricType.Broker ? 'Broker' : 'Topic'} 范围`,
showSearch: dashboardType === MetricType.Topic,
scopeName: dashboardType === MetricType.Broker ? 'Broker' : 'Topic',
scopeLabel: `自定义 ${dashboardType === MetricType.Broker ? 'Broker' : 'Topic'} 范围`,
}}
indicatorSelectModule={{
hide: false,

View File

@@ -26,8 +26,8 @@ const OptionsDefault = [
const NodeScope = ({ nodeScopeModule, change }: propsType) => {
const {
customScopeList: customList,
scopeName = '自定义节点范围',
showSearch = false,
scopeName = '',
scopeLabel = '自定义范围',
searchPlaceholder = '输入内容进行搜索',
} = nodeScopeModule;
const [topNum, setTopNum] = useState<number>(5);
@@ -70,7 +70,7 @@ const NodeScope = ({ nodeScopeModule, change }: propsType) => {
change(checkedListTemp, false);
setIsTop(false);
setTopNum(null);
setInputValue(`已选${checkedListTemp?.length}`);
setInputValue(`${checkedListTemp?.length}`);
setPopVisible(false);
}
};
@@ -109,7 +109,7 @@ const NodeScope = ({ nodeScopeModule, change }: propsType) => {
{/* <span>时间:</span> */}
<div className="flx_con">
<div className="flx_l">
<h6 className="time_title">top</h6>
<h6 className="time_title"> top </h6>
<Radio.Group
optionType="button"
buttonStyle="solid"
@@ -128,7 +128,7 @@ const NodeScope = ({ nodeScopeModule, change }: propsType) => {
</Radio.Group>
</div>
<div className="flx_r">
<h6 className="time_title">{scopeName}</h6>
<h6 className="time_title">{scopeLabel}</h6>
<div className="custom-scope">
<div className="check-row">
<Checkbox className="check-all" indeterminate={indeterminate} onChange={onCheckAllChange} checked={checkAll}>
@@ -136,9 +136,7 @@ const NodeScope = ({ nodeScopeModule, change }: propsType) => {
</Checkbox>
<Input
className="search-input"
suffix={
<IconFont type="icon-fangdajing" style={{ fontSize: '16px' }} />
}
suffix={<IconFont type="icon-fangdajing" style={{ fontSize: '16px' }} />}
size="small"
placeholder={searchPlaceholder}
onChange={(e) => setScopeSearchValue(e.target.value)}
@@ -148,7 +146,7 @@ const NodeScope = ({ nodeScopeModule, change }: propsType) => {
<Checkbox.Group style={{ width: '100%' }} onChange={checkChange} value={checkedListTemp}>
<Row gutter={[10, 12]}>
{customList
.filter((item) => !showSearch || item.label.includes(scopeSearchValue))
.filter((item) => item.label.includes(scopeSearchValue))
.map((item) => (
<Col span={12} key={item.value}>
<Checkbox value={item.value}>{item.label}</Checkbox>
@@ -180,6 +178,7 @@ const NodeScope = ({ nodeScopeModule, change }: propsType) => {
return (
<>
<div id="d-node-scope">
<div className="scope-title">{scopeName}</div>
<Popover
trigger={['click']}
visible={popVisible}

View File

@@ -1,5 +1,5 @@
import React, { useEffect, useState } from 'react';
import { Tooltip, Select, IconFont, Utils, Divider } from 'knowdesign';
import { Tooltip, Select, IconFont, Utils, Divider, Button } from 'knowdesign';
import moment from 'moment';
import { DRangeTime } from 'knowdesign';
import IndicatorDrawer from './IndicatorDrawer';
@@ -48,7 +48,7 @@ export interface IcustomScope {
export interface InodeScopeModule {
customScopeList: IcustomScope[];
scopeName?: string;
showSearch?: boolean;
scopeLabel?: string;
searchPlaceholder?: string;
change?: () => void;
}
@@ -138,9 +138,13 @@ const SingleChartHeader = ({
};
const reloadRangeTime = () => {
const timeLen = rangeTime[1] - rangeTime[0] || 0;
const curTimeStamp = moment().valueOf();
setRangeTime([curTimeStamp - timeLen, curTimeStamp]);
if (isRelativeRangeTime) {
const timeLen = rangeTime[1] - rangeTime[0] || 0;
const curTimeStamp = moment().valueOf();
setRangeTime([curTimeStamp - timeLen, curTimeStamp]);
} else {
setRangeTime([...rangeTime]);
}
};
const openIndicatorDrawer = () => {
@@ -174,12 +178,10 @@ const SingleChartHeader = ({
{!hideGridSelect && (
<Select className="grid-select" style={{ width: 70 }} value={gridNum} options={GRID_SIZE_OPTIONS} onChange={sizeChange} />
)}
<Divider type="vertical" style={{ height: 20, top: 0 }} />
<Tooltip title="点击指标筛选,可选择指标" placement="bottomRight">
<div className="icon-box" onClick={openIndicatorDrawer}>
<IconFont className="icon" type="icon-shezhi1" />
</div>
</Tooltip>
{(!hideNodeScope || !hideGridSelect) && <Divider type="vertical" style={{ height: 20, top: 0 }} />}
<Button type="primary" onClick={openIndicatorDrawer}>
</Button>
</div>
</div>
</div>

View File

@@ -3,8 +3,13 @@
@import '~knowdesign/es/basic/style/mixins/index';
#d-node-scope {
display: flex;
align-items: center;
position: relative;
display: inline-block;
.scope-title {
font-size: 14px;
color: #74788d;
}
.input-span {
cursor: pointer;
}
@@ -29,10 +34,10 @@
box-shadow: none;
}
&.relativeTime {
width: 160px;
width: 200px;
}
&.absoluteTime {
width: 300px;
width: 200px;
}
input {

View File

@@ -1,29 +1,19 @@
import moment from 'moment';
export const CHART_COLOR_LIST = [
'#657DFC',
'#A7B1EB',
'#2AC8E4',
'#9DDEEB',
'#3991FF',
'#556ee6',
'#94BEF2',
'#95e7ff',
'#9DDEEB',
'#A7B1EB',
'#C2D0E3',
'#F5B6B3',
'#85C80D',
'#C9E795',
'#A76CEC',
'#CCABF1',
'#FF9C1B',
'#F5C993',
'#FFC300',
'#F9D77B',
'#12CA7A',
'#8BA3C4',
'#FF7066',
'#F5C993',
'#A7E6C7',
'#F19FC9',
'#AEAEAE',
'#D1D1D1',
'#F5B6B3',
'#C9E795',
];
export const UNIT_MAP = {

View File

@@ -38,15 +38,17 @@
&-main {
.header-chart-container {
&-loading {
display: flex;
justify-content: center;
align-items: center;
}
width: 100%;
height: 244px;
margin-bottom: 12px;
.cluster-container-border();
.dcloud-spin.dcloud-spin-spinning {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 244px;
}
}
.content {

View File

@@ -14,6 +14,7 @@ import { MetricType } from '@src/api';
import { getDataNumberUnit, getUnit } from '@src/constants/chartConfig';
import SingleChartHeader, { KsHeaderOptions } from '@src/components/SingleChartHeader';
import { MAX_TIME_RANGE_WITH_SMALL_POINT_INTERVAL } from '@src/constants/common';
import RenderEmpty from '@src/components/RenderEmpty';
type ChartFilterOptions = Omit<KsHeaderOptions, 'gridNum'>;
interface MetricInfo {
@@ -64,7 +65,7 @@ const DetailChart = (props: { children: JSX.Element }): JSX.Element => {
const [messagesInMetricData, setMessagesInMetricData] = useState<MessagesInMetric>({
name: 'MessagesIn',
unit: '',
data: [],
data: undefined,
});
const [curHeaderOptions, setCurHeaderOptions] = useState<ChartFilterOptions>();
const [defaultChartLoading, setDefaultChartLoading] = useState<boolean>(true);
@@ -234,17 +235,19 @@ const DetailChart = (props: { children: JSX.Element }): JSX.Element => {
result.forEach((point) => ((point[1] as number) /= unitSize));
}
// 补充缺少的图表点
const extraMetrics = result[0][2].map((info) => ({
...info,
value: 0,
}));
const supplementaryInterval =
(curHeaderOptions.rangeTime[1] - curHeaderOptions.rangeTime[0] > MAX_TIME_RANGE_WITH_SMALL_POINT_INTERVAL ? 10 : 1) * 60 * 1000;
supplementaryPoints([line], curHeaderOptions.rangeTime, supplementaryInterval, (point) => {
point.push(extraMetrics as any);
return point;
});
if (result.length) {
// 补充缺少的图表点
const extraMetrics = result[0][2].map((info) => ({
...info,
value: 0,
}));
const supplementaryInterval =
(curHeaderOptions.rangeTime[1] - curHeaderOptions.rangeTime[0] > MAX_TIME_RANGE_WITH_SMALL_POINT_INTERVAL ? 10 : 1) * 60 * 1000;
supplementaryPoints([line], curHeaderOptions.rangeTime, supplementaryInterval, (point) => {
point.push(extraMetrics as any);
return point;
});
}
setMessagesInMetricData(line);
setDefaultChartLoading(false);
@@ -299,10 +302,9 @@ const DetailChart = (props: { children: JSX.Element }): JSX.Element => {
<div className="cluster-detail-container-main">
{/* MessageIn 图表 */}
<div className={`header-chart-container ${!messagesInMetricData.data.length ? 'header-chart-container-loading' : ''}`}>
<div className="header-chart-container">
<Spin spinning={defaultChartLoading}>
{/* TODO: 暂时通过判断是否有图表数据来修复,有时间可以查找下宽度溢出的原因 */}
{messagesInMetricData.data.length ? (
{messagesInMetricData.data && (
<>
<div className="chart-box-title">
<Tooltip
@@ -322,26 +324,27 @@ const DetailChart = (props: { children: JSX.Element }): JSX.Element => {
</span>
</Tooltip>
</div>
<SingleChart
chartKey="messagesIn"
chartTypeProp="line"
showHeader={false}
wrapStyle={{
width: 'auto',
height: 210,
}}
connectEventName="clusterChart"
eventBus={busInstance}
propChartData={[messagesInMetricData]}
{...getChartConfig({
// metricName: `${messagesInMetricData.name}{unit|${messagesInMetricData.unit}}`,
lineColor: CHART_LINE_COLORS[0],
isDefaultMetric: true,
})}
/>
{messagesInMetricData.data.length ? (
<SingleChart
chartKey="messagesIn"
chartTypeProp="line"
showHeader={false}
wrapStyle={{
width: 'auto',
height: 210,
}}
connectEventName="clusterChart"
eventBus={busInstance}
propChartData={[messagesInMetricData]}
{...getChartConfig({
lineColor: CHART_LINE_COLORS[0],
isDefaultMetric: true,
})}
/>
) : (
!defaultChartLoading && <RenderEmpty message="暂无数据" height={200} />
)}
</>
) : (
''
)}
</Spin>
</div>
@@ -408,7 +411,7 @@ const DetailChart = (props: { children: JSX.Element }): JSX.Element => {
) : chartLoading ? (
<></>
) : (
<Empty description="请先选择指标或刷新" style={{ width: '100%', height: '100%' }} />
<RenderEmpty message="请先选择指标或刷新" />
)}
</Row>
</Spin>