Guoguo-notes
主页
常用笔记
vue笔记及周边生态
  • 团队协作及规范
  • 项目框架及架构
  • 飞码篇
  • Java
  • React笔记
GitHub
主页
常用笔记
vue笔记及周边生态
  • 团队协作及规范
  • 项目框架及架构
  • 飞码篇
  • Java
  • React笔记
GitHub

拖拽组件

目前样子

image-20250121154424094

未来可拓展

image-20250121154508293

父组件

<template>
  <div class="custom-drag">
    <div class="custom-drag-left">
      <div class="custom-drag-left-item" v-for="item in theComp" @click="onClickComp(item)">
        {{ item.label }}
      </div>
    </div>
    <iframe
      src="http://localhost:8991"
      @load="sendMessage"
      style="width: 50%; height: 100%"
    ></iframe>
    <div src="http://localhost:8991" style="width: 25%; height: 100%">
      background-color: skyblue
    </div>
  </div>
</template>

<script lang="ts" setup>
import { ref, unref } from 'vue'
import { nanoid } from 'nanoid'
const theComp = ref([
  { label: '图片', type: 'picture' },
  { label: '标题', type: 'title' },
  { label: '单选', type: 'radio' }
])
const theSendData: any = ref([])

const onClickComp = (item: any) => {
  const theItem = {
    ...item,
    id: nanoid()
  }
  theSendData.value.push(theItem)
  sendMessage()
}
const sendMessage = () => {
  const iframe: any = document.querySelector('iframe')
  iframe.contentWindow.postMessage(JSON.stringify(theSendData.value), 'http://localhost:8991')
}

window.addEventListener('message', (event) => {
  if (event.origin !== 'http://localhost:8991') return // 验证来源
  const theGetData = JSON.parse(event.data)
  console.log('父组件--Received message from iframe:', theGetData)
  if (theGetData.type == 'data') {
    theSendData.value = theGetData.data
    console.log('theSendData.value ===>', theSendData.value)
  }
})
</script>
<style lang="less">
.custom-drag {
  height: 80vh;
  display: flex;
  align-items: center;
  justify-content: space-between;
  &-left {
    width: 25%;
    height: 100%;
    gap: 24px;
    display: flex;
    flex-wrap: wrap;
    align-content: flex-start;
    &-item {
      width: 50px;
      height: 50px;
      background-color: #eee;
      display: flex;
      align-items: center;
      justify-content: center;
    }
  }
}
</style>

子组件

<!-- @format -->

<template>
  <div>
    <!--使用draggable组件-->
    <VueDraggableNext
      v-model="theData"
      chosenClass="chosen"
      forceFallback="true"
      group="people"
      animation="1000"
      @start="onStart"
      @end="onEnd"
      @choose="onChoose"
    >
      <transition-group>
        <div
          :id="element.id"
          class="item"
          :class="{
            'item-selected': element.id == theCurrentItemId,
          }"
          v-for="element in theData"
          :key="element.id"
          @mouseenter="onMouseenter(element)"
        >
          <!-- <Input v-if="element.type == 'title'" v-model="element.value" /> -->
          <el-input v-if="element.type == 'title'" v-model="element.value">
          </el-input>
          <div v-else>{{ element.label }}</div>

          <!-- 删除按钮 -->
          <div class="item-tip" v-if="element.id == theCurrentItemId">
            <div>{{ element.label }}</div>
            <div
              style="margin-left: 10px; color: red; cursor: pointer"
              @click="onRemoveItem(element)"
            >
              删除
            </div>
          </div>
        </div>
      </transition-group>
    </VueDraggableNext>

    {{ theData }}
  </div>
</template>

<script setup lang="ts">
  // import Draggable from 'vuedraggable'
  import { VueDraggableNext } from 'vue-draggable-next'
  import { ref } from 'vue'
  import Input from './comp/Input.vue'

  const theCurrentItemId: any = ref('')
  const onChoose = (e) => {
    theCurrentItemId.value = e.item.getAttribute('id')
  }
  const onStart = (e) => {
    console.log('start')
  }
  const onEnd = (e) => {
    console.log('end')
  }

const onMouseenter = (e) => {
    console.log('mouseenter',e)
    e.target.style.backgroundColor = 'red'
  }

  const onRemoveItem = (item: any) => {
    theData.value = theData.value.filter((item) => {
      return item.id !== theCurrentItemId.value
    })
    const theMessage: any = {
      type: 'data',
      data: theData.value,
    }
    window.parent.postMessage(
      JSON.stringify(theMessage),
      'http://localhost:4000',
    )
  }

  const theData: any = ref([])
  window.addEventListener('message', (event: any) => {
    if (event.origin !== 'http://localhost:4000') return // 验证来源
    // console.log('子组件--自己的消息:', event, event.data)
    theData.value = JSON.parse(event.data)
    // 回复消息
    const theMessage: any = {
      type: 'message',
      data: '子组件--给父组件发消息',
    }
    event.source.postMessage(JSON.stringify(theMessage), event.origin)
  })
</script>

<style lang="less">
  /*被拖拽对象的样式*/
  .item {
    padding: 6px;
    background-color: #fdfdfd;
    border: solid 1px #eee;
    margin-bottom: 10px;
    cursor: move;
    user-select: none;
    position: relative;
    width: 70%;
    &-selected {
      border: solid 2px red !important;
    }
    &-tip {
      position: absolute;
      right: -100px;
      top: 6px;
      display: flex;
      align-items: center;
      justify-content: center;
      background-color: skyblue;
      border-radius: 6px;
      padding: 4px 8px;
      cursor: auto;
    }
  }
  .item:hover {
    border: solid 2px red !important;
  }
  /*选中样式*/
  .chosen {
    border: solid 2px #3089dc !important;
  }
</style>

Edit this page
Last Updated:
Contributors: 袁果锅