基于element-Plus的el-tooltip封装公共虚拟引用组件

vue3+vite项目中多处使用el-tooltip组件进行提示,避免重复代码过多封装公共组件,通过接收鼠标当前event位置和要展示的提示信息,在组件内部进行数据处理并展示。

按需引入element-Plus

未安装ui插件需安装,建议按需引入避免包体积过大,如果已安装跳过当前步骤开始组件封装。

  1. 自动导入插件下载
javascript 复制代码
npm install -D unplugin-vue-components unplugin-auto-import
  1. 手动导入样式包
javascript 复制代码
npm i unplugin-element-plus -D
  1. 配置文件添加自动导入(以vite为例)
javascript 复制代码
// vite.config.ts
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
// 按需配置导入
import AutoImport from "unplugin-auto-import/vite";
import Components from "unplugin-vue-components/vite";
import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
import ElementPlus from "unplugin-element-plus/vite";

export default defineConfig({
  plugins: [
    AutoImport({
      resolvers: [ElementPlusResolver()],
    }),
    Components({
      resolvers: [ElementPlusResolver()],
    }),
    // 按需引入css样式
    ElementPlus({}),
  ],
})
  1. 完整vite.config.ts配置项文件

上述代码以完成按需引入功能可直接使用,下述展示完整配置项文件包括按需引入,通配符、build打包等可借鉴。

javascript 复制代码
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
// 通配符引入
import { fileURLToPath, URL } from "node:url";
// 按需导入
import AutoImport from "unplugin-auto-import/vite";
import Components from "unplugin-vue-components/vite";
import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
import ElementPlus from "unplugin-element-plus/vite";
// 内置控制台元素检查(如使用执行npm i vite-plugin-vue-devtools)
// import vueDevTools from 'vite-plugin-vue-devtools'

export default defineConfig({
  // 端口号ip
  server: {
    host: "0.0.0.0",
    port: 8000,
  },
  plugins: [
    vue(),
    // vueDevTools(),
    // 按需导入ui插件
    AutoImport({
      resolvers: [ElementPlusResolver()],
    }),
    Components({
      resolvers: [ElementPlusResolver()],
    }),
    // 按需引入css样式
    ElementPlus({}),
  ],
  // 配置@通用符,指向src文件夹
  resolve: {
    alias: {
      "@": fileURLToPath(new URL("./src", import.meta.url)),
    },
  },
  // 预处理加载scss文件,注册全局变量(可自主配置修改文件名和路径即可)
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: `@import "@/assets/styles/variables.scss";`,
      },
    },
  },
  build: {
    // 是否生成调试文件(增加打包体积默认不需要)
    sourcemap: false,
    // 压缩格式,默认esbuild,terser更小
    minify: "terser",
    terserOptions: {
      // 移除生产环境console/debugger
      compress: {
        drop_console: true,
        drop_debugger: true,
      },
    },
    // 自定义输出目录
    outDir: "custom-dist",
    // 配置资源分类
    rollupOptions: {
      output: {
        // 非入口和入口文件的js放入js目录
        chunkFileNames: "js/[name]-[hash].js",
        entryFileNames: "js/[name]-[hash].js",
        // 资源文件命名规则
        assetFileNames: (assetInfo) => {
          // CSS文件
          if (assetInfo.name?.endsWith(".css")) {
            return "css/[name]-[hash][extname]";
          }
          // 图片资源
          const imgExts = [".png", ".jpg", ".jpeg", ".gif", ".svg", ".webp"];
          if (assetInfo.name && imgExts.some((ext) => assetInfo.name?.endsWith(ext))) {
            return "images/[name]-[hash][extname]";
          }
          // 字体文件
          const fontExts = [".woff", ".woff2", ".eot", ".ttf", ".otf"];
          if (assetInfo.name && fontExts.some((ext) => assetInfo.name?.endsWith(ext))) {
            return "fonts/[name]-[hash][extname]";
          }
          // 其他资源
          return "assets/[name]-[hash][extname]";
        },
      },
    },
  },
});

公共组件封装

创建文件:src/components/customTooltip/index.vue

javascript 复制代码
<!-- 
  el-tooltip 配置说明:
  - placement: 提示框位置,top/right/bottom/left 等
  - virtual-ref: 虚拟引用,用于自定义定位
  - virtual-triggering: 启用虚拟触发模式
  - visible: 控制提示框显示/隐藏
  - popper-class: 自定义弹出框类名
  - trigger: 触发方式,manual 表示手动控制
  - show-arrow: 是否显示箭头
