Skip to content
uView Pro
Main Navigation
介绍
效果演示
国际化(i18n)
代码提示
设计理念
注意事项
常见问题

自定义

扩展图标库
自定义样式
多主题与暗黑模式
多主题配置工具

AI 能力

LLMs
Skills
组件总览
快速起步
基础组件
表单组件
数据组件
反馈组件
布局组件
导航组件
其他组件
Http请求
便捷工具

工具库

节流防抖
对象深度克隆
对象深度合并
时间格式化
路由跳转
数组乱序
全局唯一标识符
颜色转换
颜色值
对象转URL参数
规则校验
md5加密
随机数值
去除空格
节点布局信息
颜色管理
防抖函数
节流函数
国际化
主题切换
全局提示
全局弹窗
起步

部件

Coupon 优惠券

页面

微信个人中心页
自定义键盘支付
垂直分类
提交订单栏
评论列表
订单列表
登录界面
收货地址
城市选择
快速启动模板
文章合集
资源下载
鸿蒙体验
💬交流群捐赠开源
更新日志

主题

微信扫描二维码,关注我
0
Sidebar Navigation

GuidePage 引导页

页面导航
赞助位
成为赞助商

Guide Page 引导页组件 ​

该组件基于 uni-app x 开发的的 Guide Page 引导页组件,用于应用首次启动时的新手引导场景,支持全屏引导页、新功能引导、步骤引导,支持多页滑动展示、卡片动画、高度自定义样式,支持 App、H5、微信小程序全平台。

平台差异说明 ​

仅支持 uni-app x 项目

AppH5微信小程序
√√√

安装方式 ​

只能通过 uni-app x 官方插件市场安装:立即去安装

推荐阅读

暂无推荐文章

预览效果 ​

基本使用 ​

首先,引导页需要全屏展示,所以 pages.json 中添加 "navigationStyle": "custom" 配置:

json
{
  "pages": [
    {
      "path": "pages/demo/index",
      "style": {
        "navigationBarTitleText": "uView Pro X 演示",
        "navigationStyle": "custom"
      }
    }
  ]
}
  • 通过 v-model:show 控制引导页的显示与隐藏
  • 通过 slides 传入引导页数据,支持多页配置
  • 通过 storage-key 设置本地存储键名,自动记录用户是否已查看过引导页
html
<template>
    <ux-guide-page 
        v-model:show="showGuide" 
        :slides="slides"
        storage-key="app_guide_shown"
        @complete="onComplete"
    ></ux-guide-page>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import type { OnboardingSlide } from '@/uni_modules/ux-guide-x/types'

const showGuide = ref(true)

const slides = ref<OnboardingSlide[]>([
    {
        cards: [
            {
                icon: '🚀',
                title: '快速上手',
                desc: '简单几步即可开始使用我们的应用',
                position: 'center',
                features: ['一键登录', '智能推荐', '个性定制']
            }
        ]
    },
    {
        cards: [
            {
                icon: '💡',
                title: '核心功能',
                desc: '探索应用的强大功能',
                position: 'center',
                features: ['实时同步', '数据分析', '云端存储']
            }
        ]
    }
])

function onComplete() {
    console.log('引导完成')
    // 跳转到首页
    uni.switchTab({ url: '/pages/index/index' })
}
</script>

多卡片布局 ​

每个页面支持配置多个卡片,卡片可以设置不同的位置(左上、右上、居中、左下、右下)。

html
<template>
    <ux-guide-page v-model:show="showGuide" :slides="multiCardSlides"></ux-guide-page>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import type { OnboardingSlide } from '@/uni_modules/ux-guide-x/types'

const showGuide = ref(true)

const multiCardSlides = ref<OnboardingSlide[]>([
    {
        cards: [
            {
                icon: '📊',
                title: '数据分析',
                desc: '实时查看业务数据',
                position: 'top-left'
            },
            {
                icon: '🔔',
                title: '消息通知',
                desc: '及时获取重要信息',
                position: 'top-right'
            },
            {
                icon: '👤',
                title: '个人中心',
                desc: '管理个人信息',
                position: 'bottom-left'
            },
            {
                icon: '⚙️',
                title: '系统设置',
                desc: '自定义应用配置',
                position: 'bottom-right'
            }
        ]
    }
])
</script>

自定义卡片内容 ​

可以单独自定义卡片的图标和内容部分:

html
<template>
    <ux-guide-page v-model:show="showGuide" :slides="slides">
        <!-- 自定义卡片图标 -->
        <template #card-icon="{ card, index, cIndex }">
            <view class="custom-icon">
                <image :src="card.icon" mode="aspectFit"></image>
            </view>
        </template>
        
        <!-- 自定义卡片内容 -->
        <template #card-content="{ card, index, cIndex }">
            <view class="custom-content">
                <text class="title">{{ card.title }}</text>
                <text class="desc">{{ card.desc }}</text>
                <view class="features">
                    <text v-for="(feature, i) in card.features" :key="i">
                        {{ feature }}
                    </text>
                </view>
            </view>
        </template>
    </ux-guide-page>
</template>

自定义样式 ​

组件提供了丰富的 slot,可以完全自定义引导页的各个部分:

