Vue 组件
inklayer-vue 提供 PdfViewer 和 PdfAnnotator 两个顶层组件,基于 Vue 3 Composition API 构建,使用 Pinia 管理状态,集成 shadcn-vue UI 组件库。
🚀 快速体验
🎯 在线体验(Live Demo)
体验 InkLayer 的完整功能,包括 PDF Viewer 与 PDF Annotator
👉 在线体验💻 在线运行(StackBlitz)
无需本地安装,直接在浏览器中运行并修改 Vue Starter
👉 在 StackBlitz 中打开 Vue Starter安装
npm install inklayer-vue
插件注册(必须)
使用前需要先注册 InkLayer Vue 插件:
import { createApp } from 'vue'
import { inklayerVuePlugin } from 'inklayer-vue'
import App from './App.vue'
const app = createApp(App)
app.use(inklayerVuePlugin)
app.mount('#app')
基础导入
// 组件
import { PdfViewer, PdfAnnotator } from 'inklayer-vue'
import 'inklayer-vue/style'
// 类型
import type {
PdfViewerProps,
PdfAnnotatorProps,
User,
Annotation,
AnnotationPermissions,
} from 'inklayer-vue'
PdfViewer
轻量级 PDF 查看器,不包含批注功能。
Props
| 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 就绪回调 |
Slots (PdfViewer)
| 插槽名称 | Slot Props | 说明 |
|---|---|---|
actions | context: PdfViewerContextValue | 自定义操作区域 |
toolbar | context: PdfViewerContextValue | 完全替换工具栏 |
sidebar-search-sidebar | context: PdfViewerContextValue | 自定义搜索侧边栏面板 |
sidebar-${key} | context: PdfViewerContextValue | 动态插槽,key 来自 sidebar prop 配置 |
查看器完整示例
<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>
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' } | 当前用户信息 |
| annotationPermissions | AnnotationPermissions | { mode: 'unrestricted' } | 协作批注权限,控制创建、编辑、变换、状态、删除与回复操作 |
| enableNativeAnnotations | boolean | false | 是否启用 PDF.js 原生批注 |
| enableRange | boolean | 'auto' | 'auto' | PDF 范围加载模式 |
| defaultOptions | Partial<PdfAnnotatorOptions> | — | 默认批注配置 |
| initialAnnotations | Annotation[] | [] | 初始批注数据 |
| defaultShowAnnotationAuthorLabels | boolean | false | 初始化时是否显示全部批注的作者标签;之后仍可通过工具栏或快捷键切换 |
| 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 | 天空色 |
协作批注权限
Vue 与 React 使用同一套权限规则。将应用的当前用户传给 user,再通过 annotationPermissions 开启归属控制。
<script setup lang="ts">
import { PdfAnnotator } from 'inklayer-vue'
import type { AnnotationPermissions, User } from 'inklayer-vue'
const currentUser: User = { id: 'alice', name: 'Alice' }
const permissions: AnnotationPermissions = {
mode: 'owner-only',
can: ({ currentUser }) =>
currentUser?.id === 'admin' ? true : undefined,
}
</script>
<template>
<PdfAnnotator
url="/document.pdf"
:user="currentUser"
:annotation-permissions="permissions"
default-show-annotation-author-labels
:layout-style="{ height: '100vh' }"
/>
</template>
整个批注器只读时,可传入 :annotation-permissions="{ can: () => false }"。完整的操作矩阵、业务规则覆盖和安全边界见协作批注权限。
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 } | 自定义操作区域,渲染在工具栏右侧 |
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>
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'。