从0到1开发一个Vue3的新手引导组件(附带遇到的问题以及解决方式)

news2025/2/27 16:59:53

1. 前言:

新手引导组件,顾名思义,就是强制性的要求第一次使用的用户跟随引导使用应用,可以让一些第一次使用系统的新手快速上手,正好我最近也遇到了这个需求,于是就想着开发一个通用组件拿出来使用(写完之后才发现element就有,后悔了哈哈哈😭😭)

示例图:

  1. 第一步
    第一步

  2. 第二步
    第二步

  3. 第三步
    第三步

2. 使用的技术栈以及组件:

Vue3+Vite+Element+uuid,使用了el-popover组件以及el-button组件

3. 遇到的问题:

开发这个组件不是一个简单的事情,遇到了不少的问题,尤其是样式上的问题,下面我一一说明以及对应的解决方式

  1. 需要将用户可以点击的部分凸出出来:

    在原先我是打算给组件传入一个class类名的,然后通过指定的class类名获取到dom,之后再通过dom.getBoundingclientRect()方法获取指定dom的尺寸以及位置信息,然后再组件中创建一个空白区域,但是我真正尝试的时候才发现一个致命问题,getBoundingclientRect方法获取的位置信息都是对的,但是尺寸却是固定的0,后面才发现,如果新手引导的内容是一个图片的话,就必须要等到图片加载完成后再在页面上进行引导,我尝试了onload之后再进行绘制,虽然实现了想要的效果,但是我觉得这样操作太麻烦了,就抛弃了这种方法

  2. 改为插槽:

    上面的方法行不通,操作也麻烦,然后我就创建了两个插槽,一个是content插槽,此插槽没有任何作用,只是将内容显示出来,一个是target插槽,这里的插槽将会作为新手引导的内容,这里解释下为什么需要两个插槽,因为新手引导组件可能应用于循环中,针对于循环的其他内容,可以使用content插槽填充,需要新手引导的内容再使用target插槽即可

4. 源码讲解(请查看详细注释):

<!--
 * @Author: wangZhiyu <w3209605851@163.com>
 * @Date: 2024-07-05 09:28:39
 * @LastEditTime: 2024-07-11 14:05:36
 * @LastEditors: wangZhiyu <w3209605851@163.com>
 * @Descripttion: /新手引导组件.vue
-->
<template>
  <!-- 新手引导插槽 -->
  <div class="targetSlot" v-if="isTarget">
    <!-- 提示文字区域 -->
    <el-popover :popper-style="`width:auto;color:#000;font-size:20px;z-index:${forceShowPopover ? 9999 : 2024}`" :visible="visible" placement="top" :offset="25">
      <!-- popover内的元素 -->
      <div style="white-space: nowrap; display: flex; align-items: center; justify-content: center">
        <el-button v-if="isShowAudioCourse" type="info" circle size="small" style="font-size: 18px">?</el-button>
        <span style="margin: 0 5px">{{ tipMessage }}</span>
        <el-button v-if="readBtn" type="primary" @click="onRead">{{ readMessage }}</el-button>
      </div>

      <!-- 触发popover的元素 -->
      <template #reference>
        <!-- 元素背景高亮区域 -->
        <div :class="`highLightArea highLightArea_${uuid}`" v-if="isFoucsArea"></div>
      </template>
    </el-popover>
    <slot name="target"></slot>
  </div>
  <!-- <slot name="target"></slot> -->

  <!-- 普通内容插槽(根据传入的参数进行动态改变) -->
  <slot :name="isTarget ? 'content' : 'target'"></slot>

  <!-- 新手引导遮罩层(这里给遮罩层添加了一个点击事件,并且组织事件冒泡,是为了避免点击遮罩层所产生事件冒泡罩层影响) -->
  <div class="maskArea" @click.stop="() => {}" v-if="isFoucsArea && value"></div>
</template>

<script setup>
// 导入UUID库
import { v4 as uuidv4 } from 'uuid';

import { ref, nextTick, onMounted, useSlots, watch } from 'vue';

// 设置是否显示提示信息
const visible = ref(false);

// 生成一个uuid
const uuid = uuidv4();

// 抛出事件
const emits = defineEmits(['onRead']);

