react-dnd实现表单动态定点拖拽排序

react-dnd实现表单动态定点拖拽排序


效果如下:
在这里插入图片描述

react-dnd使用方法介绍

DndProvider注入:将需要拖拽源和拖放目标组件置入其内

backend:必需。React DnD后端。除非您正在编写自定义的,否则您可能希望使用React DnD附带的HTML5后端。

import { HTML5Backend } from 'react-dnd-html5-backend'
import { DndProvider } from 'react-dnd'

export default function MyReactApp() {
  return (
    <DndProvider backend={HTML5Backend}>
      /* your drag-and-drop application */
    </DndProvider>
  )
}
useDrag:用于将当前组件用作拖动源的钩子
import { useDrag } from 'react-dnd'

function DraggableComponent(props) {
  const [collected, drag, dragPreview] = useDrag(() => ({
    type,
    item: { id }
  }))
  return collected.isDragging ? (
    <div ref={dragPreview} />
  ) : (
    <div ref={drag} {...collected}>
      ...
    </div>
  )
}

参数解释:

  • collected:包含从收集函数收集的属性的对象
  • drag:拖曳源的连接器功能。这必须附加到DOM的可拖动部分。
  • dragPreview:用于拖动预览的连接器功能。这可以附加到DOM的预览部分。
useDrop:用于将当前组件成为拖放目标的钩子
import { useDrop } from 'react-dnd'

function myDropTarget(props) {
  const [collectedProps, drop] = useDrop(() => ({
    accept
  }))

  return <div ref={drop}>Drop Target</div>
}

参数解释:

  • collectedProps:包含从收集函数收集的属性的对象
  • drop:拖放目标的连接器功能。这必须附加到DOM的拖放目标部分。

实战:

为了更加好的拓展性,将其提取为公共组件

在这里插入图片描述

实现一个拖动时,透明度变化

在这里插入图片描述

效果
在这里插入图片描述

这部分代码:保证了定点推拽功能

ref={drag}保证了只有在点击span元素,才能拖动

在这里插入图片描述

SortCard.tsx完整代码:

import type { Identifier } from 'dnd-core';
import type { FC } from 'react';
import { useDrag, useDrop } from 'react-dnd';
import { DragOutlined } from '@ant-design/icons';

export interface CardProps {
  ItemTypes: string;
  id: any;
  children: React.ReactNode;
  index: number;
  moveCard: (dragIndex: number, hoverIndex: number) => void;
  style?: React.CSSProperties;
}

interface DragItem {//拖动源的属性
  index: number;
  id: string;
  type: string;
}

export const SortCard: FC<CardProps> = ({
  ItemTypes,//区别不同场景的拖拽组件
  id,
  children,
  index,
  moveCard,
  style = {},
}) => {
  const [{ handlerId }, drop] = useDrop<
    DragItem,
    void,
    { handlerId: Identifier | null }
  >({
    accept: ItemTypes,
    collect(monitor) {
      return {
        handlerId: monitor.getHandlerId(),
      };
    },
    drop: (item: any) => {
      if (item.index === index) return;
      moveCard(item.index, index);
      item.index = index;
    },
  });

  const [{ isDragging }, drag, preview] = useDrag({
    type: ItemTypes,
    item: () => {
      return { id, index };
    },
    collect: (monitor: any) => ({
      isDragging: monitor.isDragging(),
    }),
  });

  return (
    <div ref={drop} data-handler-id={handlerId}>
      <div
        ref={preview}
        className="flex"
        style={{ opacity: isDragging ? 0.5 : 1, ...style }}
      >
        <span
          ref={drag}
          style={{ marginTop: 30, width: 30 }}
          className="cursor-pointer"//用户鼠标移到定点时,光标变成手
        >
          <DragOutlined />//antd图标
        </span>

        {children}
      </div>
    </div>
  );
};

公共组件封装好调用它:

定义一下moveCard方法:

具体:先对表单数据拷贝一份,拖拽后,更新表单数据顺序,重新赋值给表单

