upload selector improvements

This commit is contained in:
bkfox
2024-03-17 21:00:07 +01:00
parent de858f45e8
commit 024db5f307
9 changed files with 226 additions and 77 deletions

View File

@@ -1,5 +1,5 @@
<template>
<component :is="tag" @click="call" :class="buttonClass">
<component :is="tag" @click.capture.stop="call" type="button" :class="buttonClass">
<span v-if="promise && runIcon">
<i :class="runIcon"></i>
</span>
@@ -27,6 +27,8 @@ export default {
data: Object,
//! Action method, by default, `POST`
method: { type: String, default: 'POST'},
//! If provided open confirmation box before proceeding
confirm: { type: String, default: ''},
//! Action url
url: String,
//! Extra request options
@@ -60,6 +62,9 @@ export default {
call() {
if(this.promise || !this.url)
return
if(this.confirm && !confirm(this.confirm))
return
const options = Model.getOptions({
...this.fetchOptions,
method: this.method,

View File

@@ -1,27 +1,29 @@
<template>
<div class="a-select-file">
<div :class="['a-select-file-list', listClass]" ref="list">
<div class="flex-column">
<div ref="list" :class="['a-select-file-list', listClass]">
<!-- upload -->
<form ref="uploadForm" class="flex-column" v-if="state == STATE.DEFAULT">
<div class="field flex-grow-1" v-if="!uploadFile">
<label class="label">{{ uploadLabel }}</label>
<input type="file" @change="previewFile"/>
<input type="file" ref="uploadFile" :name="uploadFieldName" @change="onSubmit"/>
</div>
<slot name="upload-preview" :item="uploadFile"></slot>
<div v-if="uploadFile">
<button class="button secondary" @click="removeUpload">
<span class="icon">
<i class="fa fa-trash"></i>
<div class="flex-grow-1">
<slot name="upload-form"></slot>
</div>
</form>
<div class="flex-column" v-else>
<slot name="upload-preview" :upload="upload"></slot>
<div class="flex-row">
<progress :max="upload.total" :value="upload.loaded"/>
<button type="button" class="button small square ml-2" @click="uploadAbort">
<span class="icon small">
<i class="fa fa-close"></i>
</span>
</button>
<button class="button float-right" @click="doUpload">
<span class="icon">
<i class="fa fa-upload"></i>
</span>
Upload
</button>
</div>
</div>
<!-- tiles -->
<div v-if="prevUrl">
<a href="#" @click="load(prevUrl)">
{{ prevLabel }}
@@ -30,7 +32,7 @@
<template v-for="item in items" v-bind:key="item.id">
<div :class="['file-preview', this.item && item.id == this.item.id && 'active']" @click="select(item)">
<slot :item="item"></slot>
<slot :item="item" :load="load" :lastUrl="lastUrl"></slot>
</div>
</template>
@@ -55,55 +57,37 @@ export default {
prevLabel: { type: String, default: "Prev" },
nextLabel: { type: String, default: "Next" },
listUrl: { type: String },
uploadUrl: { type: String },
uploadFieldName: { type: String, default: "file" },
uploadLabel: { type: String, default: "Upload a file" },
},
data() {
return {
STATE: {
DEFAULT: 0,
UPLOADING: 1,
},
state: 0,
item: null,
items: [],
uploadFile: null,
uploadUrl: null,
uploadFieldName: null,
uploadCSRF: null,
nextUrl: "",
prevUrl: "",
lastUrl: "",
upload: {},
}
},
methods: {
previewFile(event) {
const [file] = event.target.files
this.uploadFile = file && {
file: file,
src: URL.createObjectURL(file)
}
},
removeUpload() {
this.uploadFile = null;
},
doUpload() {
const formData = new FormData();
formData.append('file', this.uploadFile.file)
formData.append('original_filename', this.uploadFile.file.name)
formData.append('csrfmiddlewaretoken', getCsrf())
fetch(this.uploadUrl, {
method: "POST",
body: formData
}).then(
() => {
this.uploadFile = null;
this.load()
}
)
},
load(url) {
fetch(url || this.listUrl).then(
response => response.ok ? response.json() : Promise.reject(response)
).then(data => {
this.lastUrl = url
this.nextUrl = data.next
this.prevUrl = data.previous
this.items = data.results
@@ -116,6 +100,56 @@ export default {
select(item) {
this.item = item;
},
// ---- upload
uploadAbort() {
this.upload.request && this.upload.request.abort()
},
onSubmit() {
const [file] = this.$refs.uploadFile.files
if(!file)
return
this._setUploadFile(file)
const req = new XMLHttpRequest()
req.open("POST", this.uploadUrl || this.listUrl)
req.upload.addEventListener("progress", (e) => this.onUploadProgress(e))
req.addEventListener("load", (e) => this.onUploadDone(e, true))
req.addEventListener("abort", (e) => this.onUploadDone(e))
req.addEventListener("error", (e) => this.onUploadDone(e))
const formData = new FormData(this.$refs.uploadForm);
formData.append('csrfmiddlewaretoken', getCsrf())
req.send(formData)
this._resetUpload(this.STATE.UPLOADING, false, req)
},
onUploadProgress(event) {
this.upload.loaded = event.loaded
this.upload.total = event.total
},
onUploadDone(reload=false) {
this._resetUpload(this.STATE.DEFAULT, true)
reload && this.load()
},
_setUploadFile(file) {
this.upload.file = file
this.upload.fileURL = file && URL.createObjectURL(file)
},
_resetUpload(state, resetFile=false, request=null) {
this.state = state
this.upload.loaded = 0
this.upload.total = 0
this.upload.request = request
if(resetFile)
this.upload.file = null
}
},
mounted() {

View File

@@ -1,3 +1,4 @@
import AActionButton from './AActionButton'
import AAutocomplete from './AAutocomplete'
import ACarousel from './ACarousel'
import ADropdown from "./ADropdown"
@@ -34,5 +35,5 @@ export const admin = {
export const dashboard = {
...base,
ASelectFile, AModal, APlaylistEditor,
AActionButton, ASelectFile, AModal, APlaylistEditor,
}