wangEditor最新5.0 react 自定义上传图片

使用wangEditor上传图片,默认的上传serve,一直成功但是返回数据错误,可能是接口携带的参数有问题,此代码未被注释部分是我项目用到的配置,无视频,里面所有配置基本都加上,注释较多,请耐心观看

解决方法:自定义方法上传

import React, { useState, useEffect } from "react";
import "@wangeditor/editor/dist/css/style.css"; // 引入 cssß
import { Editor, Toolbar } from "@wangeditor/editor-for-react";
import type { IDomEditor, IEditorConfig, IToolbarConfig } from "@wangeditor/editor";
// import { DomEditor } from "@wangeditor/editor";
// import api from "@/api/messageCenter";
// import { message } from "antd";
// import { uchoiceToken, uliveToken } from "@/utils/auth";
import { getUliveAdminBaseUrl } from "@/utils/getBaseUrl";

import type { SlateElement } from "@wangeditor/editor";

export type ImageElement = SlateElement & {
  src: string;
  alt: string;
  url: string;
  href: string;
};
export type InsertFnType = (url: string, alt: string, href: string) => void;
function MyEditor(props: any) {
  // editor 实例
  const [editor, setEditor] = useState<IDomEditor | any>(null); // TS 语法

  // 编辑器内容
  const [html, setHtml] = useState("");

  // 模拟 ajax 请求,异步设置 html
  // useEffect(() => {
  //   setTimeout(() => {
  //     setHtml("<p>hello world</p>");
  //   }, 1500);
  // }, []);

  // 工具栏配置
  const toolbarConfig: Partial<IToolbarConfig> = {
    //去除的选项
    excludeKeys: [
      "headerSelect", //正文,标题
      "blockquote", //引用
      // "|",//隔符
      "bold", //加粗
      "underline", //下划线
      "italic", //斜体
      "group-more-style", //样式
      "color", //颜色
      "bgColor", //背景色
      "fontSize", //字体大小
      "fontFamily", //字体
      // "lineHeight",行高
      "bulletedList", //无序列表
      "numberedList", //有序列表
      "todo", //待办
      "group-justify", //对齐
      "group-indent", //缩进
      // "emotion",//表情
      "insertLink", //链接
      // "group-image", //图片
      "group-video", //视频
      "insertTable", //插表格
      "codeBlock", //代码块
      "divider" //分割线
      // "undo",//撤销
      // "redo",//重做
      // "fullScreen"//全屏
    ]
  }; // TS 语法

  // 自定义校验图片
  function customCheckImageFn(src: string, alt: string, url: string): boolean | undefined | string {
    // TS 语法
    if (!src) {
      return;
    }
    if (src.indexOf("http") !== 0) {
      return "图片网址必须以 http/https 开头";
    }
    return true;

    // 返回值有三种选择:
    // 1. 返回 true ,说明检查通过,编辑器将正常插入图片
    // 2. 返回一个字符串,说明检查未通过,编辑器会阻止插入。会 alert 出错误信息(即返回的字符串)
    // 3. 返回 undefined(即没有任何返回),说明检查未通过,编辑器会阻止插入。但不会提示任何信息
  }
  // 转换图片链接
  function customParseImageSrc(src: string): string {
    // TS 语法
    if (src.indexOf("http") !== 0) {
      return `http://${src}`;
    }
    return src;
  }

  // 编辑器配置
  const editorConfig: Partial<IEditorConfig> = {
    // TS 语法
    placeholder: "请输入内容...",
    autoFocus: false,
    // 粘贴
    // customPaste: (editor: IDomEditor, event: ClipboardEvent): boolean => {
    //   //const html = event.clipboardData?.getData("text/html"); // 获取粘贴的 html
    //   const text = event.clipboardData?.getData("text/plain"); // 获取粘贴的纯文本
    //   // const rtf = event.clipboardData.getData('text/rtf') // 获取 rtf 数据(如从 word wsp 复制粘贴)
    //   editor.insertText(text ? text : "");
    //   event.preventDefault();
    //   return false;
    // },

    //插图
    MENU_CONF: {
      // 不一定要使用,插图配置
      insertImage: {
        onInsertedImage(imageNode: ImageElement | null) {
          // TS 语法
          if (imageNode == null) {
            return;
          }

          const { src, alt, url, href } = imageNode;
          console.log("inserted image", src, "|", alt, "|", url, "|", href);
        },
        checkImage: customCheckImageFn, // 也支持 async 函数
        parseImageSrc: customParseImageSrc // 也支持 async 函数
      },
     //上传图片
      uploadImage: {
        // server: "/api-base/upload/uploadFile",
        // form-data fieldName ,默认值 'wangeditor-uploaded-image'
        // fieldName: "file-uploaded-image",

        // 单个文件的最大体积限制,默认为 2M
        maxFileSize: 4 * 1024 * 1024, // 4M

        // 最多可上传几个文件,默认为 100
        maxNumberOfFiles: 10,

        // 选择文件时的类型限制,默认为 ['image/*'] 。如不想限制,则设置为 []
        // allowedFileTypes: [],

        // 自定义上传参数,例如传递验证的 token 等。参数会被添加到 formData 中,一起上传到服务端。
        // meta: {
        //   token: uchoiceToken.get(),
        // // otherKey: "yyy"
        // // file: ""
        // },

        // 将 meta 拼接到 url 参数中,默认 false
        // metaWithUrl: false,

        // 自定义增加 http  header
        // headers: {
        //   // Accept: "image/x-json",
        //   // otherKey: "xxx",
        //   Authorization: "Bearer " + uchoiceToken.get(),
        //   // "Content-Type": "multipart/form-data"
        //   Content: {
        //     Type: "application/json"
        //   }
        // },

        // 跨域是否传递 cookie ,默认为 false
        // withCredentials: true,

        // 超时时间,默认为 10 秒
        timeout: 5 * 1000, // 5 秒
        
        //最大字数
        // maxLength: 500

        // 上传之前触发
        // onBeforeUpload(file: File) {
        //   // file 选中的文件,格式如 { key: file }
        //   return file;

        //   // 可以 return
        //   // 1. return file 或者 new 一个 file ,接下来将上传
        //   // 2. return false ,不上传这个 file
        // },

        // 上传进度的回调函数
        onProgress(progress: number) {
          // progress 是 0-100 的数字
          console.log("progress", progress);
        },

        // 单个文件上传成功之后
        onSuccess(file: File, res: any) {
          console.log(`${file.name} 上传成功`, res);
        },

        // 单个文件上传失败
        onFailed(file: File, res: any) {
          console.log("qz-失败url", getUliveAdminBaseUrl());

          console.log(`${file.name} 上传失败`, res);
        },

        // 上传错误,或者触发 timeout 超时
        onError(file: File, err: any, res: any) {
          console.log("qz-失败", res);

          console.log(`${file.name} 上传出错`, err, res);
        },
        // 自定义插入图片
        // customInsert(res: any, insertFn: InsertFnType) { // TS 语法
        //   // res 即服务端的返回结果
        //   // const result = res;
        //   // console.log("qz", result);
        //   const { url, alt, href } = res;
        //   // 从 res 中找到 url alt href ,然后插图图片
        //   insertFn(url, alt, href);
        // },

        // customUpload(file: File, insertFn: InsertFnType) { // TS 语法
        //   // file 即选中的文件
        //   // 自己实现上传,并得到图片 url alt href
        //   // 最后插入图片
        //   console.log("qz---file", file);

        //   // const { url, alt, href } = file;
        //   // insertFn(url, alt, href);
        // }

        // 用户自定义上传图片
        customUpload(file: any, insertFn: any) {
          console.log("qz-上传地址", getUliveAdminBaseUrl());

          const axios = require("axios");
          const FormData = require("form-data");
          const data = new FormData();
          data.append("file", file); // file 即选中的文件 主要就是这个传的参数--查看接口需要带什么参数
          const config = {
            method: "post",
            url: `${getUliveAdminBaseUrl()}api-base/upload/uploadFile`, //上传图片地址 建议使用模板字符串拼接
            // headers: {
            //   "Content-Type": "multipart/form-data",
            //   Authorization: "Bearer " + uliveToken.get()
            // },//
            data: data
          };

          axios(config)
            .then(function (res: any) {
              console.log("qz-用户自定义上传图片", res);
              // const url = "https:// /" + res.data.data.path; //拼接成可浏览的图片地址
              insertFn(res.data.result); //插入图片
            })
            .catch(function (error: any) {
              console.log(error);
            });
        }
      }
    }

    
  };

  useEffect(() => {
    // console.log("qz-props.htmlContent", props.htmlContent, "<></>");
    // setHtml("<p>Hello World</p>");//初始值
    setHtml(props.htmlContent); //设置编辑器内容
  }, [props.htmlContent]);

  // 及时销毁 editor ,重要!
  useEffect(() => {
    // const toolbar = DomEditor.getToolbar(editor); //查看配置
    // console.log("qz-toolbar", toolbar);
    return () => {
      if (editor == null) {
        return;
      }
      editor.destroy();
      setEditor(null);
    };
  }, [editor]);

  return (
    <>
      <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) => {
            // console.log("qz---editor", editor.getHtml());
            setHtml(editor.getHtml());
            props.onChange(editor.getHtml());// 父组件传一个onChange 方法可以把编辑器的内容存到父组件
          }}
          mode='default'
          style={{ height: "500px", overflowY: "hidden" }}
        />
      </div>
      {/* <div style={{ marginTop: "15px" }}>{html}</div> */}
    </>
  );
}

