react-grid-layout

一个好用的拖拽、自适应布局 react 插件

基本使用:

// 显示全部 chart 内容区域
import React,{PureComponent} from 'react';
import {Responsive, WidthProvider } from "react-grid-layout";

const ReactGridLayout = WidthProvider(Responsive);
export default class ChartDashboard extends PureComponent {
    // 设置默认的 props
    static defaultProps = {
        className: "layout",
        rowHeight: 100,
        isDraggable: true,
        isResizable: true,
        onLayoutChange: function() {},
        cols: 12, //布局中的列数
        mounted: false
    };
    constructor(props) {
        super(props);
        this.state = {
            newCounter: 0,
            content: [],
        }
    }
    componentDidMount () {
        this.setState({
            mounted: true
        })
    }
    componentWillReceiveProps(props) {
        // ...
    }
    // 使用从此返回的 cols 来计算添加的新项目的位置
    onBreakpointChange = (breakpoint, cols) => {
        // ...
    }
    // 删除项目
    onRemoveItem = (i) => {
        // ...
    }
    // 改变某一个项目
    onLayoutChange = (layout) => {
        let layoutObj = {};
        layout.forEach(({i, x, y, w, h})=>{
            layoutObj[i] = {i, x, y, w, h}
        })
        // ...
    }
    // 改变 div 大小
    onResizeStop(a,b,) {
        // ...
    }
    render() {
        return <ReactGridLayout measureBeforeMount={this.state.mounted} useCSSTransforms={this.state.mounted} draggableCancel='禁止拖拽的元素的类名' margin={[8,8]} draggableHandle='.drag-element' ref='ReactGridLayoutRef' onResizeStop={this.onResizeStop} onLayoutChange={this.onLayoutChange} onBreakpointChange={this.onBreakpointChange}>
                {this.state.content}
            </ReactGridLayout>
    } 
}

 

如何设置 change 时的背影:

.react-grid-layout {
    position: relative;
    transition: height 200ms ease;
}
.react-grid-item {
    transition: all 200ms ease;
    transition-property: left, top;
}
.react-grid-item.cssTransforms {
    transition-property: transform;
}
.react-grid-item.resizing {
    z-index: 1;
}

.react-grid-item.react-draggable-dragging {
    transition: none;
    z-index: 3;
}

.react-grid-item.react-grid-placeholder {
    background: #000;
    opacity: 0.1;
    transition-duration: 100ms;
    z-index: 2;
    border-radius: 4px;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    -o-user-select: none;
    user-select: none;
}

.react-grid-item:hover > .react-resizable-handle {
    position: absolute;
    width: 20px;
    height: 20px;
    bottom: 0;
    right: 0;
    background: url(图片);
    background-position: bottom right;
    padding: 0 3px 3px 0;
    background-repeat: no-repeat;
    background-origin: content-box;
    box-sizing: border-box;
    cursor: se-resize;
}

我的某个项目源码:

// 显示全部 chart 内容区域
import React,{PureComponent} from 'react';
import echarts from 'echarts/lib/echarts'; //必须
import { connect } from 'react-redux';
import {Responsive, WidthProvider } from "react-grid-layout";
import ChartContent from './chartContent';
import configureStore from '@/store/configureStore';
import {getDashboardChartInfo} from '@/actions';
import Request from '../request';
import Editor from './quill';
import deletePng from '@/common/images/delete.png';
import {Tooltip} from 'antd';

