【JS组件篇】使用拖拽组件 react-beautiful-dnd 实现工作台自定义布局功能

项目要求

制作工作台页面:模块可根据后端配置动态展示,各模块可根据个人喜好进行拖拽布局,并保存。大致效果如下:

在这里插入图片描述

大致实现步骤

  • 整体布局。首先需要对设计稿进行分析,大致分为左右两部分,可根据后端配置动态展示左右两模块的展示内容,各模块相对独立,各自发送请求根据返回数据展示模块内数据。(此处样式布局方式不做赘述,但要注意考虑后期左右模块可通过拖拽方式更换位置,宽度可能变化,需注意各模块布局内容的自适应问题

  • 为页面布局添加 react-beautiful-dnd 拖拽组件。(组件的内部结构及参数API可查看文章末尾的参考文章)

实现代码
1.页面布局:
	<div className={styles.workbench}>
      <DragDropContext onDragEnd={onDragEnd}>
      	{/* 左侧中心布局 */}
        <Droppable droppableId="centerModule">
          {(provided) => (
            <div className={styles.centerLayout} ref={provided.innerRef} {...provided.droppableProps}>
              {renderDragItem(centerModuleList)}
              {provided.placeholder}
            </div>
          )}
        </Droppable>
      	{/* 右侧布局 */}
        <Droppable droppableId="rightModule">
          {(provided) => (
            <div className={styles.rightLayout} ref={provided.innerRef} {...provided.droppableProps}>
              {renderDragItem(rightModuleList)}
              {provided.placeholder}
            </div>
          )}
        </Droppable>
      </DragDropContext>
    </div>
2.拖拽结束的排序 js:
  // 中心模块数据
  const [centerModuleList, setCenterModuleList] = useState(Object.values(userWorkBenchConfig.centerModuleList));
  // 右侧模块数据
  const [rightModuleList, setRightModuleList] = useState(Object.values(userWorkBenchConfig.rightModuleList));

  // 深拷贝
  const deepClone = (orig) => {
    var copy = Object.create(Object.getPrototypeOf(orig))
    copyOwnPropertiesFrom(copy, orig);
    return copy;
  }
  const copyOwnPropertiesFrom = (target, source) => {
    Object.getOwnPropertyNames(source)
      .forEach(ele => {
        // 获取到每个属性的所有描述,然后重新定义对象的属性并返回此对象。
        var desc = Object.getOwnPropertyDescriptor(source, ele);
        Object.defineProperty(target, ele, desc)
      });
    return target;
  }  

  // 拖拽结束放置 - 若拖拽位置或布局有一定规则,可在此处进行判断校验,最后将排序好的布局存储起来
  const onDragEnd = (result) => {
  	// source 为源位置数据,destination 为目标位置数据
    const { destination, source } = result;
    const sourceIndex = source.index;
    const destinationIndex = destination.index;
    // 默认布局配置 - 默认中心布局为目标位置,右侧布局为源位置
    let targetModuleList = centerModuleList;
    let targetFn = setCenterModuleList;
    let sourceModuleList = rightModuleList;
    let sourceFn = setRightModuleList;
    // 若拖拽源和目标在同一Droppable中,则只需重新排序一个Droppable;
    // 若不在同一Droppable中,则两个模块都需重新排序
    if (destination.droppableId === source.droppableId) {
      if (sourceIndex === destinationIndex) {
        return;
      }
      if (destination.droppableId === 'rightModule') {
        targetModuleList = rightModuleList;
        targetFn = setRightModuleList;
      }
      const userList = deepClone(targetModuleList);
      const [draggedItem] = userList.splice(sourceIndex, 1);
      userList.splice(destinationIndex, 0, draggedItem);
      targetFn(userList);
    } else {
      if (destination.droppableId === 'rightModule') {
        targetModuleList = rightModuleList;
        targetFn = setRightModuleList;
        sourceModuleList = centerModuleList;
        sourceFn = setCenterModuleList;
      }
      // 深拷贝
      const targetList = deepClone(targetModuleList);
      const sourceList = deepClone(sourceModuleList);
      // 从源数据中取出放入目标数据中
      const [draggedSourceItem] = sourceList.splice(sourceIndex, 1);
      targetList.splice(destinationIndex, 0, draggedSourceItem);
      targetFn(targetList);
      sourceFn(sourceList);
    }
  };

在这里插入图片描述

参考文章:
组件标签结构参考:https://blog.csdn.net/dongliang3164/article/details/118905576
组件参数API参考:https://blog.csdn.net/tianxintiandisheng/article/details/107109890

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

章魚尐芄子

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

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

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

打赏作者

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

抵扣说明:

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

余额充值