vue3封装el-tour漫游式引导

vue3使用el-tour漫游式引导组件封装

我们这里先看下运行效果:

预览效果

引入的第三方库方法我这里就不写了,可以自行去官网查找。

我们看一下组件封装的部分。

tourPage.vue
html 复制代码
  <el-tour v-model="open" :show-close="false" @change="onStepChange" :content-style="{
      width: '80%',
      fontSize: '16px',
      color: '#252525',
    }">
      <el-tour-step v-for="(item, index) in data" :key="index" :target="item.target" :title="item.title ? item.title : `第${currentStep}步`" :description="item.description" :prev-button-props="{
        children: '上一步',
        onClick: handlePrevClick
      }" :next-button-props="{
        children: nextBtnName,
        onClick: handleNextClick
      }" />
      <template #indicators="{ current, total }">
        <span style="font-size: 16px; color: #252525;">{{ current + 1 }} / {{ total }}</span>
      </template>
    <!--   <template #indicators>
        <el-button size="small" @click="handleSkip">跳过</el-button>
      </template> -->
    </el-tour>

数据结构:

ts 复制代码
const tourData = computed(() => [
  {
    title: "功能引导",
    description: "让我们来学习一下",
  },
  {
    title: "功能按钮",
    description: "这里有每日一言和票房大全功能入口",
    target: () => ref1.value,
  },
  {
    title: "个人中心",
    description: "点击头像可以打开个人中心,更换头像和昵称,还可以和机器人对话",
    target: () => ref2.value,
  },
  {
    title: "新年彩蛋",
    description: "一会可以点击这里,看看有什么惊喜",
    target: () => ref3.value,
  },
  {
    title: "关于文案",
    description: "表面可以查看文案,从这里点击进入可以开启音乐",
    target: () => ref4?.value?.$el || null
  },
  {
    title: "抽奖",
    description: "点击这里可以进入抽奖页面,试试手气吧",
    target: () => ref5.value,
  },
]);

这里需要注意一下第四个部分,我这里因为要直接绑定第三方组件Vant的van-notice-bar的所以需要使用Vant中自带的NoticeBarInstance 类型

js 复制代码
import type { NoticeBarInstance } from 'vant';

所以在定义的时候需要这么写

js 复制代码
const ref4 = ref<NoticeBarInstance>()          // 文案通知栏
// 数据
target: () => ref4?.value?.$el || null

如果是引导饿了么的组件获取dom类型可以从官网上查看,比如绑定在el-button按钮上

ts 复制代码
import type { ButtonInstance } from 'element-plus'
const ref4 = ref<ButtonInstance>()   
// 数据
{
    title: "关于文案",
    description: "表面可以查看文案,从这里点击进入可以开启音乐",
    target: () => ref4?.value?.$el || null
},

如果不用官网的方法也可以直接使用js方法获取,当然你也不需要NoticeBarInstance 类型了

js 复制代码
const ref4 = ref<HTMLElement | null>(null)
// 数据
{
    title: "关于文案",
    description: "表面可以查看文案,从这里点击进入可以开启音乐",
    target: () => document.querySelector('.van-notice-bar'),
},

自定义按钮文字

html 复制代码
      <el-tour-step
        v-for="(item, index) in data"
        :key="index"
        :target="item.target"
        :title="item.title ? item.title : `第${currentStep}步`"
        :description="item.description"
        :prev-button-props="{
          children: '上一步',
          onClick: handlePrevClick
        }"
        :next-button-props="{
          children: nextBtnName,
          onClick: handleNextClick
        }"
      />

组件完整代码:

html 复制代码
<script setup lang="ts">
import { ref, computed, PropType } from "vue";
import type { ButtonInstance } from 'element-plus'
import type { NoticeBarInstance } from 'vant';
// 引导步骤项目接口定义
interface TourItem {
  title: string;                   // 步骤标题
  description: string;             // 步骤描述
  target?: () => NoticeBarInstance | HTMLElement | null | undefined | any;  // 目标元素
}
// Props 定义
const props = defineProps({
  data: {
    type: Array as PropType<TourItem[]>,
    required: true
  },
  modelValue: {
    type: Boolean,
    default: false
  }
})

// 事件发射器
const emits = defineEmits(['change', 'prev', 'next', 'update:modelValue'])

// 引导组件显示状态(双向绑定)
const open = computed({
  get: () => props.modelValue,
  set: (value) => emits('update:modelValue', value)
})

