react-quill 富文本编辑器踩坑记录

2,796 阅读5分钟

react-quill 是一个基于 React 的富文本编辑器组件,它基于 Quill 富文本编辑器库。本文主要介绍了基础配置及使用,图片 base64 转地址处理,自定义字体、字号、行间距等的实现。

该富文本编辑器是 antd 社区精选组件中推荐的,具体可以查看官网:ant-design.antgroup.com/docs/react/…

Github 地址为:github.com/zenoamaro/r…

1. 安装及基础使用

  1. 安装依赖ReactQuill
# 安装
npm install react-quill --save
  1. 引入及使用:

react-quill 提供了许多配置选项,可以自定义编辑器的行为和外观。

以下是一些常用的配置选项:

  • theme: 设置编辑器的主题,可选值有 'snow' 和 'bubble'。
  • modules: 配置编辑器的功能模块,如工具栏、历史记录等。
  • formats: 指定编辑器支持的格式。
// 引入组件及样式
import ReactQuill, { Quill } from "react-quill";
import "react-quill/dist/quill.snow.css";

// 使用组件
<ReactQuill
  theme="snow"
  onChange={handleEditorChange}
  value={content}
  modules={quillToolbar}
/>;

// 数据绑定到form表单
const [content, setContent] = useState("");
const handleEditorChange = (content: any) => {
  setContent(content);
  form.setFieldsValue({ content: content });
};

2. 基础配置

具体可查看官方文档:quilljs.com/docs/module…

以下是一些常用的基础配置

export const quillToolbar = {
  toolbar: [
    [{ header: [1, 2, 3, 4, 5, 6, false] }], // 标题
    [{ font: [] }], // 字体
    [{ size: [] }], // 字号
    [
      { color: [] }, // 文字颜色
      { background: [] }, // 背景色
    ],
    [
      "bold", // 加粗
      "italic", // 斜体
      "underline", // 下划线
      "strike", // 删除线
      "blockquote", // 引用
      "code-block", // 代码块
    ],
    [{ script: "sub" }, { script: "super" }], // superscript(x2)/subscript
    [{ align: [] }], // 对齐方式
    [{ indent: "-1" }, { indent: "+1" }],
    [{ direction: "rtl" }],
    [
      "link", // 链接
      "image", // 图片
      "video", // 视频
      // "formula", // 数学函数
    ],
    [
      { list: "ordered" }, // 有序列表
      { list: "bullet" }, // 无序列表
      { list: "check" }, // 选中列表
    ],
    ["clean"], // 清除按钮
  ],
};

基础配置展示如下所示:

quill-1.png

3. 图片 base64 转为地址上传

react-quill 提供了多种事件处理方法,如 onChange、onFocus、onBlur 等,可以根据需要进行处理。

默认情况下,react-quill 富文本编辑器中上传图片时,是将图片转为 base64 数据保存的,这样的话,保存到数据库中时,数据太大了,很不方便。所以最好的方式,是先传到服务器上,富文本组件中,只上传图片地址就行

处理逻辑如下所示:在onChange事件中,判断是否是图片,如果是图片,就上传到服务器,并返回地址,否则就返回原来的数据。

3.1 base64toFile

base64toFile:图片 base64 数据转为文件

export function base64toFile(base64String: string): File {
  // 去掉 base64 字符串的前缀(如 "data:image/png;base64,")
  const base64Data = base64String.replace(/^data:image\/\w+;base64,/, "");
  // 将 base64 字符串解码为二进制数据
  const byteCharacters = atob(base64Data);
  // 将二进制数据转换为字节数组
  const byteNumbers = new Array(byteCharacters.length);
  for (let i = 0; i < byteCharacters.length; i++) {
    byteNumbers[i] = byteCharacters.charCodeAt(i);
  }
  // 创建一个 Uint8Array
  const uint8Array = new Uint8Array(byteNumbers);
  // 创建 Blob 对象
  const blob = new Blob([uint8Array], { type: "image/png" });
  // 生成唯一的文件名
  const filename = `image_${Date.now()}.png`;
  // 创建 File 对象
  const file = new File([blob], filename, { type: "image/png" });

  return file;
}

3.2 handleEditorChange

handleEditorChange:图片文件上传到服务器,替换富文本编辑器中的内容。具体实现如下所示:

