vue3+ElementPlust实现简单的ContextMenu

一、创建WebContextmenu文件

1. html部分

<template>
  <transition :name="`el-zoom-in-${transitionType}`" :css="!hasMoveAnimation">
    <ul
      v-if="modelValue"
      class="web-contextmenu user-none"
      :style="styleCss"
      ref="targetRef"
      :class="{ 'is-transition': hasMoveAnimation }"
    >
      <li
        class="web-contextmenu-item"
        v-for="(item, index) in list"
        :key="item.name"
        :class="{ 'is-disabled': item.disabled }"
        @click="handleCommand(<boolean>item.disabled, index)"
      >
        <el-icon>
          <component :is="item.icon"></component>
        </el-icon>
        {{ item.name }}
      </li>
    </ul>
  </transition>
</template>

1.transition 使用的Elementplus内置过渡动画 具体可参考官网

2. js部分

<script setup lang="ts" name="web-contextmenu">
const targetRef = ref<HTMLElement | null>(null)
const { isOutside } = useMouseInElement(targetRef)
interface configListType {
  icon: string
  name: string
  disabled?: boolean
  show?: boolean
}
const props = withDefaults(
  defineProps<{
    transitionType?: 'top' | 'center' | 'bottom'
    configList: configListType[]
    modelValue: boolean
    x?: number
    y?: number
    hasMoveAnimation?: boolean
    zIndex?: number
  }>(),
  {
    transitionType: 'top',
    modelValue: false,
    x: 0,
    y: 0,
    hasMoveAnimation: false,
    zIndex: 10,
  },
)
const emit = defineEmits(['command', 'update:modelValue'])
//过滤item.show
const list = computed(() => {
  return props.configList.filter((item) => !item.show)
})
const handleCommand = (disabled: boolean, index: number) => {
  if (disabled) return
  //如果禁用的就不执行
  new Promise((resolve, reject) => {
    try {
      emit('command', index)
      resolve(true)
    } catch (e) {
      reject(e)
    }
  }).then(() => {
    emit('update:modelValue', false)
  })
}
watch(
  () => props.modelValue,
  (newValue) => {
    if (newValue) {
      document.addEventListener('click', closeMenu, true)
    } else {
      document.removeEventListener('click', closeMenu, true)
    }
  },
)
// 关闭
const closeMenu = () => {
  if (!isOutside.value) return
  emit('update:modelValue', false)
}
//css样式
const styleCss = computed(() => {
  let { x, y, zIndex } = props
  return { left: x + 'px', top: y + 'px', zIndex }
})
</script>

1. useMouseInElement 使用的@vueuse/core工具库 鼠标在元素中  具体可参考官网

3. style样式部分

<style lang="scss" scoped>
.web-contextmenu {
  position: fixed;
  padding: 5px 0;
  margin: 0;
  background-color: var(--el-bg-color-overlay);
  border-radius: var(--el-border-radius-base);
  list-style: none;
  box-shadow: 2px 2px 10px var(--el-color-info-light-7);
  display: flex;
  flex-direction: column;
  &.is-transition {
    transition: $base-transition;
  }
  &-item {
    display: flex;
    align-items: center;
    white-space: nowrap;
    list-style: none;
    line-height: 22px;
    padding: 5px 16px;
    margin: 0;
    font-size: var(--el-font-size-base);
    color: var(--el-text-color-regular);
    cursor: pointer;
    outline: none;
    > i {
      margin-right: 5px;
    }
    &:not(.is-disabled):hover {
      color: var(--el-color-primary);
      background: var(--el-color-primary-light-9);
    }
    &.is-disabled {
      cursor: not-allowed;
      color: var(--el-text-color-disabled);
    }
  }
}
</style>

1.css变量$base-transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1), border 0s,

color 0.1s, font-size 0s

二、使用WebContextmenu

效果图展示

 1.全显示

2. show隐藏

 

3. disabled禁用

 1. html部分

<template>
   <el-tree
    style="max-width: 600px"
    :data="data"
    @node-contextmenu="handleNodeContextmenu"
    />
    <WebContextmenu
      transition-type="center"
      :configList="contextMenuList"
      :x="contextmenuParmas.x"
      :y="contextmenuParmas.y"
      v-model="contextmenuParmas.visible"
      @command="handleCommand"
    ></WebContextmenu>

</template>

2. js部分

<script lang="ts" setup >
import WebContextmenu from '@/components/WebContextmenu/index.vue'
interface Tree {
  label: string
  show: boolean
  disabled: boolean
  children?: Tree[]
}
const data: Tree[] = [
  {
    label: '测试1',
    show: false,
    disabled: false,
    children: [
      {
        label: '测试1-1',
        show: true,
        disabled: false,
      },
      {
        label: '测试1-2',
        show: false,
        disabled: true,
      },
    ],
  },
  {
    label: '测试2',
    show: false,
    disabled: false,
    children: [
      {
        label: '测试2-1',
        show: true,
        disabled: false,
      },
      {
        label: '测试2-2',
        show: false,
        disabled: true,
      },
    ],
  },
]

interface contextmenuParmasType<T = any> {
  x: number //x轴
  y: number //y轴
  visible: boolean //是否显示 true显示 false隐藏
  row?: T //存储数据
}
/**
 * contextmenu定义参数
 */
const contextmenuParmas = ref<contextmenuParmasType<Tree>>({
  x: 0,
  y: 0,
  visible: false, //true显示 false隐藏
})

/**
 * 鼠标右键功能 默认使用的tree的右键事件也可以是表格的
 * @param treeNode tree实例对象
 * @param row row数据
 */
const handleNodeContextmenu = (treeNode: MouseEvent, row: Tree) => {
  contextmenuParmas.value = {
    x: treeNode.x,
    y: treeNode.y,
    visible: true,
    row,
  }
}
/**
 * contextMenuList2 默认
 */
const contextMenuList2 = [
  {
    icon: 'plus',
    name: '添加',
  },
  {
    icon: 'edit',
    name: '编辑',
  },
  {
    icon: 'delete',
    name: '删除',
  },
]

/**
 * contextMenu  如果数据是动态的就用computed
 */
const contextMenuList = computed(() => {
  // show      ture是隐藏  false不隐藏   可选
  // disabled  ture是禁用  false不禁用   可选
  let { row } = contextmenuParmas.value
  return [
    {
      icon: 'plus',
      name: '添加',
    },
    {
      icon: 'edit',
      name: '编辑',
    },
    {
      icon: 'delete',
      name: '删除',
    },
    //show  disabled的使用
    {
      icon: 'menu',
      name: '菜单',
      show: row?.show ? true : false,
      disabled: row?.disabled ? true : false,
    },
  ]
})

/**
 * contextMenu点击事件
 * @param index 就是配置数组下标
 */
const handleCommand = (index: number) => {
  // 如果做逻辑操作可以使用currentRow
  let currentRow = contextmenuParmas.value.row
  switch (index) {
    case 0:
      // 下标0执行
      break
    case 1:
      // 下标1执行
      break
    case 2:
      // 下标2执行
      break
    case 3:
      // 下标3执行
      break
  }
}
</script>

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值