前言
最近在使用qiankun微服务架构,有个需要给系统加引导的需求,然后就决定使用intro.js。也是看了掘金的一位博主的帖子(链接如下)。
但是我要讲的内容有,如何在微服务中进行主模块和子模块的完美引导,样式修改,以及踩的一些坑。
再附上一个官网的链接 introjs.com/
注意
这个插件是GNU AGPLv3 也就是说如果你商用那是要公开源码的,或者你得买他的版权(网上查的不知道对不对) 反正我是花钱买了授权9.99刀,当然当时为了想省这六十几块人民币也试过同类型的插件driver.js但是区别还是蛮大的。ps:别问我怎么知道的因为我用两个插件都写了一遍。 这里在多说一点把如果你是小项目,且要求不是很高,功能不是很多,可以选择Dirver。毕竟他开源免费。当然你要是觉得这个点钱不算什么别犹豫直接intro.js。
基本用法
先说使用这个基本和上面的博主相同当时也发现了一些更好的使用技巧。这是封装的代码。下面具体细节一一讲解。
import introJs from 'intro.js'
import 'intro.js/introjs.css'
// import router from '@/router'
import Vuex from '@/store'
let count = 0 // 初始化为0,后面会根据每一步传入的dom的id选择器 判断当前在第几步
// let state = 0 // skip: 0 ,done: 1;
export function guide(introSteps: Array<any>) {
let isSkip: boolean = false
const sesionData = JSON.parse(localStorage.getItem('isIntro') || 'null')
if (localStorage.getItem('isIntro') && sesionData.state === 0) {
return
}
introJs()
.setOptions({
steps: introSteps,
/* 当位置选择自动的时候,位置排列的优先级 */
positionPrecedence: ['right', 'top', 'bottom', 'left'],
prevLabel: '上一步',
nextLabel: '下一步',
skipLabel: '<div/>',
// 这里因为每个页面的最后一步其实就会显示doneLabel了,而实际上我们的新手引导操作并没有完成,需要跳转路由继续,所以显示的是下一步而非完成。
doneLabel: '下一步',
hidePrev: true,
exitOnOverlayClick: false,
/* 是否显示说明的数据步骤*/
showStepNumbers: false,
/* 是否使用点点点显示进度 */
showBullets: false,
/* 是否使用键盘Esc退出 */
exitOnEsc: false,
/* 默认提示位置 */
hintPosition: 'top-right',
/* 说明高亮区域的样式 */
highlightClass: 'customHighlight',
/* 引导说明文本框的样式 */
tooltipClass: 'customTooltip',
})
.onchange(function (targetElement: any) {
// 每次change时触发,我的id样式是“guide-step<id>”
// id代表新手引导的第几步,例如“guide-step1”,通过id判断当前是第几步
count = parseInt(targetElement.id.slice(-1))
})
.onafterchange(function (targetElement: any) {
// 跳转完成后用js的dom操作将doneLabel由“下一步”改为“完成”
if (count === 1) {
document.getElementsByClassName('introjs-fullbutton')[0].innerHTML = '开始吧'
}
if (count === 2) {
Vuex.commit('set_data', {
guideChange: 'XXX',
})
}
})
.oncomplete(function (targetElement: any) {
// 点击完成以及跳过按钮后执行的事件,不区分完成和跳过。
console.log('这个是跳出或者完成')
})
.onexit(function (targetElement: any) {
// 点击完成按钮后, 执行的事件
if (isSkip) {
return
}
setTimeout(() => {
judgeCount()
}, 200)
})
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
.onskip(function (targetElement: any) {
// 点击跳过按钮后, 执行的事件
// 存储进缓存,下次进入新手引导路由页时将不在触发Intro.js
isSkip = true
localStorage.setItem('isIntro', JSON.stringify({ nextGuideName: '', state: 0 }))
})
.start()
}
// 这个是每一步新手引导对应的点击下一步时对应应该进行的操作
function judgeCount() {
switch (count) {
case 1:
case 2:
guide([
{
title: 'XX模板',
intro:
'XX模板 中有丰富的模板,您可以“使用模板” 快速制作。当然,您也可以新建一个空白的XX。',
element: document.querySelector('#guide-step3'),
position: 'right',
},
])
break
case 3:
localStorage.setItem('isIntro', JSON.stringify({ nextGuideName: 'page1', state: 1 }))
window.postMessage({ startGuide: true }, '*')
break
case 4:
localStorage.setItem('isIntro', JSON.stringify({ nextGuideName: 'page2', state: 1 }))
window.postMessage({ startCRMGuide: true }, '*')
break
}
}
以下代码是放在你所需要的引导的.vue页面中,将他放入mounted中,vue2和3都可以,或者放入函数中通过点击事件触发都可以。
let i = setInterval(() => {
guide([
{
// title: '新手教程',
intro:
'Hi,我是您的小助理,您可以随时点击这里召唤我,接下来让我带您熟悉一下操作流程吧!',
element: document.querySelector('#guide-step1'),
position: 'right',
},
{
title: 'XX中心',
intro:
'左侧的“XX”菜单里包含 XXX、XX、XXX 三大应用,现在让我们从“XXX”应用开始了解吧!',
element: document.querySelector('#guide-step2'),
position: 'right',
},
])
clearInterval(i)
}, 500)
以上主要是之前那位博主的思路,我加了些vue3+ts的写法,我的跨页面跨服务也基本采用他的思路,具体没看明白的可以先看他那篇。链接在最开头。
分布详解
除了以上原博主封装的方法外,还可以这么玩。
直接在每一步的对象内,写自定义的属性值,然后在封装的js里来做处理。
这边用来处理刚刚写的自定义的属性值 .onexit(function () {
// 点击完成按钮后, 执行的事件
const { callback } = introSteps[count - 1] //结构刚刚添加的callback属性值
if (callback) {
handleCallback(callback)
}
})
这边用来处理刚刚自定义方法的回调
// 处理callback回调方法
function handleCallback(callback) {
switch (callback) {
case 'openKnowledgeManagementMenuEdit':
localStorage.setItem('startGuide', 'edit')
openXXXXXXXXX()
break
case 'XXXXXXXXXXXXXXXXXX':
localStorage.setItem('XXXXXXXXXXXX', 'XXXXXXXXXXX')
openXXXXXX()
break
}
}
下面我来说下qiankun中微服务,主框架和子框架如何通过联动经行引导。
两个方法:
- 通过缓存经行信息传递和触发。亲测比较麻烦不方便。
- 通过window.postMessage
传递模块
//在需要传递的框架中.onexit来触发这个方法
case 4:
localStorage.setItem('isIntro', JSON.stringify({ nextGuideName: 'page2', state: 1 }))
window.postMessage({ startCRMGuide: true }, '*')
break
接收模块
//在子框架中你需要触发引导的界面中使用
window.addEventListener('message', (message) => {
const { data } = message
if (data && data.startGuide) {
guide([
{
// title: ''XX中心',
intro:
'左侧的“XX”菜单里包含 XXX、XX、XXX 三大应用,现在让我们从“XXX”应用开始了解吧!',
element: document.querySelector('#guide-step1'),
position: 'right',
},
{
title: 'XX中心',
intro:
'左侧的“XX”菜单里包含 XXX、XX、XXX 三大应用,现在让我们从“XXX”应用开始了解吧!',
element: document.querySelector('#guide-step2'),
position: 'right',
},
])
}
})
用法主要就是传递这块。
样式修改
使用qiankun的框架的话所有的样式直接写到主框架里就行了
例如 想修改跳出按钮的位置和样式,直接在如下图跳出按钮内添加一个标签就行(我这里加了个div)
css如下,这个就不解释了应该看的懂,直接放入主框架全局样式
.introjs-skipbutton {
position: fixed;
right: 45%;
top: 20px;
>div {
width: 56px;
height: 74px;
background: url(./XXXXX.png) no-repeat;
background-size: cover;
cursor: pointer;
}
}
如果你想隐藏某个按钮也可以通过样式来直接控制,按照intro的每个按钮类名。
例如:想隐藏上一步这个按钮
//上一步按钮
.introjs-prevbutton {
display: none !important;
}
坑
下面来说一说坑
如下这个方法onskip官方API是没有公布出来的,我也是看了原博主的文章也去翻源码了,发现确实是有。
你要是vue2且没用ts的话,和原博主一样直接用就行了。但你要是VUE3+ts那可要了命了他官方的@type/intro.js是没有暴露这个方法的会一直报错。必须加上下面的注释才能在编译和打包的时候不出错。(唉,说多了都是泪,居然两行注释解决了)
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
.onskip(function (targetElement: any) {
// 点击跳过按钮后, 执行的事件
// 存储进缓存,下次进入新手引导路由页时将不在触发Intro.js
isSkip = true
localStorage.setItem('isIntro', JSON.stringify({ nextGuideName: '', state: 0 }))
})
第二个坑
就是引选中一个按钮,被选中的按钮依然是可以点击的
比如这个是一个按钮的引导,这个编辑依然可以点击,这个时候点击就会出现一些问题,那如何来阻止。
相信不少朋友肯定和我一样想到用css的pointer-events,或者用js来阻止。
但是真的不用,就在我就纠结的时候我同事发现只需调整z-index就可以了,显示效果不变。
//只需加入这个就可以阻止
.introjs-helperLayer {
z-index: 9999999 !important;
}
第三个坑 如果你在引导的过程中有些自动的弹窗出现,它是不会关闭的,需要手动关闭。
//可以在需要的地方加上这个两行
import introJs from 'intro.js'
introJs().exit()
准备下班,今天就写到这里,本来想继续写intro.js和Dirver.js的区别。