const handleEditorChange = (ctx: any, delta: any, source: any, editor: any) => {
  // 图片上传后为base64,上传至服务器文件过大,所以需要将base64格式图片进行转换,具体方法如下:
  let delta_ops = delta.ops;
  // 获取富文本内容
  let quillContent = editor.getContents();
  if (delta_ops && delta_ops.length) {
    quillContent?.ops?.map(async (item: any) => {
      if (item.insert) {
        let imgStr = item.insert.image;
        // 判断是否为图片
        if (imgStr && imgStr?.startsWith("data:image/")) {
          let file = base64toFile(imgStr);
          // 调用文件上传接口:将二进制图片文件上传至服务器,服务器返回一个图片地址
          const result: any = await OssClient.uploadFile(OssPath.AQuill, file);
          // 更新为图片地址
          item.insert.image = result.filePath;
          // 设置新的富文本内容
          setContent(quillContent);
          // 设置表单数据
          form.setFieldsValue({ content: quillContent });
        } else {
          setContent(ctx);
          form.setFieldsValue({ content: ctx });
        }
      }
    });
  }
};

quill-5.png

4. 自定义字号

正常情况下,字号只有 small、normal、large、huge 等 4 中,不符合我们的使用习惯,一般都是使用 px 来定义的,因此需要自定义处理一下。

quill-2.png

  1. 自定义字号
export const sizeArr = ["ft12", "ft14", "ft16", "ft18", "ft20", "ft24", "ft32"];
export const quillToolbar = {
  toolbar: [
    [{ size: sizeArr }], // 字号大小
  ],
};
  1. 自定义 quill 编辑器的字号
import ReactQuill, { Quill } from "react-quill";
import "react-quill/dist/quill.snow.css";

const FontAttributor = Quill.import("attributors/class/size");
FontAttributor.whitelist = sizeArr;
Quill.register(FontAttributor, true);
  1. 自定义字号样式实现

如下所示:举例写了 3 个,12px、14px、32px,其他的自己补充就行,注意:必须一一对应

.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="ft12"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="ft12"]::before {
  content: "12px";
  font-size: 12px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="ft32"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="ft32"]::before {
  content: "32px";
  font-size: 32px;
}
.ql-size-ft12 {
  font-size: 12px;
}
.ql-size-ft14 {
  font-size: 14px;
}
.ql-size-ft32 {
  font-size: 32px;
}

quill-2.1.png

5. 自定义字体

正常情况下,字体也是不常见的,也需要自定义处理一下,也需要引入对应的字体文件。

quill-3.png

  1. 自定义字体
export const fontArr = [
  "SimSun", // 宋体
  "SimHei", // 黑体
  "Microsoft-YaHei", // 微软雅黑
  "KaiTi", // 楷体
  "FangSong", // 仿宋
];
export const quillToolbar = {
  toolbar: [
    [{ font: fontArr }], // 字体
  ],
};
  1. 自定义 quill 编辑器的字体
const Font = Quill.import("formats/font");
Font.whitelist = fontArr; // 将字体加入到白名单
Quill.register(Font, true); // 注册字体
  1. 自定义字体样式实现
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="SimSun"]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="SimSun"]::before {
  font-family: "SimSun" !important;
  content: "宋体";
}
/*  设置每个字体的css字体样式,举例一个 */
.ql-font-SimSun {
  font-family: "SimSun";
}

完整配置如下所示:

quill-3.1.png

6. 自定义行高

默认没有行高这个属性配置,需要完全自定义处理,配置和上面的差不多

quill-4.png

// 1. 配置行高属性
export const lineHeightArr = ["1", "1.5", "1.75", "2", "3"];
// 2. 定义行高选项配置
export const quillToolbar = {
  toolbar: [
    [{ "line-height": lineHeightArr }], // 行间距
  ],
};

// 3. 注册行高配置
const Parchment = Quill.import("parchment");
const lineHeightStyle = new Parchment.Attributor.Style(
  "lineHeight",
  "line-height",
  {
    scope: Parchment.Scope.INLINE,
    whitelist: lineHeightArr,
  }
);
Quill.register({ "formats/lineHeight": lineHeightStyle }, true);
/* 4. 行高样式 */
.ql-snow .ql-picker.ql-line-height .ql-picker-label[data-value="2"]::before,
.ql-snow .ql-picker.ql-line-height .ql-picker-item[data-value="2"]::before {
  content: "2";
}

quill-4.1.png