diff --git a/app/components/collabora/CollaboraViewer.tsx b/app/components/collabora/CollaboraViewer.tsx index a26e3e1..b5f9f3c 100644 --- a/app/components/collabora/CollaboraViewer.tsx +++ b/app/components/collabora/CollaboraViewer.tsx @@ -10,30 +10,50 @@ * @encoding UTF-8 */ -import { useRef } from 'react'; -import type { CollaboraViewerProps } from './types'; +import { useRef, forwardRef, useImperativeHandle, useState } from 'react'; +import type { CollaboraViewerProps, CollaboraViewerHandle } from './types'; import { useCollaboraConfig, useDocumentReady, useCollaboraUnoCommands } from './hooks'; +import { sendUnoCommand } from './Uno'; /** * Collabora 文档查看器组件 * @param props - 组件属性 + * @param ref - 父组件传入的 ref,用于暴露命令接口 */ -export function CollaboraViewer({ - fileId, - mode = 'view', - userId = 'guest', - userName = '访客', -}: CollaboraViewerProps) { - const iframeRef = useRef(null); +export const CollaboraViewer = forwardRef( + function CollaboraViewer( + { + fileId, + mode = 'view', + userId = 'guest', + userName = '访客', + }, + ref + ) { + const iframeRef = useRef(null); - // 1. 加载 Collabora 配置 - const { config, loading, error } = useCollaboraConfig(fileId, mode, userId, userName); + // 调试面板状态 + const [unoCmd, setUnoCmd] = useState('.uno:GoToStartOfDoc'); + const [unoArgs, setUnoArgs] = useState('{}'); + const [unoResult, setUnoResult] = useState(null); - // 2. 监听文档加载状态 - const { isDocumentLoaded } = useDocumentReady(iframeRef); + // 1. 加载 Collabora 配置 + const { config, loading, error } = useCollaboraConfig(fileId, mode, userId, userName); + + // 2. 监听文档加载状态 + const { isDocumentLoaded } = useDocumentReady(iframeRef); + + // 3. UNO 命令封装 + const unoCommands = useCollaboraUnoCommands(iframeRef); + + // 4. 暴露接口给父组件 + useImperativeHandle(ref, () => ({ + unoCommands, + isReady: isDocumentLoaded, + mode, + getIframeWindow: () => iframeRef.current?.contentWindow || null, + }), [unoCommands, isDocumentLoaded, mode]); - // 3. UNO 命令封装 - const unoCommands = useCollaboraUnoCommands(iframeRef); // 加载中状态 if (loading) { @@ -60,8 +80,68 @@ export function CollaboraViewer({ ); } + // 发送 UNO 命令的处理函数 + const sendUno = () => { + if (!iframeRef.current?.contentWindow) { + setUnoResult('iframe 不可用'); + return; + } + + let args: Record = {}; + const raw = (unoArgs || '').trim(); + if (raw !== '') { + try { + args = JSON.parse(raw) as Record; + } catch (err) { + try { + // fallback: replace single quotes with double quotes and parse + args = JSON.parse(raw.replace(/'(.*?)'/g, '"$1"')) as Record; + } catch (err2) { + console.error('解析 UNO Args 失败:', err2); + setUnoResult('Args 解析失败,请使用有效 JSON'); + return; + } + } + } + + try { + // 使用正确的 sendUnoCommand 方法 + sendUnoCommand(iframeRef.current.contentWindow, unoCmd, args); + setUnoResult(`已发送: ${unoCmd}`); + console.log('[UNO Debug] 发送命令:', unoCmd, args); + } catch (e) { + console.error('发送 UNO 失败:', e); + setUnoResult('发送失败,请查看控制台'); + } + }; + return (
+ {/* UNO 命令测试面板 */} +
+ setUnoCmd(e.target.value)} + placeholder="UNO 命令" + aria-label="UNO 命令" + /> + setUnoArgs(e.target.value)} + placeholder="UNO Args (JSON)" + aria-label="UNO Args (JSON)" + /> + + {unoResult && {unoResult}} +
+ {/* 文档加载提示 */} {!isDocumentLoaded && (
@@ -73,7 +153,8 @@ export function CollaboraViewer({
)} - {/* Collabora iframe */} + {/* Collabora iframe - tabIndex is needed for keyboard navigation */} + {/* eslint-disable jsx-a11y/no-noninteractive-tabindex */}