# hz-novice-guidance 引导组件

一个操作引导组件，支持`vue2`和`vue3`，组件灵活，配置丰富。目前已测试了`h5`、`微信小程序`、`支付宝小程序`，其他平台可自行尝试。

## 功能概述

- ✅通过配置自定义步骤
- ✅支持配置到子组件、孙组件
- ✅支持自定义箭头
- ✅支持自定义功能按钮
- ✅支持拦截【下一步】【完成】【跳过】，并自定义行为

## 配置项

### 组件参数
| 配置项 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| baseConfig | Object | {} | 步骤配置，具体配置见下方 |
| steps | Array | [] | 步骤配置，具体配置见下方 |

### baseConfig 配置项
| 配置项 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| highligthStyle | Object | - | 高亮框自定义样式，可通过设置 --bgcolor 修改遮罩背景颜色 |
| tipMaxWidth | String | - | 提示框最大宽度，默认 '70vw' |
| tipStyle | Object | - | 提示框自定义样式 |
| arrowStyle | Object | - | 步骤提示箭头样式 |
| outerBtn | Boolean | false | 【跳过】【下一步】按钮是否在提示框内显示，为 true 时显示在提示框外 |
| customOuterBtn | Boolean | false | 是否自定义按钮，当 outerBtn=true 时生效，为 true 时，包裹按钮的父级元素的 left 值会变成 0，方便进行定位|
| next | String | '下一步' | 下一步按钮文字，当为最后一步时，会显示 '完成'，如需修改，可在steps中最后一步设置next值 |
| nextStyle | Object | - | 下一步按钮样式 |
| skip | String | '跳过' | 跳过按钮文字 |
| skipStyle | Object | - | 跳过按钮样式 |
| showSkip | Boolean | true | 是否显示跳过按钮 |
| customArrow | Boolean | false | 是否自定义箭头 |
| autoRotateArrow | Boolean | true | 箭头是否自动旋转 |
| tipContentMaxHeight | String | '' | 提示内容最大高度，当提示内容大于该值时，可进行滚动查看。默认自动计算，不会超出屏幕 |
| beforeNext | Function | - | 全局步骤守卫，步骤改变前触发，可用于拦截跳转或自定义行为。 回调参数：({to, from, index, next, skipStep}) |
| beforeSkip | Function | - | 跳过守卫，【跳过】前触发，可用于拦截跳过。 回调参数：({skip, index, from, skipStep}) |
| beforeFinished | Function | - | 完成守卫，【完成】前触发，可用于拦截完成。 回调参数：({finish, index, from, skipStep }) |

### steps 配置项
| 配置项 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| el | String | '' | 元素选择器，支持类名、id、标签名 |
| tips | String | '' | 提示内容 |
| context | Array/Function | [] | 上下文,当元素在子组件中时，需要传入上下文，组件的 ref 值（从外到内依次传入组件的ref名称） |
| style | Object | - | 当前步骤高亮框元素样式，会覆盖 highligthStyle |
| offset | Object | {top: 0, left: 0} | 元素偏移信息，在当元素位置基础上进行偏移，单位为 px |
| tipStyle | Object | - | 提示框自定义样式，该值会覆盖 baseConfig 中的 tipStyle |
| arrowStyle | Object | - | 步骤提示箭头样式，width/height单位应为 px |
| baseAlign | String | 'left' | 步骤提示提示框相对于高亮元素对齐方式（最终效果会根据提示框的宽度来确定对齐方式，只是这个值的优先级会更高），可选值有left、center、right |
| next | String | '下一步' | 同 baseConfig 中的 next |
| nextStyle | Object | - | 同 baseConfig 中的 nextStyle |
| showSkip | Boolean | true | 同 baseConfig 中的 showSkip |
| skip | String | '跳过' | 同 baseConfig 中的 skip |
| skipStyle | Object | - | 同 baseConfig 中的 skipStyle |
| customArrow | Boolean | false | 同 baseConfig 中的 customArrow |
| autoRotateArrow | Boolean | true | 同 baseConfig 中的 autoRotateArrow |
| tipContentMaxHeight | String | '' | 提示内容最大高度，当提示内容大于该值时，可进行滚动查看。默认自动计算，不会超出屏幕 |
| beforeNext | Function | - | 步骤守卫，当前步骤改变前触发，可用于拦截跳转或自定义行为。 回调参数：({to, from, index, next, skipStep})。注：baseConfig 中的 beforeNext 也会触发且先于步骤中的 beforeNext|

