Components

Table Context Menu

index.vue

index.vue

<template>
  <div class="content">
    <el-table
      :data="tableData"
      style="width: 100%"
      @row-contextmenu="handContextmenu"
    >
      <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>
    <Menu v-if="show" ref="menuRef" @close="show = false" />
  </div>
</template>

<script lang="ts" setup>
import Menu from "./Menu.vue";

const tableData = [
  {
    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 show = ref(false);
const menuRef = ref();
const handContextmenu = (row: any, column: any, event: any) => {
  const { x, y } = event;
  event.preventDefault();
  show.value = true;
  nextTick(() => {
    menuRef.value.init(row, column, event);
  });
};
</script>

<style scoped>
.content {
  width: 700px;
  border: 1px solid #ccc;
  margin: 0 auto;
}
</style>

Menu.vue

<template>
  <div id="contextmenu" class="contextmenu">
    <div class="contextmenu__item" @click="handleOne">菜单一</div>
    <div class="contextmenu__item" @click="handleThree">菜单三</div>
  </div>
</template>

<script lang="ts" setup>
const emit = defineEmits(["close"]);

const init = (row: any, column: any, event: any) => {
  const { x, y } = event;

  const content = document.querySelector(".content") as HTMLDivElement;
  const menu = document.querySelector("#contextmenu") as HTMLDivElement;

  const menuWidth = menu.offsetWidth;
  const menuHeight = menu.offsetHeight;
  const contentWidth = content.offsetWidth;
  const contentHeight = content.offsetHeight;

  const X = x - content.offsetLeft; //上边距
  const Y = y - content.offsetTop; //下边距

  menu.style.left = x + "px";
  menu.style.top = y + "px";

  if (X > contentWidth - menuWidth) {
    menu.style.left = x - menuWidth + "px";
  }
  if (Y > contentHeight - menuHeight) {
    menu.style.top = y - menuHeight + "px";
  }

  document.addEventListener("click", foo);
};

const foo = () => {
  emit("close");
};
const handleOne = () => {};
const handleThree = () => {};
onBeforeUnmount(() => {
  document.removeEventListener("click", foo);
});
defineExpose({ init });
</script>

<style scoped>
.contextmenu__item {
  display: block;
  line-height: 34px;
  text-align: center;
}

.contextmenu__item:not(:last-child) {
  border-bottom: 1px solid rgba(64, 158, 255, 0.2);
}

.contextmenu {
  position: absolute;
  background-color: #ecf5ff;
  width: 100px;
  /*height: 106px;*/
  font-size: 12px;
  color: #409eff;
  border-radius: 4px;
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
  border: 1px solid rgba(64, 158, 255, 0.2);
  white-space: nowrap;
  z-index: 1000;
}

.contextmenu__item:hover {
  cursor: pointer;
  background: #66b1ff;
  border-color: #66b1ff;
  color: #fff;
}
</style>