Skip to content

ZDrag

通用拖拽组件,对大部分浏览器支持的同时也支持触屏设备,对组件样式和内容没有约束,可以自由的发挥创意。

基础用法

绑定一个 data 即可实现拖拽。

<template>
  <z-drag class="dragGroup" v-model:data="data"></z-drag>
</template>

<script setup lang="ts">

import type { IDragData } from "@d7ee/z-base-ui/dist/types/components/ZDrag";
import { ref } from "vue";


const data = ref<IDragData[]>([
  { id: "1", name: "语文" },
  { id: "2", name: "数学" },
  { id: "3", name: "体育" },
  { id: "4", name: "英语" },
  { id: "5", name: "美术" },
  { id: "6", name: "思想教育" },
])
</script>

<style lang="less" scoped>
.dragGroup {
  display: flex;
  flex-wrap: wrap;
  grid-gap: 12px;
}
.dragGroup .z-dragGroup_name {
  cursor: pointer;
}
</style>

自定义

使用 item 插槽进行内容自定义

<template>
  <z-drag class="dragGroup" v-model:data="data">
    <template #item="{record}">
      <div class="dragGroup-item">
        <span class="icon">😊</span>
        <span>{{record.name}}</span>
      </div>
    </template>
  </z-drag>
</template>

<script setup lang="ts">
import type { IDragData } from "@d7ee/z-base-ui/dist/types/components/ZDrag";
import { ref } from "vue";


const data = ref<IDragData[]>([
  { id: "1", name: "语文" },
  { id: "2", name: "数学" },
  { id: "3", name: "体育" },
  { id: "4", name: "英语" },
  { id: "5", name: "美术" },
  { id: "6", name: "思想教育" },
])
</script>

<style lang="less" scoped>
.dragGroup {
  display: grid;
  grid-gap: 12px;
  grid-template-columns: repeat(4, 1fr);
}
.dragGroup-item {
  cursor: pointer;
  display: flex;
  align-items: center;
  border: 1px solid #c6e2ff;
  padding: 3px 6px;
  border-radius: 12px;
  background-color: #909399;
  color: #ffffff;
  .icon {
    margin-right: 4px;
  }
}
</style>

多列表相互拖拽

使用 name 给每个组设置一个ID,列表可以是一个也可以是N个。

<template>
  <z-drag class="dragGroup" name="groupA" v-model:data="groupA">
    <template #item="{record}">
      <div class="dragGroup-item">
        <span class="icon">😊</span>
        <span>{{record.name}}</span>
      </div>
    </template>
  </z-drag>

  <z-drag class="dragGroup" name="groupB" v-model:data="groupB">
    <template #item="{record}">
      <div class="dragGroup-item">
        <span class="icon">🎈</span>
        <span>{{record.name}}</span>
      </div>
    </template>
  </z-drag>

  <z-drag class="dragGroup" name="groupC" v-model:data="groupC">
    <template #item="{record}">
      <div class="dragGroup-item">
        <span class="icon">😂</span>
        <span>{{record.name}}</span>
      </div>
    </template>
  </z-drag>
</template>

<script setup lang="ts">

import type { IDragData } from "@d7ee/z-base-ui/dist/types/components/ZDrag";
import { ref } from "vue";


const groupA = ref<IDragData[]>([
  { id: "1", name: "a-1" },
  { id: "2", name: "a-2" },
  { id: "3", name: "a-3" },
  { id: "4", name: "a-4" },
  { id: "5", name: "a-5" },
])


const groupB = ref<IDragData[]>([
  { id: "b1", name: "b-1" },
  { id: "b2", name: "b-2" },
  { id: "b3", name: "b-3" },
])

const groupC = ref<IDragData[]>([
  { id: "c1", name: "c-1" },
  { id: "c2", name: "c-2" },
])
</script>

<style lang="less" scoped>
.dragGroup {
  display: grid;
  grid-gap: 12px;
  grid-template-columns: repeat(4, 1fr);
  padding: 12px;
  margin-bottom: 24px;
  border: 1px solid #c6e2ff;
}
.dragGroup-item {
  cursor: pointer;
  display: flex;
  align-items: center;
  border: 1px solid #c6e2ff;
  padding: 3px 6px;
  border-radius: 12px;
  background-color: #909399;
  color: #ffffff;
  .icon {
    margin-right: 4px;
  }
}
</style>

分组拖拽

