数组拖拽交换顺序的几种方法推荐

一、html原生方法:

eg:表格-两列直接交换顺序:

<template>
      <el-table :data="tableData" style="width: 100%">
        <el-table-column
          v-for="(item, index) in columns"
          :key="item.prop"
          :prop="item.prop"
          :label="item.label"
        >
          <template #header="{ column }">
            <span
              style="cursor: move"
              draggable="true"
              @dragstart="handleDragStart(item)"
              @drop="handleDrop(item)"
              @dragover.prevent
              >{{ column.label }}</span
            >
          </template>
        </el-table-column>
      </el-table>
</template>
<script setup lang="ts">
  import { ref, nextTick } from 'vue';

  interface TableColumn {
    prop: string;
    label: string;
  }

  const tableData = ref([
    { id: '1', name: '测试', location: '中国' },
    { id: '2', name: '张三', location: '英国' },
    { id: '3', name: '李四', location: '日本' },
    { id: '4', name: '王五', location: '韩国' },
    { id: '5', name: '小八', location: '厕所' },
  ]);
  const columns = ref<TableColumn[]>([
    { label: '主键', prop: 'id' },
    { label: '名称', prop: 'name' },
    { label: '国家', prop: 'location' },
  ]);

  const startIndex = ref();
  const handleDragStart = (column) => {
    if (columns.value.indexOf(column) !== startIndex.value) {
      startIndex.value = columns.value.indexOf(column);
    }
  };
  const handleDrop = (column) => {
    if (startIndex.value !== null) {
      const temp = columns.value[columns.value.indexOf(column)];
      columns.value[columns.value.indexOf(column)] = columns.value[startIndex.value];
      columns.value[startIndex.value] = temp;
      startIndex.value = null;
    }
  };
</script>

如果只是想把拖动的列放入另一列前面或后面,而不是交换顺序(比如不相邻的两列交换位置),则把handleDrop方法这样写就行

 if (startIndex.value !== null) {
      console.log('edn', columns.value.indexOf(column));
      columns.value.splice(
        columns.value.indexOf(column),
        0,
        columns.value.splice(startIndex.value, 1)[0],
      );
      startIndex.value = null;
    }

 同时支持拖拽交换、插入另一个元素左侧或右侧

<template>
      <el-table :data="tableData" style="width: 100%">
        <el-table-column
          v-for="(item, index) in columns"
          :key="item.prop"
          :prop="item.prop"
          :label="item.label"
        >
          <template #header="{ column }">
            <div style="display: flex; align-items: center">
              <span draggable="true" @dragover.prevent @drop="handleDropLeft(index)"
                ><el-icon class="hover_point"><ArrowLeftBold /></el-icon
              ></span>
              <span
                style="cursor: move"
                draggable="true"
                @dragstart="handleDragStart(index)"
                @drop="handleDrop(index)"
                @dragover.prevent
                >{{ column.label }}</span
              ><span draggable="true" @dragover.prevent @drop="handleDropRight(index)"
                ><el-icon class="hover_point"><ArrowRightBold /></el-icon></span
            ></div>
          </template>
        </el-table-column>
      </el-table>
</template>
<script setup lang="ts">
  import { ref, nextTick } from 'vue';

  interface TableColumn {
    prop: string;
    label: string;
  }

  const tableData = ref([
    { id: '1', name: '测试', location: '中国' },
    { id: '2', name: '张三', location: '英国' },
    { id: '3', name: '李四', location: '日本' },
    { id: '4', name: '王五', location: '韩国' },
    { id: '5', name: '小八', location: '厕所' },
  ]);
  const columns = ref<TableColumn[]>([
    { label: '主键', prop: 'id' },
    { label: '名称', prop: 'name' },
    { label: '国家', prop: 'location' },
  ]);

  const startIndex = ref();
  const handleDragStart = (index) => {
    if (index !== startIndex.value) {
      startIndex.value = index;
    }
  };
  const handleDrop = (index) => {
    if (startIndex.value !== null) {
      const temp = columns.value[index];
      columns.value[index] = columns.value[startIndex.value];
      columns.value[startIndex.value] = temp;
      startIndex.value = null;
    }
  };
  function handleDropLeft(index) {
    if (startIndex.value !== null) {
      columns.value.splice(index - 1, 0, columns.value.splice(startIndex.value, 1)[0]);
      startIndex.value = null;
    }
  }
  function handleDropRight(index) {
    if (startIndex.value !== null) {
      columns.value.splice(index, 0, columns.value.splice(startIndex.value, 1)[0]);
      startIndex.value = null;
    }
  }
