vue3中使用Vue.Draggable的clone模式及遇到的坑

最近有个需求是从左边一个列表中,拖动列表元素到右边列表中,并且不是移动拖拽,而是复制拖拽,元素在右边列表中可以重复,比如左边是参加某个游戏的队员名单,右边是出场顺序,队员可以重复上场。类似这种效果:

代码:

// 左边的列表
 <div class="left-list">
          <p class="title">队员选择</p>
          <draggable
            class="dragArea"
            :list="data.allPerson"
            :group="{ name: 'person', pull: 'clone', put: false }"
            item-key="id"
          >
            <template #item="{ element, index }">
              <div class="left-person" >
                <span class="item-name">{{ element.name }}</span>
              </div>
            </template>
          </draggable>
        </div>

//  右边的列表
<draggable
            class="dragArea "
            :list="data.allSelectedPerson"
            group="person"
            item-key="id"
          >
            <template #item="{ element, index }">
              <div class="right-item">
                <span>
                  {{ element.name }}
                </span>
                <span class="close-btn" @click="handleDeleteSelected(index)">
                  <icon-close /> </span
              ></div>
            </template>
          </draggable>

我用的是 Vue.Draggable 组件,不得不说这个组件真是太好用了,它的官方地址:vue.draggable github地址

官方演示地址: https://sortablejs.github.io/Vue.Draggable/#/clone 其中我用的就是 clone 模式。

Vue.Draggable 拿来即用,没啥难以上手的,就是我在使用的时候遇到一个问题,就是当右边有重复的数据时,多次上下快速拖动右边的元素排序,会偶发一个bug:右边列表会自动多出一个数据,并且操作不了(没法删除也没法给它排序)。

官方在 github 上的示例代码是:

<template>
  <div class="row">
    <div class="col-3">
      <h3>Draggable 1</h3>
      <draggable
        class="dragArea list-group"
        :list="list1"
        :group="{ name: 'people', pull: 'clone', put: false }"
        @change="log"
      >
        <div
          class="list-group-item"
          v-for="element in list1"
          :key="element.name"
        >
          {{ element.name }}
        </div>
      </draggable>
    </div>

    <div class="col-3">
      <h3>Draggable 2</h3>
      <draggable
        class="dragArea list-group"
        :list="list2"
        group="people"
        @change="log"
      >
        <div
          class="list-group-item"
          v-for="element in list2"
          :key="element.name"
        >
          {{ element.name }}
        </div>
      </draggable>
    </div>

    <rawDisplayer class="col-3" :value="list1" title="List 1" />

    <rawDisplayer class="col-3" :value="list2" title="List 2" />
  </div>
</template>

<script>
import draggable from "@/vuedraggable";
export default {
  name: "clone",
  display: "Clone",
  order: 2,
  components: {
    draggable
  },
  data() {
    return {
      list1: [
        { name: "John", id: 1 },
        { name: "Joao", id: 2 },
        { name: "Jean", id: 3 },
        { name: "Gerard", id: 4 }
      ],
      list2: [
        { name: "Juan", id: 5 },
        { name: "Edgard", id: 6 },
        { name: "Johnson", id: 7 }
      ]
    };
  },
  methods: {
    log: function(evt) {
      window.console.log(evt);
    }
  }
};
</script>
<style scoped></style>

经过一番曲折的找bug原因历程,后来发现是我自己用的不合适,就是在右边表格的 item-key 属性用的是id,本以为id是唯一的,后来出现上面提到那个bug的时候,控制台也报警告说有重的 itemKey 。然后我意识到由于我右边的元素是可以重复的,所以其实用id做 itemKey 也不是唯一的。

后来我用 index 做右边拖拽列表的 itemKey 了,也不知道这样合不合适,但确实是没有刚才那个bug了,因为 index 是不会重复的。

//  右边的列表
<draggable
            class="dragArea "
            :list="data.allSelectedPerson"
            group="person"
            item-key="index"
          >
            <template #item="{ element, index }">
              <div class="right-item">
                <span>
                  {{ element.name }}
                </span>
                <span class="close-btn" @click="handleDeleteSelected(index)">
                  <icon-close /> </span
              ></div>
            </template>
          </draggable>

希望本文对您有所帮助,,也希望大佬不吝赐教!