### 组件事件
| 事件名 | 说明 | 回调参数 |
| --- | --- | --- |
| next | 点击下一步按钮时触发 | stepIndex: 当前步骤索引 |
| finished | 点击跳过按钮时触发 | - |
| skiped | 引导结束时触发 | - |

### 组件方法
| 方法名 | 说明 | 参数 |
| --- | --- | --- |
| start | 开始引导 | stepIndex: 步骤索引 |
| next | 下一步 | - |
| skip | 跳过 | - |
| finish | 结束 | - |
| skipStep | 跳转任意步骤 | (stepIndex: number, {globalBeforeNextDisabled: boolean, beforeNextHandleDisabled: boolean}) ：stepIndex-步骤索引 globalBeforeNextDisabled-跳转时是否禁用全局的beforeNext函数 beforeNextHandleDisabled-跳转时是否禁用当前步骤的beforeNext函数|
| setConfig | 设置参数，由于小程序不能通过组件参数传递函数，当配置中有函数时，需调用该方法进行传递配置 | (steps, baseConfig) |

### 插槽
| 插槽名 | 说明 |
| --- | --- |
| tips | 自定义内容插槽 |
| btns | 自定义操作按钮插槽 |
| arrow | 自定义箭头插槽， customArrow 为 true 时生效 |

## 使用示例
这里介绍了一些基本使用，更详细的使用可下载`示例项目`查看。ps: 文档示例使用的是 vue3 版本，示例项目为 vue2 版本。
### 1. 基本使用
```vue
<template>
  <view class="demo-step guide-step1" style="background: #000;"></view>
  <view class="demo-step guide-step2" style="background: #ff5500;"></view>
  <view class="demo-step guide-step3" style="background: #1a6840;"></view>
  <hz-novice-guidance ref="refNoviceGuidance" :steps="steps" @finished="finished" @next="handleNext" @skiped="skiped">
	</hz-novice-guidance>
</template>

<script setup>
const refNoviceGuidance = ref()
const steps = [
  {
    el:'.guide-step1',
    tips:'新手引导组件，使用 vue3 + ts 开发，这里是组件说明',
  },
  {
    el:'.guide-step2',
    tips:`内容由 <span style='color:#ff5500;'>rich-text</span> 组件渲染，所以支持简单的HTML标签`,
  },
  {
    el:'.guide-step3',
    next:'朕知道了，下一个',
    nextStyle:{
      background:'#ff5500'
    },
    skip:'朕会了，跳过',
    skipStyle:{
      color:'#ff5500'
    },
    tips:'自定义按钮样式及文字',
  },
]

const finished = ()=>{
  console.log('引导结束')
}

const handleNext = (stepIndex)=>{
  console.log('点击下一步', stepIndex)
}

const skiped = ()=>{
  console.log('点击跳过')
}


onMounted(() => {
  // 小程序需添加 setTimeout 
  setTimeout(() => {
    refNoviceGuidance.value.start()
  }, 0)
})
</script>

<style>
.demo-step {
  width: 100%; 
  height: 100px; 
  margin-bottom: 20px;
}
</style>
```

### 2. 配置到子组件

