wangeditor自己实现关键字高亮
工作中用到了这个编辑器,本以为关键字高亮这个如此高频的功能它肯定是实现了的,结果竟然没有!!网上找了一圈也没有,好家伙我排期也没排这块时间啊,所以只能自己加班写一个了。
方案简析
wangeditor底层其实使用的是Slate这个编辑器,你要自己实现此功能,本质上就是调用Slate对应的方法。
主要使用了以下几个大类SlateEditor、SlateTransforms
SlateEditor
调用SlateEditor.nodes获取到所有的文本节点
// 使用 Editor.nodes 方法遍历所有节点
const textNodes = Array.from(
SlateEditor.nodes(editorRef, {
at: [],
match: matchText,
})
);
SlateTransforms
调用SlateTransforms.setNodes 传入参数首位坐标让指定关键字单独形成一个文本节点,从而实现高亮
// au 为首尾坐标
SlateTransforms.setNodes(
editorRef,
{
//@ts-ignore
color,
},
{
at: au,
split: true,
match: (value) => {
//@ts-ignore
return isPlainObject(value) && typeof value?.text === "string";
},
}
);
调用SlateTransforms.mergeNodes 传入指定节点让其与其他节点合并,用于关键字被删除后取消高亮
SlateTransforms.mergeNodes(editorRef, {
at: au.anchor.path,
});
完整代码
直接上代码,也不难,主要是大家对slate的api不熟悉,相信大家一看就懂,
注意一下findTemplatePositions方法,为判断文字是否高亮的条件,大家按需修改即可
<script setup lang="ts">
import { onBeforeUnmount, ref, shallowRef, onMounted } from "vue";
import { SlateEditor, SlateTransforms } from "@wangeditor/editor";
import { Editor, Toolbar } from "@wangeditor/editor-for-vue";
// 编辑器实例,必须用 shallowRef,重要!
const editorRef = shallowRef();
// 内容 HTML
const valueHtml = ref("<p>hello</p>");
function isObject(value) {
return value !== null && typeof value === "object" && !Array.isArray(value);
}
function isPlainObject(o) {
let ctor, prot;
if (isObject(o) === false) return false;
// If has modified constructor
// eslint-disable-next-line prefer-const
ctor = o.constructor;
if (ctor === undefined) return true;
// If has modified prototype
// eslint-disable-next-line prefer-const
prot = ctor.prototype;
if (isObject(prot) === false) return false;
// If constructor does not have an Object-specific method
if (prot.hasOwnProperty("isPrototypeOf") === false) {
return false;
}
// Most likely a plain Object
return true;
}
function matchText(value) {
return isPlainObject(value) && typeof value.text === "string";
}
function findTemplatePositions(str) {
const regex = /{([^}]*)}/g; // 创建正则表达式,匹配以{开头并以}结尾的内容
let match;
const positions: any = [];
while ((match = regex.exec(str)) !== null) {
const startIndex = match.index;
const endIndex = startIndex + match[0].length;
positions.push({ startIndex, endIndex, content: match[0] });
}
return positions;
}
function mergeNodes(au, editorRef) {
SlateTransforms.setNodes(
editorRef,
{
//@ts-ignore
color: "",
},
{
at: au,
split: false,
match: matchText,
}
);
SlateTransforms.mergeNodes(editorRef, {
at: au.anchor.path,
});
}
function setNodes(au, editorRef, color) {
SlateTransforms.setNodes(
editorRef,
{
//@ts-ignore
color,
},
{
at: au,
split: true,
match: (value) => {
//@ts-ignore
return isPlainObject(value) && typeof value?.text === "string";
},
}
);
}
// 模拟 ajax 异步获取内容
onMounted(() => {});
// 编辑器配置
const editorConfig = {
placeholder: "请输入内容...",
MENU_CONF: {
/* 菜单配置,下文解释 */
},
};
function parseTTSContentWithJsonPathToHtml(editorRef, color) {
// setTimeout(() => {
try {
// 使用 Editor.nodes 方法遍历所有节点
const textNodes = Array.from(
SlateEditor.nodes(editorRef, {
at: [],
match: matchText,
})
);
// 打印所有文本节点
textNodes.forEach(([node, path]: any) => {
const arr = findTemplatePositions(node.text);
if (node.color === color) {
if (
!arr.length ||
arr[0].startIndex !== 0 ||
arr[0].endIndex !== node.text.length
) {
const au = {
anchor: {
path: path,
offset: 0,
},
focus: {
path: path,
offset: node.text.length,
},
};
mergeNodes(au, editorRef);
}
} else {
if (arr.length) {
arr.forEach((item) => {
const au = {
anchor: {
path: path,
offset: item.startIndex,
},
focus: {
path: path,
offset: item.endIndex,
},
};
setNodes(au, editorRef, color);
});
}
}
});
} catch (error) {}
// }, 100)
}
function handChange() {
parseTTSContentWithJsonPathToHtml(editorRef.value, "#FAAD14");
}
const handleCreated = (editor) => {
editorRef.value = editor; // 记录 editor 实例,重要!
};
// 组件销毁时,及时销毁编辑器
onBeforeUnmount(() => {
const editor = editorRef.value;
if (editor == null) return;
editor.destroy();
});
</script>
<template>
<div style="border: 1px solid #ccc">
<Toolbar
:editor="editorRef"
style="border-bottom: 1px solid #ccc"
/>
<!-- 编辑器 -->
<Editor
v-model="valueHtml"
:defaultConfig="editorConfig"
style="height: 500px; overflow-y: hidden"
@onCreated="handleCreated"
@on-change="handChange"
/>
</div>
</template>
<!-- 别忘了引入样式 -->
<style src="@wangeditor/editor/dist/css/style.css"></style>

浙公网安备 33010602011771号