html
<template>
    <ux-guide-page v-model:show="showGuide" :slides="slides">
        <!-- 自定义背景 -->
        <template #background>
            <view class="custom-bg">
                <view class="gradient-circle"></view>
            </view>
        </template>
        
        <!-- 自定义跳过按钮 -->
        <template #skip="{ onSkip }">
            <view class="skip-btn" @click="onSkip">
                <text>跳过</text>
            </view>
        </template>
        
        <!-- 自定义头部 -->
        <template #header="{ current, total }">
            <view class="custom-header">
                <text>步骤 {{ current + 1 }} / {{ total }}</text>
            </view>
        </template>
        
        <!-- 自定义页面内容 -->
        <template #slide="{ slide, index, isActive }">
            <view v-if="isActive" class="custom-slide">
                <text class="title">{{ slide.cards[0].title }}</text>
                <text class="desc">{{ slide.cards[0].desc }}</text>
            </view>
        </template>
        
        <!-- 自定义指示器 -->
        <template #dots="{ current, total }">
            <view class="custom-dots">
                <view 
                    v-for="i in total" 
                    :key="i"
                    :class="['dot', { active: i - 1 === current }]"
                ></view>
            </view>
        </template>
        
        <!-- 自定义操作按钮 -->
        <template #actions="{ isFirst, isLast, onPrev, onNext, onComplete }">
            <view class="custom-actions">
                <button v-if="!isFirst" @click="onPrev">上一步</button>
                <button v-if="!isLast" @click="onNext">下一步</button>
                <button v-if="isLast" @click="onComplete">开始使用</button>
            </view>
        </template>
    </ux-guide-page>
</template>

双向绑定(v-model:show) ​

组件支持 v-model:show 双向绑定,可以更方便地控制引导页的显示状态。

html
<template>
    <ux-guide-page v-model:show="showGuide" :slides="slides"></ux-guide-page>
    <button @click="showGuide = true">重新显示引导</button>
</template>

<script setup lang="ts">
import { ref } from 'vue'

const showGuide = ref(false)
const slides = ref([...])
</script>

控制显示元素 ​

通过 props 可以控制引导页各部分的显示与隐藏:

html
<template>
    <!-- 隐藏跳过按钮和指示器 -->
    <ux-guide-page 
        v-model:show="showGuide" 
        :slides="slides"
        :show-skip="false"
        :show-dots="false"
    ></ux-guide-page>
    
    <!-- 隐藏头部和操作按钮 -->
    <ux-guide-page 
        v-model:show="showGuide" 
        :slides="slides"
        :show-header="false"
        :show-actions="false"
    ></ux-guide-page>
</template>

监听事件 ​

组件提供了丰富的事件回调:

html
<template>
    <ux-guide-page 
        v-model:show="showGuide" 
        :slides="slides"
        @change="onChange"
        @complete="onComplete"
        @skip="onSkip"
    ></ux-guide-page>
</template>

<script setup lang="ts">
import { ref } from 'vue'

const showGuide = ref(true)
const slides = ref([...])

function onChange(index: number) {
    console.log('切换到第', index + 1, '页')
}

function onComplete() {
    console.log('引导完成')
    // 引导完成后跳转到首页
    uni.switchTab({ url: '/pages/index/index' })
}

function onSkip() {
    console.log('用户跳过了引导')
    // 跳过引导也跳转到首页
    uni.switchTab({ url: '/pages/index/index' })
}
</script>

禁用滑动切换 ​

如果不希望用户通过滑动切换页面,可以禁用滑动功能:

html
<ux-guide-page 
    v-model:show="showGuide" 
    :slides="slides"
    :enable-swipe="false"
></ux-guide-page>

手动控制页面跳转 ​

可以通过 ref 获取组件实例,手动控制页面跳转:

html
<template>
    <ux-guide-page ref="guideRef" v-model:show="showGuide" :slides="slides"></ux-guide-page>
    <button @click="goToPage(2)">跳转到第3页</button>
</template>

<script setup lang="ts">
import { ref } from 'vue'

const showGuide = ref(true)
const slides = ref([...])
const guideRef = ref()

function goToPage(index: number) {
    guideRef.value?.goToSlide(index)
}
</script>

平台适配注意事项 ​

App 端 linear-gradient 限制 ​

App 端 linear-gradient 需要三个色值才能生效:

