react ant 表格实现 拖拽排序和多选

时间:2024-06-01 08:54:15

项目背景 : react + ant
要实现 : 有多选功能(实现批量删除 , 也可以全选) + 可以拖拽(可以复制 , 方便顶部的搜索功能)
要实现效果如下

1 这是最初的拖拽功能实现 , 不能复制表格里的内容 , 不符合要求

2 更改了ROW的内容 , 实现了可以复制表格内容
代码

//控制是否可以选中表格里的文字
const Row1 = props => {
  const {
    attributes,
    listeners,
    setNodeRef,
    setActivatorNodeRef,
    transform,
    transition,
    isDragging
  } = useSortable({
    id: props['data-row-key']
  })
  const style = {
    ...props.style,
    transform: CSS.Translate.toString(transform),
    transition,
    ...(isDragging
      ? {
          position: 'relative',
          zIndex: 9999
        }
      : {})
  }
  const contextValue = useMemo(
    () => ({
      setActivatorNodeRef,
      listeners
    }),
    [setActivatorNodeRef, listeners]
  )
  return (
    <RowContext.Provider value={contextValue}>
      <tr {...props} ref={setNodeRef} style={style} {...attributes} />
    </RowContext.Provider>
  )
}




3 多选功能ant官网也只提供了rowSelection方法 , 而rowSelection的位置总是在表格最左边 , 我需要让拖拽icon在最左边 , 多选功能在icon右边 , 目前问题如下


 









 

解决思路 : 舍弃了官网的rowSelection方法 , 添加自定义选择列

代码分为俩部分 , 一部分是父页面 , ( 父页面代码太多只显示了功能代码 )

 

import React, { useContext, useMemo, useState, useEffect } from 'react'
import { HolderOutlined } from '@ant-design/icons'
import { DndContext } from '@dnd-kit/core'
import { restrictToVerticalAxis } from '@dnd-kit/modifiers'
import {
  arrayMove,
  SortableContext,
  useSortable,
  verticalListSortingStrategy
} from '@dnd-kit/sortable'
import { CSS } from '@dnd-kit/utilities'
const RowContext = React.createContext({})


//控制是否可以选中表格里的文字
const Row1 = props => {
  const {
    attributes,
    listeners,
    setNodeRef,
    setActivatorNodeRef,
    transform,
    transition,
    isDragging
  } = useSortable({
    id: props['data-row-key']
  })
  const style = {
    ...props.style,
    transform: CSS.Translate.toString(transform),
    transition,
    ...(isDragging
      ? {
          position: 'relative',
          zIndex: 9999
        }
      : {})
  }
  const contextValue = useMemo(
    () => ({
      setActivatorNodeRef,
      listeners
    }),
    [setActivatorNodeRef, listeners]
  )
  return (
    <RowContext.Provider value={contextValue}>
      <tr {...props} ref={setNodeRef} style={style} {...attributes} />
    </RowContext.Provider>
  )
}

//拖拽图标
const DragHandle = () => {
  const { setActivatorNodeRef, listeners } = useContext(RowContext)
  return (
    <Button
      type='text'
      size='small'
      icon={<HolderOutlined />}
      style={{
        cursor: 'move'
      }}
      ref={setActivatorNodeRef}
      {...listeners}
    />
  )
}

function role () {
  //被拖拽后请求接口和数据改变
  const onDragEnd = ({ active, over }) => {
    if (active.id !== over?.id) {
      setData(data => {
        const activeIndex = data.findIndex(item => item?.key === active.id)
        const overIndex = data.findIndex(item => item?.key === over?.id)

        const newData = arrayMove(data, activeIndex, overIndex).map(
          (item, index) => ({
            ...item,
            sort: data.length - index
          })
        )

        // 收集newData中所有对象的id和sort值
        const updatedItems = newData.map(item => ({
          id: item.roleId,
          sort: item.sort
        }))

        getSortMethod({ sorts: updatedItems }) //后端接口

        return arrayMove(data, activeIndex, overIndex)
      })
    }
  }

  // 让拖拽icon在左侧
  const columns = [
    {
      width: 60,
      render: () => <DragHandle />
    },
    ...]


  return (
    <>
       <DndContext modifiers={[restrictToVerticalAxis]} onDragEnd={onDragEnd}>
          <SortableContext
            items={data.map(i => i?.key)}
            strategy={verticalListSortingStrategy}
          >
            {TableComSelect1({
              loading,
              data,
              columns,
              onSelectionChange: handleSelectionChange, // 选中的表格数量信息传递给表格
              isSelectAll: isSelectAll, //是否全选

              rowKey: 'key', //拖拽
              components: {  //拖拽
                body: {
                  row: Row1
                }
              }
            })}
          </SortableContext>
        </DndContext>
    </>
  )
}

