Vue 组件
inklayer-vue 提供 PdfAnnotator 和 PdfViewer 两个顶层组件,基于 Vue 3 Composition API 构建,使用 Pinia 管理状态,集成 shadcn-vue UI 组件库。
安装
npm install inklayer-vue
基础导入
// 组件
import { PdfAnnotator, PdfViewer } from 'inklayer-vue'
import 'inklayer-vue/style'
// 类型
import type { PdfAnnotatorProps, PdfViewerProps, User, Annotation } from 'inklayer-vue'
PdfAnnotator
完整的 PDF 批注组件,内置工具栏、批注编辑、侧边栏。
Props
| Prop | 类型 | 默认值 | 说明 |
|---|---|---|---|
| url | string | URL | — | PDF 文件的 URL 地址 |
| data | string | number[] | ArrayBuffer | Uint8Array | Uint16Array | Uint32Array | — | PDF 的二进制数据 |
| theme | 见下方主题色表格 | 'violet' | UI 主题色 |
| title | string | 'PDF ANNOTATOR' | 批注器标题 |
| locale | 'zh-CN' | 'en-US' | 'zh-CN' | 界面语言 |
| initialScale | 'auto' | 'page-actual' | 'page-fit' | 'page-width' | number | 'auto' | 初始缩放比例 |
| layoutStyle | CSSProperties | { width: '100vw', height: '100vh' } | 容器样式(Vue 版有默认值) |
| user | { id: string, name: string } | { id: 'null', name: 'unknown' } | 当前用户信息 |
| enableNativeAnnotations | boolean | false | 是否启用 PDF.js 原生批注 |
| enableRange | boolean | 'auto' | 'auto' | PDF 范围加载模式 |
| defaultOptions | Partial<PdfAnnotatorOptions> | — | 默认批注配置 |
| initialAnnotations | Annotation[] | [] | 初始批注数据 |
| defaultShowAnnotationsSidebar | boolean | false | 批注侧边栏默认是否展开 |
主题色选项
| 颜色值 | 说明 |
|---|---|
gray | 灰色 |
gold | 金色 |
bronze | 青铜色 |
brown | 棕色 |
yellow | 黄色 |
amber | 琥珀色 |
orange | 橙色 |
tomato | 番茄色 |
red | 红色 |
ruby | 红宝石色 |
crimson | 深红色 |
pink | 粉色 |
plum | 梅子色 |
purple | 紫色 |
violet | 紫罗兰色(默认) |
iris | 鸢尾花色 |
indigo | 靛蓝色 |
blue | 蓝色 |
cyan | 青色 |
teal | 蓝绿色 |
jade | 翡翠色 |
green | 绿色 |
grass | 草绿色 |
lime | 青柠色 |
mint | 薄荷色 |
sky | 天空色 |
Events
React → Vue 事件名对照:React 的
onXxxProp 对应 Vue 的@xxx事件(首字母小写)。例如:onSave→@save,onLoad→@load,onAnnotationAdded→@annotation-added。
| 事件 | 参数 | 触发时机 |
|---|---|---|
| save | (annotations: Annotation[]) | 用户点击保存 |
| load | () | PDF 加载完成 |
| annotation-added | (annotation: Annotation) | 新批注创建 |
| annotation-deleted | (id: string) | 批注删除 |
| annotation-selected | (annotation: Annotation | null, isClick: boolean) | 批注选中/取消 |
| annotation-updated | (annotation: Annotation) | 批注更新 |
Slots (PdfAnnotator)
| 插槽名称 | Slot Props | 说明 |
|---|---|---|
actions | { onSave, getAnnotations, exportToExcel, exportToPdf } | 自定义操作区域,渲染在工具栏右侧 |
Slots (PdfViewer)
| 插槽名称 | Slot Props | 说明 |
|---|---|---|
actions | context: PdfViewerContextValue | 自定义操作区域 |
toolbar | context: PdfViewerContextValue | 完全替换工具栏 |
sidebar-search-sidebar | context: PdfViewerContextValue | 自定义搜索侧边栏面板 |
sidebar-${key} | context: PdfViewerContextValue | 动态插槽,key 来自 sidebar prop 配置 |
Expose 方法
通过 ref 调用组件暴露的方法:
| 方法 | 说明 |
|---|---|
save() | 触发保存操作 |
getAnnotations() | 获取当前所有批注数据 |
完整示例:带持久化和插槽的批注器
<script setup lang="ts">
import { ref } from 'vue'
import { PdfAnnotator } from 'inklayer-vue'
import 'inklayer-vue/style'
import type { Annotation } from 'inklayer-vue'
const annotatorRef = ref<InstanceType<typeof PdfAnnotator>>()
const annotations = ref<Annotation[]>([])
function handleSave(data: Annotation[]) {
annotations.value = data
// 发送到后端
fetch('/api/annotations', {
method: 'POST',
body: JSON.stringify(data),
})
}
</script>
<template>
<PdfAnnotator
ref="annotatorRef"
url="/document.pdf"
locale="zh-CN"
:layout-style="{ height: '96vh' }"
:enable-range="true"
:default-show-annotations-sidebar="true"
:user="{ id: '9527', name: 'Lao Mai' }"
:enable-native-annotations="true"
:initial-annotations="[]"
:default-options="defaultOptions"
@save="handleSave"
@load="() => console.log('PDF Loaded')"
@annotation-added="(a) => console.log('Added:', a.id)"
@annotation-deleted="(id) => console.log('Deleted:', id)"
@annotation-updated="(a) => console.log('Updated:', a.id)"
@annotation-selected="(a, isClick) => console.log('Selected:', a?.id, isClick)"
>
<template #actions="{ onSave, getAnnotations, exportToExcel, exportToPdf }">
<button @click="onSave()">保存</button>
<button @click="console.log(getAnnotations())">获取批注</button>
<button @click="exportToExcel('my-export')">导出 Excel</button>
<button @click="exportToPdf('my-export')">导出 PDF</button>
</template>
</PdfAnnotator>
</template>
PdfViewer
轻量级 PDF 查看器,不包含批注功能。
Props(继承 PdfAnnotator 基础属性,新增以下)
| Prop | 类型 | 默认值 | 说明 |
|---|---|---|---|
| showTextLayer | boolean | true | 是否显示文本选区层 |
| showAnnotations | boolean | false | 是否显示已有批注 |
| defaultActiveSidebarKey | string | null | null | 默认展开侧边栏面板 |
| toolbar | Component | ((context: PdfViewerContextValue) => VueNode) | — | 自定义工具栏 |
| sidebar | SidebarPanel[] | — | 自定义侧边栏面板 |
| actions | Component | ((context: PdfViewerContextValue) => VueNode) | — | 自定义操作区域 |
| onDocumentLoaded | (pdfViewer: PDFViewer | null) => void | — | PDF 加载完成回调 |
| onEventBusReady | (eventBus: EventBus | null) => void | — | PDF.js EventBus 就绪回调 |
查看器完整示例
<script setup lang="ts">
import { ref } from 'vue'
import { PdfViewer } from 'inklayer-vue'
import 'inklayer-vue/style'
function onEventBusReady(eventBus: any) {
eventBus?.on('page-rendered', (e: any) => console.log('Page:', e.pageNumber))
}
function onDocumentLoaded(pdfViewer: any) {
console.log('Document loaded:', pdfViewer)
}
</script>
<template>
<PdfViewer
:enable-range="false"
title="PDF VIEWER CUSTOM"
:url="pdfUrl"
:layout-style="{ width: '100vw', height: '96vh' }"
:show-text-layer="false"
:show-annotations="true"
:default-active-sidebar-key="null"
:sidebar="customSidebar"
@event-bus-ready="onEventBusReady"
@document-loaded="onDocumentLoaded"
>
<template #actions="{ context }">
<button @click="context.print()">打印</button>
<button @click="context.download('test')">下载</button>
</template>
<template #toolbar="{ context }">
<button @click="context.toggleSidebar()">切换侧边栏</button>
</template>
</PdfViewer>
</template>
SidebarPanel 类型
sidebar prop 接受 SidebarPanel[],定义自定义侧边栏面板:
interface SidebarPanel {
key: string // 面板唯一 key
title: string // 面板标题
icon: Vue.VNode // 面板图标
render: (context: PdfViewerContextValue) => Vue.VNode // 面板内容渲染函数
}
PdfViewerContextValue 类型
actions、toolbar、sidebar 的 slot 或函数形式接收 PdfViewerContextValue 对象:
interface PdfViewerContextValue {
pdfViewer: PDFViewer | null // PDFViewer 实例
currentPage: number // 当前页码(0-based)
totalPages: number // 总页数
scale: number // 当前缩放比例
print: () => void // 打印
download: (filename?: string) => void // 下载
toggleSidebar: () => void // 切换侧边栏
scrollPageIntoView: (opts: { pageNumber: number }) => void // 滚动到指定页
}
PdfAnnotatorOptions 配置
通过 defaultOptions prop 自定义批注的默认行为:
interface PdfAnnotatorOptions {
colors?: string[] // 可用颜色列表
signature?: {
colors?: string[] // 签名可选颜色列表
type?: 'Draw' | 'Enter' | 'Upload' // 签名类型(首字母大写)
maxSize?: number // 签名图片最大文件大小(字节)
accept?: string // 接受的图片格式
defaultSignature?: string[] // 默认签名列表
defaultFont?: { // 默认字体列表
label: string
value: string
external?: boolean
url?: string // 外部字体 URL
}[]
}
stamp?: {
maxSize?: number // 印章图片最大文件大小
accept?: string // 接受的图片格式
defaultStamp?: string[] // 默认印章列表
editor?: { // 印章编辑器配置
defaultBackgroundColor?: string
defaultBorderColor?: string
defaultBorderStyle?: 'none' | 'solid' | 'dashed'
defaultTextColor?: string
defaultFont?: {
label: string
value: string
}[]
}
}
}
注意:
signature.type的值是'Draw' | 'Enter' | 'Upload'(首字母大写),不是'draw' | 'text' | 'image'。