react-beautiful-dnd垂直案例标签

时间:2024-12-15 12:57:03

缘由: 项目比较古老,ant使用的是4,tag不支持托拽。

使用  react-beautiful-dnd 插件进行开发。

官方地址:GitHub - atlassian/react-beautiful-dnd: Beautiful and accessible drag and drop for lists with React

官方案例:Storybook

其他参考: demo文档  最开始找到这个,后面发现不是官方地址。。 

import React, { useEffect, useState } from 'react';
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
import { Space } from 'antd';
import CheckableTag from 'antd/es/tag/CheckableTag';

const getItemKey = (itemName) => {
  switch (itemName) {
    case '包':
      return 'clue_pkg_id';
    case '租户':
      return 'tenant_id';
    case '通道':
      return 'channel_id';
    default:
      return itemName;
  }
};

/**
 * @function 生成初始数组
 * @returns {array}
 * @param items
 * */
const createItems = (items) =>
  items.map((item, index) => ({
    id: `${item}`,
    content: item,
  }));

/**
 * @function 生成排序后的数组
 * @param {array} list 排序前数组
 * @param {number} startIndex 元素在被拖动前所在索引
 * @param {number} endIndex 元素在被拖动后位置所在索引
 * @return {array} result 排序后数组
 * */
const reorder = (list, startIndex, endIndex) => {
  console.log('reorder:', list, startIndex, endIndex);
  const result = Array.from(list); // 深拷贝数组
  if (Math.abs(startIndex - endIndex) > 1) {
    if (startIndex < endIndex) {
      // 将 startIndex 到 endIndex-1 的元素向前移动一位
      for (let i = startIndex; i < endIndex; i++) {
        result[i] = list[i + 1];
      }
      // 将 startIndex 元素放到 endIndex 位置
      result[endIndex] = list[startIndex];
    } else {
      // 将 endIndex+1 到 startIndex 的元素向后移动一位
      for (let i = startIndex; i > endIndex; i--) {
        result[i] = list[i - 1];
      }
      // 将 startIndex 元素放到 endIndex 位置
      result[endIndex] = list[startIndex];
    }
  } else {
    // 如果相差为1,直接交换两个元素
    [result[startIndex], result[endIndex]] = [list[endIndex], list[startIndex]];
  }
  return result;
};

const HeaderSearchGroupDrag = (props) => {
  //['clue_pkg_id', 'tenant_id', 'channel_id']
  const [items, setItems] = useState(createItems(['包', '租户', '通道']));
  const [selectedTags, setSelectedTags] = useState(['包', '租户', '通道']);

  useEffect(() => {
    console.log('selectedTags:', selectedTags)
  }, [selectedTags]);

  const onDragEnd = (result) => {
    console.log('onDragEnd-result:', result);
    if (!result.destination) {
      return;
    }
    const itemsR = reorder(items, result.source.index, result.destination.index);
    console.log('onDragEnd:', result, itemsR);
    setItems(itemsR);
  };

  const tagCheckHandleChange = (tag, checked) => {
    const nextSelectedTags = checked
      ? [...selectedTags, tag]
      : selectedTags.filter((t) => t !== tag);
    console.log('You are interested in: ', nextSelectedTags);
    setSelectedTags(nextSelectedTags);
  };

  /**
   * @function 渲染拖拽项目
   * @param {array} dragArray
   * @return {array} react element
   * */
  const renderDragItem = () => {
    let dragElement = null;
    console.log('renderDragItem1212121212', items);
    dragElement = items.map((item, index) => {
      const dragItem = (
        <Draggable key={item.content} draggableId={item.content} index={index}>
          {(provided) => (
            <div ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>
              <CheckableTag
                checked={selectedTags.indexOf(item.content) > -1}
                color="success"
                onChange={(checked) => tagCheckHandleChange(item.content, checked)}
                key={'CheckableTag:' + item.content}
              >
                {item.content}
              </CheckableTag>
            </div>
          )}
        </Draggable>
      );
      return dragItem;
    });
    return dragElement;
  };

  return (
    <div className="root">
      <DragDropContext onDragEnd={onDragEnd}>
        {/* Your target */}
        <Droppable droppableId="id" direction="horizontal">
          {(provided) => (
            <div ref={provided.innerRef} {...provided.droppableProps}>
              <Space>{renderDragItem()}</Space>
              {provided.placeholder}
            </div>
          )}
        </Droppable>
      </DragDropContext>
    </div>
  );
};

export default HeaderSearchGroupDrag;

增加传参,排序。

import React, { useEffect, useState } from 'react';
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
import { Space } from 'antd';
import CheckableTag from 'antd/es/tag/CheckableTag';

