react quotes wangEditor 5.0 to customize uploading and copying and pasting of uploaded pictures and picture text

installation and use

Refer to the official website for using editor-for-react in React

yarn add @wangeditor/editor
# 或者 npm install @wangeditor/editor --save

yarn add @wangeditor/editor-for-react
# 或者 npm install @wangeditor/editor-for-react --save

Component packaging and use

Refer to the customPaste custom paste on the official website

import React, {
    
     useState, useEffect, useRef, forwardRef, useImperativeHandle } from 'react'
import '@wangeditor/editor/dist/css/style.css' // 引入 css
import styled from 'styled-components'
import {
    
     Editor, Toolbar } from '@wangeditor/editor-for-react'
import {
    
     IDomEditor, IEditorConfig, IToolbarConfig } from '@wangeditor/editor'
import {
    
     message } from 'antd'
import {
    
    v4 as uuid} from 'uuid'
import axios from '@/services/index';

const WangEditor = forwardRef((props:any, ref:any) => {
    
    
    const [editor, setEditor] = useState<IDomEditor | null>(null);
    // 编辑器内容
    const [html, setHtml] = useState(props.editorContent|| '') 
    // 模拟 ajax 请求,异步设置 html
    useEffect(() => {
    
    
        
    }, [])              // JS 语法
    // 工具栏配置
    const toolbarConfig: Partial<IToolbarConfig> = {
    
     
        
    }  // TS 语法
    // 对外暴露方法,可以让父组件调用子组件的方法
    // 作用: 减少父组件获取子组件的DOM元素属性,只暴露给父组件需要用到的DOM方法
    // 参数1: 父组件传递的ref属性
    // 参数2: 返回一个对象,父组件通过ref.current调用对象中方法
    useImperativeHandle(ref, () => ({
    
    
        
    }));
    useEffect(() => {
    
    
        setHtml(props.editorContent); //设置编辑器内容
    }, [props.editorContent]);

    // 及时销毁 editor ,重要!
    useEffect(() => {
    
    
        return () => {
    
    
            if (editor == null) return
            if(editor) {
    
    
                (editor as any).destroy() 
            }
            setEditor(null)
        }
    }, [editor])
    
    // 编辑器配置
    const editorConfig: Partial<IEditorConfig> = {
    
        // TS 语法
        placeholder: '请输入内容...',
        //插入图片
        MENU_CONF: {
    
    
            uploadImage: {
    
    
                // 单个文件的最大体积限制,默认为 2M
                maxFileSize: 4 * 1024 * 1024, // 4M
                // 最多可上传几个文件,默认为 100
                maxNumberOfFiles: 10,
                // 超时时间,默认为 10 秒
                timeout: 5 * 1000, // 5 秒
                // 用户自定义上传图片
                customUpload(file: any, insertFn: any) {
    
    
                    const data = new FormData();
                    data.append("file", file); // file 即选中的文件 主要就是这个传的参数---看接口要携带什么参数{ key :value}
                    const hide = message.loading('上传中...', 0);
                    //这里写自己的接口
                    axios({
    
    
                        method: 'post',
                        url: '/upload/uploadAvatar',
                        headers:{
    
    'Content-Type': 'multipart/form-data'},
                        data:data
                    }).then((res) => {
    
     
                        const url = res.data.data.url;
                        insertFn(url); //插入图片,看返回的数据是什么
                        hide();
                    }).catch(err=> {
    
    
                        hide();
                    })
                }
            }
        }
    }
    // // 自定义粘贴
    editorConfig.customPaste = (editor: IDomEditor, event: ClipboardEvent): boolean => {
    
         // TS 语法
        // event 是 ClipboardEvent 类型,可以拿到粘贴的数据
        // 可参考 https://developer.mozilla.org/zh-CN/docs/Web/API/ClipboardEvent
        // let html = event && event?.clipboardData?.getData('text/html') // 获取粘贴的 html
        const text = event && event?.clipboardData?.getData('text/plain') // 获取粘贴的纯文本
        let files = event && event?.clipboardData?.files
        event.preventDefault();
        
        if(files && files.length>0) {
    
    
            const file = files[0]
            const type = file.type.split('/')
            //对复制粘贴的图片进行重命名,原因是每次复制粘贴的图片名都是一样的,会导致bug,可以自行去掉重现该bug
            const newfile = new File([file], 'pictur'+uuid() + '.' + type[1], {
    
    type: file.type});
            //这里写自己的接口
            const data = new FormData();
            data.append("file", newfile); 
            const hide = message.loading('上传中...', 0);
        
            axios({
    
    
                method: 'post',
                url: '/upload/uploadAvatar',
                headers:{
    
    'Content-Type': 'multipart/form-data'},
                data:data
            }).then((res) => {
    
     
                const url = res.data.data.url;
                editor.dangerouslyInsertHtml(`<img src="${
      
      url}" />`)
                hide();
            }).catch(err=> {
    
    
                hide();
            })
            return false;
        }else {
    
    
            editor.insertText(text || '');
            return false;
        }
    }
    return (
        <Wrap>
            <div style={
    
    {
    
     border: '1px solid #ccc', zIndex: 100}}>
                <Toolbar
                    editor={
    
    editor}
                    defaultConfig={
    
    toolbarConfig}
                    mode="default"
                    style={
    
    {
    
     borderBottom: '1px solid #ccc' }}
                />
                <Editor
                    defaultConfig={
    
    editorConfig}
                    value={
    
    html}
                    onCreated={
    
    setEditor}
                    onChange={
    
    editor => {
    
    
                        setHtml(editor.getHtml());
                        props.saveHtmParams(editor.getHtml())
                    }}
                    mode="default"
                    style={
    
    {
    
     height: 'calc(100vh - 420px)', overflowY: 'hidden' }}
                />
            </div>
        </Wrap>
    )
})
const Wrap = styled.div`

  .w-e-text-container img{
    width: 300px;
    height: 400px;
  }
`;
export default WangEditor;

