react实现拖拽容器边框改变容器宽度以及实现table组件内的表头拖拽改变宽度

react实现拖拽容器边框改变容器宽度

  • 先说下原理,显然拖拽容器触发事件然后改变容器宽度,这里因为无法获取到边框的拖拽事件,所以另辟蹊径使用div模拟一条容器的边框作为事件触发的触发器,并且需要获取同时获取左边、右边以及父容器的宽度在事件触发时重新计算容器宽度。

封装拖拽方法

    // 左侧菜单栏拖拽
    /**
     *
     * @param {*} param0 { 左侧domRef的current, 右侧domRef的current, 盒元素domRef的current }
     * @param {*} draggleLineDom { 需要拖拽的线的domRef的current }
     */
    const drag = ({ leftDom: ref1, rightDom: ref2, contentDom }, draggleLineDom) => {
        const _ref1 = ref1;
        const _ref2 = ref2;
        draggleLineDom.onmousedown = e => {
            let _e = e;
            const dir = 'horizontal'; // 设置好方向 可通过变量控制默认水平方向 horizontal | vertical
            const firstX = _e.clientX; // 获取第一次点击的横坐标
            const width = ref2.offsetWidth; // 获取到元素的宽度
            // 移动过程中对左右元素宽度计算赋值
            document.onmousemove = _event => {
                _e = _event;
                // 可扩展上下拖动等
                switch (dir) {
                    case 'horizontal':
                        let leftWidth = contentDom.offsetWidth - width + (_e.clientX - firstX) - 10;
                        let rightWidth = width - (_e.clientX - firstX);
                        if (leftWidth > 360 || leftWidth < 200) {
                            return;
                        }
                        _ref1.style.width = `${leftWidth}px`;
                        _ref2.style.width = `${rightWidth}px`;
                        break;
                    default:
                        break;
                }
            };
            // 在左侧和右侧元素父容器上绑定松开鼠标解绑拖拽事件
            contentDom.onmouseup = () => {
                document.onmousemove = null;
            };
            return false;
        };
    };

使用方法

import React, { useRef, useEffect } from 'react';
import Left from '../components/Left';
import Right from '../components/Right';

export default function Index() {
    const draggleLineRef = useRef();
    const contextRef = useRef();
    const leftRef = useRef();
    const rightRef = useRef();
    
    useEffect(() => {
        drag(
            { leftDom: leftRef.current, rightDom: rightRef.current, contentDom: contextRef.current },
            draggleLineRef.current
        );
    }, []);

    return (
        <div style={{ height: '100%' }} ref={contextRef}>
            <Left ref={leftRef} code={code} prefix="/statement/report" />
            <div style={{ width: '3px', height: '100%', float: 'left', cursor: 'e-resize' }} ref={draggleLineRef}></div>
            <Right ref={rightRef} code={code} />
        </div>
    );
}

table组件实现表头拖拽改变宽度

  • 这里借助了react-resizable库来实现,借助render函数来实现拖拽功能。这里有几个问题,当设置初始width时,点击拖拽的同时会导致width瞬间的改变,显示会出问题,所以必须要将最后一列设置为充满的状态,因为无法在渲染时就获取到表头数量,也就无法获取每个表头均分的width,同时不要使用Table组件的自适应宽度
import React, { Component } from 'react';
import { Table, Pagination } from 'antd';
import { Resizable } from 'react-resizable';
import { withRouter } from 'react-router-dom';

const ResizeableTitle = props => { // 渲染table表头的组件
    const { onResize, width, ...restProps } = props;
    if (!width) {
        return <th {...restProps} />;
    }
    return (
        <Resizable width={width} height={0} onResize={onResize}>
            <th {...restProps} />
        </Resizable>
    );
};
class MyTable extends Component {
    state = {
        columns: this.props.columns // 从父组件拿到表头 或者 直接将表头写到此处
    };

    ref = React.createRef(null);

    handleResize = index => (e, { size }) => { // 改变表头宽度的方法, 通过index找到对应的表头
        this.setState(({ columns }) => {
            const nextColumns = [...columns];
            nextColumns[index] = {
                ...nextColumns[index],
                width: size.width
            };
            return { columns: nextColumns };
        });
    };

    componentDidMount() {
        this.props.query();
        this.unlisten = this.props.history.listen(route => {
            if (route.pathname === '/report') {
                this.props.query();
            }
            return;
        });
    }

    componentWillUnmount() {
        this.unlisten();
    }

    componentDidUpdate(props) {
        if (this.props.columns !== props.columns) {
            this.setState({ columns: this.props.columns });
        }
    }

    components = {
        header: {
            cell: ResizeableTitle
        }
    };
    
    render() {
        const { rowKey, data, loading, isPagination, _props } = this.props;

        const _columns = this.state.columns.map((col, index) => ({
            ...col,
            width: col.width || 160,
            onHeaderCell: column => ({
                width: col?.width || 160, // 没有设置宽度可以在此写死 例如100试下
                onResize: this.handleResize(index)
            })
        }));
        let scrollWidth = _.reduce(
            _columns,
            (width, item) => {
                return width + item.width;
            },
            80
        );

        if (this.ref && this.ref.current && !_.isEmpty(_columns)) {
            _columns[_columns.length - 1].onHeaderCell = null;
            const { width } = this.ref.current.getBoundingClientRect();
            if (scrollWidth < width) {
                _columns[_columns.length - 1].width += width - scrollWidth;
                scrollWidth = width;
            }
        }

        const _pagination = {
            current: this.props.pageNum,
            pageSize: this.props.pageSize,
            total: this.props.total
        };
        return (
            <>
                <div className="resizeable-table-wrapper" ref={this.ref}>
                    <Table
                        {..._props}
                        rowKey={rowKey}
                        loading={loading}
                        columns={_columns}
                        dataSource={data}
                        bordered
                        components={this.components}
                        // className="check-table-warp"
                        pagination={isPagination ? _pagination : false}
                        // scroll={{ x: true }}
                    />
                </div>
            </>
        );
    }
}

MyTable.defaultProps = {
    loading: false,
    rowKey: 'id',
    isPagination: true
};

export default withRouter(MyTable)
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MaxLoongLvs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值