首頁  >  文章  >  web前端  >  vue-simplemde實作圖片拖曳貼上功能(附程式碼)

vue-simplemde實作圖片拖曳貼上功能(附程式碼)

php中世界最好的语言
php中世界最好的语言原創
2018-04-28 13:59:502683瀏覽

這次帶給大家vue-simplemde實現圖片拖曳貼上功能(附程式碼),vue-simplemde實現圖片拖曳貼上功能的注意事項有哪些,以下就是實戰案例,一起來看一下。

專案使用的是vue框架,需要一個markdown的編輯框,就在npm上找了一下,發現simplemde挺不錯的,由於我比較懶,就順便在npm又搜了一下,找到了vue-simplemde這個package ,那就開始使用它吧。

但是這個vue-simplemde 不支援圖片拖曳上傳、貼上上傳,也不能說是因為這個vue-simplemde ,因為vue-simplemde 只是對simplemde 的基礎上封裝成一個Vue插件。所以最後還是因為 simplemde 沒有提供相關的功能,但為了使用者體驗考慮,這個功能時必要的,除非不使用markdown編輯器。而去使用富文本編輯器,那樣的話,項目很多的程式碼都要改變。所以就上網查了文章,及在github上查了一些程式碼。以下將進行分析

拖曳

拖曳的API核心是drop 這個事件,就是當我們從桌面拖曳一個檔案到瀏覽器裡時,放開的時候,而觸發的事件名稱。

我們都知道,你隨便拖動一個圖片到瀏覽器裡,會直接打開這個圖片,這是因為瀏覽器默認你拖動文件到瀏覽器裡時,將打開這個文件,所以,我們需要阻止原生的操作。

我們現在先寫一段程式碼,讓其屏蔽掉預設事件

window.addEventListener("drop", e => {
 e = e || event
 if (e.target.className === 'CodeMirror-scroll') { // 如果进入到编辑器的话,将阻止默认事件
 e.preventDefault()
 }
}, false)

CodeMirror-scroll 這個Class就是 simplemde 編輯框的Class名稱。

現在我們拖曳文件到這個編輯框,然後鬆掉,不會有任何反應。如果在編輯框之外的地方,還是會繼續觸發預設事件。

下面就是要取得 simplemde 方法,給他 drop 事件處理方法。

