增加Editor组件

master
LiuJiaNan 2025-12-06 15:08:03 +08:00
parent 4a8bcc8e78
commit 946e31d427
8 changed files with 150 additions and 3 deletions

29
components/Editor/index.d.ts vendored Normal file
View File

@ -0,0 +1,29 @@
import type { Editor as WangEditorInstance } from "@wangeditor/editor";
import type { ForwardRefExoticComponent, RefAttributes } from "react";
export interface EditorProps {
/** 编辑器内容值 */
value?: string;
/** 内容改变回调 */
onChange?: (html: string) => void;
/** 是否禁用 */
disabled?: boolean;
}
export interface EditorRef {
/** 获取编辑器实例 */
getEditorInstance: () => WangEditorInstance | null;
/** 获取HTML内容 */
getHtml: () => string | undefined;
/** 设置HTML内容 */
setHtml: (value: string) => void;
/** 获取文本内容 */
getText: () => string | undefined;
}
/**
*
*/
declare const Editor: ForwardRefExoticComponent<EditorProps & RefAttributes<EditorRef>>;
export default Editor;

View File

@ -0,0 +1,99 @@
import { Editor as ReactEditor, Toolbar } from "@wangeditor/editor-for-react";
import { forwardRef, useEffect, useImperativeHandle, useState } from "react";
import { normalizeEmptyHtml } from "../../utils";
import "@wangeditor/editor/dist/css/style.css";
/**
* 富文本编辑器组件
*/
const Editor = forwardRef(({
value,
onChange,
disabled,
}, ref) => {
const [editor, setEditor] = useState(null);
const [html, setHtml] = useState("");
useEffect(() => {
setHtml(value);
}, [value]);
useEffect(() => {
if (!editor)
return;
if (disabled)
editor.disable();
else editor.enable();
}, [disabled]);
useEffect(() => {
return () => {
if (!editor)
return;
editor.destroy();
setEditor(null);
};
}, [editor]);
useImperativeHandle(ref, () => ({
getEditorInstance: () => editor,
getHtml: () => editor && editor.getHtml(),
setHtml: (value) => {
editor && editor.setHtml(value);
},
getText: () => editor && editor.getText(),
}));
const handleCreated = (editor) => {
setEditor(editor);
if (disabled)
editor.disable();
};
const handleChange = (editor) => {
setHtml(editor.getHtml());
onChange?.(normalizeEmptyHtml(editor.getHtml()));
};
// 工具栏配置
const toolbarConfig = {
excludeKeys: [
"group-image",
"group-video",
"insertLink",
"emotion",
"todo",
],
};
// 编辑器配置
const editorConfig = {
placeholder: "请输入内容...",
};
return (
<>
<div style={{ border: "1px solid #ccc" }}>
<Toolbar
editor={editor}
defaultConfig={toolbarConfig}
mode="default"
style={{ borderBottom: "1px solid #ccc" }}
/>
<ReactEditor
defaultConfig={editorConfig}
value={html}
onCreated={handleCreated}
onChange={handleChange}
mode="default"
style={{ height: "500px", overflowY: "hidden" }}
/>
</div>
</>
);
});
Editor.displayName = "Editor";
export default Editor;

View File

@ -32,4 +32,6 @@ function HeaderBack(props) {
);
}
HeaderBack.displayName = "HeaderBack";
export default HeaderBack;

View File

@ -35,8 +35,6 @@ export interface AliPlayerRef {
/**
*
*/
declare const AliPlayer: ForwardRefExoticComponent<
AliPlayerProps & RefAttributes<AliPlayerRef>
>;
declare const AliPlayer: ForwardRefExoticComponent<AliPlayerProps & RefAttributes<AliPlayerRef>>;
export default AliPlayer;

View File

@ -85,4 +85,6 @@ const Video = ({
return playerElement;
};
Video.displayName = "Video";
export default Video;

View File

@ -26,6 +26,8 @@
"@ant-design/pro-components": "^2.8.10",
"@cqsjjb/jjb-common-lib": "latest",
"@cqsjjb/jjb-react-admin-component": "latest",
"@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-react": "^1.0.6",
"ahooks": "^3.9.5",
"antd": "^5.27.6",
"dayjs": "^1.11.18",

5
utils/index.d.ts vendored
View File

@ -342,3 +342,8 @@ export function dynamicLoadJs(url: string): Promise<void>;
* css
*/
export function dynamicLoadCss(url: string): Promise<void>;
/**
* html使
*/
export function normalizeEmptyHtml(html: string): string;

View File

@ -552,6 +552,16 @@ export function dynamicLoadCss(url) {
});
}
/**
* 是否空html用于给富文本编辑器使用
*/
export function normalizeEmptyHtml(html) {
if (!html)
return "";
const cleaned = html.replace(/\s+/g, "").toLowerCase();
return cleaned === "<p><br></p>" || cleaned === "<p></p>" || cleaned === "" ? "" : html;
}
/**
* 获取文件url
*/