diff --git a/frontend/src/frameworks/components/charts/ChartRenderer.tsx b/frontend/src/frameworks/components/charts/ChartRenderer.tsx index 1f16281..b2165b3 100644 --- a/frontend/src/frameworks/components/charts/ChartRenderer.tsx +++ b/frontend/src/frameworks/components/charts/ChartRenderer.tsx @@ -10,9 +10,11 @@ import { DataTable } from './DataTable'; export interface ChartRendererProps { chart: ChartInstance; data: Record[]; + /** Forwarded to EChartsBase for global instance registry. */ + chartId?: string; } -export const ChartRenderer: React.FC = ({ chart, data }) => { +export const ChartRenderer: React.FC = ({ chart, data, chartId }) => { // Use JSON key to force recalculation when any chart property changes const chartKey = JSON.stringify({ type: chart.type, bindings: chart.bindings, style: chart.style }); @@ -37,7 +39,7 @@ export const ChartRenderer: React.FC = ({ chart, data }) => const borderRadius = chart.style?.background?.borderRadius ?? 0; const overflow = borderRadius > 0 ? 'hidden' as const : undefined; return echartsOption - ? + ? :
请绑定数据字段
; } }; diff --git a/frontend/src/frameworks/components/charts/ChartWrapper.tsx b/frontend/src/frameworks/components/charts/ChartWrapper.tsx index 4edc94b..4e4a879 100644 --- a/frontend/src/frameworks/components/charts/ChartWrapper.tsx +++ b/frontend/src/frameworks/components/charts/ChartWrapper.tsx @@ -150,7 +150,7 @@ export const ChartWrapper: React.FC = ({ chartId }) => { {/* Chart content */}
- +
{/* Hover styles via inline + + +

${title}

+ ${chartsHtml} + +`; + return new Blob([html], { type: 'text/html' }); +} + +function exportTemplate( + charts: Record[], + layouts: Record[], +): Blob { + const template = { + version: '1.0', + exportedAt: new Date().toISOString(), + charts, + layouts, + }; + return new Blob([JSON.stringify(template, null, 2)], { type: 'application/json' }); +} + +// --------------------------------------------------------------------------- +// Trigger a browser download from a Blob +// --------------------------------------------------------------------------- + +function downloadBlob(blob: Blob, fileName: string): void { + const url = URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.download = fileName; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + URL.revokeObjectURL(url); +} + +// --------------------------------------------------------------------------- +// Hook +// --------------------------------------------------------------------------- + export function useExport() { const [loading, setLoading] = useState(false); const [error, setError] = useState(null); - const handleExport = useCallback(async (options: ExportRequest) => { + const handleExport = useCallback(async (request: ExportRequest) => { setLoading(true); setError(null); try { - // Create the export job on the backend - const { id } = await exportServiceClient.createExport({ - format: options.format, - chart_ids: options.chartIds, - dataset_id: options.datasetId, - file_name: options.fileName, - }); + const { format, chartIds, fileName, options = {} } = request; + const pixelRatio = options.pixelRatio ?? 2; + let blob: Blob; - // Poll until the export is ready, then download the blob - const blob = await exportServiceClient.waitForExport(id); + switch (format) { + case 'png': { + // Export first chart as single PNG + blob = await exportPNG(chartIds[0], pixelRatio); + break; + } + case 'jpg': { + const quality = (options.quality ?? 90) / 100; + blob = await exportJPG(chartIds[0], pixelRatio, quality); + break; + } + case 'svg': { + blob = await exportSVG(chartIds[0]); + break; + } + case 'pdf': { + blob = await exportPDF( + chartIds, + pixelRatio, + options.orientation ?? 'landscape', + options.pageSize ?? 'a4', + ); + break; + } + case 'excel': { + const rows = options.rows ?? []; + if (rows.length === 0) throw new Error('没有可导出的数据'); + blob = await exportExcel(rows, options.columns, options.sheetName ?? 'Sheet1'); + break; + } + case 'ppt': { + blob = await exportPPT(chartIds, pixelRatio); + break; + } + case 'html': { + blob = exportHTML(chartIds, options.htmlTitle ?? 'DataViz Pro Export'); + break; + } + case 'template': { + blob = exportTemplate(options.charts ?? [], options.layouts ?? []); + break; + } + default: + throw new Error(`Unsupported format: ${format}`); + } - // Trigger browser download - const url = URL.createObjectURL(blob); - const link = document.createElement('a'); - link.href = url; - link.download = options.fileName ?? `export.${options.format}`; - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); - URL.revokeObjectURL(url); + const ext = format === 'template' ? 'json' : format === 'excel' ? 'xlsx' : format; + downloadBlob(blob, fileName ?? `dataviz-export.${ext}`); } catch (e: any) { - setError(e.message ?? 'Export failed'); + const msg = e?.message ?? 'Export failed'; + setError(msg); throw e; } finally { setLoading(false);