Tinymce - 宇宙第一富文字編輯器?[2]

嘉禾生2310發表於2019-09-19

圖片

tinymce 提供了豐富的圖片上傳、管理、修改功能。只需要配置對應外掛及一些引數即可。

圖片上傳

引用image外掛,新增images_upload_handler配置項,圖片彈窗左側導航會多一個上傳的選項,在點選之後即可完成我們常用的圖片上傳功能

<template>
	<div class="default-tinymce">
		<textarea id="editor"></textarea>
	</div>
</template>
<script>
import Tinymce from 'tinymce'
import Upload from 'src/utils/upload'

export default {
  name: 'DefaultTinymce',
  mounted () {
    this.upload = new Upload()
    let self = this
    Tinymce.init({
      selector: '#editor',
      plugins: 'image',
      async images_upload_handler (blobInfo, success, fail) {
          const file = blobInfo.blob()
          try {
            const url = self.YB.businessURL(await self.upload.post(file))
            success(url)
          } catch (e) {
            fail(e.message || '上傳失敗,請重試')
          }
      }
    })
  }
}
</script>

複製程式碼

圖片管理

tinymce提供了類似個人相簿和公共相簿的功能。使用起來也很方便,只要配置即可。

這個我們暫時沒有這個需求,以後可能會用到。

一種是下拉選擇,配置image_list即可,值得注意的是,它的值可以是陣列,函式,或者介面。

tinymce.init({
  selector: "textarea",  // change this value according to your HTML
  plugins: "image",
  image_list: [
    {title: 'Dog', value: 'mydog.jpg'},
    {title: 'Cat', value: 'mycat.gif'}
  ]
});

tinymce.init({
  selector: "textarea",  // change this value according to your HTML
  plugins: "image",
  image_list: "/mylist.php"
});

tinymce.init({
  selector: "textarea",  // change this value according to your HTML
  plugins: "image",
  image_list: function(success) {
    success([
      {title: 'Dog', value: 'mydog.jpg'},
      {title: 'Cat', value: 'mycat.gif'}
    ]);
  }
});
複製程式碼

另一種是提供了一個按鈕,點選這個按鈕會觸發相應的方法file_picker_callback,方法的引數中有回撥函式,我們可以利用這個方法做一個個人相簿。

tinymce.init({
  selector: 'textarea',  // change this value according to your HTML
  file_picker_callback: function(callback, value, meta) {
    // 以下是虛擬碼
    // 開啟一個彈窗
    openDialog()  
    // 展示圖片列表
    showImageList()
    // 選擇圖片,確認選擇
    const url = submitActiveImage()
    // 關閉彈窗,返回圖片
    callback(url)
  }
});
複製程式碼

圖片編輯

圖片編輯分為兩部分,

一部分是對圖片屬性的編輯,比如邊框、邊距、寬度等,這部分其實就是對外掛image的配置;

一部分是對圖片自身的編輯,比如剪裁、調色等。這部分是基於外掛image Tools的配置

image

image_caption
  • 預設值 false
  • 可能值 [true/false]

圖片下方增加對圖片的描述文字。開啟後,會在彈窗皮膚多一個顯示標題的可選項。

config = {
  image_caption: true
}
複製程式碼

這會生成新的結構


<figure class="image" contenteditable="false" data-mce-selected="1">
    <img src="https://imgtest.120yibao.com/test/base/obu1oj9a5fhy4ftku49.jpeg" alt="sdf" width="650" height="639">
    <figcaption contenteditable="true">圖片底部</figcaption>
</figure>
複製程式碼
image_class_list
  • 值型別 string

可以下拉選擇給圖片加對應class


config = {
  image_class_list: [
    { title: '無', value: '' },
    { title: '自適應螢幕寬度', value: 'adaptive-screen-width' }
  ]
}

複製程式碼
image_advtab
  • 預設值 false
  • 可能值 [true/false]