```vue
<!-- 页面组件（vue3开发的支付宝小程序，组件需加上额外参数 hz-ref-name，且值和 ref 值一致）-->
<template>
	<father ref="refFather" hz-ref-name="refFather"></father>
</template>

<!--  father组件 -->
<template>
  <view class="father-container ">
    <view>我是父组件</view>
    <child ref="refChild" hz-ref-name="refChild">
    </child>
  </view>
</template>

<!-- child组件 -->
<template>
  <view class="child-container">
    <view>
      我是子组件
    </view>
    <grandson ref="refGrandson" hz-ref-name="refGrandson"></grandson>
  </view>
</template>

<!-- grandson组件 -->
<template>
  <view class="grandson-container">
    我是孙组件
  </view>
</template>

<script setup>
const steps = [
  {
    // 孙组件中元素类名
    el: '.grandson-container',
    tips: '孙组件，el 值为孙组件中的类名',
    // 传数组，值为每层组件的 ref 名称
    context: ['refFather', 'refChild', 'refGrandson'],
  },
  {
    // 子组件中元素类名
    el: '.child-container',
    tips: '子组件',
    // 传函数，返回子组件实例
    context: ['refFather', 'refChild']
  },
]
</script>
```
#### 支付宝小程序兼容性问题
如果是`vue3`开发的`支付宝小程序`，则需在相关子组件上加上额外参数`hz-ref-name`，值和 ref 的一致，否则支付宝小程序不生效。
### 3. 自定义提示框内容、样式
```vue
<template>
	<hz-novice-guidance ref="refNoviceGuidance1" :baseConfig="baseConfig" :steps="steps">
		<template #tips="{scope}">
			<view class="custom-tips">
				我是自定义的插槽内容。<br/>
				这是配置的原内容：<rich-text :nodes="scope.currentEle.tips"></rich-text><br/>
				<text style="color: red;">注意：通过 baseConfig 传给组件的 tipStyle 样式任然生效。</text>
			</view>
		</template>
	</hz-novice-guidance>
</template>

<script setup>
// 整体配置
const baseConfig = {
  highligthStyle:{
    // 配置高亮框背景颜色
    '--bgcolor': 'rgba(0, 0, 0, 0.8)',
  },
  // 提示内容样式
  tipStyle:{
    background: '#fecc11',
    color:'#1a6840',
  },
  // 箭头样式
  arrowStyle:{
    color: '#fecc11',
  }
}

const steps = [
  {
    el:'.guide-step6',
    tips:`步骤传入 tipStyle 参数将覆盖 baseConfig 中的值`,
    tipStyle:{
      background: '#1a6840',
      color:'#fff'
    },
    arrowStyle:{
      color: '#1a6840',
    },
  },
  {
    el:'.guide-step4',
    tips:`通过插槽 <span style='color:#ff5500;'>slot='tips'</span> 可完全自定义内容`,
  },
]
</script>
```
### 4. 自定义箭头样式
```vue
<template>
	<hz-novice-guidance ref="refNoviceGuidance" :baseConfig="baseConfig" :steps="steps" >
		<template #arrow>
			<view class="custom-arrow">
				<view class="arrow"></view>
				<view class="arrow-line"></view>
				<view class="arrow-head"></view>
			</view>
		</template>
	</hz-novice-guidance>
</template>

<script setup>
// 整体配置
const baseConfig = {
  // 箭头样式
  arrowStyle:{
    color: '#fecc11',
    width:36,
    height:24,
  }
}

const steps = [
  {
    el:'.guide-step13',
    tips:`步骤参数中的 arrowStyle 会覆盖组件的 baseConfig 中的值`,
    arrowStyle:{
      color: '#ff5500',
      width:12,
      height:8,
    }
  },
  {
    el:'.guide-step5',
    tips:`使用箭头插槽`,
    customArrow:true
  },
  {
    el:'.guide-step6',
    tips:`不自动旋转箭头`,
    autoRotateArrow:false,
    customArrow:true
  },
]
</script>
```