// 假设页面一共有三个编辑窗口,所以需要循环监听事件
[ this.$refs.simplemde1,
 this.$refs.simplemde2,
 this.$refs.simplemde3
].map(({simplemde}) => {
 simplemde.codemirror.on('drop', (editor, e) => {
 if (!(e.dataTransfer && e.dataTransfer.files)) {
  // 弹窗说明,此浏览器不支持此操作
  return
 }
 let dataList = e.dataTransfer.files
 let imageFiles = [] // 要上传的文件实例数组
 // 循环,是因为可能会同时拖动几个图片文件
 for (let i = 0; i < dataList.length; i++) {
 // 如果不是图片,则弹窗警告 仅支持拖拽图片文件
  if (dataList[i].type.indexOf(&#39;image&#39;) === -1) {
  // 下面的continue,作用是,如果用户同时拖动2个图片和一个文档,那么文档不给于上传,图片照常上传。
  continue
  }
  imageFiles.push(dataList[i]) // 先把当前的文件push进数组里,等for循环结束之后,统一上传。
 }
 // uploadImagesFile方法是上传图片的方法
 // simplemde.codemirror的作用是用于区分当前的图片上传是处于哪个编辑框
 this.uploadImagesFile(simplemde.codemirror, imageFiles)
 // 因为已经有了下面这段代码,所以上面的屏蔽默认事件代码就不用写了
 e.preventDefault()
 })
})

詐一看,程式碼好像有點多,那是因為註解的原因,以下是沒有註解的程式碼。你可以根據下面的程式碼,有自己的見解和理解:

[ this.$refs.simplemde1,
 this.$refs.simplemde2,
 this.$refs.simplemde3
].map(({simplemde}) => {
 simplemde.codemirror.on('drop', (editor, e) => {
 if (!(e.dataTransfer && e.dataTransfer.files)) {
  return
 }
 let dataList = e.dataTransfer.files
 let imageFiles = []
 for (let i = 0; i < dataList.length; i++) {
  if (dataList[i].type.indexOf(&#39;image&#39;) === -1) {
  continue
  }
  imageFiles.push(dataList[i])
 }
 this.uploadImagesFile(simplemde.codemirror, imageFiles)
 e.preventDefault()
 })
})

貼上

貼上的API是paste 方法,這個不像上面一樣,貼上不需要禁止預設事件,因為我們可以看到,你複製一個圖片,到瀏覽器裡按下ctrl v 的時候,是不會發生任何變化的,所以沒用必要禁止預設事件。

下面是程式碼:

simplemde.codemirror.on(&#39;paste&#39;, (editor, e) => { // 粘贴图片的触发函数
 if (!(e.clipboardData && e.clipboardData.items)) {
 // 弹窗说明,此浏览器不支持此操作
 return
 }
 try {
 let dataList = e.clipboardData.items
 if (dataList[0].kind === 'file' && dataList[0].getAsFile().type.indexOf('image') !== -1) {
  this.uploadImagesFile(simplemde.codemirror, [dataList[0].getAsFile()])
 }
 } catch (e) {
 // 弹窗说明,只能粘贴图片
 }
})

之所以這裡寫上try...catch 方法,是因為如果你貼上的時候,如果是一個文件, items 將是空的,而在下面的if循環裡,使用dataList[0].kind 。也就是 e.clipboardData.items[0].kind 。當 item 為空時,也去存取一個不存的 kind 屬性時,就會報錯了。所以這裡需要使用 try...catch 方法來判斷。

dataList[0].getAsFile().type.indexOf('image') !== -1 這句話是判斷,貼上的東西確認是圖片,而不是其他東西。

if 裡的上傳圖片,不一樣的地方是[dataList[0].getAsFile()] ,因為為了統一格式,方便uploadImagesFile 函數進行處理,我加上了[] ,使之成為數組。 dataList[0].getAsFile() 就是取得檔案實例了。

上傳

已上傳就有一點麻煩了:

uploadImagesFile (simplemde, files) {
 // 把每个文件实例使用FormData进行包装一下,然后返回一个数组
 let params = files.map(file => {
 let param = new FormData()
 param.append('file', file, file.name)
 return param
 })
 let makeRequest = params => {
 return this.$http.post('/Api/upload', params)
 }
 let requests = params.map(makeRequest)
 this.$http.spread = callback => {
 return arr => {
  return callback.apply(null, arr)
 }
 }
 // 服务端返回的格式是{state: Boolean, data: String}
 // state为false时,data就是返回的错误信息
 // state为true时,data是图片上传后url地址,这个地址是针对网站的绝对路径。如下:
 // /static/upload/2cfd6a50-3d30-11e8-b351-0d25ce9162a3.png
 Promise.all(requests)
 .then(this.$http.spread((...resps) => {
  for (let i = 0; i < resps.length; i++) {
  let {state, data} = resps[i].data
  if (!state) {
   // 弹窗显示data的错误信息
   continue
  }
  let url = `![](${location.origin + data})` // 拼接成markdown语法
  let content = simplemde.getValue()
  simplemde.setValue(content + url + &#39;\n&#39;) // 和编辑框之前的内容进行拼接
  }
 }))
}

因為我是把axiox 封裝成vue外掛程式來使用,這樣會導致, this.$http 是實例化後的,而不是他本身。 axios 維護者說的解決方案是,重新引入 axios 包,來使用。但是我覺得沒有必要。 axios.all 內部是 Promise.all 。 axios.spread 實作程式碼比較少,就直接拿過來,重新賦值給axios 就好了

#所以上面有段碼是

Promise.all(requests)
 .then(this.$http.spread((...resps) => {
 // code
 })

把這段程式碼翻譯一下就是

axios.all(requests)
 .then(axios.spread((...resps) => {
 // code
 })

關於這個問題,請看下官方的解釋:axios-all-is-not-a-function-inside-vue-component 。也可以看下 axios 的程式碼: axios.js#L45-L48

這個問題,暫時就不深了,我們回到剛剛的話題。

上面我說到當state為true時,data是檔案相對於網站的絕對路徑,如: /static/upload/2cfd6a50-3d30-11e8-b351-0d25ce9162a3.png

#

相信看了本文案例你已經掌握了方法,更多精彩請關注php中文網其它相關文章!

推薦閱讀:

jQuery編碼轉換base64透過AJAX上傳

vue元件寫法規格

#

以上是vue-simplemde實作圖片拖曳貼上功能(附程式碼)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn