Skip to content

5.6 开发编辑器组件

编辑备忘录内容需要一个富文本编辑器,我们需要借助开源的第三方包来实现。经过对比,一个名为 wangEditor 的富文本编辑器表现比较优秀,且对 Vue3 支持友好,我们就选这个。

5.6.1 编写编辑器组件

下面按照步骤安装和使用该编辑器,并且封装一个名为 CusEditor.vue 的公共编辑器组件。

(1)执行如下命令安装 wangEditor:

sh
$ yarn add @wangeditor/editor
$ yarn add @wangeditor/editor-for-vue@next

(2)创建 components/CusEditor.vue 组件,并编写如下代码:

vue
<template>
  <div class="customcomp-editor">
    <toolbar :editor="editorRef" />
    <slot /> // 插槽
    <editor
      mode="simple"
      v-model="valueHtml"
      @onCreated="(ref) => (editorRef = ref)"
    />
  </div>
</template>

<script setup lang="ts">
import { ref, shallowRef } from "vue";
import { Editor, Toolbar } from "@wangeditor/editor-for-vue";
const valueHtml = ref("<p>hello</p>");
const editorRef = shallowRef();
</script>

<style lang="less">
@import "@wangeditor/editor/dist/css/style.css";
.customcomp-editor {
  width: 100%;
}
</style>

上面的代码先引入 Editor 组件和 Toolbar 组件,分别表示编辑器和工具栏;再引入编辑器的 CSS 样式文件,并双向绑定一个状态。这样一个简单的编辑器就可以使用了。

提示:在模板中的工具栏组件和编辑器组件之间有一个默认插槽,该插槽的作用是将标题文本框插入此处。

(3)实现自定义 v-model 指令。

js
<editor ... v-model="valueHtml"/>
// 修改为如下形式
<editor ... :modelValue="props.modelValue" @onChange="onChange"/>

修改之后,在 script 部分定义 props 和 onChange:

js
const props = defineProps<{
  modelValue: string;
}>();
const emits = defineEmits<{
  (e: "update:modelValue", html: string): void;	// 自定义事件
}>();
const onChange = (editor)=> {
  // 触发自定义事件
  emits("update:modelValue", editor.getHtml());
}

经过上面的完善,这个编辑器组件在外部使用时即可用 v-model 指令绑定数据,使用方式如下:

js
<cus-editor v-model="valueHtml" />

(4)自定义编辑器工具栏。

默认的编辑器工具的栏选项非常多,但很多是不常用到的,因此可以精简一下。在 Toolbar 组件的 defaultConfig 属性下可以自定义工具栏,代码如下:

js
<toolbar :defaultConfig="config"/>
// script
const config = {
  toolbarKeys: [
    "undo", "redo", "|", "bold","italic", "through","color","bgColor","|",
    "fontSize", {
      key: "group-justify",
      menuKeys: [
        "justifyLeft",
        "justifyRight",
        "justifyCenter",
        "justifyJustify",
      ],
      title: "对齐",
    },
    "insertImage", "|",
    "clearStyle",
  ],
};

至此,编辑器组件开发完成,关于更多细节请查阅本章的配套代码。

5.6.2 实现备忘录编辑

上面实现了编辑器组件,接下来使用该组件编辑选中的备忘录内容。

(1)引入公共编辑器组件 CusEditor,并定义一个标题文本框,之后将其作为默认插槽插入编辑器内部:

vue
<template>
  ...
  <div class="detail">
    <cus-editor v-model="content">
      <input placeholder="输入标题" class="memo-title" v-model="title" />
    </cus-editor>
  </div>
</template>
<script setup lang="ts">
import { ref } from "vue";
import CusEditor from "@/components/CusEditor.vue";
const title = ref("");
const content = ref("");
</script>

上面的代码定义了 title 状态和 content 状态,分别表示备忘录的标题和内容。在默认情况下,这两个状态是空的,因此还要获取当前选中的备忘录并为其赋值。

(2)在首页 Store 中定义一个名为 activeMemo 的 Getter,用来动态获取当前选中的单条备忘录:

js
getters: {
  activeMemo(state): MemoType {
    return this.activeMemos.find((r) => r.memo_id == state.active_memoid);
  },  // 当前的单条备忘录
},

(3)在 JavaScript 代码中监听 active_memoid 状态并修改备忘录的标题和内容,这里需要使用计算属性和 watch 监听:

js
import { watch, nextTick } from "vue";
import { indexStore } from "@/stores";
const store = indexStore();

const updateCtx = () => {
  if (store.activeMemo) {
    // 避免数据修改页面不更新
    nextTick(() => {
      title.value = store.activeMemo.title;
      content.value = store.activeMemo.content;
    });
  }
};
watch(
  () => store.active_memoid,
  (val) => {
    if (val) {
      updateCtx();
    } else {
      title.value = content.value = "";
    }
  }
);

至此,实现了编辑标题和富文本内容时的自动保存。

请尝试编写一段文字并刷新浏览器,发现文本内容依然存在;或者直接查看控制台的 localStorage,发现本地数据已经更新。

最终的编辑器的页面效果如图所示。