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

【默默努力】react-drag-grid的更多相关文章

  1. 2018.5.2(7:20到的办公室开始早课 阮一峰的JS) 所有的默默努力都是为了让自己看起来毫不费力

    continue语句用于立即终止本轮循环,返回循环结构的头部,开始下一轮循环. break语句用于跳出代码块或循环. 标签(label) JavaScript 语言允许,语句的前面有标签(label) ...

  2. 【默默努力】PixelFire

    先放下我玩游戏的效果图: 关于游戏最后的结束部分其实我还没有截图,看着挺好看的,后面的效果 再放作者大大的项目地址:https://github.com/panruiplay/PixelFire 接下 ...

  3. 【默默努力】fishingGame

    这个捕鱼游戏挺有意思的,通过发射子弹,打鱼.打鱼的子弹会消耗金币,但是打鱼如果打到了鱼,就会奖励金币的数量. 我如果写这个的话,应该会画一个 背景海底,然后生成很多鱼的图片,还要有一个大炮,金币.大炮 ...

  4. 【默默努力】h5-game-heroVSmonster

    先放下作者大大的项目地址:https://github.com/yangyunhe369/h5-game-heroVSmonster 然后游戏的效果为 截动图的按键与游戏按键应该冲突,我就截几张图片了 ...

  5. 【默默努力】h5-game-blockBreaker

    先放下游戏的效果,我不太会玩游戏 然后放下无私开源的作者大大的地址:https://github.com/yangyunhe369/h5-game-blockBreaker 这个游戏的话,我觉得应该是 ...

  6. 【默默努力】ig-wxz-and-hotdog

    这个是一个非常跟热点的小游戏,思聪吃热狗.这个游戏的话,我感觉思路还挺简单的,天上会掉热狗和障碍物, 思聪在下面张开嘴巴,进行左右移动,接热狗.如果吃到的是热狗就得一分,如果思聪吃到的不是热狗,是障碍 ...

  7. 【默默努力】vue-pc-app

    最近在github上面看到了一个团队的项目,真的非常赞.他们进行vue-cli的二次开发,将项目用自己的方式打包. 今天的这个开源项目地址为:https://github.com/tffe-team/ ...

  8. 【招聘App】—— React/Nodejs/MongoDB全栈项目:信息完善&用户列表

    前言:最近在学习Redux+react+Router+Nodejs全栈开发高级课程,这里对实践过程作个记录,方便自己和大家翻阅.最终成果github地址:https://github.com/66We ...

  9. react-native聊天室|RN版聊天App仿微信实例|RN仿微信界面

    一.前言 9月,又到开学的季节.为每个一直默默努力的自己点赞!最近都沉浸在react native原生app开发中,之前也有使用vue/react/angular等技术开发过聊天室项目,另外还使用RN ...

随机推荐

  1. NX二次开发-UFUN获取边的光顺性UF_MODL_ask_edge_smoothness(找相切面)

    #include <uf.h> #include <uf_modl.h> #include <uf_obj.h> UF_initialize(); //获取面的所有 ...

  2. Linux环境上的图形化界面SVN客户端软件“RabbitVCS”

    RabbitVCS基本支持所有的Linux发行版本包括ubuntu.Debian.Fedora.Arch Linux.Gentoo.Mandriva.OpenSUSE.RHEL.CentOS 5等.其 ...

  3. c++ 枚举简单举例

    #include <iostream> enum Enumeration{ VAL1, VAL2, VAL3=100, VAL4 }; int main() { using namespa ...

  4. Mybatis笔记 - SqlMapConfig

    一.基本配置内容 1.基本格式 mybatis的全局配置文件SqlMapConfig.xml,配置内容如下: properties(属性) settings(全局配置参数) typeAliases(类 ...

  5. LeetCode 1103. Distribute Candies to People (分糖果 II)

    题目标签:Math 题目让我们分发糖果,分的糖果从1 开始依次增加,直到分完. for loop可以计数糖果的数量,直到糖果发完.但是还是要遍历array 给people 发糖,这里要用到 index ...

  6. 2019 ICPC 南昌网络赛

    2019 ICPC 南昌网络赛 比赛时间:2019.9.8 比赛链接:The 2019 Asia Nanchang First Round Online Programming Contest 总结 ...

  7. Jsoup 学习笔记

    这里写自定义目录标题 Jsoup 学习笔记 解析 HTML 的字符串解析 URL 解析 本地文件解析 解析数据 DOM 解析 使用选择器解析 选择器概述 选择器组合用法 过滤用法 修改数据 HTML ...

  8. vue多文件上传进度条 进度不更新问题

    转自 hhttp://www.cnblogs.com/muge10/p/6767493.html 感谢这位兄弟的文章,之前因为这个问题 ,我连续在sgmentflow上提问过多次,完全没人能回答.谢谢 ...

  9. 15-MySQL-Ubuntu-数据表的查询-聚合函数(四)

    聚合函数 (1)count() 统计总数 查询男性总数 select count(*) as 男性 from students where gender=1; (2)max() 最大值 查询男性中年龄 ...

  10. 转Git仓库分支(Branch)和标签(Tag)

    仓库的分支(Branch)规范,影响到每个团队的工作流的一致性:标签(Tag)便于开发团队.测 试团队和其他团队识别每个项目的版本,特别是在协同处理线上问题的时候,大家可以非常清楚 地知道线上运行版本 ...