原生HTML文档案例 wangEditor-master/packages/editor/demo/like-qq-docs.html
image.png

WangEdtior

  1. import { useState, useEffect, useMemo } from 'react';
  2. import {
  3. string,
  4. number,
  5. object,
  6. array,
  7. func,
  8. oneOf,
  9. oneOfType
  10. } from "prop-types";
  11. import { merge, noop, isEqual } from "lodash-es";
  12. import cls from 'classnames';
  13. import { Editor, Toolbar } from '@wangeditor/editor-for-react';
  14. import {
  15. IDomEditor,
  16. IEditorConfig,
  17. IToolbarConfig
  18. } from '@wangeditor/editor';
  19. import Content from './Content';
  20. const ENUM_MODE = ["simple", "default"];
  21. WangEditor.propTypes = {
  22. value: oneOfType([array, string]),
  23. onChange: func,
  24. title: string,
  25. titleChange: func,
  26. height: number,
  27. placeholder: string,
  28. mode: oneOf(ENUM_MODE),
  29. className: oneOfType([string, object]),
  30. style: object,
  31. };
  32. WangEditor.defaultProps = {
  33. onChange: noop,
  34. titleChange: noop,
  35. height: 200,
  36. placeholder: "请输入内容...",
  37. mode: "simple"
  38. };
  39. function WangEditor(props) {
  40. const {
  41. value,
  42. onChange,
  43. title,
  44. titleChange,
  45. height,
  46. placeholder,
  47. mode,
  48. className,
  49. style
  50. } = props;
  51. // editor 编辑器实例
  52. const [editorInst, setEditorInst] = useState<IDomEditor | null>(null);
  53. // 编辑器内容
  54. const [content, setContent] = useState(value);
  55. // useEffect 是在render结束之后才执行的
  56. useEffect(() => {
  57. // 及时销毁 editorInst 重要!
  58. return () => {
  59. if (!editorInst) return;
  60. editorInst.destroy();
  61. setEditorInst(null);
  62. };
  63. }, [editorInst]);
  64. // value 改变,设置 content
  65. useEffect(() => {
  66. // 设置内容
  67. if (isEqual(value, content)) return;
  68. setContent(value);
  69. }, [value]);
  70. // 工具栏配置
  71. const toolbarConfig: Partial<IToolbarConfig> = useMemo(() => ({}), []);
  72. // 编辑器配置
  73. const editorConfig: Partial<IEditorConfig> = useMemo(() => ({
  74. placeholder,
  75. // scroll: false, // 禁止编辑器滚动
  76. // maxLength: 1000,
  77. // onMaxLength(editor) {
  78. // console.log('Trigger maxlength callback')
  79. // },
  80. MENU_CONF: {
  81. // onChange(editor: any) {
  82. // console.log(editor.getHtml());
  83. // },
  84. uploadImage: {
  85. // form-data fieldName ,默认值 'wangeditor-uploaded-image'
  86. fieldName: 'your-custom-name',
  87. // 单个文件的最大体积限制,默认为 2M
  88. maxFileSize: 2 * 1024 * 1024, // 1M
  89. // 最多可上传几个文件,默认为 100
  90. maxNumberOfFiles: 10,
  91. // 选择文件时的类型限制,默认为 ['image/*'] 。如不想限制,则设置为 []
  92. allowedFileTypes: ['image/*'],
  93. /**
  94. * 小于该值就插入 base64 格式(而不上传),默认为 0
  95. * 1MB 兆 = 1024KB 千字节 * 1024B 比特 = 1048576 kb 字节
  96. * 1MB = 1024KB
  97. * 1KB = 1024B
  98. * 10 * 1024 * 1024, // 10M 以下插入 base64
  99. */
  100. // 10 * 1024 * 1024 // 10M 以下
  101. base64LimitSize: 5 * 1024, // 5kb
  102. // 跨域是否传递 cookie ,默认为 false
  103. withCredentials: true,
  104. // 超时时间,默认为 10 秒
  105. timeout: 5 * 1000, // 5 秒
  106. // 自定义增加 http header
  107. headers: {
  108. Accept: 'text/x-json',
  109. otherKey: 'xxx',
  110. },
  111. // 自定义上传参数,例如传递验证的 token 等。参数会被添加到 formData 中,一起上传到服务端。
  112. meta: {
  113. token: 'xxx',
  114. otherKey: 'yyy',
  115. },
  116. },
  117. // fontSize: { // 覆盖默认的配置
  118. // fontSizeList: [
  119. // { name: '26px', value: '26px' },
  120. // ],
  121. // }
  122. }
  123. }), [placeholder]);
  124. // 编辑器内容改变
  125. function handleChange(editor: IDomEditor) {
  126. // const html = editor.getHtml(); // 获取 HTML
  127. // const text = editor.getText(); // 获取纯文本,默认带换行符
  128. // const text = editor.getText().replace(/\n|\r/mg, '');
  129. const json = editor.children; // 获取 JSON []
  130. // editor.getSelectionText() // 选中的文字
  131. setContent(json);
  132. onChange(json, editor);
  133. }
  134. const EditorStyle = useMemo(() => {
  135. return merge({ height, overflow: "hidden auto" }, style);
  136. }, [style, height]);
  137. const isSimple = isEqual(mode, ENUM_MODE[0]);
  138. return (
  139. <div className={cls('z-100', {
  140. __simple__: isSimple,
  141. }, className)}>
  142. {/*工具栏组件*/}
  143. <Toolbar
  144. editor={editorInst}
  145. defaultConfig={toolbarConfig}
  146. mode={mode}
  147. />
  148. <Content
  149. value={title}
  150. onChange={titleChange}
  151. isSimple={isSimple}
  152. >
  153. {/*编辑器组件*/}
  154. <Editor
  155. defaultConfig={editorConfig}
  156. content={content}
  157. onCreated={setEditorInst}
  158. // 编辑器内容改变
  159. onChange={handleChange}
  160. style={EditorStyle}
  161. mode={mode}
  162. />
  163. </Content>
  164. </div>
  165. );
  166. }
  167. export default WangEditor;

