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>

  • 9
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Vue3和Pinia是一种组合使用的方式,用于实现Vue应用程序的状态管理。Pinia是一个状态管理工具,类似于Vuex,但提供了更简单和更灵活的语法。它支持Vue2和Vue3,并且是类型安全的,支持TypeScript。Pinia可以创建多个全局仓库,而不需要像Vuex那样嵌套模块,使得状态管理的结构更加简单和清晰。相比于Vuex,Pinia的API更少,使用起来更加简单。Pinia的大小仅有1KB,非常轻量级。你可以通过引入Pinia和创建Pinia实例来开始使用Pinia。同时,Pinia还支持数据持久化插件,可以帮助你实现数据的持久化存储。你可以在Pinia的官方文档中找到更详细的使用说明和示例代码。\[2\]\[3\] #### 引用[.reference_title] - *1* [一步步打造最新前端框架组合 —— vite+vue3+pinia+vue-router@4+setup+ts](https://blog.csdn.net/jmszl1991/article/details/122825151)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [Vue3+Pinia+数据持久化 20分钟快速上手](https://blog.csdn.net/zhgweb/article/details/129708483)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值