put使用put,分组模式下,只能在对应组内进行数据交换
在下面的demo中,A组数据能和B、D就行交换,而B组只能和A组就行交换,C组能和D组交换,而D组能和A、C交换

<template>
  <z-drag class="dragGroup" name="groupA" v-model:data="groupA" :put="['groupB', 'groupD']">
    <template #item="{record}">
      <div class="dragGroup-item">
        <span>{{record.name}}</span>
      </div>
    </template>
  </z-drag>

  <z-drag class="dragGroup" name="groupB" v-model:data="groupB" :put="['groupA']">
    <template #item="{record}">
      <div class="dragGroup-item">
        <span>{{record.name}}</span>
      </div>
    </template>
  </z-drag>

  <z-drag class="dragGroup" name="groupC" v-model:data="groupC" :put="['groupD']">
    <template #item="{record}">
      <div class="dragGroup-item">
        <span>{{record.name}}</span>
      </div>
    </template>
  </z-drag>

  <z-drag class="dragGroup" name="groupD" v-model:data="groupD" :put="['groupC', 'groupA']">
    <template #item="{record}">
      <div class="dragGroup-item">
        <span>{{record.name}}</span>
      </div>
    </template>
  </z-drag>
</template>

<script setup lang="ts">

import type { IDragData } from "@d7ee/z-base-ui/dist/types/components/ZDrag";
import { ref } from "vue";


const groupA = ref<IDragData[]>([
  { id: "1", name: "a-1" },
  { id: "2", name: "a-2" },
  { id: "3", name: "a-3" },
  { id: "4", name: "a-4" },
  { id: "5", name: "a-5" },
])


const groupB = ref<IDragData[]>([
  { id: "b1", name: "b-1" },
  { id: "b2", name: "b-2" },
  { id: "b3", name: "b-3" },
])

const groupC = ref<IDragData[]>([
  { id: "c1", name: "c-1" },
  { id: "c2", name: "c-2" },
])

const groupD = ref<IDragData[]>([
  { id: "d1", name: "d-1" },
  { id: "d2", name: "d-2" },
])
</script>

<style lang="less" scoped>
.dragGroup {
  display: grid;
  grid-gap: 12px;
  grid-template-columns: repeat(5, 1fr);
  padding: 12px;
  margin-bottom: 24px;
  border: 1px solid #c6e2ff;
}
.dragGroup-item {
  cursor: pointer;
  display: flex;
  align-items: center;
  border: 1px solid #c6e2ff;
  padding: 3px 6px;
  border-radius: 12px;
  background-color: #909399;
  color: #ffffff;
  .icon {
    margin-right: 4px;
  }
}
</style>

禁用

使用 disabled 开启禁用模式,disabled 可以针对某项或整体,权重:整体禁用 > 单个禁用
在下面demo中,a组的1和4无法被拖拽,B组整体被禁止拖拽

<template>
  <z-drag class="dragGroup" name="groupA" v-model:data="groupA">
    <template #item="{record}">
      <div class="dragGroup-item">
        <span class="icon">😊</span>
        <span>{{record.name}}</span>
      </div>
    </template>
  </z-drag>

  <z-drag class="dragGroup" name="groupB" disabled v-model:data="groupB">
    <template #item="{record}">
      <div class="dragGroup-item">
        <span class="icon">🎈</span>
        <span>{{record.name}}</span>
      </div>
    </template>
  </z-drag>

  <z-drag class="dragGroup" name="groupC" v-model:data="groupC">
    <template #item="{record}">
      <div class="dragGroup-item">
        <span class="icon">😂</span>
        <span>{{record.name}}</span>
      </div>
    </template>
  </z-drag>
</template>

<script setup lang="ts">

import type { IDragData } from "@d7ee/z-base-ui/dist/types/components/ZDrag";
import { ref } from "vue";


const groupA = ref<IDragData[]>([
  { id: "1", name: "a-1", disabled: true },
  { id: "2", name: "a-2" },
  { id: "3", name: "a-3" },
  { id: "4", name: "a-4", disabled: true },
  { id: "5", name: "a-5" },
])


const groupB = ref<IDragData[]>([
  { id: "b1", name: "b-1" },
  { id: "b2", name: "b-2" },
  { id: "b3", name: "b-3" },
])

const groupC = ref<IDragData[]>([
  { id: "c1", name: "c-1" },
  { id: "c2", name: "c-2" },
])
</script>

