浏览代码

Better pie responsive (#37)

* refactor Pie

* Less js calculation

* Responsive Pie

* Fix pies height

* use react-fittext
偏右 8 年之前
父节点
当前提交
c2f34d5246

+ 2 - 1
package.json

@@ -30,7 +30,8 @@
     "qs": "^6.5.0",
     "react": "^16.0.0",
     "react-document-title": "^2.0.3",
-    "react-dom": "^16.0.0"
+    "react-dom": "^16.0.0",
+    "react-fittext": "^1.0.0"
   },
   "devDependencies": {
     "babel-eslint": "^8.0.1",

+ 43 - 67
src/components/Charts/Pie/index.js

@@ -1,6 +1,8 @@
 import React, { Component } from 'react';
 import G2 from 'g2';
 import { Divider } from 'antd';
+import classNames from 'classnames';
+import ReactFitText from 'react-fittext';
 import equal from '../equal';
 import styles from './index.less';
 
@@ -8,8 +10,7 @@ import styles from './index.less';
 class Pie extends Component {
   state = {
     legendData: [],
-    left: undefined,
-  }
+  };
 
   componentDidMount() {
     this.renderChart(this.props.data);
@@ -30,9 +31,6 @@ class Pie extends Component {
   handleRef = (n) => {
     this.node = n;
   }
-  handleTotalRef = (n) => {
-    this.totalNode = n;
-  }
 
   handleLegendClick = (item, i) => {
     const newItem = item;
@@ -54,8 +52,9 @@ class Pie extends Component {
 
   renderChart(data) {
     const {
-      title, height = 0,
-      hasLegend, fit = true,
+      height = 0,
+      hasLegend,
+      fit = true,
       margin, percent, color,
       inner = 0.75,
       animate = true,
@@ -97,19 +96,6 @@ class Pie extends Component {
       return;
     }
 
-    let m = margin;
-    if (!margin) {
-      if (hasLegend) {
-        m = [24, 240, 24, 8];
-      } else if (percent) {
-        m = [0, 0, 0, 0];
-      } else {
-        m = [24, 0, 24, 0];
-      }
-    }
-
-    const h = title ? (height + m[0] + m[2] + (-46)) : (height + m[0] + m[2]);
-
     // clean
     this.node.innerHTML = '';
 
@@ -118,9 +104,9 @@ class Pie extends Component {
     const chart = new G2.Chart({
       container: this.node,
       forceFit: fit,
-      height: h,
+      height,
       plotCfg: {
-        margin: m,
+        margin,
       },
       animate,
     });
@@ -176,36 +162,25 @@ class Pie extends Component {
 
     this.setState({
       legendData,
-    }, () => {
-      let left = 0;
-      if (this.totalNode) {
-        left = -((this.totalNode.offsetWidth / 2) + ((margin || m)[1] / 2)) + lineWidth;
-      }
-      this.setState({ left });
     });
   }
 
   render() {
-    const { height, title, valueFormat, subTitle, total, hasLegend } = this.props;
-    const { legendData, left } = this.state;
-    const mt = -(((legendData.length * 38) - 16) / 2);
+    const { valueFormat, subTitle, total, hasLegend, className, style } = this.props;
+    const { legendData } = this.state;
+    const pieClassName = classNames(styles.pie, className, {
+      [styles.hasLegend]: !!hasLegend,
+    });
 
     return (
-      <div className={styles.pie} style={{ height }}>
-        <div>
-          {title && <h4 className={styles.title}>{title}</h4>}
-          <div className={styles.content}>
-            <div ref={this.handleRef} />
+      <div className={pieClassName} style={style}>
+        <ReactFitText maxFontSize={40}>
+          <div className={styles.chart}>
+            <div ref={this.handleRef} style={{ fontSize: 0 }} />
             {
               (subTitle || total) && (
-                <div
-                  className={styles.total}
-                  ref={this.handleTotalRef}
-                  style={{ marginLeft: left, opacity: left ? 1 : 0 }}
-                >
-                  {
-                    subTitle && <h4 className="pie-sub-title">{subTitle}</h4>
-                  }
+                <div className={styles.total}>
+                  {subTitle && <h4 className="pie-sub-title">{subTitle}</h4>}
                   {
                     // eslint-disable-next-line
                     total && <p className="pie-stat" dangerouslySetInnerHTML={{ __html: total }} />
@@ -213,30 +188,31 @@ class Pie extends Component {
                 </div>
               )
             }
-            {
-              hasLegend && (
-                <ul className={styles.legend} style={{ marginTop: mt }}>
-                  {
-                    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>
-        </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>
+          )
+        }
       </div>
     );
   }

+ 27 - 5
src/components/Charts/Pie/index.less

@@ -2,19 +2,28 @@
 @import "../../../utils/utils.less";
 
 .pie {
-  .content {
+  position: relative;
+  .chart {
     position: relative;
   }
+  &.hasLegend .chart {
+    width: ~"calc(100% - 264px)";
+    margin-left: 24px;
+  }
   .legend {
     position: absolute;
-    top: 50%;
     right: 0;
     min-width: 200px;
+    top: 50%;
+    transform: translateY(-50%);
     li {
       cursor: pointer;
       margin-bottom: 16px;
       height: 22px;
       line-height: 22px;
+      &:last-child {
+        margin-bottom: 0;
+      }
     }
   }
   .dot {
@@ -47,13 +56,12 @@
     margin-bottom: 8px;
   }
   .total {
-    opacity: 0;
     position: absolute;
     left: 50%;
     top: 50%;
-    margin-top: -34px;
     text-align: center;
     height: 62px;
+    transform: translate(-50%, -50%);
     & > h4 {
       color: @text-color-secondary;
       font-size: 14px;
@@ -65,9 +73,23 @@
     & > p {
       color: @heading-color;
       display: block;
-      font-size: 30px;
+      font-size: 1.3em;
       height: 32px;
       line-height: 32px;
+      white-space: nowrap;
+    }
+  }
+}
+
+@media screen and (max-width: @screen-sm) {
+  .pie {
+    &.hasLegend .chart {
+      width: 100%;
+      margin: 0 0 32px 0;
+    }
+    .legend {
+      position: static;
+      transform: none;
     }
   }
 }

+ 0 - 1
src/components/Charts/index.md

@@ -71,7 +71,6 @@ Ant Design Pro 提供的业务中常用的图表类型,都是基于 [G2](https
 | hasLegend | 是否显示 legend | boolean | `false` |
 | margin | 图表内部间距 | array | \[24, 0, 24, 0\] |
 | percent | 占比 | number | - |
-| title | 图表标题 | ReactNode\|string | - |
 | tooltip | 是否显示 tooltip | boolean | true |
 | valueFormat | 显示值的格式化函数 | function | - |
 | subTitle | 图表子标题 | ReactNode\|string | - |

+ 11 - 12
src/routes/Dashboard/Analysis.js

@@ -436,18 +436,17 @@ export default class Analysis extends Component {
               )}
               style={{ marginTop: 24 }}
             >
-              <div style={{ marginTop: 8, marginBottom: 77 }}>
-                <Pie
-                  hasLegend
-                  title="销售额"
-                  subTitle="销售额"
-                  total={yuan(salesPieData.reduce((pre, now) => now.y + pre, 0))}
-                  data={salesPieData}
-                  valueFormat={val => yuan(val)}
-                  height={268}
-                  lineWidth={4}
-                />
-              </div>
+              <h4 style={{ marginTop: 8, marginBottom: 32 }}>销售额</h4>
+              <Pie
+                hasLegend
+                subTitle="销售额"
+                total={yuan(salesPieData.reduce((pre, now) => now.y + pre, 0))}
+                data={salesPieData}
+                valueFormat={val => yuan(val)}
+                height={240}
+                lineWidth={4}
+                style={{ marginBottom: 52 }}
+              />
             </Card>
           </Col>
         </Row>

+ 3 - 3
src/routes/Dashboard/Monitor.js

@@ -119,7 +119,7 @@ export default class Monitor extends PureComponent {
                     percent={28}
                     subTitle="中式快餐"
                     total="28%"
-                    height={129}
+                    height={128}
                     lineWidth={2}
                   />
                 </Col>
@@ -130,7 +130,7 @@ export default class Monitor extends PureComponent {
                     percent={22}
                     subTitle="西餐"
                     total="22%"
-                    height={129}
+                    height={128}
                     lineWidth={2}
                   />
                 </Col>
@@ -141,7 +141,7 @@ export default class Monitor extends PureComponent {
                     percent={32}
                     subTitle="火锅"
                     total="32%"
-                    height={129}
+                    height={128}
                     lineWidth={2}
                   />
                 </Col>