vue3中实现函数式组件 场景: 右键菜单

函数式组件

利用调用函数的方式创建组件,该组件可以满足于消息提醒,模态框渲染。一般在市场上通用的ui组件库中比较常见于messageBox(消息提示组件)。

为什么要自己实现函数式组件呢

作者在使用第三方库 antv/g6 时出现在手机端无右键菜单选项时突发奇想想到自己做一个能兼顾库又能在自定义Behavior的中顺利运用所想到的。

代码实现

1、contextmenuBox.vue文件

<template>
  <div class="fnCpn"
       :style="{
          left: props.x + 'px',
          top: props.y + 'px'
       }"
  >
    <div class="item"
         v-for="item in props.list"
         :key="item.value"
         :class="[
             item.value === props.nowVal? 'active' : ''
         ]"
         @click="handleItemClick(item)"
    >
      {{item.label}}
    </div>
  </div>
</template>

<script setup lang="ts">

type ListItem = {label: string, value: string};
interface PropType {
  // 菜单选项
  list: Array<ListItem>,
  // 目前所选的目标值
  nowVal: string,
  // 右键鼠标的x值
  x: number,
  // 右键鼠标的y值
  y: number,
  // 关闭函数
  closeFn: <T = any>(value: ListItem) => void
}

const props = withDefaults(defineProps<PropType>(), {
  nowVal: ''
})

// 点击菜单处理函数
// 可拓展其他逻辑
const handleItemClick = (value: ListItem) => {
  props.closeFn(value);
}
</script>

<style scoped lang="scss">
.fnCpn{
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  //width: 80px;
  background: white;
  position: fixed;
  .item{
    width: 80px;
    border: 1px solid #eee;
    text-align: center;
    font-size: 14px;
    padding: 8px;
    cursor: pointer;
  }
  .active{
    background: #E6A23C;
    color: white;
  }
}
</style>

2、renderCpn.ts

import {createApp} from "vue";
import messageBox from "./contextmenuBox.vue";

// 函数式创建组件函数
export default function createContextmenuBox(
    // 菜单列表
    list: Array<{label: string, value: string}>,
    // 当前所选值
    nowVal: string,
    // 要渲染的位置
    position: {x: number, y: number},
    // 是否关闭的回到函数
    callback: <T = any>(value: {label: string, value: string}) => boolean | Promise<T>,
    // 渲染到根DOM
    root: HTMLElement = document.body
) {
    // 删除dom的函数,当传递的callback返回的值为false或者Promise.reject()时都不会销毁当前dom
    const close = async <T = any>(value: {label: string, value: string}) => {
        try {
            const result = await callback(value);

            if(typeof result === 'boolean' && !result) return;
            // 卸载组件
            messageComponent.unmount();
        } catch (e) {
            throw new Error(e);
        }
    }

    // 函数式组件生成
    const messageComponent = createApp(messageBox, {
        list,
        nowVal,
        x: position.x,
        y: position.y,
        closeFn: close
    });
    // 挂载到相应的dom上
    messageComponent.mount(root);
}

3、在组件的中的使用(这里贴的代码于g6无关,只是简单的通用场景)

<template>
  <div class="clickArea"
       style="width: 100vw;height: 1000px;background: #747bff"
       @contextmenu.prevent="handleContextmenu($event)"
       ref="contentRef"
  >

  </div>
</template>

<script lang="ts" setup>
import createContextmenuBox from "@/components/functionComponent/renderCpn.ts";
import {ref} from "vue"

const list = [
  {label: '复制', value: 'copy'},
  {label: '粘贴', value: 'stick'},
  {label: '删除', value: 'delete'}
]

// 右键菜单事件
const handleMenuClick = (value: {label: string, value: string}) => {
  console.log("传递的值接受到了:", value)
  return Promise.reject("特意设计的error");
  // return false
}

const contentRef = ref()
// 右键菜单处理事件
const handleContextmenu = (e: MouseEvent) => {
  const {offsetX, offsetY} = e;

  createContextmenuBox(
      list,
      'delete',
        {x: offsetX, y: offsetY},
      handleMenuClick,
      contentRef.value
  )
}
</script>

4、代码结构图
在这里插入图片描述

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值