Quill 富文本编辑器二次封装
Quill 是一个很流行的富文本编辑器,github 上 star 大约 21k。但是很少项目中使用时直接使用它,都要对其进行二次封装,你作为一个页面仔是挡不住有想法的产品经理的。
安装 quill 库
- 原生应用中使用
<link href="https://cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet" />
<script src="https://cdn.quilljs.com/1.3.6/quill.js"></script>
- spa 应用中使用
// npm install quill
import Quill from "quill";
实例化 Quil 对象
实例化 Quill,挂载到当前上下文中,然后就能使用它的相关方法及属性
import Quill from "quill";
import "quill/dist/quill.snow.css";
const options = {
debug: "warn",
modules: {
toolbar: [
["bold", "italic", "underline", "blockquote"],
[{ align: "justify" }, { align: "center" }, { align: "right" }]
// ['link', 'image'],
]
},
placeholder: this.props.placeholder || "请输入内容...",
readOnly: false,
theme: "snow"
};
const quill = new Quill(".my-editer", options); // 将其实例化到dom树上.my-editor的节点上
getSelection
我们在插入内容时,要得知几个必要条件:
- 1、从哪里开始插入
- 2、插入的内容是什么 (文字、图片、标题、视频、音频)?
- 3、插入的内容是否需要格式化?(标题的话,需要插入文本然后格式化成标题格式)
那么 getSelection()
方法便是用来获取光标的,就让我们知道了现在用户光标置于何处,那么就知道了第一个条件
this.quill.getSelection();
insertText
insertText()
方法是插入文本用的,一般都会在插入后,格式化其他格式,所以下面的方法基本要连用
format
format()
方法是格式化数据的,能将文本格式化成链接、标题、对齐、大小等等
// 格式化成标题
this.quill.insertText(section_index, getFieldValue("title"));
this.quill.getSelection();
this.quill.format(
"header",
toolbar[activeToolIndex].value,
getFieldValue("title")
);
// 格式化成链接
getFieldValue("link") && this.quill.format("link", getFieldValue("link"));
on
on()
是监听某些变化用的,比如 text-change,就是在富文本中的编辑内容更改时会触发,这里面会处理很多的变化相关
this.quill = new Quill(".mys-editor", this.options);
this.quill.on("text-change", this.handleChange.bind(this));
注册自定义的标签
因为 quill 中的标签是有限的,如果要使用其他标签的话,需要注册进去,以 video 标签为例:
- 同级目录下新建 quil-video.js
const Quill = require("quill");
const BlockEmbed = Quill.import("blots/block/embed");
export class VideoBlot extends BlockEmbed {
static create(value: any) {
let node = super.create();
node.setAttribute("src", value.url);
node.setAttribute("controls", value.controls);
node.setAttribute("width", value.width);
node.setAttribute("height", value.height);
node.setAttribute("webkit-playsinline", true);
node.setAttribute("playsinline", true);
node.setAttribute("x5-playsinline", true);
return node;
}
static value(node: any) {
return {
url: node.getAttribute("src"),
controls: node.getAttribute("controls"),
width: node.getAttribute("width"),
height: node.getAttribute("height")
};
}
}
然后在富文本编辑器的组件中在实例化的 Quill 上注册该标签
import Quill from "quill";
import { VideoBlot } from "./quill-video";
VideoBlot.blotName = "simpleVideo";
VideoBlot.tagName = "video";
Quill.register({ "formats/simpleVideo": VideoBlot }, true);
便可以在相应的位置进行插入 video 标签了
const p = this.getSelection(); // 获取插入的光标位置
this.quill.insertEmbed(p, "simpleVideo", {
url, // 视频的地址
controls: "controls",
width: "100%"
});
oss 加密引起问题
图片、视频使用三方 oss 存储,那么存在一个问题,就是回显图片时需要图片地址后面带上 ossToken,上传数据保存时,需要去除 ossToken,所以需要以下正则来实现,就是富文本最终结果是个字符串,使用正则匹配每个 img,video 标签的 src,将其对应的替换:
- 上传时清除 ossToken
export function formatEditData(value: string) {
const imgReg = /<img.*?(?:>|\/>)/gi;
const videoReg = /(<video.*?)\<\/video>/gi;
if (value.match(imgReg)) {
value = changeImgurl(value);
}
if (value.match(videoReg)) {
value = changeVideoUrl(value);
}
return value;
}
export function changeImgurl(value: string) {
const result = value.replace(/<img [^>]*src=['"]([^'"]+)[^>]*>/gi, function(
match,
capture
) {
const ret = capture.replace("https://img.schtwz.cn/", "");
const result = ret.slice(0, ret.indexOf("?"));
return `<img src="${result}">`;
});
return result;
}
export function changeVideoUrl(value: string) {
const result = value.replace(
/<video [^>]*src=['"]([^'"]+).*?\<\/video>/gi,
function(match, capture) {
const ret = capture.replace("https://img.schtwz.cn/", "");
const result = ret.slice(0, ret.indexOf("?"));
return `<video src="${result}" controls="controls" width="100%" height="undefined" webkit-playsinline="true" playsinline="true" x5-playsinline="true"></video>`;
}
);
return result;
}
- c 端解析富文本地址
export function changeUrl(value: string) {
if (typeof value != "string") return "";
let result = value.replace(/<img [^>]*src=['"]([^'"]+)[^>]*>/gi, function(
match,
capture
) {
return `<img src="${signatureUrl(capture)}">`;
});
result = result.replace(
/<video [^>]*src=['"]([^'"]+).*?\<\/video>/gi,
function(match, capture) {
// console.log(capture);
return `<video src="${signatureUrl(
capture
)}" controls="controls" width="100%" height="undefined" webkit-playsinline="true" playsinline="true" x5-playsinline="true"></video>`;
}
);
return result;
}