Skip to content

外部拖拽

该示例演示了从栅格外部拖拽部件到栅格中。

将部件放到栅格中后,您将获得其坐标/属性,并可以据此执行操作。

你也可以通过外部设置拖入时的部件大小。

<template>
  <div class="container">
    <DisplayLayout :layout="state.layout" />

    <div
      class="droppable-element"
      draggable="true"
      unselectable="on"
      @drag="drag"
      @dragend="dragend"
    >
      Droppable Element (Drag me!)
    </div>

    <div id="content">
      <b-grid-layout
        :ref="setLayoutRef"
        v-model:layout="state.layout"
        :col-num="12"
        :row-height="30"
        :is-draggable="true"
        :is-resizable="true"
        :vertical-compact="true"
        :use-css-transforms="true"
      >
        <b-grid-item
          v-for="item in state.layout"
          :key="item.i"
          :ref="e => setItemRef(item, e)"
          :x="item.x"
          :y="item.y"
          :w="item.w"
          :h="item.h"
          :i="item.i"
        >
          <div class="item-box">
            <span class="text">{{ item.i }}</span>
          </div>
        </b-grid-item>
      </b-grid-layout>
    </div>
  </div>
</template>

<script setup>
import { reactive, nextTick } from 'vue'

const state = reactive({
  layout: [
    { x: 0, y: 0, w: 2, h: 2, i: '0' },
    { x: 2, y: 0, w: 2, h: 4, i: '1' },
    { x: 4, y: 0, w: 2, h: 5, i: '2' },
    { x: 6, y: 0, w: 2, h: 3, i: '3' },
    { x: 8, y: 0, w: 2, h: 3, i: '4' },
    { x: 10, y: 0, w: 2, h: 3, i: '5' },
    { x: 0, y: 5, w: 2, h: 5, i: '6' },
    { x: 2, y: 5, w: 2, h: 5, i: '7' },
    { x: 4, y: 5, w: 2, h: 5, i: '8' },
    { x: 5, y: 10, w: 4, h: 3, i: '9' }
  ],
  colNum: 12,
  layoutRef: {},
  itemRefs: {},
  dragW: 2,
  dragH: 2
})

const mouseXY = { x: null, y: null }
const DragPos = { x: null, y: null, w: 2, h: 2, i: null }

document.addEventListener(
  'dragover',
  e => {
    mouseXY.x = e.clientX
    mouseXY.y = e.clientY
  },
  false
)

function setItemRef(item, e) {
  state.itemRefs[item.i] = e
}

function setLayoutRef(e) {
  state.layoutRef = e
}

async function drag() {
  const parentRect = document.getElementById('content').getBoundingClientRect()
  let mouseInGrid = false
  if (
    mouseXY.x > parentRect.left &&
    mouseXY.x < parentRect.right &&
    mouseXY.y > parentRect.top &&
    mouseXY.y < parentRect.bottom
  ) {
    mouseInGrid = true
  }
  if (mouseInGrid === true && state.layout.findIndex(item => item.i === 'drop') === -1) {
    state.layout.push({
      x: (state.layout.length * 2) % (state.colNum || 12),
      y: state.layout.length + (state.colNum || 12), // puts it at the bottom
      w: state.dragW,
      h: state.dragH,
      i: 'drop'
    })
    await nextTick()
  }
  if (!state.itemRefs?.drop) {
    return
  }
  const index = state.layout.findIndex(item => item.i === 'drop')
  if (index !== -1) {
    if (state.itemRefs?.drop?.el?.style) {
      state.itemRefs.drop.el.style.display = 'none'
    }
    const itemRef = state.itemRefs.drop
    const new_pos = itemRef.calcXY(mouseXY.y - parentRect.top, mouseXY.x - parentRect.left)
    if (mouseInGrid === true) {
      state.layoutRef.emitter.emit('dragEvent', [
        'dragstart',
        'drop',
        new_pos.x,
        new_pos.y,
        state.dragW,
        state.dragH
      ])
      DragPos.i = String(index)
      DragPos.x = state.layout[index].x
      DragPos.y = state.layout[index].y
    }
    if (mouseInGrid === false) {
      state.layoutRef.emitter.emit('dragEvent', [
        'dragend',
        'drop',
        new_pos.x,
        new_pos.y,
        state.dragW,
        state.dragH
      ])
      state.layout = state.layout.filter(obj => obj.i !== 'drop')
      await nextTick()
    }
  }
}

async function dragend() {
  const parentRect = document.getElementById('content').getBoundingClientRect()
  let mouseInGrid = false
  if (
    mouseXY.x > parentRect.left &&
    mouseXY.x < parentRect.right &&
    mouseXY.y > parentRect.top &&
    mouseXY.y < parentRect.bottom
  ) {
    mouseInGrid = true
  }
  if (mouseInGrid === true) {
    state.layoutRef.emitter.emit('dragEvent', [
      'dragend',
      'drop',
      DragPos.x,
      DragPos.y,
      state.dragW,
      state.dragH
    ])
    state.layout = state.layout.filter(obj => obj.i !== 'drop')
    state.layout.push({
      x: DragPos.x,
      y: DragPos.y,
      w: state.dragW,
      h: state.dragH,
      i: DragPos.i
    })
    await nextTick()
    state.layoutRef.emitter.emit('dragEvent', [
      'dragend',
      DragPos.i,
      DragPos.x,
      DragPos.y,
      state.dragW,
      state.dragH
    ])
  }
}
</script>

<style scoped>
.container {
  .item-box {
    padding: 8px;
    .text {
      font-size: 18px;
      text-align: center;
      margin: auto;
      height: 100%;
      width: 100%;
    }
  }
  .droppable-element {
    width: 170px;
    text-align: center;
    background: #fdd;
    border: 1px dashed rgb(250, 155, 155);
    box-shadow: rgba(149, 157, 165, 0.2);
    margin: 10px 0;
    padding: 10px;
    border-radius: 6px;
    cursor: grab;
  }
}
</style>