The component references the encapsulated wangEditor

import React, {
    
     useRef, useState, forwardRef, useImperativeHandle, useEffect, useCallback } from 'react'
import {
    
     Button, Form, Input, InputNumber, message, Modal, Popover, Tree, Radio, Breadcrumb, Card, Select, } from 'antd';
import {
    
     useSelector, shallowEqual } from "react-redux";
import {
    
     useLocation, useNavigate } from 'react-router-dom';
import WangEditor from '@/components/content/wangEditor'
import Reback from '@/components/content/reBack'
import Save from '@/components/content/save';
import axios from '@/services/index'

const Content = (props:any) => {
    
    
    const [content, setContent] = useState({
    
    })
    const [form] = Form.useForm();
    //保存按钮父子组件交互使用
    let motherMethodRef = useRef<any>(null);
    const {
    
    state} = useLocation()
    useEffect(() => {
    
    
        if(state.product) {
    
    
            form.setFieldsValue(state.product)
        }
    }, [])
    const navigate = useNavigate()
    const {
    
     users } = useSelector(
        (state: any) => ({
    
     users: state.login.users }),
        shallowEqual
    );
    
    //获取保存按钮上传子组件
    const saveContent = ()=>{
    
    
        form.submit();
        
    } 
    const saveHtmParams = (value: string)=>{
    
    
        form.setFieldsValue({
    
    
            content: value
        })
    }
    //弹框点击提交
    const onFinish = (values: any) => {
    
    
        const params = {
    
    
            id: state?.product?.id,
            ...values,
        }
    };   
    //form组件弹框报错
    const onFinishFailed = (errorInfo: any) => {
    
    
        
    };
    return (
        <div className="content">
            <Reback/>
            <div className="content_form">
                <Form
                    name="basic"
                    labelCol={
    
    {
    
     span: 5 }}
                    wrapperCol={
    
    {
    
     span: 18 }}
                    onFinish={
    
    onFinish}
                    onFinishFailed={
    
    onFinishFailed}
                    autoComplete="off"
                    form={
    
    form}
                    >   
                    <Form.Item
                        label="产品名称"
                        name="title"
                        rules={
    
    [{
    
     required: true, message: "请输入产品名称" }]}
                    >
                        <Input placeholder="请输入产品名称"/>
                    </Form.Item>
                    {
    
    /* // 表单嵌套 */}
                    <Form.Item label="产品内容" name="content" initialValue="" rules={
    
    [{
    
     required: true, message: "请输入产品内容" }]}>
                        <WangEditor saveHtmParams={
    
    saveHtmParams} editorContent={
    
    state?.product?.content} ref={
    
    quillMethodRef}/>
                    </Form.Item>

                </Form>
            </div>
            <Save saveContent={
    
    saveContent} ref={
    
    motherMethodRef}/>
        </div>
    )
}

export default Content

Take a look at the demo

QQ20230403-095115-HD

Written at the end, wangEditor is still good in terms of use and interface documentation. From the perspective of copy and paste, it is better than react-quill. Using react-quill, there are several problems:
1. Copy and paste cannot find the current mouse subscript. Generally, you can only write the cursor to death, and paste it to the end or the front.
2. When copying and pasting, if the scroll bar is at the bottom, the paste is complete will run straight to the top

Guess you like

Origin blog.csdn.net/chenacxz/article/details/129922131