You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现支持多展开项的可拖拽排序Accordion组件?

实现支持多展开/折叠的可拖拽排序Accordion组件

核心思路

  • 用状态单独记录每个面板的展开/折叠状态(支持多面板同时展开)
  • 集成拖拽排序能力,拖拽结束后更新面板的展示顺序
  • 每个面板独立控制自身的展开/折叠状态,互不干扰

关键实现步骤(以React为例)

1. 依赖准备

使用react-beautiful-dnd实现拖拽排序(也可使用HTML5原生拖拽API,不过前者封装了更多细节),先安装依赖:

npm install react-beautiful-dnd

2. 初始化状态

定义面板数据列表和状态,包含每个面板的ID、标题、内容及展开状态:

import { useState } from 'react';
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';

// 初始面板数据
const initialPanels = [
  { id: 'panel-1', title: '面板1', content: '这是面板1的内容', isOpen: true },
  { id: 'panel-2', title: '面板2', content: '这是面板2的内容', isOpen: false },
  { id: 'panel-3', title: '面板3', content: '这是面板3的内容', isOpen: false },
];

function DraggableAccordion() {
  const [panels, setPanels] = useState(initialPanels);

3. 实现展开/折叠切换逻辑

给每个面板标题绑定点击事件,切换对应面板的isOpen状态:

const togglePanel = (panelId) => {
  setPanels(prevPanels => 
    prevPanels.map(panel => 
      panel.id === panelId ? { ...panel, isOpen: !panel.isOpen } : panel
    )
  );
};

4. 实现拖拽排序逻辑

通过react-beautiful-dnd的核心组件包裹面板区域,处理拖拽结束后的顺序更新:

const handleDragEnd = (result) => {
  // 拖拽到区域外则不处理
  if (!result.destination) return;

  const updatedPanels = Array.from(panels);
  // 取出拖拽项,插入到目标位置
  const [movedItem] = updatedPanels.splice(result.source.index, 1);
  updatedPanels.splice(result.destination.index, 0, movedItem);

  setPanels(updatedPanels);
};

5. 渲染完整组件

将每个面板包装为可拖拽项,同时根据isOpen状态控制内容显示:

return (
  <DragDropContext onDragEnd={handleDragEnd}>
    <Droppable droppableId="accordion-container">
      {(provided) => (
        <div 
          {...provided.droppableProps} 
          ref={provided.innerRef}
          style={{ maxWidth: 600, margin: '0 auto' }}
        >
          {panels.map((panel, index) => (
            <Draggable key={panel.id} draggableId={panel.id} index={index}>
              {(provided, snapshot) => (
                <div
                  ref={provided.innerRef}
                  {...provided.draggableProps}
                  className={`accordion-panel ${snapshot.isDragging ? 'dragging' : ''}`}
                >
                  {/* 面板标题栏:可点击切换展开,同时作为拖拽手柄 */}
                  <div
                    className="accordion-header"
                    onClick={() => togglePanel(panel.id)}
                    {...provided.dragHandleProps}
                  >
                    <h3>{panel.title}</h3>
                    <span>{panel.isOpen ? '▼' : '▶'}</span>
                  </div>
                  {/* 面板内容:根据isOpen状态显示/隐藏 */}
                  {panel.isOpen && (
                    <div className="accordion-content">
                      {panel.content}
                    </div>
                  )}
                </div>
              )}
            </Draggable>
          ))}
          {provided.placeholder}
        </div>
      )}
    </Droppable>
  </DragDropContext>
);
}

export default DraggableAccordion;

6. 添加基础样式

优化组件视觉效果,区分拖拽状态:

.accordion-panel {
  border: 1px solid #e0e0e0;
  margin-bottom: 8px;
  border-radius: 4px;
  overflow: hidden;
}

.accordion-header {
  padding: 12px 16px;
  background-color: #f5f5f5;
  cursor: pointer;
  display: flex;
  justify-content: space-between;
  align-items: center;
  transition: background-color 0.2s;
}

.accordion-header:hover {
  background-color: #eeeeee;
}

.accordion-content {
  padding: 16px;
  background-color: #ffffff;
  border-top: 1px solid #e0e0e0;
}

.dragging {
  opacity: 0.6;
  border: 2px dashed #999;
}

额外优化建议

  • 若面板数量较多,可改用useReducer管理状态,让逻辑更清晰
  • 可添加展开/折叠的过渡动画,提升用户体验
  • 若不需要第三方库,可基于HTML5原生drag、drop事件实现拖拽,但需自行处理拖拽状态、位置计算等细节

内容的提问来源于stack exchange,提问作者PRANAV

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 14:25:19