227 lines
8.6 KiB
TypeScript
227 lines
8.6 KiB
TypeScript
import React, {MouseEvent, useRef, useState} from "react";
|
|
import './plugins/index.ts'
|
|
import {Editor} from '@wangeditor/editor-for-react'
|
|
import {IDomEditor} from "@wangeditor/editor";
|
|
import {Descendant} from "slate";
|
|
import '@wangeditor/editor/dist/css/style.css'
|
|
import './style.less'
|
|
import {PROOFREAD_TYPE_DESC, WangEditorContent, WangEditorProofreadElement} from "../../types/editor.ts";
|
|
import {useMount} from "ahooks";
|
|
import {Button} from "antd";
|
|
import * as classNames from "classnames";
|
|
import IconArrowRight from "../../components/icons/IconArrowRight.tsx";
|
|
import IconCheckFill from "../../components/icons/IconCheckFill.tsx";
|
|
import IconCancelFill from "../../components/icons/IconCancelFill.tsx";
|
|
import {ProofreadPopover} from "./components/ProofreadPopover.tsx";
|
|
|
|
// {
|
|
// "attributes": {
|
|
// replace: {
|
|
// origin: '湖北省张家界市',
|
|
// text: '湖南省张家界市',
|
|
// type: 'address',
|
|
// },
|
|
// // "bold":true,
|
|
// },
|
|
// "insert": "湖北省张家界市",
|
|
// }
|
|
const defaultData: WangEditorContent[] = [
|
|
{
|
|
"type": "paragraph",
|
|
"children": [
|
|
{"text": "《", "fontFamily": "宋体"},
|
|
{
|
|
"text": "皇帝的新装",
|
|
"bold": true,
|
|
"color": "rgb(106, 57, 201)"
|
|
|
|
},
|
|
{"text": "》的作者是", "fontFamily": "宋体"},
|
|
{
|
|
"text": "闻名世界",
|
|
"bold": true,
|
|
"italic": true,
|
|
"underline": true,
|
|
"through": true,
|
|
"fontSize": "18pt"
|
|
},
|
|
{"text": "的", "fontFamily": "宋体"},
|
|
{
|
|
type: 'proofread',
|
|
proofread: {
|
|
origin: '湖北省张家界市',
|
|
text: '湖南省张家界市',
|
|
type: 'address',
|
|
},
|
|
children: [
|
|
{"text": "湖北省张家界市"},
|
|
]
|
|
},
|
|
{
|
|
"text": "丹麦作家",
|
|
"bold": true,
|
|
"color": "rgb(54, 88, 226)"
|
|
},
|
|
{
|
|
type: 'proofread',
|
|
proofread: {
|
|
origin: '安徒声',
|
|
text: '安徒生',
|
|
type: 'words',
|
|
},
|
|
children: [
|
|
{"text": "安徒声"},
|
|
]
|
|
},
|
|
{"text": "的作品。", "fontFamily": "宋体"}
|
|
]
|
|
},
|
|
{
|
|
"type": "paragraph",
|
|
"children": [
|
|
{
|
|
"text": "故事里有一个愚蠢的笨国王。他罕少关心国家,一昧追求的就是衣着入时。",
|
|
},
|
|
{
|
|
"text": "有一天,王国里来了俩个骗子。",
|
|
"fontFamily": "宋体"
|
|
}
|
|
]
|
|
}
|
|
]
|
|
const WangEditor: React.FC = () => {
|
|
const [html, setHtml] = useState<string>()
|
|
const [content, setContent] = useState<Descendant[]>()
|
|
const [editor, setEditor] = useState<IDomEditor>() // 存储 editor 实例
|
|
const [ops, setOps] = useState<WangEditorProofreadElement[]>([])
|
|
const [currentIndex, setCurrentIndex] = useState<number>(-1);
|
|
const editorConfig = {
|
|
placeholder: '请输入内容...',
|
|
}
|
|
const onMounted = (editor: IDomEditor) => {
|
|
setEditor(editor)
|
|
editor.children = defaultData
|
|
editor.on('select', (a, b) => {
|
|
console.log('select=》a,b', a, b)
|
|
})
|
|
}
|
|
const parseDesc = (data: WangEditorProofreadElement) => {
|
|
const {type, origin, text} = data.proofread
|
|
const desc = PROOFREAD_TYPE_DESC[type];
|
|
return desc.replace(/%origin%/ig, origin).replace(/%text%/ig, text)
|
|
}
|
|
const divRef = useRef<HTMLDivElement>(null)
|
|
|
|
|
|
const handleDivClick = (e: MouseEvent<HTMLDivElement>) => {
|
|
if (divRef.current) {
|
|
const target = e.target as HTMLElement;
|
|
if (target.classList.contains('data-proofread-item')) {
|
|
target.classList.add('data-proofread-item-selected')
|
|
return;
|
|
}
|
|
const selectArr = divRef.current.querySelectorAll('.data-proofread-item-selected')
|
|
Array.from(selectArr).forEach(it => it.classList.remove('data-proofread-item-selected'))
|
|
setCurrentIndex(-1)
|
|
}
|
|
}
|
|
useMount(() => {
|
|
const _arr: WangEditorProofreadElement[] = [];
|
|
let index = 0;
|
|
defaultData.forEach((it) => {
|
|
if (it.type == 'paragraph' && it.children && it.children.length > 0) {
|
|
it.children.forEach(cit => {
|
|
if (Object.hasOwn(cit, 'type')) {
|
|
const data = cit as WangEditorProofreadElement;
|
|
if (data.type && data.type == 'proofread') {
|
|
data.proofread.id = index++;
|
|
data.proofread.description = parseDesc(data)
|
|
_arr.push(data)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
})
|
|
setOps(_arr)
|
|
if (divRef.current) {
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
// @ts-ignore
|
|
divRef.current.addEventListener('proofread-click', handleDivClick)
|
|
}
|
|
})
|
|
|
|
const setSelectIndex = (index: number) => {
|
|
setCurrentIndex(index)
|
|
const span = document.querySelector(`.data-proofread-item-${index}`)
|
|
if (span) {
|
|
if (span.classList.contains('data-proofread-item-selected')) return;
|
|
document.querySelector(`.data-proofread-item-selected`)?.classList.remove('data-proofread-item-selected')
|
|
span.classList.add('data-proofread-item-selected')
|
|
}
|
|
}
|
|
|
|
|
|
return (<div className="wang-editor-page">
|
|
<div className={"editor-container"}>
|
|
<div
|
|
ref={divRef}
|
|
onClick={handleDivClick}
|
|
className="h-full flex-1"
|
|
>
|
|
<Editor
|
|
className="wang-editor-instance h-full"
|
|
defaultConfig={editorConfig}
|
|
value={html}
|
|
onCreated={onMounted}
|
|
onChange={editor => {
|
|
const op = editor.operations[0]
|
|
if (op?.type == 'set_selection') {
|
|
// console.log('onchange->', JSON.stringify(editor.selection))
|
|
} else {
|
|
setHtml(editor.getHtml())
|
|
setContent(editor.children)
|
|
// editor.getFragment
|
|
}
|
|
}}
|
|
mode="default"
|
|
/>
|
|
</div>
|
|
<div className="operation-wrapper">
|
|
<div className="operation-container h-full">
|
|
<div style={{margin: 10}}>
|
|
<Button onClick={() => {
|
|
const elems = editor?.getElemsByTypePrefix('header')
|
|
console.log(elems)
|
|
}}>获取所有目录</Button>
|
|
</div>
|
|
{ops.map((op, key) => (<div
|
|
className={classNames('operation-proofread-item', {selected: op.proofread.id == currentIndex})}
|
|
key={key}
|
|
onClick={() => setSelectIndex(Number(op.proofread.id))}
|
|
>
|
|
<div className="proofread-item-container">
|
|
<div className="proofread-data">
|
|
<span className="origin">{op.proofread.origin}</span>
|
|
<span className="icon"><IconArrowRight/></span>
|
|
<span className="text">{op.proofread.text}</span>
|
|
</div>
|
|
<div className="proofread-description">{op.proofread.description}</div>
|
|
</div>
|
|
<div className="proofread-action">
|
|
<ProofreadPopover content={<span>选用</span>}>
|
|
<IconCheckFill className="process"/>
|
|
</ProofreadPopover>
|
|
<ProofreadPopover content={<span>忽略</span>}>
|
|
<IconCancelFill className="cancel"/>
|
|
</ProofreadPopover>
|
|
</div>
|
|
</div>))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="content">{JSON.stringify(content)}</div>
|
|
</div>)
|
|
}
|
|
export default WangEditor;
|