English | 简体中文
一个现代化的开源可视化编辑器,基于 Vue 3 + TypeScript 构建
GitHub: https://github.com/garlicwu/polyeditor Gitee: https://gitee.com/wu_fan_xin/polyeditor
Poly Editor 是从 聚汇排版翻译大师 PolyPrint Studio 中衍生的开源项目。我们将核心的可视化编辑功能开源,希望为社区提供一个强大、易用的编辑器基础框架。
Poly Editor 是一个功能完整的可视化编辑器,提供类似 PowerPoint 的编辑体验,但更加灵活和可定制。主要功能包括:
- 多页面文档编辑:支持创建、管理多个页面,每个页面可以独立设置尺寸和样式
- 丰富的组件类型:文本、图片、图标、形状、表格、SVG 等多种组件,满足各种排版需求
- 专业的文本编辑:基于 Quill 2.0 的富文本编辑器,支持字体、颜色、对齐、列表等完整的文本格式
- 精确的布局控制:标尺、网格、吸附对齐、参考线等专业排版工具
- 强大的表格功能:集成 ag-grid,支持复杂的表格编辑和数据展示
- 完整的编辑操作:拖拽、缩放、旋转、翻转、图层管理、组合、撤销重做等
- PDF 导入功能:支持导入 PDF 文件并自动识别文本内容(基于智谱 AI OCR)
- 本地化存储:所有数据保存在浏览器本地,无需后端服务器,保护数据隐私
- 数据导出:支持导出为 JSON 格式和 PDF 文档
适用场景:文档排版、海报设计、报告制作、演示文稿、证书生成、表单设计等。
PolyPrint Studio 试用地址: http://demo.jhu-ai.com/
完整版产品在开源版本基础上增加了:
- 多语言翻译功能
- 云端存储和协作
- 更多专业模板
- 高级排版功能
欢迎大家试用完整版产品,并支持开源项目的发展!
新功能
- ✨ PDF 导入功能: 支持导入 PDF 文件并自动识别文本内容
- 集成智谱 AI OCR API 进行文档解析
- 自动提取文本块的位置和内容
- 支持多页 PDF 文档导入
- 可配置默认字体和字号
- 导入的页面自动插入到当前页面后面
使用方法
- 点击顶部工具栏"导入 PDF"按钮
- 输入智谱 AI API Key(首次使用需要)
- 选择 PDF 文件(支持 ≤50MB,最多 100 页)
- 等待 OCR 识别完成
- 调整导入配置(可选)
- 点击"导入"按钮
技术说明
- 使用智谱 AI glm-ocr 模型进行文档解析
- 支持的文件格式:PDF
- 文件大小限制:≤50MB
- 页数限制:最多 100 页
- API 文档:智谱 AI OCR 服务
- 邮箱: 124005421@qq.com
- 微信: melevenalk
如有任何问题、建议或合作意向,欢迎随时联系!
- 🎨 可视化编辑 - 所见即所得的拖拽式编辑体验
- 📝 富文本编辑 - 基于 Quill 2.0 的专业文本编辑器,支持多种格式
- 🖼️ 多媒体支持 - 图片、图标、形状、SVG 等多种组件类型
- 📊 表格编辑 - 集成 ag-grid 35.0 的强大表格功能
- 📄 多页面管理 - 支持创建、删除、排序多个页面
- 🎯 精确定位 - 标尺、网格线、吸附对齐等辅助工具
↔️ 自由变换 - 拖拽移动、缩放、旋转、翻转- 📐 智能对齐 - 自动吸附、对齐线、分布排列
- 🔄 撤销重做 - 完整的操作历史记录
- 📋 剪贴板 - 复制、粘贴、剪切、删除
- 🎭 图层管理 - 置顶、置底、上移、下移
- 🔗 组合操作 - 组合、取消组合、批量选择
- 💾 本地存储 - 数据保存在浏览器 localStorage,无需后端
- 📤 JSON 导出 - 导出完整的编辑数据为 JSON 格式
- 📥 数据导入 - 支持导入之前导出的 JSON 数据
- 📄 PDF 导入 - 导入 PDF 文件并自动识别文本内容(智谱 AI OCR)
- 📑 PDF 导出 - 将页面导出为 PDF 文档
- Node.js 22.14.0 或更高版本(推荐使用 Volta 管理)
- pnpm 10.30.2 或更高版本
# 克隆项目(GitHub)
git clone https://github.com/garlicwu/polyeditor.git
# 或者从 Gitee 克隆
# git clone https://gitee.com/wu_fan_xin/polyeditor.git
# 进入项目目录
cd polyeditor
# 安装依赖
pnpm install# 启动开发服务器
pnpm dev
# 访问 http://localhost:8081# 构建生产版本
pnpm build
# 预览生产构建
pnpm preview
# 类型检查
npx vue-tsc --noEmit- Vue 3.4 - 渐进式 JavaScript 框架
- TypeScript 5.8 - 类型安全的 JavaScript 超集
- Vite 5.3 - 下一代前端构建工具
- PrimeVue 4.2 - 丰富的 Vue UI 组件库
- Tailwind CSS 4.0 - 实用优先的 CSS 框架
- @tailwindcss/vite - Tailwind CSS Vite 插件
- Pinia 2.1 - Vue 的直观状态管理库(Composition API 风格)
- Quill 2.0.3 - 强大的富文本编辑器
- ag-grid 35.0 - 企业级表格组件
- simple-panzoom - 画布缩放和平移
- vue-draggable-plus - 拖拽排序功能
- Lodash - JavaScript 实用工具库
- Axios - HTTP 客户端
- Moment.js - 日期时间处理
- html2canvas + jspdf - PDF 导出功能
- @vueuse/core - Vue Composition API 工具集
poly-editor/
├── src/
│ ├── assets/ # 静态资源
│ │ ├── svg/ # SVG 图标
│ │ └── images/ # 图片资源
│ ├── lib/ # 工具库和配置
│ │ ├── mitt.ts # 事件总线
│ │ ├── storage.ts # localStorage 封装
│ │ ├── pdf-util.ts # PDF 导出工具
│ │ └── mockData.ts # 模拟数据
│ ├── net/ # 网络请求(保留但不依赖后端)
│ ├── router/ # 路由配置
│ ├── store/ # Pinia 状态管理
│ │ ├── editorStore.ts # 编辑器核心状态
│ │ ├── textEdiotrSotre.ts # 文本编辑器状态
│ │ ├── useAreaMoveStore.ts # 组件移动状态
│ │ ├── useTableStateStore.ts # 表格状态
│ │ └── useViewDraggerStore.ts # 拖拽状态
│ ├── style/ # 全局样式
│ ├── types/ # TypeScript 类型定义
│ └── view/ # 视图组件
│ ├── common/ # 通用组件
│ │ ├── drager/ # 拖拽组件系统
│ │ └── sketch-ruler/ # 标尺组件
│ └── editor/ # 编辑器核心
│ ├── component/ # 编辑器组件
│ │ ├── area-view.vue # 画布区域
│ │ ├── quill-text-view.vue # 富文本编辑
│ │ ├── image-view.vue # 图片组件
│ │ ├── table-view.vue # 表格组件
│ │ └── popover/ # 弹出式工具栏
│ ├── hooks/ # 组合式函数
│ │ ├── useActions.ts # 编辑操作
│ │ └── useArea.ts # 区域选择
│ ├── layout/ # 布局组件
│ │ ├── topLayout/ # 顶部工具栏
│ │ ├── leftLayout/ # 左侧面板
│ │ └── centerLayout/ # 中央画布
│ ├── tool/ # 工具函数
│ └── utils/ # 工具类
├── public/ # 公共资源
├── main/ # 构建输出目录
├── CLAUDE.md # AI 开发指南
├── CONTRIBUTING.md # 贡献指南
└── LICENSE # MIT 许可证
- 基于 Quill 2.0.3 的富文本编辑器
- 支持字体、字号、颜色、粗体、斜体、下划线
- 支持对齐方式(左对齐、居中、右对齐、两端对齐)
- 支持行高、字间距调整
- 支持有序列表、无序列表
- 支持文本背景色
- 支持本地图片上传
- 支持图片缩放、裁剪
- 支持图片旋转、翻转
- 图片库管理(分类、搜索)
- 内置图标库
- 支持 SVG 图标
- 图标颜色自定义
- 图标大小调整
- 矩形、圆形、三角形等基础形状
- 支持填充色、边框色
- 支持边框宽度、样式调整
- 基于 ag-grid 35.0
- 支持单元格编辑
- 支持行列增删
- 支持表格样式自定义
- 支持数据排序、筛选
- 支持 SVG 图形编辑
- 独立的 SVG 编辑器页面
- 支持路径编辑
- 单选: 点击组件进行选择
- ��选: Ctrl/Cmd + 点击多个组件
- 框选: 拖拽选择多个组件
- 移动: 拖拽组件或使用方向键(支持 Shift 加速)
- 缩放: 拖拽控制点调整大小
- 旋转: 拖拽旋转控制点
- 翻转: 水平翻转、垂直翻转
- 对齐: 左对齐、右对齐、顶部对齐、底部对齐、水平居中、垂直居中
- 分布: 水平分布、垂直分布
- 吸附: 自动吸附到其他组件或画布边缘
- 对齐线: 实时显示对齐参考线
- 置顶: 将组件移到最上层
- 置底: 将组件移到最下层
- 上移一层: 向上移动一层
- 下移一层: 向下移动一层
- 组合: 将多个组件组合为一个整体
- 取消组合: 解散组合
- 复制 (Ctrl/Cmd + C): 复制选中的组件
- 粘贴 (Ctrl/Cmd + V): 粘贴复制的组件
- 剪切 (Ctrl/Cmd + X): 剪切选中的组件
- 删除 (Delete): 删除选中的组件
- 全选 (Ctrl/Cmd + A): 选择当前页面所有组件
- 复制 (Ctrl/Cmd + D): 快速复制选中的组件
- 撤销 (Ctrl/Cmd + Z): 撤销上一步操作
- 重做 (Ctrl/Cmd + Y): 重做上一步操作
- 支持无限次撤销重做
- 操作历史记录
- 新建页面: 创建新的空白页面
- 删除页面: 删除当前页面
- 复制页面: 复制当前页面及其所有组件
- 页面排序: 拖拽调整页面顺序
- 页面预览: 左侧面板实时预览所有页面
- 页面尺寸: A4、A3、A5、Letter 等预设尺寸
- 自定义尺寸: 支持自定义宽度和高度
- DPI 设置: 调整页面分辨率(72-300 DPI)
- 页面方向: 横向、纵向
- 页码设置: 自定义页码样式和位置
- 缩放: 10% - 300% 缩放范围
- 平移: 拖拽画布进行平移
- 适应窗口: 自动调整缩放以适应窗口
- 实际大小: 100% 显示
- 标尺: 显示水平和垂直标尺
- 网格: 显示网格线辅助对齐
- 参考线: 拖拽标尺创建参考线
- 吸附: 自动吸附到网格、参考线、其他组件
- 所有编辑数据自动保存到浏览器 localStorage
- 无需后端服务器,完全离线可用
- 支持多个项目独立存储
- JSON 导出: 导出完整的编辑数据为 JSON 格式
- 复制到剪贴板: 一键复制 JSON 数据
- 下载文件: 下载 JSON 文件到本地
- JSON 导入: 导入之前导出的 JSON 数据
- 数据恢复: 从备份恢复编辑数据
- 将所有页面导出为 PDF 文档
- 支持自定义 PDF 尺寸和质量
- 显示导出进度
-
创建新页面
- 点击左侧面板底部的"+"按钮
- 或右键点击页面列表选择"新建页面"
-
添加组件
- 从左侧面板选择组件类型(文本、图片、图标、形状、表格)
- 拖拽到画布上
- 或点击组件后在画布上点击放置
-
编辑组件
- 点击选中组件
- 拖拽移动位置
- 拖拽控制点调整大小
- 拖拽旋转控制点旋转
- 右键打开上下文菜单进行更多操作
-
文本编辑
- 双击文本组件进入编辑模式
- 使用顶部工具栏调整文本样式
- 支持快捷键(Ctrl+B 粗体、Ctrl+I 斜体等)
-
保存数据
- 点击顶部"保存"按钮
- 数据自动保存到浏览器本地
- 弹出导出对话框,可选择复制或下载 JSON
Ctrl/Cmd + C- 复制Ctrl/Cmd + V- 粘贴Ctrl/Cmd + X- 剪切Ctrl/Cmd + D- 快速复制Ctrl/Cmd + A- 全选Delete- 删除Ctrl/Cmd + Z- 撤销Ctrl/Cmd + Y- 重做
↑ ↓ ← →- 移动组件(1px)Shift + ↑ ↓ ← →- 快速移动(10px)Ctrl/Cmd + Shift + E- 切换对齐线Ctrl/Cmd + Shift + Y- 删除所有参考线Ctrl/Cmd + Shift + U- 删除当前页面参考线
-
批量编辑
- 按住 Ctrl/Cmd 点击多个组件
- 或框选多个组件
- 统一调整位置、大小、样式
-
精确定位
- 使用标尺和参考线
- 开启网格吸附
- 使用对齐工具
-
组件复用
- 复制常用组件
- 使用组合功能创建模板
- 跨页面复制粘贴
-
性能优化
- 避免单页面组件过多(建议 < 100 个)
- 大图片建议压缩后使用
- 定期清理不需要的页面
- 在
src/view/editor/component/创建新组件 - 在
editorComponentInit.ts注册组件类型 - 在
EComponentType枚举中添加类型定义
项目使用 Tailwind CSS,可以在 tailwind.config.js 中自定义主题:
export default {
theme: {
extend: {
colors: {
primary: '#your-color',
},
},
},
}- 在
src/view/editor/layout/topLayout/添加工具栏组件 - 在
useActions.ts中添加对应的操作函数 - 在
editorStore.ts中添加状态管理
Poly Editor 使用 JSON 格式存储所有编辑数据。数据保存在浏览器的 localStorage 中,也可以导出为 JSON 文件。
interface IEditorInfo {
id: string // 编辑器实例 ID
name: string // 项目名称
pageList: IEditorPageInfo[] // 页面列表
fontList: string[] // 使用的字体列表
dpi: number // DPI 设置
directoryType: string // 目录类型
footerType: string // 页脚类型
}每个页面包含以下信息:
interface IEditorPageInfo {
pageId: string // 页面唯一 ID
pageName: string // 页面名称
pageSize: IPageSize // 页面尺寸配置
componentList: IComponentInfo[] // 组件列表
pageFooter?: IPageFooter // 页脚配置
pageDirectory?: IPageDirectory // 目录配置
}interface IPageSize {
width: number // 宽度(毫米)
height: number // 高度(毫米)
sizeName: string // 尺寸名称(如 "A4")
type: EPageSizeType // 尺寸类型(毫米/像素)
isCustomize: boolean // 是否自定义尺寸
dpi: number // DPI 设置
pixelWidth: number // 像素宽度
pixelHeight: number // 像素高度
}所有组件共享的基础属性:
interface IComponentInfo {
componentId: string // 组件唯一 ID
componentType: EComponentType // 组件类型
left: number // X 坐标
top: number // Y 坐标
width: number // 宽度
height: number // 高度
rotate: number // 旋转角度
selected: boolean // 是否选中
zIndex: number // 层级
lock: boolean // 是否锁定
// 可选属性(根据组件类型)
content?: string // 文本内容(文本组件)
imgUrl?: string // 图片 URL(图片/图标组件)
style?: IComponentStyle // 样式配置
// ... 其他特定属性
}enum EComponentType {
Text = 'Text', // 文本组件
Image = 'Image', // 图片组件
Icon = 'Icon', // 图标组件
Shape = 'Shape', // 形状组件
Table = 'Table', // 表格组件
SVG = 'SVG', // SVG 组件
Group = 'Group', // 组合组件
}interface ITextComponent extends IComponentInfo {
componentType: 'Text'
content: string // 富文本内容(Quill Delta 格式)
style: {
fontSize: number // 字号
fontFamily: string // 字体
color: string // 文字颜色
backgroundColor: string // 背景色
textAlign: string // 对齐方式
lineHeight: number // 行高
letterSpacing: number // 字间距
bold: boolean // 粗体
italic: boolean // 斜体
underline: boolean // 下划线
}
}interface IImageComponent extends IComponentInfo {
componentType: 'Image'
imgUrl: string // 图片 URL 或 base64
originalWidth: number // 原始宽度
originalHeight: number // 原始高度
flipX: boolean // 水平翻转
flipY: boolean // 垂直翻转
opacity: number // 透明度 (0-1)
borderRadius: number // 圆角
}interface ITableComponent extends IComponentInfo {
componentType: 'Table'
tableData: {
columnDefs: any[] // 列定义(ag-grid 格式)
rowData: any[] // 行数据
gridOptions: any // 表格配置
}
style: {
headerBgColor: string // 表头背景色
headerTextColor: string // 表头文字颜色
cellBgColor: string // 单元格背景色
cellTextColor: string // 单元格文字颜色
borderColor: string // 边框颜色
borderWidth: number // 边框宽度
}
}{
"id": "project-123",
"name": "我的项目",
"dpi": 254,
"directoryType": "auto",
"footerType": "auto",
"fontList": ["Arial", "Microsoft YaHei"],
"pageList": [
{
"pageId": "page-1",
"pageName": "第 1 页",
"pageSize": {
"width": 210,
"height": 297,
"sizeName": "A4",
"type": "Millimetre",
"isCustomize": false,
"dpi": 254,
"pixelWidth": 2100,
"pixelHeight": 2970
},
"componentList": [
{
"componentId": "page-1C1234567890",
"componentType": "Text",
"left": 100,
"top": 100,
"width": 300,
"height": 100,
"rotate": 0,
"selected": false,
"zIndex": 1,
"lock": false,
"content": "<p>Hello World</p>",
"style": {
"fontSize": 16,
"fontFamily": "Arial",
"color": "#000000",
"textAlign": "left"
}
},
{
"componentId": "page-1C1234567891",
"componentType": "Image",
"left": 100,
"top": 250,
"width": 200,
"height": 150,
"rotate": 0,
"selected": false,
"zIndex": 2,
"lock": false,
"imgUrl": "data:image/svg+xml,...",
"originalWidth": 200,
"originalHeight": 150,
"flipX": false,
"flipY": false,
"opacity": 1
}
]
}
]
}点击"保存"按钮后,会弹出导出对话框:
// 导出的 JSON 数据
const exportData = {
version: "1.0.0", // 数据格式版本
exportTime: "2025-01-01", // 导出时间
editorInfo: { /* 完整的编辑器数据 */ }
}导入 JSON 文件时,系统会验证数据格式:
// 导入验证
if (data.editorInfo && data.editorInfo.pageList) {
// 加载数据到编辑器
editorStore.loadEditorInfo(data.editorInfo)
}数据存储在浏览器 localStorage 中:
// 存储键
const STORAGE_KEY = 'poly-editor-data'
// 存储格式
localStorage.setItem(STORAGE_KEY, JSON.stringify(editorInfo))
// 读取数据
const data = JSON.parse(localStorage.getItem(STORAGE_KEY))如果数据格式发生变化,系统会自动进行数据迁移:
// 版本检查和迁移
function migrateData(data: any) {
if (!data.version || data.version < "1.0.0") {
// 执行数据迁移
data = migrateToV1(data)
}
return data
}- 数据大小限制: localStorage 通常限制为 5-10MB,建议单个项目不超过 5MB
- 图片存储: 建议使用 base64 或外部 URL,避免存储过大的图片
- 浏览器兼容性: 需要浏览器支持 localStorage API
- 数据备份: 定期导出 JSON 文件作为备份
- 隐私安全: localStorage 数据存储在本地,不会上传到服务器
如果需要添加自定义字段:
// 扩展组件数据
interface ICustomComponent extends IComponentInfo {
componentType: 'Custom'
customData: {
// 自定义字段
myField: string
myValue: number
}
}
// 在组件工厂中注册
EditorComponent.register('Custom', CustomComponentRenderer)我们欢迎所有形式的贡献!请查看 贡献指南 了解详情。
- Fork 本仓库
- 创建特性分支 (
git checkout -b feature/AmazingFeature) - 提交更改 (
git commit -m 'Add some AmazingFeature') - 推送到分支 (
git push origin feature/AmazingFeature) - 创建 Pull Request
- 使用 TypeScript 编写代码
- 遵循 ESLint 和 Prettier 配置
- 编写清晰的提交信息
- 为新功能添加适当的注释
本项目采用 MIT License 开源协议。
MIT 许可证是一个宽松的开源许可证,允许您:
- ✅ 商业使用
- ✅ 修改代码
- ✅ 分发
- ✅ 私人使用
唯一的要求是在所有副本中包含原始许可证和版权声明。
- 邮箱: 124005421@qq.com
- 微信: melevenalk
- Gitee 仓库: https://gitee.com/wu_fan_xin/polyeditor
- 提交 Issue: Gitee Issues
- 完整版产品: PolyPrint Studio
欢迎通过以上方式与我们交流,无论是技术问题、功能建议还是商业合作!
本项目使用了以下优秀的开源项目:
- Vue.js - 渐进式 JavaScript 框架
- Vite - 下一代前端构建工具
- PrimeVue - Vue UI 组件库
- Tailwind CSS - CSS 框架
- Quill - 富文本编辑器
- ag-grid - 表格组件
- Pinia - Vue 状态管理
- VueUse - Vue Composition API 工具集
感谢所有开源社区的贡献者!
Made with ❤️ by Poly Editor Contributors







