這次帶給大家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('image') === -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('image') === -1) { continue } imageFiles.push(dataList[i]) } this.uploadImagesFile(simplemde.codemirror, imageFiles) e.preventDefault() }) })
貼上
貼上的API是paste 方法,這個不像上面一樣,貼上不需要禁止預設事件,因為我們可以看到,你複製一個圖片,到瀏覽器裡按下ctrl v 的時候,是不會發生任何變化的,所以沒用必要禁止預設事件。
下面是程式碼:
simplemde.codemirror.on('paste', (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 + '\n') // 和编辑框之前的内容进行拼接 } })) }
因為我是把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中文網其它相關文章!
推薦閱讀:
#以上是vue-simplemde實作圖片拖曳貼上功能(附程式碼)的詳細內容。更多資訊請關注PHP中文網其他相關文章!