如何在antd中使用富文本编辑器Braft Editor并支持上传图片

1,026 阅读2分钟

前言

最近项目上需要使用富文本编辑器撰写一个类似新闻发布的表单,同时配合当前最流行的UI库,Antd。

在查询了相关的资料以后,发现,“Braft Editor”是一个不错的推荐。

这篇文章主要介绍Braft Editor与Antd的结合使用。

功能实现

文档中本身就已经有记载,关于集成antd的上传。 braft.margox.cn/demos/antd-…

功能要点
  • 使用controls或者excludeControls来隐藏编辑器自带的媒体库控件
  • 使用component类型的extendControls将Upload组件集成到编辑器工具栏
  • 使用ContentUtils来将上传后的图片插入到编辑器

最主要的关键点是补充:customRequest里面的方法。

封装组件

import request from '@/utils/request';
import { message, Upload } from 'antd';
import BraftEditor from 'braft-editor';
import 'braft-editor/dist/index.css';
import { ContentUtils } from 'braft-utils';
import React, { forwardRef, useEffect } from 'react';

const MyEditor = React.memo(
  forwardRef(({ value = '', onChange = () => {} }, ref) => {
    const controls = [
      'bold',
      'italic',
      'underline',
      'text-color',
      'separator',
      'link',
      'separator',
    ];


    useEffect(() => {
      onChange(value);
    }, [value]);

    const handleChange = (v) => {
      onChange(v);
    };

    const uploadHandler = (param) => {
      if (!param.file) {
        return false;
      }

      const fmData = new FormData();
      const config = {
        headers: { 'content-type': 'multipart/form-data' },
      };
      fmData.append('file', param.file);
      // 需要修改为自己的Url
      return request('/api/file/upload', {
        method: 'POST',
        processData: false,
        data: fmData,
        config,
      })
        .then((res) => {
          if (res && res.code === 0) {
            message.success('上传成功');
            // 此处的url需要重写为能返回显示的图片url
            onChange(
              ContentUtils.insertMedias(value, [
                {
                  type: 'IMAGE',
                  url: `previewUrl?fileKey=${res.data.fileKey}`,
                },
              ]),
            );
          } else {
            message.error(res?.message || '上传失败');
          }
        })
        .catch(() => {
          // const err = new Error('上传失败');
          message.error('上传失败');
        });
    };

    const extendControls = [
      {
        key: 'antd-uploader',
        type: 'component',
        component: (
          <Upload accept="image/*" showUploadList={false} customRequest={uploadHandler}>
            {/* 这里的按钮最好加上type="button",以避免在表单容器中触发表单提交,用Antd的Button组件则无需如此 */}
            <button
              type="button"
              className="control-item button upload-button"
              data-title="插入图片"
            >
              上传图片
            </button>
          </Upload>
        ),
      },
    ];

    return (
      <div ref={ref}>
        <div className="editor-wrapper">
          <BraftEditor
            value={value}
            onChange={handleChange}
            controls={controls}
            extendControls={extendControls}
          />
        </div>
      </div>
    );
  }),
);

export default MyEditor;

注意点

  1. 需要后端返回一个相应的上传完成的图片url,或者相应传到oss上的fileKey,用于预览。
  2. uploadHandler 主要就是这个方法,手动上传图片

使用

import { useMount } from 'ahooks';
import { Button, Col, Form, Input, Layout, message, Row } from 'antd';
import BraftEditor from 'braft-editor';
import 'braft-editor/dist/index.css';
import 'bytemd/dist/index.min.css';
// import zhHans from 'bytemd/lib/locales/zh_Hans.json';
import 'highlight.js/styles/vs.css';
import React from 'react';
import MyEditor from '../../../components/MyEditor/index';
import styles from './index.less';


const Document = () => {
  const [form] = Form.useForm();

  useMount(() => {
    // 测试回填
    form.setFieldsValue({
      title: "新闻标题",
      content: BraftEditor.createEditorState(`
      <p>4234324</p><p></p><p></p><div class="media-wrap image-wrap"><img src="xxx.png"/></div><p></p>`),
    });
  });

  const handleOk = () => {
    if (!form.getFieldValue('title')) {
      message.error('标题不能为空');
      return;
    }
    form
      .validateFields()
      .then((r) => {
        console.log('content', r.content.toHTML());
      })
      .catch((e) => console.log(e));
  };

  return (
    <div>
      <div className={styles.container}>
        <Layout style={{ position: 'relative' }}>
          <Layout className="site-layout">
            <>
              <Form
                layout="inline"
                form={form}
                // onFinish={handleSubmit}
                initialValues={{ title: '' }}
              >
                <Row style={{ width: '100%' }}>
                  <Col span={18}>
                    <Form.Item name="title" label="">
                      <Input placeholder="请输入文章标题" />
                    </Form.Item>
                  </Col>
                  <Col style={{ flex: 1 }}>
                    <Form.Item className="search_item">
                      <Button type="primary" onClick={() => handleOk()}>
                        发布
                      </Button>
                    </Form.Item>
                  </Col>
                </Row>
                <Form.Item name="content" label="内容" noStyle>
                  <MyEditor />
                </Form.Item>
              </Form>
            </>
          </Layout>
        </Layout>
      </div>
    </div>
  );
};

export default Document;


提交时需要将内容修改,将内容的值toHTML()