Use when adding new error messages to React, or seeing "unknown error code" warnings.
npx skills add CcSimple/skill-sv-print
Or install specific skill: npx add-skill https://github.com/CcSimple/skill-sv-print
# Description
可视化打印设计器组件。基于 Svelte 构建,支持 Vue、React、Angular、jQuery 等框架引入。提供拖拽式模板设计、预览、打印、导出 PDF/图片功能,支持插件扩展元素(ECharts、二维码、Fabric等)。
# SKILL.md
name: sv-print
description: 可视化打印设计器组件。基于 Svelte 构建,支持 Vue、React、Angular、jQuery 等框架引入。提供拖拽式模板设计、预览、打印、导出 PDF/图片功能,支持插件扩展元素(ECharts、二维码、Fabric等)。
sv-print
可视化打印设计器组件。基于 Svelte 构建,支持 Vue、React、Angular、jQuery 等框架引入。
快速开始
安装
# Svelte/Vanilla JS
npm i sv-print
# React
npm i @sv-print/react
# Vue2
npm i @sv-print/vue
# Vue3
npm i @sv-print/vue3
# 核心库
npm i @sv-print/hiprint
引入样式
在 main.ts 或 main.js 文件中引入组件样式
import 'sv-print/dist/style.css';
引入打印样式
需要复制 node_modules/@sv-print/hiprint/dist/print-lock.css 到开发资源目录。
例如: Vue 项目的 public 目录。
假如你部署的网站是: https://www.abcd.com/index.html 那么确保 https://www.abcd.com/print-lock.css 能够正常访问。
在 index.html 中添加打印样式:
<link rel="stylesheet" type="text/css" media="print" href="/print-lock.css" />
基本使用
<template>
<div style="width:100vw; height:100vh">
<Designer :template="template" @onDesigned="onDesigned" />
</div>
</template>
<script setup>
import { ref } from 'vue';
import { Designer } from '@sv-print/vue3';
const template = ref({});
const onDesigned = (e) => {
const { hiprint, designerUtils } = e.detail;
console.log(designerUtils.printTemplate);
};
</script>
核心概念
模板 JSON
模板数据格式包含 panels 数组,每个面板支持:
width/height: 面板尺寸(单位 mm)printElements: 打印元素列表paperHeader/paperFooter: 页眉/页脚线paperNumberLeft/paperNumberTop: 页码位置backgroundColor: 背景颜色watermarkOptions: 水印配置
See template.md for details.
全局对象
| 对象 | 用途 |
|---|---|
hiprint |
创建模板、打印预览、provider 管理 |
hiwebSocket |
连接打印客户端 |
hinnn |
工具类(单位转换、事件等) |
HIPRINT_CONFIG |
全局配置对象 |
See api.md for details.
设计器组件
Designer 组件支持丰富的参数:
template: 模板 JSON 数据printData: 打印预览数据config: 配置参数plugins: 插件列表events: 事件回调theme: 主题配置
See designer.md for details.
插件系统
官方插件:
@sv-print/plugin-ele-bwip-js- 二维码/条形码@sv-print/plugin-ele-echarts- ECharts 图表@sv-print/plugin-ele-fabric- Fabric 绘制@sv-print/plugin-ele-e2table- Excel 表格@sv-print/plugin-text-auto- 文本自适应@sv-print/plugin-formatter- 数据格式化
See plugins.md for details.
常见场景
浏览器打印
const printTemplate = new hiprint.PrintTemplate({ template: json });
const printData = { name: 'i不简' };
await printTemplate.print(printData);
// 批量打印
await printTemplate.print([printData, printData, printData]);
静默打印(需要打印客户端)
const res = await printTemplate.print2(printData, {
printer: '打印机名称',
pageSize: { width: 210 * 1000, height: 297 * 1000 },
copies: 2,
landscape: false,
color: true,
});
获取预览 HTML
const html = (await printTemplate.getHtml(printData)).html();
导出
导出 PDF(需插件)
import pluginApiPdf from '@sv-print/plugin-api-pdf3';
import { hiprint } from 'sv-print';
// 注册插件
hiprint.register({
plugins: [pluginApiPdf({})],
});
// 导出 PDF
const printTemplate = new hiprint.PrintTemplate({ template: json });
const printData = { name: '测试数据' };
// 方式一:直接下载
await printTemplate.toPdf(printData, '文件名称');
// 方式二:获取 Blob 对象
const res = await printTemplate.toPdf(printData, {
name: 'pdf名称',
isDownload: false, // 不自动下载,返回结果对象
type: 'blob', // 输出类型: blob | bloburl | dataurl | pdfobjectnewwindow
onProgress: (cur, total) => {
console.log('进度', Math.floor((cur / total) * 100) + '%');
},
});
console.log('PDF Blob:', res);
// 方式三:小模板填充到指定纸张大小
// 适用于:模板中 panelLayoutOptions.layoutRowGap > 0 或 layoutColumnGap > 0
// 且 printData 为数组时,小模板会自动填充到指定纸张大小
await printTemplate.toPdf(printDataArray, '发货单', {
paperWidth: 210, // 纸张宽度 (mm)
paperHeight: 297, // 纸张高度 (mm)
});
toPdf 参数说明:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| name | string | 'print' | 下载文件名 |
| isDownload | boolean | true | 是否自动下载 |
| type | string | 'blob' | 输出类型: blob, bloburl, dataurl, pdfobjectnewwindow |
| paperWidth | number | - | 纸张宽度 (mm),小模板填充时有效 |
| paperHeight | number | - | 纸张高度 (mm),小模板填充时有效 |
| onProgress | function | - | 进度回调 (cur, total) |
导出图片(需插件)
import pluginApiImage from '@sv-print/plugin-api-image';
import { hiprint } from 'sv-print';
// 注册插件
hiprint.register({
plugins: [pluginApiImage({})],
});
// 导出图片
const printTemplate = new hiprint.PrintTemplate({ template: json });
const printData = { name: '测试数据' };
// 方式一:直接下载
await printTemplate.toImage(printData, '图片名称');
// 方式二:获取 URL 或 Blob
const res = await printTemplate.toImage(printData, {
name: '图片名称',
isDownload: false, // 不自动下载
type: 'image/jpeg', // 图片类型: image/jpeg | image/png
quality: 0.92, // 图片质量 0-1
toType: 'url', // 返回类型: url | blob
limit: 10, // 多少页为一个图片文件
onProgress: (cur, total) => {
console.log('进度', Math.floor((cur / total) * 100) + '%');
},
});
console.log('图片URL:', res);
// 方式三:小模板填充到指定纸张大小
await printTemplate.toImage(printDataArray, '发货单', {
paperWidth: 210, // 纸张宽度 (mm)
paperHeight: 297, // 纸张高度 (mm)
});
toImage 参数说明:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| name | string | 'print' | 下载文件名 |
| isDownload | boolean | true | 是否自动下载 |
| type | string | 'image/jpeg' | 图片类型: image/jpeg, image/png |
| quality | number | 0.92 | 图片质量 0-1 |
| toType | string | 'url' | 返回类型: url, blob |
| limit | number | 10 | 多少页为一个图片文件 |
| paperWidth | number | - | 纸张宽度 (mm),小模板填充时有效 |
| paperHeight | number | - | 纸张高度 (mm),小模板填充时有效 |
| onProgress | function | - | 进度回调 (cur, total) |
小模板填充说明
当需要将多个小模板(如名片、标签等)填充到一张大纸(如 A4)时使用:
// 模板 JSON:配置行列间距
const templateJson = {
panels: [
{
index: 0,
name: '小模板',
height: 50, // 小模板高度 (mm)
width: 90, // 小模板宽度 (mm)
panelLayoutOptions: {
layoutType: 'row', // 行列布局类型: row, column
layoutRowGap: 10, // 行间距 (mm)
layoutColumnGap: 8, // 列间距 (mm)
},
printElements: [
// 元素配置
],
},
],
};
// 打印数据:数组格式
const printDataArray = [
{ name: '张三', phone: '13800138000' },
{ name: '李四', phone: '13900139000' },
{ name: '王五', phone: '13700137000' },
// 更多数据...
];
// 导出到 A4 纸
await printTemplate.toPdf(printDataArray, '批量名片', {
paperWidth: 210, // A4 宽度 (mm)
paperHeight: 297, // A4 高度 (mm)
});
完整导出示例
<template>
<div class="designer-container">
<Designer :template="template" @onDesigned="onDesigned" />
<div class="export-buttons">
<button @click="exportPdf">导出 PDF</button>
<button @click="exportImage">导出 图片</button>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { Designer } from '@sv-print/vue3';
import pluginApiPdf from '@sv-print/plugin-api-pdf3';
import pluginApiImage from '@sv-print/plugin-api-image';
import { hiprint } from 'sv-print';
const printTemplate = ref(null);
// 注册导出插件
hiprint.register({
plugins: [pluginApiPdf({}), pluginApiImage({})],
});
const onDesigned = (e) => {
printTemplate.value = e.detail.designerUtils.printTemplate;
};
// 导出 PDF
const exportPdf = async () => {
if (!printTemplate.value) return;
const printData = { name: '测试数据' };
try {
// 显示下载进度
await printTemplate.value.toPdf(printData, '发货单', {
isDownload: true,
paperWidth: 210,
paperHeight: 297,
onProgress: (cur, total) => {
console.log('PDF 导出进度:', Math.floor((cur / total) * 100) + '%');
},
});
console.log('PDF 导出完成');
} catch (error) {
console.error('PDF 导出失败', error);
}
};
// 导出图片
const exportImage = async () => {
if (!printTemplate.value) return;
const printData = { name: '测试数据' };
try {
const res = await printTemplate.value.toImage(printData, '发货单', {
isDownload: false,
type: 'image/jpeg',
quality: 0.92,
toType: 'url',
onProgress: (cur, total) => {
console.log('图片导出进度:', Math.floor((cur / total) * 100) + '%');
},
});
// 获取到 URL 后可以自定义处理
console.log('图片 URL:', res);
// 打开新窗口查看
window.open(res, '_blank');
} catch (error) {
console.error('图片导出失败', error);
}
};
</script>
<style>
.export-buttons {
position: fixed;
right: 20px;
top: 20px;
z-index: 1000;
}
.export-buttons button {
margin-left: 10px;
padding: 8px 16px;
background: #409eff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.export-buttons button:hover {
background: #66b1ff;
}
</style>
实际业务场景示例
场景一:自定义 Header 标题和菜单(保存到服务器)
<template>
<div class="designer-container">
<Designer
headerTitle="发货单模板设计"
:headerNewMenu="true"
:headerMenuList="headerMenuList"
:events="events"
@onDesigned="onDesigned"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { Designer } from '@sv-print/vue3';
import axios from 'axios';
const designerUtils = ref(null);
// 1. 自定义菜单列表
const headerMenuList = ref([
{
icon: 'sv-save',
title: '保存模板',
desc: 'Ctrl + S',
click: (util) => util.save(), // 调用内置保存,会触发 events.onSave
},
{
icon: 'sv-preview',
title: '预览',
desc: '',
click: async (util) => {
util.preview.show();
},
},
]);
// 2. 事件处理
const events = ref({
// 保存模板 - 发送到服务器
onSave: async (key, data) => {
try {
await axios.post('/api/template/save', {
key: key || 'default-template',
template: data,
templateName: '发货单模板',
});
console.log('保存成功');
return true; // 返回 true 阻止冒泡(不弹出内置保存提示)
} catch (error) {
console.error('保存失败', error);
return false;
}
},
// 编辑模板数据
onEdit: (data) => {
console.log('模板已编辑');
return true;
},
// 编辑打印数据
onEditData: (data) => {
console.log('打印数据已编辑', data);
return true;
},
// 快捷键
onKeyDownEvent: (e, util) => {
if (e.ctrlKey && e.key === 's') {
e.preventDefault();
util.save();
return true;
}
return false;
},
});
// 3. 初始化完成回调
const onDesigned = (e) => {
const { designerUtils: utils } = e.detail;
designerUtils.value = utils;
// 可在此处加载已有模板
loadTemplate();
};
// 加载模板
const loadTemplate = async () => {
const res = await axios.get('/api/template/get?id=1');
if (res.data.template) {
designerUtils.value.printTemplate.update(res.data.template);
}
};
</script>
<style>
.designer-container {
width: 100vw;
height: 90vh;
overflow: hidden;
}
</style>
场景二:隐藏默认菜单,只保留自定义保存按钮
<template>
<div class="designer-container">
<Designer
headerTitle="入库单设计"
:headerNewMenu="true"
:headerMenuList="headerMenuList"
:showOption="{ showToolbar: true }"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { Designer } from '@sv-print/vue3';
const headerMenuList = ref([
{
icon: 'sv-save',
title: '保存',
desc: 'Ctrl+S',
click: async (util) => {
const templateData = util.printTemplate.getJson();
console.log('保存模板:', templateData);
// 保存逻辑
},
},
{
icon: 'sv-preview',
title: '预览',
click: (util) => {
util.preview.show();
},
},
{
gap: true, // 分割线
},
{
icon: 'sv-print',
title: '直接打印',
click: async (util) => {
await util.printTemplate.print2({ name: '测试数据' });
},
},
]);
</script>
场景三:根据 ID 从服务端加载模板并预览
<template>
<!-- 设计器外层容器需要设置宽高 -->
<div v-if="showDesigner" class="designer-container">
<Designer
:template="template"
:printData="printData"
:events="events"
:onPreviewClick="onPreviewClick"
@onDesigned="onDesigned"
/>
</div>
<!-- 加载中状态 -->
<div v-else class="loading">
<span>加载中...</span>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { Designer } from '@sv-print/vue3';
import axios from 'axios';
const route = useRoute();
const router = useRouter();
const showDesigner = ref(false); // 控制设计器显示
const template = ref({}); // 模板数据
const printData = ref({}); // 打印数据
const designerUtils = ref(null);
// 从路由获取模板 ID
const templateId = route.query.id as string;
// 事件处理
const events = ref({
onSave: async (key, data) => {
try {
// 保存到服务器
await axios.post('/api/template/save', {
id: templateId,
template: data,
});
console.log('保存成功');
return true;
} catch (error) {
console.error('保存失败', error);
return false;
}
},
});
// 预览点击事件
const onPreviewClick = () => {
// 可自定义预览逻辑
console.log('点击预览');
};
// 初始化完成回调
const onDesigned = (e) => {
const { designerUtils: utils } = e.detail;
designerUtils.value = utils;
};
// 从服务器加载模板和打印数据
const loadTemplate = async () => {
try {
if (!templateId) {
// 没有 ID,跳转到新建页面或使用默认空模板
template.value = {};
showDesigner.value = true;
return;
}
// 并行请求模板和打印数据
const [templateRes, printDataRes] = await Promise.all([
axios.get(`/api/template/${templateId}`),
axios.get(`/api/template/${templateId}/printData`),
]);
// 设置模板数据
if (templateRes.data.template) {
template.value = templateRes.data.template;
}
// 设置打印数据
if (printDataRes.data.printData) {
printData.value = printDataRes.data.printData;
}
// 数据加载完成后显示设计器
showDesigner.value = true;
} catch (error) {
console.error('加载模板失败', error);
showDesigner.value = true;
}
};
onMounted(() => {
loadTemplate();
});
</script>
<style scoped>
.designer-container {
width: 100vw;
height: 90vh;
overflow: hidden;
}
.loading {
display: flex;
justify-content: center;
align-items: center;
width: 100vw;
height: 90vh;
font-size: 16px;
color: #666;
}
</style>
场景四:切换主题和语言
<template>
<div class="designer-container">
<Designer :theme="theme" :themeList="themeList" headerTitle="多主题模板设计" />
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { Designer } from '@sv-print/vue3';
// 主题
const theme = ref('light');
// 可选主题列表
const themeList = ref([
'light',
'dark',
'cupcake',
'bumblebee',
'emerald',
'corporate',
'synthwave',
'retro',
'cyberpunk',
'dracula',
'business',
'winter',
]);
// 切换主题
const changeTheme = (newTheme: string) => {
theme.value = newTheme;
};
</script>
<style>
.designer-container {
width: 100vw;
height: 90vh;
overflow: hidden;
}
</style>
场景五:自定义可拖拽元素
<template>
<div class="designer-container">
<Designer :providers="providers" :providerMap="providerMap" :clearProviderContainer="true" />
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { Designer } from '@sv-print/vue3';
import { hiprint } from 'sv-print';
// 定义 provider
const customProvider = (options) => {
var addElementTypes = function (context) {
context.removePrintElementTypes('customModule');
context.addPrintElementTypes('customModule', [
// 常规分组
new hiprint.PrintElementTypeGroup('业务字段', [
{
tid: 'customModule.companyName',
type: 'text',
title: '公司名称',
options: {
title: '公司名称',
field: 'companyName',
testData: '不简科技',
fontSize: 16,
fontWeight: 'bolder',
},
},
{
tid: 'customModule.orderNo',
type: 'text',
title: '订单号',
options: {
title: '订单号',
field: 'orderNo',
testData: 'DD20240129001',
},
},
]),
// 辅助分组
new hiprint.PrintElementTypeGroup('辅助', [
{
tid: 'customModule.hline',
type: 'hline',
title: '横线',
},
{
tid: 'customModule.rect',
type: 'rect',
title: '矩形',
},
]),
]);
};
return { addElementTypes };
};
const providers = ref([customProvider({})]);
const providerMap = ref({
container: '.hiprintEpContainer',
value: 'customModule',
});
</script>
<style>
.designer-container {
width: 100vw;
height: 90vh;
overflow: hidden;
}
</style>
场景六:表格元素使用
// 表格元素模板
const tableTemplate = {
panels: [
{
index: 0,
name: '面板0',
height: 297,
width: 210,
printElements: [
{
options: {
left: 60,
top: 60,
height: 100,
width: 500,
field: 'table',
columnFields: [
{ text: 'ID', field: 'id' },
{ text: '姓名', field: 'name' },
{ text: '数量', field: 'count' },
],
columns: [
[
{ width: 100, title: 'ID', field: 'id', checked: true },
{ width: 150, title: '姓名', field: 'name', checked: true },
{ width: 100, title: '数量', field: 'count', checked: true },
],
],
},
printElementType: {
title: '表格',
type: 'table',
editable: true,
columnResizable: true,
isEnableInsertRow: true,
isEnableDeleteRow: true,
},
},
],
},
],
};
// 打印数据
const printData = {
table: [
{ id: 1, name: '商品A', count: 100 },
{ id: 2, name: '商品B', count: 200 },
{ id: 3, name: '商品C', count: 300 },
],
};
场景七:二维码/条形码元素
方式一:使用文本元素(无需插件)
通过 text 元素设置 textType 属性实现二维码或条形码:
// 二维码模板
const qrcodeTemplate = {
panels: [
{
index: 0,
name: '面板0',
height: 297,
width: 210,
printElements: [
{
options: {
left: 100,
top: 60,
height: 50,
width: 50,
title: '123456789', // 显示内容
textType: 'qrcode', // 设置为二维码
color: '#000000',
},
printElementType: {
title: '文本',
type: 'text',
},
},
],
},
],
};
// 条形码模板
const barcodeTemplate = {
panels: [
{
index: 0,
name: '面板0',
height: 297,
width: 210,
printElements: [
{
options: {
left: 100,
top: 60,
height: 40,
width: 150,
title: '123456789',
textType: 'barcode', // 设置为条形码
color: '#000000',
},
printElementType: {
title: '文本',
type: 'text',
},
},
],
},
],
};
方式二:使用插件扩展的新元素类型(推荐)
引入 @sv-print/plugin-ele-bwip-js 插件后,会新增 barcode 和 qrcode 元素类型:
<template>
<div class="designer-container">
<Designer :plugins="plugins" :template="template" headerTitle="发货单(含条码)" />
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { Designer } from '@sv-print/vue3';
import pluginBwip from '@sv-print/plugin-ele-bwip-js';
const plugins = ref([
pluginBwip({}), // 插件无配置参数,直接引入即可
]);
// 使用插件新增的 qrcode 元素类型
const template = ref({
panels: [
{
index: 0,
name: '面板0',
height: 297,
width: 210,
printElements: [
{
options: {
left: 100,
top: 60,
height: 60,
width: 60,
title: '二维码内容', // 显示内容
},
printElementType: {
title: '二维码', // 插件新增的元素类型
type: 'qrcode',
},
},
{
options: {
left: 200,
top: 60,
height: 50,
width: 150,
title: '条形码内容',
},
printElementType: {
title: '条形码', // 插件新增的元素类型
type: 'barcode',
},
},
],
},
],
});
// 打印数据
const printData = {
name: '张三',
};
</script>
<style>
.designer-container {
width: 100vw;
height: 90vh;
overflow: hidden;
}
</style>
场景八:接收外部保存信号触发设计器保存
<template>
<div class="page-container">
<div class="toolbar">
<button @click="saveTemplate">外部保存按钮</button>
</div>
<div class="designer-container">
<Designer :events="events" @onDesigned="onDesigned" />
</div>
</div>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue';
import { Designer } from '@sv-print/vue3';
import { hinnn } from 'sv-print';
const designerUtils = ref(null);
const saveTrigger = ref(0); // 保存触发器
// 事件
const events = ref({
onSave: (key, data) => {
console.log('保存模板', key);
// 发送到服务器或 localStorage
return true;
},
});
const onDesigned = (e) => {
const { designerUtils: utils } = e.detail;
designerUtils.value = utils;
};
// 外部保存
const saveTemplate = () => {
if (designerUtils.value) {
designerUtils.value.save();
}
};
// 监听外部保存信号(可通过 pinia/vuex 或事件总线触发)
watch(saveTrigger, () => {
saveTemplate();
});
// 模拟外部触发
// hinnn.event.trigger('saveTemplate');
</script>
<style>
.page-container {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
}
.toolbar {
height: 50px;
padding: 0 20px;
display: flex;
align-items: center;
background: #f5f5f5;
}
.designer-container {
flex: 1;
overflow: hidden;
}
</style>
场景九:设置默认纸张和尺寸
<template>
<div class="designer-container">
<Designer :paperList="paperList" />
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { Designer } from '@sv-print/vue3';
const paperList = ref([
{ type: 'A4', width: 210, height: 297 },
{ type: 'A5', width: 148, height: 210 },
{ type: 'Custom', width: 100, height: 150 }, // 自定义尺寸
]);
</script>
<style>
.designer-container {
width: 100vw;
height: 90vh;
overflow: hidden;
}
</style>
场景十:禁用/调整 UI 组件
通过 styleOption、showOption、rulerStyle 可以隐藏或调整各个小组件的位置:
<template>
<div class="designer-container">
<Designer
:styleOption="styleOption"
:showOption="showOption"
:rulerStyle="rulerStyle"
:showPanels="false"
headerTitle="简化版设计器"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { Designer } from '@sv-print/vue3';
// 控制各小组件的显示和位置
const styleOption = ref({
draggableEls: {
// 可拖拽元素区域
mode: 'left', // 固定在左侧
show: true, // 是否显示
style: 'left:0;top:95px;width:200px;height:calc(100% - 340px);',
},
options: {
// 属性面板 - 隐藏
show: false,
},
pageStructure: {
// 页面结构 - 隐藏
show: false,
},
locationExchange: {
// 位置交换 - 隐藏
show: false,
},
miniMap: {
// 小地图 - 隐藏
show: false,
},
editableTools: {
// 编辑工具 - 隐藏
show: false,
},
zIndexTools: {
// 层级工具 - 隐藏
show: false,
},
fontTools: {
// 字体工具 - 隐藏
show: false,
},
zoomTools: {
// 缩放工具 - 显示并调整位置
mode: 'right',
style: 'right:10px;top:100px;',
},
rotateTools: {
// 旋转工具 - 隐藏
show: false,
},
});
// 控制主区域显示
const showOption = ref({
showHeader: true, // 显示顶部 Header
showToolbar: true, // 显示工具栏 Toolbar
showFooter: true, // 显示底部 Footer
showPower: false, // 隐藏 "powered by sv-print"
});
// 控制标尺
const rulerStyle = ref({
show: false, // 隐藏标尺
});
</script>
<style>
.designer-container {
width: 100vw;
height: 90vh;
overflow: hidden;
}
</style>
styleOption 可配置项说明:
| 组件 | 说明 | 可配置项 |
|---|---|---|
| panels | 多面板区域 | mode |
| draggableEls | 可拖拽元素 | show, mode, style, html |
| options | 属性面板 | show, mode, style, html |
| pageStructure | 页面结构 | show, mode, style, html |
| locationExchange | 位置交换 | show, mode, style, html |
| miniMap | 小地图 | show, mode, style, html |
| history | 历史记录 | show, mode, style, html |
| editableTools | 编辑工具 | show, mode, style |
| zIndexTools | 层级工具 | show, mode, style |
| fontTools | 字体工具 | show, mode, style |
| zoomTools | 缩放工具 | show, mode, style |
| rotateTools | 旋转工具 | show, mode, style |
mode 取值说明:
default- 默认位置top- 固定在顶部bottom- 固定在底部left- 固定在左侧right- 固定在右侧fixed- 自由拖拽(需配合 style 设置初始位置)
# README.md
skill-sv-print
可视化打印设计器组件。基于 Svelte 构建,支持 Vue、React、Angular、jQuery 等框架引入。提供拖拽式模板设计、预览、打印、导出 PDF/图片功能,支持插件扩展元素(ECharts、二维码、Fabric等)。
官方文档:https://www.ibujian.cn/svp/
What is sv-print?
sv-print 是一个功能强大的可视化打印设计&打印解决方案:
- 拖拽式设计器 - 通过拖拽元素快速构建打印模板
- 多框架支持 - Vue 2/3、React、Svelte、Angular、jQuery 均可使用
- 多种打印方式 - 浏览器打印、静默打印、PDF/图片导出
- 插件扩展 - 支持 ECharts、二维码/条形码、Fabric 绘制、Excel 表格等
- 高度可定制 - 支持自定义主题、元素 Provider、UI 组件
Install Skill
npx skills add https://github.com/CcSimple/skill-sv-print
For more information about agent-skills, visit: https://github.com/vercel-labs/skills
Use sv-print Skill
eg: claude code skill sv-print
/sv-print xxx提示词
AI Prompts
以下是几个常用场景的 AI 提示词示例:
场景一:引入设计器
使用 sv-print 在 Vue3 中创建模板设计器页面:
1. 在 designer.vue 中引入 Designer 组件
2. 通过路由参数 id 获取模板(如 /designer?id=123)
3. 页面加载时通过 API GET /api/template/:id 获取模板数据并回显
4. header 菜单只保留"保存"和"预览"按钮
5. 保存事件通过 API POST /api/template/save 保存到服务器
场景二:加载模板实现功能
通过 id获取模板json. 创建预览,浏览器打印,直接打印按钮实现打印功能
问题反馈
License
MIT
# Supported AI Coding Agents
This skill is compatible with the SKILL.md standard and works with all major AI coding agents:
Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.