// 当前引导步骤
const currentStep = ref(0);

// 下一步按钮文本
const nextBtnName = computed(() => {
  let name = ''
  if (!currentStep.value) {
    name = '开始'
  } else if (currentStep.value === props.data.length - 1) {
    name = '完成'
  } else {
    name = `下一步(${currentStep.value} / ${props.data.length - 1})`
  }
  return name
})

// 步骤变化处理函数
const onStepChange = (step: number) => {
  currentStep.value = step
  emits('change', step)
}

// 跳过按钮处理函数(暂未启用)
/* const handleSkip = () => {
  open.value = false
} */

// 上一步按钮处理函数
const handlePrevClick = () => {
  emits('prev', currentStep.value)
}

// 下一步按钮处理函数
const handleNextClick = () => {
  emits('next', currentStep.value)
}
</script>

<template>
  <div>
    <el-tour v-model="open" :show-close="false" @change="onStepChange" :content-style="{
      width: '80%',
      fontSize: '16px',
      color: '#252525',
    }">
      <el-tour-step
        v-for="(item, index) in data"
        :key="index"
        :target="item.target"
        :title="item.title ? item.title : `第${currentStep}步`"
        :description="item.description"
        :prev-button-props="{
          children: '上一步',
          onClick: handlePrevClick
        }"
        :next-button-props="{
          children: nextBtnName,
          onClick: handleNextClick
        }"
      />
      <template #indicators="{ current, total }">
        <span style="font-size: 16px; color: #252525;">{{ current + 1 }} / {{ total }}</span>
      </template>
      <!-- 跳过按钮(暂未启用) -->
      <!-- <template #indicators>
        <el-button size="small" @click="handleSkip">跳过</el-button>
      </template> -->
    </el-tour>
  </div>
</template>

<style scoped></style>

这里的记步条可以用插槽indicators去做,详细的写法看上述代码即可

最后在父组件使用:

html 复制代码
    <TourPage
      v-model="tourVisible"
      :data="tourData"
      @change="(step) => console.log('当前步骤:', step)"
      @prev="(step) => console.log('上一步:', step)"
      @next="(step) => console.log('下一步:', step)"
    />
    <el-button color="#626aef" @click="startTour">
      快速引导
    </el-button>
js 复制代码
const tourVisible = ref(false)
// 点击时开启
const startTour = () => {
  tourVisible.value = true
}

数据附下

js 复制代码
// 引导步骤配置
const tourData = computed(() => [
  {
    title: "功能引导",
    description: "让我们来学习一下",
  },
  {
    title: "功能按钮",
    description: "这里有每日一言和票房大全功能入口",
    target: () => ref1.value,
  },
  {
    title: "个人中心",
    description: "点击头像可以打开个人中心,更换头像和昵称,还可以和机器人对话",
    target: () => ref2.value,
  },
  {
    title: "新年彩蛋",
    description: "一会可以点击这里,看看有什么惊喜",
    target: () => ref3.value,
  },
  {
    title: "关于文案",
    description: "表面可以查看文案,从这里点击进入可以开启音乐",
    target: () => ref4?.value?.$el || null
  },
  {
    title: "抽奖",
    description: "点击这里可以进入抽奖页面,试试手气吧",
    target: () => ref5.value,
  },
]);
相关推荐
韩立学长1 天前
【开题答辩实录分享】以《基于Vue的非遗文化知识分享平台的设计与实现》为例进行选题答辩实录分享
前端·javascript·vue.js
优弧1 天前
离开舒适区100天,我后悔了吗?
前端·后端·面试
胡gh1 天前
css的臂膀,前端动效的利器,还是布局的“隐形陷阱”?
前端·css·html
灵感菇_1 天前
Flutter Riverpod 完整教程:从入门到实战
前端·flutter·ui·状态管理
用户21411832636021 天前
紧急修复!Dify CVE-2025-55182 高危漏洞,手把手教你升级避坑
前端
Vic101011 天前
解决 Spring Security 在异步线程中用户信息丢失的问题
java·前端·spring
wordbaby1 天前
Expo (React Native) 最佳实践:TanStack Query 深度集成指南
前端·react native
~无忧花开~1 天前
Vue二级弹窗关闭错误解决指南
开发语言·前端·javascript·vue.js
软件技术NINI1 天前
前端面试题:请描述一下你对盒模型的理解
前端
码事漫谈1 天前
VS Code终端从入门到精通完全指南
前端·后端