Vue3封装table表格右键菜单功能

1) 效果,右键单击单元格,打开菜单弹窗:

点击菜单选项,可选择只读/编辑,可在只读/编辑方法中,拿到该行列表格的数据,进行相关操作

2) 思路

1、右键菜单组件 出现的时机,是右键单击table表格@row-contextmenu ,Element-Plus有提供;且需要阻止右键单击默认事件@contextmenu.prevent。

2、右键菜单组件 出现的位置,根据右键单击table表格的位置 对 右键菜单组件的位置进行设置。

3、右键菜单组件 展示内容,数据的传递 textList 和 点击事件的位置clickXY(决定右键菜单组件出现的位置,需要在视口内)。

3、右键菜单组件 事件传递,这里没有使用emit的方式,而是调用textList数组对象中fnn字段传递给子组件的,子组件调用就可以触发 父组件定义的方法。

4、右键菜单组件 如何关闭,右键单击打开,左键单击关闭,需要监听点击点击事件的e.button,值如为 0:鼠标左键、 1:滚轮按钮或中间按钮(如果有)、 2:鼠标右键

3) 简单的写一下.vue文件代码

前端代码在这里:https://github.com/wwaini/tao-vue3/tree/release240625

后端代码笔者用node写的,在这里:https://github.com/wwaini/tao-express

父组件 (代码中有详细备注)

复制代码
<template>
  <div>
    <!-- @contextmenu.prevent 阻止默认事件 -->
    <el-table @row-contextmenu="rightClickFn" @contextmenu.prevent :data="tableData" style="width: 100%">
      <el-table-column prop="date" label="Date" width="180" />
      <el-table-column prop="name" label="Name" width="180" />
      <el-table-column prop="address" label="Address" />
    </el-table>
    <right-menu :textList="textList" :clickXY="clickXY"></right-menu>
  </div>
</template>
<script setup>
import rightMenu from './component/index.vue'
import { reactive } from 'vue';
let tableData = reactive([
  {
    date: '2016-05-03',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
  },
  {
    date: '2016-05-02',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
  },
  {
    date: '2016-05-04',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
  },
  {
    date: '2016-05-01',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
  },
])
const textList = reactive([])
const clickXY = reactive({})
const rightClickFn = (row, column, event) => {
  Object.assign(textList,
    [
      {
        // 使用回调函数的方式fnn,定义在父组件,在子组件调用即可触发
        icon: 'view', text: '只读', fnn: view,
        params: { row, column, event }
      },
      {
        icon: 'edit', text: '编辑', fnn: edit,
        params: { row, column, event }
      }
    ]
  )
  Object.assign(clickXY,
    {
      position: {
        x: event.clientX,
        y: event.clientY
      }
    }
  )
}
const view = (val) => {
  // val拿到该行该列的信息
  console.log('view-----', val);
}
const edit = (val) => {
  // val拿到该行该列的信息
  console.log('edit-----', val);
}
</script>

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

子组件 - 右键菜单组件

复制代码
<!-- 右键菜单功能 -->
<template>
  <div id="rightMenu">
    <ul class="table-right-menu">
      <li @click="fn1(item)" v-for="(item) in textList" :key="item.text">
        <el-icon :size="15">
          <View v-if="item.icon === 'view'" />
          <Edit v-if="item.icon === 'edit'" />
        </el-icon>
        {{ item.text }}
      </li>
    </ul>
  </div>
</template>
<script setup>
import { defineProps, watch } from "vue";
const props = defineProps({
  textList: {
    type: Array,
    default: () => []
  },
  clickXY: {
    type: Object,
    default: () => { }
  }
})
const hide = (e) => {
  if (e.button === 0) {
    let positionXY = document.getElementById('rightMenu')
    positionXY.style.display = 'none'
    // 接触监听事件
    document.removeEventListener("mouseup", hide)
  }
}
const fn1 = (item) => {
  item.fnn(item);
}
watch(
  () => props.clickXY.position,
  (val) => {
    let x = val.x; // 获取x轴坐标
    let y = val.y; // 获取y轴坐标
    let innerWidth = window.innerWidth; // 获取页面可是区域宽度,即页面的宽度
    let innerHeight = window.innerHeight; // 获取可视区域高度,即页面的高度
    let menuHeight = props.textList.length * 30 // 弹窗高
    let menuWidth = 100 // 弹窗宽

    let positionXY = document.getElementById('rightMenu')
    positionXY.style.display = 'block'
    // 如果点击点 在视口最右方或者最下方 保证弹窗显示在视口内
    positionXY.style.top = (y + menuHeight > innerHeight ? innerHeight - menuHeight - 10 : val.y) + 'px'
    positionXY.style.left = (x + menuWidth > innerWidth ? innerWidth - menuWidth - 10 : val.x) + 'px'
    // false 表示 hide 这个事件处理函数将在鼠标点击(mouseup)事件的冒泡阶段被调用。
    // 如果为true,表示捕获阶段调用
    document.addEventListener("mouseup", hide, false);
  }
)
</script>
<style scoped>
#rightMenu {
  width: 108px;
  position: fixed;
  z-index: 999;
  display: none;
  background: #fff;
  box-shadow: 0 0 10px #ccc;
  border-radius: 5px;

  .table-right-menu {
    line-height: 30px;
    text-align: center;
    font-size: 12px;
    padding-left: 0;

    li {
      height: 30px;
      display: flex;
      justify-content: space-evenly;
      padding: 0 15px;
      align-items: center;
    }
  }
}
</style>

如有不足,欢迎指正。

不要忽视你达成的每个小目标,它是你前进路上的垫脚石。冲!

相关推荐
ooseabiscuit1 小时前
Laravel6.x核心优化与特性全解析
android·开发语言·javascript
哆啦A梦15881 小时前
20, Springboot3+vue3实现前台轮播图和详情页的设计
javascript·数据库·spring boot·mybatis·vue3
gogoing1 小时前
ESLint 配置字段说明
前端·javascript
Lkstar2 小时前
面试官让我手写 Promise.all / Promise.race / Promise.allSettled,我直接水灵灵地写出来了
javascript·面试
gogoing2 小时前
webpack 的性能优化
前端·javascript
gogoing2 小时前
Node.js 模块查找策略(require 完整流程)
javascript·node.js
gogoing2 小时前
await fetch() 的两阶段设计
前端·javascript
gogoing2 小时前
前端首屏加载优化
前端·javascript
gogoing2 小时前
重排与重绘
前端·javascript
徐小夕3 小时前
100小时,我做了一款AI CAD建模软件,开源!
前端·vue.js·github