圖示編輯彈窗多了一個屬性編輯的tab,包含外邊距(margin),邊框(border)的編輯。修改後,圖片的style的屬性值會被覆蓋。

image_description
  • 預設值 true
  • 可能值 [true/false]

對應img標籤的alt屬性

image_dimensions
  • 預設值 true
  • 可能值 [true/false]

是否可以在彈窗中通過input設定圖片的寬高

image Tools

此外掛會編輯圖片的原始資料。

編輯後自動上傳

涉及修改圖片原始資料的操作(如剪裁、翻轉等),需要重新上傳圖片。可以配合服務端介面重新上傳,也可以前端間接呼叫images_upload_handler方法進行上傳。

我們看一下前端上傳


function handleImageUrlToBlob (url, callback) {
   function imageToCanvas (src, cb) {
     const canvas = document.createElement('canvas')
     const ctx = canvas.getContext('2d')
     const img = new Image()
     img.src = src + '?t=2'
     img.crossOrigin = ''
     img.onload = function () {
       canvas.width = img.width
       canvas.height = img.height
       ctx.drawImage(img, 0, 0)
       cb(canvas)
     }
   }

   function dataURLToBlob (dataURL) {
     const arr = dataURL.split(',')
     const mime = arr[0].match(/:(.*?);/)[1]
     const bStr = atob(arr[1])
     let n = bStr.length
     const u8arr = new Uint8Array(n)
     while (n--) {
       u8arr[n] = bStr.charCodeAt(n)
     }
     return new Blob([u8arr], { type: mime })
   }

   function canvasToDataURL (canvas, format, quality) {
     return canvas.toDataURL(format || 'image/jpeg', quality || 1.0)
   }

   imageToCanvas(url, function (canvas) {
     callback(dataURLToBlob(canvasToDataURL(canvas)))
   })
 }

tinymce.init({
  // 此兩項在使用imagetools外掛時必填,只在使用服務端上傳時有用。但前端使用了其他方法獲取圖片(imagetools_fetch_image),所以隨便寫了些值
  imagetools_cors_hosts: [],
  imagetools_proxy: 'just a string and do nothing',
  // 修改圖片之後,獲取圖片
  imagetools_fetch_image: image => {
    return new tinymce.util.Promise(function (resolve) {
      // 需要將圖片轉為blob格式
      handleImageUrlToBlob(image.src, resolve)
    })
  },
})

複製程式碼

修改完圖片之後,編輯器會在圖片失去焦點之後自動上傳圖片,並生成新的URL替換掉之前的。

編輯功能列表配置

imagetools_toolbar可配置編輯功能列表

tinymce.init({
  selector: "textarea",  
  toolbar: "image",
  plugins: "image imagetools",
  imagetools_toolbar: "rotateleft rotateright | flipv fliph | editimage imageoptions"
});
複製程式碼

格式化

有時候你需要對內容進行一些格式化,例如改變字型大小、將內容轉換為標題,或者直接清除選擇內容的所有樣式。這不需要引入外掛,至於要配置即可。

