@sv-print/plugin-driver
v0.1.0
Published
sv-print plugin: driver 引导
Readme
@sv-print/plugin-driver
sv-print 的 设计器新手引导插件。基于 Driver.js 在设计器挂载后弹出一组引导气泡,引导用户熟悉界面布局与功能入口。
- 通过
config.steps完全数据化地描述引导流程 - 首次挂载自动开启;后续按
cacheKey跳过,可主动resetGuide重置 - 步骤既可是静态对象,也可是返回
DriveStep的函数(支持Promise),便于在引导前通过 DOM 动态定位目标 - 通过 sv-print 自带的
preferences(localStorage)持久化"已引导过"标记 - 在
designerUtils上挂载运行时 API,便于在任意位置触发/重置引导
特性
- 通过参数形式动态设置引导流程
- 支持
cacheKey版本号管理:升级引导内容时改 key 即可重新触达老用户 - 步骤支持静态
DriveStep也支持函数形式(可异步查询 DOM) - 自动跟随
onDesigned钩子触发;可通过autoStart: false关闭 - 内置
onDoneClick/onCloseClick包装,自动写入"已完成"缓存并在适当时机清理 driver 实例 - 暴露
designerUtils.startGuide(force?)/designerUtils.resetGuide()/designerUtils.getGuideCacheKey()三个 API - 引导过程支持
driverOptions透传 Driver.js 全局配置(按钮文案、进度、动画等) - 自带 driver.js 的样式资源,可通过
@sv-print/plugin-driver/dist/style.css一键导入
安装
npm install @sv-print/plugin-driver
# 或
pnpm add @sv-print/plugin-driver要求 sv-print >= 0.2.0
使用
sv-print 插件引入的两种方式见:sv-print 插件文档
记得先导入 driver.js 的样式:
import "@sv-print/plugin-driver/dist/style.css";
import pluginDriver from "@sv-print/plugin-driver";
const plugins = [
pluginDriver({
// 缓存 key,用于判断某个版本的 steps 是否需要执行
cacheKey: "driver:DesignGuidance-v1",
// 是否自动开启引导(默认 true)
autoStart: true,
// 引导步骤
steps: [
{
element: ".svp-header",
popover: { title: "这是菜单栏", description: "菜单栏支持插槽,也可以完全自定义" },
},
{
element: ".svp-header .children .svp-flex:first-child",
popover: { title: "左侧标题区域", description: "组件可以传递参数展示不同内容" },
},
{
element: () => {
// 也支持函数式 element,可结合页面状态动态定位
const previewBox = document.querySelector(".previewBox");
return previewBox?.closest(".svp-modal-box");
},
onHighlightStarted: () => {
// 高亮开始时主动打开预览弹窗,便于引导用户
onPreviewClick();
},
onDeselected: () => {
designerUtils.preview.hide();
},
popover: {
title: "这是预览弹窗",
description: "可以浏览器打印、静默打印、导出图片、导出 pdf",
},
},
],
// driver 选项(透传给 driver.js 的 Config)
driverOptions: {
showProgress: true,
nextBtnText: "下一步",
prevBtnText: "上一步",
doneBtnText: "我知道了",
},
// 引导走完回调
onComplete: () => {
console.log("onboarded");
},
// 用户中途关闭 / 跳过时回调
onSkip: () => {
console.log("onSkip");
},
}),
];在线 demo 示例代码
import "@sv-print/plugin-driver/dist/style.css";
import pluginDriver from "@sv-print/plugin-driver";
// 重置滚动条位置:driver.js 执行时会居中显示。执行完后重置滚动条,避免样式错乱。
const restScroll = () => {
globalThis.$(`#paperBox`).scrollTop(0);
globalThis.$(`.svp-designer`).scrollTop(0);
globalThis.$(`#SVPrint`).scrollTop(0);
};
const plugins = [
pluginDriver({
// 缓存key,判断某个版本的 steps 是否需要执行
cacheKey: "driver:DesignGuidance-v1",
// 引导步骤
steps: [
{
element: ".svp-header",
popover: { title: "这是菜单栏", description: "这个菜单栏支持插槽,也可以完全自定义哟!" },
},
{
element: ".svp-header .children .svp-flex:first-child",
popover: {
title: "左侧标题区域",
description: "组件可以传递参数展示不同内容",
},
},
{
element: ".svp-header .children .svp-flex:nth-child(2)",
popover: {
title: "中间元素区域",
description: "可通过 eleList 配置添加元素列表。reEle:true 则完全自定义。",
},
},
{
element: ".svp-header .children .svp-flex:last-child",
popover: {
title: "右侧菜单列表区域",
description: "可通过 menuList 配置添加菜单列表。reMenu:true 则完全自定义。",
},
},
{
element: ".svp-header .children .svp-flex:last-child > div:nth-last-child(3) .children",
onHighlightStarted: () => {
globalThis
.$(".svp-header .children .svp-flex:last-child > div:nth-last-child(3) .children")
.css({ visibility: "visible", "z-index": "666" });
},
onDeselected: () => {
globalThis
.$(".svp-header .children .svp-flex:last-child > div:nth-last-child(3) .children")
.css({ visibility: "", "z-index": "" });
},
popover: {
title: "这是保存菜单中的子菜单",
description:
"子菜单可以自定义的。<br/>其中<span style='color:red;'>编辑模板数据、编辑打印数据、编辑拖拽元素</span> 是需要 <span style='color:#3c0aa8;'>编辑器插件(有官方插件,或自定义)<span style='color:red;'> 的哟!",
},
},
{
element: ".svp-header .children .svp-flex:last-child .svp-header-menu:nth-last-child(2)",
popover: {
title: "预览按钮",
description:
"点击可预览当前模板。记得要设置 printData 参数。 预览,打印,导出都需要它关联数据的。",
},
},
{
element: () => {
const previewBox = document.querySelector(".previewBox");
const targetParent = previewBox.closest(".svp-modal-box");
return targetParent;
},
onHighlightStarted: () => {
console.log("onHighlightStarted");
onPreviewClick();
},
onDeselected: () => {
console.log("onDeselected");
designerUtilsIns.preview.hide();
},
popover: {
title: "这是预览弹窗",
description:
"这个组件可以在非设计器中使用,顶部支持插槽。可以浏览器打印、静默打印、导出图片、导出pdf。",
},
},
{
element: ".toolbar",
popover: {
title: "这是工具栏",
description:
"选中元素可进行 对齐,字体调整,复制,删除等操作。左侧的设置按钮可自定义显示,排序。",
},
},
{
element: ".toolbar .right",
popover: {
title: "右侧工具栏",
description: "显示/隐藏悬浮拖拽盒子。同时还有主题,语言的设置。",
},
},
{
element: "#dragBox-draggableEls",
popover: {
title: "拖拽元素列表",
side: "right",
description:
"可拖拽的元素列表,可以通过provider自定义配置。当然样式也是可以自定义的。 拖拽到中间画布即可添加元素。<br/> <span style='color:#3c0aa8;'>同时这也是一个拖拽盒子。可以拖拽调整位置,大小。参数可控制起始位置。</span>",
},
},
{
element: "#dragBox-draggableEls .sv-edit",
popover: {
title: "编辑元素json",
side: "right",
description: "可拖拽的元素列表数据。可以通过provider自定义配置。可导入导出。",
},
},
{
element: "#printTemplate",
onDeselected: () => {
restScroll();
},
disableActiveInteraction: true,
popover: {
title: "这是面板(画布)编辑区域",
description: "可拖拽元素到画布即可添加元素。点击元素查看属性。点击空白查看面板属性。",
},
},
{
element: () => {
const list = Array.from(
document.querySelectorAll(".hiprint-printElement-text-content")
).filter((el) => el.textContent.includes("sv-print"));
if (list.length === 0) return null;
const targetParent = list[0].closest(".hiprint-printElement");
return targetParent;
},
onDeselected: () => {
restScroll();
},
popover: {
title: "这是面板(画布)中的打印元素",
description:
"点击元素可查看打印元素的属性。<br/>文本元素可以双击编辑内容。<br/>右键元素有菜单。<br/><span style='color:#3c0aa8;'>设计好样式后可以保存为自定义元素。然后可以导出,方便后续使用。</span>",
},
},
{
element: "#printTemplate .hiprint-headerLine",
onDeselected: () => {
restScroll();
},
popover: {
title: "这个是页眉线",
description: "可从顶部拖拽出来(面板属性也有)。页眉线之上的内容,每页显示。",
},
},
{
element: "#printTemplate .hiprint-footerLine",
onDeselected: () => {
restScroll();
},
popover: {
title: "这个是页脚线",
description: "可从底部拖拽出来(面板属性也有)。页脚线下的内容,每页显示。",
},
},
{
element: "#dragBox-panels .body",
popover: {
title: "多面板操作区域",
description: "一个模板可以有多个面板,每个面板可以有多个元素。面板",
},
},
{
element: "#dragBox-options",
popover: {
title: "属性区域",
side: "left",
description: "点击面板(画布)空白,可查看面板的属性。点击元素,可查看元素的属性。",
},
},
{
element: "#dragBox-pageStructure",
popover: {
title: "页面结构区域",
side: "left",
description: "鼠标悬停在某个位置,可查看当前元素在面板的哪个位置。",
},
},
{
element: "#dragBox-history",
popover: {
title: "历史记录区域",
side: "left",
description: "记录部分操作的历史记录。点击可回退到某一步操作。",
},
},
{
element: "#dragBox-miniMap",
popover: {
title: "概览图区域",
side: "right",
description: "当前画布/面板的概览图。可以拖拽,方向键调整画布显示区域。",
},
},
],
autoStart: true, // 是否自动开启引导,默认 true
// driver 选项
driverOptions: {
showProgress: true,
nextBtnText: "下一步",
prevBtnText: "上一步",
doneBtnText: "我知道了",
},
// 引导完成回调
onComplete: () => {
restScroll();
console.log("onboarded");
},
// 引导跳过回调
onSkip: () => {
restScroll();
console.log("onSkip");
},
}),
];配置项
type PluginConfig = {
/** 引导步骤,支持静态 DriveStep 或返回 DriveStep 的函数(可异步) */
steps?: GuideStep[];
/** 写入 sv-print preferences 的 key,默认 plugin-driver:default */
cacheKey?: string;
/** 透传给 driver.js 的全局配置,如按钮文案、动画等 */
driverOptions?: Partial<driver.Config>;
/** 设计器挂载后是否自动开启引导,默认 true */
autoStart?: boolean;
/** 引导走完最后一步时回调 */
onComplete?: () => void;
/** 用户中途关闭 / 跳过时回调 */
onSkip?: () => void;
};GuideStep 类型:
type GuideStep = DriveStep | (() => DriveStep | Promise<DriveStep>);可直接复用 driver.js 的 DriveStep 所有能力:元素定位、onHighlightStarted / onDeselected 等生命周期钩子、popover.title / description / side / align 等气泡外观。
运行时 API
插件在挂载时会把以下方法挂到 designerUtils 上,业务代码可以在任意位置调用:
| 方法 | 说明 |
| ---------------------------------- | -------------------------------------------------------------------- |
| designerUtils.startGuide(force?) | 立即开启引导;force=true 时忽略已读缓存强制开启 |
| designerUtils.resetGuide() | 清除已读标记并立刻开启引导,等价于 startGuide(true) + clearCache |
| designerUtils.getGuideCacheKey() | 取得当前插件所使用的缓存 key |
// 例如:用户点击"再看一次引导"按钮
button.onclick = () => designerUtils.resetGuide?.();缓存机制
- 写入位置:sv-print 的
preferencesstore(底层是localStorage上的sv-print-preferences) - 写入时机:用户走完最后一步点击 Done 时
- 跳过时机:用户点击关闭按钮或遮罩时不会写入缓存,下次挂载会自动重新开启
- 主动重置:
designerUtils.resetGuide()或setPreferences("driver:DesignGuidance-v1", false)
升级引导文案 / 增减步骤时,建议同步更新 cacheKey 的版本号(如 v1 → v2),否则老用户无法感知到新版引导。
License
MIT
开源使用须知
- 请自觉遵守 MIT 协议,其他用途可联系作者;
- 允许用于个人学习、毕业设计、教学案例、公益事业、商业使用;
- 商用请仔细审查代码和漏洞,不得用于任一国家许可范围之外的商业应用,产生的一切任何后果责任自负;
