Просмотр исходного кода

Upgrade BizCharts (#370)

* update bizcharts

* fixed lint

* add charts autoHeight decorator

* 升级 TimelineChart

* 升级 TagCloud

* upgrade bizcharts-plugin-slider

* fix(chart): tag cloud overlapping (#461)

The key point is: `chart.coord().reflect()`.
and you can adjust gaps between words by setting padding
for tag-cloud transform

* upgrade react@16 & bizcharts@3.1.0

* update bizcharts to 3.1.0-beta2

* fix bizcharts repaint

* upgrade to bizcharts@3.1.0-beta.4

* fix TimelineCharts detail style
niko 8 лет назад
Родитель
Сommit
c148b64145

+ 1 - 0
.gitignore

@@ -14,3 +14,4 @@ _roadhog-api-doc
 npm-debug.log*
 
 /coverage
+.idea

+ 0 - 5
.roadhogrc

@@ -13,11 +13,6 @@
       ]
     }
   },
-  "externals": {
-    "g2": "G2",
-    "g-cloud": "Cloud",
-    "g2-plugin-slider": "G2.Plugin.slider"
-  },
   "ignoreMomentLocale": true,
   "theme": "./src/theme.js",
   "hash": true

+ 3 - 3
package.json

@@ -19,17 +19,17 @@
     "test:all": "node ./tests/run-tests.js"
   },
   "dependencies": {
+    "@antv/data-set": "^0.8.0",
     "antd": "^3.0.0",
     "babel-polyfill": "^6.26.0",
     "babel-runtime": "^6.9.2",
+    "bizcharts": "^3.1.0-beta.4",
+    "bizcharts-plugin-slider": "^2.0.1",
     "classnames": "^2.2.5",
     "core-js": "^2.5.1",
     "dva": "^2.1.0",
     "enquire-js": "^0.1.1",
     "fastclick": "^1.0.6",
-    "g-cloud": "^1.0.2-beta",
-    "g2": "^2.3.13",
-    "g2-plugin-slider": "^1.2.1",
     "lodash": "^4.17.4",
     "lodash-decorators": "^4.4.1",
     "lodash.clonedeep": "^4.5.0",

+ 24 - 27
src/components/ActiveChart/index.js

@@ -1,4 +1,4 @@
-import React, { PureComponent } from 'react';
+import React, { Component } from 'react';
 
 import { MiniArea } from '../Charts';
 import NumberInfo from '../NumberInfo';
@@ -14,16 +14,16 @@ function getActiveData() {
   for (let i = 0; i < 24; i += 1) {
     activeData.push({
       x: `${fixedZero(i)}:00`,
-      y: (i * 50) + (Math.floor(Math.random() * 200)),
+      y: Math.floor(Math.random() * 200) + (i * 50),
     });
   }
   return activeData;
 }
 
-export default class ActiveChart extends PureComponent {
+export default class ActiveChart extends Component {
   state = {
     activeData: getActiveData(),
-  }
+  };
 
   componentDidMount() {
     this.timer = setInterval(() => {
@@ -42,43 +42,40 @@ export default class ActiveChart extends PureComponent {
 
     return (
       <div className={styles.activeChart}>
-        <NumberInfo
-          subTitle="目标评估"
-          total="有望达到预期"
-        />
+        <NumberInfo subTitle="目标评估" total="有望达到预期" />
         <div style={{ marginTop: 32 }}>
           <MiniArea
             animate={false}
             line
             borderWidth={2}
             height={84}
+            scale={{
+              y: {
+                tickCount: 3,
+              },
+            }}
             yAxis={{
-              tickCount: 3,
               tickLine: false,
-              labels: false,
+              label: false,
               title: false,
               line: false,
             }}
             data={activeData}
           />
         </div>
-        {
-          activeData && (
-            <div className={styles.activeChartGrid}>
-              <p>{[...activeData].sort()[activeData.length - 1].y + 200} 亿元</p>
-              <p>{[...activeData].sort()[Math.floor(activeData.length / 2)].y} 亿元</p>
-            </div>
-          )
-        }
-        {
-          activeData && (
-            <div className={styles.activeChartLegend}>
-              <span>00:00</span>
-              <span>{activeData[Math.floor(activeData.length / 2)].x}</span>
-              <span>{activeData[activeData.length - 1].x}</span>
-            </div>
-          )
-        }
+        {activeData && (
+          <div className={styles.activeChartGrid}>
+            <p>{[...activeData].sort()[activeData.length - 1].y + 200} 亿元</p>
+            <p>{[...activeData].sort()[Math.floor(activeData.length / 2)].y} 亿元</p>
+          </div>
+        )}
+        {activeData && (
+          <div className={styles.activeChartLegend}>
+            <span>00:00</span>
+            <span>{activeData[Math.floor(activeData.length / 2)].x}</span>
+            <span>{activeData[activeData.length - 1].x}</span>
+          </div>
+        )}
       </div>
     );
   }

+ 1 - 1
src/components/Charts/Bar/index.d.ts

@@ -2,7 +2,7 @@ import * as React from "react";
 export interface BarProps {
   title: React.ReactNode;
   color?: string;
-  margin?: [number, number, number, number];
+  padding?: [number, number, number, number];
   height: number;
   data: Array<{
     x: string;

+ 39 - 89
src/components/Charts/Bar/index.js

@@ -1,37 +1,26 @@
-import React, { PureComponent } from 'react';
-import G2 from 'g2';
+import React, { Component } from 'react';
+import { Chart, Axis, Tooltip, Geom } from 'bizcharts';
 import Debounce from 'lodash-decorators/debounce';
 import Bind from 'lodash-decorators/bind';
-import equal from '../equal';
+import autoHeight from '../autoHeight';
 import styles from '../index.less';
 
-class Bar extends PureComponent {
+@autoHeight()
+class Bar extends Component {
   state = {
     autoHideXLabels: false,
-  }
+  };
 
   componentDidMount() {
-    this.renderChart(this.props.data);
-
     window.addEventListener('resize', this.resize);
   }
 
-  componentWillReceiveProps(nextProps) {
-    if (!equal(this.props, nextProps)) {
-      this.renderChart(nextProps.data);
-    }
-  }
-
   componentWillUnmount() {
     window.removeEventListener('resize', this.resize);
-    if (this.chart) {
-      this.chart.destroy();
-    }
-    this.resize.cancel();
   }
 
   @Bind()
-  @Debounce(200)
+  @Debounce(400)
   resize() {
     if (!this.node) {
       return;
@@ -49,99 +38,60 @@ class Bar extends PureComponent {
         this.setState({
           autoHideXLabels: true,
         });
-        this.renderChart(data);
       }
     } else if (autoHideXLabels) {
       this.setState({
         autoHideXLabels: false,
       });
-      this.renderChart(data);
     }
   }
 
+  handleRoot = (n) => {
+    this.root = n;
+  };
+
   handleRef = (n) => {
     this.node = n;
-  }
-
-  renderChart(data) {
-    const { autoHideXLabels } = this.state;
-    const {
-      height = 0,
-      fit = true,
-      color = 'rgba(24, 144, 255, 0.85)',
-      margin = [32, 0, (autoHideXLabels ? 8 : 32), 40],
-    } = this.props;
-
-
-    if (!data || (data && data.length < 1)) {
-      return;
-    }
+  };
 
-    // clean
-    this.node.innerHTML = '';
-
-    const { Frame } = G2;
-    const frame = new Frame(data);
-
-    const chart = new G2.Chart({
-      container: this.node,
-      forceFit: fit,
-      height: height - 22,
-      legend: null,
-      plotCfg: {
-        margin,
-      },
-    });
+  render() {
+    const { height, title, forceFit = true, data, color = 'rgba(24, 144, 255, 0.85)', padding } = this.props;
 
-    if (autoHideXLabels) {
-      chart.axis('x', {
-        title: false,
-        tickLine: false,
-        labels: false,
-      });
-    } else {
-      chart.axis('x', {
-        title: false,
-      });
-    }
-    chart.axis('y', {
-      title: false,
-      line: false,
-      tickLine: false,
-    });
+    const { autoHideXLabels } = this.state;
 
-    chart.source(frame, {
+    const scale = {
       x: {
         type: 'cat',
       },
       y: {
         min: 0,
       },
-    });
-
-    chart.tooltip({
-      title: null,
-      crosshairs: false,
-      map: {
-        name: 'x',
-      },
-    });
-    chart.interval().position('x*y').color(color).style({
-      fillOpacity: 1,
-    });
-    chart.render();
+    };
 
-    this.chart = chart;
-  }
-
-  render() {
-    const { height, title } = this.props;
+    const tooltip = [
+      'x*y',
+      (x, y) => ({
+        name: x,
+        value: y,
+      }),
+    ];
 
     return (
-      <div className={styles.chart} style={{ height }}>
-        <div>
-          { title && <h4>{title}</h4>}
-          <div ref={this.handleRef} />
+      <div className={styles.chart} style={{ height }} ref={this.handleRoot}>
+        <div ref={this.handleRef}>
+          {title && <h4>{title}</h4>}
+          <Chart
+            scale={scale}
+            height={height}
+            forceFit={forceFit}
+            data={data}
+            padding={padding || 'auto'}
+          >
+            <Axis name="x" title={false} label={!autoHideXLabels} tickLine={!autoHideXLabels} />
+            <Axis name="y" title={false} line={false} tickLine={false} min={0} />
+            <Tooltip showTitle={false} crosshairs={false} />
+            <Geom type="interval" position="x*y" color={color} tooltip={tooltip} />
+          </Chart>
         </div>
       </div>
     );

+ 155 - 190
src/components/Charts/Gauge/index.js

@@ -1,202 +1,167 @@
-import React, { PureComponent } from 'react';
-import G2 from 'g2';
-import equal from '../equal';
-
-const { Shape } = G2;
-
-const primaryColor = '#2F9CFF';
-const backgroundColor = '#F0F2F5';
-
-/* eslint no-underscore-dangle: 0 */
-class Gauge extends PureComponent {
-  componentDidMount() {
-    setTimeout(() => {
-      this.renderChart();
-    }, 10);
+import React from 'react';
+import { Chart, Geom, Axis, Coord, Guide, Shape } from 'bizcharts';
+import autoHeight from '../autoHeight';
+
+const { Arc, Html, Line } = Guide;
+
+const defaultFormatter = (val) => {
+  switch (val) {
+    case '2':
+      return '差';
+    case '4':
+      return '中';
+    case '6':
+      return '良';
+    case '8':
+      return '优';
+    default:
+      return '';
   }
-
-  componentWillReceiveProps(nextProps) {
-    if (!equal(this.props, nextProps)) {
-      setTimeout(() => {
-        this.renderChart(nextProps);
-      }, 10);
-    }
-  }
-
-  componentWillUnmount() {
-    if (this.chart) {
-      this.chart.destroy();
-    }
-  }
-
-  handleRef = (n) => {
-    this.node = n;
-  }
-
-  initChart(nextProps) {
-    const { title, color = primaryColor } = nextProps || this.props;
-
-    Shape.registShape('point', 'dashBoard', {
-      drawShape(cfg, group) {
-        const originPoint = cfg.points[0];
-        const point = this.parsePoint({ x: originPoint.x, y: 0.4 });
-
-        const center = this.parsePoint({
-          x: 0,
-          y: 0,
-        });
-
-        const shape = group.addShape('polygon', {
-          attrs: {
-            points: [
-              [center.x, center.y],
-              [point.x + 8, point.y],
-              [point.x + 8, point.y - 2],
-              [center.x, center.y - 2],
-            ],
-            radius: 2,
-            lineWidth: 2,
-            arrow: false,
-            fill: color,
-          },
-        });
-
-        group.addShape('Marker', {
-          attrs: {
-            symbol: 'circle',
-            lineWidth: 2,
-            fill: color,
-            radius: 8,
-            x: center.x,
-            y: center.y,
-          },
-        });
-        group.addShape('Marker', {
-          attrs: {
-            symbol: 'circle',
-            lineWidth: 2,
-            fill: '#fff',
-            radius: 5,
-            x: center.x,
-            y: center.y,
-          },
-        });
-
-        const { origin } = cfg;
-        group.addShape('text', {
-          attrs: {
-            x: center.x,
-            y: center.y + 80,
-            text: `${origin._origin.value}%`,
-            textAlign: 'center',
-            fontSize: 24,
-            fill: 'rgba(0, 0, 0, 0.85)',
-          },
-        });
-        group.addShape('text', {
-          attrs: {
-            x: center.x,
-            y: center.y + 45,
-            text: title,
-            textAlign: 'center',
-            fontSize: 14,
-            fill: 'rgba(0, 0, 0, 0.43)',
-          },
-        });
-
-        return shape;
-      },
+};
+
+Shape.registerShape('point', 'pointer', {
+  drawShape(cfg, group) {
+    let point = cfg.points[0];
+    point = this.parsePoint(point);
+    const center = this.parsePoint({
+      x: 0,
+      y: 0,
     });
-  }
-
-  renderChart(nextProps) {
-    const {
-      height, color = primaryColor, bgColor = backgroundColor, title, percent, format,
-    } = nextProps || this.props;
-    const data = [{ name: title, value: percent }];
-
-    if (this.chart) {
-      this.chart.clear();
-    }
-    if (this.node) {
-      this.node.innerHTML = '';
-    }
-
-    this.initChart(nextProps);
-
-    const chart = new G2.Chart({
-      container: this.node,
-      forceFit: true,
-      height,
-      animate: false,
-      plotCfg: {
-        margin: [10, 10, 30, 10],
+    group.addShape('line', {
+      attrs: {
+        x1: center.x,
+        y1: center.y,
+        x2: point.x,
+        y2: point.y,
+        stroke: cfg.color,
+        lineWidth: 2,
+        lineCap: 'round',
       },
     });
-
-    chart.source(data);
-
-    chart.tooltip(false);
-
-    chart.coord('gauge', {
-      startAngle: -1.2 * Math.PI,
-      endAngle: 0.20 * Math.PI,
-    });
-    chart.col('value', {
-      type: 'linear',
-      nice: true,
-      min: 0,
-      max: 100,
-      tickCount: 6,
-    });
-    chart.axis('value', {
-      subTick: false,
-      tickLine: {
-        stroke: color,
-        lineWidth: 2,
-        value: -14,
+    return group.addShape('circle', {
+      attrs: {
+        x: center.x,
+        y: center.y,
+        r: 6,
+        stroke: cfg.color,
+        lineWidth: 3,
+        fill: '#fff',
       },
-      labelOffset: -12,
-      formatter: format,
     });
-    chart.point().position('value').shape('dashBoard');
-    draw(data);
-
-    /* eslint no-shadow: 0 */
-    function draw(data) {
-      const val = data[0].value;
-      const lineWidth = 12;
-      chart.guide().clear();
-
-      chart.guide().arc(() => {
-        return [0, 0.95];
-      }, () => {
-        return [val, 0.95];
-      }, {
-        stroke: color,
-        lineWidth,
-      });
-
-      chart.guide().arc(() => {
-        return [val, 0.95];
-      }, (arg) => {
-        return [arg.max, 0.95];
-      }, {
-        stroke: bgColor,
-        lineWidth,
-      });
-
-      chart.changeData(data);
-    }
-
-    this.chart = chart;
-  }
+  },
+});
 
+@autoHeight()
+export default class Gauge extends React.Component {
   render() {
+    const {
+      title,
+      height,
+      percent,
+      forceFit = true,
+      formatter = defaultFormatter,
+      color = '#2F9CFF',
+      bgColor = '#F0F2F5',
+    } = this.props;
+    const cols = {
+      value: {
+        type: 'linear',
+        min: 0,
+        max: 10,
+        tickCount: 6,
+        nice: true,
+      },
+    };
+    const data = [{ value: percent / 10 }];
     return (
-      <div ref={this.handleRef} />
+      <Chart height={height} data={data} scale={cols} padding={[-16, 0, 16, 0]} forceFit={forceFit}>
+        <Coord type="polar" startAngle={-1.25 * Math.PI} endAngle={0.25 * Math.PI} radius={0.8} />
+        <Axis name="1" line={null} />
+        <Axis
+          line={null}
+          tickLine={null}
+          subTickLine={null}
+          name="value"
+          zIndex={2}
+          gird={null}
+          label={{
+            offset: -12,
+            formatter,
+            textStyle: {
+              fontSize: 12,
+              fill: 'rgba(0, 0, 0, 0.65)',
+              textAlign: 'center',
+            },
+          }}
+        />
+        <Guide>
+          <Line
+            start={[3, 0.905]}
+            end={[3, 0.85]}
+            lineStyle={{
+              stroke: color,
+              lineDash: null,
+              lineWidth: 2,
+            }}
+          />
+          <Line
+            start={[5, 0.905]}
+            end={[5, 0.85]}
+            lineStyle={{
+              stroke: color,
+              lineDash: null,
+              lineWidth: 3,
+            }}
+          />
+          <Line
+            start={[7, 0.905]}
+            end={[7, 0.85]}
+            lineStyle={{
+              stroke: color,
+              lineDash: null,
+              lineWidth: 3,
+            }}
+          />
+          <Arc
+            zIndex={0}
+            start={[0, 0.965]}
+            end={[10, 0.965]}
+            style={{
+              stroke: bgColor,
+              lineWidth: 10,
+            }}
+          />
+          <Arc
+            zIndex={1}
+            start={[0, 0.965]}
+            end={[data[0].value, 0.965]}
+            style={{
+              stroke: color,
+              lineWidth: 10,
+            }}
+          />
+          <Html
+            position={['50%', '95%']}
+            html={() => {
+              return `
+                <div style="width: 300px;text-align: center;font-size: 12px!important;">
+                  <p style="font-size: 14px; color: rgba(0,0,0,0.43);margin: 0;">${title}</p>
+                  <p style="font-size: 24px;color: rgba(0,0,0,0.85);margin: 0;">
+                    ${data[0].value * 10}%
+                  </p>
+                </div>`;
+            }}
+          />
+        </Guide>
+        <Geom
+          line={false}
+          type="point"
+          position="value*1"
+          shape="pointer"
+          color={color}
+          active={false}
+        />
+      </Chart>
     );
   }
 }
-
-export default Gauge;

+ 65 - 99
src/components/Charts/MiniArea/index.js

@@ -1,125 +1,91 @@
-import React, { PureComponent } from 'react';
-import G2 from 'g2';
-import equal from '../equal';
+import React from 'react';
+import { Chart, Axis, Tooltip, Geom } from 'bizcharts';
+import autoHeight from '../autoHeight';
 import styles from '../index.less';
 
-class MiniArea extends PureComponent {
-  static defaultProps = {
-    borderColor: '#1890FF',
-    color: 'rgba(24, 144, 255, 0.2)',
-  };
-
-  componentDidMount() {
-    this.renderChart(this.props.data);
-  }
-
-  componentWillReceiveProps(nextProps) {
-    if (!equal(this.props, nextProps)) {
-      this.renderChart(nextProps.data);
-    }
-  }
-
-  componentWillUnmount() {
-    if (this.chart) {
-      this.chart.destroy();
-    }
-  }
-
-  handleRef = (n) => {
-    this.node = n;
-  }
-
-  renderChart(data) {
+@autoHeight()
+export default class MiniArea extends React.Component {
+  render() {
     const {
-      height = 0, fit = true, color, borderWidth = 2, line, xAxis, yAxis, animate = true,
+      height,
+      data = [],
+      forceFit = true,
+      color = 'rgba(24, 144, 255, 0.2)',
+      scale = {},
+      borderWidth = 2,
+      line,
+      xAxis,
+      yAxis,
+      animate = true,
     } = this.props;
-    const borderColor = this.props.borderColor || color;
-
-    if (!data || (data && data.length < 1)) {
-      return;
-    }
-
-    // clean
-    this.node.innerHTML = '';
 
-    const chart = new G2.Chart({
-      container: this.node,
-      forceFit: fit,
-      height: height + 54,
-      animate,
-      plotCfg: {
-        margin: [36, 5, 30, 5],
-      },
-      legend: null,
-    });
-
-    if (!xAxis && !yAxis) {
-      chart.axis(false);
-    }
-
-    if (xAxis) {
-      chart.axis('x', xAxis);
-    } else {
-      chart.axis('x', false);
-    }
+    const borderColor = this.props.borderColor || color;
 
-    if (yAxis) {
-      chart.axis('y', yAxis);
-    } else {
-      chart.axis('y', false);
-    }
+    const padding = [36, 5, 30, 5];
 
-    const dataConfig = {
+    const scaleProps = {
       x: {
         type: 'cat',
         range: [0, 1],
-        ...xAxis,
+        ...scale.x,
       },
       y: {
         min: 0,
-        ...yAxis,
+        ...scale.y,
       },
     };
 
-    chart.tooltip({
-      title: null,
-      crosshairs: false,
-      map: {
-        title: null,
-        name: 'x',
-        value: 'y',
-      },
-    });
-
-    const view = chart.createView();
-    view.source(data, dataConfig);
+    const tooltip = [
+      'x*y',
+      (x, y) => ({
+        name: x,
+        value: y,
+      }),
+    ];
 
-    view.area().position('x*y').color(color).shape('smooth')
-      .style({ fillOpacity: 1 });
-
-    if (line) {
-      const view2 = chart.createView();
-      view2.source(data, dataConfig);
-      view2.line().position('x*y').color(borderColor).size(borderWidth)
-        .shape('smooth');
-      view2.tooltip(false);
-    }
-    chart.render();
-
-    this.chart = chart;
-  }
-
-  render() {
-    const { height } = this.props;
+    const chartHeight = height + 54;
 
     return (
       <div className={styles.miniChart} style={{ height }}>
         <div className={styles.chartContent}>
-          <div ref={this.handleRef} />
+          {height > 0 && (
+            <Chart
+              animate={animate}
+              scale={scaleProps}
+              height={chartHeight}
+              forceFit={forceFit}
+              data={data}
+              padding={padding}
+            >
+              <Axis name="x" label={false} line={false} tickLine={false} grid={false} {...xAxis} />
+              <Axis name="y" label={false} line={false} tickLine={false} grid={false} {...yAxis} />
+              <Tooltip showTitle={false} crosshairs={false} />
+              <Geom
+                type="area"
+                position="x*y"
+                color={color}
+                tooltip={tooltip}
+                shape="smooth"
+                style={{
+                  fillOpacity: 1,
+                }}
+              />
+              {line ? (
+                <Geom
+                  type="line"
+                  position="x*y"
+                  shape="smooth"
+                  color={borderColor}
+                  size={borderWidth}
+                  tooltip={false}
+                />
+              ) : (
+                <span style={{ display: 'none' }} />
+              )}
+            </Chart>
+          )}
         </div>
       </div>
     );
   }
 }
-
-export default MiniArea;

+ 29 - 66
src/components/Charts/MiniBar/index.js

@@ -1,87 +1,50 @@
-import React, { PureComponent } from 'react';
-import G2 from 'g2';
-import equal from '../equal';
+import React from 'react';
+import { Chart, Tooltip, Geom } from 'bizcharts';
+import autoHeight from '../autoHeight';
 import styles from '../index.less';
 
-class MiniBar extends PureComponent {
-  componentDidMount() {
-    this.renderChart(this.props.data);
-  }
-
-  componentWillReceiveProps(nextProps) {
-    if (!equal(this.props, nextProps)) {
-      this.renderChart(nextProps.data);
-    }
-  }
-
-  componentWillUnmount() {
-    if (this.chart) {
-      this.chart.destroy();
-    }
-  }
-
-  handleRef = (n) => {
-    this.node = n;
-  }
-
-  renderChart(data) {
-    const { height = 0, fit = true, color = '#1890FF' } = this.props;
-
-    if (!data || (data && data.length < 1)) {
-      return;
-    }
-
-    // clean
-    this.node.innerHTML = '';
-
-    const { Frame } = G2;
-    const frame = new Frame(data);
-
-    const chart = new G2.Chart({
-      container: this.node,
-      forceFit: fit,
-      height: height + 54,
-      plotCfg: {
-        margin: [36, 5, 30, 5],
-      },
-      legend: null,
-    });
-
-    chart.axis(false);
+@autoHeight()
+export default class MiniBar extends React.Component {
+  render() {
+    const { height, forceFit = true, color = '#1890FF', data = [] } = this.props;
 
-    chart.source(frame, {
+    const scale = {
       x: {
         type: 'cat',
       },
       y: {
         min: 0,
       },
-    });
+    };
 
-    chart.tooltip({
-      title: null,
-      crosshairs: false,
-      map: {
-        name: 'x',
-      },
-    });
-    chart.interval().position('x*y').color(color);
-    chart.render();
+    const padding = [36, 5, 30, 5];
 
-    this.chart = chart;
-  }
+    const tooltip = [
+      'x*y',
+      (x, y) => ({
+        name: x,
+        value: y,
+      }),
+    ];
 
-  render() {
-    const { height } = this.props;
+    // for tooltip not to be hide
+    const chartHeight = height + 54;
 
     return (
       <div className={styles.miniChart} style={{ height }}>
         <div className={styles.chartContent}>
-          <div ref={this.handleRef} />
+          <Chart
+            scale={scale}
+            height={chartHeight}
+            forceFit={forceFit}
+            data={data}
+            padding={padding}
+          >
+            <Tooltip showTitle={false} crosshairs={false} />
+            <Geom type="interval" position="x*y" color={color} tooltip={tooltip} />
+          </Chart>
         </div>
       </div>
     );
   }
 }
-
-export default MiniBar;

+ 1 - 1
src/components/Charts/Pie/index.d.ts

@@ -4,7 +4,7 @@ export interface PieProps {
   color?: string;
   height: number;
   hasLegend?: boolean;
-  margin?: [number, number, number, number];
+  padding?: [number, number, number, number];
   percent?: number;
   data?: Array<{
     x: string;

+ 135 - 150
src/components/Charts/Pie/index.js

@@ -1,40 +1,64 @@
 import React, { Component } from 'react';
-import G2 from 'g2';
+import { Chart, Tooltip, Geom, Coord } from 'bizcharts';
+import { DataView } from '@antv/data-set';
 import { Divider } from 'antd';
 import classNames from 'classnames';
 import ReactFitText from 'react-fittext';
 import Debounce from 'lodash-decorators/debounce';
 import Bind from 'lodash-decorators/bind';
-import equal from '../equal';
+import autoHeight from '../autoHeight';
+
 import styles from './index.less';
 
 /* eslint react/no-danger:0 */
-class Pie extends Component {
+@autoHeight()
+export default class Pie extends Component {
   state = {
     legendData: [],
-    legendBlock: true,
+    legendBlock: false,
   };
 
   componentDidMount() {
-    this.renderChart();
+    this.getLengendData();
     this.resize();
     window.addEventListener('resize', this.resize);
   }
 
   componentWillReceiveProps(nextProps) {
-    if (!equal(this.props, nextProps)) {
-      this.renderChart(nextProps.data);
+    if (this.props.data !== nextProps.data) {
+      this.getLengendData();
     }
   }
 
   componentWillUnmount() {
     window.removeEventListener('resize', this.resize);
-    if (this.chart) {
-      this.chart.destroy();
-    }
     this.resize.cancel();
   }
 
+  getG2Instance = (chart) => {
+    this.chart = chart;
+  };
+
+  // for custom lengend view
+  getLengendData = () => {
+    if (!this.chart) return;
+    const geom = this.chart.getAllGeoms()[0]; // 获取所有的图形
+    const items = geom.get('dataArray') || []; // 获取图形对应的
+
+    const legendData = items.map((item) => {
+      /* eslint no-underscore-dangle:0 */
+      const origin = item[0]._origin;
+      origin.color = item[0].color;
+      origin.checked = true;
+      return origin;
+    });
+
+    this.setState({
+      legendData,
+    });
+  };
+
+  // for window resize auto responsive legend
   @Bind()
   @Debounce(300)
   resize() {
@@ -47,26 +71,18 @@ class Pie extends Component {
       if (!this.state.legendBlock) {
         this.setState({
           legendBlock: true,
-        }, () => {
-          this.renderChart();
         });
       }
     } else if (this.state.legendBlock) {
       this.setState({
         legendBlock: false,
-      }, () => {
-        this.renderChart();
       });
     }
   }
 
-  handleRef = (n) => {
-    this.node = n;
-  }
-
   handleRoot = (n) => {
     this.root = n;
-  }
+  };
 
   handleLegendClick = (item, i) => {
     const newItem = item;
@@ -75,37 +91,57 @@ class Pie extends Component {
     const { legendData } = this.state;
     legendData[i] = newItem;
 
+    const filteredLegendData = legendData.filter(l => l.checked).map(l => l.x);
+
     if (this.chart) {
-      const filterItem = legendData.filter(l => l.checked).map(l => l.x);
-      this.chart.filter('x', filterItem);
-      this.chart.repaint();
+      this.chart.filter('x', val => filteredLegendData.indexOf(val) > -1);
     }
 
     this.setState({
       legendData,
     });
-  }
-
-  renderChart(d) {
-    let data = d || this.props.data;
+  };
 
+  render() {
     const {
-      height = 0,
-      hasLegend,
-      fit = true,
-      margin = [12, 0, 12, 0], percent, color,
+      valueFormat,
+      subTitle,
+      total,
+      hasLegend = false,
+      className,
+      style,
+      height,
+      forceFit = true,
+      percent = 0,
+      color,
       inner = 0.75,
       animate = true,
       colors,
-      lineWidth = 0,
+      lineWidth = 1,
     } = this.props;
 
-    const defaultColors = colors;
+    const { legendData, legendBlock } = this.state;
+    const pieClassName = classNames(styles.pie, className, {
+      [styles.hasLegend]: !!hasLegend,
+      [styles.legendBlock]: legendBlock,
+    });
 
+    const defaultColors = colors;
+    let data = this.props.data || [];
     let selected = this.props.selected || true;
-    let tooltip = this.props.tooltips || true;
-
+    let tooltip = this.props.tooltip || true;
     let formatColor;
+
+    const scale = {
+      x: {
+        type: 'cat',
+        range: [0, 1],
+      },
+      y: {
+        min: 0,
+      },
+    };
+
     if (percent) {
       selected = false;
       tooltip = false;
@@ -117,7 +153,6 @@ class Pie extends Component {
         }
       };
 
-      /* eslint no-param-reassign: */
       data = [
         {
           x: '占比',
@@ -130,131 +165,81 @@ class Pie extends Component {
       ];
     }
 
-    if (!data || (data && data.length < 1)) {
-      return;
-    }
-
-    // clean
-    this.node.innerHTML = '';
-
-    const { Stat } = G2;
-
-    const chart = new G2.Chart({
-      container: this.node,
-      forceFit: fit,
-      height,
-      plotCfg: {
-        margin,
-      },
-      animate,
-    });
-
-    if (!tooltip) {
-      chart.tooltip(false);
-    } else {
-      chart.tooltip({
-        title: null,
-      });
-    }
-
-    chart.axis(false);
-    chart.legend(false);
-
-    chart.source(data, {
-      x: {
-        type: 'cat',
-        range: [0, 1],
-      },
-      y: {
-        min: 0,
-      },
-    });
-
-    chart.coord('theta', {
-      inner,
-    });
-
-    chart
-      .intervalStack()
-      .position(Stat.summary.percent('y'))
-      .style({ lineWidth, stroke: '#fff' })
-      .color('x', percent ? formatColor : defaultColors)
-      .selected(selected);
-
-    chart.render();
-
-    this.chart = chart;
-
-    let legendData = [];
-    if (hasLegend) {
-      const geom = chart.getGeoms()[0]; // 获取所有的图形
-      const items = geom.getData(); // 获取图形对应的数据
-      legendData = items.map((item) => {
-        /* eslint no-underscore-dangle:0 */
-        const origin = item._origin;
-        origin.color = item.color;
-        origin.checked = true;
-        return origin;
-      });
-    }
-
-    this.setState({
-      legendData,
-    });
-  }
-
-  render() {
-    const { valueFormat, subTitle, total, hasLegend, className, style } = this.props;
-    const { legendData, legendBlock } = this.state;
-    const pieClassName = classNames(styles.pie, className, {
-      [styles.hasLegend]: !!hasLegend,
-      [styles.legendBlock]: legendBlock,
+    const tooltipFormat = [
+      'x*percent',
+      (x, p) => ({
+        name: x,
+        value: `${(p * 100).toFixed(2)}%`,
+      }),
+    ];
+
+    const padding = [12, 0, 12, 0];
+
+    const dv = new DataView();
+    dv.source(data).transform({
+      type: 'percent',
+      field: 'y',
+      dimension: 'x',
+      as: 'percent',
     });
 
     return (
       <div ref={this.handleRoot} className={pieClassName} style={style}>
         <ReactFitText maxFontSize={25}>
           <div className={styles.chart}>
-            <div ref={this.handleRef} style={{ fontSize: 0 }} />
-            {
-              (subTitle || total) && (
-                <div className={styles.total}>
-                  {subTitle && <h4 className="pie-sub-title">{subTitle}</h4>}
-                  {
-                    // eslint-disable-next-line
-                    total && <div className="pie-stat" dangerouslySetInnerHTML={{ __html: total }} />
-                  }
-                </div>
-              )
-            }
+            <Chart
+              scale={scale}
+              height={height}
+              forceFit={forceFit}
+              data={dv}
+              padding={padding}
+              animate={animate}
+              onGetG2Instance={this.getG2Instance}
+            >
+              {!!tooltip && <Tooltip showTitle={false} />}
+              <Coord type="theta" innerRadius={inner} />
+              <Geom
+                style={{ lineWidth, stroke: '#fff' }}
+                tooltip={tooltip && tooltipFormat}
+                type="intervalStack"
+                position="percent"
+                color={['x', percent ? formatColor : defaultColors]}
+                selected={selected}
+              />
+            </Chart>
+
+            {(subTitle || total) && (
+              <div className={styles.total}>
+                {subTitle && <h4 className="pie-sub-title">{subTitle}</h4>}
+                {/* eslint-disable-next-line */}
+                {total && <div className="pie-stat" dangerouslySetInnerHTML={{ __html: total }} />}
+              </div>
+            )}
           </div>
         </ReactFitText>
 
-        {
-          hasLegend && (
-            <ul className={styles.legend}>
-              {
-                legendData.map((item, i) => (
-                  <li key={item.x} onClick={() => this.handleLegendClick(item, i)}>
-                    <span className={styles.dot} style={{ backgroundColor: !item.checked ? '#aaa' : item.color }} />
-                    <span className={styles.legendTitle}>{item.x}</span>
-                    <Divider type="vertical" />
-                    <span className={styles.percent}>{`${(item['..percent'] * 100).toFixed(2)}%`}</span>
-                    <span
-                      className={styles.value}
-                      dangerouslySetInnerHTML={{
-                        __html: valueFormat ? valueFormat(item.y) : item.y,
-                      }}
-                    />
-                  </li>
-                ))
-              }
-            </ul>
-          )
-        }
+        {hasLegend && (
+          <ul className={styles.legend}>
+            {legendData.map((item, i) => (
+              <li key={item.x} onClick={() => this.handleLegendClick(item, i)}>
+                <span
+                  className={styles.dot}
+                  style={{ backgroundColor: !item.checked ? '#aaa' : item.color }}
+                />
+                <span className={styles.legendTitle}>{item.x}</span>
+                <Divider type="vertical" />
+                <span className={styles.percent}>{`${(item.percent * 100).toFixed(2)}%`}</span>
+                <span
+                  className={styles.value}
+                  dangerouslySetInnerHTML={{
+                    __html: valueFormat ? valueFormat(item.y) : item.y,
+                  }}
+                />
+              </li>
+            ))}
+          </ul>
+        )}
       </div>
     );
   }
 }
-
-export default Pie;

+ 1 - 1
src/components/Charts/Radar/index.d.ts

@@ -2,7 +2,7 @@ import * as React from "react";
 export interface RadarProps {
   title?: React.ReactNode;
   height: number;
-  margin?: [number, number, number, number];
+  padding?: [number, number, number, number];
   hasLegend?: boolean;
   data: Array<{
     name: string;

+ 128 - 137
src/components/Charts/Radar/index.js

@@ -1,34 +1,57 @@
-import React, { PureComponent } from 'react';
-import G2 from 'g2';
+import React, { Component } from 'react';
+import { Chart, Tooltip, Geom, Coord, Axis } from 'bizcharts';
 import { Row, Col } from 'antd';
-import equal from '../equal';
+import autoHeight from '../autoHeight';
 import styles from './index.less';
 
 /* eslint react/no-danger:0 */
-class Radar extends PureComponent {
+@autoHeight()
+export default class Radar extends Component {
   state = {
     legendData: [],
-  }
+  };
 
   componentDidMount() {
-    this.renderChart(this.props.data);
+    this.getLengendData();
   }
 
   componentWillReceiveProps(nextProps) {
-    if (!equal(this.props, nextProps)) {
-      this.renderChart(nextProps.data);
+    if (this.props.data !== nextProps.data) {
+      this.getLengendData();
     }
   }
 
-  componentWillUnmount() {
-    if (this.chart) {
-      this.chart.destroy();
-    }
-  }
+  getG2Instance = (chart) => {
+    this.chart = chart;
+  };
+
+  // for custom lengend view
+  getLengendData = () => {
+    if (!this.chart) return;
+    const geom = this.chart.getAllGeoms()[0]; // 获取所有的图形
+    const items = geom.get('dataArray') || []; // 获取图形对应的
+
+    const legendData = items.map((item) => {
+      // eslint-disable-next-line
+      const origins = item.map(t => t._origin);
+      const result = {
+        name: origins[0].name,
+        color: item[0].color,
+        checked: true,
+        value: origins.reduce((p, n) => p + n.value, 0),
+      };
+
+      return result;
+    });
+
+    this.setState({
+      legendData,
+    });
+  };
 
   handleRef = (n) => {
     this.node = n;
-  }
+  };
 
   handleLegendClick = (item, i) => {
     const newItem = item;
@@ -37,153 +60,121 @@ class Radar extends PureComponent {
     const { legendData } = this.state;
     legendData[i] = newItem;
 
+    const filteredLegendData = legendData.filter(l => l.checked).map(l => l.name);
+
     if (this.chart) {
-      const filterItem = legendData.filter(l => l.checked).map(l => l.name);
-      this.chart.filter('name', filterItem);
+      this.chart.filter('name', val => filteredLegendData.indexOf(val) > -1);
       this.chart.repaint();
     }
 
     this.setState({
       legendData,
     });
-  }
-
-  renderChart(data) {
-    const { height = 0,
-      hasLegend = true,
-      fit = true,
-      tickCount = 4,
-      margin = [24, 30, 16, 30] } = this.props;
+  };
 
-    const colors = [
-      '#1890FF', '#FACC14', '#2FC25B', '#8543E0', '#F04864', '#13C2C2', '#fa8c16', '#a0d911',
+  render() {
+    const defaultColors = [
+      '#1890FF',
+      '#FACC14',
+      '#2FC25B',
+      '#8543E0',
+      '#F04864',
+      '#13C2C2',
+      '#fa8c16',
+      '#a0d911',
     ];
 
-    if (!data || (data && data.length < 1)) {
-      return;
-    }
-
-    // clean
-    this.node.innerHTML = '';
-
-    const chart = new G2.Chart({
-      container: this.node,
-      forceFit: fit,
-      height: height - (hasLegend ? 80 : 22),
-      plotCfg: {
-        margin,
-      },
-    });
+    const {
+      data = [],
+      height = 0,
+      title,
+      hasLegend = false,
+      forceFit = true,
+      tickCount = 4,
+      padding = [35, 30, 16, 30],
+      animate = true,
+      colors = defaultColors,
+    } = this.props;
 
-    this.chart = chart;
+    const { legendData } = this.state;
 
-    chart.source(data, {
+    const scale = {
       value: {
         min: 0,
         tickCount,
       },
-    });
-
-    chart.coord('polar');
-    chart.legend(false);
-
-    chart.axis('label', {
-      line: null,
-      labelOffset: 8,
-      labels: {
-        label: {
-          fill: 'rgba(0, 0, 0, .65)',
-        },
-      },
-      grid: {
-        line: {
-          stroke: '#e9e9e9',
-          lineWidth: 1,
-          lineDash: [0, 0],
-        },
-      },
-    });
-
-    chart.axis('value', {
-      grid: {
-        type: 'polygon',
-        line: {
-          stroke: '#e9e9e9',
-          lineWidth: 1,
-          lineDash: [0, 0],
-        },
-      },
-      labels: {
-        label: {
-          fill: 'rgba(0, 0, 0, .65)',
-        },
-      },
-    });
-
-    chart.line().position('label*value').color('name', colors);
-    chart.point().position('label*value').color('name', colors).shape('circle')
-      .size(3);
-
-    chart.render();
-
-    if (hasLegend) {
-      const geom = chart.getGeoms()[0]; // 获取所有的图形
-      const items = geom.getData(); // 获取图形对应的数据
-      const legendData = items.map((item) => {
-        /* eslint no-underscore-dangle:0 */
-        const origin = item._origin;
-        const result = {
-          name: origin[0].name,
-          color: item.color,
-          checked: true,
-          value: origin.reduce((p, n) => p + n.value, 0),
-        };
-
-        return result;
-      });
-
-      this.setState({
-        legendData,
-      });
-    }
-  }
+    };
 
-  render() {
-    const { height, title, hasLegend } = this.props;
-    const { legendData } = this.state;
+    const chartHeight = height - (hasLegend ? 80 : 22);
 
     return (
       <div className={styles.radar} style={{ height }}>
         <div>
           {title && <h4>{title}</h4>}
-          <div ref={this.handleRef} />
-          {
-            hasLegend && (
-              <Row className={styles.legend}>
-                {
-                  legendData.map((item, i) => (
-                    <Col
-                      span={(24 / legendData.length)}
-                      key={item.name}
-                      onClick={() => this.handleLegendClick(item, i)}
-                    >
-                      <div className={styles.legendItem}>
-                        <p>
-                          <span className={styles.dot} style={{ backgroundColor: !item.checked ? '#aaa' : item.color }} />
-                          <span>{item.name}</span>
-                        </p>
-                        <h6>{item.value}</h6>
-                      </div>
-                    </Col>
-                  ))
-                }
-              </Row>
-            )
-          }
+          <Chart
+            scale={scale}
+            height={chartHeight}
+            forceFit={forceFit}
+            data={data}
+            padding={padding}
+            animate={animate}
+            onGetG2Instance={this.getG2Instance}
+          >
+            <Tooltip />
+            <Coord type="polar" />
+            <Axis
+              name="label"
+              line={null}
+              tickLine={null}
+              grid={{
+                lineStyle: {
+                  lineDash: null,
+                },
+                hideFirstLine: false,
+              }}
+            />
+            <Axis
+              name="value"
+              grid={{
+                type: 'polygon',
+                lineStyle: {
+                  lineDash: null,
+                },
+              }}
+            />
+            <Geom type="line" position="label*value" color={['name', colors]} size={1} />
+            <Geom
+              type="point"
+              position="label*value"
+              color={['name', colors]}
+              shape="circle"
+              size={3}
+            />
+          </Chart>
+          {hasLegend && (
+            <Row className={styles.legend}>
+              {legendData.map((item, i) => (
+                <Col
+                  span={24 / legendData.length}
+                  key={item.name}
+                  onClick={() => this.handleLegendClick(item, i)}
+                >
+                  <div className={styles.legendItem}>
+                    <p>
+                      <span
+                        className={styles.dot}
+                        style={{ backgroundColor: !item.checked ? '#aaa' : item.color }}
+                      />
+                      <span>{item.name}</span>
+                    </p>
+                    <h6>{item.value}</h6>
+                  </div>
+                </Col>
+              ))}
+            </Row>
+          )}
         </div>
       </div>
     );
   }
 }
-
-export default Radar;

+ 87 - 113
src/components/Charts/TagCloud/index.js

@@ -1,9 +1,10 @@
-import React, { PureComponent } from 'react';
-import classNames from 'classnames';
-import G2 from 'g2';
-import Cloud from 'g-cloud';
+import React, { Component } from 'react';
+import { Chart, Geom, Coord, Shape } from 'bizcharts';
+import DataSet from '@antv/data-set';
 import Debounce from 'lodash-decorators/debounce';
 import Bind from 'lodash-decorators/bind';
+import classNames from 'classnames';
+import autoHeight from '../autoHeight';
 import styles from './index.less';
 
 /* eslint no-underscore-dangle: 0 */
@@ -11,157 +12,130 @@ import styles from './index.less';
 
 const imgUrl = 'https://gw.alipayobjects.com/zos/rmsportal/gWyeGLCdFFRavBGIDzWk.png';
 
-class TagCloud extends PureComponent {
+@autoHeight()
+class TagCloud extends Component {
+  state = {
+    dv: null,
+  };
+
   componentDidMount() {
     this.initTagCloud();
     this.renderChart();
-
     window.addEventListener('resize', this.resize);
   }
 
   componentWillReceiveProps(nextProps) {
-    if (this.props.data !== nextProps.data) {
-      this.renderChart(nextProps.data);
+    if (JSON.stringify(nextProps.data) !== JSON.stringify(this.props.data)) {
+      this.renderChart(nextProps);
     }
   }
 
   componentWillUnmount() {
     window.removeEventListener('resize', this.resize);
-    this.renderChart.cancel();
   }
 
   resize = () => {
     this.renderChart();
-  }
+  };
 
-  initTagCloud = () => {
-    const { Util, Shape } = G2;
+  saveRootRef = (node) => {
+    this.root = node;
+  };
 
+  initTagCloud = () => {
     function getTextAttrs(cfg) {
-      const textAttrs = Util.mix(true, {}, {
-        fillOpacity: cfg.opacity,
-        fontSize: cfg.size,
-        rotate: cfg.origin._origin.rotate,
-        // rotate: cfg.origin._origin.rotate,
-        text: cfg.origin._origin.text,
-        textAlign: 'center',
-        fill: cfg.color,
-        textBaseline: 'Alphabetic',
-      }, cfg.style);
-      return textAttrs;
+      return Object.assign(
+        {},
+        {
+          fillOpacity: cfg.opacity,
+          fontSize: cfg.origin._origin.size,
+          rotate: cfg.origin._origin.rotate,
+          text: cfg.origin._origin.text,
+          textAlign: 'center',
+          fontFamily: cfg.origin._origin.font,
+          fill: cfg.color,
+          textBaseline: 'Alphabetic',
+        },
+        cfg.style
+      );
     }
 
     // 给point注册一个词云的shape
-    Shape.registShape('point', 'cloud', {
+    Shape.registerShape('point', 'cloud', {
       drawShape(cfg, container) {
-        cfg.points = this.parsePoints(cfg.points);
         const attrs = getTextAttrs(cfg);
-        const shape = container.addShape('text', {
-          attrs: Util.mix(attrs, {
-            x: cfg.points[0].x,
-            y: cfg.points[0].y,
+        return container.addShape('text', {
+          attrs: Object.assign(attrs, {
+            x: cfg.x,
+            y: cfg.y,
           }),
         });
-        return shape;
       },
     });
-  }
-
-  saveRootRef = (node) => {
-    this.root = node;
-  }
-
-  saveNodeRef = (node) => {
-    this.node = node;
-  }
+  };
 
   @Bind()
   @Debounce(500)
-  renderChart(newData) {
-    const data = newData || this.props.data;
-    if (!data || data.length < 1) {
-      return;
-    }
+  renderChart = (nextProps) => {
+    // const colors = ['#1890FF', '#41D9C7', '#2FC25B', '#FACC14', '#9AE65C'];
+    const { data, height } = nextProps || this.props;
 
-    const colors = ['#1890FF', '#41D9C7', '#2FC25B', '#FACC14', '#9AE65C'];
-
-    const height = this.props.height * 4;
-    let width = 0;
-    if (this.root) {
-      width = this.root.offsetWidth * 4;
+    if (data.length < 1 || !this.root) {
+      return;
     }
 
-    data.sort((a, b) => b.value - a.value);
-
-    const max = data[0].value;
-    const min = data[data.length - 1].value;
-
-    // 构造一个词云布局对象
-    const layout = new Cloud({
-      words: data,
-      width,
-      height,
-
-      rotate: () => 0,
-
-      // 设定文字大小配置函数(默认为12-24px的随机大小)
-      size: words => (((words.value - min) / (max - min)) * 50) + 30,
-
-      // 设定文字内容
-      text: words => words.name,
-    });
-
-    layout.image(imgUrl, (imageCloud) => {
-      // clean
-      if (this.node) {
-        this.node.innerHTML = '';
-      }
-
-      // 执行词云布局函数,并在回调函数中调用G2对结果进行绘制
-      imageCloud.exec((texts) => {
-        const chart = new G2.Chart({
-          container: this.node,
-          width,
-          height,
-          plotCfg: {
-            margin: 0,
-          },
-        });
-
-        chart.legend(false);
-        chart.axis(false);
-        chart.tooltip(false);
-
-        chart.source(texts);
-
-        // 将词云坐标系调整为G2的坐标系
-        chart.coord().reflect();
-
-        chart
-          .point()
-          .position('x*y')
-          .color('text', colors)
-          .size('size', size => size)
-          .shape('cloud')
-          .style({
-            fontStyle: texts[0].style,
-            fontFamily: texts[0].font,
-            fontWeight: texts[0].weight,
-          });
+    const h = height * 4;
+    const w = this.root.offsetWidth * 4;
+
+    const imageMask = new Image();
+    imageMask.crossOrigin = '';
+    imageMask.src = imgUrl;
+
+    imageMask.onload = () => {
+      const dv = new DataSet.View().source(data);
+      const range = dv.range('value');
+      const [min, max] = range;
+      dv.transform({
+        type: 'tag-cloud',
+        fields: ['name', 'value'],
+        imageMask,
+        font: 'Verdana',
+        size: [w, h], // 宽高设置最好根据 imageMask 做调整
+        padding: 5,
+        timeInterval: 5000, // max execute time
+        rotate() {
+          return 0;
+        },
+        fontSize(d) {
+          // eslint-disable-next-line
+          return Math.pow((d.value - min) / (max - min), 2) * (70 - 20) + 20;
+        },
+      });
 
-        chart.render();
+      this.setState({
+        dv,
+        w,
+        h,
       });
-    });
-  }
+    };
+  };
 
   render() {
+    const { className, height } = this.props;
+    const { dv, w, h } = this.state;
+
     return (
       <div
-        className={classNames(styles.tagCloud, this.props.className)}
+        className={classNames(styles.tagCloud, className)}
+        style={{ width: '100%', height }}
         ref={this.saveRootRef}
-        style={{ width: '100%' }}
       >
-        <div ref={this.saveNodeRef} style={{ height: this.props.height }} />
+        {dv && (
+          <Chart width={w} height={h} data={dv} padding={0}>
+            <Coord reflect="y" />
+            <Geom type="point" position="x*y" color="text" shape="cloud" />
+          </Chart>
+        )}
       </div>
     );
   }

+ 1 - 0
src/components/Charts/TimelineChart/index.d.ts

@@ -6,6 +6,7 @@ export interface TimelineChartProps {
     y2: string;
   }>;
   titleMap: { y1: string; y2: string };
+  padding?: [number, number, number, number];
   height?: number;
 }
 

+ 103 - 105
src/components/Charts/TimelineChart/index.js

@@ -1,125 +1,123 @@
-import React, { Component } from 'react';
-import G2 from 'g2';
-import Slider from 'g2-plugin-slider';
+import React from 'react';
+import { Chart, Tooltip, Geom, Legend, Axis } from 'bizcharts';
+import DataSet from '@antv/data-set';
+import Slider from 'bizcharts-plugin-slider';
+import autoHeight from '../autoHeight';
 import styles from './index.less';
 
-class TimelineChart extends Component {
-  componentDidMount() {
-    this.renderChart(this.props.data);
-  }
-
-  componentWillReceiveProps(nextProps) {
-    if (nextProps.data !== this.props.data) {
-      this.renderChart(nextProps.data);
-    }
-  }
-
-  componentWillUnmount() {
-    if (this.chart) {
-      this.chart.destroy();
-    }
-    if (this.slider) {
-      this.slider.destroy();
-    }
-  }
-
-  sliderId = `timeline-chart-slider-${Math.random() * 1000}`
-
-  handleRef = (n) => {
-    this.node = n;
-  }
-
-  renderChart(data) {
-    const { height = 400, margin = [60, 20, 40, 40], titleMap, borderWidth = 2 } = this.props;
-
-    if (!data || (data && data.length < 1)) {
-      return;
-    }
-
-    // clean
-    if (this.sliderId) {
-      document.getElementById(this.sliderId).innerHTML = '';
-    }
-    this.node.innerHTML = '';
-
-    const chart = new G2.Chart({
-      container: this.node,
-      forceFit: true,
-      height,
-      plotCfg: {
-        margin,
+@autoHeight()
+export default class TimelineChart extends React.Component {
+  render() {
+    const {
+      title,
+      height = 400,
+      padding = [60, 20, 40, 40],
+      titleMap = {
+        y1: 'y1',
+        y2: 'y2',
       },
-    });
-
-    chart.axis('x', {
-      title: false,
-    });
-    chart.axis('y1', {
-      title: false,
-    });
-    chart.axis('y2', false);
-
-    chart.legend({
-      mode: false,
-      position: 'top',
-    });
+      borderWidth = 2,
+      data = [
+        {
+          x: 0,
+          y1: 0,
+          y2: 0,
+        },
+      ],
+    } = this.props;
+
+    data.sort((a, b) => a.x - b.x);
 
     let max;
     if (data[0] && data[0].y1 && data[0].y2) {
-      max = Math.max(data.sort((a, b) => b.y1 - a.y1)[0].y1,
-        data.sort((a, b) => b.y2 - a.y2)[0].y2);
+      max = Math.max(
+        [...data].sort((a, b) => b.y1 - a.y1)[0].y1,
+        [...data].sort((a, b) => b.y2 - a.y2)[0].y2
+      );
     }
 
-    chart.source(data, {
-      x: {
-        type: 'timeCat',
-        tickCount: 16,
-        mask: 'HH:MM',
-        range: [0, 1],
-      },
-      y1: {
-        alias: titleMap.y1,
-        max,
-        min: 0,
+    const ds = new DataSet({
+      state: {
+        start: data[0].x,
+        end: data[data.length - 1].x,
       },
-      y2: {
-        alias: titleMap.y2,
+    });
+
+    const dv = ds.createView();
+    dv
+      .source(data)
+      .transform({
+        type: 'filter',
+        callback: (obj) => {
+          const date = obj.x;
+          return date <= ds.state.end && date >= ds.state.start;
+        },
+      })
+      .transform({
+        type: 'map',
+        callback(row) {
+          const newRow = { ...row };
+          newRow[titleMap.y1] = row.y1;
+          newRow[titleMap.y2] = row.y2;
+          return newRow;
+        },
+      })
+      .transform({
+        type: 'fold',
+        fields: [titleMap.y1, titleMap.y2], // 展开字段集
+        key: 'key', // key字段
+        value: 'value', // value字段
+      });
+
+    const timeScale = {
+      type: 'time',
+      tickCount: 10,
+      mask: 'HH:MM',
+      range: [0, 1],
+    };
+
+    const cols = {
+      x: timeScale,
+      value: {
         max,
         min: 0,
       },
-    });
-
-    chart.line().position('x*y1').color('#1890FF').size(borderWidth);
-    chart.line().position('x*y2').color('#2FC25B').size(borderWidth);
-
-    this.chart = chart;
-
-    /* eslint new-cap:0 */
-    const slider = new Slider({
-      domId: this.sliderId,
-      height: 26,
-      xDim: 'x',
-      yDim: 'y1',
-      charts: [chart],
-    });
-    slider.render();
-
-    this.slider = slider;
-  }
-
-  render() {
-    const { height, title } = this.props;
+    };
+
+    const SliderGen = () => (
+      <Slider
+        padding={[0, padding[1] + 20, 0, padding[3]]}
+        width="auto"
+        height={26}
+        xAxis="x"
+        yAxis="y1"
+        scales={{ x: timeScale }}
+        data={data}
+        start={ds.state.start}
+        end={ds.state.end}
+        backgroundChart={{ type: 'line' }}
+        onChange={({ startValue, endValue }) => {
+          ds.setState('start', startValue);
+          ds.setState('end', endValue);
+        }}
+      />
+    );
 
     return (
-      <div className={styles.timelineChart} style={{ height }}>
+      <div className={styles.timelineChart} style={{ height: height + 30 }}>
         <div>
-          { title && <h4>{title}</h4>}
-          <div ref={this.handleRef} />
-          <div id={this.sliderId} />
+          {title && <h4>{title}</h4>}
+          <Chart height={height} padding={padding} data={dv} scale={cols} forceFit>
+            <Axis name="x" />
+            <Tooltip />
+            <Legend name="key" position="top" />
+            <Geom type="line" position="x*value" size={borderWidth} color="key" />
+          </Chart>
+          <div style={{ marginRight: -20 }}>
+            <SliderGen />
+          </div>
         </div>
       </div>
     );
   }
 }
-
-export default TimelineChart;

+ 22 - 25
src/components/Charts/WaterWave/index.js

@@ -1,16 +1,16 @@
 import React, { PureComponent } from 'react';
+import autoHeight from '../autoHeight';
 import styles from './index.less';
 
 /* eslint no-return-assign: 0 */
+/* eslint no-mixed-operators: 0 */
 // riddle: https://riddle.alibaba-inc.com/riddles/2d9a4b90
 
-class WaterWave extends PureComponent {
-  static defaultProps = {
-    height: 160,
-  }
+@autoHeight()
+export default class WaterWave extends PureComponent {
   state = {
     radio: 1,
-  }
+  };
 
   componentDidMount() {
     this.renderChart();
@@ -33,7 +33,7 @@ class WaterWave extends PureComponent {
     this.setState({
       radio: offsetWidth < height ? offsetWidth / height : 1,
     });
-  }
+  };
 
   renderChart() {
     const { percent, color = '#1890FF' } = this.props;
@@ -51,12 +51,12 @@ class WaterWave extends PureComponent {
     const canvasHeight = canvas.height;
     const radius = canvasWidth / 2;
     const lineWidth = 2;
-    const cR = radius - (lineWidth);
+    const cR = radius - lineWidth;
 
     ctx.beginPath();
     ctx.lineWidth = lineWidth * 2;
 
-    const axisLength = canvasWidth - (lineWidth);
+    const axisLength = canvasWidth - lineWidth;
     const unit = axisLength / 8;
     const range = 0.2; // 振幅
     let currRange = range;
@@ -66,15 +66,12 @@ class WaterWave extends PureComponent {
     const waveupsp = 0.005; // 水波上涨速度
 
     let arcStack = [];
-    const bR = radius - (lineWidth);
+    const bR = radius - lineWidth;
     const circleOffset = -(Math.PI / 2);
     let circleLock = true;
 
-    for (let i = circleOffset; i < circleOffset + (2 * Math.PI); i += 1 / (8 * Math.PI)) {
-      arcStack.push([
-        radius + (bR * Math.cos(i)),
-        radius + (bR * Math.sin(i)),
-      ]);
+    for (let i = circleOffset; i < circleOffset + 2 * Math.PI; i += 1 / (8 * Math.PI)) {
+      arcStack.push([radius + bR * Math.cos(i), radius + bR * Math.sin(i)]);
     }
 
     const cStartPoint = arcStack.shift();
@@ -87,10 +84,10 @@ class WaterWave extends PureComponent {
 
       const sinStack = [];
       for (let i = xOffset; i <= xOffset + axisLength; i += 20 / axisLength) {
-        const x = sp + ((xOffset + i) / unit);
+        const x = sp + (xOffset + i) / unit;
         const y = Math.sin(x) * currRange;
         const dx = i;
-        const dy = ((2 * cR * (1 - currData)) + (radius - cR)) - (unit * y);
+        const dy = 2 * cR * (1 - currData) + (radius - cR) - unit * y;
 
         ctx.lineTo(dx, dy);
         sinStack.push([dx, dy]);
@@ -130,7 +127,7 @@ class WaterWave extends PureComponent {
 
           ctx.beginPath();
           ctx.save();
-          ctx.arc(radius, radius, radius - (3 * lineWidth), 0, 2 * Math.PI, 1);
+          ctx.arc(radius, radius, radius - 3 * lineWidth, 0, 2 * Math.PI, 1);
 
           ctx.restore();
           ctx.clip();
@@ -157,10 +154,10 @@ class WaterWave extends PureComponent {
             currRange -= t;
           }
         }
-        if ((data - currData) > 0) {
+        if (data - currData > 0) {
           currData += waveupsp;
         }
-        if ((data - currData) < 0) {
+        if (data - currData < 0) {
           currData -= waveupsp;
         }
 
@@ -177,7 +174,11 @@ class WaterWave extends PureComponent {
     const { radio } = this.state;
     const { percent, title, height } = this.props;
     return (
-      <div className={styles.waterWave} ref={n => (this.root = n)} style={{ transform: `scale(${radio})` }}>
+      <div
+        className={styles.waterWave}
+        ref={n => (this.root = n)}
+        style={{ transform: `scale(${radio})` }}
+      >
         <div style={{ width: height, height, overflow: 'hidden' }}>
           <canvas
             className={styles.waterWaveCanvasWrapper}
@@ -187,14 +188,10 @@ class WaterWave extends PureComponent {
           />
         </div>
         <div className={styles.text} style={{ width: height }}>
-          {
-            title && <span>{title}</span>
-          }
+          {title && <span>{title}</span>}
           <h4>{percent}%</h4>
         </div>
       </div>
     );
   }
 }
-
-export default WaterWave;

+ 63 - 0
src/components/Charts/autoHeight.js

@@ -0,0 +1,63 @@
+/* eslint eqeqeq: 0 */
+import React from 'react';
+
+function computeHeight(node) {
+  const totalHeight = parseInt(getComputedStyle(node).height, 10);
+  const padding =
+    parseInt(getComputedStyle(node).paddingTop, 10) +
+    parseInt(getComputedStyle(node).paddingBottom, 10);
+  return totalHeight - padding;
+}
+
+function getAutoHeight(n) {
+  if (!n) {
+    return 0;
+  }
+
+  let node = n;
+
+  let height = computeHeight(node);
+
+  while (!height) {
+    node = node.parentNode;
+    if (node) {
+      height = computeHeight(node);
+    } else {
+      break;
+    }
+  }
+
+  return height;
+}
+
+const autoHeight = () => (WrappedComponent) => {
+  return class extends React.Component {
+    state = {
+      computedHeight: 0,
+    };
+
+    componentDidMount() {
+      const { height } = this.props;
+      if (!height) {
+        const h = getAutoHeight(this.root);
+        // eslint-disable-next-line
+        this.setState({ computedHeight: h });
+      }
+    }
+
+    handleRoot = (node) => {
+      this.root = node;
+    };
+
+    render() {
+      const { height } = this.props;
+      const { computedHeight } = this.state;
+      const h = height || computedHeight;
+      return (
+        <div ref={this.handleRoot}>{h > 0 && <WrappedComponent {...this.props} height={h} />}</div>
+      );
+    }
+  };
+};
+
+export default autoHeight;

+ 0 - 17
src/components/Charts/equal.js

@@ -1,17 +0,0 @@
-/* eslint eqeqeq: 0 */
-
-function equal(old, target) {
-  let r = true;
-  for (const prop in old) {
-    if (typeof old[prop] === 'function' && typeof target[prop] === 'function') {
-      if (old[prop].toString() != target[prop].toString()) {
-        r = false;
-      }
-    } else if (old[prop] != target[prop]) {
-      r = false;
-    }
-  }
-  return r;
-}
-
-export default equal;

+ 2 - 24
src/g2.js

@@ -1,26 +1,4 @@
 // 全局 G2 设置
-import G2 from 'g2';
+import { track } from 'bizcharts';
 
-G2.track(false);
-
-const colors = [
-  '#8543E0', '#F04864', '#FACC14', '#1890FF', '#13C2C2', '#2FC25B', '#fa8c16', '#a0d911',
-];
-
-const config = {
-  ...G2.Theme,
-  defaultColor: '#1089ff',
-  colors: {
-    default: colors,
-    intervalStack: colors,
-  },
-  tooltip: {
-    background: {
-      radius: 4,
-      fill: '#000',
-      fillOpacity: 0.75,
-    },
-  },
-};
-
-G2.Global.setTheme(config);
+track(false);

+ 4 - 2
src/index.ejs

@@ -1,5 +1,6 @@
 <!DOCTYPE html>
 <html lang="en">
+
 <head>
   <meta charset="UTF-8">
   <meta http-equiv="X-UA-Compatible" content="IE=edge">
@@ -7,8 +8,9 @@
   <title>Ant Design Pro</title>
   <link rel="icon" href="/favicon.png" type="image/x-icon">
 </head>
+
 <body>
   <div id="root"></div>
-  <script src="https://gw.alipayobjects.com/as/g/??datavis/g2/2.3.12/index.js,datavis/g-cloud/1.0.2/index.js,datavis/g2-plugin-slider/1.2.1/slider.js"></script>
 </body>
-</html>
+
+</html>

+ 7 - 1
src/index.less

@@ -1,7 +1,13 @@
-html, body, :global(#root) {
+html,
+body,
+:global(#root) {
   height: 100%;
 }
 
+canvas {
+  display: block;
+}
+
 body {
   text-rendering: optimizeLegibility;
   -webkit-font-smoothing: antialiased;

+ 92 - 91
src/routes/Dashboard/Analysis.js

@@ -1,9 +1,29 @@
 import React, { Component } from 'react';
 import { connect } from 'dva';
-import { Row, Col, Icon, Card, Tabs, Table, Radio, DatePicker, Tooltip, Menu, Dropdown } from 'antd';
+import {
+  Row,
+  Col,
+  Icon,
+  Card,
+  Tabs,
+  Table,
+  Radio,
+  DatePicker,
+  Tooltip,
+  Menu,
+  Dropdown,
+} from 'antd';
 import numeral from 'numeral';
 import {
-  ChartCard, yuan, MiniArea, MiniBar, MiniProgress, Field, Bar, Pie, TimelineChart,
+  ChartCard,
+  yuan,
+  MiniArea,
+  MiniBar,
+  MiniProgress,
+  Field,
+  Bar,
+  Pie,
+  TimelineChart,
 } from '../../components/Charts';
 import Trend from '../../components/Trend';
 import NumberInfo from '../../components/NumberInfo';
@@ -30,7 +50,7 @@ export default class Analysis extends Component {
     salesType: 'all',
     currentTabKey: '',
     rangePickerValue: getTimeDistance('year'),
-  }
+  };
 
   componentDidMount() {
     this.props.dispatch({
@@ -49,13 +69,13 @@ export default class Analysis extends Component {
     this.setState({
       salesType: e.target.value,
     });
-  }
+  };
 
   handleTabChange = (key) => {
     this.setState({
       currentTabKey: key,
     });
-  }
+  };
 
   handleRangePickerChange = (rangePickerValue) => {
     this.setState({
@@ -65,7 +85,7 @@ export default class Analysis extends Component {
     this.props.dispatch({
       type: 'chart/fetchSalesData',
     });
-  }
+  };
 
   selectDate = (type) => {
     this.setState({
@@ -75,7 +95,7 @@ export default class Analysis extends Component {
     this.props.dispatch({
       type: 'chart/fetchSalesData',
     });
-  }
+  };
 
   isActive(type) {
     const { rangePickerValue } = this.state;
@@ -83,7 +103,10 @@ export default class Analysis extends Component {
     if (!rangePickerValue[0] || !rangePickerValue[1]) {
       return;
     }
-    if (rangePickerValue[0].isSame(value[0], 'day') && rangePickerValue[1].isSame(value[1], 'day')) {
+    if (
+      rangePickerValue[0].isSame(value[0], 'day') &&
+      rangePickerValue[1].isSame(value[1], 'day')
+    ) {
       return styles.currentDate;
     }
   }
@@ -104,10 +127,10 @@ export default class Analysis extends Component {
       loading,
     } = chart;
 
-    const salesPieData = salesType === 'all' ?
-      salesTypeData
-      :
-      (salesType === 'online' ? salesTypeDataOnline : salesTypeDataOffline);
+    const salesPieData =
+      salesType === 'all'
+        ? salesTypeData
+        : salesType === 'online' ? salesTypeDataOnline : salesTypeDataOffline;
 
     const menu = (
       <Menu>
@@ -191,13 +214,13 @@ export default class Analysis extends Component {
             subTitle="转化率"
             gap={2}
             total={`${data.cvr * 100}%`}
-            theme={(currentKey !== data.name) && 'light'}
+            theme={currentKey !== data.name && 'light'}
           />
         </Col>
         <Col span={12} style={{ paddingTop: 36 }}>
           <Pie
             animate={false}
-            color={(currentKey !== data.name) && '#BDE4FF'}
+            color={currentKey !== data.name && '#BDE4FF'}
             inner={0.55}
             tooltip={false}
             margin={[0, 0, 0, 0]}
@@ -224,7 +247,11 @@ export default class Analysis extends Component {
             <ChartCard
               bordered={false}
               title="总销售额"
-              action={<Tooltip title="指标说明"><Icon type="info-circle-o" /></Tooltip>}
+              action={
+                <Tooltip title="指标说明">
+                  <Icon type="info-circle-o" />
+                </Tooltip>
+              }
               total={yuan(126560)}
               footer={<Field label="日均销售额" value={`¥${numeral(12423).format('0,0')}`} />}
               contentHeight={46}
@@ -241,38 +268,43 @@ export default class Analysis extends Component {
             <ChartCard
               bordered={false}
               title="访问量"
-              action={<Tooltip title="指标说明"><Icon type="info-circle-o" /></Tooltip>}
+              action={
+                <Tooltip title="指标说明">
+                  <Icon type="info-circle-o" />
+                </Tooltip>
+              }
               total={numeral(8846).format('0,0')}
               footer={<Field label="日访问量" value={numeral(1234).format('0,0')} />}
               contentHeight={46}
             >
-              <MiniArea
-                color="#975FE4"
-                height={46}
-                data={visitData}
-              />
+              <MiniArea color="#975FE4" data={visitData} />
             </ChartCard>
           </Col>
           <Col {...topColResponsiveProps}>
             <ChartCard
               bordered={false}
               title="支付笔数"
-              action={<Tooltip title="指标说明"><Icon type="info-circle-o" /></Tooltip>}
+              action={
+                <Tooltip title="指标说明">
+                  <Icon type="info-circle-o" />
+                </Tooltip>
+              }
               total={numeral(6560).format('0,0')}
               footer={<Field label="转化率" value="60%" />}
               contentHeight={46}
             >
-              <MiniBar
-                height={46}
-                data={visitData}
-              />
+              <MiniBar data={visitData} />
             </ChartCard>
           </Col>
           <Col {...topColResponsiveProps}>
             <ChartCard
               bordered={false}
               title="运营活动效果"
-              action={<Tooltip title="指标说明"><Icon type="info-circle-o" /></Tooltip>}
+              action={
+                <Tooltip title="指标说明">
+                  <Icon type="info-circle-o" />
+                </Tooltip>
+              }
               total="78%"
               footer={
                 <div style={{ whiteSpace: 'nowrap', overflow: 'hidden' }}>
@@ -291,37 +323,27 @@ export default class Analysis extends Component {
           </Col>
         </Row>
 
-        <Card
-          loading={loading}
-          bordered={false}
-          bodyStyle={{ padding: 0 }}
-        >
+        <Card loading={loading} bordered={false} bodyStyle={{ padding: 0 }}>
           <div className={styles.salesCard}>
             <Tabs tabBarExtraContent={salesExtra} size="large" tabBarStyle={{ marginBottom: 24 }}>
               <TabPane tab="销售额" key="sales">
                 <Row>
                   <Col xl={16} lg={12} md={12} sm={24} xs={24}>
                     <div className={styles.salesBar}>
-                      <Bar
-                        height={295}
-                        title="销售额趋势"
-                        data={salesData}
-                      />
+                      <Bar height={270} title="销售额趋势" data={salesData} />
                     </div>
                   </Col>
                   <Col xl={8} lg={12} md={12} sm={24} xs={24}>
                     <div className={styles.salesRank}>
                       <h4 className={styles.rankingTitle}>门店销售额排名</h4>
                       <ul className={styles.rankingList}>
-                        {
-                          rankingListData.map((item, i) => (
-                            <li key={item.title}>
-                              <span className={(i < 3) ? styles.active : ''}>{i + 1}</span>
-                              <span>{item.title}</span>
-                              <span>{numeral(item.total).format('0,0')}</span>
-                            </li>
-                          ))
-                        }
+                        {rankingListData.map((item, i) => (
+                          <li key={item.title}>
+                            <span className={i < 3 ? styles.active : ''}>{i + 1}</span>
+                            <span>{item.title}</span>
+                            <span>{numeral(item.total).format('0,0')}</span>
+                          </li>
+                        ))}
                       </ul>
                     </div>
                   </Col>
@@ -331,26 +353,20 @@ export default class Analysis extends Component {
                 <Row>
                   <Col xl={16} lg={12} md={12} sm={24} xs={24}>
                     <div className={styles.salesBar}>
-                      <Bar
-                        height={292}
-                        title="访问量趋势"
-                        data={salesData}
-                      />
+                      <Bar height={292} title="访问量趋势" data={salesData} />
                     </div>
                   </Col>
                   <Col xl={8} lg={12} md={12} sm={24} xs={24}>
                     <div className={styles.salesRank}>
                       <h4 className={styles.rankingTitle}>门店访问量排名</h4>
                       <ul className={styles.rankingList}>
-                        {
-                          rankingListData.map((item, i) => (
-                            <li key={item.title}>
-                              <span className={(i < 3) && styles.active}>{i + 1}</span>
-                              <span>{item.title}</span>
-                              <span>{numeral(item.total).format('0,0')}</span>
-                            </li>
-                          ))
-                        }
+                        {rankingListData.map((item, i) => (
+                          <li key={item.title}>
+                            <span className={i < 3 && styles.active}>{i + 1}</span>
+                            <span>{item.title}</span>
+                            <span>{numeral(item.total).format('0,0')}</span>
+                          </li>
+                        ))}
                       </ul>
                     </div>
                   </Col>
@@ -385,11 +401,7 @@ export default class Analysis extends Component {
                     status="up"
                     subTotal={17.1}
                   />
-                  <MiniArea
-                    line
-                    height={45}
-                    data={visitData2}
-                  />
+                  <MiniArea line height={45} data={visitData2} />
                 </Col>
                 <Col sm={12} xs={24} style={{ marginBottom: 24 }}>
                   <NumberInfo
@@ -399,11 +411,7 @@ export default class Analysis extends Component {
                     subTotal={26.2}
                     gap={8}
                   />
-                  <MiniArea
-                    line
-                    height={45}
-                    data={visitData2}
-                  />
+                  <MiniArea line height={45} data={visitData2} />
                 </Col>
               </Row>
               <Table
@@ -425,7 +433,7 @@ export default class Analysis extends Component {
               bordered={false}
               title="销售额类别占比"
               bodyStyle={{ padding: 24 }}
-              extra={(
+              extra={
                 <div className={styles.salesCardExtra}>
                   {iconGroup}
                   <div className={styles.salesTypeRadio}>
@@ -436,7 +444,7 @@ export default class Analysis extends Component {
                     </Radio.Group>
                   </div>
                 </div>
-              )}
+              }
               style={{ marginTop: 24, minHeight: 509 }}
             >
               <h4 style={{ marginTop: 8, marginBottom: 32 }}>销售额</h4>
@@ -460,25 +468,18 @@ export default class Analysis extends Component {
           bodyStyle={{ padding: '0 0 32px 0' }}
           style={{ marginTop: 32 }}
         >
-          <Tabs
-            activeKey={activeKey}
-            onChange={this.handleTabChange}
-          >
-            {
-              offlineData.map(shop => (
-                <TabPane
-                  tab={<CustomTab data={shop} currentTabKey={activeKey} />}
-                  key={shop.name}
-                >
-                  <div style={{ padding: '0 24px' }}>
-                    <TimelineChart
-                      data={offlineChartData}
-                      titleMap={{ y1: '客流量', y2: '支付笔数' }}
-                    />
-                  </div>
-                </TabPane>)
-              )
-            }
+          <Tabs activeKey={activeKey} onChange={this.handleTabChange}>
+            {offlineData.map(shop => (
+              <TabPane tab={<CustomTab data={shop} currentTabKey={activeKey} />} key={shop.name}>
+                <div style={{ padding: '0 24px' }}>
+                  <TimelineChart
+                    height={400}
+                    data={offlineChartData}
+                    titleMap={{ y1: '客流量', y2: '支付笔数' }}
+                  />
+                </div>
+              </TabPane>
+            ))}
           </Tabs>
         </Card>
       </div>

+ 14 - 20
src/routes/Dashboard/Monitor.js

@@ -40,16 +40,10 @@ export default class Monitor extends PureComponent {
                   />
                 </Col>
                 <Col md={6} sm={12} xs={24}>
-                  <NumberInfo
-                    subTitle="销售目标完成率"
-                    total="92%"
-                  />
+                  <NumberInfo subTitle="销售目标完成率" total="92%" />
                 </Col>
                 <Col md={6} sm={12} xs={24}>
-                  <NumberInfo
-                    subTitle="活动剩余时间"
-                    total={<CountDown target={targetTime} />}
-                  />
+                  <NumberInfo subTitle="活动剩余时间" total={<CountDown target={targetTime} />} />
                 </Col>
                 <Col md={6} sm={12} xs={24}>
                   <NumberInfo
@@ -61,7 +55,10 @@ export default class Monitor extends PureComponent {
               </Row>
               <div className={styles.mapChart}>
                 <Tooltip title="等待后期实现">
-                  <img src="https://gw.alipayobjects.com/zos/rmsportal/HBWnDEUXCnGnGrRfrpKa.png" alt="map" />
+                  <img
+                    src="https://gw.alipayobjects.com/zos/rmsportal/HBWnDEUXCnGnGrRfrpKa.png"
+                    alt="map"
+                  />
                 </Tooltip>
               </div>
             </Card>
@@ -143,20 +140,17 @@ export default class Monitor extends PureComponent {
             </Card>
           </Col>
           <Col xl={6} lg={12} sm={24} xs={24} style={{ marginBottom: 24 }}>
-            <Card title="热门搜索" bordered={false} bodyStyle={{ overflow: 'hidden' }}>
-              <TagCloud
-                data={tags}
-                height={161}
-              />
+            <Card title="热门搜索" bordered={false}>
+              <TagCloud data={tags} height={161} />
             </Card>
           </Col>
           <Col xl={6} lg={12} sm={24} xs={24} style={{ marginBottom: 24 }}>
-            <Card title="资源剩余" bodyStyle={{ textAlign: 'center', fontSize: 0 }} bordered={false}>
-              <WaterWave
-                height={161}
-                title="补贴资金剩余"
-                percent={34}
-              />
+            <Card
+              title="资源剩余"
+              bodyStyle={{ textAlign: 'center', fontSize: 0 }}
+              bordered={false}
+            >
+              <WaterWave height={161} title="补贴资金剩余" percent={34} />
             </Card>
           </Col>
         </Row>