const getItemKey = (itemName) => {
  switch (itemName) {
    case '包':
      return 'clue_pkg_id';
    case '租户':
      return 'tenant_id';
    case '通道':
      return 'channel_id';
    default:
      return itemName;
  }
};

/**
 * @function 生成初始数组
 * @returns {array}
 * @param items
 * */
const createItems = (items) =>
  items.map((item, index) => ({
    id: `${item}`,
    content: item,
  }));

/**
 * @function 生成排序后的数组
 * @param {array} list 排序前数组
 * @param {number} startIndex 元素在被拖动前所在索引
 * @param {number} endIndex 元素在被拖动后位置所在索引
 * @return {array} result 排序后数组
 * */
const reorder = (list, startIndex, endIndex) => {
  console.log('reorder:', list, startIndex, endIndex);
  const result = Array.from(list); // 深拷贝数组
  if (Math.abs(startIndex - endIndex) > 1) {
    if (startIndex < endIndex) {
      // 将 startIndex 到 endIndex-1 的元素向前移动一位
      for (let i = startIndex; i < endIndex; i++) {
        result[i] = list[i + 1];
      }
      // 将 startIndex 元素放到 endIndex 位置
      result[endIndex] = list[startIndex];
    } else {
      // 将 endIndex+1 到 startIndex 的元素向后移动一位
      for (let i = startIndex; i > endIndex; i--) {
        result[i] = list[i - 1];
      }
      // 将 startIndex 元素放到 endIndex 位置
      result[endIndex] = list[startIndex];
    }
  } else {
    // 如果相差为1,直接交换两个元素
    [result[startIndex], result[endIndex]] = [list[endIndex], list[startIndex]];
  }
  return result;
};

// 按照 items 的顺序 调整 selectedTags 的顺序
// selectedTags 的所有值 都包含在 items 中,可能会比 items值要少。 需要 将selectedTags 已有值 按照 items值的顺序进行调整。
const selectedTagsSort = (items, selectedTags) => {
  const result = Array.from(selectedTags); // 深拷贝数组
  var i = 0;
  for (let j = 0; j < items.length; j++) {
    var item = items[j];
    if (selectedTags.includes(item.id)) {
      result[i] = item.id;
      i++;
    }
  }
  return result;
};

const HeaderSearchGroupDrag = (props) => {
  const { setGroupFiledList } = props;

  //['clue_pkg_id', 'tenant_id', 'channel_id']
  const [items, setItems] = useState(createItems(['包', '租户', '通道']));
  const [selectedTags, setSelectedTags] = useState(['包', '租户', '通道']);

  useEffect(() => {
    console.log('selectedTags:', selectedTags);
    console.log('items:', items);
    // 转换为 key。
    setGroupFiledList(selectedTags.map((item) => getItemKey(item)));
  }, [selectedTags]);

  const onDragEnd = (result) => {
    if (!result.destination) {
      return;
    }
    const itemsR = reorder(items, result.source.index, result.destination.index);
    setItems(itemsR);

    let sort = selectedTagsSort(itemsR, selectedTags);
    setSelectedTags(sort)
  };

  const tagCheckHandleChange = (tag, checked) => {
    const nextSelectedTags = checked
      ? [...selectedTags, tag]
      : selectedTags.filter((t) => t !== tag);
    console.log('You are interested in: ', nextSelectedTags);
    let sort = selectedTagsSort(items, nextSelectedTags);
    setSelectedTags(sort);
  };

  /**
   * @function 渲染拖拽项目
   * @param {array} dragArray
   * @return {array} react element
   * */
  const renderDragItem = () => {
    let dragElement = null;
    dragElement = items.map((item, index) => {
      const dragItem = (
        <Draggable key={item.content} draggableId={item.content} index={index}>
          {(provided) => (
            <div ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>
              <CheckableTag
                checked={selectedTags.indexOf(item.content) > -1}
                color="success"
                onChange={(checked) => tagCheckHandleChange(item.content, checked)}
                key={'CheckableTag:' + item.content}
              >
                {item.content}
              </CheckableTag>
            </div>
          )}
        </Draggable>
      );
      return dragItem;
    });
    return dragElement;
  };

  return (
    <div className="root">
      <DragDropContext onDragEnd={onDragEnd}>
        {/* Your target */}
        <Droppable droppableId="id" direction="horizontal">
          {(provided) => (
            <div ref={provided.innerRef} {...provided.droppableProps}>
              <Space>{renderDragItem()}</Space>
              {provided.placeholder}
            </div>
          )}
        </Droppable>
      </DragDropContext>
    </div>
  );
};

export default HeaderSearchGroupDrag;