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

项目背景 : 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方法 , 添加自定义选择列

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


特别注意 : 会出现第一行的拖拽失败 , 原因(当key是0时则不能拖拽 , 可以把data中的key赋值为id或者key+1 )

 

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

下拉多选可以使用antd-mobile的CheckboxItem组件来实现。具体代码如下: ```jsx import React, { useState } from 'react'; import { List, Checkbox } from 'antd-mobile'; const CheckboxItem = Checkbox.CheckboxItem; const options = [ { label: '选项1', value: '1' }, { label: '选项2', value: '2' }, { label: '选项3', value: '3' }, { label: '选项4', value: '4' }, ]; const MultipleSelect = () => { const [selectedValues, setSelectedValues] = useState([]); const handleCheckboxChange = (value) => { const currentIndex = selectedValues.indexOf(value); const newValues = [...selectedValues]; if (currentIndex === -1) { newValues.push(value); } else { newValues.splice(currentIndex, 1); } setSelectedValues(newValues); }; return ( <List> {options.map((option) => ( <CheckboxItem key={option.value} checked={selectedValues.indexOf(option.value) !== -1} onChange={() => handleCheckboxChange(option.value)} > {option.label} </CheckboxItem> ))} </List> ); }; export default MultipleSelect; ``` 上面的代码中,我们定义了一个`options`数组来存储选项列表。然后使用`useState`来管理已选中的值,初始值为空数组。在`handleCheckboxChange`方法中,我们根据选项的值来判断它是否已经被选中,如果已经被选中,则从已选中的值数组中删除该值,否则将该值添加到已选中的值数组中。最后通过`setSelectedValues`更新已选中的值数组。 在渲染时,我们遍历选项列表,并为每个选项渲染一个`CheckboxItem`组件。我们使用`checked`属性来判断该选项是否已经被选中,使用`onChange`事件来处理选项的选择和取消选择操作。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值