123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260 |
- import React, { Component } from 'react';
- import G2 from 'g2';
- 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 styles from './index.less';
- /* eslint react/no-danger:0 */
- class Pie extends Component {
- state = {
- legendData: [],
- legendBlock: true,
- };
- componentDidMount() {
- this.renderChart();
- this.resize();
- 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(300)
- resize() {
- const { hasLegend } = this.props;
- if (!hasLegend || !this.root) {
- window.removeEventListener('resize', this.resize);
- return;
- }
- if (this.root.parentNode.clientWidth <= 380) {
- 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;
- newItem.checked = !newItem.checked;
- const { legendData } = this.state;
- legendData[i] = newItem;
- if (this.chart) {
- const filterItem = legendData.filter(l => l.checked).map(l => l.x);
- this.chart.filter('x', filterItem);
- this.chart.repaint();
- }
- this.setState({
- legendData,
- });
- }
- renderChart(d) {
- let data = d || this.props.data;
- const {
- height = 0,
- hasLegend,
- fit = true,
- margin = [12, 0, 12, 0], percent, color,
- inner = 0.75,
- animate = true,
- colors,
- lineWidth = 0,
- } = this.props;
- const defaultColors = colors;
- let selected = this.props.selected || true;
- let tooltip = this.props.tooltips || true;
- let formatColor;
- if (percent) {
- selected = false;
- tooltip = false;
- formatColor = (value) => {
- if (value === '占比') {
- return color || 'rgba(24, 144, 255, 0.85)';
- } else {
- return '#F0F2F5';
- }
- };
- /* eslint no-param-reassign: */
- data = [
- {
- x: '占比',
- y: parseFloat(percent),
- },
- {
- x: '反比',
- y: 100 - parseFloat(percent),
- },
- ];
- }
- 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,
- });
- 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>
- )
- }
- </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>
- );
- }
- }
- export default Pie;
|