// 父组件传入的参数
const props = defineProps({
  // 是否继承父组件样式
  isInheritStyle: {
    type: Boolean,
    default: () => false,
  },
  // 高亮区域盒子的占比大小-->宽
  highLightAreaWidth: {
    type: [String, Number],
  },
  // 高亮区域盒子的占比大小-->高
  highLightAreaHeight: {
    type: [String, Number],
  },
  // 高亮区域盒子的占比位置-->垂直距离调整
  highLightAreaTop: {
    type: [String, Number],
  },
  // 高亮区域盒子的占比位置-->水平距离调整
  highLightAreaLeft: {
    type: [String, Number],
  },
  // 是否强制显示提示(避免提示被遮住的情况)
  forceShowPopover: {
    type: Boolean,
    default: () => false,
  },
  // 是否显示已阅读按钮
  readBtn: {
    type: Boolean,
    default: () => false,
  },
  // 内部按钮文字
  readMessage: {
    type: String,
    default: () => '下一步',
  },
  // 提示文字
  tipMessage: {
    type: String,
    default: () => '',
  },
  // 是否显示提示按钮,点击后可显示对应的视频教程
  isShowAudioCourse: {
    type: Boolean,
    default: () => false,
  },
  // 是否为当前应该显示的教程步骤(用于一个页面多个步骤的流程引导)
  isTarget: {
    type: Boolean,
    default: () => true,
  },
  // 是否为当前应该显示的教程步骤(用于一个页面多个步骤的流程引导)
  value: {
    type: Boolean,
    default: () => true,
  },
});

// 当前组件中被使用的插槽
const slot = useSlots();

// 已完成引导
const onRead = () => {
  visible.value = false;
  emits('onRead');
};

// 是否聚焦按钮,高亮新手引导
let isFoucsArea = ref(false);

// 如果 slot.target 成立,则表示使用了新手引导插槽
isFoucsArea.value = slot.target ? true : false;

// 页面初始化函数
const init = () => {
  nextTick(() => {
    // 判断是否指定了新手引导插槽的高层级盒子继承父元素的全部css属性(最小程度的影响添加一个div布局的影响)
    if (props.isInheritStyle) {
      // 获取新手引导插槽的高层级盒子
      const targetSlot = document.querySelector('.targetSlot');
      targetSlot.className = 'inheritFather';
    }

    // 获取高亮盒子
    const highLightArea = document.querySelector(`.highLightArea_${uuid}`);
    // 判断高亮盒子是否存在
    if (highLightArea) {
      // 根据指定参数配置盒子的高亮区域尺寸
      highLightArea.style.width = `${props.highLightAreaWidth || 100}%`;
      highLightArea.style.height = `${props.highLightAreaHeight || 100}%`;
      highLightArea.style.top = `${props.highLightAreaTop || 50}%`;
      highLightArea.style.left = `${props.highLightAreaLeft || 50}%`;
    }
    // TODO:将显示提示放在setTimeout中,存入宏任务队列,避免顺序执行错误,应该等到highLightArea盒子全部处理完成后再显示提示区域,否则有可能显示的位置不对,这里设置是否显示提示信息表示为isFoucsArea.value,表示只给设置了新手引导插槽中添加提示,其余盒子不添加提示
    setTimeout(() => (visible.value = isFoucsArea.value), 100);
  });
};

watch(
  () => props.isTarget,
  () => {
    setTimeout(() => {
      visible.value = true;
    }, 1);
  }
);

onMounted(init);
</script>

<style>
.maskArea {
  position: fixed;
  top: 0px;
  left: 0px;
  width: 100vw;
  height: 100vh;
  background: rgba(0, 0, 0, 0.4);
  z-index: 1;
  cursor: default;
}
.handGif {
  position: absolute;
  left: 100%;
  width: 60px;
  height: 70px;
  transform: translateX(50%);
}

.targetSlot {
  position: relative;
  z-index: 10;
  display: flex;
  justify-content: center;
  align-items: center;
}

.inheritFather {
  all: inherit;
  z-index: 10;
}
.highLightArea {
  position: absolute;
  width: 100%;
  height: 100%;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  border: 3px solid #fff;
  border-radius: 5px;
  z-index: 9;
  font-size: 16px;
  pointer-events: none;
  padding: 10px;
}
</style>

5. 使用新手引导组件示例:

<!--
 * @Author: wangZhiyu <w3209605851@163.com>
 * @Date: 2024-07-09 13:44:13
 * @LastEditTime: 2024-07-09 15:49:38
 * @LastEditors: wangZhiyu <w3209605851@163.com>
 * @FilePath: \Vue3新手引导组件\Vue3-Tour\src\App.vue
 * @Descripttion: 
-->
<template>
  <div class="container">
    <w-tour @onRead="onRead" readBtn tipMessage="这是新手教程的第一步" :isTarget="step === 1" :value="open">
      <template #target>
        <el-button type="primary">按钮1</el-button>
      </template>
    </w-tour>

    <w-tour @onRead="onRead" readBtn tipMessage="这是新手教程的第二步" :isTarget="step === 2" :value="open">
      <template #target>
        <el-button type="primary">按钮2</el-button>
      </template>
    </w-tour>

    <w-tour @onRead="onRead" readBtn tipMessage="这是新手教程的第三步" :isTarget="step === 3" :value="open">
      <template #target>
        <el-button type="primary">按钮3</el-button>
      </template>
    </w-tour>
  </div>
