@szjy/pdf-coms
v0.1.3
Published
vue3 pdf component
Readme
@szjy/pdf-coms
一个功能强大、易于使用的 Vue 3 PDF 编辑器组件库,支持在线编辑 PDF 文档,包括添加图片、文本、绘图等多种对象,并提供完整的交互式编辑体验。
项目简介
@szjy/pdf-coms 是一个基于 Vue 3 和 TypeScript 开发的 PDF 编辑器组件库,旨在为 Web 应用提供强大的 PDF 在线编辑能力。该组件库解决了以下核心问题:
- 在线 PDF 编辑:无需下载 PDF 文件即可在浏览器中直接编辑
- 多类型对象支持:支持添加图片、文本、手绘图形等多种对象类型
- 交互式操作:提供拖拽、缩放、旋转等丰富的交互操作
- 中文字体支持:内置微软雅黑等中文字体,完美支持中文内容编辑
- 响应式设计:适配桌面端和移动端,提供一致的用户体验
适用场景
- 合同文档在线签署与标注
- PDF 表单填写与编辑
- 文档审批与批注
- 图纸标注与说明
- 教育培训材料编辑
目录结构
pdf-coms/
├── packages/ # 核心源代码目录
│ ├── api/ # API 接口层
│ │ └── project.ts # 项目相关 API
│ ├── common/ # 通用组件
│ │ ├── PDFObject/ # PDF 对象组件
│ │ │ ├── ImageObject.vue # 图片对象组件
│ │ │ ├── TextObject.vue # 文本对象组件
│ │ │ └── DrawingObject.vue # 绘图对象组件
│ │ ├── PDFPage.vue # PDF 页面渲染组件
│ │ ├── DrawingCanvas.vue # 绘图画布组件
│ │ ├── ThumbnailPanel.vue # 缩略图面板组件
│ │ ├── ObjectToolbar.vue # 对象工具栏组件
│ │ └── styles/ # 样式文件
│ ├── components/ # 主要组件
│ │ ├── PdfEditor/ # PDF 编辑器主组件
│ │ │ └── src/
│ │ │ └── index.vue # 编辑器核心实现
│ │ ├── components.ts # 组件导出
│ │ ├── installer.ts # 组件安装器
│ │ ├── create.ts # 组件创建工具
│ │ └── contants.ts # 常量定义
│ ├── composables/ # 组合式函数
│ │ ├── useObjects.ts # 对象管理逻辑
│ │ ├── usePDF.ts # PDF 处理逻辑
│ │ ├── usePannable.ts # 拖拽功能逻辑
│ │ └── useClickOutside.ts # 点击外部检测逻辑
│ ├── types/ # TypeScript 类型定义
│ │ ├── index.ts # 类型导出
│ │ ├── objects.ts # 对象类型定义
│ │ ├── pdf.ts # PDF 类型定义
│ │ └── errors.ts # 错误类型定义
│ └── utils/ # 工具函数
│ ├── pdfOperations.ts # PDF 操作工具
│ ├── asyncReader.ts # 异步文件读取
│ ├── assetLoader.ts # 资源加载器
│ ├── helpers.ts # 辅助函数
│ ├── request.ts # HTTP 请求工具
│ └── ... # 其他工具函数
├── examples/ # 示例代码
│ ├── App.vue # 示例应用入口
│ ├── components/ # 示例组件
│ │ └── PdfEditorTab.vue # PDF 编辑器示例
│ └── main.ts # 示例应用启动文件
├── dist/ # 构建输出目录
├── public/ # 静态资源
│ ├── fonts/ # 字体文件
│ └── scripts/ # 脚本文件
├── index.ts # 库入口文件
├── package.json # 项目配置
├── vite.config.mts # Vite 构建配置
├── tsconfig.json # TypeScript 配置
└── README.md # 项目文档核心模块说明
| 模块 | 职责 | 关键文件 | |------|------|----------| | components | 提供可复用的 UI 组件 | PdfEditor/index.vue | | composables | 封装业务逻辑的组合式函数 | usePDF.ts, useObjects.ts | | types | TypeScript 类型定义,确保类型安全 | objects.ts, pdf.ts | | utils | 通用工具函数和辅助方法 | pdfOperations.ts, asyncReader.ts | | common | 通用 UI 组件和样式 | PDFPage.vue, PDFObject/* |
模块依赖关系
graph TD
A[PdfEditor 主组件] --> B[composables 组合式函数]
A --> C[common 通用组件]
B --> D[utils 工具函数]
B --> E[types 类型定义]
C --> E
D --> E
C --> B环境与依赖配置
运行环境要求
- Node.js: >= 16.0.0
- 包管理器: pnpm (推荐) / npm / yarn
- 浏览器兼容性:
- Chrome >= 90
- Firefox >= 88
- Safari >= 14
- Edge >= 90
核心依赖
| 依赖 | 版本 | 用途 | |------|------|------| | vue | ^3.4.21 | Vue 3 框架 | | element-plus | 2.7.1 | UI 组件库 | | pdfjs-dist | ^2.3.200 | PDF 渲染引擎 | | pdf-lib | ^1.17.1 | PDF 文档操作库 | | axios | ^1.4.0 | HTTP 请求库 | | echarts | ^5.5.0 | 图表库(可选) | | moment | ^2.29.4 | 日期处理库 | | downloadjs | ^1.4.7 | 文件下载工具 |
开发依赖
| 依赖 | 版本 | 用途 | |------|------|------| | typescript | ^5.2.2 | TypeScript 支持 | | vite | ^5.1.6 | 构建工具 | | vite-plugin-dts | ^3.7.3 | TypeScript 声明文件生成 | | unplugin-icons | ^22.1.0 | 图标自动导入 | | unplugin-vue-components | ^0.26.0 | 组件自动导入 | | less | ^4.2.0 | Less 预处理器 | | sass | ^1.79.3 | Sass 预处理器 |
安装步骤
1. 克隆项目(开发者)
git clone <repository-url>
cd pdf-coms2. 安装依赖
# 使用 pnpm(推荐)
pnpm install
# 或使用 npm
npm install
# 或使用 yarn
yarn install3. 作为 npm 包使用
npm install @szjy/pdf-coms
# 或
pnpm add @szjy/pdf-coms推荐开发工具
- IDE: Visual Studio Code
- VSCode 插件:
- Vue Language Features (Volar)
- TypeScript Vue Plugin (Volar)
- ESLint
- Prettier
项目运行指南
开发模式
启动开发服务器,支持热更新:
pnpm dev服务器将在 http://localhost:8822 启动,并自动打开浏览器。
生产构建
标准构建(生产环境)
pnpm build构建产物将输出到 dist/ 目录,包括:
dist/index.mjs- ES Module 格式dist/index.umd.js- UMD 格式dist/index.d.ts- TypeScript 类型声明文件
开发构建(保留调试信息)
pnpm build:dev此模式会保留变量名和标识符,便于调试。
预览构建结果
pnpm preview发布版本
# 自动更新版本号并生成 CHANGELOG
pnpm release
# 同步到 cnpm
pnpm sync运行模式说明
| 命令 | 模式 | 用途 | 端口 |
|------|------|------|------|
| pnpm dev | 开发模式 | 本地开发调试 | 8822 |
| pnpm build | 生产构建 | 打包发布 | - |
| pnpm build:dev | 开发构建 | 调试构建产物 | - |
| pnpm preview | 预览模式 | 预览构建结果 | 默认 |
功能模块与使用示例
核心功能
PDF 文档加载与渲染
- 支持本地文件上传
- 支持拖拽上传
- 支持通过 API 加载远程 PDF
对象编辑功能
- 图片对象:添加、移动、缩放、删除
- 文本对象:添加、编辑、格式化、删除
- 绘图对象:手绘、移动、缩放、删除
页面管理
- 缩略图预览
- 页面添加与删除
- 页面跳转与选择
视图控制
- 缩放控制(30% - 300%)
- 滚轮缩放(Shift + 滚轮)
- 自适应缩放
导出功能
- 下载编辑后的 PDF
- 打印预览
- 生成 PDF Blob 对象
基础使用示例
1. 全局注册组件
// main.ts
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import PdfComs from '@szjy/pdf-coms'
import App from './App.vue'
const app = createApp(App)
app.use(ElementPlus)
app.use(PdfComs)
app.mount('#app')2. 在组件中使用
<template>
<sz-pdf-editor
ref="editorRef"
:show-upload="true"
>
<!-- 可选:自定义头部右侧内容 -->
<template #header-right>
<el-button @click="handleCustomAction">自定义操作</el-button>
</template>
</sz-pdf-editor>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const editorRef = ref(null)
function handleCustomAction() {
console.log('自定义操作')
}
</script>3. 通过 API 初始化 PDF
<template>
<sz-pdf-editor ref="editorRef" :show-upload="false" />
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import axios from 'axios'
const editorRef = ref(null)
onMounted(async () => {
try {
// 从服务器获取 PDF 数据
const response = await axios.get('/api/pdf/document.pdf', {
responseType: 'arraybuffer'
})
// 初始化 PDF 数据
await editorRef.value.initPdfData(response.data, 'document.pdf')
} catch (error) {
console.error('PDF 加载失败:', error)
}
})
</script>4. 获取编辑后的 PDF
<script setup lang="ts">
import { ref } from 'vue'
const editorRef = ref(null)
async function savePdfToServer() {
try {
// 获取编辑后的 PDF Blob
const pdfBlob = await editorRef.value.getPdfBlob()
// 上传到服务器
const formData = new FormData()
formData.append('file', pdfBlob, 'edited-document.pdf')
await axios.post('/api/pdf/upload', formData)
console.log('PDF 保存成功')
} catch (error) {
console.error('PDF 保存失败:', error)
}
}
</script>组件 Props
| 属性 | 类型 | 默认值 | 说明 | |------|------|--------|------| | showUpload | boolean | true | 是否显示"选择 PDF"按钮 |
组件方法(通过 ref 调用)
| 方法 | 参数 | 返回值 | 说明 | |------|------|--------|------| | initPdfData | (data: File | ArrayBuffer | Uint8Array, filename?: string) | Promise<boolean> | 初始化 PDF 数据 | | getPdfBlob | - | Promise<Blob> | 获取编辑后的 PDF Blob 对象 |
组件插槽
| 插槽名 | 说明 | |--------|------| | header-right | 头部工具栏右侧自定义内容 |
核心流程图
flowchart TD
A[用户上传 PDF] --> B[PDF 解析与渲染]
B --> C[显示 PDF 页面]
C --> D{用户操作}
D -->|添加图片| E[图片对象管理]
D -->|添加文本| F[文本对象管理]
D -->|添加绘图| G[绘图对象管理]
D -->|页面管理| H[添加/删除页面]
E --> I[对象交互编辑]
F --> I
G --> I
I --> J[实时预览]
J --> K{保存操作}
K -->|下载| L[生成 PDF 文件]
K -->|预览| M[打开新窗口预览]
K -->|获取 Blob| N[返回 PDF Blob]数据流转图
sequenceDiagram
participant User as 用户
participant Editor as PdfEditor 组件
participant Composable as usePDF/useObjects
participant Utils as pdfOperations
participant PDFLib as pdf-lib
User->>Editor: 上传 PDF 文件
Editor->>Composable: loadPDF(file)
Composable->>Utils: readAsPDF(file)
Utils-->>Composable: PDF 页面数组
Composable-->>Editor: 渲染页面
User->>Editor: 添加对象(图片/文本/绘图)
Editor->>Composable: addObject(pageIndex, object)
Composable-->>Editor: 更新对象列表
User->>Editor: 保存 PDF
Editor->>Composable: savePDF(file, objects)
Composable->>Utils: generatePDFBytes(file, objects)
Utils->>PDFLib: 嵌入对象到 PDF
PDFLib-->>Utils: PDF 字节数据
Utils-->>Composable: 下载文件项目维护规范
代码风格
项目使用 TypeScript 和 Vue 3 Composition API,遵循以下规范:
命名规范
- 组件名称: 使用 PascalCase(如
PdfEditor.vue) - 文件名: 组件使用 PascalCase,工具函数使用 camelCase
- 变量和函数: 使用 camelCase(如
loadPDF,pdfFile) - 常量: 使用 UPPER_SNAKE_CASE(如
COMP_PREFIX) - 类型定义: 使用 PascalCase(如
PDFObjectType)
TypeScript 规范
// ✅ 推荐:明确的类型定义
interface Props {
id: string
x: number
y: number
}
// ✅ 推荐:使用类型推断
const count = ref(0) // 自动推断为 Ref<number>
// ❌ 避免:使用 any 类型
const data: any = {} // 应该定义具体类型Vue 组件规范
<script setup lang="ts">
// ✅ 推荐:使用 Composition API
import { ref, computed } from 'vue'
interface Props {
title: string
}
const props = defineProps<Props>()
const emit = defineEmits<{
(e: 'update', value: string): void
}>()
</script>Git 工作流
分支命名规范
main- 主分支,保持稳定develop- 开发分支feature/xxx- 功能分支bugfix/xxx- 修复分支hotfix/xxx- 紧急修复分支
提交信息格式(Conventional Commits)
<type>(<scope>): <subject>
<body>
<footer>Type 类型:
feat: 新功能fix: 修复 bugdocs: 文档更新style: 代码格式调整(不影响功能)refactor: 代码重构perf: 性能优化test: 测试相关chore: 构建工具或辅助工具的变动
示例:
feat(editor): 添加页面删除功能
- 实现页面删除逻辑
- 更新缩略图面板
- 添加删除确认提示
Closes #123代码审查流程
创建 Pull Request
- 确保代码通过 TypeScript 类型检查
- 填写 PR 描述,说明改动内容
- 关联相关 Issue
代码审查要点
- 代码逻辑正确性
- 类型定义完整性
- 性能影响评估
- 代码可读性和可维护性
合并要求
- 至少一位审查者批准
- 通过所有自动化检查
- 解决所有审查意见
CI/CD 流程
graph LR
A[代码提交] --> B[类型检查]
B --> C[构建测试]
C --> D{检查通过?}
D -->|是| E[合并到主分支]
D -->|否| F[修复问题]
F --> A
E --> G[自动发布]版本发布流程
更新版本号和 CHANGELOG
pnpm release推送标签到远程仓库
git push --follow-tags origin main发布到 npm
npm publish同步到 cnpm
pnpm sync
常见问题与故障排查(FAQ)
1. PDF 加载失败
问题描述: 上传 PDF 文件后无法正常显示
可能原因:
- PDF 文件损坏或格式不正确
- PDF 文件过大导致内存溢出
- pdfjs-dist 版本不兼容
解决方案:
// 检查文件类型
if (file.type !== 'application/pdf') {
console.error('不是有效的 PDF 文件')
}
// 检查文件大小(建议限制在 50MB 以内)
if (file.size > 50 * 1024 * 1024) {
console.error('PDF 文件过大')
}2. 中文字体显示异常
问题描述: 添加中文文本后显示为方块或乱码
可能原因:
- 字体文件未正确加载
- 字体路径配置错误
- 浏览器不支持该字体
解决方案:
// 确保使用支持中文的字体
const fontFamily = '微软雅黑' // 或 'SimSun', 'SimHei'
// 预加载字体
import { loadFont } from '@/utils/assetLoader'
await loadFont(fontFamily)最佳实践:
- 优先使用微软雅黑字体
- 在
public/fonts/目录下放置字体文件 - 确保字体文件路径正确配置
3. 对象拖拽不流畅
问题描述: 拖拽图片或文本对象时出现卡顿
可能原因:
- PDF 页面缩放比例过大
- 对象数量过多
- 浏览器性能限制
解决方案:
// 优化渲染性能
const renderScale = computed(() => {
// 根据视图缩放调整渲染质量
return Math.max(1, Math.min(viewScale.value, 2))
})
// 限制对象数量
const MAX_OBJECTS_PER_PAGE = 504. 保存 PDF 时报错
问题描述: 点击下载按钮后提示"Failed to save PDF"
可能原因:
- 字体嵌入失败
- 图片格式不支持
- 内存不足
解决方案:
// 捕获详细错误信息
try {
await savePDF(pdfFile, objects, fileName, pagesScale)
} catch (error) {
console.error('保存失败详情:', error)
// 检查是否是字体问题
if (error.message.includes('font')) {
console.error('字体嵌入失败,请检查字体配置')
}
}5. 移动端触摸事件不响应
问题描述: 在移动设备上无法拖拽或编辑对象
可能原因:
- 触摸事件未正确绑定
- CSS touch-action 属性设置不当
解决方案:
<style scoped>
.objects-layer {
touch-action: none; /* 禁用浏览器默认触摸行为 */
}
</style>6. 打印预览窗口被拦截
问题描述: 点击打印预览按钮后没有反应
可能原因:
- 浏览器拦截了弹窗
- 用户未授权弹窗权限
解决方案:
const previewWindow = window.open(url, '_blank')
if (!previewWindow) {
ElMessage.warning('请允许浏览器弹窗,以便预览 PDF')
// 提供备用方案:直接下载
download(pdfBytes, fileName, 'application/pdf')
}7. TypeScript 类型错误
问题描述: 编译时出现类型错误
解决方案:
# 清理缓存并重新安装依赖
rm -rf node_modules pnpm-lock.yaml
pnpm install
# 重新生成类型声明文件
pnpm build性能优化建议
PDF 文件优化
- 压缩 PDF 文件大小
- 限制单个文件不超过 50MB
- 避免加载过多页面的 PDF
渲染优化
- 使用虚拟滚动加载页面
- 按需渲染可见区域
- 降低非活动页面的渲染质量
内存管理
- 及时释放不再使用的对象
- 避免创建过多的临时变量
- 使用 WeakMap 管理大对象引用
参考资料与延伸阅读
Vue 3 官方文档
https://cn.vuejs.org/
Vue 3 核心概念、Composition API 和最佳实践PDF.js 文档
https://mozilla.github.io/pdf.js/
Mozilla 开源的 PDF 渲染引擎,本项目用于 PDF 页面渲染pdf-lib 文档
https://pdf-lib.js.org/
JavaScript PDF 操作库,用于创建和修改 PDF 文档Element Plus 组件库
https://element-plus.org/zh-CN/
基于 Vue 3 的 UI 组件库,本项目使用的 UI 框架TypeScript 官方文档
https://www.typescriptlang.org/docs/
TypeScript 类型系统和高级特性Vite 构建工具文档
https://cn.vitejs.dev/
下一代前端构建工具,本项目使用的构建工具Canvas API 教程
https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API
用于图片渲染和绘图功能的 Canvas API 详解Web 字体加载最佳实践
https://web.dev/font-best-practices/
字体加载优化和中文字体处理方案
许可证
本项目采用 MIT 许可证。详见 LICENSE 文件。
贡献指南
欢迎提交 Issue 和 Pull Request!在提交 PR 之前,请确保:
- 代码通过 TypeScript 类型检查
- 遵循项目代码规范
- 提交信息符合 Conventional Commits 规范
- 更新相关文档
联系方式
如有问题或建议,请通过以下方式联系:
- 提交 Issue: GitHub Issues
- 邮箱: [联系邮箱]
版本: 0.1.1
最后更新: 2025-10-27
