Fixes #5950 -- Fix Vue3 reactivity on flow uploads.
This commit is contained in:
parent
ec50492047
commit
4c61d6288a
|
@ -1,16 +1,19 @@
|
|||
<template>
|
||||
<div class="flow-upload">
|
||||
<div class="upload-progress">
|
||||
<template v-for="(file, _) in files">
|
||||
<div v-if="file.is_visible" class="uploading-file pt-1" :id="'file_upload_' + file.uniqueIdentifier"
|
||||
:class="{ 'text-success': file.is_completed, 'text-danger': file.error }">
|
||||
<template v-for="(file, key) in files.value" :key="key">
|
||||
<div v-if="file.isVisible" class="uploading-file pt-1" :id="'file_upload_' + file.uniqueIdentifier"
|
||||
:class="{ 'text-success': file.isCompleted, 'text-danger': file.error }">
|
||||
<h6 class="fileuploadname m-0">{{ file.name }}</h6>
|
||||
<b-progress v-if="!file.is_completed" :value="file.progress_percent" :max="100"
|
||||
show-progress class="h-15 my-1"></b-progress>
|
||||
<div v-if="!file.isCompleted" class="progress h-15 my-1">
|
||||
<div class="progress-bar h-15" role="progressbar" :style="{width: file.progressPercent+'%'}"
|
||||
:aria-valuenow="file.progressPercent" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
|
||||
<div class="upload-status" v-if="file.error">
|
||||
{{ file.error }}
|
||||
</div>
|
||||
<div class="size">{{ formatFileSize(file.size) }}</div>
|
||||
<div class="size">{{ file.size }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
@ -64,131 +67,146 @@ div.flow-upload {
|
|||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import formatFileSize from '~/functions/formatFileSize.js';
|
||||
import Flow from '@flowjs/flow.js';
|
||||
import Icon from './Icon';
|
||||
import _ from 'lodash';
|
||||
import {computed, onMounted, onUnmounted, reactive, ref} from "vue";
|
||||
import Flow from "@flowjs/flow.js";
|
||||
|
||||
export default {
|
||||
name: 'FlowUpload',
|
||||
components: {Icon},
|
||||
emits: ['complete', 'success', 'error'],
|
||||
props: {
|
||||
targetUrl: String,
|
||||
allowMultiple: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
validMimeTypes: {
|
||||
type: Array,
|
||||
default() {
|
||||
return ['*'];
|
||||
}
|
||||
},
|
||||
flowConfiguration: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
}
|
||||
const props = defineProps({
|
||||
targetUrl: String,
|
||||
allowMultiple: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
validMimeTypes: {
|
||||
type: Array,
|
||||
default() {
|
||||
return ['*'];
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
flow: null,
|
||||
files: []
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
let defaultConfig = {
|
||||
target: () => {
|
||||
return this.targetUrl
|
||||
},
|
||||
singleFile: !this.allowMultiple,
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'X-API-CSRF': App.api_csrf
|
||||
},
|
||||
withCredentials: true,
|
||||
allowDuplicateUploads: true,
|
||||
fileParameterName: 'file_data',
|
||||
uploadMethod: 'POST',
|
||||
testMethod: 'GET',
|
||||
method: 'multipart',
|
||||
maxChunkRetries: 3,
|
||||
testChunks: false
|
||||
};
|
||||
let config = _.defaultsDeep({}, this.flowConfiguration, defaultConfig);
|
||||
|
||||
this.flow = new Flow(config);
|
||||
|
||||
this.flow.assignBrowse(this.$refs.file_browse_target);
|
||||
this.flow.assignDrop(this.$refs.file_drop_target);
|
||||
|
||||
this.flow.on('fileAdded', (file) => {
|
||||
file.progress_percent = 0;
|
||||
file.is_completed = false;
|
||||
file.error = null;
|
||||
file.is_visible = true;
|
||||
|
||||
this.files.push(file);
|
||||
return true;
|
||||
});
|
||||
|
||||
this.flow.on('filesSubmitted', () => {
|
||||
this.flow.upload();
|
||||
});
|
||||
|
||||
this.flow.on('fileProgress', (file) => {
|
||||
file.progress_percent = file.progress() * 100;
|
||||
});
|
||||
|
||||
this.flow.on('fileSuccess', (file, message) => {
|
||||
console.log(message);
|
||||
|
||||
file.is_completed = true;
|
||||
let messageJson = JSON.parse(message);
|
||||
this.$emit('success', file, messageJson);
|
||||
});
|
||||
|
||||
this.flow.on('error', (message, file, chunk) => {
|
||||
console.error(message, file, chunk);
|
||||
|
||||
let messageText = this.$gettext('Could not upload file.');
|
||||
try {
|
||||
if (typeof message !== 'undefined') {
|
||||
let messageJson = JSON.parse(message);
|
||||
if (typeof messageJson.message !== 'undefined') {
|
||||
messageText = messageJson.message;
|
||||
if (messageText.indexOf(': ') > -1) {
|
||||
messageText = messageText.split(': ')[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
file.error = messageText;
|
||||
this.$emit('error', file, messageText);
|
||||
});
|
||||
|
||||
this.flow.on('complete', () => {
|
||||
_.forEach(this.files, (file) => {
|
||||
file.is_visible = false;
|
||||
});
|
||||
|
||||
this.$emit('complete');
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
validMimeTypesList() {
|
||||
return this.validMimeTypes.join(', ');
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formatFileSize(bytes) {
|
||||
return formatFileSize(bytes);
|
||||
flowConfiguration: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const emit = defineEmits(['complete', 'success', 'error']);
|
||||
|
||||
const validMimeTypesList = computed(() => {
|
||||
return props.validMimeTypes.join(', ');
|
||||
});
|
||||
|
||||
let flow = null;
|
||||
|
||||
const files = reactive({
|
||||
value: {},
|
||||
push(file) {
|
||||
this.value[file.uniqueIdentifier] = {
|
||||
name: file.name,
|
||||
uniqueIdentifier: file.uniqueIdentifier,
|
||||
size: formatFileSize(file.size),
|
||||
isVisible: true,
|
||||
isCompleted: false,
|
||||
progressPercent: 0,
|
||||
error: null
|
||||
};
|
||||
},
|
||||
get(file) {
|
||||
return this.value[file.uniqueIdentifier] ?? {};
|
||||
},
|
||||
hideAll() {
|
||||
_.forEach(this.value, (file) => {
|
||||
file.isVisible = false;
|
||||
});
|
||||
},
|
||||
reset() {
|
||||
this.value = {};
|
||||
}
|
||||
});
|
||||
|
||||
const file_browse_target = ref(); // Template Ref
|
||||
const file_drop_target = ref(); // Template Ref
|
||||
|
||||
onMounted(() => {
|
||||
let defaultConfig = {
|
||||
target: () => {
|
||||
return props.targetUrl
|
||||
},
|
||||
singleFile: !props.allowMultiple,
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'X-API-CSRF': App.api_csrf
|
||||
},
|
||||
withCredentials: true,
|
||||
allowDuplicateUploads: true,
|
||||
fileParameterName: 'file_data',
|
||||
uploadMethod: 'POST',
|
||||
testMethod: 'GET',
|
||||
method: 'multipart',
|
||||
maxChunkRetries: 3,
|
||||
testChunks: false
|
||||
};
|
||||
let config = _.defaultsDeep({}, props.flowConfiguration, defaultConfig);
|
||||
|
||||
flow = new Flow(config);
|
||||
|
||||
flow.assignBrowse(file_browse_target.value);
|
||||
flow.assignDrop(file_drop_target.value);
|
||||
|
||||
flow.on('fileAdded', (file) => {
|
||||
files.push(file);
|
||||
return true;
|
||||
});
|
||||
|
||||
flow.on('filesSubmitted', () => {
|
||||
flow.upload();
|
||||
});
|
||||
|
||||
flow.on('fileProgress', (file) => {
|
||||
files.get(file).progressPercent = parseInt(file.progress() * 100);
|
||||
});
|
||||
|
||||
flow.on('fileSuccess', (file, message) => {
|
||||
files.get(file).isCompleted = true;
|
||||
|
||||
let messageJson = JSON.parse(message);
|
||||
emit('success', file, messageJson);
|
||||
});
|
||||
|
||||
flow.on('error', (message, file, chunk) => {
|
||||
console.error(message, file, chunk);
|
||||
|
||||
let messageText = this.$gettext('Could not upload file.');
|
||||
try {
|
||||
if (typeof message !== 'undefined') {
|
||||
let messageJson = JSON.parse(message);
|
||||
if (typeof messageJson.message !== 'undefined') {
|
||||
messageText = messageJson.message;
|
||||
if (messageText.indexOf(': ') > -1) {
|
||||
messageText = messageText.split(': ')[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
files.get(file).error = messageText;
|
||||
emit('error', file, messageText);
|
||||
});
|
||||
|
||||
flow.on('complete', () => {
|
||||
files.hideAll();
|
||||
|
||||
emit('complete');
|
||||
});
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
flow = null;
|
||||
files.reset();
|
||||
});
|
||||
</script>
|
||||
|
|
Loading…
Reference in New Issue