vue-tour跨页面 漫游式引导全流程

2,093 阅读2分钟

跨页面跳转 卡点问题:

vue-tour跨页面引导时,会因为页面未加载完成=>获取不到下个步骤的dom,导致下个步骤无法被框选。

目标效果:

1.点击下一步时,自动点击相应按钮打开弹窗或跳转页面等

2.点击跳转页面时,等页面加载完毕才显示该步骤引导的UI


先附上vue-tour官方文档:github.com/pulsardev/v…

主要流程

  1. 安装vue-tour并在main.js中引入

  1. 在需要添加引导的所有组件的共同父组件中,配置v-tour

在vue中,所有组件的父组件一定有的是App.vue,官方默认也是写在App.vue;但如果写了layout等模板,在layout里配置也未尝不可,只要该父组件包含了要添加引导的所有页面子组件即可。

以下是v-tour的配置项:

  • steps(步骤对应的dom、显示文字和before UI步进函数)
  • options(包含按钮名称、是否高亮、是否打开debug等配置)
  • callbacks(回调函数,允许您在漫游的不同时刻执行自定义操作)

  1. 在第一步骤的组件完成渲染的时候,this.$tours['myTour'].start()开始漫游

跨页面跳转 解决流程

每个步骤都可以配置一个before函数,函数在该步骤的UI渲染之前调用,返回一个promise。

  1. 「父组件」:在步骤的before函数中,判断该步骤对应的DOM是否已存在,若不存在则 a.等待/跳转等 b.记录该操作 c.不resolve(即拦截了此次展示该步骤引导UI);若存在则resolve。

  1. 「跳转后的子组件」:在步骤DOM渲染结束之后(比如mounted、数据接口调用结束后等),通知父组件。

  1. 「父组件」:接到通知,根据记录的操作再次走上一步/下一步。此时before函数判断步骤的DOM已存在,成功展示步骤引导UI。

1. 安装vue-tour并在main.js中引入

npm install vue-tour

在main.js中引入

//main.js
import VueTour from 'vue-tour'
require('vue-tour/dist/vue-tour.css')
Vue.use(VueTour)

2. 在需要添加引导的所有组件的共同父组件中,配置v-tour

若需要自定义样式,请参考官方文档 Customizing the Template (如果用到了highlight,需要将highlight属性传递给v-step的props哦!)

以下使用默认样式演示:

//父组件 layout.vue / App.vue / ……
<template>
  <div>
     <!-- 记得绑定监听子组件事件 -->
    <router-view @domDone="domDone"></router-view>
    <v-tour name="myTour" :steps="steps" :options="myOptions" :callbacks="myCallbacks"></v-tour>
  </div>
</template>

<script>
export default {
  data() {
    return {
      steps: [
        {
          target: '#v-step-1', 
          content: `第一步:点击 <strong>XXXXX</strong> !`,
          //如需点击上一步,请配置
          before: type => new Promise((resolve, reject) => {
            //判断如果要展示第一步UI的时候,尚未跳转至第一步页面,则跳转
            if (this.$route.path !== '/first-page') {
              //标识正在操作步骤的类型
              this.pendingStatus = type;
              //跳转页面
              this.$router.push('/first-step-page')
            }
            //如果已跳转至第一步页面,则成功展示第一步
            else if (this.$route.path === '/first-step-page') {
              resolve('foo')
            }
          })
        },
        {
          target: '#v-step-2',
          content: '第二步:XXXXX',
          before: type => new Promise((resolve, reject) => {
            //判断如果要展示第二步UI的时候,尚未跳转至第二步页面,则跳转
            if (this.$route.name !== 'second-step-page') {
              //标识正在操作步骤的类型
              this.pendingStatus= type;
              //点击按钮跳转页面
              document.getElementById('v-step-1').click()
            } 
            //如果已跳转至第二步页面,则成功展示第二步
            else if (this.$route.path === '/second-step-page') {
              //记录Cookies,按需取舍
              this.setHadTourCookies()
              resolve('foo')
            }
          })
        }
      ],
      myOptions: {
        useKeyboardNavigation: false,   //是否使用键盘控制引导
        labels: {                       //配置按钮显示名称
          buttonSkip: '跳过引导',
          buttonPrevious: '上一步',
          buttonNext: '下一步',
          buttonStop: '完成引导'
        },
        highlight: true,//步骤Dom是否需要高亮,默认高亮是一个小shadow,若需要大遮罩请看style设置
      },
      myCallbacks: {
        //跳过引导的时候也记录Cookies
        onSkip: this.setHadTourCookies,
      },
      pendingStatus:'',  // 添加一个标志来记录页面跳转状态
    }
  },
  mounted() {
    //按需判断何时开始引导
    if (!Cookies.get('hadTour')) {
      //一定要在第一步骤渲染完,才开始引导,可以放在第一步骤所在组件的mounted
      this.$tours['myTour'].start()
    }
  },
  methods: {
    setHadTourCookies() {
      //此处设置Cookies,按个人需求取舍
      Cookies.set('hadTour', true, {
        expires: new Date(new Date().getTime() + 24 * 60 * 60 * 1000),
      })
    },
    //新页面时,domDone获取页面加载成功的信息,并手动调用下一步的方法
    domDone() {
      //按pendingStatus控制上一步下一步
      if (this.pendingStatus === 'next') {
        this.$tours['myTour1'].nextStep()
        this.pendingStatus = '';
      } else if (this.pendingStatus === 'previous') {
        this.$tours['myTour1'].previousStep()
        this.pendingStatus = '';
      }
    }
  },
</script>

<style lang="scss" scoped>
//官方推荐的写法,将shadow设置很大,即变成了遮罩(感觉还挺机智的)
.v-tour__target--highlighted {
  box-shadow: 0 0 0 99999px rgba(0, 0, 0, .4) !important; 
}
</style>
//子组件
<template>
  <div>
    <div class="item" id="v-step-2">步骤二</div>
  </div>
</template>

<script>
export default {
  mounted() {
    //页面对应的dom加载结束时,通知父组件
    //不一定在mounted,也可以是数据接口调用完成后,按个人需求放置
    if (this.$tours['myTour']) {
      this.$emit('domDone')
    }
  },
</script>