@@ -79,37 +79,31 @@ This project provides foundational services for document preview components in t
7979
8080📚 ** Preview Component Documentation** : [ https://chaxus.github.io/ran/src/ranui/preview/ ] ( https://chaxus.github.io/ran/src/ranui/preview/ )
8181
82- ## 🧩 iframe 嵌入使用方式
82+ ## 🧩 Embedding via iframe
8383
84- 本项目支持通过 iframe 嵌入到其他业务系统中。推荐架构是: ** 父系统负责鉴权、下载文件和上传保存结果; iframe 只负责文档编辑 ** 。这样 token、cookie、业务接口都留在父系统内,编辑器不需要知道业务系统的授权细节。
84+ This project supports embedding into other systems via iframe. The recommended architecture is: ** the parent system handles authentication, file fetching, and upload; the iframe handles document editing only ** . This keeps tokens, cookies, and business APIs inside the parent system — the editor never needs to know about your auth details.
8585
86- 项目内置了一个示例页面:
87-
88- ``` text
89- /embed-demo.html
90- ```
91-
92- 本地启动后可以访问:
86+ A built-in demo page is included:
9387
9488``` text
9589http://127.0.0.1:8082/embed-demo.html
9690```
9791
98- ### 1. 嵌入编辑器
92+ ### 1. Embed the editor
9993
10094``` html
101- <iframe id =" documentEditor" src =" http://127.0.0.1:8082/?embed=1" style =" width : 100% ; height : 720px ; border : 0 " ></iframe >
95+ <iframe id =” documentEditor” src =” http://127.0.0.1:8082/?embed =1” style =” width : 100%; height: 720px; border: 0” ></iframe >
10296```
10397
104- 如果需要限制只接收指定父页面来源,可以增加 ` embedOrigin ` :
98+ To restrict messages to a specific parent origin, add ` embedOrigin ` :
10599
106100``` html
107- <iframe id =" documentEditor" src =" http://127.0.0.1:8082/?embed=1&embedOrigin=https://your-system.example.com" ></iframe >
101+ <iframe id =” documentEditor” src =” http://127.0.0.1:8082/?embed =1&embedOrigin =https://your-system.example.com” ></iframe >
108102```
109103
110- ### 2. 发送命令
104+ ### 2. Send commands
111105
112- 建议每条命令带上 ` id ` ,便于父页面匹配响应:
106+ Include an ` id ` on each command so you can match it to the response:
113107
114108``` js
115109const iframe = document .getElementById (' documentEditor' );
@@ -122,7 +116,7 @@ function sendEditorCommand(type, payload = {}) {
122116}
123117```
124118
125- 监听 iframe 响应:
119+ Listen for responses:
126120
127121``` js
128122window .addEventListener (' message' , (event ) => {
@@ -131,27 +125,16 @@ window.addEventListener('message', (event) => {
131125 const { id , type , payload } = event .data || {};
132126 if (! type || ! type .startsWith (' document:' )) return ;
133127
134- if (type === ' document:ready' ) {
135- console .log (' 编辑器已就绪' );
136- }
137-
138- if (type === ' document:opened' ) {
139- console .log (' 文档已打开' , id, payload);
140- }
141-
142- if (type === ' document:saved' ) {
143- console .log (' 保存完成' , payload .fileName , payload .file );
144- }
145-
146- if (type === ' document:error' ) {
147- console .error (' 编辑器错误' , payload .message );
148- }
128+ if (type === ' document:ready' ) console .log (' Editor ready' );
129+ if (type === ' document:opened' ) console .log (' Document opened' , id, payload);
130+ if (type === ' document:saved' ) console .log (' Saved' , payload .fileName , payload .file );
131+ if (type === ' document:error' ) console .error (' Error' , payload .message );
149132});
150133```
151134
152- ### 3. 打开文档
135+ ### 3. Open a document
153136
154- 通过 URL 打开:
137+ From URL:
155138
156139``` js
157140sendEditorCommand (' document:open-url' , {
@@ -161,133 +144,96 @@ sendEditorCommand('document:open-url', {
161144});
162145```
163146
164- 如果 URL 接口需要授权,可以传 ` fetchOptions ` ,但更推荐由父系统自己 ` fetch ` 后传入文件对象:
147+ If the URL requires auth headers, pass ` fetchOptions ` — though it's preferable to have the parent system fetch the file and pass it as a buffer:
165148
166149``` js
167150sendEditorCommand (' document:open-url' , {
168151 url: ' https://example.com/api/files/1' ,
169152 fileName: ' demo.xlsx' ,
170- readonly: false ,
171- fetchOptions: {
172- headers: {
173- Authorization: ` Bearer ${ token} ` ,
174- },
175- },
153+ fetchOptions: { headers: { Authorization: ` Bearer ${ token} ` } },
176154});
177155```
178156
179- 通过本地文件对话框打开:
157+ From a local file picker:
180158
181159``` js
182160const input = document .createElement (' input' );
183161input .type = ' file' ;
184162input .accept = ' .xlsx,.xls,.csv,.docx,.doc,.pptx,.ppt' ;
185163input .onchange = () => {
186- const file = input .files [0 ];
187- sendEditorCommand (' document:open-file' , {
188- file,
189- readonly: false ,
190- });
164+ sendEditorCommand (' document:open-file' , { file: input .files [0 ], readonly: false });
191165};
192166input .click ();
193167```
194168
195- 通过父系统授权请求后打开二进制数据:
169+ From an authenticated fetch (recommended for protected files):
196170
197171``` js
198172const response = await fetch (' /api/files/1' , {
199- headers: {
200- Authorization: ` Bearer ${ token} ` ,
201- },
173+ headers: { Authorization: ` Bearer ${ token} ` },
202174});
203-
204175const buffer = await response .arrayBuffer ();
205-
206- sendEditorCommand (' document:open-buffer' , {
207- fileName: ' demo.xlsx' ,
208- buffer,
209- readonly: false ,
210- });
176+ sendEditorCommand (' document:open-buffer' , { fileName: ' demo.xlsx' , buffer, readonly: false });
211177```
212178
213- ### 4. 设置只读
214-
215- 打开文档时可以直接设置:
216-
217- ``` js
218- sendEditorCommand (' document:open-buffer' , {
219- fileName: ' demo.xlsx' ,
220- buffer,
221- readonly: true ,
222- });
223- ```
179+ ### 4. Read-only mode
224180
225- 文档打开后也可以切换:
181+ Set at open time, or toggle afterwards:
226182
227183``` js
228- sendEditorCommand (' document:set-readonly' , {
229- readonly: true ,
230- });
184+ sendEditorCommand (' document:set-readonly' , { readonly: true });
231185```
232186
233- 只读模式下编辑权限会关闭,保存命令会返回 ` document:error ` 。
187+ In read-only mode editing is disabled and ` document:save ` returns ` document: error` .
234188
235- ### 5. 保存并上传到服务端
189+ ### 5. Save and upload
236190
237- 保存命令会触发编辑器导出当前正在编辑的内容,并通过 ` document:saved ` 返回一个新的 ` File ` 对象。默认保存为 ` XLSX ` ,也可以传入其他格式,例如 ` DOCX ` 、 ` PPTX ` 、 ` CSV ` 。
191+ The save command exports the current document and returns a ` File ` via ` document:saved ` . Default format is ` XLSX ` ; pass ` targetExt ` to change it ( ` DOCX ` , ` PPTX ` , ` CSV ` ).
238192
239193``` js
240- sendEditorCommand (' document:save' , {
241- targetExt: ' XLSX' ,
242- });
194+ sendEditorCommand (' document:save' , { targetExt: ' XLSX' });
243195```
244196
245- 默认情况下,保存命令必须等到编辑器返回当前编辑后的文件数据;如果超时会返回 ` document:error ` ,避免误把原始文件上传到服务端。如果你的业务确实希望“没有修改时也回传原文件”,可以显式开启:
197+ By default the command waits for the editor to return the edited file — if it times out, ` document:error ` is returned instead of silently uploading the original. To opt in to returning the original on timeout:
246198
247199``` js
248- sendEditorCommand (' document:save' , {
249- targetExt: ' XLSX' ,
250- returnOriginalOnTimeout: true ,
251- });
200+ sendEditorCommand (' document:save' , { targetExt: ' XLSX' , returnOriginalOnTimeout: true });
252201```
253202
254- 父页面拿到文件后自行上传:
203+ Upload the returned file from the parent:
255204
256205``` js
257206window .addEventListener (' message' , async (event ) => {
258207 if (event .origin !== editorOrigin) return ;
259-
260208 const { type , payload } = event .data || {};
261209 if (type !== ' document:saved' ) return ;
262210
263211 await fetch (' /api/files/1' , {
264212 method: ' POST' ,
265- headers: {
266- Authorization: ` Bearer ${ token} ` ,
267- },
213+ headers: { Authorization: ` Bearer ${ token} ` },
268214 body: payload .file ,
269215 });
270216});
271217```
272218
273- 注意:不要只用 ` size ` 判断文件是否变化, ` xlsx ` 是压缩包格式,轻微编辑后文件大小可能刚好不变。建议在调试时对返回的 ` File ` 计算 hash,项目内置的 ` /embed-demo.html ` 已经会在保存日志里打印 ` sha256 ` 。
274-
275- ### 6. 支持的消息
276-
277- | 方向 | 类型 | 说明 |
278- | --------------- | --------------------------- | ------------------------------------------ |
279- | 父页面 → iframe | ` document:open-url ` | 通过 URL 打开文档 |
280- | 父页面 → iframe | ` document:open-file ` | 通过 ` File ` / ` Blob ` 打开文档 |
281- | 父页面 → iframe | ` document:open-buffer ` | 通过 ` ArrayBuffer ` / ` Uint8Array ` 打开文档 |
282- | 父页面 → iframe | ` document:set-readonly ` | 设置只读或可编辑 |
283- | 父页面 → iframe | ` document:save ` | 保存并返回 ` File ` |
284- | 父页面 → iframe | ` document:get-state ` | 获取当前状态 |
285- | iframe → 父页面 | ` document:ready ` | iframe 初始化完成 |
286- | iframe → 父页面 | ` document:opened ` | 文档打开完成 |
287- | iframe → 父页面 | ` document:readonly-changed ` | 只读状态已切换 |
288- | iframe → 父页面 | ` document:saved ` | 保存完成,返回文件 |
289- | iframe → 父页面 | ` document:state ` | 返回当前状态 |
290- | iframe → 父页面 | ` document:error ` | 操作失败 |
219+ > ** Note: ** Do not rely on ` file. size` alone to detect changes — ` . xlsx` files are zip archives and a minor edit may produce the same byte count. The built-in ` /embed-demo.html ` prints a ` sha256 ` hash in the save log for debugging.
220+
221+ ### 6. Message reference
222+
223+ | Direction | Type | Description |
224+ | --------------- | --------------------------- | ---------------------------------------- |
225+ | parent → iframe | ` document:open-url ` | Open document from URL |
226+ | parent → iframe | ` document:open-file ` | Open document from ` File ` / ` Blob ` |
227+ | parent → iframe | ` document:open-buffer ` | Open document from ` ArrayBuffer ` / ` Uint8Array ` |
228+ | parent → iframe | ` document:set-readonly ` | Set read-only or editable |
229+ | parent → iframe | ` document:save ` | Save and return ` File ` |
230+ | parent → iframe | ` document:get-state ` | Query current state |
231+ | iframe → parent | ` document:ready ` | Editor initialised |
232+ | iframe → parent | ` document:opened ` | Document opened |
233+ | iframe → parent | ` document:readonly-changed ` | Read-only state changed |
234+ | iframe → parent | ` document:saved ` | Save complete, file returned |
235+ | iframe → parent | ` document:state ` | Current state response |
236+ | iframe → parent | ` document:error ` | Operation failed |
291237
292238## 🛠️ Technical Architecture
293239
@@ -347,8 +293,8 @@ services:
347293` ` ` bash
348294git clone https://github.com/ranuts/document.git
349295cd document
350- npm install
351- npm run dev
296+ pnpm install
297+ pnpm run dev
352298` ` `
353299
354300# # 🔤 Font Management
0 commit comments