tinymce.init({
  selector: "textarea",  
  /**
   * 「塊」樣式格式化
   */
  block_formats: '段落=p; 標題 1=h1; 標題 2=h2; 標題 3=h3; 標題 4=h4; 標題 5=h5; 標題 6=h6;',

  /**
   * 工具欄「段落」下拉元件的預設值
   */
  style_formats: [
      { title: 'Headings',
        items: [
          { title: 'Heading 1', format: 'h1' },
          { title: 'Heading 2', format: 'h2' },
          { title: 'Heading 3', format: 'h3' },
          { title: 'Heading 4', format: 'h4' },
          { title: 'Heading 5', format: 'h5' },
          { title: 'Heading 6', format: 'h6' }
        ] },
      { title: 'Inline',
        items: [
          { title: 'Bold', format: 'bold' },
          { title: 'Italic', format: 'italic' },
          { title: 'Underline', format: 'underline' },
          { title: 'Strikethrough', format: 'strikethrough' },
          { title: 'Superscript', format: 'superscript' },
          { title: 'Subscript', format: 'subscript' },
          { title: 'Code', format: 'code' }
        ] },
      { title: 'Blocks',
        items: [
          { title: 'Paragraph', format: 'p' },
          { title: 'Blockquote', format: 'blockquote' },
          { title: 'Div', format: 'div' },
          { title: 'Pre', format: 'pre' }
        ] },
      { title: 'Align',
        items: [
          { title: 'Left', format: 'alignleft' },
          { title: 'Center', format: 'aligncenter' },
          { title: 'Right', format: 'alignright' },
          { title: 'Justify', format: 'alignjustify' }
        ] }
    ],
  /**
   * 字型大小可選列表
   */
  fontsize_formats: '12px 14px 16px 18px 24px 36px 48px',
  /**
   * 格式化
   */
  formats: {
    // 清除格式
    removeformat: [
      {
        selector: 'b,strong,em,i,font,u,strike,sub,sup,dfn,code,samp,kbd,var,cite,mark,q,del,ins',
        remove: 'all',
        split: true,
        block_expand: true,
        expand: false,
        deep: true
      },
      {
        selector: 'span',
        attributes: ['style', 'class'],
        remove: 'empty',
        split: true,
        expand: false,
        deep: true },
      {
        selector: '*',
        attributes: ['style', 'class'],
        split: false,
        expand: false,
        deep: true
      }
    ]
  }
});
複製程式碼

轉換外鏈圖片

如果貼上過來的內容中有圖片,並且圖片是外鏈圖片。可能需要把這些外鏈圖片轉換成自己伺服器的地址,這需要後端配合:給後端這些圖片的url,後端返回給你自己的url,由後端完成圖片上傳。

如果貼上內容中的圖片是base64,會自動觸發之前配置的images_upload_handler圖片上傳鉤子,自動上傳圖片。

這一塊的實現比較複雜,我不貼程式碼,只說下大致思路:

  1. paste_postprocess方法中標註這些外鏈圖片
  2. 然後監聽編輯器的內容插入事件(mceInsertContent),在事件的回撥方法中,將這些標註的外鏈圖片進行轉化。

需要注意的是,因為一些圖片的提供者會對圖片進行保護,伺服器不一定你傳過去的每張圖片都能傳到自己的伺服器,需要和後端約定處理方法。

封裝成Vue元件

首先,我們要實現Vue的雙向繫結:在編輯器內容改變的時候,更新ViewModel;ViewModel改變的時候,更新編輯器的內容。

實現起來很簡單,我們可以監聽可以改變編輯器內容的事情,以及使用vm.$watch監聽值的變化。

<template>
	<div class="editor-wrap">
		<textarea v-model="value" class="editor-textarea"></textarea>
	</div>
</template>
<script>
export default {
  name: 'TinymceEditor',
  model: {
    prop: 'value',
    event: 'change'
  },
  props: {
    value: {
      type: String,
      required: true,
      default: ''
    }
  },
  async mounted () {
    try {
      const self = this
      const editor = await Tinymce.init({
          selector: '.editor-textarea',
          // 編輯器例項初始化完成的回撥
          init_instance_callback: editor => {
              // 資料雙向繫結
              self.$nextTick(() => {
                  let currentContent = ''
                  // 雙向繫結資料
                  self.$watch('value', (val, prevVal) => {
                    if (editor && typeof val === 'string' && val !== currentContent && val !== prevVal) {
                      editor.setContent(val)
                      currentContent = val
                    }
                  })
                  editor.on('change keyup undo redo', () => {
                    currentContent = editor.getContent()
                    self.$emit('change', currentContent)
                  })
              })
          }
        })
        this.editor = editor[0]
    } catch (e) {
      this.$error(e)
    }
  }
}
</script>

複製程式碼

下一篇:Tinymce - 宇宙第一富文字編輯器?[3]

參考

  1. tinymce官方文件

相關文章