<style lang="less" scoped>
.dragGroup {
  display: grid;
  grid-gap: 12px;
  grid-template-columns: repeat(4, 1fr);
  padding: 12px;
  margin-bottom: 24px;
  border: 1px solid #c6e2ff;
}
.dragGroup-item {
  cursor: pointer;
  display: flex;
  align-items: center;
  border: 1px solid #c6e2ff;
  padding: 3px 6px;
  border-radius: 12px;
  background-color: #909399;
  color: #ffffff;
  .icon {
    margin-right: 4px;
  }
}
</style>

自定义样式

使用 drag-name 自定义一个类名, 通过对class类名的样式覆盖从而实现修改元素被选中后发生拖拽时的样式,您可拖拽下方的元素体验该效果。

<template>
  <z-drag class="dragGroup" v-model:data="data" drag-name="customStyle">
    <template #item="{record}">
      <div class="dragGroup-item">
        <span class="icon">😊</span>
        <span>{{record.name}}</span>
      </div>
    </template>
  </z-drag>
</template>

<script setup lang="ts">
import type { IDragData } from "@d7ee/z-base-ui/dist/types/components/ZDrag";
import { ref } from "vue";


const data = ref<IDragData[]>([
  { id: "1", name: "语文" },
  { id: "2", name: "数学" },
  { id: "3", name: "体育" },
  { id: "4", name: "英语" },
  { id: "5", name: "美术" },
  { id: "6", name: "思想教育" },
]);
</script>

