react引用wangEditor 5.0 自定义上传上传图片以及图片文字的复制粘贴

安装使用

参考官网在React中的使用 editor-for-react

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

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

组件封装以及使用

参考官网的customPaste自定义粘贴

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;

组件引用封装后的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

写在最后wangEditor在使用和接口文档来说,还是不错的。单从复制粘贴来说,比react-quill要好用。使用react-quill,存在几个问题:
1、复制粘贴找不到当前鼠标下标,一般只能把光标写死,粘贴到最后或者最前
2、复制粘贴时,如果滚动条在最下面,粘贴完成后会直接跑到顶部

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值