export default MyEditor;

上手就用: 稍微做了整理

import React, { useState, useEffect } from "react";
import "@wangeditor/editor/dist/css/style.css"; // 引入 cssß
import { Editor, Toolbar } from "@wangeditor/editor-for-react";
import type { IDomEditor, IEditorConfig, IToolbarConfig } from "@wangeditor/editor";
// import { DomEditor } from "@wangeditor/editor";
// import api from "@/api/messageCenter";
// import { message } from "antd";
// import { uchoiceToken, uliveToken } from "@/utils/auth";
import { getUliveAdminBaseUrl } from "@/utils/getBaseUrl";

import type { SlateElement } from "@wangeditor/editor";

export type ImageElement = SlateElement & {
  src: string;
  alt: string;
  url: string;
  href: string;
};
export type InsertFnType = (url: string, alt: string, href: string) => void;

function MyEditor(props: any) {
  // editor 实例
  const [editor, setEditor] = useState<IDomEditor | any>(null); // TS 语法
  // 编辑器内容
  const [html, setHtml] = useState("");
  // 工具栏配置
  const toolbarConfig: Partial<IToolbarConfig> = {
    //去除的选项
    excludeKeys: [
      "headerSelect", //正文,标题
      "blockquote", //引用
      // "|",//隔符
      "bold", //加粗
      "underline", //下划线
      "italic", //斜体
      "group-more-style", //样式
      "color", //颜色
      "bgColor", //背景色
      "fontSize", //字体大小
      "fontFamily", //字体
      // "lineHeight",行高
      "bulletedList", //无序列表
      "numberedList", //有序列表
      "todo", //待办
      "group-justify", //对齐
      "group-indent", //缩进
      // "emotion",//表情
      "insertLink", //链接
      // "group-image", //图片
      "group-video", //视频
      "insertTable", //插表格
      "codeBlock", //代码块
      "divider" //分割线
      // "undo",//撤销
      // "redo",//重做
      // "fullScreen"//全屏
    ]
  };
  // 编辑器配置
  const editorConfig: Partial<IEditorConfig> = {
    placeholder: "请输入内容...",
    autoFocus: false,
    //插入图片
    MENU_CONF: {
      uploadImage: {
        // 单个文件的最大体积限制,默认为 2M
        maxFileSize: 4 * 1024 * 1024, // 4M
        // 最多可上传几个文件,默认为 100
        maxNumberOfFiles: 10,
        // 超时时间,默认为 10 秒
        timeout: 5 * 1000, // 5 秒
        // 用户自定义上传图片
        customUpload(file: any, insertFn: any) {
          const axios = require("axios");
          const FormData = require("form-data");
          const data = new FormData();
          data.append("file", file); // file 即选中的文件 主要就是这个传的参数---看接口要携带什么参数{ key :value}
          const config = {
            method: "post",
            url: `${getUliveAdminBaseUrl()}api-base/upload/uploadFile`, //上传图片地址
            // headers: {
            //   "Content-Type": "multipart/form-data",
            //   Authorization: "Bearer " + uliveToken.get()
            // }, //需要加的自己参考接口加
            data: data
          };
          axios(config)
            .then(function (res: any) {
              console.log("qz-用户自定义上传图片", res);
              // const url = "https:// /" + res.data.data.path; //拼接成可浏览的图片地址
              insertFn(res.data.result); //插入图片,看返回的数据是什么
            })
            .catch(function (error: any) {
              console.log(error);
            });
        }
      }
    }
  };

  useEffect(() => {
    // setHtml("<p>Hello World</p>");
    setHtml(props.htmlContent); //设置编辑器内容
  }, [props.htmlContent]);

  // 及时销毁 editor ,重要!
  useEffect(() => {
    // const toolbar = DomEditor.getToolbar(editor); //查看配置
    // console.log("qz-toolbar", toolbar);
    return () => {
      if (editor == null) {
        return;
      }
      editor.destroy();
      setEditor(null);
    };
  }, [editor]);

  return (
    <>
      <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) => {
            // console.log("qz---editor", editor.getHtml());
            //获取纯文本可使用 getText()
            setHtml(editor.getHtml());
            props.onChange(editor.getHtml());
          }}
          mode='default'
          style={{ height: "500px", overflowY: "hidden" }}
        />
      </div>
      {/* <div style={{ marginTop: "15px" }}>
        {html} //可显示编辑器的内容
      </div> */}
    </>
  );
}

