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>

如有不足,欢迎指正。

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

相关推荐
别拿曾经看以后~9 分钟前
【el-form】记一例好用的el-input输入框回车调接口和el-button按钮防重点击
javascript·vue.js·elementui
川石课堂软件测试15 分钟前
性能测试|docker容器下搭建JMeter+Grafana+Influxdb监控可视化平台
运维·javascript·深度学习·jmeter·docker·容器·grafana
JerryXZR30 分钟前
前端开发中ES6的技术细节二
前端·javascript·es6
problc44 分钟前
Flutter中文字体设置指南:打造个性化的应用体验
android·javascript·flutter
Gavin_9151 小时前
【JavaScript】模块化开发
前端·javascript·vue.js
懒大王爱吃狼2 小时前
Python教程:python枚举类定义和使用
开发语言·前端·javascript·python·python基础·python编程·python书籍
待磨的钝刨3 小时前
【格式化查看JSON文件】coco的json文件内容都在一行如何按照json格式查看
开发语言·javascript·json
Devil枫6 小时前
Vue 3 单元测试与E2E测试
前端·vue.js·单元测试
GIS程序媛—椰子8 小时前
【Vue 全家桶】6、vue-router 路由(更新中)
前端·vue.js
前端青山8 小时前
Node.js-增强 API 安全性和性能优化
开发语言·前端·javascript·性能优化·前端框架·node.js