This commit is contained in:
LittleBoy 2023-08-06 22:27:38 +08:00
parent 738d23ca9d
commit d899c14bad
5 changed files with 163 additions and 18 deletions

View File

@ -3,6 +3,7 @@ import '@/App.less'
import {BrowserRouter, Navigate, Route, Routes} from "react-router-dom";
import TextPage from "./pages/quill/text.tsx";
import WangEditor from "./pages/wang/wang-editor.tsx";
import {Test} from "./pages/Test.tsx";
function App() {
@ -12,6 +13,7 @@ function App() {
<Route path="/" element={<Navigate to="/wang"/>}/>
<Route path="/proofread" element={<TextPage />}/>
<Route path="/wang" element={<WangEditor />}/>
<Route path="/test" element={<Test />}/>
</Routes>
</BrowserRouter>)

45
src/pages/Test.tsx Normal file
View File

@ -0,0 +1,45 @@
import React, {useRef} from "react";
import {Button, message, Space} from "antd";
export const Test: React.FC = () => {
const div = useRef<HTMLDivElement>(null)
const [messageApi, contextHolder] = message.useMessage();
const selectSpan = (selector: string) => {
return () => {
if (!div.current) {
return;
}
const span = div.current.querySelector(selector);
if (!span) return;
const sel = window.getSelection();
sel?.selectAllChildren(span);
}
}
const showSelection = async () => {
const sel = window.getSelection();
if (sel) {
const str = sel.toString();
messageApi.info(str || '没有选区')
}
}
return (<div ref={div} style={{width: '90%', margin: 'auto'}}>
{contextHolder}
<div style={{padding: 15}}>
<Space>
<Button type="primary" onClick={selectSpan('.js')}>select js</Button>
<Button type="primary" onClick={selectSpan('.cq')}>select cq</Button>
<Button type="primary" onClick={showSelection}>get selection</Button>
</Space>
</div>
<div className="container">
<div className="ql-editor" data-gramm="false" contentEditable="true">
<p className="ql-align-justify">访</p>
<p className="ql-align-justify"> <span className="js"></span> <span
className="cq"></span> </p>
<p className="ql-align-justify">访</p>
</div>
</div>
</div>
)
}

View File

@ -24,7 +24,7 @@ export function withProofread<T extends IDomEditor>(editor: T) {
* @param children void @param _editor
* @returns ( snabbdom.js h )
*/
export function renderProofread(data: SlateElement, children: VNode[] | null) { // , _editor: IDomEditor
export function renderProofread(data: SlateElement, children: VNode[] | null, _editor: IDomEditor) { // , _editor: IDomEditor
const {proofread} = data as WangEditorProofreadElement;
return h(
'span',
@ -33,14 +33,34 @@ export function renderProofread(data: SlateElement, children: VNode[] | null) {
dataSlateId: '123123'
}, // HTML 属性,驼峰式写法
attrs: {
'data-proofread-id': String(proofread.id)
'data-proofread-id': String(proofread.id),
'data-proofread-text': proofread.origin
},
attachData: {
proofread
},
className: `data-proofread-item data-proofread-item-${proofread.id}`,
className: `data-proofread-item data-proofread-item-${proofread.id} data-proofread-type-${proofread.type}`,
style: { /* 其他... */}, // style ,驼峰式写法 display: 'inline-block', margin: '0 2px',
on: {
process() {
// const path = DomEditor.getSelectedElems(_editor)
// DomEditor.findPath(_editor,span)
const path = DomEditor.findPath(_editor, data);
_editor.select(path)
_editor.insertText(proofread.text)
const span = this.elm as HTMLSpanElement;
span.classList.add('proofread-processed')
// if(proofread.type == 'delete'){
//
// return;
// }
//console.log('proofread process', data, this);
//Transforms.select(_editor, this.elm)
//window.getSelection()?.selectAllChildren(span);
//
// SlateTransforms.
},
click(e) {
e.stopPropagation()
e.preventDefault()

View File

@ -21,12 +21,24 @@
.data-proofread-item {
border-bottom: solid 3px #f00;
display: inline;
margin: 0 5px;
//margin: 0 5px;
cursor: pointer;
&.data-proofread-type-delete {
&::after {
content: attr(data-proofread-text);
text-decoration: line-through;
display: none;
}
&.proofread-processed::after{
display: inherit;
}
}
}
.data-proofread-item-selected {
background-color: #fbbbbb;
//outline: solid 1px #fbbbbb;
}
.editor-container {
@ -75,6 +87,10 @@
color: #ff0000;
}
.insert-text {
margin-left: 3px;
}
.icon {
margin: 0 6px;
position: relative;

View File

@ -7,7 +7,7 @@ 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 {Button, Space} from "antd";
import * as classNames from "classnames";
import IconArrowRight from "../../components/icons/IconArrowRight.tsx";
import IconCheckFill from "../../components/icons/IconCheckFill.tsx";
@ -66,14 +66,26 @@ const defaultData: WangEditorContent[] = [
type: 'proofread',
proofread: {
origin: '安徒声',
text: '安徒生',
type: 'words',
text: '汉斯·克里斯汀·安徒生',
type: 'words', // (汉斯·克里斯汀·安徒生,通称安徒生,丹麦作家暨诗人。)
},
children: [
{"text": "安徒声"},
]
},
{"text": "的作品。", "fontFamily": "宋体"}
{"text": "的作品", "fontFamily": "宋体"},
{
type: 'proofread',
proofread: {
origin: '作品',
text: '',
type: 'delete'
},
children: [
{"text": "作品"},
]
},
{"text": "。", "fontFamily": "宋体"},
]
},
{
@ -83,7 +95,18 @@ const defaultData: WangEditorContent[] = [
"text": "故事里有一个愚蠢的笨国王。他罕少关心国家,一昧追求的就是衣着入时。",
},
{
"text": "有一天,王国里来了俩个骗子。",
type: 'proofread',
proofread: {
origin: '有天',
text: '有一天',
type: 'words'
},
children: [
{"text": "有一天"},
]
},
{
"text": ",王国里来了俩个骗子。",
"fontFamily": "宋体"
}
]
@ -117,6 +140,12 @@ const WangEditor: React.FC = () => {
if (divRef.current) {
const target = e.target as HTMLElement;
if (target.classList.contains('data-proofread-item')) {
const originNode = divRef.current.querySelector('.data-proofread-item-selected')
if(originNode && originNode == target) return;
originNode?.classList.remove('data-proofread-item-selected')
if(target.classList.contains('proofread-processed')){
return;
}
target.classList.add('data-proofread-item-selected')
return;
}
@ -157,6 +186,20 @@ const WangEditor: React.FC = () => {
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')
// const sel = window.getSelection();
// window.getSelection()?.selectAllChildren(span);
}
}
const proofreadProcess = (type: 'confirm' | 'cancel', index: number) => {
return () => {
const span = document.querySelector(`.data-proofread-item-${index}`)
if (span && span.classList.contains('data-proofread-item-selected')) {
const e = new Event('process');
console.log(type)
span.dispatchEvent(e)
}
}
}
@ -189,10 +232,18 @@ const WangEditor: React.FC = () => {
<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>
<Space>
<Button onClick={() => {
const elems = editor?.getElemsByTypePrefix('header')
console.log(elems)
}}></Button>
<Button onClick={() => {
setContent(editor?.children)
console.log(
editor?.getHtml()
)
}}></Button>
</Space>
</div>
{ops.map((op, key) => (<div
className={classNames('operation-proofread-item', {selected: op.proofread.id == currentIndex})}
@ -201,18 +252,29 @@ const WangEditor: React.FC = () => {
>
<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>
{
op.proofread.type == 'delete' ? <>
<span className="origin"><del>{op.proofread.origin}</del></span>
</> : (op.proofread.type == 'insert' ? <>
<span className="origin insert"><del>{op.proofread.text}</del></span>
<span className="insert-text">()</span>
</> : <>
<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"/>
<IconCheckFill onClick={proofreadProcess('confirm', Number(op.proofread.id))}
className="process"/>
</ProofreadPopover>
<ProofreadPopover content={<span></span>}>
<IconCancelFill className="cancel"/>
<IconCancelFill onClick={proofreadProcess('cancel', Number(op.proofread.id))}
className="cancel"/>
</ProofreadPopover>
</div>
</div>))}