先放项目地址: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

最新文章

  1. update kernel 3.10-3.12
  2. PLSQL Developer导入Excel数据
  3. 答:SQLServer DBA 三十问之六:Job信息我们可以通过哪些表获取;系统正在运行的语句可以通过哪些视图获取;如何获取某个T-SQL语句的IO、Time等信息;
  4. js:插入节点appendChild insertBefore使用方法
  5. Android手机的上网功能需要用到APN(网络接入点)的设置 电信
  6. 车销 商场 批发零售无线POS开单 智能POS开单打印 开单APP应用-云POS扫描打印一体方案
  7. ubuntu 安装 OpenCv 及其Qt的开发环境配置
  8. windows7开启虚拟wifi和虚拟无线AP的方法
  9. Nginx重定向[Rewrite]配置 for wordpress &amp; Discuz
  10. Android USB Host 通信程序
  11. 安装ARM交叉编译器
  12. 201521123096《Java程序设计》第十二周学习总结
  13. Effective Java 第三版——3. 使用私有构造方法或枚类实现Singleton属性
  14. Mysql_安装
  15. 如何执行shell命令
  16. JavaScript 随意记
  17. 第一册:lesson ninety-five。
  18. java.lang.Comparable 接口 详解
  19. Linux命令:logout
  20. 如何修改config?

热门文章

  1. NX二次开发-UFUN遍历图层UF_LAYER_cycle_by_layer
  2. Cstring转char、string、int等数据类型的方法(转载)
  3. [NOI.AC] count
  4. ionic:目录
  5. Instrumentation 实践详解
  6. 剑指offer——11旋转数组中最小的数字
  7. 剑指offer——05重建二叉树
  8. Git 学习(一)安装 Git
  9. C#委托的实质
  10. nodejs--express安装失败的原因