<style lang="less" scoped>
.dragGroup {
  display: grid;
  grid-gap: 12px;
  grid-template-columns: repeat(4, 1fr);
  .customStyle {
    .dragGroup-item {
      background: linear-gradient(45deg, #b697e8 50%, #6a23df 50%);
    }
  }
}

.dragGroup-item {
  cursor: pointer;
  display: flex;
  align-items: center;
  border: 1px solid #c6e2ff;
  padding: 3px 6px;
  border-radius: 12px;
  background-color: #909399;
  color: #ffffff;
  .icon {
    margin-right: 4px;
  }
}
</style>

自定义句柄

使用 handle 定义句柄类名,只有具备该类名的元素才能进行拖动,在下面的demo中,只有鼠标移至😊上才能进行拖动。

<template>
  <z-drag class="dragGroup" v-model:data="data" handle="handle" :unselectable="true">
    <template #item="{record}">
      <div class="dragGroup-item">
        <span class="icon handle">😊</span>
        <span>{{record.name}}</span>
      </div>
    </template>
  </z-drag>
</template>

<script setup lang="ts">
import type { IDragData } from "@d7ee/z-base-ui/dist/types/components/ZDrag";
import { ref } from "vue";


const data = ref<IDragData[]>([
  { id: "1", name: "语文" },
  { id: "2", name: "数学" },
  { id: "3", name: "体育" },
  { id: "4", name: "英语" },
  { id: "5", name: "美术" },
  { id: "6", name: "思想教育" },
]);
</script>

<style lang="less" scoped>
.dragGroup {
  display: grid;
  grid-gap: 12px;
  grid-template-columns: repeat(4, 1fr);
  .customStyle {
    .dragGroup-item {
      background: linear-gradient(45deg, #b697e8 50%, #6a23df 50%);
    }
  }
}

.dragGroup-item {
  cursor: pointer;
  display: flex;
  align-items: center;
  border: 1px solid #c6e2ff;
  padding: 3px 6px;
  border-radius: 12px;
  background-color: #909399;
  color: #ffffff;
  .icon {
    margin-right: 4px;
  }
}
</style>

严格模式

使用 strict 开启严格模式,严格模式是为了防止拖拽项乱串而设计的一个开关,在严格模式下数据从哪来就只能回哪去。
在下面的demo中,数据只能从A,B,C,移动到D,而D内的数据只能原路返回,A组的数据无法被拖到B,C中。

WARNING

注意:该功能仍处于开发和测试中,目前只是一个先行版,存在一个较为致命的缺陷,不能对D组设置默认值,设置默认值后在拖动D组原数据时会出现一些意外的状况。

<template>
  <z-drag class="dragGroup" name="groupA" v-model:data="groupA" :put="['groupD']" strict>
    <template #item="{record}">
      <div class="dragGroup-item">
        <span>{{record.name}}</span>
      </div>
    </template>
  </z-drag>

  <z-drag class="dragGroup" name="groupB" v-model:data="groupB" :put="['groupD']" strict>
    <template #item="{record}">
      <div class="dragGroup-item">
        <span>{{record.name}}</span>
      </div>
    </template>
  </z-drag>

  <z-drag class="dragGroup" name="groupC" v-model:data="groupC" :put="['groupD']" strict>
    <template #item="{record}">
      <div class="dragGroup-item">
        <span>{{record.name}}</span>
      </div>
    </template>
  </z-drag>

  <z-drag class="dragGroup" name="groupD" v-model:data="groupD" :put="['groupC', 'groupB', 'groupA']" strict>
    <template #item="{record}">
      <div class="dragGroup-item">
        <span>{{record.name}}</span>
      </div>
    </template>
  </z-drag>
</template>

<script setup lang="ts">

import type { IDragData } from "@d7ee/z-base-ui/dist/types/components/ZDrag";
import { ref } from "vue";


const groupA = ref<IDragData[]>([
  { id: "1", name: "a-1" },
  { id: "2", name: "a-2" },
  { id: "3", name: "a-3" },
  { id: "4", name: "a-4" },
  { id: "5", name: "a-5" },
])


const groupB = ref<IDragData[]>([
  { id: "b1", name: "b-1" },
  { id: "b2", name: "b-2" },
  { id: "b3", name: "b-3" },
])

const groupC = ref<IDragData[]>([
  { id: "c1", name: "c-1" },
  { id: "c2", name: "c-2" },
])

const groupD = ref<IDragData[]>([
  { id: "d1", name: "d-1", disabled: true },
])
</script>

<style lang="less" scoped>
.dragGroup {
  display: grid;
  grid-gap: 12px;
  grid-template-columns: repeat(5, 1fr);
  padding: 12px;
  margin-bottom: 24px;
  border: 1px solid #c6e2ff;
}
.dragGroup-item {
  cursor: pointer;
  display: flex;
  align-items: center;
  border: 1px solid #c6e2ff;
  padding: 3px 6px;
  border-radius: 12px;
  background-color: #909399;
  color: #ffffff;
  .icon {
    margin-right: 4px;
  }
}
</style>

API 属性

属性名说明类型默认值
data数据源,通过v-model绑定,数据格式详见IDragDataIDragData[][]
name组名称类似于唯一ID,需要多列表互相拖拽时需设置该值,详见多列表互相拖拽string
sort禁用排序,禁用后当前组无法拖拽排序booleanfalse
put是否允许其它元素被拖拽进当前组,为true时允许其它组拖拽元素进入,false则禁止,string[]时可约束那些组能将元素拖拽到当前元素,string来源于name,详见分组拖拽boolean | string[]true
pull移出设置,true时允许当前组内元素被拖拽到其它组,false时则禁止移出booleantrue
strict严格模式,约束数据类型,从哪来就只能回哪去,详见严格模式booleanfalse
disabled禁用,禁用后当前组内的元素无法被拖拽,详见禁用booleanfalse
dragName元素被拖拽时的类名,详见自定义样式string
handle句柄,定义可被拖拽的元素类名,详见自定义句柄string
unselectable禁止用户选中内容,详见自定义句柄booleanfalse

slot 插槽

通过插槽可接收一个record参数,参数类型详见类型声明

slotName说明参数
item用于自定义内容{record: IDragData}
Vue
<z-drag class="dragGroup" v-model:data="data">
    <template #item="{record}">
      自定义内容
    </template>
</z-drag>

event 事件

各事参数类型详见类型声明

事件名称说明回调参数
onStart开始拖拽时回调(event: any) => void
onEnd拖拽结束时回调(event: any) => void
onAdd其它元素移动到当前组后回调(event: IZDragOnAdd) => void
onRemove元素从 当前组移动到其它组后回调(event: IZDragOnRemove) => void
update:data数据发生改变时通过v-mode:data更新绑定的变量

TS类型

IDragData

typescript
interface IDragData {
  // ID
  id: string;
  // 名称
  name: string;
  // 禁用拖拽
  disabled?: boolean;
  // 拓展属性,可自定义其它字段
  expand?: Record<string, any>;
}

IZDragOnAdd

typescript
interface IZDragOnAdd {
  // 数据项
  data: IDragData,
  // 原组所在下标
  formIndex: number,
  // 当前组内所在下标
  toIndex: number
}

IZDragOnRemove

typescript
interface IZDragOnRemove {
  // 数据项
  data: IDragData,
  // 下标
  index: number
}