Skip to content

Instantly share code, notes, and snippets.

@acidjazz
Created August 29, 2021 07:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save acidjazz/ae48c2bd5f9ac60ee677d11b3564b141 to your computer and use it in GitHub Desktop.
Save acidjazz/ae48c2bd5f9ac60ee677d11b3564b141 to your computer and use it in GitHub Desktop.
image upload
<template>
<div class="w-64 h-52 flex justify-center items-center border-2 border-gray-300 border-dashed rounded-md">
<div class="space-y-1 text-center">
<icon-images class="mx-auto w-12 h-12" />
<div class="flex text-sm text-gray-600">
<label for="file-upload" class="relative cursor-pointer bg-white rounded-md font-medium text-fuchsia-600 hover:text-indigo-500 focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-indigo-500">
<span>Upload an image</span>
<input
id="file-upload"
name="file-upload"
type="file"
class="sr-only"
accept="image/png, image/jpeg, image/gif"
@change="change"
>
</label>
<p class="pl-1">or drag and drop</p>
</div>
<p class="text-xs text-gray-500">
PNG, JPG, GIF up to 10MB
</p>
<progress-indicator
v-for="file in files"
:key="file.name"
:indeterminate="file.progress === 100"
:progress="file.progress"
/>
</div>
</div>
</template>
<script lang="ts">
import Vue from 'vue'
import { PropType } from '@nuxtjs/composition-api'
import ProgressIndicator from '@/components/form/ProgressIndicator.vue'
export interface UploadFile extends File {
// Upload completion progress
progress: Number,
// Mark complete when axios returns
complete: boolean,
webkitRelativePath?: string
}
export type UploadFiles = Array<UploadFile>
export default Vue.extend({
components: { ProgressIndicator },
props: {
type: String as PropType<'menu'>,
id: String,
},
data () {
return {
files: [] as UploadFiles,
uploaded: [] as Array<number>,
}
},
methods: {
async change (event: Event & { target: EventTarget & { files: FileList } }): Promise<void> {
this.files = Object.values(event.target.files).filter(f => f.name !== '.DS_Store') as UploadFiles
await this.uploadAll()
},
async uploadAll (): Promise<void> {
for (const [index, file] of this.files.entries())
await this.upload(file, index)
},
upload (file: UploadFile, index: number): Promise<void>|undefined {
if (file.size === 0) {
this.$toast.danger('File has zero bytes')
this.cancel()
return
}
const data = new FormData()
data.append('file', file)
data.append('type', this.type)
data.append('id', this.id)
const config = {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
onUploadProgress: (event: ProgressEvent) => this.progress(event, file, index),
}
this.$axios.post('/image', data, config)
.then((result) => {
this.$toast.show(result.data.data)
this.files.forEach((f, i) => {
if (f.name === result.data.data.data.name) {
f.complete = true
this.$set(this.files, i, f)
this.uploaded.push(result.data.data.data.id as number)
}
})
// if all are marked complete, we are done with the upload
if (this.files.filter(f => !f.complete).length === 0) {
this.files = [] as UploadFiles
this.uploaded = []
this.$emit('complete')
}
}).catch((error) => {
console.log('error', error)
})
},
cancel (): void {
this.files = []
this.$emit('complete')
},
progress (event: ProgressEvent, file: UploadFile, index: number): void {
file.progress = Math.round(event.loaded * 100 / event.total)
this.$set(this.files, index, file)
},
},
})
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment