react拖拽排序(写入Table中的components)

97 阅读2分钟
import { Button, Popconfirm, Table, Space } from 'antd';
import { connect } from 'umi';
const lodash = require('lodash');
const { isEmpty, cloneDeep } = lodash;
import { getGuid } from '@/utils/index';
import TextAreaChange from '../AddModal/components/TextAreaChange';
import type { SortableContainerProps, SortEnd } from 'react-sortable-hoc';
import {
  SortableContainer,
  SortableElement,
  SortableHandle
} from 'react-sortable-hoc';
import { MenuOutlined } from '@ant-design/icons';
import { useState } from 'react';
import { arrayMoveImmutable } from 'array-move';
import styles from './index.less';
import React, { useEffect } from 'react';

const AddModal = (props: any) => {
  // 当前编辑的指引数据
  const [dataSource, setDataSource] = useState<any>([]);

  // 当前待删除数据
  const [delIds, setDelIds] = useState<Array<any>>([]);

  useEffect(() => {
    setDataSource(localData);
  }, [localData]);

  /**
   * @method 输入框改变的回调
   */
  const onChange = (e: any, rowId: any, type: string) => {
    let newList: Array<any> = [];
    newList = dataSource.map((item: any) => {
            if (rowId === item.guideEntryId || rowId === item.identifyId) {
              item.guideContent = e;
              return item;
            } else {
              return item;
            }
          });

    setDataSource(newList);
  };

  // 招标指引columns
  const columns: any = [
    {
      title: intl.formatMessage({ id: 'Order1' }),
      dataIndex: 'sort',
      align: 'center',
      width: '8%',
      className: styles.drag_hidden,
      render: () => {
        return <DragHandle />;
      }
    },
    {
      title: intl.formatMessage({ id: 'Bid Control' }),
      dataIndex: 'controlDegree',
      align: 'center',
    },
    {
      title: intl.formatMessage({ id: 'Contents' }),
      dataIndex: 'guideContent',
      align: 'center',
      width: '40%',
      render: (_: any, record: any) => {
        const { bomCode, id, content } = record;
        return (
          <TextAreaChange
            rowId={id}
            value={content}
            type="content"
            onChange={onChange}
          />
        );
      }
    },
    {
      title: intl.formatMessage({ id: 'Related Reports' }),
      dataIndex: 'fileName',
      align: 'center',
      width: '20%',
    },
    {
      title: intl.formatMessage({ id: 'Operation' }),
      dataIndex: 'edit',
      align: 'center',
      width: '10%',
      render: (_: any, record: any, index: number) => {
        return (
          <Space>
            <div
              title={intl.formatMessage({ id: 'Add' })}
              onClick={() => _addOne(record, index)}
            >
              {addOne}
            </div>
            <Popconfirm
              title={intl.formatMessage({
                id: 'Are you sure you want to delete it?'
              })}
              onConfirm={() => _delOne(record)}
            >
             delete
            </Popconfirm>
          </Space>
        );
      }
    }
  ];

  const DragHandle = SortableHandle(() => (
    <MenuOutlined style={{ cursor: 'grab', color: '#999' }} />
  ));


  const DraggableContainer = (props: SortableContainerProps) => (
    <SortableBody
      lockAxis="y"
      useDragHandle
      disableAutoscroll={false}
      // lockToContainerEdges
      helperClass={styles.row_dragging}
      onSortEnd={_onSortEnd}
      {...props}
    />
  );

  const DraggableBodyRow = ({ ...props }) => {
    const index = dataSource?.findIndex(
      (x: any) => x.sort === props['data-row-key']
    );
    return <SortableItem index={index} {...props} />;
  };

  const SortableItem = SortableElement(
    (props: React.HTMLAttributes<HTMLTableRowElement>) => <tr {...props} />
  );

  const SortableBody = SortableContainer(
    (props: React.HTMLAttributes<HTMLTableSectionElement>) => (
      <tbody {...props} />
    )
  );

  /**
   * @method 排序结束时的回调
   * @param {Number} oldIndex 排序前的序号
   * @param {Number} newIndex 排序后的序号
   */
  const _onSortEnd = ({ oldIndex, newIndex }: SortEnd) => {
    if (oldIndex !== newIndex) {
      const newData = arrayMoveImmutable(
        dataSource?.slice(),
        oldIndex,
        newIndex
      ).filter((el: any) => !!el);
      newData.forEach((item: any, index: number) => {
        item.sort = index + 1;
      });
      setDataSource(newData);
    }
  };

  return (
    <>
      <Modal
        canDragm
        destroyOnClose={true}
        maskClosable={false}
        title={intl.formatMessage({ id: 'Selected Item(s)' })}
        visible
        onCancel={_onCancel}
        className={styles.productAndService_modifyGuideModal_editModal}
        footer={
          <div
            className={styles.productAndService_modifyGuideModal_operateBtns}
          >
            <Button
              type="primary"
              className={styles.productAndService_modifyGuideModal_okBtn}
              onClick={_handleSave}
            >
              {intl.formatMessage({ id: 'OK' })}
            </Button>
            <Button
              onClick={_onCancel}
            >
              {intl.formatMessage({ id: 'Cancel' })}
            </Button>
          </div>
        }
      >
        <Table
          size="small"
          rowKey="sort"
          columns={columns}
          dataSource={dataSource}
          bordered
          pagination={false}
          scroll={{ y: 'calc(480px)' }}
          components={{
            body: {
              wrapper: DraggableContainer,
              row: DraggableBodyRow
            }
          }}
        />
      </Modal>
    </>
  );
};

export default connect(({ dataCenter }: any) => {
  const { localData } = dataCenter;
  return {
    localData
  };
})(AddModal);

拖拽的样式

.row_dragging {
  position: relative;
  z-index: 1000;
  background: #f7f8fa;
  border: 1px solid #ccc;
  width: 100%;

  .drag_hidden {
    visibility: hidden;
  }

  td {
    padding: 6px 8px;
    background-color: #f7f8fa !important;
  }
}