const ReactGridLayout = WidthProvider(Responsive);
class ChartDashboard extends PureComponent {
    // 设置默认的 props
    static defaultProps = {
        className: "layout",
        rowHeight: 100,
        isDraggable: true,
        isResizable: true,
        onLayoutChange: function() {},
        cols: 12, //布局中的列数
        mounted: false
    };
    constructor(props) {
        super(props);
        this.state = {
            newCounter: 0,
            content: [],
        }
        this.cacheContent = [];
        this.onLayoutChange = this.onLayoutChange.bind(this);
        this.onResizeStop = this.onResizeStop.bind(this);
        this.getContent = this.getContent.bind(this);
        this.onBreakpointChange = this.onBreakpointChange.bind(this);
    }
    componentDidMount () {
        this.setState({
            mounted: true
        })
    }
    componentWillReceiveProps(props) {
        let {dashboardChartInfo} = props;
        let content = dashboardChartInfo && dashboardChartInfo.length > 0 ? this.getContent(dashboardChartInfo) : []
        this.setState ({
            dashboardChartInfo:dashboardChartInfo,
            content: content
        })
    }
    // 使用从此返回的 cols 来计算添加的新项目的位置
    onBreakpointChange(breakpoint, cols) {
        console.log(breakpoint,cols)
    }
    // 删除项目
    onRemoveItem(i) {
        let curContent = [...this.state.content];
        let stateChartInfo = [...this.state.dashboardChartInfo];
        curContent.deleteObjectInArray('key',i);
        stateChartInfo.deleteObjectInArray('position','i',i)
        this.updateDashboardInfo(stateChartInfo);
        return false;
    }
    // 改变某一个项目
    onLayoutChange(layout) {
        let layoutObj = {};
        layout.forEach(({i, x, y, w, h})=>{
            layoutObj[i] = {i, x, y, w, h}
        })
        // state 中保存的 dashboardChartInfo 信息
        let stateChartInfo = this.state.dashboardChartInfo;
        let dashboardChartInfo = stateChartInfo.map(val=>{
            let position = layoutObj[val.position.i];
            return {id:val.id,position,type:val.type||'chart'}
        });
        if (this.isContIdentical(stateChartInfo, dashboardChartInfo)) {
            this.updateDashboardInfo(dashboardChartInfo);
        }
    }
    // 改变 div 大小,重新渲染 echarts 图表
    onResizeStop(a,b,) {
        let idname = `.react-grid-layout div[data-]`;
        let parentDom = document.querySelector(idname);
        
        // let parentDom = event.target.parentElement;
        let echartDom = parentDom.querySelector('div[_echarts_instance_]');
        if(echartDom) {
            let echartObj = echarts.getInstanceByDom(echartDom);
            echartObj.resize();
        }
    }
    getContent(dashboardChartInfo) {
        return dashboardChartInfo.map((val,index)=>{
            if(val.position.y === null) {
                val.position.y = Infinity;
            }
            if(val.type=='text') {
                return (<div className="drag-element" key={val.position.i} data-id={val.position.i} data-grid={{...val.position}}>
                    <Editor placeholder='请输入内容...' id={val.id} dashboardChartInfo={dashboardChartInfo} user_chart_set_id={this.props.dashboardInfo && this.props.dashboardInfo.userChartSetId} curContent={val.content}/>
                    <Tooltip placement="top" title='删除'><img src={deletePng} className="remove" key={val.position.i}  onClick={this.onRemoveItem.bind(this,val.position.i)}/></Tooltip>
                </div>)
            }else {
                return (<div className="drag-element" key={val.position.i} data-id={val.position.i} data-grid={{...val.position}}>
                    <ChartContent id={val.id} history={this.props.history}/>
                    <Tooltip placement="top" title='删除'><img src={deletePng} className="remove" key={val.position.i}  onClick={this.onRemoveItem.bind(this,val.position.i)}/></Tooltip>
                </div>)
            }
        })
    }    
    isContIdentical(cacheContent, content) {
        if (cacheContent.length === content.length) {
            for (let i = cacheContent.length - 1; i >= 0; i --) {
                let cacheData = cacheContent[i].position;
                let contData = content[i].position;
                let flag = cacheData.i === contData.i &&
                cacheData.x === contData.x &&
                cacheData.y === contData.y &&
                cacheData.w === contData.w &&
                cacheData.h === contData.h;
                if (!flag) return true;
            }
            return false;
        }
        return true;
    }
    /**
     * 更新 dashboard 信息
     * @param {} user_chart_set_info 
     */
    updateDashboardInfo(user_chart_set_info) {
        Request.selectDashboardInfo({
            operationType: 'edit',
            user_chart_set_id: this.props.dashboardInfo.userChartSetId,
            user_chart_set_info: JSON.stringify(user_chart_set_info)
        },(data)=>{
            if(data.code == 1) {
                this.setState ({
                    dashboardChartInfo:user_chart_set_info,
                })
                configureStore.dispatch(getDashboardChartInfo(user_chart_set_info));
            }
        })
    }
    render() {
        return <div className='right-dashboard' style={{
                paddingRight: '30px',
                overflow: 'auto',
                marginBottom: '20px'
            }}>
            <ReactGridLayout 8px'}} measureBeforeMount={this.state.mounted} useCSSTransforms={this.state.mounted} draggableCancel='.map,.quill,.remove,.edit-chart' margin={[8,8]} draggableHandle='.drag-element' ref='ReactGridLayoutRef' onResizeStop={this.onResizeStop} onLayoutChange={this.onLayoutChange} onBreakpointChange={this.onBreakpointChange}>
                {this.state.content}
            </ReactGridLayout>
        </div>
    } 
}
const mapStateToProps = (state) => {
    return {
        dashboardChartInfo: state.DashBoard.dashboardChartInfo,
        dashboardInfo: state.DashBoard.dashboardInfo
    }
}
export default connect(mapStateToProps)(ChartDashboard);
/**
 * 删除数组中的某个对象
 * @param {*} key1 
 * @param {*} key2 
 * @param {*} key3 
 */
Array.prototype.deleteObjectInArray = function (key1,key2,key3) {
    if(arguments.length == 3) {
        this.forEach((val,index)=>{
            if(val[key1][key2] == key3) {
                this.splice(index,1)
            }
        });
    }else {
        this.forEach((val,index)=>{
            if(val[key1] == key2) {
                this.splice(index,1)
            }
        });
    }
    
    return this;
}