How to use vue-quill-editor in Nuxt

Nuxt
vue-quill-editor
Quill

為了後續方便文章編輯與製作,在admin下實作了文章編輯器


當前文章編輯器的package多來自於對Quill的修改


vue-quill-editor亦同,因此許多function的客製化修改可參考Quill的API文件說明


安裝

可參見vue-quill-editor Github的指引,沒啥問題

npm install vue-quill-editor --save
# or
yarn add vue-quill-editor


與Nuxt整合

在/plugins下創建一個vue-quill-editor.js (名稱可自訂)

// in ~/plugins/vue-quill-editor.js

import Vue from 'vue'
import VueQuillEditor from 'vue-quill-editor'

Vue.use(VueQuillEditor)


修改nuxt.config.js 的plugins,添加此js file

plugins: [
    { src: '~plugins/vue-quill-editor.js', ssr: false },
  ],

ssr: false 避免雜七雜八的套件問題,反正文本編輯器不需要SEO優化Ovo


創建Component

這部分可獨立建一個客製化component(建議),或直接寫在page file內(不建議,很亂)


~/components/ui/AppEditor.vue

<template>
  <section>
    <client-only>
      <quill-editor
        ref="editor"
        v-model.lazy="editedContent"
        :options="editorOption"
        class="editor--border relative"
        @change="debounceTextChange"
      />
    </client-only>
  </section>
</template>

ref="editor"設定在之後客製化圖片上傳功能會用到

v-model.lazy避免打字時過快同步而lag

:option很重要,所有文本編輯器的功能都寫在這一項

@change="debounceTextChange" 很重要,用來將變動後的文本內容emit至parent component


接著是<script>

(這裡import 'vue-debounce'將於另外一篇文章說明)

<script>
import { debounce } from 'vue-debounce'
export default {
  // 如果是編輯文章,則會從parent收到文章當前的content
  props: {
    content: {
      type: String,
      default: () => '',
    },
  },
  data() {
    return {
      editedContent: this.content,
      // 所有文本編輯器功能設定均寫在editorOption
      editorOption: {
        theme: 'snow', // 可換
        modules: {
          toolbar: {
            // container這裡是個大坑,[]表分群
            container: [
              ['bold', 'italic', 'underline', 'strike', 'code'],
              ['blockquote', 'code-block'],
              [{ header: [1, 2, 3, 4, 5, 6, false] }],
              [{ list: 'ordered' }, { list: 'bullet' }],
              [{ script: 'sub' }, { script: 'super' }],
              [{ indent: '-1' }, { indent: '+1' }],
              [{ size: ['small', false, 'large', 'huge'] }],
              [{ color: [] }, { background: [] }],
              [{ align: [] }],
              ['clean'],
              ['link', 'image', 'video'],
            ],
            // 客製化圖片上傳功能用的
            handlers: {
              image: this.uploadImage,
            },
          },
        },
      },
    }
  },
  methods: {
    debounceTextChange: debounce(function () {
      //don't use arrow function
      this.$emit('text-change', this.editedContent)
    }, 3000),

    // 後面再補上uploadImage說明如何客製化圖片上傳功能
  },
}
</script>


上述container所設定出來的成果如圖


這邊我只選了我會用到的功能項目,可自行增減


設定全域CSS

在nuxt.config.js內設定snow主題的css (第2項)

css: [
  '~assets/css/main.scss',
  'quill/dist/quill.snow.css',
],


客製化圖片上傳功能

若沒特別需求,前述已完成最基本的editor component建置


quill editor預設圖片上傳為base64形式


為了使DOM更簡潔以及管理上傳的圖片


我選擇以firebase作為上傳儲存的database,客製化function uploadImage()如下

uploadImage() {
  if (!process.client) {
    return
  }
  let fileInput = this.$el.querySelector('input.ql-image[type=file]')

  if (fileInput == null) {
    fileInput = document.createElement('input')
    fileInput.setAttribute('type', 'file')
    fileInput.setAttribute(
      'accept',
      'image/png, image/gif, image/jpeg, image/bmp, image/x-icon'
    )
    fileInput.classList.add('ql-image')
    fileInput.addEventListener('change', () => {
      if (fileInput.files != null && fileInput.files[0] != null) {
        // customize image upload function
        const file = fileInput.files[0]
        const fileName = file.name.split('.').slice(0, -1).join('.') //without .png .jpg .svg
        // 使用nuxt firebase module,圖儲存在store,URL儲存在Realtime Database
        this.$fire.storage
          .ref('images/' + file.name)
          .put(file)
          .then((response) => {
            console.log('===image upload succeeded===')
            response.ref.getDownloadURL().then((downloadURL) => {
              this.$fire.database
                .ref('images')
                .child(fileName)
                .update({ imageUrl: downloadURL })
                .then(() => {
                  console.log('===imageUrl stored in RTDB successfully===')
                  // Quill API提供之當前編輯器內選取的範圍,用來插入連結
                  let range = this.$refs.editor.quill.getSelection()
                  this.$refs.editor.quill.insertEmbed(
                    range.index,
                    'image',
                    downloadURL
                  )
                })
                .catch((err) => console.log(err))
            })
          })
          .catch((err) => console.log(err))
      }
    })
    let container = this.$el.querySelector('.ql-toolbar.ql-snow')
    container.appendChild(fileInput)
  }
  fileInput.click()
},



BONUS: Fix editor tool bar

預設tool bar在文本過長產生scrollbar時,並不會固定於視窗上方


這會使得編輯起來很沒效率


欲固定之,需使用到CSS deep selector


程式碼如下/deep/ .ql-toolbar

(順便加上邊框 .editor--border)

<style lang="scss" scoped>
.quill-editor {
  min-height: 300px;
  max-height: 70vh;
  overflow-y: auto;
  /deep/ .ql-toolbar {
    position: sticky;
    top: 0;
    left: 0;
    background-color: #fff;
    z-index: 10;
  }
  /deep/ .ql-editor {
    min-height: 300px;
    font-family: Roboto;
    font-size: 1rem;
  }
}
.editor--border {
  border: 1px solid #ccc;
}
</style>


完成!👏👏👏

© 2021 Hamsterism. All rights reserved github