【默默努力】react-drag-grid

先放项目地址:https://github.com/Bilif/react-drag-grid
项目运行效果

感谢无私开源的程序员
先看项目入口文件

//index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

ReactDOM.render(<App />, document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

可以看到主要内容主要在App.js中
接下来我们来看App.js

//App.js
import React from 'react';
import './App.css';
import DragLayout from './DragLayout';

function App() {
  return (
    <DragLayout></DragLayout>
  );
}

export default App;

我们可以看到App中主要引用的是DragLayout组件
也就是核心功能点在DragLayout组件中
在DragLayout中我们会看到引用了一些组件
比如antd,用来布局以及样式
react-grid-layout是一个好用的拖拽、自适应布局 react 插件
它的使用方法为

import { WidthProvider, Responsive } from "react-grid-layout";
const ResponsiveReactGridLayout = WidthProvider(Responsive);

在React的render方法中渲染可拖拽布局。ResponsiveReactGridLayout组件有多个属性。
cols:定义了响应式布局划分成几列。
rowHeight:响应式布局中组件的行高。
onLayoutChange:当响应式布局中的组件发生拖拽或者放大缩小时触发该函数。

     <ResponsiveReactGridLayout
            className="layout"
            {...this.props}
            layouts={this.state.layouts}
            onLayoutChange={(layout, layouts) =>
              this.onLayoutChange(layout, layouts)
            }
          >
            {this.generateDOM()}
          </ResponsiveReactGridLayout>

使用echarts-for-react插件可以在React中调用echarts接口直接渲染出Echarts图表,只要传入相关的参数和数据即可。
安装

npm install --save echarts-for-react]
npm install --save echarts

使用

  let component = (
        <ReactEcharts
          option={option}
          notMerge={true}
          lazyUpdate={true}
          style={{width: '100%',height:'100%'}}
        />
      )

这边将三种图封装在了chart中

//chart.js
export function getBarChart() {
  const option = {
    tooltip: {
      trigger: 'axis',
      axisPointer: { // 坐标轴指示器,坐标轴触发有效
        type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
      }
    },
    grid: {
      left: '3%',
      right: '4%',
      bottom: '3%',
      containLabel: true
    },
    xAxis: [{
      type: 'category',
      data: ['2014', '2015', '2016', '2017', '2018', '2019'],
      axisLine:{
        lineStyle:{
          color:'#8FA3B7',//y轴颜色
        }
      },
      axisLabel: {
        show: true,
        textStyle: {
          color: '#6D6D6D',
        }
      },
      axisTick: {show: false}
    }],
    yAxis: [{
      type: 'value',
      splitLine:{show: false},
      //max: 700,
      splitNumber: 3,
      axisTick: {show: false},
      axisLine:{
        lineStyle:{
          color:'#8FA3B7',//y轴颜色
        }
      },
      axisLabel: {
        show: true,
        textStyle: {
          color: '#6D6D6D',
        }
      },
    }],
    series: [

      {
        name: 'a',
        type: 'bar',
        barWidth: '40%',
        itemStyle: {
          normal: {
            color: '#FAD610'
          }
        },
        stack: '信息',
        data: [320, 132, 101, 134, 90, 30]
      },
      {
        name: 'b',
        type: 'bar',
        itemStyle: {
          normal: {
            color: '#27ECCE'
          }
        },
        stack: '信息',
        data: [220, 182, 191, 234, 290, 230]
      },
      {
        name: 'c',
        type: 'bar',
        itemStyle: {
          normal: {
            color: '#4DB3F5'
          }
        },
        stack: '信息',
        data: [150, 132, 201, 154, 90, 130]
      }
    ]
  };
  return option;
}

export function getLineChart() {
  //option
  const option = {
    color: ['#D53A35'],
    tooltip: {
      trigger: 'axis',
      //formatter: "{b} <br> 合格率: {c}%"
    },
    grid: {
      left: '3%',
      right: '4%',
      bottom: '3%',
      containLabel: true
    },
    xAxis: {
      type: 'category',
      name: '',
      boundaryGap: false,
      axisLine:{
        show:false,
        lineStyle:{
          color:'#525252'
        }
      },
      axisTick:{
        show:false
      },
      axisLabel:{
        color:'#525252'
      },
      data: ['01', '02', '03', '04', '05', '06', '07','08','09','10','11','12','13','14','15','16','17','18','19','20','21','22','23','24']
    },
    yAxis: {
      type: 'value',
      name: '',
      axisLine:{
        show:false,
      },
      axisTick:{
        show:false
      },
      axisLabel:{
        color:'#525252'
      },
      splitLine:{
        lineStyle:{
          type:'dotted',
          color:'#AAA'//F3F3F3
        }
      }
    },
    series: [{
      name: 'a',
      type: 'line',
      symbol: 'circle',
      data: [100,120, 132, 101, 134, 90, 230, 210,80,20,90,210,200,100,120, 132, 101, 134, 90, 230, 210,80,20,90]
    }
    ]
  };
  return option;
}

export function getPieChart() {
  //option
  const option = {
    color: ['#3AA1FF', '#36CBCB', '#4ECB73', '#FBD338'],
    tooltip: {
      trigger: 'item',
      formatter: '{a} <br/>{b}: {c} ({d}%)'
    },
    grid: {
      left: '3%',
      right: '4%',
      bottom: '3%',
      containLabel: true
    },
    series: [{
      name: '消费能力',
      type: 'pie',
      radius: ['40%', '55%'],
      center: ['50%', '55%'],
      avoidLabelOverlap: true,
      itemStyle: {
        normal: {
          borderColor: '#FFFFFF',
          borderWidth: 2
        }
      },
      label: {
        normal: {
          show: false,
        },
      },
      labelLine: {
        normal: {
          show: false
        }
      },
      data: [{
        name: 'a',
        value: '20'
      }, {
        name: 'b',
        value: '40'
      }, {
        name: 'c',
        value: '10'
      }, {
        name: 'd',
        value: '10'
      }]
    }]
  };
  return option;
}

