likes
comments
collection
share

适配vue3的万能富文本编辑器,傻瓜式套用基于Quill的二次封装 本文的组件是基于Quill封装成的组件,可以嵌入到项

作者站长头像
站长
· 阅读数 51

基于Quill的二次封装

本文的组件是基于Quill封装成的组件,可以嵌入到项目直接使用,vue2,vue3都可以使用,Quill 这是Quill的官方网站,看不懂的api可以进去查阅 先看一张效果图吧

适配vue3的万能富文本编辑器,傻瓜式套用基于Quill的二次封装 本文的组件是基于Quill封装成的组件,可以嵌入到项

废话不多说,直接上教程: 先新建富文本组件,比如

适配vue3的万能富文本编辑器,傻瓜式套用基于Quill的二次封装 本文的组件是基于Quill封装成的组件,可以嵌入到项

// 安装插件
npm install @vueup/vue-quill@alpha --save
// 局部引入  这里可以在全局 main.ts 引入,也可以在封装的组件引入
import { QuillEditor } from '@vueup/vue-quill'
import '@vueup/vue-quill/dist/vue-quill.snow.css'

全部代码如下:

<template>
  <div>
    <!-- 此处注意写法v-model:content -->
    <QuillEditor
      ref="myQuillEditor"
      theme="snow"
      v-model:content="content"
      :options="data.editorOption"
      contentType="html"
      @update:content="setValue()"
    />
    <!-- 使用自定义图片上传 -->
    <input
      type="file"
      hidden
      accept=".jpg,.png"
      ref="fileBtn"
      @change="handleUpload"
    />
  </div>
</template>

<script lang="ts" setup>
import { QuillEditor } from '@vueup/vue-quill';
import '@vueup/vue-quill/dist/vue-quill.snow.css';
import { reactive, onMounted, ref, toRaw, watch } from 'vue';
import { uploadImgApi } from '@/api/modules/upload';

const props = defineProps<{
  value: any;
  readOnly: boolean;
}>();

const emit = defineEmits(['updateValue']);
const content = ref('');
const myQuillEditor = ref();

// 只监听一次 通过watch监听回显
const stopWatch = watch(
  () => props.value,
  (val) => {
    if (myQuillEditor.value) {
      toRaw(myQuillEditor.value).setHTML(val);
    }
    // 停止监听
    stopWatch();
  },
  { deep: true }
);
const fileBtn = ref();
const data = reactive({
  content: '',
  editorOption: {
    readOnly: props.readOnly ?? false,
    modules: {
      toolbar: [
        ['bold', 'italic', 'underline', 'strike'],
        [{ size: ['small', false, 'large', 'huge'] }],
        [{ font: [] }],
        [{ align: [] }],
        [{ list: 'ordered' }, { list: 'bullet' }],
        [{ indent: '-1' }, { indent: '+1' }],
        [{ header: 1 }, { header: 2 }],
        ['image'],
        [{ direction: 'rtl' }],
        [{ color: [] }, { background: [] }],
      ],
    },
    placeholder: '请输入内容...',
  },
});
const imgHandler = (state: any) => {
  if (state) {
    fileBtn.value.click();
  }
};
// 抛出更改内容,此处避免出错直接使用文档提供的getHTML方法
const setValue = () => {
  const text = toRaw(myQuillEditor.value).getHTML();
  emit('updateValue', text);
};
const handleUpload = (e: any) => {
  const files = Array.prototype.slice.call(e.target.files);

  if (!files) {
    return;
  }
  const formdata = new FormData();
  formdata.append('file', files[0]);
  uploadImgApi(formdata) // 此处使用服务端提供上传接口
    .then((res) => {
      if (res.data.largeLink) {
        const quill = toRaw(myQuillEditor.value).getQuill();
        const length = quill.getSelection().index;
        // 插入图片,res为服务器返回的图片链接地址
        quill.insertEmbed(length, 'image', res.data.largeLink);
        // 调整光标到最后
        quill.setSelection(length + 1);
      }
    });
};
// 初始化编辑器
onMounted(() => {
  const quill = toRaw(myQuillEditor.value).getQuill();
  if (myQuillEditor.value) {
    quill.getModule('toolbar').addHandler('image', imgHandler);
  }
  toRaw(myQuillEditor.value).setHTML(props.value);
});
</script>
<style scoped lang="scss">
// 调整样式
:deep(.ql-editor) {
  min-height: 180px;
}
:deep(.ql-formats) {
  height: 21px;
  line-height: 21px;
}
</style>

注意:由于我需要回显的功能,所以对watch进行了赋值,watch监听只执行一次

转载自:https://juejin.cn/post/7416235834918109194
评论
请登录