工作中用到了这个编辑器,本以为关键字高亮这个如此高频的功能它肯定是实现了的,结果竟然没有!!网上找了一圈也没有,好家伙我排期也没排这块时间啊,所以只能自己加班写一个了。
方案简析
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-ignorecolor,},{at: au,split: true,match: (value) => {//@ts-ignorereturn 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-constctor = o.constructor;if (ctor === undefined) return true;// If has modified prototype// eslint-disable-next-line prefer-constprot = ctor.prototype;if (isObject(prot) === false) return false;// If constructor does not have an Object-specific methodif (prot.hasOwnProperty("isPrototypeOf") === false) {return false;}// Most likely a plain Objectreturn 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-ignorecolor: "",},{at: au,split: false,match: matchText,});SlateTransforms.mergeNodes(editorRef, {at: au.anchor.path,});
}
function setNodes(au, editorRef, color) {SlateTransforms.setNodes(editorRef,{//@ts-ignorecolor,},{at: au,split: true,match: (value) => {//@ts-ignorereturn 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"/><!-- 编辑器 --><Editorv-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>