scss
/* ✅ 正确 */
.custom-bg {
    background: linear-gradient(180deg, #667eea 0%, #764ba2 50%, #764ba2 100%);
}

/* ❌ 错误 - 两个色值在 App 端不生效 */
.custom-bg {
    background: linear-gradient(180deg, #667eea 0%, #764ba2 100%);
}

App 端动画实现 ​

App 端不支持 @keyframes,请使用 transition:

scss
/* ✅ 正确 - 使用 transition */
.card {
    opacity: 0;
    transform: translateY(30rpx);
    transition: all 0.4s ease;
}

.card-visible {
    opacity: 1;
    transform: translateY(0);
}

/* ❌ 错误 - App 端不支持 */
@keyframes fadeIn {
    from { opacity: 0; }
    to { opacity: 1; }
}

API ​

Props ​

参数说明类型默认值可选值版本
show是否显示引导页(支持 v-model:show)Booleanfalsetrue/false-
storageKey本地存储键名,用于记录用户是否已查看引导String---
slides引导页数据数组Array[]--
enableSwipe是否允许滑动切换页面Booleantruetrue/false-
showDots是否显示页面指示器Booleantruetrue/false-
showSkip是否显示跳过按钮Booleantruetrue/false-
showHeader是否显示头部区域Booleantruetrue/false-
showActions是否显示底部操作按钮Booleantruetrue/false-

Slides 数据结构 ​

typescript
type OnboardingCard = {
    icon: string;              // 卡片图标(可以是 emoji 或图片 URL)
    title: string;             // 卡片标题
    desc: string;              // 卡片描述
    position: 'top-left' | 'top-right' | 'center' | 'bottom-left' | 'bottom-right';
    features?: string[];       // 特性列表(可选)
    delay?: number;            // 动画延迟时间(毫秒,可选)
}

type OnboardingSlide = {
    cards: OnboardingCard[];   // 该页面的卡片数组
}

Methods ​

名称说明参数版本
goToSlide跳转到指定页面(index: number)-
handleNext切换到下一页--
handlePrev切换到上一页--
handleComplete完成引导--

Slots ​

名称说明作用域参数版本
background自定义背景--
skip自定义跳过按钮-
header自定义头部-
slide自定义页面内容-
card-icon自定义卡片图标-
card-content自定义卡片内容-
dots自定义页面指示器-
actions自定义操作按钮-

Events ​

事件名说明回调参数版本
change页面切换时触发(index: number)-
complete点击完成按钮时触发--
skip点击跳过按钮时触发--
update:showv-model:show 双向绑定事件(show: boolean)-

完整示例代码 ​

html
<template>
    <view class="demo-container">
        <!-- 全屏引导页 - 示例1: 使用默认样式 -->
        <ux-guide-page
            v-if="exampleType == 'default'"
            v-model:show="guideVisible"
            enable-swipe
            :storage-key="STORAGE_KEY"
            :slides="customSlides"
            @complete="onGuideComplete"
            @skip="onGuideSkip"
        />

        <!-- 全屏引导页 - 示例2: 使用 slot 自定义卡片卡片图标和内容 -->
        <ux-guide-page
            v-else-if="exampleType == 'custom-card'"
            v-model:show="guideVisible"
            :storage-key="STORAGE_KEY"
            :slides="customSlides"
            enable-swipe
            @complete="onGuideComplete"
            @skip="onGuideSkip"
        >
            <!-- 自定义卡片图标 -->
            <template #card-icon="{ cIndex }">
                <view class="custom-card-icon">
                    <text class="custom-card-icon-emoji">
                        {{ cIndex == 0 ? '🎯' : cIndex == 1 ? '❤️' : cIndex == 2 ? '🏆' : '⭐' }}
                    </text>
                </view>
            </template>

            <!-- 自定义卡片内容 -->
            <template #card-content="{ index, cIndex }">
                <view class="custom-card-content">
                    <text class="custom-card-title" :style="{ color: getCardColor(cIndex as number) }">
                        {{ customSlides[index as number].cards[cIndex as number].title }}
                    </text>
                    <text class="custom-card-desc">
                        {{ customSlides[index as number].cards[cIndex as number].desc }}
                    </text>
                    <view v-if="customSlides[index as number].cards[cIndex as number].features" class="custom-card-tags">
                        <text
                            v-for="(feature, fIndex) in customSlides[index as number].cards[cIndex as number].features"
                            :key="fIndex"
                            class="custom-card-tag"
                            :style="{ backgroundColor: getTagColor(cIndex as number) }"
                        >
                            {{ feature }}
                        </text>
                    </view>
                </view>
            </template>
        </ux-guide-page>

        <!-- 全屏引导页 - 示例3: 使用 slot 自定义页面内容 -->
        <ux-guide-page
            v-else-if="exampleType == 'custom-slide'"
            v-model:show="guideVisible"
            :storage-key="STORAGE_KEY"
            :slides="simpleSlides"
            enable-swipe
            :show-skip="false"
            :show-header="false"
            :show-actions="false"
            @complete="onGuideComplete"
            @skip="onGuideSkip"
        >
            <!-- 自定义背景 -->
            <template #background>
                <view class="custom-bg">
                    <view class="custom-bg-gradient"></view>
                </view>
            </template>

            <!-- 自定义跳过按钮 -->
            <template #skip="{ onSkip }">
                <view class="custom-skip" @click="onSkip">
                    <text class="custom-skip-text">SKIP</text>
                </view>
            </template>

            <!-- 自定义头部 -->
            <template #header="{ current, total }">
                <view class="custom-header">
                    <text class="custom-header-title">欢迎使用</text>
                    <text class="custom-header-subtitle">uni-app x 组件库</text>
                    <text class="custom-header-progress">{{ (current as number) + 1 }} / {{ total }}</text>
                </view>
            </template>

            <!-- 自定义页面内容 -->
            <template #slide="{ index, isActive }">
                <view class="custom-slide" v-if="isActive">
                    <view class="custom-slide-icon">
                        <text class="custom-slide-icon-text">{{ simpleSlides[index as number].cards[0].icon }}</text>
                    </view>
                    <text class="custom-slide-title">{{ simpleSlides[index as number].cards[0].title }}</text>
                    <text class="custom-slide-desc">{{ simpleSlides[index as number].cards[0].desc }}</text>
                    <view class="custom-slide-features">
                        <text
                            v-for="(feature, fIndex) in simpleSlides[index as number].cards[0].features"
                            :key="fIndex"
                            class="custom-slide-feature"
                        >
                            {{ feature }}
                        </text>
                    </view>
                </view>
            </template>

            <!-- 自定义指示器 -->
            <template #dots="{ current, total }">
                <view class="custom-dots">
                    <view
                        v-for="(item, idx) in (total as number)"
                        :key="idx"
                        class="custom-dot"
                        :class="{ 'custom-dot--active': idx == (current as number) }"
                    ></view>
                </view>
            </template>

            <!-- 自定义操作按钮 -->
            <template #actions="{ isFirst, isLast, onPrev, onNext, onComplete }">
                <view class="custom-actions">
                    <view v-if="isFirst == false" class="custom-btn custom-btn--prev" @click="onPrev">
                        <text class="custom-btn-text">← 上一页</text>
                    </view>
                    <view v-if="isLast == false" class="custom-btn custom-btn--next" @click="onNext">
                        <text class="custom-btn-text">继续 →</text>
                    </view>
                    <view v-if="isLast == true" class="custom-btn custom-btn--complete" @click="onComplete">
                        <text class="custom-btn-text">🚀 开始使用</text>
                    </view>
                </view>
            </template>
        </ux-guide-page>

        <!-- 全屏引导页 - 示例4: 使用 Props 控制显示 -->
        <ux-guide-page
            v-else-if="exampleType == 'props-control'"
            v-model:show="guideVisible"
            :storage-key="STORAGE_KEY"
            :slides="customSlides"
            enable-swipe
            :show-dots="false"
            :show-skip="false"
            @complete="onGuideComplete"
            @skip="onGuideSkip"
        />

        <!-- 全屏引导页 - 示例5: 完全自定义(使用所有 slot + props 控制显示) -->
        <ux-guide-page
            v-else-if="exampleType == 'full-custom'"
            v-model:show="guideVisible"
            :storage-key="STORAGE_KEY"
            :slides="simpleSlides"
            enable-swipe
            :show-dots="false"
            :show-skip="false"
            :show-header="false"
            @complete="onGuideComplete"
            @skip="onGuideSkip"
        >
            <!-- 自定义背景 -->
            <template #background>
                <view class="full-custom-bg">
                    <view class="full-custom-bg-shape shape-1"></view>
                    <view class="full-custom-bg-shape shape-2"></view>
                    <view class="full-custom-bg-shape shape-3"></view>
                </view>
            </template>

            <!-- 自定义跳过 -->
            <template #skip="{ onSkip }">
                <view class="full-custom-skip" @click="onSkip">
                    <text class="full-custom-skip-icon">✕</text>
                </view>
            </template>

            <!-- 自定义头部 -->
            <template #header="{ current, total }">
                <view class="full-custom-header">
                    <view class="full-custom-logo">
                        <text class="full-custom-logo-icon">🎨</text>
                    </view>
                    <text class="full-custom-header-title">UX Guide</text>
                    <view class="full-custom-steps">
                        <view
                            v-for="(item, idx) in (total as number)"
                            :key="idx"
                            class="full-custom-step"
                            :class="{ 'full-custom-step--active': idx <= (current as number) }"
                        >
                            <text class="full-custom-step-num">{{ idx + 1 }}</text>
                        </view>
                    </view>
                </view>
            </template>

            <!-- 自定义页面内容 -->
            <template #slide="{ index, isActive }">
                <view class="full-custom-slide" v-if="isActive">
                    <view class="full-custom-card">
                        <view class="full-custom-card-icon">
                            <text class="full-custom-card-icon-text">{{ simpleSlides[index as number].cards[0].icon }}</text>
                        </view>
                        <text class="full-custom-card-title">{{ simpleSlides[index as number].cards[0].title }}</text>
                        <text class="full-custom-card-desc">{{ simpleSlides[index as number].cards[0].desc }}</text>
                        <view class="full-custom-card-features">
                            <view
                                v-for="(feature, fIndex) in simpleSlides[index as number].cards[0].features"
                                :key="fIndex"
                                class="full-custom-card-feature"
                            >
                                <view class="full-custom-card-feature-check">
                                    <text class="full-custom-card-feature-check-text">✓</text>
                                </view>
                                <text class="full-custom-card-feature-text">{{ feature }}</text>
                            </view>
                        </view>
                    </view>
                </view>
            </template>

            <!-- 自定义操作按钮 -->
            <template #actions="{ isFirst, isLast, onPrev, onNext, onComplete }">
                <view class="full-custom-actions">
                    <view
                        class="full-custom-btn"
                        :class="{ 'full-custom-btn--disabled': isFirst == true }"
                        @click="onPrev"
                    >
                        <text class="full-custom-btn-text">上一页</text>
                    </view>
                    <view
                        v-if="isLast == false"
                        class="full-custom-btn full-custom-btn--primary full-custom-btn--margin-left"
                        @click="onNext"
                    >
                        <text class="full-custom-btn-text">下一页</text>
                    </view>
                    <view
                        v-if="isLast == true"
                        class="full-custom-btn full-custom-btn--success full-custom-btn--margin-left"
                        @click="onComplete"
                    >
                        <text class="full-custom-btn-text">完成</text>
                    </view>
                </view>
            </template>
        </ux-guide-page>

        <!-- 单卡片展示 - Emoji 图标 -->
        <ux-guide-page
            v-else-if="exampleType == 'single-emoji'"
            v-model:show="guideVisible"
            :storage-key="STORAGE_KEY"
            :slides="singleCardEmojiSlides"
            enable-swipe
            @complete="onGuideComplete"
            @skip="onGuideSkip"
        />

        <!-- 单卡片展示 - 图片图标 -->
        <ux-guide-page
            v-else-if="exampleType == 'single-image'"
            v-model:show="guideVisible"
            :storage-key="STORAGE_KEY"
            :slides="singleCardImageSlides"
            enable-swipe
            @complete="onGuideComplete"
            @skip="onGuideSkip"
        />

        <!-- 控制面板 -->
        <view v-if="guideVisible == false" class="control-panel">
            <text class="control-title">全屏引导页展示示例</text>
            <view class="control-buttons">
                <view
                    class="control-btn"
                    :class="{ 'control-btn--active': exampleType == 'default' }"
                    @click="switchExample('default')"
                >
                    <text
                        class="control-btn-text"
                        :class="{ 'control-btn-text--active': exampleType == 'default' }"
                    >
                        多卡片样式
                    </text>
                </view>
                <view
                    class="control-btn"
                    :class="{ 'control-btn--active': exampleType == 'custom-card' }"
                    @click="switchExample('custom-card')"
                >
                    <text
                        class="control-btn-text"
                        :class="{ 'control-btn-text--active': exampleType == 'custom-card' }"
                    >
                        自定义卡片
                    </text>
                </view>
                <view
                    class="control-btn"
                    :class="{ 'control-btn--active': exampleType == 'custom-slide' }"
                    @click="switchExample('custom-slide')"
                >
                    <text
                        class="control-btn-text"
                        :class="{ 'control-btn-text--active': exampleType == 'custom-slide' }"
                    >
                        自定义页面
                    </text>
                </view>
                <view
                    class="control-btn"
                    :class="{ 'control-btn--active': exampleType == 'full-custom' }"
                    @click="switchExample('full-custom')"
                >
                    <text
                        class="control-btn-text"
                        :class="{ 'control-btn-text--active': exampleType == 'full-custom' }"
                    >
                        完全自定义
                    </text>
                </view>
                <view
                    class="control-btn"
                    :class="{ 'control-btn--active': exampleType == 'props-control' }"
                    @click="switchExample('props-control')"
                >
                    <text
                        class="control-btn-text"
                        :class="{ 'control-btn-text--active': exampleType == 'props-control' }"
                    >
                        Props控制
                    </text>
                </view>
                <view
                    class="control-btn"
                    :class="{ 'control-btn--active': exampleType == 'single-emoji' }"
                    @click="switchExample('single-emoji')"
                >
                    <text
                        class="control-btn-text"
                        :class="{ 'control-btn-text--active': exampleType == 'single-emoji' }"
                    >
                        单卡片-Emoji
                    </text>
                </view>
                <view
                    class="control-btn"
                    :class="{ 'control-btn--active': exampleType == 'single-image' }"
                    @click="switchExample('single-image')"
                >
                    <text
                        class="control-btn-text"
                        :class="{ 'control-btn-text--active': exampleType == 'single-image' }"
                    >
                        单卡片-图片
                    </text>
                </view>
            </view>
            <view class="demo-replay-btn" @click="showGuide">
                <text class="demo-replay-text">重新播放引导</text>
            </view>
        </view>
    </view>
</template>

<script setup lang="uts">
import type { OnboardingSlide } from '@/uni_modules/ux-guide-page/types';

const STORAGE_KEY = 'ux_guide_demo_onboarding_viewed';

const guideVisible = ref<boolean>(true);
const exampleType = ref<string>('default');

const switchExample = (type: string) => {
    exampleType.value = type;
};

// 根据卡片索引获取颜色
const getCardColor = (index: number): string => {
    const colors = ['#ff6b6b', '#4ecdc4', '#45b7d1', '#f9ca24'];
    return colors[index % colors.length];
};

// 根据卡片索引获取标签背景色
const getTagColor = (index: number): string => {
    const colors = ['rgba(255, 107, 107, 0.15)', 'rgba(78, 205, 196, 0.15)', 'rgba(69, 183, 209, 0.15)', 'rgba(249, 202, 36, 0.15)'];
    return colors[index % colors.length];
};

// 用于完全自定义示例的简单 slides(每个 slide 只有一张卡片)
const simpleSlides: OnboardingSlide[] = [
    {
        cards: [
            {
                icon: '🎉',
                title: '功能介绍',
                desc: '了解核心功能,探索更多可能性',
                position: 'center',
                size: 'medium',
                features: ['智能推荐', '数据分析', '多端同步']
            }
        ]
    },
    {
        cards: [
            {
                icon: '⚡',
                title: '快速开始',
                desc: '三步即可上手,轻松开启高效之旅',
                position: 'center',
                size: 'medium',
                features: ['一键安装', '自动配置', '即时使用']
            }
        ]
    },
    {
        cards: [
            {
                icon: '🛡️',
                title: '安全保障',
                desc: '企业级安全防护,守护您的数据资产',
                position: 'center',
                size: 'medium',
                features: ['数据加密', '权限管理', '操作审计']
            }
        ]
    },
    {
        cards: [
            {
                icon: '🚀',
                title: '持续升级',
                desc: '定期更新迭代,为您提供更好的体验',
                position: 'center',
                size: 'medium',
                features: ['功能更新', '性能优化', 'Bug修复']
            }
        ]
    }
];

const customSlides: OnboardingSlide[] = [
    {
        cards: [
            {
                icon: '📊',
                title: '数据分析',
                desc: '实时查看业务数据,多维度分析报表,助力决策',
                position: 'top-left',
                size: 'medium',
                features: ['实时图表', '数据导出', '智能分析', '趋势预测']
            },
            {
                icon: '🔔',
                title: '消息通知',
                desc: '及时获取重要信息,支持多种通知方式',
                position: 'top-right',
                size: 'medium',
                delay: 100,
                features: ['实时推送', '消息分类', '一键已读', '免打扰']
            },
            {
                icon: '👤',
                title: '个人中心',
                desc: '管理个人信息,查看账户状态和使用记录',
                position: 'bottom-left',
                size: 'medium',
                delay: 200,
                features: ['资料编辑', '账户安全', '使用记录', '会员权益']
            },
            {
                icon: '⚙️',
                title: '系统设置',
                desc: '自定义应用配置,打造个性化使用体验',
                position: 'bottom-right',
                size: 'medium',
                delay: 300,
                features: ['主题切换', '语言设置', '隐私管理', '缓存清理']
            }
        ]
    },
    {
        cards: [
            {
                icon: '🛍️',
                title: '商品管理',
                desc: '高效管理商品信息,支持批量操作和智能分类',
                position: 'top-left',
                size: 'medium',
                features: ['批量导入', '智能分类', '库存预警', '价格监控']
            },
            {
                icon: '📦',
                title: '订单处理',
                desc: '自动化订单流程,提升处理效率',
                position: 'top-right',
                size: 'medium',
                delay: 100,
                features: ['自动接单', '物流跟踪', '售后管理', '评价回复']
            },
            {
                icon: '💰',
                title: '财务管理',
                desc: '清晰的财务报表,收支一目了然',
                position: 'bottom-left',
                size: 'medium',
                delay: 200,
                features: ['收支明细', '对账功能', '发票管理', '资金流水']
            },
            {
                icon: '📈',
                title: '营销工具',
                desc: '丰富的营销活动模板,助力业务增长',
                position: 'bottom-right',
                size: 'medium',
                delay: 300,
                features: ['优惠券', '满减活动', '拼团秒杀', '会员营销']
            }
        ]
    },
    {
        cards: [
            {
                icon: '🤝',
                title: '客户管理',
                desc: '建立完善的客户档案,提供精准服务',
                position: 'top-left',
                size: 'medium',
                features: ['客户画像', '标签管理', '跟进记录', '智能推荐']
            },
            {
                icon: '💬',
                title: '在线客服',
                desc: '多渠道客服接入,快速响应客户需求',
                position: 'top-right',
                size: 'medium',
                delay: 100,
                features: ['即时通讯', '机器人客服', '工单系统', '满意度评价']
            },
            {
                icon: '📋',
                title: '内容管理',
                desc: '便捷的内容发布和管理系统',
                position: 'bottom-left',
                size: 'medium',
                delay: 200,
                features: ['文章发布', '素材库', '审核流程', '定时发布']
            },
            {
                icon: '🎯',
                title: '运营分析',
                desc: '深度运营数据分析,优化运营策略',
                position: 'bottom-right',
                size: 'medium',
                delay: 300,
                features: ['用户留存', '转化漏斗', '渠道分析', '活动效果']
            }
        ]
    }
]

// 单卡片展示 - Emoji 图标示例
const singleCardEmojiSlides: OnboardingSlide[] = [
    {
        cards: [
            {
                icon: '🛍️',
                title: '海量商品',
                desc: '精选百万优质商品,满足你的各种需求',
                position: 'center',
                features: ['品质保证', '七天无理由', '极速发货']
            }
        ]
    },
    {
        cards: [
            {
                icon: '💰',
                title: '优惠多多',
                desc: '新人专享大礼包,首单立减 50 元',
                position: 'center',
                features: ['新人礼包', '每日签到', '积分抵现']
            }
        ]
    },
    {
        cards: [
            {
                icon: '🚚',
                title: '极速配送',
                desc: '全国 300+ 城市次日达',
                position: 'center',
                features: ['次日达', '实时追踪', '送货上门']
            }
        ]
    }
];

// 单卡片展示 - 图片图标示例
const singleCardImageSlides: OnboardingSlide[] = [
    {
        cards: [
            {
                icon: 'https://img.icons8.com/color/96/shopping-bag.png',
                title: '购物商城',
                desc: '海量商品等你选购,品质保证',
                position: 'center',
                features: ['正品保障', '极速发货', '售后无忧']
            }
        ]
    },
    {
        cards: [
            {
                icon: 'https://img.icons8.com/color/96/discount.png',
                title: '优惠中心',
                desc: '领券购物更省钱,每日上新',
                position: 'center',
                features: ['大额优惠券', '限时秒杀', '会员专享']
            }
        ]
    },
    {
        cards: [
            {
                icon: 'https://img.icons8.com/color/96/delivery.png',
                title: '极速物流',
                desc: '全国配送,实时追踪物流信息',
                position: 'center',
                features: ['次日达', '全程追踪', '送货上门']
            }
        ]
    }
];

const showGuide = () => {
    guideVisible.value = true;
};

const onGuideComplete = () => {
    uni.showToast({
        title: '引导完成',
        icon: 'success'
    });
};

const onGuideSkip = () => {
    uni.showToast({
        title: '已跳过',
        icon: 'none'
    });
};
</script>

<style lang="scss">
.demo-container {
    flex: 1;
    position: relative;
}

.control-panel {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 600rpx;
    display: flex;
    flex-direction: column;
    align-items: center;
}

.control-title {
    font-size: 32rpx;
    font-weight: 600;
    color: #303133;
    margin-bottom: 32rpx;
}

.control-buttons {
    width: 100%;
    display: flex;
    flex-direction: column;
}

.control-btn + .control-btn {
    margin-top: 24rpx;
}

.control-btn {
    height: 88rpx;
    border-radius: 44rpx;
    background-color: #f5f5f5;
    display: flex;
    align-items: center;
    justify-content: center;
}

.control-btn--active {
    background-color: #2979ff;
}

.control-btn-text {
    font-size: 28rpx;
    color: #606266;
}

.control-btn-text--active {
    color: #ffffff;
}

.demo-replay-btn {
    margin-top: 48rpx;
    display: flex;
    flex-direction: row;
    align-items: center;
    padding: 24rpx 48rpx;
    background: linear-gradient(135deg, #2979ff, #1957b1);
    border-radius: 44rpx;
}

.demo-replay-text {
    margin-left: 12rpx;
    color: #fff;
    font-size: 28rpx;
    font-weight: 600;
}

.custom-header {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 40rpx 0;
}

.custom-header-title {
    font-size: 48rpx;
    font-weight: 700;
    color: #ffffff;
}

.custom-header-subtitle {
    font-size: 28rpx;
    color: rgba(255, 255, 255, 0.8);
    margin-top: 16rpx;
}

.custom-slide {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 40rpx;
}

.custom-slide-icon {
    width: 200rpx;
    height: 200rpx;
    border-radius: 100rpx;
    background-color: rgba(255, 255, 255, 0.2);
    display: flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 48rpx;
}

.custom-slide-icon-text {
    font-size: 100rpx;
}

.custom-slide-title {
    font-size: 48rpx;
    font-weight: 700;
    color: #ffffff;
    margin-bottom: 24rpx;
}

.custom-slide-desc {
    font-size: 28rpx;
    color: rgba(255, 255, 255, 0.8);
    text-align: center;
    margin-bottom: 32rpx;
}

.custom-slide-features {
    display: flex;
    flex-direction: row;
}

.custom-slide-feature {
    padding: 12rpx 24rpx;
    border-radius: 24rpx;
    background-color: rgba(255, 255, 255, 0.2);
    color: #ffffff;
    font-size: 24rpx;
    margin-right: 16rpx;
}

.custom-slide-feature:last-child {
    margin-right: 0;
}

.custom-dots {
    display: flex;
    flex-direction: row;
    justify-content: center;
    align-items: center;
    margin: 20rpx 0;
}

.custom-dots-text {
    font-size: 28rpx;
    color: rgba(255, 255, 255, 0.8);
}

.custom-actions {
    display: flex;
    flex-direction: row;
    justify-content: center;
    width: 100%;
    padding: 0 40rpx 40rpx;
}

.custom-btn {
    flex: 1;
    height: 80rpx;
    border-radius: 40rpx;
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 0 12rpx;
}

.custom-btn--prev {
    background-color: rgba(255, 255, 255, 0.2);
    border: 2rpx solid #ffffff;
}

.custom-btn--next {
    background-color: rgba(255, 255, 255, 0.3);
}

.custom-btn--complete {
    background-color: #ffffff;
}

.custom-btn-text {
    font-size: 28rpx;
    font-weight: 600;
    color: #ffffff;
}

.custom-btn--complete .custom-btn-text {
    color: #2979ff;
}

.custom-card-icon {
    width: 80rpx;
    height: 80rpx;
    border-radius: 20rpx;
    background-color: rgba(255, 107, 107, 0.1);
    display: flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 16rpx;
}

.custom-card-icon-emoji {
    font-size: 48rpx;
}

.custom-card-content {
    display: flex;
    flex-direction: column;
}

.custom-card-tags {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    margin-top: 12rpx;
}

.custom-card-tag {
    padding: 6rpx 12rpx;
    border-radius: 8rpx;
    background-color: rgba(255, 107, 107, 0.1);
    color: #ff6b6b;
    font-size: 20rpx;
    margin-right: 8rpx;
    margin-bottom: 8rpx;
}

/* 自定义背景样式 */
.custom-bg {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: linear-gradient(180deg, #667eea 0%, #764ba2 50%, #764ba2 100%);
    overflow: hidden;
}

.custom-bg-gradient {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: linear-gradient(180deg, #667eea 0%, #764ba2 50%, #764ba2 100%);
}

/* 自定义跳过按钮 */
.custom-skip {
    position: absolute;
    top: 60rpx;
    /* #ifdef MP */
    left: 30rpx;
    /* #endif */
    /* #ifndef MP */
    right: 30rpx;
    /* #endif */
    z-index: 10;
    padding: 12rpx 32rpx;
    border-radius: 30rpx;
    background-color: rgba(255, 255, 255, 0.15);
    border: 2rpx solid rgba(255, 255, 255, 0.3);
}

.custom-skip-text {
    font-size: 24rpx;
    color: #ffffff;
    font-weight: 600;
    letter-spacing: 2rpx;
}

/* 自定义头部进度 */
.custom-header-progress {
    font-size: 24rpx;
    color: rgba(255, 255, 255, 0.7);
    margin-top: 16rpx;
    padding: 8rpx 20rpx;
    border-radius: 20rpx;
    background-color: rgba(255, 255, 255, 0.1);
}

/* 自定义指示器 */
.custom-dot {
    width: 16rpx;
    height: 16rpx;
    border-radius: 8rpx;
    background-color: rgba(255, 255, 255, 0.3);
    margin: 0 8rpx;
    transition: all 0.3s ease;
}

.custom-dot--active {
    width: 40rpx;
    background-color: #ffffff;
}

/* ========== 完全自定义样式 ========== */
.full-custom-bg {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: linear-gradient(180deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
    overflow: hidden;
}

.full-custom-bg-shape {
    position: absolute;
    border-radius: 50%;
}

.full-custom-bg-shape.shape-1 {
    width: 400rpx;
    height: 400rpx;
    background: rgba(233, 69, 96, 0.3);
    top: -100rpx;
    right: -100rpx;
}

.full-custom-bg-shape.shape-2 {
    width: 300rpx;
    height: 300rpx;
    background: rgba(77, 171, 247, 0.3);
    bottom: 20%;
    left: -80rpx;
}

.full-custom-bg-shape.shape-3 {
    width: 250rpx;
    height: 250rpx;
    background: rgba(255, 206, 86, 0.2);
    bottom: -50rpx;
    right: 20%;
}

.full-custom-skip {
    position: absolute;
    top: 60rpx;
    /* #ifdef MP */
    left: 30rpx;
    /* #endif */
    /* #ifndef MP */
    right: 30rpx;
    /* #endif */
    z-index: 10;
    width: 60rpx;
    height: 60rpx;
    border-radius: 30rpx;
    background-color: rgba(255, 255, 255, 0.1);
    display: flex;
    align-items: center;
    justify-content: center;
}

.full-custom-skip-icon {
    font-size: 28rpx;
    color: #ffffff;
}

.full-custom-header {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 60rpx 40rpx 40rpx;
}

.full-custom-logo {
    width: 100rpx;
    height: 100rpx;
    border-radius: 50rpx;
    background: linear-gradient(135deg, #e94560, #ff6b6b);
    display: flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 20rpx;
    box-shadow: 0 8rpx 32rpx rgba(233, 69, 96, 0.3);
}

.full-custom-logo-icon {
    font-size: 48rpx;
}

.full-custom-header-title {
    font-size: 40rpx;
    font-weight: 700;
    color: #ffffff;
    margin-bottom: 32rpx;
}

.full-custom-steps {
    display: flex;
    flex-direction: row;
    align-items: center;
}

.full-custom-step {
    width: 48rpx;
    height: 48rpx;
    border-radius: 24rpx;
    background-color: rgba(255, 255, 255, 0.1);
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 0 12rpx;
    border: 2rpx solid rgba(255, 255, 255, 0.2);
}

.full-custom-step--active {
    background: linear-gradient(135deg, #e94560, #ff6b6b);
    border-color: transparent;
}

.full-custom-step-num {
    font-size: 24rpx;
    color: #ffffff;
    font-weight: 600;
}

.full-custom-slide {
    width: 100%;
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 40rpx;
}

.full-custom-card {
    width: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    background-color: rgba(255, 255, 255, 0.05);
    border-radius: 32rpx;
    padding: 48rpx;
    border: 2rpx solid rgba(255, 255, 255, 0.1);
}

.full-custom-card-icon {
    width: 120rpx;
    height: 120rpx;
    border-radius: 60rpx;
    background: linear-gradient(135deg, rgba(233, 69, 96, 0.2), rgba(255, 107, 107, 0.2));
    display: flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 32rpx;
}

.full-custom-card-icon-text {
    font-size: 60rpx;
}

.full-custom-card-title {
    font-size: 40rpx;
    font-weight: 700;
    color: #ffffff;
    margin-bottom: 16rpx;
}

.full-custom-card-desc {
    font-size: 28rpx;
    color: rgba(255, 255, 255, 0.7);
    margin-bottom: 32rpx;
    line-height: 1.6;
}

.full-custom-card-features {
    display: flex;
    flex-direction: column;
}

.full-custom-card-feature {
    display: flex;
    flex-direction: row;
    align-items: center;
    margin-bottom: 16rpx;
}

.full-custom-card-feature:last-child {
    margin-bottom: 0;
}

.full-custom-card-feature-check {
    width: 32rpx;
    height: 32rpx;
    border-radius: 16rpx;
    background: linear-gradient(135deg, #e94560, #ff6b6b);
    display: flex;
    align-items: center;
    justify-content: center;
    margin-right: 16rpx;
}

.full-custom-card-feature-check-text {
    font-size: 20rpx;
    color: #ffffff;
}

.full-custom-card-feature-text {
    font-size: 26rpx;
    color: rgba(255, 255, 255, 0.8);
}

.full-custom-actions {
    display: flex;
    flex-direction: row;
    justify-content: center;
    align-items: center;
    padding: 0 40rpx 60rpx;
}

.full-custom-btn--margin-left {
    margin-left: 24rpx;
}

.full-custom-btn {
    flex: 1;
    height: 88rpx;
    border-radius: 44rpx;
    background-color: rgba(255, 255, 255, 0.1);
    border: 2rpx solid rgba(255, 255, 255, 0.2);
    display: flex;
    align-items: center;
    justify-content: center;
}

.full-custom-btn--disabled {
    opacity: 0.3;
}

.full-custom-btn--primary {
    background: linear-gradient(135deg, #e94560, #ff6b6b);
    border-color: transparent;
}

.full-custom-btn--success {
    background: linear-gradient(135deg, #4ade80, #22c55e);
    border-color: transparent;
}

.full-custom-btn-text {
    font-size: 28rpx;
    font-weight: 600;
    color: #ffffff;
}
</style>

扫码预览

微信小程序
微信小程序
微信扫码体验
支付宝小程序
支付宝小程序
支付宝扫码体验
H5
H5
浏览器扫码访问
安卓
安卓
浏览器扫码下载
鸿蒙
鸿蒙
HarmonyOS 5.0+

赞赏支持

如果我的项目对您有帮助,可以请作者喝杯咖啡 ☕️
微信支付
微信支付
支付宝
支付宝

最后更新于:

备案许可:鲁ICP备2021040594号-5

Copyright © 2026 uView Pro