</script>

二、使用sortablejs(推荐)

pnpm install sortablejs --save || npm install sortablejs --save

代码示例:

<template>
  <!-- 页面表格 -->
  <el-table :data="tableData" border align="left" height="400">
    <el-table-column
      style="cursor: move"
      v-for="(item, index) in changeCol"
      :key="`index`"
      :prop="changeCol[index].prop"
      :label="item.label"
    >
      <template #default="{ row, column, $index }" style=''>
        <div style="display: flex; align-items: center;">
          <el-icon :size='20' class="handle" style='cursor: move;' v-show="column.property === 'date'" ><Rank /></el-icon>
          <span style="margin-left: 10px">{{ row[column.property] }}</span>
        </div>
      </template>
    </el-table-column>
  </el-table>
  <!-- 弹框表格 -->
</template>
<script setup lang="ts">
  import { onMounted, ref } from 'vue';
  import Sortable from 'sortablejs';

  const changeCol = ref([
    {
      label: '日期',
      prop: 'date',
      state: 'California',
    },
    {
      label: '姓名',
      prop: 'name',
      state: 'California',
    },
    {
      label: '地址',
      prop: 'address',
      state: 'California',
    },
  ]);

  const tableData = ref([
    {
      date: '2023-04-28',
      name: 'aaa',
      address: '长沙市雨花区',
      done: false,
    },
    {
      date: '2023-04-29',
      name: 'bbb',
      address: '长沙市岳麓区',
      done: false,
    },
    {
      date: '2023-04-30',
      name: 'ccc',
      address: '长沙市天心区',
      done: false,
    },
    {
      date: '2023-05-01',
      name: 'ddd',
      address: '长沙市芙蓉区',
      done: false,
    },
    {
      date: '2023-05-01',
      name: 'ddd',
      address: '长沙市芙蓉区',
      done: false,
    },
    {
      date: '2023-05-01',
      name: 'ddd',
      address: '长沙市芙蓉区',
      done: false,
    },
    {
      date: '2023-05-01',
      name: 'ddd',
      address: '长沙市芙蓉区',
      done: false,
    },
    {
      date: '2023-05-01',
      name: 'ddd',
      address: '长沙市芙蓉区',
      done: false,
    },
    {
      date: '2023-05-01',
      name: 'ddd',
      address: '长沙市芙蓉区',
      done: false,
    },
    {
      date: '2023-05-01',
      name: 'ddd',
      address: '长沙市芙蓉区',
      done: false,
    },
    {
      date: '2023-05-01',
      name: 'ddd',
      address: '长沙市芙蓉区',
      done: false,
    },
  ]);
  const dropCol = ref([
    {
      label: '日期',
      prop: 'date',
    },
    {
      label: '姓名',
      prop: 'name',
    },
    {
      label: '地址',
      prop: 'address',
    },
  ]);

  // 行拖拽
  const rowDrop = () => {
    const tbody = document.querySelector('tbody');
    Sortable.create(tbody, {
      group: {
        name: 'words',
        pull: true,
        put: true,
      },
      handle:'.handle',//设置可拖拽的
      animation: 300, //动画参数,
      onEnd({ newIndex, oldIndex }) {
        const currRow = tableData.value.splice(oldIndex, 1)[0];
        tableData.value.splice(newIndex, 0, currRow);
      },
    });
  };

  // 列拖拽
  const columnDrop = () => {
    const wrapperTr = document.querySelector('tr');
    Sortable.create(wrapperTr, {
      animation: 180,
      delay: 0,
      onEnd: (evt) => {
        setTimeout(() => {
          const oldItem = dropCol.value[evt.oldIndex];
          dropCol.value.splice(evt.oldIndex, 1);
          dropCol.value.splice(evt.newIndex, 0, oldItem);
        }, 5);
      },
    });
  };
  // 弹框显示隐藏

  // 初始化
  onMounted(() => {
    rowDrop();
    columnDrop();
  });
