From 50ad074a8899ee1d6820e85aa53e0ff13ebdc2cb Mon Sep 17 00:00:00 2001 From: yxh01132861 Date: Mon, 31 Jul 2023 10:48:43 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20minChart=20=E5=9B=BE=E8=A1=A8=E4=B8=BB?= =?UTF-8?q?=E9=A2=98=E8=89=B2=E5=8F=AF=E9=80=89=E6=8B=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Component/Chart/IntervalLine.tsx | 15 +- .../widgets/MiniChart/Component/Chart/Pie.tsx | 18 +- .../MiniChart/Component/Chart/helper.ts | 407 ++++++++++++++++++ .../MiniChart/Component/Chart/index.tsx | 4 + .../src/widgets/MiniChart/Component/index.tsx | 2 + .../src/widgets/MiniChart/demos/config.ts | 1 + .../widgets/MiniChart/register-form/index.tsx | 19 +- 7 files changed, 458 insertions(+), 8 deletions(-) diff --git a/packages/li-analysis-assets/src/widgets/MiniChart/Component/Chart/IntervalLine.tsx b/packages/li-analysis-assets/src/widgets/MiniChart/Component/Chart/IntervalLine.tsx index e6e36b4d..8ee6e070 100644 --- a/packages/li-analysis-assets/src/widgets/MiniChart/Component/Chart/IntervalLine.tsx +++ b/packages/li-analysis-assets/src/widgets/MiniChart/Component/Chart/IntervalLine.tsx @@ -2,12 +2,13 @@ import { Chart } from '@antv/g2'; import classNames from 'classnames'; import { isEmpty } from 'lodash-es'; import React, { useEffect, useRef } from 'react'; -import { formatNumber, numberFormatThousandsSeparator } from './helper'; +import { formatNumber, getChartTheme, numberFormatThousandsSeparator } from './helper'; type Props = { className?: string; width: number; height: number; + theme: 'classic' | 'classicDark'; data: Record[]; xField: string; yField: string; @@ -21,6 +22,7 @@ const IntervalLine = ({ height, width, data = [], + theme = 'classicDark', xField, yField, showLegend = false, @@ -76,7 +78,7 @@ const IntervalLine = ({ if (!plotRef.current) { const chart = new Chart({ container: containerRef.current!, - theme: 'classicDark', + theme: theme, autoFit: true, style: { viewFill: 'transparent' }, padding: 'auto', @@ -109,6 +111,15 @@ const IntervalLine = ({ plotRef.current?.render(); }, [data, xField, yField, showLegend, type, isCount]); + useEffect(() => { + if (theme && plotRef.current) { + const themeCfg = getChartTheme(theme) as Record; + plotRef.current.theme(themeCfg); + + plotRef.current.render(); + } + }, [theme]); + useEffect(() => { if (height && width && plotRef.current) { plotRef.current.forceFit(); diff --git a/packages/li-analysis-assets/src/widgets/MiniChart/Component/Chart/Pie.tsx b/packages/li-analysis-assets/src/widgets/MiniChart/Component/Chart/Pie.tsx index d15b45a0..6a95230f 100644 --- a/packages/li-analysis-assets/src/widgets/MiniChart/Component/Chart/Pie.tsx +++ b/packages/li-analysis-assets/src/widgets/MiniChart/Component/Chart/Pie.tsx @@ -1,7 +1,7 @@ import { Chart } from '@antv/g2'; import classNames from 'classnames'; import React, { useEffect, useRef } from 'react'; -import { formatNumber } from './helper'; +import { formatNumber, getChartTheme } from './helper'; type PieProps = { className?: string; @@ -12,9 +12,10 @@ type PieProps = { colorField: string; showLegend: boolean; isCount: boolean; + theme: 'classic' | 'classicDark'; }; -const Pie = ({ className, height, width, data = [], angleField, colorField, showLegend, isCount }: PieProps) => { +const Pie = ({ className, theme, height, width, data = [], angleField, colorField, showLegend, isCount }: PieProps) => { const plotRef = useRef(); const containerRef = useRef(); @@ -48,7 +49,7 @@ const Pie = ({ className, height, width, data = [], angleField, colorField, show if (!plotRef.current) { const chart = new Chart({ container: containerRef.current!, - theme: 'classicDark', + theme: theme, autoFit: true, style: { viewFill: 'transparent', lineWidth: 2 }, padding: 30, @@ -77,7 +78,7 @@ const Pie = ({ className, height, width, data = [], angleField, colorField, show children: [{ ...commConfig }], }); plotRef.current?.render(); - }, [data, angleField, colorField, showLegend, isCount]); + }, [data, angleField, colorField, showLegend, isCount, theme]); useEffect(() => { if (height && width && plotRef.current) { @@ -85,6 +86,15 @@ const Pie = ({ className, height, width, data = [], angleField, colorField, show } }, [height, width]); + useEffect(() => { + if (theme && plotRef.current) { + const themeCfg = getChartTheme(theme) as Record; + plotRef.current.theme(themeCfg); + + plotRef.current.render(); + } + }, [theme]); + useEffect(() => { // 组件销毁时销毁图表 return () => { diff --git a/packages/li-analysis-assets/src/widgets/MiniChart/Component/Chart/helper.ts b/packages/li-analysis-assets/src/widgets/MiniChart/Component/Chart/helper.ts index 2c45846c..0ca0c1f7 100644 --- a/packages/li-analysis-assets/src/widgets/MiniChart/Component/Chart/helper.ts +++ b/packages/li-analysis-assets/src/widgets/MiniChart/Component/Chart/helper.ts @@ -62,3 +62,410 @@ export const formatNumber = ( } } }; + +export const getChartTheme = (theme: 'classic' | 'classicDark') => { + const COLORS = + theme === 'classicDark' + ? { + BLACK: '#fff', + WHITE: '#000', + STROKE: '#416180', + } + : { + BLACK: '#000', + WHITE: '#fff', + STROKE: '#416180', + }; + + const BACKGROUND_COLOR = theme === 'classicDark' ? '#141414' : 'transparent'; + + const DEFAULT_COLOR = '#5B8FF9'; + + return { + defaultColor: DEFAULT_COLOR, + defaultCategory10: 'category10', + defaultCategory20: 'category20', + defaultSize: 1, + elementActiveStroke: COLORS.BLACK, + enter: { + duration: 300, + fill: 'both', + delay: 0, + }, + update: { + duration: 300, + fill: 'both', + delay: 0, + }, + exit: { + duration: 300, + fill: 'both', + delay: 0, + }, + // --- Theme of area style + viewFill: BACKGROUND_COLOR, + plotFill: 'transparent', + mainFill: 'transparent', + contentFill: 'transparent', + // --- Theme of mark shape + line: { + line: { + fill: '', + strokeOpacity: 1, + lineWidth: 1, + }, + }, + point: { + point: { + r: 3, + fillOpacity: 0.95, + lineWidth: 0, + }, + hollow: { + r: 3, + strokeOpacity: 0.95, + lineWidth: 1, + }, + plus: { + r: 3, + strokeOpacity: 0.95, + lineWidth: 3, + }, + diamond: { + r: 3, + strokeOpacity: 0.95, + lineWidth: 1, + }, + }, + interval: { + rect: { + fillOpacity: 0.95, + }, + hollow: { + fill: '', + strokeOpacity: 1, + lineWidth: 2, + }, + }, + area: { + area: { + fillOpacity: 0.85, + lineWidth: 0, + }, + }, + polygon: { + polygon: { + fillOpacity: 0.95, + }, + }, + cell: { + cell: { + fillOpacity: 0.95, + }, + hollow: { + fill: '', + strokeOpacity: 1, + lineWidth: 2, + }, + }, + rect: { + rect: { + fillOpacity: 0.95, + }, + hollow: { + fill: '', + strokeOpacity: 1, + lineWidth: 2, + }, + }, + link: { + link: { + fill: '', + strokeOpacity: 1, + }, + }, + vector: { + vector: { + fillOpacity: 1, + }, + }, + box: { + box: { + fillOpacity: 0.95, + stroke: COLORS.BLACK, + lineWidth: 1, + }, + }, + text: { + text: { + fill: COLORS.BLACK, + fillOpacity: 0.65, + fontSize: 12, + strokeWidth: 0, + connectorStroke: COLORS.STROKE, + connectorStrokeOpacity: 0.45, + connectorLineWidth: 1, + backgroundFill: COLORS.STROKE, + backgroundFillOpacity: 0.15, + backgroundPadding: [2, 4], + startMarkerSymbol: 'circle', + startMarkerSize: 4, + endMarkerSymbol: 'circle', + endMarkerSize: 4, + }, + badge: { + fill: '#1D2129', + fillOpacity: 0.65, + strokeWidth: 0, + fontSize: 10, + textAlign: 'center', + textBaseline: 'middle', + markerFill: COLORS.STROKE, + markerFillOpacity: 0.25, + markerStrokeOpacity: 0, + }, + }, + lineX: { + line: { + stroke: COLORS.STROKE, + strokeOpacity: 0.45, + lineWidth: 1, + }, + }, + lineY: { + line: { + stroke: COLORS.STROKE, + strokeOpacity: 0.45, + lineWidth: 1, + }, + }, + rangeX: { + range: { + fill: COLORS.STROKE, + fillOpacity: 0.15, + lineWidth: 0, + }, + }, + rangeY: { + range: { + fill: COLORS.STROKE, + fillOpacity: 0.15, + lineWidth: 0, + }, + }, + connector: { + connector: { + stroke: COLORS.STROKE, + strokeOpacity: 0.45, + lineWidth: 1, + connectLength1: 12, + endMarker: true, + endMarkerSize: 6, + endMarkerFill: COLORS.STROKE, + endMarkerFillOpacity: 0.95, + }, + }, + interaction: { + active: { + line: { + line: { lineWidth: 3 }, + }, + interval: { + rect: { stroke: COLORS.BLACK }, + }, + area: { + area: { fillOpacity: 0.5 }, + }, + }, + inactive: { + area: { + area: { fillOpacity: 0.3 }, + }, + }, + selected: {}, + disabled: {}, + }, + axis: { + arrow: false, + gridLineDash: [0, 0], + gridLineWidth: 0.5, + gridStroke: COLORS.BLACK, + gridStrokeOpacity: 0.05, + labelAlign: 'horizontal', + labelFill: COLORS.BLACK, + labelFillOpacity: 0.65, + labelFontSize: 12, + labelFontWeight: 'lighter', + labelSpacing: 8, // spacing between label and it's tick + lineLineWidth: 0.5, + lineStroke: COLORS.BLACK, + lineStrokeOpacity: 0.45, + tickLength: 4, + tickLineWidth: 1, + tickStroke: COLORS.BLACK, + tickStrokeOpacity: 0.25, + titleFill: COLORS.BLACK, + titleFillOpacity: 0.65, + titleFontSize: 12, + titleFontWeight: 'normal', + titleSpacing: 12, + titleTransformOrigin: 'center', + }, + axisTop: { + gridDirection: 'positive', + labelDirection: 'negative', + labelSpacing: 8, + tickDirection: 'negative', + titlePosition: 'top', + titleSpacing: 0, + titleTextBaseline: 'middle', + }, + axisBottom: { + gridDirection: 'negative', + labelDirection: 'positive', + labelSpacing: 12, + tickDirection: 'positive', + titlePosition: 'bottom', + titleSpacing: 10, + titleTextBaseline: 'bottom', + }, + axisLeft: { + gridDirection: 'negative', + labelAutoRotate: false, + labelDirection: 'positive', + labelSpacing: 4, + tickDirection: 'positive', + titlePosition: 'left', + titleSpacing: 10, + titleTextBaseline: 'middle', + titleTransform: `translate(50%, 0) rotate(-90)`, + titleTransformOrigin: 'center', + }, + axisRight: { + gridDirection: 'positive', + labelDirection: 'negative', + labelSpacing: 4, + tickDirection: 'negative', + titlePosition: 'right', + titleSpacing: 0, + titleTextBaseline: 'top', + titleTransform: `translate(-50%, 0) rotate(-90)`, + titleTransformOrigin: 'center', + }, + axisLinear: { + girdClosed: true, + gridConnect: 'arc', + gridDirection: 'negative', + gridType: 'surround', + titlePosition: 'top', + titleTextBaseline: 'bottom', + }, + axisRadar: { + girdClosed: true, + gridStrokeOpacity: 0.3, + gridType: 'surround', + label: false, + tick: false, + titlePosition: 'start', + }, + legend: { + backgroundFill: 'transparent', + itemBackgroundFill: 'transparent', + itemLabelFill: COLORS.BLACK, + itemLabelFillOpacity: 0.65, + itemLabelFontSize: 12, + itemLabelFontWeight: 'normal', + itemMarkerFillOpacity: 1, + itemMarkerSize: 8, + itemSpacing: [5, 8], + itemValueFill: COLORS.BLACK, + itemValueFillOpacity: 0.65, + itemValueFontSize: 12, + itemValueFontWeight: 'normal', + navButtonFill: COLORS.BLACK, + navButtonFillOpacity: 0.45, + navPageNumFill: COLORS.BLACK, + navPageNumFillOpacity: 0.45, + navPageNumFontSize: 12, + padding: 8, + title: false, + titleFill: COLORS.BLACK, + titleFillOpacity: 0.45, + titleFontSize: 12, + titleFontWeight: 'normal', + titleSpacing: 4, + }, + continuousLegend: { + handleHeight: 12, + handleLabelFill: COLORS.BLACK, + handleLabelFillOpacity: 0.45, + handleLabelFontSize: 12, + handleLabelFontWeight: 'normal', + handleMarkerFill: COLORS.BLACK, + handleMarkerFillOpacity: 0.6, + handleMarkerLineWidth: 1, + handleMarkerStroke: COLORS.BLACK, + handleMarkerStrokeOpacity: 0.25, + handleWidth: 10, + labelFill: COLORS.BLACK, + labelFillOpacity: 0.45, + labelFontSize: 12, + labelFontWeight: 'normal', + // title: false, + // [todo] legend rail + }, + label: { + fill: COLORS.BLACK, + fillOpacity: 0.65, + fontSize: 12, + fontWeight: 'normal', + stroke: undefined, + offset: 12, + connectorStroke: COLORS.BLACK, + connectorStrokeOpacity: 0.45, + connectorLineWidth: 1, + connectorLength: 12, + connectorLength2: 8, + connectorDistance: 4, + }, + innerLabel: { + fill: COLORS.WHITE, + fontSize: 12, + fillOpacity: 0.85, + fontWeight: 'normal', + stroke: undefined, + offset: 0, + }, + slider: { + trackSize: 16, + trackFill: COLORS.STROKE, + trackFillOpacity: 1, + selectionFill: DEFAULT_COLOR, + selectionFillOpacity: 0.15, + handleIconSize: 10, + handleIconFill: '#f7f7f7', + handleIconFillOpacity: 1, + handleIconStroke: COLORS.BLACK, + handleIconStrokeOpacity: 0.25, + handleIconLineWidth: 1, + handleIconRadius: 2, + handleLabelFill: COLORS.BLACK, + handleLabelFillOpacity: 0.45, + handleLabelFontSize: 12, + handleLabelFontWeight: 'normal', + }, + scrollbar: {}, + title: { + titleFill: COLORS.BLACK, + titleFillOpacity: 0.85, + titleFontSize: 14, + titleFontWeight: 'bold', + titleTextBaseline: 'top', + subtitleFill: COLORS.BLACK, + subtitleFillOpacity: 0.65, + subtitleFontSize: 12, + subtitleFontWeight: 'normal', + subtitleTextBaseline: 'top', + }, + }; +}; diff --git a/packages/li-analysis-assets/src/widgets/MiniChart/Component/Chart/index.tsx b/packages/li-analysis-assets/src/widgets/MiniChart/Component/Chart/index.tsx index 6275219f..728ff678 100644 --- a/packages/li-analysis-assets/src/widgets/MiniChart/Component/Chart/index.tsx +++ b/packages/li-analysis-assets/src/widgets/MiniChart/Component/Chart/index.tsx @@ -11,6 +11,7 @@ type ChartProps = { width: number; height: number; adaptive: boolean; + theme: 'classic' | 'classicDark'; name: string; data: Record[]; type: ChartType; @@ -27,6 +28,7 @@ const Chart = ({ height, width, adaptive, + theme, name, data = [], type, @@ -61,6 +63,7 @@ const Chart = ({ data={data} xField={xField} yField={yField} + theme={theme} showLegend={showLegend} isCount={isCount} type={type === 'column' ? 'interval' : type} @@ -80,6 +83,7 @@ const Chart = ({ = ({ angleField, colorField, adaptive = true, + theme = 'classicDark', chartWidth = DefaultSize.width, chartHeight = DefaultSize.height, }) => { @@ -98,6 +99,7 @@ const MiniChart: React.FC = ({ width={chartWidth} height={chartHeight} adaptive={adaptive} + theme={theme} /> ); }; diff --git a/packages/li-analysis-assets/src/widgets/MiniChart/demos/config.ts b/packages/li-analysis-assets/src/widgets/MiniChart/demos/config.ts index c3ed133c..51596d3a 100644 --- a/packages/li-analysis-assets/src/widgets/MiniChart/demos/config.ts +++ b/packages/li-analysis-assets/src/widgets/MiniChart/demos/config.ts @@ -58,6 +58,7 @@ const config: Application = { chartType: 'line', yField: 'temperature', xField: 'name', + theme: 'classic', }, container: { id: 'AnalysisLayout', diff --git a/packages/li-analysis-assets/src/widgets/MiniChart/register-form/index.tsx b/packages/li-analysis-assets/src/widgets/MiniChart/register-form/index.tsx index 37f98c69..5db27e15 100644 --- a/packages/li-analysis-assets/src/widgets/MiniChart/register-form/index.tsx +++ b/packages/li-analysis-assets/src/widgets/MiniChart/register-form/index.tsx @@ -14,6 +14,7 @@ export type Properties = { chartType: ChartType; showLegend: boolean; adaptive: boolean; + theme: 'classic' | 'classicDark'; chartWidth?: number; chartHeight?: number; xField?: string; @@ -25,6 +26,11 @@ export type Properties = { colorField?: string; }; +const ThemOption = [ + { label: '暗色', value: 'classicDark' }, + { label: '亮色', value: 'classic' }, +]; + export default (props: WidgetRegisterFormProps): WidgetRegisterForm => { const schema = { ...getDatasetSelectFormSchema(props, 'datasetId', '数据源'), @@ -118,6 +124,17 @@ export default (props: WidgetRegisterFormProps): WidgetRegisterForm tab: '图表样式', }, properties: { + theme: { + title: '主题色', + type: 'string', + default: 'classicDark', + enum: ThemOption, + 'x-decorator': 'FormItem', + 'x-component': 'Select', + 'x-component-props': { + placeholder: '请选择类型', + }, + }, showLegend: { title: '图例', type: 'boolean', @@ -135,7 +152,6 @@ export default (props: WidgetRegisterFormProps): WidgetRegisterForm }, ], }, - adaptive: { title: '宽高自适应', type: 'boolean', @@ -165,7 +181,6 @@ export default (props: WidgetRegisterFormProps): WidgetRegisterForm }, ], }, - chartHeight: { type: 'number', title: '图表高度',