初始化模版工程
This commit is contained in:
121
components/html-editor/mode/baseEdit.tsx
Normal file
121
components/html-editor/mode/baseEdit.tsx
Normal file
@@ -0,0 +1,121 @@
|
||||
import React, { useEffect, useRef } from 'react'
|
||||
import { useDebounceFn } from 'ahooks'
|
||||
import { cleanDom } from '../lib/core/utils'
|
||||
import { Html } from '../components/html-render/task-html'
|
||||
import { useIframeMode } from '../hooks/useIframeMode'
|
||||
import { useDiff } from '../hooks/useDiff'
|
||||
import { useEditState } from '../hooks/useEditState'
|
||||
import { FloatingToolbar } from '../../ppt-editor/FloatingToolbar'
|
||||
import type { ArtifactEditState } from '../types'
|
||||
import { saveMarkdown } from '../server'
|
||||
import { TaskArtifact } from '@/components/nova-sdk'
|
||||
|
||||
export const HtmlWithEditMode: React.FC<{
|
||||
taskId: string
|
||||
taskArtifact: TaskArtifact
|
||||
onStateChange?: (state: ArtifactEditState) => void
|
||||
content: string
|
||||
isDoc?: boolean
|
||||
|
||||
}> = props => {
|
||||
const { taskId, isDoc ,taskArtifact, content, onStateChange } = props
|
||||
const htmlIframe = useRef<HTMLIFrameElement>(null)
|
||||
const editState = useEditState()
|
||||
|
||||
const saveHandler = async (type: 'auto' | 'manual' = 'auto', callback?: () => void) => {
|
||||
if (editState.isSaving) return
|
||||
if (!htmlIframe.current) return
|
||||
const iframeDoc = htmlIframe.current.contentDocument?.documentElement
|
||||
if (!iframeDoc) return
|
||||
|
||||
editState.setSaveType(type)
|
||||
editState.setIsSaving(true)
|
||||
try {
|
||||
const srcDoc = cleanDom(iframeDoc)
|
||||
await saveMarkdown({
|
||||
task_id: taskId,
|
||||
content: srcDoc,
|
||||
path: taskArtifact.path,
|
||||
})
|
||||
callback?.()
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
} finally {
|
||||
editState.setIsSaving(false)
|
||||
editState.setSaveType(null)
|
||||
}
|
||||
}
|
||||
|
||||
const handleSave = useDebounceFn(saveHandler, { wait: 1000 })
|
||||
|
||||
const manualSave = () => {
|
||||
handleSave.cancel()
|
||||
saveHandler('manual')
|
||||
}
|
||||
|
||||
const useIframeReturn = useIframeMode(taskId, htmlIframe, {
|
||||
enableGlobalContentEditable: isDoc,
|
||||
onContentChange: content => {
|
||||
handleSave.run('auto')
|
||||
},
|
||||
onHistoryChange: (_state, instance) => {
|
||||
editState.handleHistoryChangeEvent(instance)
|
||||
// 同时向上传递状态,保持兼容性
|
||||
if (onStateChange && instance) {
|
||||
const canRedo = instance.EditorRegistry.canRedo()
|
||||
const canUndo = instance.EditorRegistry.canUndo()
|
||||
onStateChange({
|
||||
canRedo,
|
||||
canUndo,
|
||||
redo: () => instance.EditorRegistry.redo(),
|
||||
undo: () => instance.EditorRegistry.undo(),
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
const saveHandler = (callback?: () => void) => {
|
||||
if (!useIframeReturn.loadSuccess) {
|
||||
callback?.()
|
||||
return
|
||||
}
|
||||
if (!htmlIframe.current) return
|
||||
const iframeDoc = htmlIframe.current.contentDocument?.documentElement
|
||||
if (iframeDoc) {
|
||||
const srcDoc = cleanDom(iframeDoc)
|
||||
handleSave.run('auto', callback)
|
||||
}
|
||||
}
|
||||
// eventBus.on('html-edit-save', saveHandler)
|
||||
return () => {
|
||||
// eventBus.off('html-edit-save', saveHandler)
|
||||
}
|
||||
}, [useIframeReturn.loadSuccess])
|
||||
|
||||
// doc编辑与web编辑差异逻辑
|
||||
useDiff(useIframeReturn, isDoc)
|
||||
|
||||
return (
|
||||
<div className="relative h-full ">
|
||||
{/* 悬浮工具栏 */}
|
||||
|
||||
<Html
|
||||
className={`opacity-0 ${'opacity-100'} h-full`}
|
||||
content={content}
|
||||
ref={htmlIframe}
|
||||
/>
|
||||
<div style={{ display: 'flex', justifyContent: 'center', padding: '12px 0 12px', zIndex: 100, position: 'absolute', left: 0, right: 0, top: 80 }}>
|
||||
<FloatingToolbar
|
||||
canUndo={editState.canUndo}
|
||||
canRedo={editState.canRedo}
|
||||
onUndo={() => editState.undo.current()}
|
||||
onRedo={() => editState.redo.current()}
|
||||
onSave={manualSave}
|
||||
isSaving={editState.isSaving}
|
||||
saveType={editState.saveType}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
39
components/html-editor/mode/html-doc.tsx
Normal file
39
components/html-editor/mode/html-doc.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import React, { useRef } from 'react'
|
||||
import { Html } from '../components/html-render/task-html'
|
||||
import { HtmlWithEditMode } from './baseEdit'
|
||||
import { PPTEditProvider } from '../context'
|
||||
import { PPTEditToolBar } from '../components/toolbar-doc'
|
||||
import type { TaskArtifact } from '@/components/nova-sdk/types'
|
||||
import { useLoadContent } from '../hooks/useLoadContent'
|
||||
import type { ArtifactEditState } from '../types'
|
||||
|
||||
const HtmlDoc = ({
|
||||
taskId,
|
||||
taskArtifact,
|
||||
onStateChange,
|
||||
editable = false,
|
||||
}: {
|
||||
taskId: string
|
||||
taskArtifact: TaskArtifact
|
||||
onStateChange?: (state: ArtifactEditState) => void
|
||||
editable?: boolean
|
||||
}) => {
|
||||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
const content = useLoadContent(taskArtifact);
|
||||
|
||||
if (!editable) {
|
||||
return <Html className='h-full' content={content} />
|
||||
}
|
||||
return (
|
||||
<PPTEditProvider>
|
||||
<div className='relative h-full'>
|
||||
<PPTEditToolBar containerRef={containerRef} />
|
||||
<div className='h-full' ref={containerRef}>
|
||||
<HtmlWithEditMode taskId={taskId} taskArtifact={taskArtifact} content={content} isDoc={true} onStateChange={onStateChange}/>
|
||||
</div>
|
||||
</div>
|
||||
</PPTEditProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export const TaskHtmlDoc = React.memo(HtmlDoc)
|
||||
39
components/html-editor/mode/html-web.tsx
Normal file
39
components/html-editor/mode/html-web.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import React, { useRef } from 'react'
|
||||
import { Html } from '../components/html-render/task-html'
|
||||
import { PPTEditToolBar } from '../components/toolbar-web'
|
||||
import { PPTEditProvider } from '../context'
|
||||
import { HtmlWithEditMode } from './baseEdit'
|
||||
import type { TaskArtifact } from '@/components/nova-sdk/types'
|
||||
import { useLoadContent } from '../hooks/useLoadContent'
|
||||
import type { ArtifactEditState } from '../types'
|
||||
|
||||
function HtmlWeb({
|
||||
taskId,
|
||||
taskArtifact,
|
||||
onStateChange,
|
||||
editable = false,
|
||||
}: {
|
||||
taskId: string
|
||||
taskArtifact: TaskArtifact
|
||||
onStateChange?: (state: ArtifactEditState) => void
|
||||
editable?: boolean
|
||||
}) {
|
||||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
const content = useLoadContent(taskArtifact);
|
||||
|
||||
if (!editable) {
|
||||
return <Html className='h-full' content={content} />
|
||||
}
|
||||
return (
|
||||
<PPTEditProvider>
|
||||
<div className='relative h-full'>
|
||||
<PPTEditToolBar containerRef={containerRef} />
|
||||
<div className='h-full' ref={containerRef}>
|
||||
<HtmlWithEditMode taskId={taskId} taskArtifact={taskArtifact} content={content} isDoc={false} onStateChange={onStateChange}/>
|
||||
</div>
|
||||
</div>
|
||||
</PPTEditProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export const TaskHtmlWeb = React.memo(HtmlWeb)
|
||||
Reference in New Issue
Block a user