-->
<template>
  <el-tooltip
    placement="top"
    :virtual-ref="virtualRef"
    virtual-triggering
    :visible="visible"
    popper-class="custom-tooltip"
    trigger="manual"
    :show-arrow="true"
  >
    <template #content>
      <div style="white-space: pre-line">{{ content }}</div>
    </template>
  </el-tooltip>
</template>

<script setup lang="ts">
import { ref, watch } from "vue";

defineOptions({
  name: "CustomTooltipPage",
});

const props = defineProps<{
  // 是否显示
  visible: boolean;
  // 提示内容
  content: string;
  // 位置信息(由鼠标事件提供)
  position?: { x: number; y: number };
}>();

// 虚拟引用,用于 el-tooltip 的虚拟定位,转换位置信息为 el-tooltip 需要的虚拟 DOM 结构
const virtualRef = ref<{
  getBoundingClientRect: () => DOMRect;
} | null>(null);

// 监听位置变化,更新虚拟 DOM 位置
watch(
  () => props.position,
  (newPos) => {
    if (newPos) {
    // 创建虚拟 DOM 矩形信息
      virtualRef.value = {
        getBoundingClientRect: () =>
          ({
            width: 0,      // 宽度为0,tooltip 会自适应内容
            height: 0,     // 高度为0,tooltip 会自适应内容
            x: newPos.x,   // x 坐标
            y: newPos.y,   // y 坐标
            left: newPos.x, // 左边界
            right: newPos.x, // 右边界
            top: newPos.y,  // 上边界
            bottom: newPos.y, // 下边界
          } as DOMRect),
      };
    }
  },
  { immediate: true }
);
</script>

<style lang="scss" scoped></style>

样式调整

el-tooltip样式需要再全局scss文件处理,避免层级不够或样式丢失。

  1. 例:src/assets/style/elementPlus.scss。
javascript 复制代码
// tooltip全局样式
.custom-tooltip {
  // 亮色主题
  --el-bg-color-overlay: rgba(5, 130, 150, 1);
  --el-border-color-light: rgba(0, 255, 244, 1);
  // 暗色主题
  --el-bg-color: #fff;
  --el-text-color-primary: rgba(5, 130, 150, 1);
  --el-border-color: rgba(0, 255, 244, 1);

  font-size: 10px !important;
  padding: 2px 5px !important;
  border: 1px solid var(--el-border-color) !important;
}

组件使用

当前展示为页面组件导入,如果需要配置全局在main.ts手动配置即可。

  1. cameraList为自定义循环数组,可替换为div行数据,主要功能在鼠标移入移出。
  2. mouseenter鼠标移入传递内容、坐标轴,鼠标移出隐藏,不在组件内部处理便于使用。
javascript 复制代码
<template>
	<div
      v-for="(item, index) in cameraList"
      :key="'camera' + index"
      @mouseenter="showTooltip($event, item)"
      @mouseleave="hideTooltip"
     >
       <!-- ...自定义盒子内容 -->
    </div>
    <!-- 提示框组件 -->
    <CustomTooltip
      :visible="tooltipVisible"
      :content="tooltipContent"
      :position="tooltipPosition"
    />
</template>
// 组件导入
import CustomTooltip from "@/components/customTooltip/index.vue";

// tooltip是否显示
const tooltipVisible = ref(false);
// 提示内容
const tooltipContent = ref("");
// 位置
const tooltipPosition = ref<{ x: number; y: number } | undefined>(undefined);

// tooltip显示事件
const showTooltip = (event: MouseEvent, cell: any) => {
  if (cell.status === "none") return;
  // 自定义字符串拼接,换行使用\n
  tooltipContent.value = `名称:${cell.name}\nip:${cell.ip}\n编号:${cell.no}`;
  // 传递鼠标当前坐标轴
  tooltipPosition.value = {
    x: event.clientX,
    y: event.clientY,
  };
  tooltipVisible.value = true;
};

// 隐藏tooltip事件
const hideTooltip = () => {
  tooltipVisible.value = false;
};
相关推荐
by__csdn1 小时前
Electron入门:跨平台桌面开发指南
前端·javascript·vue.js·typescript·electron·html
Nan_Shu_6144 小时前
学习:ES6(2)
前端·学习·es6
命运之光8 小时前
【最新】ChromeDriver最新版本下载安装教程,ChromeDriver版本与Chrome不匹配问题
前端·chrome
星离~9 小时前
Vue响应式原理详解:从零实现一个迷你Vue
前端·javascript·vue.js
梦65010 小时前
React 简介
前端·react.js·前端框架
一只小阿乐10 小时前
react 中的判断显示
前端·javascript·vue.js·react.js·react
光影少年10 小时前
useMemo 和 React.memo区别
前端·react.js·前端框架
小沐°10 小时前
React-页码组件
前端·javascript·react.js