最近工作中需要开发一个微信小程序的功能:微信小程序首页需要增加用户引导操作(重要的地方给出引导提示,可以通过下一步进行提示,或者直接跳过。而且是首次操作之后(跳过)就不需要再弹出了)。研究了一下,为了方便之后的页面都可以用,遂做成组件的方式,一次开发,到处都可用。
1. 效果展示 #

2. 实现方式 #
2.1 组件化设计 #
- 该插件基于微信小程序的 Component 组件化机制实现。
- 将页面引导功能封装为独立的组件,便于复用和维护。
2.2 核心逻辑 #
- 数据绑定: 通过 data 属性存储引导步骤的相关信息(如 guideList、index 等)。
- 动态样式计算: 根据 DOM 元素的位置和尺寸,动态调整提示框的位置和样式。
- 事件驱动: 通过用户交互(如点击“下一步”或“跳过”按钮),触发状态更新和界面变化。
2.3 关键方法 #
- viewTips 方法:
- 根据当前步骤的 DOM 信息,动态调整提示框的位置和样式。
- 计算三角形箭头的位置,并将其应用到样式中。
- getDomInfo 方法:
- 使用 wx.createSelectorQuery 获取目标 DOM 的位置和尺寸信息。
- 调用 viewTips 方法完成提示框的渲染。
- getStyle 方法:
- 动态生成提示框的主要样式(如宽度、高度、定位等)。
- skip 和 next 方法:
- 分别用于跳过引导或进入下一步。
- 当到达最后一步时,隐藏引导层并保存状态到本地存储。
2.4 生命周期管理 #
- attached 阶段:
- 初始化组件数据(如 guideList 和 stepName)。
- 检查本地存储,判断是否需要显示引导。
- detached 阶段:
- 在组件销毁时清理资源(目前未实现具体逻辑)。
3. 功能特点 #
3.1 多步骤引导 #
- 支持定义多个引导步骤,每个步骤包含提示内容、目标 DOM 元素等信息。
- 用户可以通过“下一步”按钮逐步完成引导。
3.2 动态适配 #
- 根据屏幕尺寸和目标 DOM 的位置,动态调整提示框的位置和样式。
- 如果目标 DOM 的宽度超过屏幕宽度,则自动缩小其展示宽度。
3.3 跳过功能 #
- 提供“跳过”按钮,允许用户直接跳过整个引导流程。
- 跳过后的状态会保存到本地存储,避免重复显示。
3.4 样式自定义 #
- 提示框的样式(如背景颜色、字体大小等)可通过 CSS 自定义。
- 提供了两种字体(pingfang_re 和 pingfang),增强视觉效果。
3.5 遮罩层 #
- 在引导过程中,添加遮罩层防止用户误操作。
3.6 不重复引导、多页面都可引用 #
- 页面中step.name变量(对应组件中stepName,用于标识当前引导步骤的名称。),它的作用是通过存储在本地缓存中的值来判断是否需要再次显示引导信息。
- 单页面首次进入点击完成下一步后,或者跳过后不会再次引导。
- 不同的页面通过对step.name的设置可以做到相互隔离,每个页面都可以对组件进行使用(只有首次才会引导)。
4. 如何使用 #
4.1 引入组件 #
- 将 tour 组件文件夹(包含 index.js、index.wxml 和 index.wxss)复制到项目中。
- 在需要使用的页面或组件中声明引用:
{
"usingComponents": {
"tour": "/components/tour/index"
}
}
4.2 配置引导步骤 #
- 定义 guideList,描述每个步骤的目标 DOM 和提示内容。例如:
const step = {
name: 'step1',
guideList: [
{
el: '#demo1',
tips: '这是第一个功能',
littletips: '点击这里试试看'
},
{
el: '#demo2',
tips: '这是第二个功能',
littletips: '可以在这里设置参数'
}
]
};
4.3 绑定组件属性 #
- 在 WXML 中使用组件,并传递 step 属性:
<tour step="{{step}}" />
4.4 在页面上需要引导的位置的class上增加对应的el #
<view class="b_btn_item demo1" bindtap="toFiveBlessings" data-ed="1">
<image class="b_btn_img" src="../../../images/home/b_btn1.png"></image>
<view class="b_btn_text">诚信用工承诺企业</view>
<image class="b_btn_arrow" src="../../../images/home/b_arrow.png"></image>
</view>
<view class="b_btn_item demo2" bindtap="toFiveBlessings" data-ed="2">
<image class="b_btn_img" src="../../../images/home/b_btn2.png"></image>
<view class="b_btn_text">劳务协作服务站点</view>
<image class="b_btn_arrow" src="../../../images/home/b_arrow.png"></image>
</view>
4.5 运行效果 #
- 当用户首次访问页面时,如果本地存储中未记录该引导步骤的状态,则会显示引导层。
- 用户可以通过“下一步”按钮逐步完成引导,或直接点击“跳过”按钮结束引导。
5. 注意事项 #
- 目标 DOM 必须存在
- guideList 中的 el 属性必须指向页面中存在的 DOM 元素,否则可能导致查询失败。
- 样式冲突
- 插件中的样式可能会与项目其他样式发生冲突,建议在引入前检查并调整。
- 性能优化
- 如果引导步骤较多,可能会影响页面加载性能。建议按需加载组件。
6. 实战代码 #
6.1 参照代码 #
6.1.1 插件代码 #
index.wxml
<!--wxml-->
<view class="guide" wx:if="{{showGuide}}">
<view style="{{guideStyle}}" class="guide-box">
<view class="tips guide-step-tips" style="{{tipPosition}}">
<view class="text">{{ guideList[index].tips }}</view>
<view class="tool-btn">
<text bind:tap="skip">跳过</text>
<view class="next" style="" bind:tap="next">下一步</view>
</view>
</view>
<view class="arrow" style="{{arrowTop}}"></view>
</view>
<!-- 遮罩层,防止点击 -->
<view class="v-model"></view>
</view>
index.json
{
"component": true,
"usingComponents": {}
}
index.js
// created by li.ba
Component({
/**
* 组件的属性列表
*/
properties: {
step: {
type: Object,
default: () => {},
},
},
/**
* 组件的初始数据
*/
data: {
stepName: "step", //该提示步骤的名称,用于不在重复展示
guideList: [],
index: 0, // 当前展示的索引
showGuide: true, // 是否显示引导
guideStyle: "", // 默认样式
arrowTop: "", //步骤提示三角形的定位
tipPosition: "", //步骤提示的定位
systemInfo: "", //屏幕宽度高度等信息
tipWidth: 200, //步骤提示默认的宽度
},
/**
* 组件的方法列表
*/
methods: {
// 展示新手提示
viewTips(data, scrollTop) {
console.log(data, scrollTop);
let {
systemInfo,
tipWidth,
index,
guideList,
arrowTop,
tipPosition,
guideStyle,
} = this.data;
if (data) {
console.log(systemInfo);
// 如果dom宽度大于或者等于窗口宽度,需要重新调整dom展示宽度
let newWidth = systemInfo.windowWidth - 20;
if (data.width >= newWidth) {
data.width = newWidth;
}
// 如果距离左边为0,自动增加一点左边距
if (data.left == 0) {
data.left = 10;
}
let domRW = systemInfo.windowWidth - data.left;
let left = 0;
// 如果dom距离右边没有tips的宽度大的话,就要让tips向左便宜
if (domRW < tipWidth) {
left = domRW - tipWidth - 30;
}
// const index = index;
// 步骤条展示的高度需要加上屏幕滚动的高度
data.top += scrollTop;
// 根据实际情况需要滚动到展示区域
wx.pageScrollTo({
scrollTop: data.top > 20 ? data.top - 20 : 0,
duration: 100,
});
let obj = Object.assign(guideList[index], data);
// 设置三角形高度
let arrArrowTop = data.height + 9;
arrowTop = "top:" + arrArrowTop + "px;";
// 设置提示框定位
tipPosition = "top:" + (arrArrowTop + 5) + "px;left:" + left + "px;";
// 重新设置guideList的值
guideList.splice(index, 1, obj);
guideStyle = this.getStyle();
console.log(arrowTop, tipPosition, guideList, guideStyle);
this.setData({
arrowTop,
tipPosition,
guideList,
guideStyle,
});
} else {
index += 1;
this.setData({
index,
});
this.getDomInfo();
}
},
// 获取步骤提示的主要样式
getStyle() {
const { guideList, index } = this.data;
const { width, height, left, top, style } = guideList[index];
let newstyle = "width:" + width + "px;";
newstyle += "height:" + height + "px;";
newstyle += "left:" + left + "px;";
newstyle += "top:" + top + "px;";
newstyle +=
"box-shadow: rgb(33 33 33 / 80%) 0px 0px 0px 0px, rgb(33 33 33 / 50%) 0px 0px 0px 5000px;";
newstyle += style;
return newstyle;
},
// 获取dom信息
getDomInfo() {
const { guideList, index } = this.data;
console.log(guideList, index);
const { el } = guideList[index];
console.log(el);
// const query = wx.createSelectorQuery().in(this.$root);
const query = wx.createSelectorQuery();
// console.log(query);
setTimeout(() => {
query.select(el).boundingClientRect();
query.selectViewport().scrollOffset();
var _this = this;
query.exec(function (res) {
console.log("打印demo的元素的信息", res);
let data = res[0]; // #the-id节点的上边界坐标
let scrollTop = res[1].scrollTop; // 显示区域的竖直滚动位置
_this.viewTips(data, scrollTop);
});
}, 10);
},
skip() {
this.setData({
showGuide: false,
});
wx.setStorageSync(this.data.stepName, "true");
},
// 下一步
next() {
let { index, guideList, stepName } = this.data;
if (index === guideList.length - 1) {
this.setData({
showGuide: false,
});
wx.setStorageSync(stepName, "true");
} else {
index += 1;
this.setData({
index,
});
this.getDomInfo();
}
},
},
lifetimes: {
attached: function () {
console.log(this.properties);
const { step } = this.properties;
let { guideList, stepName } = this.data;
guideList = step.guideList;
stepName = step.name;
// const systemInfo = wx.getSystemInfoSync();
// systemInfo = systemInfo;
this.setData({
guideList,
stepName,
systemInfo: wx.getSystemInfoSync(),
});
const guide = wx.getStorageSync(step.name);
if (!guide) {
this.getDomInfo();
} else {
this.setData({
showGuide:false,
});
// this.showGuide = false;
}
},
detached: function () {
// 在组件实例被从页面节点树移除时执行
},
},
});
index.wxss
/* wxss */
.v-model {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
z-index: 1000;
}
.guide {
z-index: 1001;
}
.guide-box {
position: absolute;
z-index: 10001;
transition: all 0.2s;
}
.guide-box::before {
content: '';
height: 100%;
width: 100%;
border: 1px dashed #fff;
border-radius: 8rpx;
position: absolute;
top: -8rpx;
left: -8rpx;
padding: 7rpx;
}
.arrow {
height: 20rpx;
width: 20rpx;
background: #1cbbb4;
position: absolute;
top: 144rpx;
left: 45%;
transform: rotate(45deg);
}
.tips {
width: 400rpx;
background: linear-gradient(180deg, #1cbbb4, #0081ff);
box-shadow: 0px 2px 9px 0px rgba(0, 0, 0, 0.1);
color: #fff;
position: absolute;
top: 152rpx;
left: -50%;
padding: 15rpx 20rpx;
font-size: 28rpx;
border-radius: 12rpx;
}
.tool-btn {
display: flex;
justify-content: space-between;
align-items: center;
padding-right: 0rpx;
margin-top: 20rpx;
}
.next {
background: #fff;
height: 48rpx;
width: 100rpx;
text-align: center;
border-radius: 8rpx;
color: #666;
line-height: 48rpx;
font-size: 24rpx
}
6.1.2 页面代码 #
homePage.wxml
<view class="neui-base-view index-bg" style="background-image: url('../../../images/home/home_top_bg.png');background-size: 100% 100%">
<!-- 导航栏 -->
<view class="neui-navigation" style="height: calc({{safeTop}}px + 80rpx);">
<!-- 安全区域占位 -->
<view class="neui-navigation-safe" style="height: {{safeTop}}px"></view>
<view class="neui-navigation-bar">
<image class="home_icon" src="../../../images/home/home_icon.png"></image>
<view class="home_title_text">福建跨省劳务协作系统</view>
</view>
</view>
<view class="page-main">
<view class="search">
<image class="icon_search" src="../../../images/home/home_search_icon.png"></image>
<input class="input_search" bindtap='search' value="{{keyword}}" placeholder-class="phcolor" placeholder="请输入搜索关键字" ></input>
<view class="btn_search" bindtap='search'>搜索</view>
</view>
<view class="top_btn_area">
<view class="top_btn_item content0" style="margin-right: 54rpx;" bindtap="toHot" data-ed="grqzdj">
<image src="../../../images/home/home_qzdj.png"></image>
<view>求职登记</view>
</view>
<view class="top_btn_item" style="margin-right: 54rpx;" bindtap="toHot" data-ed="zwxqfa">
<image src="../../../images/home/home_zwfb.png"></image>
<view>职位发布</view>
</view>
<view class="top_btn_item content1" style="margin-right: 54rpx;" bindtap="toHot" data-ed="lwhdbm">
<image src="../../../images/home/home_lwhd.png"></image>
<view>劳务活动</view>
</view>
<view class="top_btn_item" bindtap="toHot" data-ed="fgfgbm">
<image src="../../../images/home/home_fgfg.png"></image>
<view>返工返岗</view>
</view>
</view>
<view class="middle_btn_area">
<view class="m_btn_item content2" style="background-color: #e5f4ff;" bindtap="toFW" data-ed="zgz">
<image class="m_img1" src="../../../images/home/m_btn_wyqz.png"></image>
<view>我要求职</view>
</view>
<view class="m_btn_item" style="background-color: #fff3e5;" bindtap="toFW" data-ed="zrc">
<image class="m_img1" src="../../../images/home/m_btn_wyzq.png"></image>
<view>我要招聘</view>
</view>
<view class="m_btn_item" style="background-color: #e6fbf7;" bindtap="toFW" data-ed="wyff">
<image class="m_img1" src="../../../images/home/m_btn_wyfw.png"></image>
<view>我要服务</view>
</view>
<view class="m_btn_item" style="background-color: #f6f4ff;" bindtap="toFW" data-ed="wyzc">
<image class="m_img1" src="../../../images/home/m_btn_wyzc.png"></image>
<view>我要政策</view>
</view>
</view>
<view class="bottom_btn_area" style="background-image: url(../../../images/home/home_five.png);">
<view class="b_content">
<view class="b_btn_item content3" bindtap="toFiveBlessings" data-ed="1">
<image class="b_btn_img" src="../../../images/home/b_btn1.png"></image>
<view class="b_btn_text">诚信用工承诺企业</view>
<image class="b_btn_arrow" src="../../../images/home/b_arrow.png"></image>
</view>
<view class="b_btn_item" bindtap="toFiveBlessings" data-ed="2">
<image class="b_btn_img" src="../../../images/home/b_btn2.png"></image>
<view class="b_btn_text">劳务协作服务站点</view>
<image class="b_btn_arrow" src="../../../images/home/b_arrow.png"></image>
</view>
<view class="b_btn_item" bindtap="toFiveBlessings" data-ed="3">
<image class="b_btn_img" src="../../../images/home/b_btn3.png"></image>
<view class="b_btn_text">以老带新引工大使</view>
<image class="b_btn_arrow" src="../../../images/home/b_arrow.png"></image>
</view>
<view class="b_btn_item" bindtap="toFiveBlessings" data-ed="4">
<image class="b_btn_img" src="../../../images/home/b_btn4.png"></image>
<view class="b_btn_text">诚信招工人资机构</view>
<image class="b_btn_arrow" src="../../../images/home/b_arrow.png"></image>
</view>
<view class="b_btn_item" bindtap="toFiveBlessings" data-ed="5">
<image class="b_btn_img" src="../../../images/home/b_btn5.png"></image>
<view class="b_btn_text">线上对接数字引工</view>
<image class="b_btn_arrow" src="../../../images/home/b_arrow.png"></image>
</view>
</view>
</view>
</view>
</view>
<tabBar selected="0"></tabBar>
<guideStep step="{{step}}"> </guideStep>
<!--组件可以放在任意位置,建议放在页面的底部 -->
homePage.json
{
"usingComponents": {
"tabBar":"/components/custom-tab-bar2/custom-tab-bar2",
"guideStep":"/components/tour/index"
},
"navigationStyle": "custom"
}
homePage.js
const app = getApp();
Page({
/**
* 页面的初始数据
*/
data: {
safeTop: 0,
aab301:'35',
keyword:'',
step: {
name: "workbenchKey",
guideList: [
{
el: ".content0",
tips: "快速登记 轻松上岗",
style: "border-radius: 8rpx;margin:0"
},
{
el: ".content1",
tips: "招聘会找工作",
style: "border-radius: 8rpx;margin:0"
},
{
el: ".content2",
tips: "求职三步轻松办",
style: "border-radius: 8rpx;margin:0"
},
{
el: ".content3",
tips: "明星企业招聘",
style: "border-radius: 8rpx;margin:0"
}
]
},
},
search:function(){
var self = this;
wx.navigateTo({
url: '/packageRlzy/website/job/job_list/jobList?aab301=' + self.data.aab301
})
},
toHot:function(e){
var that = this;
var fw_name = e.currentTarget.dataset.ed;
if(fw_name=="grqzdj"){
if (app.isLogin()) {
wx.navigateTo({
url: '/packageRlzy/job/person/center/person_resume/personresume_list/personresume_list'
})
}else{
that.toLogin();
}
}else if(fw_name=="lwhdbm"){
wx.redirectTo({
url: '/pages/rlzy/recruitment_list/recruitmentList',
})
}else if(fw_name=="zwxqfa"){
if(app.isLogin() && app.isAuthenticatedCom()){
wx.redirectTo({
url: '/packageRlzy/job/company/center/position_list/position_list',
})
}else{
that.setData({
dialogShow:true,
dialogCon:'只有单位账号登录后发布可职位需求!'
})
return;
}
}else if(fw_name=="fgfgbm"){
wx.navigateTo({
url: '/packageLaborCooperation/website/returntowork/returntowork'
})
}
},
toFW:function(e){
var that = this;
var fw_name = e.currentTarget.dataset.ed;
if(fw_name=="zgz"){
wx.redirectTo({
url: '/packageRlzy/website/job/job_list/jobList',
})
}else if(fw_name=="wyff"){
wx.redirectTo({
url: '/pages/rlzy/recruitment_list/recruitmentList',
})
}else if(fw_name=="wyzc"){
wx.redirectTo({
url: '/packageWebsite/pages/website/news_list/news_list',
})
}
else if(fw_name=="zrc"){
if (!(app.isLogin() && app.isAuthenticated())) {
that.setData({
dialogShow:true,
dialogCon:'只有单位账号登录后可查看务工信息!'
})
return;
}else if(wx.getStorageSync('userinfo').usertype!='1'){
wx.showToast({
title: '个人用户无法查看务工信息',
icon: 'none',
duration: 2000
});
return;
}
wx.redirectTo({
url: '/packageRlzy/website/resume/resume_list/resume_list',
})
}else if(fw_name=="zbzp"){
wx.redirectTo({
url: '/packageRlzy/website/specialColumn/detail/detail',
})
}else if(fw_name=="ztlm"){
wx.redirectTo({
url: '/packageRlzy/website/specialColumn/main/main',
})
}else if(fw_name=="zczx"){
wx.redirectTo({
url: '/packageWebsite/pages/website/news_list/news_list',
})
} else if (fw_name == "rlzyfwjg") {
wx.navigateTo({
url: '/packageWebsite/pages/website/rlzyserviceagency/agencyinformation?acd10y=1',
})
} else if (fw_name == "lwxzfwjg") {
wx.navigateTo({
url: '/packageWebsite/pages/website/rlzyserviceagency/agencyinformation?acd10y=2',
})
}else if(fw_name=="fwpp"){
wx.navigateTo({
url: '/packageWebsite/pages/website/fiveblessing/fiveblessing'
})
}
},
toFiveBlessings:function(e){
var that = this;
var fw_name = e.currentTarget.dataset.ed;
if(fw_name=="1"){
wx.navigateTo({
url: '/packageTrain/website/creditenterprise/creditenterpriselist/creditenterpriselist',
})
}else if(fw_name=="2"){
wx.navigateTo({
url: '/packageWebsite/pages/website/rlzyserviceagency/agencyinformation?acd10y=2',
})
}else if(fw_name=="3"){
wx.navigateTo({
url: '/packageTrain/website/fiveblessings/attractworklist/attractworklist',
})
}else if(fw_name=="4"){
wx.navigateTo({
url: '/packageWebsite/pages/website/rlzyserviceagency/agencyinformation?acd10y=1',
})
}else if(fw_name=="5"){
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
this.setData({
safeTop: wx.getWindowInfo().safeArea.top
})
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})
homePage.wxss
page {
background-color: #f6f7fb;
font-family: 'pingfang';
}
@font-face {
font-family: 'pingfang';
src: url("http://10.0.66.101:3322/wechatimages/PINGFANG BOLD.TTF")
}
.neui-base-view {
width: 100%;
background-color: white;
display: flex;
flex-wrap: wrap;
overflow-y: scroll;
padding-bottom: calc(env(safe-area-inset-bottom) + 450rpx);
overflow-x: hidden;
}
.index-bg {
background-repeat: no-repeat;
background-color: #f6f7fb;
}
.neui-navigation {
width: 100%;
}
.neui-navigation-safe {
width: 100%;
}
.neui-navigation-bar {
width: 100%;
height: 80rpx;
display: flex;
align-items: center;
}
.home_icon{
width: 39rpx;
height: 32rpx;
margin-left: 31rpx;
}
.home_title_text {
font-family: 'pingfang';
height: 36rpx;
font-size: 36rpx;
line-height: 36rpx;
color: #ffffff;
margin-left: 12rpx;
}
.page-main {
width: 100%;
overflow: hidden;
display: flex;
flex-direction: column;
align-items: center;
position: absolute;
top: 200rpx;
}
.search {
width: 690rpx;
height: 96rpx;
background-color: #ffffff;
border-radius: 20rpx;
/* margin: 0 auto; */
}
.icon_search {
float: left;
width: 40rpx;
height: 40rpx;
margin-top: 28rpx;
margin-left: 31rpx;
}
.input_search{
float: left;
width: 500rpx;
height: 96rpx;
margin-left: 28rpx;
}
.btn_search {
float: left;
font-family: 'pingfang';
width: 60rpx;
height: 96rpx;
font-size: 30rpx;
line-height: 96rpx;
color: #0280ff;
}
.top_btn_area {
width: 635rpx;
height: 149rpx;
margin-top: 40rpx;
}
.top_btn_item {
float: left;
width: 119rpx;
height: 149rpx;
}
.top_btn_item image {
width: 100rpx;
height: 100rpx;
background-color: #ffffff;
border-radius: 30rpx;
margin-left: 9rpx;
}
.top_btn_item view {
width: 119rpx;
height: 40rpx;
font-size: 29rpx;
line-height: 40rpx;
color: #ffffff;
margin-top: 5rpx;
}
.middle_btn_area {
width: 690rpx;
height: 350rpx;
background-color: #ffffff;
border-radius: 30px;
margin-top: 213rpx;
}
.m_btn_item {
float: left;
margin-top: 30rpx;
margin-left: 30rpx;
width: 300rpx;
height: 130rpx;
border-radius: 15rpx;
}
.m_img1 {
float:left;
width: 60rpx;
height: 56rpx;
margin-top: 37rpx;
margin-left: 30rpx;
}
.m_btn_item view {
float: left;
margin-left: 21rpx;
width: 134rpx;
height: 130rpx;
font-size: 34rpx;
line-height: 130rpx;
color: #000000;
}
.bottom_btn_area {
width: 690rpx;
height: 810rpx;
background-size: 690rpx 810rpx;
background-repeat: no-repeat;
margin-bottom: 200rpx;
}
.b_content {
padding-left: 40rpx;
padding-top: 80rpx;
}
.b_btn_item {
width: 610rpx;
height: 96rpx;
margin-top: 40rpx;
}
.b_btn_img {
float: left;
width: 112rpx;
height: 114rpx;
margin-top: -9rpx;
}
.b_btn_text {
float: left;
width: 270rpx;
height: 96rpx;
font-size: 34rpx;
line-height: 96rpx;
color: #000000;
margin-left: 34rpx;
}
.b_btn_arrow {
width: 46rpx;
height: 46rpx;
float: right;
margin-top: 25rpx;
}
6.2 效果展示的代码 #
6.2.1 插件代码 #
index.wxml
<!--wxml-->
<view class="guide" wx:if="{{showGuide}}">
<view style="{{guideStyle}}" class="guide-box">
<view class="tips guide-step-tips" style="{{tipPosition}}">
<view class="text bigtips">{{ guideList[index].tips }}</view>
<view class="text smalltips">{{ guideList[index].littletips }}</view>
<view class="tool-btn">
<text class="skipclass" bind:tap="skip">跳过</text>
<view class="next" style="" bind:tap="next">{{index == guideList.length - 1 ? '完成' : '下一步'}}</view>
</view>
</view>
<view class="arrow" style="{{arrowTop}}"></view>
</view>
<!-- 遮罩层,防止点击 -->
<view class="v-model"></view>
</view>
index.json
{
"component": true,
"usingComponents": {}
}
index.js
// created by li.ba
Component({
/**
* 组件的属性列表
*/
properties: {
step: {
type: Object,
default: () => {},
},
},
/**
* 组件的初始数据
*/
data: {
stepName: "step", //该提示步骤的名称,用于不在重复展示
guideList: [],
index: 0, // 当前展示的索引
showGuide: true, // 是否显示引导
guideStyle: "", // 默认样式
arrowTop: "", //步骤提示三角形的定位
tipPosition: "", //步骤提示的定位
systemInfo: "", //屏幕宽度高度等信息
tipWidth: 200, //步骤提示默认的宽度
},
/**
* 组件的方法列表
*/
methods: {
// 展示新手提示
viewTips(data, scrollTop) {
console.log(data, scrollTop);
let {
systemInfo,
tipWidth,
index,
guideList,
arrowTop,
tipPosition,
guideStyle,
} = this.data;
if (data) {
console.log(systemInfo);
// 如果dom宽度大于或者等于窗口宽度,需要重新调整dom展示宽度
let newWidth = systemInfo.windowWidth - 20;
if (data.width >= newWidth) {
data.width = newWidth;
}
// 如果距离左边为0,自动增加一点左边距
if (data.left == 0) {
data.left = 10;
}
let domRW = systemInfo.windowWidth - data.left;
let left = 0;
// 如果dom距离右边没有tips的宽度大的话,就要让tips向左便宜
if (domRW < tipWidth) {
left = domRW - tipWidth - 30;
}
// const index = index;
// 步骤条展示的高度需要加上屏幕滚动的高度
data.top += scrollTop;
// 根据实际情况需要滚动到展示区域
wx.pageScrollTo({
scrollTop: data.top > 20 ? data.top - 180 : 0,
duration: 100,
});
let obj = Object.assign(guideList[index], data);
// 设置三角形高度
let arrArrowTop = data.height + 9;
arrowTop = "top:" + arrArrowTop + "px;";
// 设置提示框定位
tipPosition = "top:" + (arrArrowTop + 5) + "px;left:" + left + "px;";
// 重新设置guideList的值
guideList.splice(index, 1, obj);
guideStyle = this.getStyle();
console.log(arrowTop, tipPosition, guideList, guideStyle);
this.setData({
arrowTop,
tipPosition,
guideList,
guideStyle,
});
} else {
index += 1;
this.setData({
index,
});
this.getDomInfo();
}
},
// 获取步骤提示的主要样式
getStyle() {
const { guideList, index } = this.data;
const { width, height, left, top, style } = guideList[index];
let newstyle = "width:" + width + "px;";
newstyle += "height:" + height + "px;";
newstyle += "left:" + left + "px;";
newstyle += "top:" + top + "px;";
newstyle +=
"box-shadow: rgb(33 33 33 / 80%) 0px 0px 0px 0px, rgb(33 33 33 / 50%) 0px 0px 0px 5000px;";
newstyle += style;
return newstyle;
},
// 获取dom信息
getDomInfo() {
const { guideList, index } = this.data;
console.log(guideList, index);
const { el } = guideList[index];
console.log(el);
// const query = wx.createSelectorQuery().in(this.$root);
const query = wx.createSelectorQuery();
// console.log(query);
setTimeout(() => {
query.select(el).boundingClientRect();
query.selectViewport().scrollOffset();
var _this = this;
query.exec(function (res) {
console.log("打印demo的元素的信息", res);
let data = res[0]; // #the-id节点的上边界坐标
let scrollTop = res[1].scrollTop; // 显示区域的竖直滚动位置
_this.viewTips(data, scrollTop);
});
}, 10);
},
skip() {
this.setData({
showGuide: false,
});
wx.setStorageSync(this.data.stepName, "true");
},
// 下一步
next() {
let { index, guideList, stepName } = this.data;
if (index === guideList.length - 1) {
this.setData({
showGuide: false,
});
wx.setStorageSync(stepName, "true");
} else {
index += 1;
this.setData({
index,
});
this.getDomInfo();
}
},
},
lifetimes: {
attached: function () {
console.log(this.properties);
const { step } = this.properties;
let { guideList, stepName } = this.data;
guideList = step.guideList;
stepName = step.name;
// const systemInfo = wx.getSystemInfoSync();
// systemInfo = systemInfo;
this.setData({
guideList,
stepName,
systemInfo: wx.getSystemInfoSync(),
});
const guide = wx.getStorageSync(step.name);
if (!guide) {
this.getDomInfo();
} else {
this.setData({
showGuide:false,
});
// this.showGuide = false;
}
},
detached: function () {
// 在组件实例被从页面节点树移除时执行
},
},
});
index.wxss
/* wxss */
@font-face {
font-family: 'pingfang_re';
src: url("http://10.0.66.101:3322/wechatimages/PINGFANG REGULAR.TTF")
}
@font-face {
font-family: 'pingfang';
src: url("http://10.0.66.101:3322/wechatimages/PINGFANG BOLD.TTF")
}
.v-model {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
z-index: 1000;
}
.guide {
z-index: 1001;
}
.guide-box {
position: absolute;
z-index: 10001;
transition: all 0.2s;
}
.guide-box::before {
content: '';
height: 100%;
width: 100%;
border: 1px dashed #fff;
border-radius: 8rpx;
position: absolute;
top: -8rpx;
left: -8rpx;
padding: 7rpx;
}
.arrow {
height: 20rpx;
width: 20rpx;
background: #6bbaff;
position: absolute;
top: 144rpx;
left: 45%;
transform: rotate(45deg);
}
.tips {
width: 400rpx;
/* background: linear-gradient(180deg, #1cbbb4, #0081ff); */
background: linear-gradient(90deg,
#75c4ff 0%,
#3b7ffe 100%);
box-shadow: 0px 2px 9px 0px rgba(0, 0, 0, 0.1);
color: #fff;
position: absolute;
top: 152rpx;
left: -50%;
padding: 27rpx 20rpx;
font-size: 28rpx;
border-radius: 12rpx;
}
.tool-btn {
display: flex;
justify-content: space-between;
align-items: center;
padding-right: 0rpx;
margin-top: 20rpx;
}
.next {
/* background: #fff; */
/* height: 48rpx; */
/* width: 100rpx; */
/* text-align: center; */
/* border-radius: 8rpx; */
/* color: #666; */
/* line-height: 48rpx; */
/* font-size: 24rpx; */
font-family: 'pingfang_re';
width: 120rpx;
height: 50rpx;
line-height: 50rpx;
background-color: #ffffff;
border-radius: 25rpx;
text-align: center;
color: #0195ff;
}
.bigtips {
font-family: 'pingfang';
font-weight: bold;
font-size: 30rpx;
line-height: 50rpx;
}
.smalltips {
font-family: 'pingfang_re';
font-size: 24rpx;
line-height: 50rpx;
}
.skipclass {
font-family: 'pingfang_re';
width: 100rpx;
height: 50rpx;
line-height: 50rpx;
background-color: #65b3ff;
border-radius: 25rpx;
border: solid 1px #ffffff;
text-align: center;
}
6.2.2 页面代码 #
homePage.wxml
<view class="neui-base-view index-bg" style="background-image: url('../../../images/home/home_top_bg.png');background-size: 100% 100%">
<!-- 导航栏 -->
<view class="neui-navigation" style="height: calc({{safeTop}}px + 80rpx);">
<!-- 安全区域占位 -->
<view class="neui-navigation-safe" style="height: {{safeTop}}px"></view>
<view class="neui-navigation-bar">
<image class="home_icon" src="../../../images/home/home_icon.png"></image>
<view class="home_title_text">福建跨省劳务协作系统</view>
</view>
</view>
<view class="page-main">
<view class="search">
<image class="icon_search" src="../../../images/home/home_search_icon.png"></image>
<input class="input_search" bindtap='search' value="{{keyword}}" placeholder-class="phcolor" placeholder="请输入搜索关键字" ></input>
<view class="btn_search" bindtap='search'>搜索</view>
</view>
<view class="top_btn_area">
<view class="top_btn_item content0" style="margin-right: 54rpx;" bindtap="toHot" data-ed="grqzdj">
<image src="../../../images/home/home_qzdj.png"></image>
<view>求职登记</view>
</view>
<view class="top_btn_item" style="margin-right: 54rpx;" bindtap="toHot" data-ed="zwxqfa">
<image src="../../../images/home/home_zwfb.png"></image>
<view>职位发布</view>
</view>
<view class="top_btn_item content1" style="margin-right: 54rpx;" bindtap="toHot" data-ed="lwhdbm">
<image src="../../../images/home/home_lwhd.png"></image>
<view>劳务活动</view>
</view>
<view class="top_btn_item" bindtap="toHot" data-ed="fgfgbm">
<image src="../../../images/home/home_fgfg.png"></image>
<view>返工返岗</view>
</view>
</view>
<view class="middle_btn_area">
<view class="m_btn_item content2" style="background-color: #e5f4ff;" bindtap="toFW" data-ed="zgz">
<image class="m_img1" src="../../../images/home/m_btn_wyqz.png"></image>
<view>我要求职</view>
</view>
<view class="m_btn_item" style="background-color: #fff3e5;" bindtap="toFW" data-ed="zrc">
<image class="m_img1" src="../../../images/home/m_btn_wyzq.png"></image>
<view>我要招聘</view>
</view>
<view class="m_btn_item" style="background-color: #e6fbf7;" bindtap="toFW" data-ed="wyff">
<image class="m_img1" src="../../../images/home/m_btn_wyfw.png"></image>
<view>我要服务</view>
</view>
<view class="m_btn_item" style="background-color: #f6f4ff;" bindtap="toFW" data-ed="wyzc">
<image class="m_img1" src="../../../images/home/m_btn_wyzc.png"></image>
<view>我要政策</view>
</view>
</view>
<view class="bottom_btn_area" style="background-image: url(../../../images/home/home_five.png);">
<view class="b_content">
<view class="b_btn_item content3" bindtap="toFiveBlessings" data-ed="1">
<image class="b_btn_img" src="../../../images/home/b_btn1.png"></image>
<view class="b_btn_text">诚信用工承诺企业</view>
<image class="b_btn_arrow" src="../../../images/home/b_arrow.png"></image>
</view>
<view class="b_btn_item" bindtap="toFiveBlessings" data-ed="2">
<image class="b_btn_img" src="../../../images/home/b_btn2.png"></image>
<view class="b_btn_text">劳务协作服务站点</view>
<image class="b_btn_arrow" src="../../../images/home/b_arrow.png"></image>
</view>
<view class="b_btn_item" bindtap="toFiveBlessings" data-ed="3">
<image class="b_btn_img" src="../../../images/home/b_btn3.png"></image>
<view class="b_btn_text">以老带新引工大使</view>
<image class="b_btn_arrow" src="../../../images/home/b_arrow.png"></image>
</view>
<view class="b_btn_item" bindtap="toFiveBlessings" data-ed="4">
<image class="b_btn_img" src="../../../images/home/b_btn4.png"></image>
<view class="b_btn_text">诚信招工人资机构</view>
<image class="b_btn_arrow" src="../../../images/home/b_arrow.png"></image>
</view>
<view class="b_btn_item" bindtap="toFiveBlessings" data-ed="5">
<image class="b_btn_img" src="../../../images/home/b_btn5.png"></image>
<view class="b_btn_text">线上对接数字引工</view>
<image class="b_btn_arrow" src="../../../images/home/b_arrow.png"></image>
</view>
</view>
</view>
</view>
</view>
<tabBar selected="0"></tabBar>
<guideStep step="{{step}}"> </guideStep>
<!--组件可以放在任意位置,建议放在页面的底部 -->
homePage.json
{
"usingComponents": {
"tabBar":"/components/custom-tab-bar2/custom-tab-bar2",
"guideStep":"/components/tour/index"
},
"navigationStyle": "custom"
}
homePage.js
const app = getApp();
Page({
/**
* 页面的初始数据
*/
data: {
safeTop: 0,
aab301:'35',
keyword:'',
step: {
name: "homePage",
guideList: [
{
el: ".content0",
tips: "快速登记 轻松上岗",
littletips: "求职登记简单,推荐岗位无忧",
style: "border-radius: 8rpx;margin:0"
},
{
el: ".content1",
tips: "招聘会找工作",
littletips: "招聘会现场,工作直接找",
style: "border-radius: 8rpx;margin:0"
},
{
el: ".content2",
tips: "求职三步轻松办",
littletips: "找职位,投简历,去面试",
style: "border-radius: 8rpx;margin:0"
},
{
el: ".content3",
tips: "明星企业招聘",
littletips: "名企招聘,包吃住有社保",
style: "border-radius: 8rpx;margin:0"
}
]
},
},
search:function(){
var self = this;
wx.navigateTo({
url: '/packageRlzy/website/job/job_list/jobList?aab301=' + self.data.aab301
})
},
toHot:function(e){
var that = this;
var fw_name = e.currentTarget.dataset.ed;
if(fw_name=="grqzdj"){
if (app.isLogin()) {
wx.navigateTo({
url: '/packageRlzy/job/person/center/person_resume/personresume_list/personresume_list'
})
}else{
that.toLogin();
}
}else if(fw_name=="lwhdbm"){
wx.redirectTo({
url: '/pages/rlzy/recruitment_list/recruitmentList',
})
}else if(fw_name=="zwxqfa"){
if(app.isLogin() && app.isAuthenticatedCom()){
wx.redirectTo({
url: '/packageRlzy/job/company/center/position_list/position_list',
})
}else{
that.setData({
dialogShow:true,
dialogCon:'只有单位账号登录后发布可职位需求!'
})
return;
}
}else if(fw_name=="fgfgbm"){
wx.navigateTo({
url: '/packageLaborCooperation/website/returntowork/returntowork'
})
}
},
toFW:function(e){
var that = this;
var fw_name = e.currentTarget.dataset.ed;
if(fw_name=="zgz"){
wx.redirectTo({
url: '/packageRlzy/website/job/job_list/jobList',
})
}else if(fw_name=="wyff"){
wx.redirectTo({
url: '/pages/rlzy/recruitment_list/recruitmentList',
})
}else if(fw_name=="wyzc"){
wx.redirectTo({
url: '/packageWebsite/pages/website/news_list/news_list',
})
}
else if(fw_name=="zrc"){
if (!(app.isLogin() && app.isAuthenticated())) {
that.setData({
dialogShow:true,
dialogCon:'只有单位账号登录后可查看务工信息!'
})
return;
}else if(wx.getStorageSync('userinfo').usertype!='1'){
wx.showToast({
title: '个人用户无法查看务工信息',
icon: 'none',
duration: 2000
});
return;
}
wx.redirectTo({
url: '/packageRlzy/website/resume/resume_list/resume_list',
})
}else if(fw_name=="zbzp"){
wx.redirectTo({
url: '/packageRlzy/website/specialColumn/detail/detail',
})
}else if(fw_name=="ztlm"){
wx.redirectTo({
url: '/packageRlzy/website/specialColumn/main/main',
})
}else if(fw_name=="zczx"){
wx.redirectTo({
url: '/packageWebsite/pages/website/news_list/news_list',
})
} else if (fw_name == "rlzyfwjg") {
wx.navigateTo({
url: '/packageWebsite/pages/website/rlzyserviceagency/agencyinformation?acd10y=1',
})
} else if (fw_name == "lwxzfwjg") {
wx.navigateTo({
url: '/packageWebsite/pages/website/rlzyserviceagency/agencyinformation?acd10y=2',
})
}else if(fw_name=="fwpp"){
wx.navigateTo({
url: '/packageWebsite/pages/website/fiveblessing/fiveblessing'
})
}
},
toFiveBlessings:function(e){
var that = this;
var fw_name = e.currentTarget.dataset.ed;
if(fw_name=="1"){
wx.navigateTo({
url: '/packageTrain/website/creditenterprise/creditenterpriselist/creditenterpriselist',
})
}else if(fw_name=="2"){
wx.navigateTo({
url: '/packageWebsite/pages/website/rlzyserviceagency/agencyinformation?acd10y=2',
})
}else if(fw_name=="3"){
wx.navigateTo({
url: '/packageTrain/website/fiveblessings/attractworklist/attractworklist',
})
}else if(fw_name=="4"){
wx.navigateTo({
url: '/packageWebsite/pages/website/rlzyserviceagency/agencyinformation?acd10y=1',
})
}else if(fw_name=="5"){
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
this.setData({
safeTop: wx.getWindowInfo().safeArea.top
})
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})
homePage.wxss
page {
background-color: #f6f7fb;
font-family: 'pingfang';
}
@font-face {
font-family: 'pingfang';
src: url("http://10.0.66.101:3322/wechatimages/PINGFANG BOLD.TTF")
}
.neui-base-view {
width: 100%;
background-color: white;
display: flex;
flex-wrap: wrap;
overflow-y: scroll;
padding-bottom: calc(env(safe-area-inset-bottom) + 450rpx);
overflow-x: hidden;
}
.index-bg {
background-repeat: no-repeat;
background-color: #f6f7fb;
}
.neui-navigation {
width: 100%;
}
.neui-navigation-safe {
width: 100%;
}
.neui-navigation-bar {
width: 100%;
height: 80rpx;
display: flex;
align-items: center;
}
.home_icon{
width: 39rpx;
height: 32rpx;
margin-left: 31rpx;
}
.home_title_text {
font-family: 'pingfang';
height: 36rpx;
font-size: 36rpx;
line-height: 36rpx;
color: #ffffff;
margin-left: 12rpx;
}
.page-main {
width: 100%;
overflow: hidden;
display: flex;
flex-direction: column;
align-items: center;
position: absolute;
top: 200rpx;
}
.search {
width: 690rpx;
height: 96rpx;
background-color: #ffffff;
border-radius: 20rpx;
/* margin: 0 auto; */
}
.icon_search {
float: left;
width: 40rpx;
height: 40rpx;
margin-top: 28rpx;
margin-left: 31rpx;
}
.input_search{
float: left;
width: 500rpx;
height: 96rpx;
margin-left: 28rpx;
}
.btn_search {
float: left;
font-family: 'pingfang';
width: 60rpx;
height: 96rpx;
font-size: 30rpx;
line-height: 96rpx;
color: #0280ff;
}
.top_btn_area {
width: 635rpx;
height: 149rpx;
margin-top: 40rpx;
}
.top_btn_item {
float: left;
width: 119rpx;
height: 149rpx;
}
.top_btn_item image {
width: 100rpx;
height: 100rpx;
background-color: #ffffff;
border-radius: 30rpx;
margin-left: 9rpx;
}
.top_btn_item view {
width: 119rpx;
height: 40rpx;
font-size: 29rpx;
line-height: 40rpx;
color: #ffffff;
margin-top: 5rpx;
}
.middle_btn_area {
width: 690rpx;
height: 350rpx;
background-color: #ffffff;
border-radius: 30px;
margin-top: 213rpx;
}
.m_btn_item {
float: left;
margin-top: 30rpx;
margin-left: 30rpx;
width: 300rpx;
height: 130rpx;
border-radius: 15rpx;
}
.m_img1 {
float:left;
width: 60rpx;
height: 56rpx;
margin-top: 37rpx;
margin-left: 30rpx;
}
.m_btn_item view {
float: left;
margin-left: 21rpx;
width: 134rpx;
height: 130rpx;
font-size: 34rpx;
line-height: 130rpx;
color: #000000;
}
.bottom_btn_area {
width: 690rpx;
height: 810rpx;
background-size: 690rpx 810rpx;
background-repeat: no-repeat;
margin-bottom: 200rpx;
}
.b_content {
padding-left: 40rpx;
padding-top: 80rpx;
}
.b_btn_item {
width: 610rpx;
height: 96rpx;
margin-top: 40rpx;
}
.b_btn_img {
float: left;
width: 112rpx;
height: 114rpx;
margin-top: -9rpx;
}
.b_btn_text {
float: left;
width: 270rpx;
height: 96rpx;
font-size: 34rpx;
line-height: 96rpx;
color: #000000;
margin-left: 34rpx;
}
.b_btn_arrow {
width: 46rpx;
height: 46rpx;
float: right;
margin-top: 25rpx;
}