export default MyEditor;

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
React自定义Hooks是一种自定义React Hook函数,用于在无需编写类组件的情况下,复用和共享状态逻辑。通过使用自定义Hooks,我们可以将组件中的逻辑提取出来,以便在多个组件之间共享和重用。引用中提到的Hook是React 16.8新增的特性,它可以让我们在函数组件中使用状态和其他React特性,而无需编写类组件。而引用中的useUpdateEffect是一个自定义React Hook,它类似于React的useEffect,但是只在组件更新时执行副作用操作,忽略了组件的初始渲染阶段。 使用React自定义Hooks的步骤如下: 1. 创建自定义Hook函数,函数名以"use"开头,例如"useCustomHook"。 2. 在自定义Hook函数中,可以使用其他React Hooks和自定义逻辑来实现所需的功能。 3. 在组件中通过调用自定义Hook函数来使用该Hook,以获取状态和执行副作用操作。 下面是一个示例,展示如何创建和使用一个自定义Hook来管理计数器的状态: ```javascript // 自定义Hook import { useState } from 'react'; function useCounter(initialValue) { const [count, setCount = useState(initialValue); const increment = () => { setCount(count + 1); }; const decrement = () => { setCount(count - 1); }; return { count, increment, decrement }; } // 使用自定义Hook的组件 import React from 'react'; import useCounter from './useCounter'; function Counter() { // 使用useCounter自定义Hook,获取计数器的状态和操作函数 const { count, increment, decrement } = useCounter(0); return ( <div> <p>Count: {count}</p> <button onClick={increment}>Increment</button> <button onClick={decrement}>Decrement</button> </div> ); } ``` 在上述示例中,我们创建了一个名为useCounter的自定义Hook函数,它使用useState来管理计数器的状态,并提供了increment和decrement函数来增加和减少计数器的值。然后,我们在Counter组件中使用useCounter来获取计数器的状态和操作函数,并展示计数器的值以及按钮来增加和减少计数器的值。 通过使用React自定义Hooks,我们可以轻松地复用和共享状态逻辑,使代码更加可维护和可扩展。通过创建自定义Hooks,我们可以将组件的逻辑分离出来,提高代码的重用性和可读性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值