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>

如有不足,欢迎指正。

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

相关推荐
星星会笑滴2 分钟前
vue+node+Express+xlsx+emements-plus实现导入excel,并且将数据保存到数据库
vue.js·excel·express
前端百草阁22 分钟前
【TS简单上手,快速入门教程】————适合零基础
javascript·typescript
彭世瑜23 分钟前
ts: TypeScript跳过检查/忽略类型检查
前端·javascript·typescript
Backstroke fish24 分钟前
Token刷新机制
前端·javascript·vue.js·typescript·vue
zwjapple24 分钟前
typescript里面正则的使用
开发语言·javascript·正则表达式
小五Five25 分钟前
TypeScript项目中Axios的封装
开发语言·前端·javascript
临枫54126 分钟前
Nuxt3封装网络请求 useFetch & $fetch
前端·javascript·vue.js·typescript
RAY_CHEN.27 分钟前
vue3 pinia 中actions修改状态不生效
vue.js·typescript·npm
酷酷的威朗普27 分钟前
医院绩效考核系统
javascript·css·vue.js·typescript·node.js·echarts·html5
前端每日三省27 分钟前
面试题-TS(八):什么是装饰器(decorators)?如何在 TypeScript 中使用它们?
开发语言·前端·javascript