相关文章推荐
ant.Table拖拽列宽

ant.Table拖拽列宽

8 个月前

table中推拽列宽达到扩宽目标列是项目必用组件,使用过Ant的小伙伴应该都在ant.design组件库找过并没有该组件的实现,当然都有尝试百度过,也都找到了答案。

先来点基础code

import * as React from 'react';
import './style.css';
import 'antd/dist/antd.css';
import ResizeTable from './ResizeTable';
const randomString = () => Math.random().toString(36).slice(-6);
const randomNumber = (min, max) =>
  min + Math.round(Math.random() * (max - min));
const dataSource = new Array(100).fill('').map((_, i) => ({
  id: randomNumber(0, 100000),
  name: randomString(),
  age: randomNumber(5, 20),
  grade: randomNumber(1, 6),
export default function App() {
  const columns = [
      title: 'ID',
      width: 100,
      dataIndex: 'id',
      dataIndex: 'name',
      width: 100,
      title: 'name',
      dataIndex: 'name',
      width: 100,
      title: 'name',
      dataIndex: 'age',
      width: 100,
      title: 'age',
      dataIndex: 'grade',
      width: 100,
      title: 'grade',
  return (
      <ResizeTable rowkey={'id'} dataSource={dataSource} columns={columns} />

该组件大概实现是这样的,为了方便链接里点一点吧

import { Table } from 'antd';
import type { TableProps } from 'antd/lib/table';
import type { TableColumnType } from 'antd';
import React from 'react';
import { Resizable } from 'react-resizable';
import 'react-resizable/css/styles.css';
import styled from 'styled-components';
const ResizeCellStyle = styled(Resizable)`
    &::before {
        position: absolute;
        top: 50%;
        right: 0;
        width: 1px;
        height: 1.6em;
        background-color: rgba(0,0,0,.06);
        transform: translateY(-50%);
        transition: background-color .3s;
        content: "";
    &:last-child::before {
        display: none;
    .react-resizable {
        position: relative;
        background-clip: padding-box;
    .react-resizable-handle {
        position: absolute;
        width: 10px;
        height: 100%;
        bottom: 0;
        right: -5px;
        cursor: col-resize;
        background-image:none;
        z-index: 1;
const TableHeaderCell: React.FC<Record<string, any>> = (props) => {
  const { onResize, width, ...restProps } = props;
  if (!width) return <th {...restProps} />;
  return (
    <ResizeCellStyle
      width={width}
      height={0}
      onResize={onResize}
      draggableOpts={{ enableUserSelectHack: false }}
      <th {...restProps} />
    </ResizeCellStyle>
const ResizeTable: React.FC<TableProps<any>> = (props) => {
  const { columns = [], ...rest } = props;
  const [tableColumns, setTableColumns] =
    React.useState<TableColumnType<any>>(columns);
  const handleResize =
    (index: number) =>
    (e: any, { size }: any) => {
      const nextColumns = [...tableColumns];
      nextColumns[index] = {
        ...nextColumns[index],
        width: size.width,
      setTableColumns(nextColumns);
  return (
    <Table
      {...rest}
      components={{
        header: {
          cell: TableHeaderCell,
      scroll={{
        x: 'max-content',
      columns={tableColumns.map((col: any, index: any) => ({
        ...col,
        onHeaderCell: (column: any) => ({
          width: column.width,
          onResize: handleResize(index),
export default ResizeTable;

这个拖拽没什么大的毛病,主要是columns更新带动整个Table更新,这个状态改变影响太大了,数据较多的情况下会比较卡,可以尝试多弄点数据拖拽试一下。

为了让拖拽更加流畅简单优化了一下 React Ts (forked) - StackBlitz 为了让拖拽更加流畅简单优化了一下

import React from 'react';
import { Resizable } from 'react-resizable';
import 'react-resizable/css/styles.css';
import styled from 'styled-components';
import { Table } from 'antd';
import type { TableProps } from 'antd/lib/table';
const Container = styled.div`
    position: relative;
    width: 100%;
type ResizeLineProps = {
  left: number;
const ResizeLine = styled.div.attrs((props: ResizeLineProps) => ({
  style: {
    left: props.left,
}))<ResizeLineProps>`
    width: 0px;
    height: 100%;
    position: absolute;
    left: 0;
    top: 0;
    border-left: 1px dashed #d9d9d9;
    z-index: 9999;
type StyleProps = {
  resize?: boolean;
const ResizeTableStyle = styled(Resizable)<StyleProps>`
    user-select: none;
    &::before {
        position: absolute;
        top: 50%;
        right: 0;
        width: 1px;
        height: 1.6em;
        background-color: rgba(0,0,0,.06);
        transform: translateY(-50%);
        transition: background-color .3s;
        content: "";
    &:last-child::before {
        display: none;
    .react-resizable {
        position: relative;
        background-clip: padding-box;
    .react-resizable-handle {
        position: absolute;
        width: 10px;
        height: 100%;
        bottom: 0;
        right: -5px;
        cursor: col-resize;
        background-image:none;
        z-index: 1;
const ResizeableTitle = (props: any) => {
  const { width, onResizeStart, onResize, onResizeStop, ...restProps } = props;
  if (!width) return <th {...restProps} />;
  return (
    <ResizeTableStyle
      width={width}
      onResizeStart={onResizeStart}
      onResize={onResize}
      onResizeStop={onResizeStop}
      height={0}
      draggableOpts={{ enableUserSelectHack: false }}
      <th {...restProps} />
    </ResizeTableStyle>
const ResizeTable: React.FC<TableProps<any>> = (props) => {
  const { columns = [], ...rest } = props;
  const [tableColumns, setTableColumns] = React.useState<any[]>(columns);
  const [end, setEnd] = React.useState(0);
  const [start, setStart] = React.useState(0);
  const [show, setShow] = React.useState(false);
  const ref = React.useRef<HTMLDivElement>(null);
  const handleResizeStart = React.useCallback(
    (index: number) =>
      (e: any, { size }: any) => {
        const { clientX } = e;
        const drageX =
          clientX - (ref.current as any)?.getBoundingClientRect().x;
        setStart(drageX);
        setEnd(drageX);
        setShow(true);
    [columns, ref]
  const handleResizeMove = React.useCallback(
    (index: number) => (e: any) => {
      const { clientX } = e;
      const drageX = clientX - (ref.current as any)?.getBoundingClientRect().x;
      setEnd(drageX);
    [ref]
  const handleResizeStop = React.useCallback(
    (index: number) =>
      (e: any, { size }: any) => {
        const { clientX } = e;
        const drageX =
          clientX - (ref.current as any)?.getBoundingClientRect().x;
        const nextColumns = [...tableColumns];
        nextColumns[index] = {
          ...nextColumns[index],
          width: (size.width += drageX - start),
        setTableColumns(nextColumns);
        console.log('stop');
        setEnd(0);
        setStart(0);
        setShow(false);
    [tableColumns, start, ref]
  return (
    <Container ref={ref}>
      <Table
        {...rest}
        components={{
          header: {
            cell: ResizeableTitle,
        scroll={{
          x: 'max-content',
        columns={tableColumns.map((col: any, index: any) => ({
          ...col,
          onHeaderCell: (column: any) => ({
            width: column.width,
            onResizeStart: handleResizeStart(index),
            onResize: handleResizeMove(index),
            onResizeStop: handleResizeStop(index),
 
推荐文章