Skip to content

sortablejs

https://juejin.cn/post/7572161976592990260

模板结构

vue
<template>
  <el-table ref="typeTableRef" :data="filteredTypeData" stripe row-key="id">
    <!-- 排序列:显示拖拽图标 -->
    <el-table-column label="排序" width="131">
      <template #default>
        <el-icon class="drag-handle">
          <Operation />
        </el-icon>
      </template>
    </el-table-column>
    
    <!-- 其他列 -->
    <el-table-column prop="name" label="名称" />
    <el-table-column prop="enabled" label="启用/禁用">
      <template #default="{ row }">
        <el-switch v-model="row.enabled" />
      </template>
    </el-table-column>
  </el-table>
</template>

核心实现代码

js
// 表格引用
const typeTableRef = ref<InstanceType<typeof ElTable>>();

// Sortable 实例(用于后续销毁)
let sortableInstance: Sortable | null = null;

/**
 * 初始化拖拽排序功能
 */
const initSortable = () => {
  // 1. 销毁旧实例,避免重复创建
  if (sortableInstance) {
    sortableInstance.destroy();
    sortableInstance = null;
  }

  // 2. 等待 DOM 更新完成
  nextTick(() => {
    // 3. 获取表格的 tbody 元素
    const tbody = typeTableRef.value?.$el?.querySelector(
      ".el-table__body-wrapper tbody"
    );
    
    if (!tbody) return;

    // 4. 创建 Sortable 实例
    sortableInstance = Sortable.create(tbody, {
      // 指定拖拽手柄(只能通过拖拽图标来拖拽)
      handle: ".drag-handle",
      
      // 动画时长(毫秒)
      animation: 300,
      
      // 拖拽结束回调
      onEnd: ({ newIndex, oldIndex }) => {
        // 5. 更新数据顺序
        if (newIndex !== undefined && ldIndex !== undefined && filterStatus.value === "all") {
          // 只在"全部"状态下允许排序
          // 获取被移动的项
          const movedItem = typeData.value[oldIndex];
          // 从原位置删除
          typeData.value.splice(oldIndex, 1);
          // 插入到新位置
          typeData.value.splice(newIndex, 0, movedItem);
          // 更新排序字段
          typeData.value.forEach((item, index) => {
            item.sortOrder = index + 1;
          });
        }
      }
    });
  });
};

生命周期管理

js
/**
 * 监听标签页切换,初始化拖拽
 */
const watchActiveTab = () => {
  if (activeTab.value === "type") {
    // 延迟初始化,确保表格已完全渲染
    setTimeout(() => {
      initSortable();
    }, 300);
  }
};

// 组件挂载时初始化
onMounted(() => {
  watchActiveTab();
});

// 监听标签页切换
watch(activeTab, () => {
  watchActiveTab();
});

// 监听过滤器变化,重新初始化拖拽
watch(filterStatus, () => {
  if (activeTab.value === "type") {
    setTimeout(() => {
      initSortable();
    }, 100);
  }
});

样式定义

/* 拖拽手柄样式 */
.drag-handle {
  color: #909399;
  cursor: move;
  font-size: 18px;
  transition: color 0.3s;
}

.drag-handle:hover {
  color: #1890ff;
}

/* 表格样式 */
.type-table {
  margin-top: 0;
}

:deep(.type-table .el-table__header-wrapper) {
  background-color: #f9fafc;
}

:deep(.type-table .el-table th) {
  background-color: #f9fafc;
  font-size: 14px;
  font-weight: 500;
  color: #33425cfa;
  font-family: PingFang SC;
  border-bottom: 1px solid #dcdfe6;
}

使用sortablejs拖拽排序 视图显示与数据不一致、拖拽结束后回跳

数据的顺序是对的,视图是乱的 https://juejin.cn/post/7370841518792015907

CAUTION

  • v-for循环一定要给一个唯一的key!!! 不能偷懒用index,害我搞半天

  • el-table 需要设置row-key="fieldKey",默认使用index

// 重新赋值来刷新视图 (这里我调的父组件的变量,你的可以换成当前组件的变量)
        that.$parent.moduleList = [] // 必须有此步骤,不然拖拽后回弹
        that.$nextTick(function () {
          that.$parent.moduleList = newArray // 重新赋值,用新数据来刷新视图 
          console.log(that.moduleList)
        })

上次更新时间:

最近更新