### 5. 自定义按钮样式
##### 修改文字、样式
```vue
<template>
	<hz-novice-guidance ref="refNoviceGuidance" :baseConfig="baseConfig" :steps="steps" ></hz-novice-guidance>
</template>

<script setup>
// 整体配置
const baseConfig = {
  next:'next',
  nextStyle:{
    background: '#ff5500',
  },
  skip:'skip',
  skipStyle:{
    color: '#ff5500',
  },
}
const steps = [
  {
    el:'.guide-step1',
    tips:'通过参数或插槽控制按钮样式',
    showSkip:false,
  },
  {
    el:'.guide-step3',
    tips:`可通过参数，控制按钮的文字、样式`,
    next:'done'
  },
]
</script>
```

##### 按钮展示在外部
```vue
<template>
	<hz-novice-guidance ref="refNoviceGuidance" :baseConfig="baseConfig" :steps="steps" ></hz-novice-guidance>
</template>

<script setup>
// 整体配置
const baseConfig = {
  outerBtn: true,
}
const steps = [
  {
    el:'.guide-step4',
    tips:`按钮可显示在提示框外部`,
  },
]
</script>
```

##### 使用插槽自定义按钮
```vue
<template>
	<hz-novice-guidance ref="refNoviceGuidance" :baseConfig="baseConfig" :steps="steps" >
		<template #btns="{scope}">
			<view class="custom-btns-outertips">
				<view class="skip-btn" @click="customSkip" v-if="scope.currentStep < steps3.length - 1">跳过</view>
				<view class="next-btn" @click="customNext">{{scope.currentStep < steps3.length - 1 ? '下一步' : '完成'}}</view>
			</view>
		</template>
	</hz-novice-guidance>
</template>

<script setup>
// 整体配置
const baseConfig = {
  outerBtn: true,
}
const steps = [
  {
    el:'.guide-step5',
    tips:`通过插槽 <span style='color:#ff5500;'>slot='btns'</span> 可完全自定义按钮`,
  },
  {
    el:'.guide-step15',
    tips:`通过调用组件的 next、skip、finished 方法控制引导流程`,
  },
]

const customNext = () => {
  refNoviceGuidance.value.next()
}
const customSkip = () => {
  refNoviceGuidance.value.skip()
}
</script>
```

## 常见问题
1. 目前只测试过h5、微信小程序、支付宝小程序，其他平台大家可自行测试。
2. el 值应是唯一的，最好使用class选择器（如 `.guide-step1`）。
3. 该组件暂不适用于高度过大的元素，可能会导致提示框显示不全。
3. 小程序端，由于通过组件参数不能直接传递函数，当配置中有函数时，需调用组件 `setConfig` 方法进行传递配置。

## 兼容性问题
##### 1. 如果是`vue3`开发的`支付宝小程序`，且需配置到子组件时，则需在相关所有子组件上加上额外参数`hz-ref-name`，值和 ref 的一致，否则支付宝小程序不生效。
```vue
<template>
<!-- 页面组件（vue3开发的支付宝小程序，组件需加上额外参数 hz-ref-name，且值和 ref 值一致）-->
	<father ref="refFather" hz-ref-name="refFather"></father>
</template>
<!-- father组件 -->
<template>
  <view class="father-container ">
    <view>我是父组件</view>
    <child ref="refChild" hz-ref-name="refChild">
    </child>
  </view>
</template>

<script setup>
const steps = [
  {
    el: '.child-container',
    tips: '子组件',
    context: ['refFather', 'refChild']
  },
]
</script>
```

##### 2. 使用`vue3`开发并编译到`支付宝小程序`时，如果使用插槽且使用了插槽传值，会导致其他插槽的默认内容无法显示。该问题官方暂未修复。
```vue
<!-- 这种写法在 vue3 并编译到支付宝小程序，会导致提示内容不展示 -->
<hz-novice-guidance>
	<template #btns="{scope}"></template>
</hz-novice-guidance>

<!--  这种写法正常显示 -->
<hz-novice-guidance>
<template #btns></template>
</hz-novice-guidance>

```