Content.tsx

  1. /**
  2. * @author lulongwen
  3. * Date: 2023-08-20 21:50
  4. * Description:
  5. */
  6. import { memo, useState, useEffect } from 'react';
  7. import { string, func, node, bool } from 'prop-types';
  8. import cls from 'classnames';
  9. import styles from "./style.module.less";
  10. Content.propTypes = {
  11. value: string,
  12. onChange: func.isRequired,
  13. children: node,
  14. isSimple: bool,
  15. };
  16. function Content(props) {
  17. const {
  18. value,
  19. onChange,
  20. children,
  21. isSimple,
  22. } = props;
  23. // 标题
  24. const [title, setTitle] = useState(value);
  25. useEffect(() => {
  26. // 设置标题
  27. if (value === title) return;
  28. setTitle(value);
  29. }, [value]);
  30. // 标题组件表单改变
  31. function inputChange(e) {
  32. const inputValue = e.target.value;
  33. setTitle(inputValue);
  34. onChange(inputValue);
  35. }
  36. if (isSimple) {
  37. return children;
  38. }
  39. return (
  40. <div className={styles.editor}>
  41. <section className={styles.area}>
  42. <header className={cls('py-3', styles.border)}>
  43. <input
  44. value={title}
  45. type="text"
  46. placeholder='请输入标题'
  47. className='w-full border-none text-3xl outline-none'
  48. onChange={inputChange}
  49. />
  50. </header>
  51. {children}
  52. </section>
  53. </div>
  54. );
  55. }
  56. export default memo(Content);

style.module.less

  1. /**
  2. * @author lulongwen
  3. * Date: 2023-02-04 22:53
  4. * Update: 2023-08-20 20:30:41
  5. * Description:
  6. */
  7. .editor {
  8. position: relative;
  9. height: calc(100% - 50px);
  10. background-color: rgb(245, 245, 245);
  11. overflow-y: auto;
  12. }
  13. .area {
  14. width: 70%;
  15. min-height: 900px;
  16. margin: 24px auto 120px;
  17. padding: 16px 24px 24px;
  18. border: 1px solid #e8e8e8;
  19. background-color: #fff;
  20. box-shadow: 0 2px 10px rgb(0 0 0 / 8%);
  21. .border {
  22. border-bottom: 1px solid #e8e8e8;
  23. }
  24. }