export default role




另一部分是封装的表格组件 ( 全部代码如下 )

import React, { useState, useEffect, useContext } from 'react'
import { Table, Button, Checkbox } from 'antd'

import SimpleBar from 'simplebar-react'

import 'simplebar/dist/simplebar.min.css' // 引入 simplebar 的样式
import './index.less'
import { useTranslation } from 'react-i18next' // 引入 useTranslation 钩子
import i18n from '@/utils/i18n' //国际化组件

const TableComSelect1 = props => {
  const { t } = useTranslation() // 获取翻译函数和语言切换函数

  const [obj, setObj] = useState({})
  const {
    components,
    rowKey,
    columns = [],
    data = [],
    loading = false,
    onSelectionChange,
    isSelectAll
  } = props
  const [selectedRowKeys, setSelectedRowKeys] = useState([]) //让批量删除后不被选中
  const [selectionType, setSelectionType] = useState('checkbox')

  //接收父传递的key 用来控制表格选中
  const onSelectChange = newSelectedRowKeys => {
    console.log('selectedRowKeys changed: ', newSelectedRowKeys)
    setSelectedRowKeys(newSelectedRowKeys) //让子表格可以选中
    onSelectionChange(newSelectedRowKeys) //将选中的子表格选中的key值赋给父组件
  }
  //旧的选择功能,一直在最左侧
  const rowSelection = {
    selectedRowKeys,
    onChange: onSelectChange
  }

  // 点击全选
  useEffect(() => {
    if (isSelectAll) {
      setSelectedRowKeys(data.map(item => item.key))
    } else {
      setSelectedRowKeys([])
    }
  }, [isSelectAll])

  // 创建一个自定义的选择列
  const selectionColumn = {
    width: '100px',
    title: t('select'), // 或者根据需要设置标题
    fixed: 'left', // 如果需要固定列,请保留此行
    render: (_, record) => (
      <Checkbox
        checked={selectedRowKeys.includes(record[rowKey])} // 假设rowKey是用于唯一标识记录的字段
        onChange={() => {
          const newSelectedRowKeys = [...selectedRowKeys]
          if (newSelectedRowKeys.includes(record[rowKey])) {
            newSelectedRowKeys.splice(
              newSelectedRowKeys.indexOf(record[rowKey]),
              1
            )
          } else {
            newSelectedRowKeys.push(record[rowKey])
          }
          setSelectedRowKeys(newSelectedRowKeys)
          onSelectionChange(newSelectedRowKeys) // 更新父组件的选中项
        }}
      />
    )
  }

  // 在columns数组的第二位插入自定义的选择列
  const updatedColumns = [
    ...columns.slice(0, 1), // 取前一列
    selectionColumn, // 插入选择列
    ...columns.slice(1) // 取剩余列
  ]

  return (
    <div className='TableComSelect1'>
      <SimpleBar
        style={{ maxHeight: '600px', overflowY: 'auto', display: 'block' }}
        className='SimpleBar'
      >
        <Table
          components={components} // 应用自定义行组件等
          rowKey={rowKey} // 设置行键
          columns={updatedColumns}
          dataSource={data}
          loading={loading}
          pagination={false}
          // rowSelection={{ //旧的选择功能会一直在表格最左边
          //   ...rowSelection,
          //   type: selectionType,
          //   columnTitle: t('select'),
          //   columnWidth: '100px'
          // }}
          scroll={{
            x: 1700
          }}
        ></Table>
      </SimpleBar>
    </div>
  )
}
export default TableComSelect1