Browse Source

图表缩略图使用ECharts的getDataUrl保存图片展示而非前台渲染option

zhuth 6 years ago
parent
commit
34d92e7e35

+ 5 - 0
.eslintrc.js

@@ -1,4 +1,9 @@
 module.exports = {
+    "settings": {
+        "react": {
+            "version": "detect"
+        }
+    },
     "parser": "babel-eslint",
     "extends": 'umi',
     "parserOptions": {

+ 1 - 1
src/components/chart/list.jsx

@@ -280,7 +280,7 @@ class ChartList extends React.Component {
                                 </div> : (!l.access ? <div className='deny-body'>
                                     <div className='deny-tip'>无数据权限</div>
                                 </div> : null)}
-                                <Thumbnail key={l.code} style={{ opacity: (l.access && l.database) ? 1 : 0.3 }} type={l.type} code={l.code} option={l.chartOption}/>
+                                <Thumbnail key={l.code} style={{ opacity: (l.access && l.database) ? 1 : 0.3 }} type={l.type} code={l.code} thumbnail={l.thumbnail}/>
                             </Row>
                             <Row className='footer' type='flex' justify='end' align='bottom'>
                                 <Col style={{ textAlign: 'left', lineHeight: '28px' }} span={21}>

+ 20 - 16
src/components/chart/list.less

@@ -78,6 +78,9 @@
                         padding: 0;
                         box-shadow: none;
                         border-radius: @border-radius-base;
+                        &:hover {
+                            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
+                        }
                         &.hidden {
                             display: none;
                         }
@@ -130,24 +133,25 @@
                                     canvas {
                                         cursor: pointer;
                                     }
-                                    .table-default {
-                                        background-image: url(../../../static/images/table-default.png);
-                                        width: 100%;
-                                        height: 100%;
-                                        background-position: center;
-                                        background-size: contain;
-                                        background-repeat: no-repeat;
-                                        cursor: pointer;
-                                    }
-                                    .chart-default {
-                                        background-image: url(../../../static/images/chart-default.png);
+                                    .chart-thumbnail {
                                         width: 100%;
                                         height: 100%;
-                                        background-position: center;
-                                        background-size: contain;
-                                        background-repeat: no-repeat;
-                                        cursor: pointer;
-                                        opacity: .3;
+
+                                        &-table {
+                                            background-image: url(../../../static/images/table-default.png);
+                                            background-position: center;
+                                            background-size: contain;
+                                            background-repeat: no-repeat;
+                                            cursor: pointer;
+                                        }
+                                        &-empty {
+                                            background-image: url(../../../static/images/chart-default.png);
+                                            background-position: center;
+                                            background-size: contain;
+                                            background-repeat: no-repeat;
+                                            cursor: pointer;
+                                            opacity: .3;
+                                        }
                                     }
                                 }
                                 .desc {

+ 11 - 10
src/components/chart/thumbnail.jsx

@@ -1,18 +1,19 @@
 import React from 'react'
-import Echarts from '../common/echarts/index'
-import resolveChartThumbnailOption from './resolveChartThumbnailOption'
-
-const Thumbnail = ({ style, type, code, option }) => {
-    const newOption = resolveChartThumbnailOption(option);
+const Thumbnail = ({ style, type, code, thumbnail }) => {
     const viewType = ['bar', 'line', 'pie', 'scatter'].indexOf(type) !== -1 ? 'echarts' :
         (['aggregateTable', 'dataView'].indexOf(type) !== -1 ? 'table' : 'default');
-        
+    
     return (
         <div style={{ ...style, width: '100%', height: '100%' }}>
-            {viewType === 'echarts' && !!newOption ? <Echarts
-                key={code}
-                option={newOption}
-            /> : ( viewType === 'table' ? <div className='table-default'></div> : <div className='chart-default'></div>)}
+            {
+                viewType === 'echarts' ? (
+                    thumbnail ? <img className='chart-thumbnail chart-thumbnail-echarts' alt={code} src={thumbnail} />
+                    : <div className='chart-thumbnail chart-thumbnail-empty'></div>
+                ) : (
+                    viewType === 'table' ? <div className='chart-thumbnail chart-thumbnail-table'></div>
+                    : <div className='chart-thumbnail chart-thumbnail-empty'></div>
+                )
+            }
         </div>
     );
 }

+ 16 - 5
src/components/chartDesigner/charts/echartsView.jsx

@@ -2,16 +2,27 @@ import React from 'react'
 import Echarts from '../../common/echarts/index'
 import { hashcode } from '../../../utils/baseUtils'
 import EmptyContent from '../../common/emptyContent'
+import Thumbnail from '../../common/echarts/thumbnail'
 
 const EchartsView = ({ chartOption }) => {
+    let children;
     if(!chartOption || ((!chartOption.series || chartOption.series.length === 0) && (!chartOption.baseOption || !chartOption.baseOption.series || chartOption.baseOption.series.length === 0))) {
-        return <EmptyContent />
+        children = <EmptyContent />
     }else {
-        return <Echarts
-        key={hashcode(chartOption)}
-        option={chartOption}
-    />
+        children =  <Echarts
+            key={hashcode(chartOption)}
+            option={chartOption}
+        />
     }
+    return <div style={{ background: '#fff', width: '100%', height: '100%' }}>
+        {/* 创建一个隐藏echarts容器用于截缩略图 */}
+        <Thumbnail
+            style={{ position: 'absolute', display: 'none', top: 0, left: 0 }}
+            key={`thumbnail-${hashcode(chartOption)}`}
+            option={chartOption}
+        />
+        {children}
+    </div>
 }
 
 export default EchartsView;

+ 6 - 6
src/components/chart/resolveChartThumbnailOption.js → src/components/common/echarts/resolveChartThumbnailOption.js

@@ -7,17 +7,17 @@
  */
 export default (option) => {
     if(!option || ((!option.series || option.series.length === 0) && (!option.baseOption || !option.baseOption.series || option.baseOption.series.length === 0))) {
-        return;
+        return {};
     }
-    let _option = !!option.baseOption ? { ...option.baseOption } : { ...option };
-    let viewType = _option.series ? (_option.series[0].type) : null;
+    let _option = !!option.baseOption ? JSON.parse(JSON.stringify(option.baseOption)) : JSON.parse(JSON.stringify(option));
+    let viewType = _option.series ? (_option.series[0].type) : undefined;
 
     // 动画
     _option.animation = false;
     // 悬浮提示
-    _option.tooltip = null;
+    _option.tooltip = undefined;
     // 图例
-    _option.legend = null;
+    _option.legend = undefined;
     // 容器
     _option.grid = {
         left: 10,
@@ -26,7 +26,7 @@ export default (option) => {
         bottom: 10,
         containLabel: false
     }
-    _option.dataZoom = null;
+    _option.dataZoom = undefined;
     _option.series = _option.series ? _option.series.map(s => ({
         ...s, label: {
             normal: { show: false }

+ 38 - 0
src/components/common/echarts/thumbnail.jsx

@@ -0,0 +1,38 @@
+import React from 'react';
+import { connect } from 'dva';
+import ReactEchartsCore from 'echarts-for-react/lib/core';
+import echarts from 'echarts/lib/echarts';
+import 'echarts/lib/chart/bar';
+import 'echarts/lib/chart/line';
+import 'echarts/lib/chart/pie';
+import 'echarts/lib/chart/scatter';
+import 'echarts/lib/component/tooltip';
+import 'echarts/lib/component/grid';
+import resolveChartThumbnailOption from './resolveChartThumbnailOption';
+
+class ThumbnailEcharts extends React.Component {
+    componentDidMount() {
+        const { dispatch, option } = this.props;
+        let base64;
+        if(!option || ((!option.series || option.series.length === 0) && (!option.baseOption || !option.baseOption.series || option.baseOption.series.length === 0))) {
+            base64 = '';
+        }else {
+            base64 = this.echartsRef.getEchartsInstance().getDataURL({
+                pixelRatio: 1,
+            });
+        }
+        dispatch({ type: 'chartDesigner/silentSetField', name: 'thumbnail', value: base64 });
+    }
+    render() {
+        const { style, option } = this.props;
+        return <ReactEchartsCore
+            ref={node => this.echartsRef = node}
+            echarts={echarts}
+            option={resolveChartThumbnailOption(option)}
+            className='rc-echarts'
+            style={{ ...style, width: '187px', height: '132px' }}
+        />
+    }
+}
+
+export default connect()(ThumbnailEcharts);

+ 3 - 0
src/components/dataConnect/list.less

@@ -42,6 +42,9 @@
                     margin: 8px;
                     box-shadow: none;
                     cursor: pointer;
+                    &:hover {
+                        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
+                    }
                     .ant-card {
                         height: 100%;
                         display: flex;

+ 4 - 2
src/models/chart.js

@@ -219,6 +219,7 @@ export default {
                             theme: d.theme,
                             styleConfig: JSON.parse(d.style || '{}'),
                             demo: d.demo,
+                            thumbnail: d.thumbnail,
                         }
                     }).sort((a, b) => {
                         return new Date(b.createTime) - new Date(a.createTime)
@@ -287,7 +288,7 @@ export default {
                         group: resData.chartsGroup+'',
                         filters: filters,
                         chartOption: chartOption,
-                        demo: resData.demo
+                        demo: resData.demo,
                     }
 
                     if(viewType === 'bar') {
@@ -378,7 +379,7 @@ export default {
             try{
                 const chartDesigner = yield select(state => state.present.chartDesigner);
                 const { filters, code, header, baseConfig, pieConfig, lineConfig, aggregateTableConfig, dataViewConfig,
-                    barConfig, scatterConfig, otherConfig, description, group, chartOption, fetchConfig, styleConfig } = chartDesigner;
+                    barConfig, scatterConfig, otherConfig, description, group, chartOption, fetchConfig, styleConfig, thumbnail } = chartDesigner;
                 let body = {
                     chartId: code,
                     filters: JSON.stringify(filters),
@@ -390,6 +391,7 @@ export default {
                     chartsGroup: group+'' ? group : '-1',
                     chartOption: JSON.stringify(chartOption),
                     fetchConfig: JSON.stringify(fetchConfig),
+                    thumbnail: thumbnail
                 }; // 基本属性
                 let styleObj = {};
                 if(!!baseConfig.viewType) {

+ 6 - 1
src/models/chartDesigner.js

@@ -70,7 +70,8 @@ export default {
             chartOption: {},
             dirty: false,
             fetchConfig: {},
-            demo: false
+            demo: false,
+            thumbnail: null,
         },
     },
     reducers: {
@@ -445,6 +446,7 @@ export default {
                     },
                     yAxis: {
                         columnRename: yAxis.column.value,
+                        columnType: yAxis.column.type,
                         showDataType: yAxis.gauge.value
                     },
                     filters: getBodyFilters(filters),
@@ -484,6 +486,7 @@ export default {
                     },
                     series: {
                         columnRename: pieConfig.yAxis.column.value,
+                        columnType: pieConfig.yAxis.column.type,
                         columnName: pieConfig.yAxis.column.label,
                         showDataType: pieConfig.yAxis.gauge.value
                     },
@@ -522,6 +525,7 @@ export default {
                     },
                     yAxis: {
                         columnRename: lineConfig.yAxis.column.value,
+                        columnType: lineConfig.yAxis.column.type,
                         showDataType: lineConfig.yAxis.gauge.value
                     },
                     groups: lineConfig.groupBy && lineConfig.groupBy.key ? [lineConfig.groupBy.key] : [],
@@ -559,6 +563,7 @@ export default {
                     },
                     yAxis: {
                         columnRename: scatterConfig.yAxis.column.value,
+                        columnType: scatterConfig.yAxis.column.type,
                         showDataType: scatterConfig.yAxis.gauge.value
                     },
                     groups: scatterConfig.groupBy && scatterConfig.groupBy.key ? [scatterConfig.groupBy.key] : [],