mirror of
https://github.com/csunny/DB-GPT.git
synced 2025-07-29 14:57:35 +00:00
feat(web): add download image function&fix null data point (#1653)
This commit is contained in:
parent
889c3451d6
commit
c57ee0289b
@ -19,7 +19,7 @@ const en = {
|
||||
Description: 'Description',
|
||||
Storage: 'Storage',
|
||||
Please_input_the_description: 'Please input the description',
|
||||
Please_select_the_storage:'Please select the storage',
|
||||
Please_select_the_storage: 'Please select the storage',
|
||||
Next: 'Next',
|
||||
the_name_can_only_contain: 'the name can only contain numbers, letters, Chinese characters, "-" and "_"',
|
||||
Text: 'Text',
|
||||
@ -223,6 +223,7 @@ const en = {
|
||||
Chinese: 'Chinese',
|
||||
English: 'English',
|
||||
refreshSuccess: 'Refresh Success',
|
||||
Download: 'Download'
|
||||
} as const;
|
||||
|
||||
export type I18nKeys = keyof typeof en;
|
||||
@ -249,7 +250,7 @@ const zh: Resources['translation'] = {
|
||||
Description: '描述',
|
||||
Storage: '存储类型',
|
||||
Please_input_the_description: '请输入描述',
|
||||
Please_select_the_storage:'请选择存储类型',
|
||||
Please_select_the_storage: '请选择存储类型',
|
||||
Next: '下一步',
|
||||
the_name_can_only_contain: '名称只能包含数字、字母、中文字符、-或_',
|
||||
Text: '文本',
|
||||
@ -452,6 +453,7 @@ const zh: Resources['translation'] = {
|
||||
Chinese: '中文',
|
||||
English: '英文',
|
||||
refreshSuccess: '刷新成功',
|
||||
Download: '下载',
|
||||
} as const;
|
||||
|
||||
i18n.use(initReactI18next).init({
|
||||
|
@ -66,3 +66,12 @@ export const sortData = ({ data, chartType, xField }: {
|
||||
}
|
||||
return sortedData
|
||||
}
|
||||
|
||||
/** 数据空值处理:后端返回的空数据为 '-', 在展示为图表时会有问题,修改为 null */
|
||||
export const processNilData = (data: Datum[], emptyValue = '-') => data.map((datum) => {
|
||||
const processedDatum: Record<string, string | number | null> = {};
|
||||
Object.keys(datum).forEach((key) => {
|
||||
processedDatum[key] = datum[key] === emptyValue ? null : datum[key];
|
||||
});
|
||||
return processedDatum;
|
||||
});
|
||||
|
@ -1,26 +1,31 @@
|
||||
import { Empty, Row, Col, Select, Tooltip } from 'antd';
|
||||
import { Advice, Advisor } from '@antv/ava';
|
||||
import { Chart } from '@berryv/g2-react';
|
||||
import { Empty, Row, Col, Select, Tooltip, Button, Space } from 'antd';
|
||||
import { Advice, Advisor, Datum } from '@antv/ava';
|
||||
import { Chart, ChartRef } from '@berryv/g2-react';
|
||||
import i18n, { I18nKeys } from '@/app/i18n';
|
||||
import { customizeAdvisor, getVisAdvices } from './advisor/pipeline';
|
||||
import { useContext, useEffect, useMemo, useState } from 'react';
|
||||
import { useContext, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { defaultAdvicesFilter } from './advisor/utils';
|
||||
import { AutoChartProps, ChartType, CustomAdvisorConfig, CustomChart, Specification } from './types';
|
||||
import { customCharts } from './charts';
|
||||
import { ChatContext } from '@/app/chat-context';
|
||||
import { compact, concat, uniq } from 'lodash';
|
||||
import { sortData } from './charts/util';
|
||||
import { processNilData, sortData } from './charts/util';
|
||||
import { downloadImage } from '../helpers/downloadChartImage';;
|
||||
import { DownloadOutlined } from '@ant-design/icons';
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
export const AutoChart = (props: AutoChartProps) => {
|
||||
const { data, chartType, scopeOfCharts, ruleConfig } = props;
|
||||
const { chartType, scopeOfCharts, ruleConfig, data: originalData } = props;
|
||||
|
||||
// 处理空值数据 (为'-'的数据)
|
||||
const data = processNilData(originalData) as Datum[];
|
||||
const { mode } = useContext(ChatContext);
|
||||
|
||||
const [advisor, setAdvisor] = useState<Advisor>();
|
||||
const [advices, setAdvices] = useState<Advice[]>([]);
|
||||
const [renderChartType, setRenderChartType] = useState<ChartType>();
|
||||
const chartRef = useRef<ChartRef>()
|
||||
|
||||
useEffect(() => {
|
||||
const input_charts: CustomChart[] = customCharts;
|
||||
@ -106,6 +111,7 @@ export const AutoChart = (props: AutoChartProps) => {
|
||||
autoFit: true,
|
||||
height: 300,
|
||||
}}
|
||||
ref={chartRef}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -115,28 +121,38 @@ export const AutoChart = (props: AutoChartProps) => {
|
||||
if (renderChartType) {
|
||||
return (
|
||||
<div>
|
||||
<Row justify="start" className="mb-2">
|
||||
<Col>{i18n.t('Advices')}</Col>
|
||||
<Col style={{ marginLeft: 24 }}>
|
||||
<Select
|
||||
className="w-52"
|
||||
value={renderChartType}
|
||||
placeholder={'Chart Switcher'}
|
||||
onChange={(value) => setRenderChartType(value)}
|
||||
size={'small'}
|
||||
>
|
||||
{advices?.map((item) => {
|
||||
const name = i18n.t(item.type as I18nKeys);
|
||||
|
||||
return (
|
||||
<Option key={item.type} value={item.type}>
|
||||
<Tooltip title={name} placement={'right'}>
|
||||
<div>{name}</div>
|
||||
</Tooltip>
|
||||
</Option>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
<Row justify='space-between' className="mb-2">
|
||||
<Col>
|
||||
<Space>
|
||||
<span>{i18n.t('Advices')}</span>
|
||||
<Select
|
||||
className="w-52"
|
||||
value={renderChartType}
|
||||
placeholder={'Chart Switcher'}
|
||||
onChange={(value) => setRenderChartType(value)}
|
||||
size={'small'}
|
||||
>
|
||||
{advices?.map((item) => {
|
||||
const name = i18n.t(item.type as I18nKeys);
|
||||
return (
|
||||
<Option key={item.type} value={item.type}>
|
||||
<Tooltip title={name} placement={'right'}>
|
||||
<div>{name}</div>
|
||||
</Tooltip>
|
||||
</Option>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
</Space>
|
||||
</Col>
|
||||
<Col>
|
||||
<Tooltip title={i18n.t('Download')}>
|
||||
<Button
|
||||
onClick={() => downloadImage(chartRef.current, i18n.t(renderChartType as I18nKeys))}
|
||||
icon={<DownloadOutlined />}
|
||||
type='text'
|
||||
/>
|
||||
</Tooltip>
|
||||
</Col>
|
||||
</Row>
|
||||
<div className="auto-chart-content">{visComponent}</div>
|
||||
|
40
web/components/chart/helpers/downloadChartImage.ts
Normal file
40
web/components/chart/helpers/downloadChartImage.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import { ChartRef as G2Chart } from "@berryv/g2-react";
|
||||
|
||||
const getChartCanvas = (chart: G2Chart) => {
|
||||
if (!chart) return;
|
||||
const chartContainer = chart.getContainer();
|
||||
const canvasNode = chartContainer.getElementsByTagName('canvas')[0];
|
||||
return canvasNode
|
||||
}
|
||||
|
||||
/** 获得 g2 Chart 实例的 dataURL */
|
||||
function toDataURL(chart: G2Chart) {
|
||||
const canvasDom = getChartCanvas(chart);
|
||||
if (canvasDom) {
|
||||
const dataURL = canvasDom.toDataURL('image/png');
|
||||
return dataURL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 图表图片导出
|
||||
* @param chart chart 实例
|
||||
* @param name 图片名称
|
||||
*/
|
||||
export function downloadImage(chart: G2Chart, name: string = 'Chart') {
|
||||
const link = document.createElement('a');
|
||||
const filename = `${name}.png`;
|
||||
|
||||
setTimeout(() => {
|
||||
const dataURL = toDataURL(chart);
|
||||
if (dataURL) {
|
||||
link.addEventListener('click', () => {
|
||||
link.download = filename;
|
||||
link.href = dataURL;
|
||||
});
|
||||
const e = document.createEvent('MouseEvents');
|
||||
e.initEvent('click', false, false);
|
||||
link.dispatchEvent(e);
|
||||
}
|
||||
}, 16);
|
||||
}
|
Loading…
Reference in New Issue
Block a user