</script>
常用配置
  //一个网页存在多个分组时设置,组名相同的组之间元素可以相互拖拽
  group: "name",
  //2种group写法选一种就可以了
  group: { 
    name: 'name',
    pull: 'clone', //克隆元素
  },
  //是否允许元素内部排序,如果为false当有多个排序组时,多个组之间可以拖拽,本身不能拖拽(默认true)
  sort: true,
  //是否禁用拖拽和排序
  disabled: false,
  //动画效果持续时间(不设置或0都没有过渡效果)
  animation: 150,
  //点击指定class类的元素才能拖拽(比如点击元素内的图标才能拖拽元素,可以给图标设置my-handle class)
  //class可以定义在元素本身上,也可以定义在子元素上
  handle: ".my-handle",
  // class为ignore的元素不能拖动
  filter: ".ignore",
  //含有item 类的元素可以被拖拽(class只能定义在元素本身上)
  draggable: ".item",
  //指定获取拖动后排序的属性
  dataIdAttr: 'data-id',
  //给停靠位置添加的class(可以给这个class定义样式)
  ghostClass: "ghost",
  //选中元素添加的类(包括悬浮的元素和停靠位置的元素)
  chosenClass: "chosen",
  //拖拽对象移动时添加的类
  dragClass: "drag",
  //禁用html5原生拖拽
  forceFallback: false,
  ...
 
  //克隆事件
  onClone: function (evt) {
      //被克隆的对象(被移到另外地方的那个元素)
      var origEl = evt.item;
      //克隆后的对象(还是在原来位置的元素)
      var cloneEl = evt.clone;
      cloneEl.innerHTML = "clone出的元素";
  },
  ...

三、vuedraggable(推荐)

 vuedeaggable是基于sortable.js二次封封装的一个js库, 二者本质上没啥区别, 一个针对于vue去使用的, 一个可以在其他地方更广泛的去使用,两个都是非常优秀的

1、安装

npm i -S vuedraggable

2、.vue 文件引入

import draggable from "vuedraggable";

components: { draggable },

3、简单的使用

template

<draggable

       v-model="draggableList"

       animation="300"

       :options="{

         sort: true

       }"

       dragClass="pointer"

     >

       <div v-for="(item, index) in draggableList" :key="index">

         <div class="item">

           <i

             v-if="draggableTitle === '保存排序'"

             class="el-icon-rank mr15 pointer"

           ></i>

           {{ item.name }}

         </div>

       </div>

     </draggable>

data

 draggableTitle: "修改排序",

     draggableList: [

       { id: 1, name: "11" },

       { id: 2, name: "22" },

       { id: 3, name: "33" },

       { id: 4, name: "44" },

       { id: 5, name: "55" }

     ],

methods

draggableClick() {

     if (this.draggableTitle === "修改排序") {

       this.draggableTitle = "保存排序";

     } else {

       this.$message.success("保存成功");

       this.draggableTitle = "修改排序";

     }

   },

总结

如果是 element、 ant 组件里的表格 拖拽排序 建议用Sortable, 其他简单的列表排序用vuedraggable

vuedraggable 使用是一个标签 包含你所要拖拽的内容list, 而element ant 这种ui框架,对 表格进行了封装,

vuedraggable无法准确的去包含到表格行(及tbody里的tr td)数据, 而Sortable是直接绑定一个dom,可以根据dom去绑定

如果这两种方法都无法实现想要的效果那就再使用html原生方法吧

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值