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

看一下演示效果

QQ20230403-095115-HD

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

猜你喜欢

转载自blog.csdn.net/chenacxz/article/details/129922131