通过generateDOM函数生成布局中的组件,首先先遍历组件数组,通过每个组件的类型判断生产柱状图组件,
折线组件,还是饼图组件。每个组件必须定义一个全局唯一的key值。data-grid为每一个组件绑定了其属性。

  generateDOM = () => {
    return _.map(this.state.widgets, (l, i) => {
      let option;
      if (l.type === 'bar') {
        option = getBarChart();
      }else if (l.type === 'line') {
        option = getLineChart();
      }else if (l.type === 'pie') {
        option = getPieChart();
      }
      let component = (
        <ReactEcharts
          option={option}
          notMerge={true}
          lazyUpdate={true}
          style={{width: '100%',height:'100%'}}
        />
      )
      return (
        <div key={l.i} data-grid={l}>
          <span className='remove' onClick={this.onRemoveItem.bind(this, i)}>x</span>
          {component}
        </div>
      );
    });
  };

通过addItem函数来新增组件。
每个组件属性如下:

x: 组件在x轴坐标
y: 组件在y轴坐标
w: 组件宽度
h: 组件高度
i: 组件key值

addItem(type,widgetId) {
    const addItem = {
      x: (this.state.widgets.length * 2) % (this.state.cols || 12),
      y: Infinity, // puts it at the bottom
      w: 2,
      h: 2,
      i: widgetId || new Date().getTime().toString(),
    };
    this.setState(
      {
        widgets: this.state.widgets.concat({
          ...addItem,
          type,
        }),
      },
    );
  };

通过onRemoveItem函数来移除增组件。

onRemoveItem(i) {
    console.log(this.state.widgets)
    this.setState({
      widgets: this.state.widgets.filter((item,index) => index !=i)
    });

  }

DragLayout.js全部代码为

//DragLayout
import React, { PureComponent } from 'react';
import { Layout,Button } from 'antd';
import { WidthProvider, Responsive } from "react-grid-layout";
import _ from "lodash";
import ReactEcharts from 'echarts-for-react';
import { getBarChart,getLineChart,getPieChart } from "./chart";

const ResponsiveReactGridLayout = WidthProvider(Responsive);
const { Header, Content} = Layout;

export default class DragLayout extends PureComponent {
  static defaultProps = {
    cols: { lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 },
    rowHeight: 100,
  };

  constructor(props) {
    super(props);

    this.state = {
      layouts: this.getFromLS("layouts") || {},
      widgets:[]
    }
  }

  getFromLS(key) {
    let ls = {};
    if (global.localStorage) {
      try {
        ls = JSON.parse(global.localStorage.getItem("rgl-8")) || {};
      } catch (e) {
        /*Ignore*/
      }
    }
    return ls[key];
  }

  saveToLS(key, value) {
    if (global.localStorage) {
      global.localStorage.setItem(
        "rgl-8",
        JSON.stringify({
          [key]: value
        })
      );
    }
  }
  generateDOM = () => {
    return _.map(this.state.widgets, (l, i) => {
      let option;
      if (l.type === 'bar') {
        option = getBarChart();
      }else if (l.type === 'line') {
        option = getLineChart();
      }else if (l.type === 'pie') {
        option = getPieChart();
      }
      let component = (
        <ReactEcharts
          option={option}
          notMerge={true}
          lazyUpdate={true}
          style={{width: '100%',height:'100%'}}
        />
      )
      return (
        <div key={l.i} data-grid={l}>
          <span className='remove' onClick={this.onRemoveItem.bind(this, i)}>x</span>
          {component}
        </div>
      );
    });
  };

  addChart(type) {
    const addItem = {
      x: (this.state.widgets.length * 3) % (this.state.cols || 12),
      y: Infinity, // puts it at the bottom
      w: 3,
      h: 2,
      i: new Date().getTime().toString(),
    };
    this.setState(
      {
        widgets: this.state.widgets.concat({
          ...addItem,
          type,
        }),
      },
    );
  };

  onRemoveItem(i) {
    console.log(this.state.widgets)
    this.setState({
      widgets: this.state.widgets.filter((item,index) => index !=i)
    });

  }

  onLayoutChange(layout, layouts) {
    this.saveToLS("layouts", layouts);
    this.setState({ layouts });
  }

  render() {
   return(
     <Layout>
      <Header style={{ position: 'fixed', zIndex: 1, width: '100%','padding': '0 30px' }}>
        <Button type="primary" style={{'marginRight':'7px'}} onClick={this.addChart.bind(this,'bar')}>添加柱状图</Button>
        <Button type="primary" style={{'marginRight':'7px'}} onClick={this.addChart.bind(this,'line')}>添加折线图</Button>
        <Button type="primary" style={{'marginRight':'7px'}} onClick={this.addChart.bind(this,'pie')}>添加饼图</Button>
      </Header>
      <Content style={{ marginTop: 44 }}>
        <div style={{ background: '#fff', padding: 20, minHeight: 800 }}>
          <ResponsiveReactGridLayout
            className="layout"
            {...this.props}
            layouts={this.state.layouts}
            onLayoutChange={(layout, layouts) =>
              this.onLayoutChange(layout, layouts)
            }
          >
            {this.generateDOM()}
          </ResponsiveReactGridLayout>
        </div>
      </Content>
    </Layout>
   )}
}

部分讲解内容学习自博客:https://juejin.im/post/5cda5719e51d453a36384923

猜你喜欢

转载自www.cnblogs.com/smart-girl/p/11428207.html