![在这里插入图片描述](https://img-blog.csdnimg.cn/03fd6d41bd5b409590f313c96bd40aa7.pn

对需要拖拽源和拖拽目标做处理,看一个例子

 <DndProvider backend={HTML5Backend}>//DndProvider注入
  <Form.List name="bannerList" initialValue={[{}]}>
            {(fields, { remove }) => (
              <>
                {fields.map(({ key, name, ...restField }, index: number) => (
                  <SortCard//用到刚刚定义的公共组件
                    ItemTypes="bannerCode"
                    index={index}
                    id={key}
                    moveCard={(dragIndex, hoverIndex) =>
                      moveCard(dragIndex, hoverIndex, 'bannerList')//调用moveCard	
                    }
                    style={{
                      border: '1px solid #ccc',
                      padding: 10,
                      marginBottom: 10,
                    }}
                  >
                    <div className="flex flex-start">
                      <Form.Item
                        {...restField}
                        name={[name, 'imagesourcesrc1']}
                        label="图片资源链接:"
                      >
                        <Input />
                      </Form.Item>
                      <MinusCircleOutlined
                        style={{ padding: '38px 0 0 10px' }}
                        onClick={() => remove(name)}
                      />
                    </div>
                  </SortCard>
                ))}
              </>
            )}
          </Form.List>
    </DndProvider>

完整代码

import { SortCard } from '@/components/SortList/SortCard';
import { MinusCircleOutlined } from '@ant-design/icons';
import { Button, Form, Input, Typography } from 'antd';
import React, { FC } from 'react';
import { HTML5Backend } from 'react-dnd-html5-backend';
import { DndProvider } from 'react-dnd';
import './walletModalForm.scoped.scss';

interface Props {
  ctrlRef: any;
  formValues: any;
  channelConfigList: any[];
  method: '1' | '2';
}

const { Title } = Typography;

const WForm: FC<Props> = () => {

  const [form] = Form.useForm();

  function onFinish() {}

  function moveCard(dragIndex: number, hoverIndex: number, name: string) {
    const list = form.getFieldValue(name);
    const listClone = [...list];
    listClone.splice(hoverIndex, 0, listClone.splice(dragIndex, 1)[0]);
    form.setFieldsValue({
      [name]: listClone,
    });
  }

  return (
    <DndProvider backend={HTML5Backend}>
      <div className="walletModalForm">
        <Form
          name="dynamic_form_nest_item"
          onFinish={onFinish}
          autoComplete="off"
          form={form}
          layout="vertical"
        >
          <Title level={4}>天天向上</Title>
          <Form.List name="FormList" initialValue={[{}]}>
            {(fields, { add, remove }) => (
              <>
                {fields.map(({ key, name, ...restField }, index: number) => (
                  <SortCard
                    ItemTypes="channelCode"
                    index={index}
                    id={key}
                    moveCard={(dragIndex, hoverIndex) =>
                      moveCard(dragIndex, hoverIndex, 'FormList')
                    }
                    style={{
                      border: '1px solid #ccc',
                      padding: 10,
                      marginBottom: 10,
                    }}
                  >
                    <div className="flex flex-1 justify-around">
                      <Form.Item
                        {...restField}
                        name={[name, 'showname']}
                        label="showname:"
                        style={{ display: 'inline-block' }}
                      >
                        <Input placeholder="showname" />
                      </Form.Item>
                      <Form.Item
                        {...restField}
                        name={[name, 'imagesourcesrc']}
                        label="imagesourcesrc:"
                      >
                        <Input placeholder="imagesourcesrc" />
                      </Form.Item>

                      <MinusCircleOutlined
                        style={{ padding: '38px 0 0 10px' }}
                        onClick={() => remove(name)}
                      />
                    </div>
                  </SortCard>
                ))}
                <Form.Item name="add">
                  <div
                    onClick={() => add({})}
                    className="inline-block"
                    style={{
                      color: '#3F90F7',
                      padding: '10px',
                    }}
                  >
                    添加一条
                  </div>
                </Form.Item>
              </>
            )}
          </Form.List>

          <Title level={4}>好好学习</Title>
          <Form.List name="bannerList" initialValue={[{}]}>
            {(fields, { add, remove }) => (
              <>
                {fields.map(({ key, name, ...restField }, index: number) => (
                  <SortCard
                    ItemTypes="bannerCode"
                    index={index}
                    id={key}
                    moveCard={(dragIndex, hoverIndex) =>
                      moveCard(dragIndex, hoverIndex, 'bannerList')
                    }
                    style={{
                      border: '1px solid #ccc',
                      padding: 10,
                      marginBottom: 10,
                    }}
                  >
                    <div className="flex flex-start">
                      <Form.Item
                        {...restField}
                        name={[name, 'imagesourcesrc1']}
                        label="第一个数据:"
                      >
                        <Input />
                      </Form.Item>
                      <Form.Item
                        {...restField}
                        name={[name, 'inconsourcesrc1']}
                        label="第二个数据:"
                        style={{ padding: '0 0 0 10px' }}
                      >
                        <Input placeholder="inconsourcesrc1" />
                      </Form.Item>
                      <MinusCircleOutlined
                        style={{ padding: '38px 0 0 10px' }}
                        onClick={() => remove(name)}
                      />
                    </div>
                  </SortCard>
                ))}
                <Form.Item name="add2">
                  <div
                    onClick={() => add({})}
                    className="inline-block"
                    style={{
                      color: '#3F90F7',
                      padding: '10px',
                    }}
                  >
                    添加一条
                  </div>
                </Form.Item>
              </>
            )}
          </Form.List>

          <Form.Item>
            <Button type="primary" htmlType="submit">
              保存
            </Button>
          </Form.Item>
        </Form>
      </div>
    </DndProvider>
  );
};
export default WForm;

码字不易,如果对你有帮助,帮小编点个赞,关注一波。
如有错误,请大佬们指出。尽力改正

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
React-dnd 拖拽排序是一种常见的前端交互方式,可以通过以下代码实现:1. 首先需要安装 react-dndreact-dnd-html5-backend 两个库:``` npm install --save react-dnd react-dnd-html5-backend ```2. 在组件中引入 DragDropContext、Droppable 和 Draggable 组件:``` import { DragDropContext, Droppable, Draggable } from 'react-dnd'; import { HTML5Backend } from 'react-dnd-html5-backend'; ```3. 定义一个数组作为拖拽列表的数据源:``` const items = [ { id: 1, name: 'Item 1' }, { id: 2, name: 'Item 2' }, { id: 3, name: 'Item 3' }, { id: 4, name: 'Item 4' }, { id: 5, name: 'Item 5' }, ]; ```4. 在组件中使用 DragDropContext 组件包裹整个列表,并在其中使用 Droppable 组件包裹每个拖拽项:``` <DragDropContext backend={HTML5Backend}> <Droppable droppableId="items"> {(provided) => ( <ul {...provided.droppableProps} ref={provided.innerRef}> {items.map((item, index) => ( <Draggable key={item.id} draggableId={item.id.toString()} index={index}> {(provided) => ( <li {...provided.draggableProps} {...provided.dragHandleProps} ref={provided.innerRef} > {item.name} </li> )} </Draggable> ))} {provided.placeholder} </ul> )} </Droppable> </DragDropContext> ```5. 在 Draggable 组件中使用 provided.draggableProps 和 provided.dragHandleProps 属性来实现拖拽功能,同时使用 provided.innerRef 属性来获取拖拽元素的引用。6. 在 Droppable 组件中使用 provided.droppableProps 和 provided.innerRef 属性来实现拖拽排序功能。7. 最后,需要在 DragDropContext 组件中定义 onDragEnd 回调函数来处理拖拽结束后的逻辑:``` function onDragEnd(result) { if (!result.destination) { return; } const newItems = Array.from(items); const [reorderedItem] = newItems.splice(result.source.index, 1); newItems.splice(result.destination.index, , reorderedItem); setItems(newItems); }<DragDropContext backend={HTML5Backend} onDragEnd={onDragEnd}> ... </DragDropContext> ```8. 在 onDragEnd 回调函数中,首先判断是否有目标位置,如果没有则直接返回。然后使用 Array.from 方法复制一份原始数据源,从中取出被拖拽的元素并删除,再将其插入到目标位置中,最后使用 setItems 函数更新数据源。以上就是 react-dnd 拖拽排序的代码实现

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

万希&

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

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

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

打赏作者

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

抵扣说明:

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

余额充值