@os-team-11/editor 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,26 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 os-team-11 contributors
4
+
5
+ This software bundles or references @hufe921/canvas-editor
6
+ (https://github.com/Hufe921/canvas-editor), which is licensed under the MIT
7
+ License by Hufe921. All rights in the upstream software remain with its
8
+ authors.
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,285 @@
1
+ # @os-team-11/editor
2
+
3
+ 基于 [@hufe921/canvas-editor](https://github.com/Hufe921/canvas-editor) 的招投标文档编辑器 React 组件,提供结构化 JSON 数据(`BidSchema`)与所见即所得编辑之间的双向适配。
4
+
5
+ - **结构化数据优先**:以 `BidSchema` 作为单一数据源,AI/后端无需直接操作 canvas-editor 内部结构
6
+ - **工具栏开箱即用**:内置 36 个常用按钮(字体、段落、列表、表格、图片、公式等),支持动态溢出折叠到「更多」
7
+ - **可定制**:主题(亮/暗)、工具栏按钮、渲染器均可覆盖
8
+ - **AI 问答**:M5.2 起内置选区右键菜单接入自定义 `ask` 函数
9
+
10
+ ## 安装
11
+
12
+ ```bash
13
+ npm install @os-team-11/editor
14
+ # 或
15
+ pnpm add @os-team-11/editor
16
+ ```
17
+
18
+ ### Peer dependencies
19
+
20
+ 本包把以下依赖声明为 `peerDependencies`,需要使用方自行安装:
21
+
22
+ ```bash
23
+ npm install react@^18 react-dom@^18 @hufe921/canvas-editor@^0.9.0
24
+ ```
25
+
26
+ React 19 也支持。canvas-editor 必须与 peer range 内版本兼容。
27
+
28
+ ## 快速开始
29
+
30
+ ```tsx
31
+ import { BidEditor, validateBid, type BidSchema } from '@os-team-11/editor'
32
+
33
+ const sample: BidSchema = {
34
+ meta: {
35
+ title: 'XX 项目招标文件',
36
+ docNo: 'ZB-2026-001',
37
+ tenderer: 'XX 采购中心',
38
+ version: '1.0.0',
39
+ generatedAt: '2026-06-16T00:00:00Z',
40
+ },
41
+ page: {
42
+ size: 'A4',
43
+ margins: { top: 96, right: 96, bottom: 96, left: 96 },
44
+ },
45
+ sections: [
46
+ {
47
+ id: 'sec-1',
48
+ title: '第一章 投标邀请',
49
+ level: 1,
50
+ blocks: [
51
+ { type: 'heading', level: 2, text: '一、项目概况' },
52
+ { type: 'paragraph', text: '本次招标的项目为 XX 系统建设。' },
53
+ { type: 'list', ordered: true, items: ['独立法人', '具备相关资质'] },
54
+ ],
55
+ },
56
+ ],
57
+ }
58
+
59
+ export default function App() {
60
+ const result = validateBid(sample)
61
+ if (!result.success) throw new Error('invalid bid')
62
+
63
+ return (
64
+ <div style={{ height: '100vh' }}>
65
+ <BidEditor bid={result.data} onChange={next => console.log(next)} />
66
+ </div>
67
+ )
68
+ }
69
+ ```
70
+
71
+ 样式通过入口 `import './react/styles.css'` 自动注入,多数构建器(Vite / Webpack 5 / Rollup)会自动打包到产物里。若样式没生效,手动 `import '@os-team-11/editor/dist/index.css'` 即可。
72
+
73
+ ## Props
74
+
75
+ ### `BidEditorProps`
76
+
77
+ | Prop | 类型 | 默认值 | 说明 |
78
+ | ------------- | ----------------------------------- | -------- | ------------------------------------------------------ |
79
+ | `bid` | `BidSchema` | - | **必填**,编辑器数据源 |
80
+ | `onChange` | `(bid: BidSchema) => void` | - | 调用 `handle.getBid()` 时触发(读时触发,非实时) |
81
+ | `readOnly` | `boolean` | `false` | 只读模式,工具栏隐藏 |
82
+ | `toolbar` | `ToolbarConfig \| false` | `true` | 工具栏配置,`false` 完全隐藏 |
83
+ | `theme` | `Partial<Theme>` | 内置亮色 | 主题覆盖,`{ dark: true }` 切换暗色 |
84
+ | `renderers` | `Renderers` | - | 覆盖按钮、下拉、AI 对话等渲染 |
85
+ | `fieldPanel` | `boolean` | `false` | 显示右侧字段面板 |
86
+ | `ai` | `AIConfig` | - | AI 问答配置(必传 `ask` 函数才启用) |
87
+ | `breakpoint` | `'mobile' \| 'tablet' \| 'desktop'` | 自动 | 强制断点;`mobile` 隐藏工具栏 |
88
+ | `className` | `string` | - | 根容器 class |
89
+ | `style` | `CSSProperties` | - | 根容器 inline style |
90
+ | `ref` | `Ref<BidEditorHandle>` | - | 命令式 API 句柄 |
91
+
92
+ ### `BidEditorHandle`(ref 方法)
93
+
94
+ | Method | 签名 | 说明 |
95
+ | ------------ | ------------------- | ------------------------------------------------------------ |
96
+ | `getBid` | `() => BidSchema` | 拉取当前文档,触发 `onChange` |
97
+ | `getCommand` | `() => unknown` | 暴露 canvas-editor 的 `command` 对象(执行具体格式化命令) |
98
+ | `print` | `() => void` | 调用浏览器打印,可用于导出 PDF |
99
+ | `focus` | `() => void` | 当前版本为 noop(canvas-editor 0.9.x 无公开 focus API) |
100
+
101
+ ## 工具栏配置
102
+
103
+ `ToolbarConfig` 三种形态:
104
+
105
+ ```tsx
106
+ // 1. 默认全套(36 个按钮分 8 组)
107
+ <BidEditor bid={bid} toolbar={true} />
108
+
109
+ // 2. 自定义扁平按钮列表
110
+ <BidEditor bid={bid} toolbar={['bold', 'italic', 'underline', '|', 'undo', 'redo']} />
111
+
112
+ // 3. 按组配置 + 隐藏指定按钮
113
+ <BidEditor
114
+ bid={bid}
115
+ toolbar={{
116
+ hide: ['math', 'comment'], // 从默认配置里移除指定按钮
117
+ }}
118
+ />
119
+
120
+ // 4. 关闭工具栏
121
+ <BidEditor bid={bid} toolbar={false} />
122
+ ```
123
+
124
+ ### 36 个可用按钮 ID
125
+
126
+ ```
127
+ font-family, font-size,
128
+ bold, italic, underline, strikeout, superscript, subscript,
129
+ color, highlight,
130
+ align-left, align-center, align-right, align-justify, line-height,
131
+ h1, h2, h3, h4, list-ol, list-ul, indent, outdent,
132
+ undo, redo, format-painter, clear-format,
133
+ table, image, hyperlink, separator, page-break, math, control, date,
134
+ comment
135
+ ```
136
+
137
+ 工具栏宽度超出容器时,溢出的按钮组会自动折叠到「⋯ 更多」下拉,无需手动配置。
138
+
139
+ ## 主题
140
+
141
+ ```tsx
142
+ <BidEditor
143
+ bid={bid}
144
+ theme={{
145
+ dark: true, // 切换暗色
146
+ colors: { primary: '#FF7A00' }, // 覆盖品牌色
147
+ fontSize: 14,
148
+ }}
149
+ />
150
+ ```
151
+
152
+ 完整 `Theme` 结构:
153
+
154
+ ```ts
155
+ interface Theme {
156
+ colors: {
157
+ primary: string
158
+ text: string
159
+ border: string
160
+ background: string
161
+ hover: string
162
+ active: string
163
+ }
164
+ radius: { button: number; container: number }
165
+ spacing: { button: number; group: number }
166
+ fontSize: number
167
+ fontFamily: string
168
+ dark?: boolean
169
+ }
170
+ ```
171
+
172
+ ## AI 问答(M5.2)
173
+
174
+ 传入 `ask` 函数即可启用。用户选中文本后右键菜单会出现「AI 问答」选项。
175
+
176
+ ```tsx
177
+ <BidEditor
178
+ bid={bid}
179
+ ai={{
180
+ ask: async ({ selectedText, question, fullDocument }) => {
181
+ const res = await fetch('/api/ai-ask', {
182
+ method: 'POST',
183
+ body: JSON.stringify({ selectedText, question, fullDocument }),
184
+ })
185
+ return (await res.json()).answer
186
+ },
187
+ label: 'AI 问答',
188
+ placeholder: '问 AI 任何问题...',
189
+ includeFullDocument: false,
190
+ }}
191
+ />
192
+ ```
193
+
194
+ AI 对话 UI 默认内置,可通过 `renderers.aiDialog` 完全替换。
195
+
196
+ ## 数据结构
197
+
198
+ ### `BidSchema`
199
+
200
+ ```ts
201
+ interface BidSchema {
202
+ meta: BidMeta
203
+ page: BidPage
204
+ sections: BidSection[]
205
+ }
206
+
207
+ interface BidMeta {
208
+ title: string
209
+ docNo: string
210
+ tenderer: string
211
+ version: string
212
+ generatedAt: string
213
+ generator?: string
214
+ }
215
+
216
+ interface BidPage {
217
+ size: 'A4' | 'Letter' | { width: number; height: number }
218
+ margins: { top: number; right: number; bottom: number; left: number }
219
+ header?: { template: string; showOnFirstPage: boolean }
220
+ footer?: { template: string }
221
+ watermark?: { text: string; opacity: number }
222
+ }
223
+
224
+ interface BidSection {
225
+ id: string
226
+ title: string
227
+ level: 1 | 2 | 3
228
+ blocks: BidBlock[]
229
+ }
230
+
231
+ type BidBlock =
232
+ | { type: 'heading'; level: 2 | 3 | 4; text: string }
233
+ | { type: 'paragraph'; text: string }
234
+ | { type: 'list'; ordered: boolean; items: string[] }
235
+ | { type: 'table'; rows: string[][]; caption?: string }
236
+ | { type: 'image'; src: string; alt?: string; caption?: string }
237
+ ```
238
+
239
+ 用 `validateBid(sample)` 在渲染前校验数据合法性,避免坏数据导致运行时崩溃。
240
+
241
+ ## 命令式 API 示例
242
+
243
+ ```tsx
244
+ import { useRef } from 'react'
245
+ import { BidEditor, type BidEditorHandle } from '@os-team-11/editor'
246
+
247
+ function App() {
248
+ const ref = useRef<BidEditorHandle>(null)
249
+
250
+ const handlePrint = () => ref.current?.print()
251
+ const handleSave = () => {
252
+ const latest = ref.current?.getBid() // 触发 onChange
253
+ // 发送到后端
254
+ }
255
+
256
+ return (
257
+ <>
258
+ <button onClick={handlePrint}>打印 / PDF</button>
259
+ <button onClick={handleSave}>保存</button>
260
+ <BidEditor ref={ref} bid={bid} />
261
+ </>
262
+ )
263
+ }
264
+ ```
265
+
266
+ ## 已知限制
267
+
268
+ - **`onChange` 是读时触发**:仅在调用 `ref.getBid()` 时触发,不是编辑时实时推送。canvas-editor 0.9.x 没有公开的内容变更事件,M6 评估接入事件化。
269
+ - **`bid` prop 对身份敏感**:传入新对象(即使内容相同)会触发 destroy + recreate。父组件频繁重渲染时用 `useState` / `useMemo` 记忆。
270
+ - **`readOnly` 仅隐藏工具栏**:底层 canvas-editor 仍可编辑。完整只读等上游支持,计划 M5.1。
271
+ - **部分命令可能不生效**:`indent` / `outdent` / `comment` 在 canvas-editor 0.9.x 上方法名可能不一致,M5.1 修复。
272
+
273
+ ## 浏览器支持
274
+
275
+ - Chrome / Edge ≥ 90
276
+ - Safari ≥ 14
277
+ - Firefox ≥ 88
278
+
279
+ 依赖 `ResizeObserver`、`PointerEvent`、CSS Custom Properties。
280
+
281
+ ## 许可证
282
+
283
+ [MIT](./LICENSE) © os-team-11 contributors
284
+
285
+ 本包基于 [@hufe921/canvas-editor](https://github.com/Hufe921/canvas-editor)(MIT License, © Hufe921)构建。