uni-answer-sheet 1.0.5 → 2.0.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/README.md +312 -0
- package/THIRD_PARTY_NOTICES.md +1521 -0
- package/dist/browser.js +200 -0
- package/dist/chunks/index-D612hHe6.js +1081 -0
- package/dist/chunks/runtime-DFHaUhsH.js +24041 -0
- package/dist/index.d.ts +534 -0
- package/dist/index.js +5 -2
- package/dist/style.css +1 -0
- package/package.json +32 -149
- package/dist/index.css +0 -30267
package/README.md
ADDED
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
# Uni Answer Sheet
|
|
2
|
+
|
|
3
|
+
独立制卡插件,保留题卡布局、题型设置、分页和题卡合一编辑。通过显式注册 `uni-editor/plugin` 接入富文本。公共 API 只使用 DOM、普通对象和 Promise,适用于原生 JavaScript、Vue、React 等浏览器项目;宿主无需安装 React、Tiptap 或 MathLive。
|
|
4
|
+
|
|
5
|
+
`2.0.0` 使用全新的 DOM 插件 API,与原 `1.x` Vue 组件版接口不兼容。升级时请按本文重新挂载和注册富文本插件,旧版数据需转换为本文说明的文档格式。
|
|
6
|
+
|
|
7
|
+
## 安装与依赖
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install uni-answer-sheet
|
|
11
|
+
# 富文本由业务方单独安装,不会随制卡包自动安装。
|
|
12
|
+
npm install uni-editor@^0.2.0
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`uni-answer-sheet` 不依赖、不打包、不自动下载 `uni-editor`,也没有自动安装编辑器的 peer dependency。`uni-editor/plugin` 从 **0.2.0** 提供,旧版 0.1.0 没有此入口。两个包各自包含内部所需运行时。
|
|
16
|
+
|
|
17
|
+
本地验证未发布的版本时,可以分别安装构建生成的 `.tgz` 文件。
|
|
18
|
+
|
|
19
|
+
如果 npm 尚未提供配套的 `uni-editor@0.2.0`,需先手动安装该版本的本地包;`uni-editor@0.1.0` 无法作为制卡编辑器插件使用:
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
npm install ./uni-editor-0.2.0.tgz
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## 原生 JavaScript / TypeScript
|
|
26
|
+
|
|
27
|
+
```html
|
|
28
|
+
<div id="card" style="height: 800px; width: 100%"></div>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { createAnswerSheet } from 'uni-answer-sheet';
|
|
33
|
+
import { createEditorPlugin } from 'uni-editor/plugin';
|
|
34
|
+
import 'uni-answer-sheet/style.css';
|
|
35
|
+
import 'uni-editor/style.css';
|
|
36
|
+
|
|
37
|
+
const sheet = createAnswerSheet('#card', {
|
|
38
|
+
plugins: [createEditorPlugin()],
|
|
39
|
+
// initialValue: previouslySavedData,
|
|
40
|
+
uploadImage: async file => {
|
|
41
|
+
const form = new FormData();
|
|
42
|
+
form.append('file', file);
|
|
43
|
+
const response = await fetch('/your-upload-endpoint', { method: 'POST', body: form });
|
|
44
|
+
if (!response.ok) throw new Error('上传失败');
|
|
45
|
+
const { url } = await response.json();
|
|
46
|
+
return url;
|
|
47
|
+
},
|
|
48
|
+
onChange(draft) { /* 草稿变化,不代表已经通过完整性校验。 */ },
|
|
49
|
+
onSave(result) { /* 内置「保存」按钮的结果;由业务方决定如何持久化。 */ },
|
|
50
|
+
onError(error) { console.error(error); }
|
|
51
|
+
});
|
|
52
|
+
await sheet.ready;
|
|
53
|
+
|
|
54
|
+
const result = await sheet.save();
|
|
55
|
+
if (result.ok) {
|
|
56
|
+
console.log(result.data); // 可直接 JSON.stringify;提交后端由业务方负责。
|
|
57
|
+
} else {
|
|
58
|
+
console.log(result.issues);
|
|
59
|
+
sheet.focusIssue(result.issues[0]);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 路由离开或宿主组件卸载时:
|
|
63
|
+
sheet.destroy();
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
未传 `initialValue` 且未调用 `initialize()` 时,从白纸开始并显示新建弹窗,页面布局仍可在制卡设置中选择。也可以调用兼容的新建方法(采用默认布局):
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
await sheet.newDocument({
|
|
70
|
+
mode: 'template', // 或 'exam'
|
|
71
|
+
name: '高一数学答题卡', grade: '高一', subject: '数学'
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
上传回调可以省略,此时图片以 data URL 存入文档。回调返回的 URL 必须可供浏览器加载;临时 `blob:` URL 不适合持久保存,会被完整性校验拦截。
|
|
76
|
+
|
|
77
|
+
## 初始化试卷和页面布局
|
|
78
|
+
|
|
79
|
+
接入方已完成试卷信息、页面布局设置时,在创建实例后**立即调用** `initialize()`,无需先等待 `ready`。首屏直接显示配置好的空白题卡,不会先闪出新建弹窗。
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
const sheet = createAnswerSheet('#card', {
|
|
83
|
+
plugins: [createEditorPlugin()]
|
|
84
|
+
});
|
|
85
|
+
await sheet.initialize({
|
|
86
|
+
name: '高一数学期中试卷',
|
|
87
|
+
gradeCode: 'S01',
|
|
88
|
+
courseCode: 'MATHS',
|
|
89
|
+
mode: 'exam', // 可选,默认 template
|
|
90
|
+
layout: {
|
|
91
|
+
preset: 'a3-two', // a4 单栏 / a3-two 双栏 / a3-three 三栏
|
|
92
|
+
margin: 15, // 页边距,mm
|
|
93
|
+
gap: 10, // 栏间距,mm
|
|
94
|
+
questionCard: true,
|
|
95
|
+
scanMarking: false,
|
|
96
|
+
halfPoint: false,
|
|
97
|
+
choiceDirection: 'row', // row 横排 / column 竖排
|
|
98
|
+
mergeObjective: false
|
|
99
|
+
},
|
|
100
|
+
header: {
|
|
101
|
+
focusText: true, // 注意事项
|
|
102
|
+
writeKaohao: false, // 考场/座位号
|
|
103
|
+
missExam: false, // 填涂标记
|
|
104
|
+
mifeng: false, // 密封线
|
|
105
|
+
kaohao: 'school', // school 准考证号 / code 条形码
|
|
106
|
+
weishu: 8 // 准考证号位数,4–12 的整数
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
- 推荐传 `name`、`gradeCode`、`courseCode`,名称去除首尾空格后最多 30 个字符。仍兼容 `{ name, grade: '高一', subject: '数学' }` 旧参数。`mode`、`layout`、`header` 可省略,默认 A3 双栏、15 mm 边距、10 mm 栏间距及默认卷头。
|
|
112
|
+
- 方法返回 `Promise<void>`,等待工作区及编辑器就绪。生成名称对应的卷头,初始没有题目;后续可在界面修改布局、添加题块。空卡仍无法通过 `save()` 的完整性校验。
|
|
113
|
+
- 未调用此方法时保留原有弹窗流程。若配置来自异步请求,先取到配置,再创建实例并调用 `initialize()`;若等弹窗已经显示后才调用,也会应用配置并关闭弹窗。
|
|
114
|
+
- 非法参数抛出 `AnswerSheetDataError`,`issues` 包含字段位置,当前题卡保持不变;不会用静默回退掩盖错误配置。
|
|
115
|
+
- **再次调用会新建空卡并替换当前内容**,同时清空旧卡的选中状态和撤销记录。恢复已有题卡请用 `load(savedData)` 或 `initialValue`,无需再初始化。
|
|
116
|
+
|
|
117
|
+
## 年级、课程与学科字典
|
|
118
|
+
|
|
119
|
+
沿用参考项目的三层关系:`gradeCode` 年级、`courseCode` 学校课程、`subjectCode` 标准学科。课程名称可自定义,题型可用范围和高中数学计分规则按 `subjectCode` 判断。保存结果在 `data.creation` 中同时保留编码和显示名称;回显不依赖当时的权限字典。
|
|
120
|
+
|
|
121
|
+
未传字典时,年级为 `P01`–`P06`(小学)、`J01`–`J03`(初中)、`S01`–`S03`(高中)。内置基础学科使用 `CHINESE`、`MATHS`、`ENGLISH`、`PHYSICAL`、`CHEMICAL`、`BIOLOGICAL`、`POLITICAL`、`HISTORY`、`GEOGRAPHIC`、`SCIENCE`、`IT`、`JAPANESE`,按参考项目的学段限制显示。这只是默认基础字典;学校实际课程和权限由接入方传入,插件不会请求后端。
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
const sheet = createAnswerSheet('#card', {
|
|
125
|
+
plugins: [createEditorPlugin()],
|
|
126
|
+
dictionaries: {
|
|
127
|
+
// 可直接使用参考项目 allowGrades 的 value/label 选项。
|
|
128
|
+
grades: [{ value: 'S01', label: '高一' }],
|
|
129
|
+
// 对应 getGradeCourseMap;选项顺序由接入方决定。
|
|
130
|
+
gradeCourseMap: {
|
|
131
|
+
S01: [{ value: 'maths-school-01', label: '数学实验班' }]
|
|
132
|
+
},
|
|
133
|
+
// 对应 getCourseCodeToSubjectCode / getCourseToSubjectObj。
|
|
134
|
+
courseCodeToSubjectCode: { 'maths-school-01': 'MATHS' },
|
|
135
|
+
defaultGradeCode: 'S01',
|
|
136
|
+
defaultCourseCode: 'maths-school-01'
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
await sheet.initialize({
|
|
140
|
+
name: '高一数学试卷', mode: 'exam',
|
|
141
|
+
gradeCode: 'S01', courseCode: 'maths-school-01',
|
|
142
|
+
exampaperId: 'paper-123', createMode: 'MANUALLY',
|
|
143
|
+
layout: { pageNum: 2, quesCard: true, choiceType: false }
|
|
144
|
+
});
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
- 课程选项也可直接携带 `subjectCode`;若同时传映射表,两者须一致。自定义课程必须有明确学科映射,不从名称或编码前缀猜测。
|
|
148
|
+
- 新建弹窗过滤没有课程的年级;默认选指定年级/课程,无效默认值退到首个可用项。切换年级后选该年级第一门课程,与参考项目一致。
|
|
149
|
+
- 显式传 `grades: []` 或 `gradeCourseMap: {}` 表示没有可用项,不会回退到全部课程。异步取到字典后可 `sheet.setOptions({ dictionaries })`;完整替换字典,不影响当前题卡内容。若需要首屏免弹窗,应先取到字典再挂载并立即初始化。
|
|
150
|
+
- 字典选项使用 `{ value, label }`;原始 `GRADE` 字典的 `{ dictCode, dictValue }` 需由接入方转换。自定义年级可带 `classify: 'P' | 'J' | 'S'`;`J01-NEW`、`S01-NEW` 可作为接入方选项传入。
|
|
151
|
+
- 编码初始化检查年级/课程是否可用;同时传名称或 `subjectCode` 时必须与字典一致。传入字典后,旧名称初始化也必须匹配字典,同名课程需改用编码。非法字典抛出带字段路径的 `AnswerSheetDataError`,不替换当前配置。
|
|
152
|
+
- `id`(模板 ID)、`exampaperId`(参考项目的考试试卷 ID 拼写)、`createMode`(`MANUALLY` / `IMPORTED` / `THIRD`)是可选业务元数据,原样保存,不触发服务端创建或导入。
|
|
153
|
+
- 未调用初始化时,弹窗兼容路由 `gradeCode`、`courseCode`、`name`、`mode`、`id`、`exampaperId` 以及旧的 `grade`、`subject`;无效路由选项回退到当前可用项。
|
|
154
|
+
|
|
155
|
+
参考布局字段可直接放在 `layout`:
|
|
156
|
+
|
|
157
|
+
| 参考字段 | 当前文档字段 | 值含义 |
|
|
158
|
+
| --- | --- | --- |
|
|
159
|
+
| `pageNum` | `preset` | `1` → `a4`,`2` → `a3-two`,`3` → `a3-three` |
|
|
160
|
+
| `quesCard` | `questionCard` | 题卡合一 |
|
|
161
|
+
| `scoreType` | `halfPoint` | 允许半分 |
|
|
162
|
+
| `choiceType` | `choiceDirection` | `false` 横排 / `row`,`true` 竖排 / `column` |
|
|
163
|
+
| `mixOb` | `mergeObjective` | 客观题合并 |
|
|
164
|
+
|
|
165
|
+
别名仅用于初始化输入,保存仍使用当前文档字段;同时传两种字段时必须一致。卷头的 `focusText`、`writeKaohao`、`missExam`、`mifeng`、`kaohao`、`weishu` 已与参考项目同名,放在 `header`。参考项目 `answerInfo.courseCode` 是二维码显示开关,和这里的课程编码不是同一字段;当前不接收该二维码开关,也不接收整个 `pageinfoJsonstr` 作为初始化配置。
|
|
166
|
+
|
|
167
|
+
## 保存与完整性
|
|
168
|
+
|
|
169
|
+
`save(): Promise<SaveResult>` 等待输入、图片上传、字体加载和分页测量稳定,然后进行校验。连续保存调用共享当前进行中的保存过程。不会自动请求后端、下载文件或写 localStorage。
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
type SaveResult =
|
|
173
|
+
| { ok: true; data: AnswerSheetData }
|
|
174
|
+
| { ok: false; issues: ValidationIssue[] };
|
|
175
|
+
|
|
176
|
+
interface ValidationIssue {
|
|
177
|
+
code: string;
|
|
178
|
+
path: string;
|
|
179
|
+
message: string;
|
|
180
|
+
blockId?: string;
|
|
181
|
+
questionId?: string;
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
成功数据包含原始 `CardDocument`(`version: 1`、创建信息、卷头、题块、富文本、题型配置)以及参考项目格式的 `sectionInfoList`、`quesTextMap`。稳定题目 ID 与展示题号分别保留。完整数据可直接通过 `initialValue` 或 `load()` 回读;导出的对象是独立副本,外部修改不会影响当前题卡。
|
|
186
|
+
|
|
187
|
+
校验包括:
|
|
188
|
+
|
|
189
|
+
- 名称、年级、学科、纸面标题,以及非空题块/题目。
|
|
190
|
+
- 题块/题目/选项 ID、组内题号、题型、选项数、子题数、分值和子题总分。
|
|
191
|
+
- 少选得分、选做数量、断句评分、作文格数/行数等题型约束。
|
|
192
|
+
- 富文本结构、空公式、图片地址和可见图片加载失败。
|
|
193
|
+
- 题卡合一的题干、材料、选项内容,以及所选填涂位置的对应关系。普通答题卡不要求填写题干,也不要求提供正确答案。
|
|
194
|
+
- 纸面标题、不可拆分内容的纵向溢出,以及未分片编辑区域的横向溢出。
|
|
195
|
+
|
|
196
|
+
无法在 8 秒内完成输入/上传或稳定测量时返回 `EDITOR_BUSY` / `LAYOUT_PENDING`;容器隐藏或未设置有效尺寸时返回 `LAYOUT_UNAVAILABLE`,不会跳过布局校验。校验是当前数据和实际渲染的完整性检查,不判断学科内容或答案是否正确。外部图片/字体稍后失效仍需业务方管理。
|
|
197
|
+
|
|
198
|
+
结构非法的数据在加载前抛出 `AnswerSheetDataError`,其 `issues` 提供字段位置;缺少题干、分值等可编辑草稿允许加载,但不能通过保存校验。实例未就绪/已销毁属于调用错误,会抛异常。
|
|
199
|
+
|
|
200
|
+
## 回显已保存的题卡
|
|
201
|
+
|
|
202
|
+
将之前 `save()` 成功返回的 **`result.data` 整体保存**,回显时传给 `load(data): Promise<void>`。JSON 字符串先 `JSON.parse()`,不要传 `{ ok, data }` 外层结果,也不要只传 `sectionInfoList`。
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
// savedData 是业务方从自己的存储中取回的完整对象。
|
|
206
|
+
await sheet.load(savedData);
|
|
207
|
+
// 回显后可继续编辑;再次保存仍会检查完整性。
|
|
208
|
+
const result = await sheet.save();
|
|
209
|
+
if (result.ok) {
|
|
210
|
+
const json = JSON.stringify(result.data);
|
|
211
|
+
// 业务方自行持久化 json。
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
首次挂载即可回显,无需先创建空白卡:
|
|
216
|
+
|
|
217
|
+
```ts
|
|
218
|
+
const sheet = createAnswerSheet('#card', {
|
|
219
|
+
plugins: [createEditorPlugin()],
|
|
220
|
+
initialValue: savedData
|
|
221
|
+
});
|
|
222
|
+
await sheet.ready;
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
`load()` 替换整张卡,恢复纸张/分栏、名称与卷头、题块顺序、题型配置、富文本和题目 ID;分页按恢复的内容重新计算。旧卡的选中状态、设置弹窗和撤销记录会清空;插件、上传回调、只读设置与事件回调沿用当前实例。数据以副本载入,不会与外部对象共享修改。
|
|
226
|
+
|
|
227
|
+
`load()` 可在 `ready` 前调用,会等待工作区和编辑器初始化。它只检查数据结构,允许回显未完成草稿;输入结构非法时抛出 `AnswerSheetDataError`,原卡保持不变。字体、图片和分页是否完整可用仍由 `validate()` / `save()` 检查。若连续调用 `load()`,最后一次为准,被替换的调用以 `AbortError` 结束。保留的 `setDocument()` 与 `load()` 行为相同。
|
|
228
|
+
|
|
229
|
+
## API
|
|
230
|
+
|
|
231
|
+
| 方法 | 用途 |
|
|
232
|
+
| --- | --- |
|
|
233
|
+
| `ready: Promise<void>` | 等待工作区挂载;`save()` 自行继续等待各编辑器和布局就绪 |
|
|
234
|
+
| `initialize(options)` | 设置试卷信息、布局和卷头并新建空卡;创建实例后立即调用可跳过新建弹窗 |
|
|
235
|
+
| `getDocument()` | 当前草稿副本,不做完整性校验 |
|
|
236
|
+
| `load(data)` | 回显完整保存数据或草稿,清空旧卡的编辑状态 |
|
|
237
|
+
| `setDocument(value)` | `load()` 的兼容方法 |
|
|
238
|
+
| `newDocument(creation)` | 新建空白题卡 |
|
|
239
|
+
| `getReferenceData()` | 返回 `sectionInfoList` / `quesTextMap` |
|
|
240
|
+
| `setReferenceData(data)` | 接入参考项目的题目结构,保留当前纸张和卷头 |
|
|
241
|
+
| `validate()` | 返回 `{ valid, issues }`,不导出数据 |
|
|
242
|
+
| `save()` | 校验并返回完整保存数据 |
|
|
243
|
+
| `focusIssue(issue)` | 定位对应题块并打开设置 |
|
|
244
|
+
| `setOptions({ readOnly, uploadImage, onChange, onSave, onError })` | 更新行为,不重建文档;插件不可中途替换 |
|
|
245
|
+
| `destroy()` | 卸载画布、编辑器、菜单和监听 |
|
|
246
|
+
|
|
247
|
+
内置保存按钮触发 `onSave`。直接调用 `sheet.save()` 时从返回值接收结果,不额外触发该回调。
|
|
248
|
+
|
|
249
|
+
## Vue 3
|
|
250
|
+
|
|
251
|
+
```vue
|
|
252
|
+
<script setup lang="ts">
|
|
253
|
+
import { onMounted, onBeforeUnmount, ref } from 'vue';
|
|
254
|
+
import { createAnswerSheet, type AnswerSheetInstance } from 'uni-answer-sheet';
|
|
255
|
+
import { createEditorPlugin } from 'uni-editor/plugin';
|
|
256
|
+
import 'uni-answer-sheet/style.css';
|
|
257
|
+
import 'uni-editor/style.css';
|
|
258
|
+
|
|
259
|
+
const container = ref<HTMLElement>();
|
|
260
|
+
let sheet: AnswerSheetInstance | undefined;
|
|
261
|
+
onMounted(() => { sheet = createAnswerSheet(container.value!, { plugins: [createEditorPlugin()] }); });
|
|
262
|
+
onBeforeUnmount(() => sheet?.destroy());
|
|
263
|
+
defineExpose({ save: () => sheet!.save() });
|
|
264
|
+
</script>
|
|
265
|
+
<template><div ref="container" style="height: 800px" /></template>
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## React
|
|
269
|
+
|
|
270
|
+
```tsx
|
|
271
|
+
import { useEffect, useRef } from 'react';
|
|
272
|
+
import { createAnswerSheet, type AnswerSheetInstance } from 'uni-answer-sheet';
|
|
273
|
+
import { createEditorPlugin } from 'uni-editor/plugin';
|
|
274
|
+
import 'uni-answer-sheet/style.css';
|
|
275
|
+
import 'uni-editor/style.css';
|
|
276
|
+
|
|
277
|
+
export function CardDesigner() {
|
|
278
|
+
const container = useRef<HTMLDivElement>(null);
|
|
279
|
+
const instance = useRef<AnswerSheetInstance | null>(null);
|
|
280
|
+
useEffect(() => {
|
|
281
|
+
const sheet = createAnswerSheet(container.current!, { plugins: [createEditorPlugin()] });
|
|
282
|
+
instance.current = sheet;
|
|
283
|
+
return () => { instance.current = null; sheet.destroy(); };
|
|
284
|
+
}, []);
|
|
285
|
+
return <div ref={container} style={{ height: 800 }} />;
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
## 不使用构建工具
|
|
290
|
+
|
|
291
|
+
将两个包各自的 `dist/` 完整复制到不同目录,保留 `assets/` 字体和 CSS 相对路径。不要只复制 JavaScript。
|
|
292
|
+
|
|
293
|
+
```html
|
|
294
|
+
<link rel="stylesheet" href="/vendor/card/style.css">
|
|
295
|
+
<link rel="stylesheet" href="/vendor/editor/style.css">
|
|
296
|
+
<div id="card" style="height:800px"></div>
|
|
297
|
+
<script src="/vendor/editor/browser.js"></script>
|
|
298
|
+
<script src="/vendor/card/browser.js"></script>
|
|
299
|
+
<script>
|
|
300
|
+
const sheet = UniAnswerSheet.createAnswerSheet('#card', {
|
|
301
|
+
plugins: [UniEditor.createEditorPlugin()]
|
|
302
|
+
});
|
|
303
|
+
</script>
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
## 使用边界与插件协议
|
|
307
|
+
|
|
308
|
+
支持现代浏览器的 DOM 环境。SSR 可以导入模块,但须在客户端挂载后创建实例。样式限定在工作区及编辑器菜单内,不设置宿主 `body` 的宽高或滚动。
|
|
309
|
+
|
|
310
|
+
当前工作区使用一组页面导航/全屏 ID,**同一 document 同时只允许一个制卡实例**;第二次创建会明确报错。切换页面前销毁旧实例即可。独立富文本实例数量不受此限制。原生小程序、服务端排版不在支持范围内。
|
|
311
|
+
|
|
312
|
+
协议类型 `AnswerSheetEditorPlugin` 可直接从制卡包导入,使用结构类型,无跨包运行时依赖。v1 要求分页切片、内容身份、填涂绑定、作答区域、HTML 转换和忙碌状态能力,并约定 `.rt-content` 测量表面。`prepare()` 在制卡挂载时自动调用;缺少插件、重复注册或版本不兼容会立即报错。其他编辑器需完整实现该协议才能替换,不能仅实现一个文本输入框。
|