SoFunction
Updated on 2025-04-04

Implement image drag and drop and paste functions based on vue-simplemde

The project uses a vue framework and needs a markdown editing box. I looked for it on npm and found that simplemde is pretty good. Since I am lazy, I searched it again on npm and found itvue-simplemdeThis package, then start using it.

But thisvue-simplemdeIt does not support image drag-and-drop uploading, pasting and uploading, and it cannot be said that it is because of this vue-simplemde, because vue-simplemde is only encapsulated into a Vue plug-in based on simplemde. So in the end, it is because simplemde does not provide relevant functions, but for user experience, this function is necessary unless the markdown editor is not used. If you use a rich text editor, a lot of the code of the project will have to be changed. So I checked the article online and looked up some code on github. The analysis will be performed below

Drag and drag

The core of the drag API is drop event, which is the event name that triggers when we drag a file from the desktop to the browser and release it.

We all know that if you drag an image into the browser, you will open the image directly. This is because the browser defaults to the browser when you drag a file into the browser, the file will be opened, so we need to block native operations.

Let's write a piece of code first to block the default event

("drop", e => {
 e = e || event
 if ( === 'CodeMirror-scroll') { // If you enter the editor, the default event will be blocked ()
 }
}, false)

CodeMirror-scroll The Class is the Class name of the simplemde edit box.

Now we drag the file to this edit box and then release it without any reaction. If it is outside the edit box, the default event will continue to be triggered.

The following is to get the simplemde method and give it the drop event handling method.

// Assume that the page has three editing windows in total, so you need to listen to events in a loop[ this.$refs.simplemde1,
 this.$refs.simplemde2,
 this.$refs.simplemde3
].map(({simplemde}) => {
 ('drop', (editor, e) => {
 if (!( && )) {
  // Pop-up window states that this browser does not support this operation  return
 }

 let dataList = 
 let imageFiles = [] // Array of file instances to upload
 // Loop because several image files may be dragged at the same time for (let i = 0; i < ; i++) {
 // If it is not a picture, pop-up warning Only dragging and dropping image files is supported.  if (dataList[i].('image') === -1) {
  // The following continue function is that if the user drags 2 pictures and one document at the same time, the document will not be uploaded and the picture will be uploaded as usual.  continue
  }
  (dataList[i]) // First push the current file into the array, and upload it uniformly after the for loop is completed. }
 // uploadImagesFile method is the method of uploading images // It is used to distinguish which edit box the current image upload is in. (, imageFiles)
 // Because the following code is already available, the above blocking default event code is not necessary to be written ()
 })
})

It seems that there is a lot of code, because of the comments. The following is the code without comments. You can have your own insights and understanding based on the following code:

[ this.$refs.simplemde1,
 this.$refs.simplemde2,
 this.$refs.simplemde3
].map(({simplemde}) => {
 ('drop', (editor, e) => {
 if (!( && )) {
  return
 }
 let dataList = 
 let imageFiles = []
 for (let i = 0; i < ; i++) {
  if (dataList[i].('image') === -1) {
  continue
  }
  (dataList[i])
 }
 (, imageFiles)
 ()
 })
})

Paste

The pasted API is the paste method. This is not like the above. Pasting does not require the default event to be disabled, because we can see that when you copy an image and press ctrl+v in the browser, no changes will occur, so there is no need to prohibit the default event.

Here is the code:

('paste', (editor, e) =&gt; { // Trigger function for pasting the picture if (!( &amp;&amp; )) {
 // Pop-up window states that this browser does not support this operation return
 }
 try {
 let dataList = 
 if (dataList[0].kind === 'file' &amp;&amp; dataList[0].getAsFile().('image') !== -1) {
  (, [dataList[0].getAsFile()])
 }
 } catch (e) {
 // Pop-up window instructions, only pictures can be pasted }
})

The reason why I wrote the try...catch method here is because if you paste it, if it is a file, items will be empty, and in the if loop below, use dataList[0].kind. That is [0].kind . When the item is empty and you still access a non-existing kind attribute, an error will be reported. So here we need to use the try...catch method to make judgments.

dataList[0].getAsFile().('image') !== -1This sentence is a judgment, and the pasted thing confirms that it is a picture, not something else.

The difference between uploading images in if is [dataList[0].getAsFile()], because in order to unify the format and facilitate the processing of the uploadImagesFile function, I added [] to make it an array. dataList[0].getAsFile() is to get the file instance.

Upload

Uploading is a little troublesome:

uploadImagesFile (simplemde, files) {
 // wrap each file instance using FormData, and then return an array let params = (file =&gt; {
 let param = new FormData()
 ('file', file, )
 return param
 })

 let makeRequest = params =&gt; {
 return this.$('/Api/upload', params)
 }
 let requests = (makeRequest)

 this.$ = callback =&gt; {
 return arr =&gt; {
  return (null, arr)
 }
 }

 // The format returned by the server is {state: Boolean, data: String} // When state is false, data is the error message returned // When state is true, data is the URL address after the image is uploaded, and this address is the absolute path for the website.  as follows: // /static/upload/
 (requests)
 .then(this.$((...resps) =&gt; {
  for (let i = 0; i &lt; ; i++) {
  let {state, data} = resps[i].data
  if (!state) {
   // Pop-up window displays data error message   continue
  }
  let url = `![](${ + data})` // Splicing into markdown syntax  let content = ()
  (content + url + '\n') //Stitch with the content before the edit box  }
 }))
}

Because I encapsulate axiox into a vue plugin to use it, this will cause this.$http to be instantiated, not itself. The solution the axios maintainer said is to reintroduce the axios package to use. But I don't think it's necessary. Inside is . There is less implementation code, just take it over and reassign it to axios

So there is a code above

(requests)
 .then(this.$((...resps) => {
 // code
 })

Translate this code

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

For this question, please see the official explanation:axios-all-is-not-a-function-inside-vue-component . You can also look at the code of axios:#L45-L48

I won’t go into this issue in depth for the time being, let’s go back to the topic just now.

I mentioned above that when state is true, data is the absolute path of the file relative to the website, such as: /static/upload/

If we need to splice it, so there is![](${ + data}) This code is spliced. The last two lines are to get the previous content, and then append the URL address.