sortablejs + vue2 + ElementUi 实现拖拽排序

SortableJS 是一个JavaScript库,用于创建具有拖放排序功能的交互式列表和表格。它允许您通过拖放方式重新排列HTML元素,例如列表或表格行,以自定义其顺序。SortableJS支持多种拖放手势和交互,包括触摸屏操作和鼠标拖放。除此之外,SortableJS还提供了许多选项和事件供您自定义和扩展其功能。它的主要作用是使网页更加交互和用户友好。

一、安装

npm i sortablejs -S

二、常用配置

const config = {
  //一个网页存在多个分组时设置,组名相同的组之间元素可以相互拖拽
  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出的元素";
  },
  ...
}

三、案例

1.  通过 blur 和 focus 事件,对类名进行添加和删除。使用 Sortable 里的 handle 参数对拖拽功能进行使用和取消。

2. 使用 uuid 设置唯一值,保证key不会重复...

3. 页面效果  ↓↓↓↓↓

<template>
  <div>
    <!-- 拖拽排序 -->
    <!-- 
      1. element ui + sortablejs + vue2 + uuid
      2. 实现拖拽排序
      3. 在el-input 内实现,可复制
     -->
    <div class="p-10">
      <div class="title-btn-wrapper flex-end-center">
        <div class="add-delete-warp">
          <el-button type="primary" size="small" @click="addTable('repairSteps')">新 增</el-button>
          <el-button type="danger" size="small" @click.stop="deleteSelection('repairSteps')">删 除</el-button>
        </div>
      </div>
      <!-- 表单 -->
      <div class="draggable">
        <el-table :data="repairList" max-height="600" style="width: 100%"
                  border :header-cell-class-name="formHeadStarClass" row-key="uniqueId"
                  :cell-class-name="notSort"
                  @selection-change="handleSelectionChange($event,'repairSteps')">
          <el-table-column type="selection" width="50" align="center"></el-table-column>
          <el-table-column type="index" label="序号" align="center" width="50"></el-table-column>
          <el-table-column prop="step" align="center" label="步骤顺序" width="140">
            <template slot-scope="scope">
              <el-input type="text" :disabled="false" placeholder="请输入步骤顺序" size="mini"
                        v-model="scope.row.step"
                        @blur="handleInputBlur" @focus="handleInputFocus"></el-input>
            </template>
          </el-table-column>
          <el-table-column prop="content" align="center" label="步骤内容">
            <template slot-scope="scope">
              <el-input type="text" :disabled="false" placeholder="请输入步骤内容" size="mini"
                        v-model="scope.row.content"
                        @blur="handleInputBlur" @focus="handleInputFocus"></el-input>
            </template>
          </el-table-column>
          <el-table-column prop="num" align="center" label="数量" width="140">
            <template slot-scope="scope">
              <el-input type="number" placeholder="请输入数量" :min="0"
                        size="mini" v-model="scope.row.num" @input="shipmentQuantityInput($event, scope.row, 'num')"
                        ></el-input>
            </template>
          </el-table-column>
          <el-table-column prop="remark" align="center" label="备注" width="260">
            <template slot-scope="scope">
              <el-input type="text" :disabled="false" placeholder="请输入备注" size="mini"
                        v-model="scope.row.remark"
                        @blur="handleInputBlur" @focus="handleInputFocus"></el-input>
            </template>
          </el-table-column>
        </el-table>
      </div>
    </div>
  </div>
</template>

<script>
import { v4 as uuid4 } from 'uuid'; // 通用唯一识别码
import Sortable from 'sortablejs';

export default {
components: {},
  props: {},
  data() {
    return {
      contentUnit: [],
      notSort: 'my-handle',
      repairList: [
        {
          step: '',
          content: '',
          unit: '',
          num: '',
          id: '',
          remark: '',
          uniqueId: '',
          repairId: '',
          ShipId: '',
        },
      ],
    }
  },
  computed: {},
  watch: {},
  created() {
    this.repairList.forEach((item) => {
      item.uniqueId = this.getUniqueId();
    });
  },
  mounted(){
    this.rowDrop() // 初始化拖拽
  },
  methods: {
     getUniqueId() { // 获取唯一值
      const reg = new RegExp('-', 'g'); // 正则表达式 /-/g
      return uuid4().replace(reg, ''); // 获取唯一标识,并去除 -
    },
    handleInputFocus() { // 失去焦点时,进行清除
      this.notSort = '';
    },
    handleInputBlur() { // 获取焦点时,进行生成
      this.notSort = 'my-handle';
    },
    rowDrop() { // 拖拽功能
      const tbody = document.querySelector('.draggable tbody');
      const that = this;
      Sortable.create(tbody, {
        animation: 150,
        handle: '.my-handle',
        filter: '.not-sort',
        draggable: '.draggable .el-table__row',
        onEnd({ newIndex, oldIndex }) {
          // 1.将要排序的数据 拷贝一份
          const list = that.repairList.slice();
          // 2. 通过拷贝的数据 进行排序
          const currRow = list.splice(oldIndex, 1)[0];
          list.splice(newIndex, 0, currRow);
          list.forEach((item, index) => {
            item.sequence = index + 1;
          });
          // 3.将源数据清空
          that.repairList = [];
          // 4.通过 $nextTick 的方式从新更新视图
          that.$nextTick(() => {
            that.repairList = list;
          });
        },
      });
    },
    addTable(action) { // 新增
      if (action === 'repairSteps') { // 步骤顺序
        this.repairList.push({
          repairId: '',
          ShipId: '',
          uniqueId: this.getUniqueId(),
          id: '',
        });
      }
    },
    deleteSelection(action) { // 删除
      const newShowArr = [];
      if (action === 'repairSteps') { // 步骤顺序
        const deleteList = this[action].map((i) => i.uniqueId);
        this.repairList.forEach((item) => {
          if (!deleteList.includes(item.uniqueId)) {
            newShowArr.push(item);
          }
        });
      }
      this.repairList = newShowArr;
    },
    formHeadStarClass({ column }) { // 添加 * 必填标识
      const requiredLabels = ['数量', '步骤内容', '步骤顺序', '单位'];
      return requiredLabels.includes(column.label) ? 'star-row' : '';
    },
    shipmentQuantityInput(event, row, field) { // 步骤的数量
      row.num = parseInt(event.replace(/[^0-9]/g, 0), 10);
    },
    handleSelectionChange(val, action) { // 选中的数据
      if (val.length) {
        this[action] = val;
      } else {
        this[action] = [];
      }
    },
  },

}
</script>

<style lang="scss" scoped>
.p-10 {
  // padding: 10px 10px 100px;
  width: 900px;
  margin-left: 20px;
}
.title-btn-wrapper {
  margin: 10px 0;
}
.flex-end-center {
    display: flex;
    justify-content: flex-end;
    align-items: center;
}
.add-delete-warp {
  margin-bottom: 10px;
}
</style>

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值