</template>
<script setup>
import { ref } from 'vue';
const step = ref(1);
const open = ref(true);

const onRead = () => {
  step.value++;

  if (step.value === 4) {
    // step.value = 0;
    open.value = false;
  }
  console.log(step.value);
};
</script>

<style scoped>
.container {
  display: flex;
  justify-content: space-around;
  align-items: center;
  width: 500px;
  height: 100px;
  background-color: #7c7777;
  margin: 100px auto;
  padding: 10px;
}
</style>

6. 总结:

以上就是从0到1开发一个能简单使用的新手引导组件的全过程以及源码了,可能使用起来不是很方便,哈哈,这里不得不佩服那些开发通用组件的大佬,确实很不容易👍🏻👍🏻

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1918180.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

FastGPT连接OneAI接入网络模型

文章目录 FastGPT连接OneAI接入网络模型1.准备工作2.开始部署2.1下载 docker-compose.yml2.2修改docker-compose.yml里的参数 3.打开FastGPT添加模型3.1打开OneAPI3.2接入网络模型3.3重启服务 FastGPT连接OneAI接入网络模型 1.准备工作 本文档参考FastGPT的官方文档 主机ip接…

树的结构(b,b+树)

无论线性表&#xff0c;栈还是队列&#xff0c;都是一对一&#xff0c;查询的时候&#xff0c;效率较低&#xff0c;数据量比较的大的情况 1.树的定义 一种数据结构&#xff0c;有层次关系的集合&#xff0c;根朝上&#xff0c;叶朝下 除了根节点外&#xff0c;每个子节点都…

v-model指令详情——04

V-bind 和v-mode的区别和联系&#xff1a; 1.v-bind 和 v-mode 这两个指令都可以完成数据绑定 2.v-bind是单项数据绑定 data>试图 3.v-mode 是双向数据绑定 data<>试图 4.v-bind 可以使用在任何HTML标签当中。v-mode 只能使用在表单元素上例如&#xff1a; inp…

盘一盘3DGS对哪些行业影响最大?

在日新月异的科技时代&#xff0c;每一个新兴技术的诞生都像是宇宙中的新星&#xff0c;以其独特的光芒照亮并改变着世界。3DGS正是这样一项技术&#xff0c;它的出现无疑对传统3D视觉产生了巨大的冲击&#xff0c;也给各行各业带来了巨大的影响。作为CV界的新晋黑马&#xff0…

【游戏引擎之路】登神长阶(七)——x86汇编学习:凡做难事,必有所得

5月20日-6月4日&#xff1a;攻克2D物理引擎。 6月4日-6月13日&#xff1a;攻克《3D数学基础》。 6月13日-6月20日&#xff1a;攻克《3D图形教程》。 6月21日-6月22日&#xff1a;攻克《Raycasting游戏教程》。 6月23日-7月1日&#xff1a;攻克《Windows游戏编程大师技巧》。 7月…

数据结构-二叉搜索树与红黑树

4.二叉搜索树 又叫二叉查找树、有序二叉树、排序二叉树。树中任意一个结点&#xff0c;其左子树的每个节点值都要小于该节点&#xff0c;其右子树的每个节点值都要大于该节点 作用&#xff1a;能够进行快速查找、插入、删除操作 4.1 二叉搜索树的时间复杂度 注&#xff1a;二…

Python基础教学之五:异常处理与文件操作——让程序更健壮

Python基础教学之五&#xff1a;异常处理与文件操作——让程序更健壮 一、异常处理概念 1. 理解异常 异常是程序运行中发生的错误或意外情况&#xff0c;比如除以零、访问不存在的列表元素等。如果不进行处理&#xff0c;异常会导致程序终止运行。在编程过程中&#xff0c;我…

Java语言程序设计——篇三(2)

循环结构 概述1️⃣while循环例题讲解 2️⃣do-while循环例题讲解 &#x1f6a9;while循环与do-while循环区别3️⃣for循环例题讲解 4️⃣循环的嵌套&#x1f3ee;例题讲解 概述 ⭐️Java语言提供了4种循环结构&#xff1a; (1) while循环 (2) do-while循环 (3) for循环 (4)增…

wangEdtior富文本编辑器 结合element使用,博客必备,通俗易懂

效果展示 使用步骤 安装 npm install wangeditor/editor-for-vue --save创建自定义组件 引入组件并注册&#xff0c;使用组件 <template><div style"border: 1px solid #ccc;"><Toolbarstyle"border-bottom: 1px solid #ccc":editor"…

