update
This commit is contained in:
parent
738d23ca9d
commit
d899c14bad
@ -3,6 +3,7 @@ import '@/App.less'
|
|||||||
import {BrowserRouter, Navigate, Route, Routes} from "react-router-dom";
|
import {BrowserRouter, Navigate, Route, Routes} from "react-router-dom";
|
||||||
import TextPage from "./pages/quill/text.tsx";
|
import TextPage from "./pages/quill/text.tsx";
|
||||||
import WangEditor from "./pages/wang/wang-editor.tsx";
|
import WangEditor from "./pages/wang/wang-editor.tsx";
|
||||||
|
import {Test} from "./pages/Test.tsx";
|
||||||
|
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
@ -12,6 +13,7 @@ function App() {
|
|||||||
<Route path="/" element={<Navigate to="/wang"/>}/>
|
<Route path="/" element={<Navigate to="/wang"/>}/>
|
||||||
<Route path="/proofread" element={<TextPage />}/>
|
<Route path="/proofread" element={<TextPage />}/>
|
||||||
<Route path="/wang" element={<WangEditor />}/>
|
<Route path="/wang" element={<WangEditor />}/>
|
||||||
|
<Route path="/test" element={<Test />}/>
|
||||||
|
|
||||||
</Routes>
|
</Routes>
|
||||||
</BrowserRouter>)
|
</BrowserRouter>)
|
||||||
|
45
src/pages/Test.tsx
Normal file
45
src/pages/Test.tsx
Normal 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>
|
||||||
|
)
|
||||||
|
}
|
@ -24,7 +24,7 @@ export function withProofread<T extends IDomEditor>(editor: T) {
|
|||||||
* @param children 元素子节点,void 元素可忽略 @param _editor 编辑器实例
|
* @param children 元素子节点,void 元素可忽略 @param _editor 编辑器实例
|
||||||
* @returns 节点(通过 snabbdom.js 的 h 函数生成)
|
* @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;
|
const {proofread} = data as WangEditorProofreadElement;
|
||||||
return h(
|
return h(
|
||||||
'span',
|
'span',
|
||||||
@ -33,14 +33,34 @@ export function renderProofread(data: SlateElement, children: VNode[] | null) {
|
|||||||
dataSlateId: '123123'
|
dataSlateId: '123123'
|
||||||
}, // HTML 属性,驼峰式写法
|
}, // HTML 属性,驼峰式写法
|
||||||
attrs: {
|
attrs: {
|
||||||
'data-proofread-id': String(proofread.id)
|
'data-proofread-id': String(proofread.id),
|
||||||
|
'data-proofread-text': proofread.origin
|
||||||
},
|
},
|
||||||
attachData: {
|
attachData: {
|
||||||
proofread
|
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',
|
style: { /* 其他... */}, // style ,驼峰式写法 display: 'inline-block', margin: '0 2px',
|
||||||
on: {
|
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) {
|
click(e) {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
@ -21,12 +21,24 @@
|
|||||||
.data-proofread-item {
|
.data-proofread-item {
|
||||||
border-bottom: solid 3px #f00;
|
border-bottom: solid 3px #f00;
|
||||||
display: inline;
|
display: inline;
|
||||||
margin: 0 5px;
|
//margin: 0 5px;
|
||||||
cursor: pointer;
|
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 {
|
.data-proofread-item-selected {
|
||||||
background-color: #fbbbbb;
|
background-color: #fbbbbb;
|
||||||
|
//outline: solid 1px #fbbbbb;
|
||||||
}
|
}
|
||||||
|
|
||||||
.editor-container {
|
.editor-container {
|
||||||
@ -75,6 +87,10 @@
|
|||||||
color: #ff0000;
|
color: #ff0000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.insert-text {
|
||||||
|
margin-left: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
margin: 0 6px;
|
margin: 0 6px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -7,7 +7,7 @@ import '@wangeditor/editor/dist/css/style.css'
|
|||||||
import './style.less'
|
import './style.less'
|
||||||
import {PROOFREAD_TYPE_DESC, WangEditorContent, WangEditorProofreadElement} from "../../types/editor.ts";
|
import {PROOFREAD_TYPE_DESC, WangEditorContent, WangEditorProofreadElement} from "../../types/editor.ts";
|
||||||
import {useMount} from "ahooks";
|
import {useMount} from "ahooks";
|
||||||
import {Button} from "antd";
|
import {Button, Space} from "antd";
|
||||||
import * as classNames from "classnames";
|
import * as classNames from "classnames";
|
||||||
import IconArrowRight from "../../components/icons/IconArrowRight.tsx";
|
import IconArrowRight from "../../components/icons/IconArrowRight.tsx";
|
||||||
import IconCheckFill from "../../components/icons/IconCheckFill.tsx";
|
import IconCheckFill from "../../components/icons/IconCheckFill.tsx";
|
||||||
@ -66,14 +66,26 @@ const defaultData: WangEditorContent[] = [
|
|||||||
type: 'proofread',
|
type: 'proofread',
|
||||||
proofread: {
|
proofread: {
|
||||||
origin: '安徒声',
|
origin: '安徒声',
|
||||||
text: '安徒生',
|
text: '汉斯·克里斯汀·安徒生',
|
||||||
type: 'words',
|
type: 'words', // (汉斯·克里斯汀·安徒生,通称安徒生,丹麦作家暨诗人。)
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{"text": "安徒声"},
|
{"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": "故事里有一个愚蠢的笨国王。他罕少关心国家,一昧追求的就是衣着入时。",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"text": "有一天,王国里来了俩个骗子。",
|
type: 'proofread',
|
||||||
|
proofread: {
|
||||||
|
origin: '有天',
|
||||||
|
text: '有一天',
|
||||||
|
type: 'words'
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{"text": "有一天"},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": ",王国里来了俩个骗子。",
|
||||||
"fontFamily": "宋体"
|
"fontFamily": "宋体"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -117,6 +140,12 @@ const WangEditor: React.FC = () => {
|
|||||||
if (divRef.current) {
|
if (divRef.current) {
|
||||||
const target = e.target as HTMLElement;
|
const target = e.target as HTMLElement;
|
||||||
if (target.classList.contains('data-proofread-item')) {
|
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')
|
target.classList.add('data-proofread-item-selected')
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -157,6 +186,20 @@ const WangEditor: React.FC = () => {
|
|||||||
if (span.classList.contains('data-proofread-item-selected')) return;
|
if (span.classList.contains('data-proofread-item-selected')) return;
|
||||||
document.querySelector(`.data-proofread-item-selected`)?.classList.remove('data-proofread-item-selected')
|
document.querySelector(`.data-proofread-item-selected`)?.classList.remove('data-proofread-item-selected')
|
||||||
span.classList.add('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-wrapper">
|
||||||
<div className="operation-container h-full">
|
<div className="operation-container h-full">
|
||||||
<div style={{margin: 10}}>
|
<div style={{margin: 10}}>
|
||||||
<Button onClick={() => {
|
<Space>
|
||||||
const elems = editor?.getElemsByTypePrefix('header')
|
<Button onClick={() => {
|
||||||
console.log(elems)
|
const elems = editor?.getElemsByTypePrefix('header')
|
||||||
}}>获取所有目录</Button>
|
console.log(elems)
|
||||||
|
}}>获取所有目录</Button>
|
||||||
|
<Button onClick={() => {
|
||||||
|
setContent(editor?.children)
|
||||||
|
console.log(
|
||||||
|
editor?.getHtml()
|
||||||
|
)
|
||||||
|
}}>获取内容</Button>
|
||||||
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
{ops.map((op, key) => (<div
|
{ops.map((op, key) => (<div
|
||||||
className={classNames('operation-proofread-item', {selected: op.proofread.id == currentIndex})}
|
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-item-container">
|
||||||
<div className="proofread-data">
|
<div className="proofread-data">
|
||||||
<span className="origin">{op.proofread.origin}</span>
|
{
|
||||||
<span className="icon"><IconArrowRight/></span>
|
op.proofread.type == 'delete' ? <>
|
||||||
<span className="text">{op.proofread.text}</span>
|
<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>
|
||||||
<div className="proofread-description">{op.proofread.description}</div>
|
<div className="proofread-description">{op.proofread.description}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="proofread-action">
|
<div className="proofread-action">
|
||||||
<ProofreadPopover content={<span>选用</span>}>
|
<ProofreadPopover content={<span>选用</span>}>
|
||||||
<IconCheckFill className="process"/>
|
<IconCheckFill onClick={proofreadProcess('confirm', Number(op.proofread.id))}
|
||||||
|
className="process"/>
|
||||||
</ProofreadPopover>
|
</ProofreadPopover>
|
||||||
<ProofreadPopover content={<span>忽略</span>}>
|
<ProofreadPopover content={<span>忽略</span>}>
|
||||||
<IconCancelFill className="cancel"/>
|
<IconCancelFill onClick={proofreadProcess('cancel', Number(op.proofread.id))}
|
||||||
|
className="cancel"/>
|
||||||
</ProofreadPopover>
|
</ProofreadPopover>
|
||||||
</div>
|
</div>
|
||||||
</div>))}
|
</div>))}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user