fix: chart resize
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { throttle } from 'echarts/core';
|
import { throttle } from 'lodash';
|
||||||
import { useCallback, useEffect, useRef } from 'react';
|
import { useCallback, useEffect, useRef } from 'react';
|
||||||
import echarts, { ECOption } from '.';
|
import echarts, { ECOption } from '.';
|
||||||
|
|
||||||
@@ -9,6 +9,8 @@ const Chart: React.FC<{
|
|||||||
}> = ({ options, width, height }) => {
|
}> = ({ options, width, height }) => {
|
||||||
const container = useRef<HTMLDivElement>(null);
|
const container = useRef<HTMLDivElement>(null);
|
||||||
const chart = useRef<echarts.EChartsType>();
|
const chart = useRef<echarts.EChartsType>();
|
||||||
|
const resizeable = useRef(false);
|
||||||
|
const resizeObserver = useRef<ResizeObserver>();
|
||||||
|
|
||||||
const init = useCallback(() => {
|
const init = useCallback(() => {
|
||||||
if (container.current) {
|
if (container.current) {
|
||||||
@@ -30,27 +32,48 @@ const Chart: React.FC<{
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
init();
|
if (container.current) {
|
||||||
|
init();
|
||||||
|
}
|
||||||
return () => {
|
return () => {
|
||||||
chart.current?.dispose();
|
chart.current?.dispose();
|
||||||
};
|
};
|
||||||
}, [init]);
|
}, [init]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
resizeable.current = false;
|
||||||
resize();
|
resize();
|
||||||
setOption(options);
|
setOption(options);
|
||||||
}, [options]);
|
}, [options]);
|
||||||
|
|
||||||
// resize on window resize
|
useEffect(() => {
|
||||||
|
let timer: any = null;
|
||||||
|
timer = setTimeout(() => {
|
||||||
|
resizeable.current = true;
|
||||||
|
}, 300);
|
||||||
|
return () => {
|
||||||
|
clearTimeout(timer);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleResize = throttle(() => {
|
const handleResize = throttle(() => {
|
||||||
resize();
|
if (resizeable.current) {
|
||||||
|
chart.current?.resize();
|
||||||
|
}
|
||||||
}, 100);
|
}, 100);
|
||||||
window.addEventListener('resize', handleResize);
|
|
||||||
|
if (container.current) {
|
||||||
|
resizeObserver.current = new ResizeObserver(handleResize);
|
||||||
|
|
||||||
|
resizeObserver.current.observe(container.current);
|
||||||
|
}
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener('resize', handleResize);
|
if (container.current) {
|
||||||
|
resizeObserver.current?.unobserve(container.current);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}, [resize]);
|
}, []);
|
||||||
|
|
||||||
return <div ref={container} style={{ width: width, height }}></div>;
|
return <div ref={container} style={{ width: width, height }}></div>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import type {
|
import type {
|
||||||
// 系列类型的定义后缀都为 SeriesOption
|
|
||||||
BarSeriesOption,
|
BarSeriesOption,
|
||||||
GaugeSeriesOption,
|
GaugeSeriesOption,
|
||||||
LineSeriesOption
|
LineSeriesOption
|
||||||
@@ -8,18 +7,16 @@ import { BarChart, GaugeChart, LineChart } from 'echarts/charts';
|
|||||||
import type {
|
import type {
|
||||||
DatasetComponentOption,
|
DatasetComponentOption,
|
||||||
GridComponentOption,
|
GridComponentOption,
|
||||||
// 组件类型的定义后缀都为 ComponentOption
|
|
||||||
TitleComponentOption,
|
TitleComponentOption,
|
||||||
TooltipComponentOption
|
TooltipComponentOption
|
||||||
} from 'echarts/components';
|
} from 'echarts/components';
|
||||||
import {
|
import {
|
||||||
// 数据集组件
|
|
||||||
DatasetComponent,
|
DatasetComponent,
|
||||||
GridComponent,
|
GridComponent,
|
||||||
LegendComponent,
|
LegendComponent,
|
||||||
TitleComponent,
|
TitleComponent,
|
||||||
TooltipComponent,
|
TooltipComponent,
|
||||||
// 内置数据转换器组件 (filter, sort)
|
// (filter, sort)
|
||||||
TransformComponent
|
TransformComponent
|
||||||
} from 'echarts/components';
|
} from 'echarts/components';
|
||||||
import type { ComposeOption } from 'echarts/core';
|
import type { ComposeOption } from 'echarts/core';
|
||||||
@@ -27,7 +24,6 @@ import * as echarts from 'echarts/core';
|
|||||||
import { LabelLayout, UniversalTransition } from 'echarts/features';
|
import { LabelLayout, UniversalTransition } from 'echarts/features';
|
||||||
import { CanvasRenderer } from 'echarts/renderers';
|
import { CanvasRenderer } from 'echarts/renderers';
|
||||||
|
|
||||||
// 通过 ComposeOption 来组合出一个只有必须组件和图表的 Option 类型
|
|
||||||
type ECOption = ComposeOption<
|
type ECOption = ComposeOption<
|
||||||
| BarSeriesOption
|
| BarSeriesOption
|
||||||
| LineSeriesOption
|
| LineSeriesOption
|
||||||
@@ -38,7 +34,7 @@ type ECOption = ComposeOption<
|
|||||||
| GaugeSeriesOption
|
| GaugeSeriesOption
|
||||||
>;
|
>;
|
||||||
|
|
||||||
// 注册必须的组件
|
// register components and charts
|
||||||
echarts.use([
|
echarts.use([
|
||||||
LegendComponent,
|
LegendComponent,
|
||||||
TitleComponent,
|
TitleComponent,
|
||||||
|
|||||||
@@ -49,16 +49,15 @@ export default function useOverlayScroller(options?: any) {
|
|||||||
scrollEventElement.current =
|
scrollEventElement.current =
|
||||||
instanceRef.current?.elements()?.scrollEventElement;
|
instanceRef.current?.elements()?.scrollEventElement;
|
||||||
|
|
||||||
const throttledScroll = React.useMemo(
|
const throttledScroll = React.useCallback(
|
||||||
() =>
|
throttle(() => {
|
||||||
throttle(() => {
|
scrollEventElement.current?.scrollTo?.({
|
||||||
scrollEventElement.current?.scrollTo?.({
|
top: scrollEventElement.current.scrollHeight,
|
||||||
top: scrollEventElement.current.scrollHeight,
|
behavior: 'smooth'
|
||||||
behavior: 'smooth'
|
});
|
||||||
});
|
instanceRef.current?.update?.();
|
||||||
instanceRef.current?.update?.();
|
}, 100),
|
||||||
}, 300),
|
[(scrollEventElement.current, instanceRef.current)]
|
||||||
[scrollEventElement.current, instanceRef.current]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const scrollauto = React.useCallback(() => {
|
const scrollauto = React.useCallback(() => {
|
||||||
|
|||||||
@@ -37,78 +37,6 @@ const TypeKeyMap = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const option = {
|
|
||||||
title: {
|
|
||||||
text: ''
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
itemWidth: 8,
|
|
||||||
itemHeight: 8,
|
|
||||||
data: []
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
left: 0,
|
|
||||||
right: 20,
|
|
||||||
bottom: 20,
|
|
||||||
containLabel: true
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
trigger: 'axis',
|
|
||||||
formatter(params: any) {
|
|
||||||
let result = `<span class="tooltip-x-name">${params[0].axisValue}</span>`;
|
|
||||||
params.forEach((item: any) => {
|
|
||||||
result += `<span class="tooltip-item">
|
|
||||||
<span class="tooltip-item-name">
|
|
||||||
<span style="display:inline-block;margin-right:5px;border-radius:8px;width:8px;height:8px;background-color:${item.color};"></span>
|
|
||||||
<span class="tooltip-title">${item.seriesName}</span>:
|
|
||||||
</span>
|
|
||||||
<span class="tooltip-value">${item.data.value}</span>
|
|
||||||
</span>`;
|
|
||||||
});
|
|
||||||
return `<div class="tooltip-wrapper">${result}</div>`;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
xAxis: {
|
|
||||||
type: 'category',
|
|
||||||
boundaryGap: true,
|
|
||||||
axisTick: {
|
|
||||||
show: true,
|
|
||||||
lineStyle: {
|
|
||||||
color: chartColorMap.tickLineColor
|
|
||||||
}
|
|
||||||
},
|
|
||||||
axisLabel: {
|
|
||||||
color: chartColorMap.axislabelColor,
|
|
||||||
// fontFamily: 'unset',
|
|
||||||
fontSize: 12
|
|
||||||
},
|
|
||||||
axisLine: {
|
|
||||||
show: false
|
|
||||||
},
|
|
||||||
data: []
|
|
||||||
},
|
|
||||||
yAxis: {
|
|
||||||
max: 100,
|
|
||||||
min: 0,
|
|
||||||
splitLine: {
|
|
||||||
show: true,
|
|
||||||
lineStyle: {
|
|
||||||
type: 'dashed'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
axisLabel: {
|
|
||||||
color: chartColorMap.axislabelColor,
|
|
||||||
// fontFamily: 'unset',
|
|
||||||
fontSize: 12
|
|
||||||
},
|
|
||||||
axisTick: {
|
|
||||||
show: false
|
|
||||||
},
|
|
||||||
type: 'value'
|
|
||||||
},
|
|
||||||
series: []
|
|
||||||
};
|
|
||||||
|
|
||||||
const UtilizationOvertime: React.FC = () => {
|
const UtilizationOvertime: React.FC = () => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const data = useContext(DashboardContext)?.system_load?.history || {};
|
const data = useContext(DashboardContext)?.system_load?.history || {};
|
||||||
|
|||||||
@@ -353,6 +353,7 @@ const MessageInput: React.FC<MessageInputProps> = ({
|
|||||||
type="text"
|
type="text"
|
||||||
icon={<ClearOutlined />}
|
icon={<ClearOutlined />}
|
||||||
size="middle"
|
size="middle"
|
||||||
|
disabled={loading}
|
||||||
onClick={handleClearAll}
|
onClick={handleClearAll}
|
||||||
></Button>
|
></Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|||||||
@@ -217,7 +217,6 @@ const ModelItem: React.FC<ModelItemProps> = forwardRef(
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleApplyToAllModels = (e: any) => {
|
const handleApplyToAllModels = (e: any) => {
|
||||||
console.log('checkbox change:', e.target.checked);
|
|
||||||
isApplyToAllModels.current = e.target.checked;
|
isApplyToAllModels.current = e.target.checked;
|
||||||
if (e.target.checked) {
|
if (e.target.checked) {
|
||||||
setGlobalParams({
|
setGlobalParams({
|
||||||
@@ -334,7 +333,6 @@ const ModelItem: React.FC<ModelItemProps> = forwardRef(
|
|||||||
}, [modelList, intl]);
|
}, [modelList, intl]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('globalParams:', globalParams.model, globalParams);
|
|
||||||
setParams({
|
setParams({
|
||||||
...params,
|
...params,
|
||||||
model: model,
|
model: model,
|
||||||
@@ -384,7 +382,7 @@ const ModelItem: React.FC<ModelItemProps> = forwardRef(
|
|||||||
<div className="header">
|
<div className="header">
|
||||||
<span className="title">
|
<span className="title">
|
||||||
<Select
|
<Select
|
||||||
style={{ minWidth: '120px', maxWidth: '200px' }}
|
style={{ minWidth: '120px', maxWidth: '180px' }}
|
||||||
variant="borderless"
|
variant="borderless"
|
||||||
options={modelFullList}
|
options={modelFullList}
|
||||||
onChange={handleModelChange}
|
onChange={handleModelChange}
|
||||||
@@ -404,7 +402,7 @@ const ModelItem: React.FC<ModelItemProps> = forwardRef(
|
|||||||
}}
|
}}
|
||||||
></Select>
|
></Select>
|
||||||
</span>
|
</span>
|
||||||
<ReferenceParams usage={tokenResult}></ReferenceParams>
|
<ReferenceParams usage={tokenResult} scaleable></ReferenceParams>
|
||||||
<span className="action">
|
<span className="action">
|
||||||
<Dropdown
|
<Dropdown
|
||||||
menu={{
|
menu={{
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import { WarningOutlined } from '@ant-design/icons';
|
import { WarningOutlined } from '@ant-design/icons';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Alert, Space, Tooltip } from 'antd';
|
import { Alert, Space, Tooltip } from 'antd';
|
||||||
|
import classNames from 'classnames';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import '../style/reference-params.less';
|
import '../style/reference-params.less';
|
||||||
|
|
||||||
interface ReferenceParamsProps {
|
interface ReferenceParamsProps {
|
||||||
showOutput?: boolean;
|
showOutput?: boolean;
|
||||||
|
scaleable?: boolean;
|
||||||
usage: {
|
usage: {
|
||||||
error?: boolean;
|
error?: boolean;
|
||||||
errorMessage?: string;
|
errorMessage?: string;
|
||||||
@@ -20,7 +22,7 @@ interface ReferenceParamsProps {
|
|||||||
|
|
||||||
const ReferenceParams = (props: ReferenceParamsProps) => {
|
const ReferenceParams = (props: ReferenceParamsProps) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const { usage, showOutput = true } = props;
|
const { usage, showOutput = true, scaleable } = props;
|
||||||
if (!usage || _.isEmpty(usage)) {
|
if (!usage || _.isEmpty(usage)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -41,8 +43,16 @@ const ReferenceParams = (props: ReferenceParamsProps) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="reference-params">
|
<div
|
||||||
<span className="usage">
|
className={classNames('reference-params', {
|
||||||
|
scaleable: scaleable
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className={classNames('usage', {
|
||||||
|
scaleable: scaleable
|
||||||
|
})}
|
||||||
|
>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={
|
title={
|
||||||
<Space>
|
<Space>
|
||||||
@@ -65,7 +75,11 @@ const ReferenceParams = (props: ReferenceParamsProps) => {
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
{showOutput && (
|
{showOutput && (
|
||||||
<span className="usage">
|
<span
|
||||||
|
className={classNames('usage', {
|
||||||
|
scaleable: scaleable
|
||||||
|
})}
|
||||||
|
>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={
|
title={
|
||||||
<Space>
|
<Space>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
border: 1px solid var(--ant-color-border);
|
border: 1px solid var(--ant-color-border);
|
||||||
border-radius: var(--border-radius-base);
|
border-radius: var(--border-radius-base);
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
background-color: var(--color-white-1);
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
padding-inline: 2px 16px;
|
padding-inline: 2px 16px;
|
||||||
|
|||||||
@@ -6,7 +6,16 @@
|
|||||||
color: var(--ant-orange);
|
color: var(--ant-orange);
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
|
|
||||||
|
&.scaleable {
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
.usage {
|
.usage {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
line-height: 16px;
|
||||||
|
|
||||||
|
&.scaleable {
|
||||||
|
transform: scale(0.9);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user