如何使用Vger对已经过身份验证的Jupyter实例进行安全检测

关于Vger Vger是一款功能强大的交互式命令行应用程序&#xff0c;广大研究人员可以利用Vger与已经过身验证的Jupyter实例进行交互&#xff0c;并对其执行人工智能或机器学习方面的安全检测操作。 使用场景 1、作为红队研究人员&#xff0c;当我们寻找到了Jupyter凭证之后&…

经验分享|如何维护自己的大数据信用?

在当今数字化时代&#xff0c;大数据信用成为越来越多人生活中不可或缺的一部分。大数据信用评估是基于个人的信用历史、信用行为、信用记录等信息&#xff0c;对个人信用状况进行综合评估的一种方式。良好的大数据信用可以帮助个人获得更多的金融服务、更高的信用额度、更低的…

一招解决 Windows 文件重命名时后缀名不显示的困扰

在日常使用电脑的过程中&#xff0c;我们经常需要对文件进行重命名。然而&#xff0c;有时你会发现&#xff0c;在 Windows 系统中重命名文件时&#xff0c;默认情况下不会显示文件的扩展名&#xff08;即后缀名&#xff09;&#xff0c;这可能会导致意外地更改文件类型或者混淆…

Flutter开发者必读:sp_util - SharedPreferences的终极解决方案

简介 sp_util 是一个在 Flutter 社区中广泛使用的轻量级、无依赖的 SharedPreferences 封装库。 SharedPreferences 是 Android 和 iOS 平台上用于存储少量数据的简单键值存储系统。 典型场景 sp_util 库在 Flutter 应用开发中的典型使用场景包括但不限于以下几点&#xff…

模块化沙箱:多场景零信任数据安全解决方案

在政企领域&#xff0c;不同场景和用户往往有着层次不一的安全需求。传统的单一安全解决方案已经无法满足这些复杂而多样化的需求&#xff0c;因此&#xff0c;高灵活性和高扩展性的模块化沙箱应运而生。这种新型数据安全产品通过“积木式”的模块组合&#xff0c;能够精确、灵…

Keysight 是德 DSAX93204A 高性能示波器

Keysight 是德 DSAX93204A 高性能示波器 DSAX93204A Infiniium 高性能示波器&#xff1a; 33 GHz 高带宽实时示波器 zui佳的 33 GHz 真正模拟带宽80 GSa/s 采样率&#xff0c;2 通道&#xff1b;40 GSa/s 采样率&#xff0c;4 通道zui深的存储深度――高达 2 Gpts 存储器&am…

怎么办?我的C盘又爆红了!别慌!博主手把手带你管理你的C盘空间~

怎么办&#xff1f;我的C盘又爆红了&#xff01;别慌&#xff01;博主手把手带你管理你的C盘空间~ 文章目录 怎么办&#xff1f;我的C盘又爆红了&#xff01;别慌&#xff01;博主手把手带你管理你的C盘空间~0. 在开始清理之前1. 推荐执行的操作1.1 清理系统缓存文件1.2 磁盘清…

极客天成ScaleFlash与南大通用数据库兼容互认证

近日&#xff0c;北京极客天成科技有限公司与天津南大通用数据技术股份有限公司完成产品兼容性互认证&#xff0c;测试结果表明&#xff0c;极客天成分布式存储软件[简称NVMatrix]V3.0与南大通用多模多态数据库管理系统[简称GBase 8c]V5产品兼容性良好、运行稳定。 北京极客天成…

第三期书生大模型实战营 第2关 Python 基础知识

第三期书生大模型实战营 第2关 Python 基础知识 第三期书生大模型实战营 第2关 Python 基础知识Python 基础函数定义常见的内置方法replace(old, new)lower()split() 字典dict 使用Python实现词频统计使用VSCode进行调试总结 第三期书生大模型实战营 第2关 Python 基础知识 Hel…

Python基础教学之三:函数与模块篇——实现代码重用和模块化

Python基础教学之三&#xff1a;函数与模块篇——实现代码重用和模块化 一、函数的定义与使用 1. 理解函数 函数是组织好的、可重复使用的、用来实现单一或相关联功能的代码块。它可以提高代码的重用性和程序的清晰度。 你可以定义一个由自己想要功能的函数&#xff0c;以下是…

星环科技知识平台TKH:引领企业构建高效AI基础设施,加速数智化转型新纪元

5月30-31日&#xff0c;2024向星力未来数据技术峰会期间&#xff0c;星环科技正式发布其最新人工智能基础设施产品——Transwarp Knowledge Hub星环知识平台&#xff08;以下简称TKH&#xff09;。该平台旨在为企业打通从人工智能基础设施建设到大数据、人工智能等研发应用的完…