vue3+ts + element plus 实现拖动效果

实现效果

左右两模块支持左侧内容拖拽到右侧,左侧树支持单独拖动,右侧内容支持单独拖动。当左侧内容拖动到右侧时,内容被覆盖,右侧内容内部拖动时,不进行覆盖 内容更换位置。

index.vue

<template>
  <div class="dragdrop">
    <el-row :gutter="20">
      <el-col :span="6">
        <div :class="['global-layout']">
          <left-tree />
        </div>
      </el-col>
      <el-col :span="18">
        <div :class="['global-layout']">
          <right-content />
        </div>
      </el-col>
    </el-row>
  </div>
</template>

<script lang="ts" setup>
import { ref, onMounted, reactive } from 'vue'
import leftTree from './leftTree.vue'
import rightContent from './rightContent.vue'
</script>

<style scoped lang="scss">
.global-layout {
  min-height: calc(100vh - 100px);
  overflow-y: auto;
  background: #fff;
  border-radius: 10px;
  padding: 20px;
  box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
}
</style>

leftTree.vue

<template>
  <div>
    <el-tree
      style="max-width: 600px"
      :data="data"
      node-key="id"
      draggable
      :allow-drop="allowDrop"
      :allow-drag="allowDrag"
      default-expand-all
      :expand-on-click-node="false"
      @node-drag-start="handleDragStart"
    >
      <template #default="{ node, data }">
        <span class="custom-tree-node">
          <span>{{ node.label }}</span>
        </span>
      </template>
    </el-tree>
  </div>
</template>
<script lang="ts" setup>
import Vue from 'vue'
import type Node from 'element-plus/es/components/tree/src/model/node'
import type {
  AllowDropType,
  NodeDropType,
} from 'element-plus/es/components/tree/src/tree.type'
const data = [
  {
    label: 'Level one 1',
    children: [
      {
        label: 'Level two 1-1',
        children: [
          {
            label: 'Level three 1-1-1',
          },
        ],
      },
    ],
  },
  {
    label: 'Level one 2',
    children: [
      {
        label: 'Level two 2-1',
        children: [
          {
            label: 'Level three 2-1-1',
          },
        ],
      },
      {
        label: 'Level two 2-2',
        children: [
          {
            label: 'Level three 2-2-1',
          },
        ],
      },
    ],
  },
  {
    label: 'Level one 3',
    children: [
      {
        label: 'Level two 3-1',
        children: [
          {
            label: 'Level three 3-1-1',
          },
        ],
      },
      {
        label: 'Level two 3-2',
        children: [
          {
            label: 'Level three 3-2-1',
          },
        ],
      },
    ],
  },
]

const renderTreeNodes = (data: Node[]) => {}

const handleDragStart = (node: Node, ev: any) => {
  ev.dataTransfer.setData('text/plain', node.data.label)
}

const handleDrop = (ev: any) => {
  console.log('tree drop111:', ev)
}

const allowDrop = (draggingNode: Node, dropNode: Node, type: AllowDropType) => {
  if (dropNode.data.label === 'Level two 3-1') {
    return type !== 'inner'
  } else {
    return true
  }
}
const allowDrag = (draggingNode: Node) => {
  return true
  // return !draggingNode.data.label.includes('Level three 3-1-1')
}
</script>

<style lang="sass" scoped>
</style>

rightContent.vue

 <template>
  <div class="rightContent">
    <div class="headerRight">
      <el-icon :size="34" :class="colorFour">
        <Menu @click="handlechangeolor('four')" />
      </el-icon>
      <el-icon :size="34" :class="colorNine">
        <Grid @click="handlechangeolor('nine')" />
      </el-icon>
    </div>
    <div class="gridContent">
      <el-row :gutter="20">
        <el-col
          :span="colorFour == 'colorActive' ? 12 : 8"
          v-for="(item, index) in boxs"
          :key="index"
        >
          <div
            :class="[
              'glaobalBox',
              colorFour == 'colorActive' ? 'bigBox' : 'smallBox',
            ]"
            draggable="true"
            @dragstart="(e) => dragStartFn(e, item, index)"
            @dragenter="dragEnterFn($event, index)"
            @dragover="dragoverFn($event, index)"
            @drop="(e) => dropFn(e, item, index)"
          >
            {{ item }}
          </div>
        </el-col>
      </el-row>
    </div>
  </div>
</template>

<script lang="ts" setup>
import Vue, { onMounted, reactive, ref } from 'vue'

