|
@@ -3,6 +3,7 @@ import { useCallback, useEffect, useRef, useState } from "react"
|
|
|
import Expression, { emoList } from "./Expression";
|
|
|
const { Text } = Typography
|
|
|
import style from './index.less'
|
|
|
+import AddLink from "@/pages/MiniApp/EntWeChat/Welcome/components/link";
|
|
|
|
|
|
type Props = {
|
|
|
/**是否展示底部*/
|
|
@@ -46,10 +47,12 @@ export function TextEditor(props: Props) {
|
|
|
}
|
|
|
})
|
|
|
}
|
|
|
- setText(newValue?.replaceAll('\n', '<br/>'))
|
|
|
+ setText(newValue)//?.replaceAll('\n', '<br/>')
|
|
|
} else {
|
|
|
setText(value?.innerHTML)
|
|
|
}
|
|
|
+ } else {//不存在内容设置焦点
|
|
|
+ ref?.current?.focus()
|
|
|
}
|
|
|
}, [value])
|
|
|
|
|
@@ -60,20 +63,20 @@ export function TextEditor(props: Props) {
|
|
|
let range = selection?.getRangeAt(0)
|
|
|
setRange(range)
|
|
|
//点击表情不更新
|
|
|
- if (!e.relatedTarget.className.includes('myEmo')) {
|
|
|
- onChange?.(ref.current)
|
|
|
+ if (!e?.relatedTarget?.className?.match(/(myEmo)|(textEditor_btn)/g)) {
|
|
|
+ onChange?.(ref.current?.innerHTML)
|
|
|
}
|
|
|
} catch (err) {
|
|
|
}
|
|
|
}
|
|
|
- // 插入按钮
|
|
|
- const btnClick = useCallback((btnName: string) => {
|
|
|
+ // 自定义文本插入按钮
|
|
|
+ const btnClick = useCallback((btnName: any) => {
|
|
|
if (range) {
|
|
|
- let str = "" //` <span style="display: inline-block;margin:0 1px;position: relative; border: 1px solid ${token.colorBorder}; padding: ${token.paddingXS}px; border-radius: ${token.borderRadius}px;color: ${token.colorTextBase}; background: ${token.colorSuccess};color:${token.colorTextLightSolid}" contenteditable="false">${btnName}<strong data-name="${btnName}" style="padding: 0 6px;cursor:pointer" onclick="let html =document.querySelector('[data-name=${btnName}]').parentElement.parentElement.innerHTML;let span = ' '+document.querySelector('[data-name=${btnName}]').parentElement.outerHTML+' ';console.log('=',html,'=');console.log('=',span,'=');document.execCommand('selectAll');document.execCommand('delete'); document.execCommand('insertHTML', true, html.replace(span,''));">X</strong></span> `
|
|
|
+ let str = btnName //` <span style="display: inline-block;margin:0 1px;position: relative; border: 1px solid ${token.colorBorder}; padding: ${token.paddingXS}px; border-radius: ${token.borderRadius}px;color: ${token.colorTextBase}; background: ${token.colorSuccess};color:${token.colorTextLightSolid}" contenteditable="false">${btnName}<strong data-name="${btnName}" style="padding: 0 6px;cursor:pointer" onclick="let html =document.querySelector('[data-name=${btnName}]').parentElement.parentElement.innerHTML;let span = ' '+document.querySelector('[data-name=${btnName}]').parentElement.outerHTML+' ';console.log('=',html,'=');console.log('=',span,'=');document.execCommand('selectAll');document.execCommand('delete'); document.execCommand('insertHTML', true, html.replace(span,''));">X</strong></span> `
|
|
|
let selection = window.getSelection()
|
|
|
selection?.empty()//清空选择range
|
|
|
selection?.addRange(range as Range)//插入新的range
|
|
|
- document.execCommand('insertHTML', false, str);//插入内容
|
|
|
+ document.execCommand('insertHTML', false, btnName);//插入内容
|
|
|
range.collapse()
|
|
|
selection?.collapseToEnd()//光标插入到末尾
|
|
|
} else {
|
|
@@ -91,7 +94,14 @@ export function TextEditor(props: Props) {
|
|
|
document.execCommand('insertHTML', false, str);
|
|
|
}
|
|
|
}, [])
|
|
|
-
|
|
|
+ const addLink = useCallback((str: string, range: Range) => {
|
|
|
+ if (range) {
|
|
|
+ let selection = window.getSelection()
|
|
|
+ selection?.empty()//清空选择range
|
|
|
+ selection?.addRange(range)//插入新的range
|
|
|
+ document.execCommand('insertHTML', false, str);
|
|
|
+ }
|
|
|
+ }, [])
|
|
|
//初始化设置
|
|
|
useEffect(() => {
|
|
|
if (initBtnNames && initBtnNames?.length > 0) {
|
|
@@ -119,13 +129,41 @@ export function TextEditor(props: Props) {
|
|
|
document.execCommand('insertHTML', false, text);
|
|
|
}
|
|
|
}
|
|
|
+ const insertHtmlAtCursor = (html: string) => {
|
|
|
+ const selection = window.getSelection();
|
|
|
+ if (!selection || selection.rangeCount === 0) return;
|
|
|
+
|
|
|
+ const range = selection.getRangeAt(0);
|
|
|
+ range.deleteContents(); // 删除光标处的内容
|
|
|
+
|
|
|
+ const tempDiv = document.createElement('div');
|
|
|
+ tempDiv.innerHTML = html; // 创建包含 HTML 的临时元素
|
|
|
+ const frag = document.createDocumentFragment();
|
|
|
+
|
|
|
+ // 将临时元素的子元素添加到文档片段中
|
|
|
+ while (tempDiv.firstChild) {
|
|
|
+ frag.appendChild(tempDiv.firstChild);
|
|
|
+ }
|
|
|
+ range.insertNode(frag); // 将内容插入光标位置
|
|
|
+ // 重新设置光标位置
|
|
|
+ range.collapse(false);
|
|
|
+ selection.removeAllRanges();
|
|
|
+ selection.addRange(range);
|
|
|
+ };
|
|
|
return <List
|
|
|
+ size="small"
|
|
|
header={<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
|
|
- <Space wrap>
|
|
|
- {emo && <Expression addEmo={addEmo} range={range as Range} />}
|
|
|
+ <Space wrap size={[20, 0]}>
|
|
|
+ {/* {emo && <Expression addEmo={addEmo} range={range as Range} />} */}
|
|
|
{btnNames?.map(name => {
|
|
|
- return <Button onClick={() => { btnClick(name) }} key={name}>{name}</Button>
|
|
|
+ return <Button className="textEditor_btn" size="small" onClick={() => { btnClick(name) }} key={name}>{name}</Button>
|
|
|
})}
|
|
|
+ {/* <AddLink onChange={(value) => {
|
|
|
+ if (value) {
|
|
|
+ addEmo("1", range as any)
|
|
|
+ }
|
|
|
+ }} /> */}
|
|
|
+ <button onClick={() => insertHtmlAtCursor('<a href="https://example.com" target="_blank">https://example.com</a>')}>插入链接</button>
|
|
|
</Space>
|
|
|
{maxStr && <Typography.Text type='secondary'><Typography.Text type={strLength > maxStr ? 'danger' : 'secondary'} >{strLength}</Typography.Text>/{maxStr}</Typography.Text>}
|
|
|
{/* <Button type="link">复制</Button> */}
|
|
@@ -134,7 +172,7 @@ export function TextEditor(props: Props) {
|
|
|
bordered
|
|
|
dataSource={['']}
|
|
|
renderItem={(item) => (
|
|
|
- <List.Item>
|
|
|
+ <List.Item style={{ padding: 5 }}>
|
|
|
<pre
|
|
|
className={style.myPre}
|
|
|
style={{
|
|
@@ -148,26 +186,22 @@ export function TextEditor(props: Props) {
|
|
|
textAlign: 'left',
|
|
|
wordBreak: 'break-all'
|
|
|
}}
|
|
|
- onInput={(e) => {
|
|
|
- if (isOnInput) {
|
|
|
- onChange?.(e.target)
|
|
|
- // setText((e.target as any).innerHTML)
|
|
|
- }
|
|
|
- }}
|
|
|
contentEditable="true"
|
|
|
dangerouslySetInnerHTML={{
|
|
|
__html: text
|
|
|
}}
|
|
|
ref={ref}
|
|
|
- onKeyDown={(e: React.KeyboardEvent<HTMLPreElement>) => {
|
|
|
- if (e.key === 'Enter') {
|
|
|
- document.execCommand('insertHTML', false, '<br/>\n')
|
|
|
- e.preventDefault()
|
|
|
- }
|
|
|
+ // onKeyDown={(e: React.KeyboardEvent<HTMLPreElement>) => {
|
|
|
+ // if (e.key === 'Enter') {
|
|
|
+ // e.preventDefault()
|
|
|
+ // document.execCommand('insertHTML', false, '\n')
|
|
|
+ // }
|
|
|
+ // }}
|
|
|
+ onInput={(e: any) => {
|
|
|
+
|
|
|
}}
|
|
|
onKeyUp={(e: React.KeyboardEvent<HTMLPreElement>) => {
|
|
|
if (e.key === 'Enter') {
|
|
|
- e.preventDefault()
|
|
|
} else {
|
|
|
if (maxStr) {
|
|
|
let childrens = (e.target as any).childNodes
|