let colorFour = ref<string>('colorActive')
let colorNine = ref<string>('colorDefualt')
let num = ref<number>(0)
let boxs = ref<Array<any>>(['a', 'b', 'c', 'd'])
const handlechangeolor = (type: string) => {
  if (type == 'four') {
    boxs.value = boxs.value.slice(0, 4)
    colorFour.value = 'colorActive'
    colorNine.value = 'colorDefualt'
  } else if (type == 'nine') {
    boxs.value = boxs.value.concat(new Array(5).fill(1))
    colorFour.value = 'colorDefualt'
    colorNine.value = 'colorActive'
  }
}

// 拖动
const dragIndex = ref<number>()
const enterIndex = ref(0)
const selectedRow = ref()
const draggable = ref(true)
// 该事件在用户开始拖动元素时触发
const dragStartFn = (event: any, item: Number, index: Number) => {
  event.dataTransfer.setData('text/plain', item)
  selectedRow.value = item
  dragIndex.value = index
}
// ondragenter事件在拖动元素进入目标元素时触发,通常用于设置拖动时的样式。
const dragEnterFn = (e: any, index: number) => {
  e.preventDefault()
  enterIndex.value = index
}
// ondragover事件在拖动元素在目标元素上方时触发,通常用于防止默认的拖放行为。可以通//过event.preventDefault()方法阻止默认行为。
const dragoverFn = (e: any, index: number) => {
  e.preventDefault()
}
//从外部拖拽放置到当前列表
const dropFn = (event: any, item: string, index: number) => {
  var data = event.dataTransfer.getData('text/plain')
  //执行自己的操作
  let arr = JSON.parse(JSON.stringify(boxs.value))
  //释放位置 原本的值
  let targetItem = arr[index]

  //将拖动的值放在释放位置上
  arr.splice(index, 1, data)

  //如果内部拖动 则 内容互换
  //若外部拖动进来的内容则直接覆盖
  if (dragIndex.value == 0 || dragIndex.value > 0) {
    arr.splice(dragIndex.value, 1, targetItem)
  }

  dragIndex.value = -1
  boxs.value = arr
}
</script>

<style lang="scss" scoped>
.rightContent {
  .headerRight {
    display: flex;
    justify-content: flex-end;
    margin-bottom: 20px;
    .colorActive {
      color: #409eff;
    }
    .colorDefualt {
      color: #999;
    }
  }
  .gridContent {
    .glaobalBox {
      background: #e5ebf136;
      border: 1px solid #409eff;
      margin-bottom: 20px;
      padding: 20px;
    }
    .bigBox {
      height: 40vh;
      min-height: 300px;
    }
    .smallBox {
      height: 26vh;
      min-height: 280px;
    }
  }
}
</style> 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue 3和TypeScript(TS)结合使用Element Plus来进行角色菜单分配,是一种很好的选择。下面是一个关于如何实现角色菜单分配的简单示例。 首先,我们需要安装Vue 3、TypeScript和Element Plus。可以使用npm命令进行安装。 ```bash npm install vue@next npm install typescript npm install element-plus ``` 然后,在Vue 3项目的入口文件中,可以引入Element Plus的模块,以及一些额外的样式文件。 ```javascript import { createApp } from 'vue' import ElementPlus from 'element-plus' import 'element-plus/lib/theme-chalk/index.css' import App from './App.vue' const app = createApp(App) app.use(ElementPlus) app.mount('#app') ``` 在组件中,我们可以使用Element Plus的Menu组件来展示菜单,并通过V-if指令来根据角色进行菜单项的展示与隐藏。 ```vue <template> <div> <el-menu> <el-menu-item v-if="hasAccess('dashboard')">仪表盘</el-menu-item> <el-menu-item v-if="hasAccess('users')">用户管理</el-menu-item> <el-menu-item v-if="hasAccess('roles')">角色管理</el-menu-item> </el-menu> </div> </template> <script> import { defineComponent } from 'vue' export default defineComponent({ data() { return { userRoles: ['dashboard', 'users', 'roles'] } }, methods: { hasAccess(role) { return this.userRoles.includes(role) } } }) </script> <style> /* 根据需要自定义样式 */ </style> ``` 在该示例中,假设用户角色以数组的形式存储在`userRoles`属性中。通过定义一个`hasAccess`方法,我们可以判断用户是否拥有某个角色,然后根据判断结果,在菜单项上使用V-if指令来展示或隐藏对应的菜单项。 当用户拥有某个角色时,该菜单项将会在界面上显示出来,反之,则不会显示。需要注意的是,需根据实际需求对示例中的角色判断逻辑进行调整。 这只是一个简单的示例,实际的角色菜单分配功能可能涉及到更复杂的逻辑和接口调用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值