comand-component-library 3.1.41 → 3.1.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/comand-component-library.css +1 -1
- package/dist/comand-component-library.umd.min.js +1 -1
- package/package.json +3 -2
- package/src/App.vue +11 -4
- package/src/assets/data/fake-select-countries.json +12 -12
- package/src/assets/data/fake-select-options.json +1 -1
- package/src/assets/styles/global-styles.scss +0 -1
- package/src/components/CmdFakeSelect.vue +56 -17
- package/src/components/CmdFormElement.vue +26 -15
- package/src/components/CmdLoginForm.vue +29 -7
- package/src/components/CmdProgressBar.vue +1 -1
- package/src/components/CmdSwitchButton.vue +5 -3
- package/src/components/CmdUploadForm.vue +722 -174
- package/src/mixins/CmdUploadForm/DefaultMessageProperties.js +49 -0
- package/src/mixins/I18n.js +56 -0
- package/src/utils/GetFileExtension.js +15 -0
@@ -1,166 +1,455 @@
|
|
1
1
|
<template>
|
2
|
-
<fieldset
|
3
|
-
<
|
4
|
-
<
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
<
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
</
|
19
|
-
</template>
|
20
|
-
<hr/>
|
21
|
-
<h2>List of files to upload</h2>
|
22
|
-
<ul v-if="listOfFiles.length" class="list-of-files">
|
23
|
-
<li v-for="(uploadFile, index) in listOfFiles" :key="index">
|
24
|
-
<a href="#" class="icon-delete" title="Remove from list" @click.prevent="removeFile(index)"></a>
|
25
|
-
<span :class="[uploadFile.allowedType ? 'allowed' : 'not-allowed']">
|
26
|
-
<strong>{{ uploadFile.file.name }}</strong> ({{ uploadFile.file.type }}, {{
|
27
|
-
formatSize(uploadFile.file.size)
|
28
|
-
}}<template v-if="uploadFile.width && uploadFile.height">, {{
|
29
|
-
uploadFile.width
|
30
|
-
}} px x {{ uploadFile.height }} px</template>)
|
31
|
-
<span v-if="uploadFile.allowedType" class="icon-check allowed" title="File ready to upload!"></span>
|
32
|
-
<span v-else class="icon-cancel not-allowed"
|
33
|
-
title="File type not allowed (file will not be uploaded)!"></span>
|
34
|
-
</span>
|
35
|
-
</li>
|
36
|
-
</ul>
|
37
|
-
<CmdSystemMessage v-if="!listOfFiles.length" status="warning" :fullWidth="true"
|
38
|
-
message="No files selected for upload!">
|
39
|
-
</CmdSystemMessage>
|
40
|
-
<CmdSystemMessage v-else :status="messageStatusUploadSize()" :fullWidth="true">
|
41
|
-
<p>Current upload size is {{ formatSize(uploadSize) }} (of max. {{ formatSize(maxUploadSize) }}).</p>
|
42
|
-
</CmdSystemMessage>
|
43
|
-
<CmdSystemMessage v-if="uploadSize > maxUploadSize" status="error" :fullWidth="true"
|
44
|
-
message="Total file size to large!">
|
2
|
+
<fieldset :class="['my-sp-upload-form flex-container', { 'upload-initiated': uploadInitiated }]">
|
3
|
+
<h3 v-if="headline">{{ headline }}</h3>
|
4
|
+
<CmdSystemMessage
|
5
|
+
v-if="systemMessageStatus && allSystemMessages.length"
|
6
|
+
:closeIcon="{ show: false }"
|
7
|
+
:status="systemMessageStatus"
|
8
|
+
:systemMessage="
|
9
|
+
allSystemMessages.length === 1
|
10
|
+
? allSystemMessages[0]
|
11
|
+
: getMessage('cmduploadform.system_message.the_following_errors_occurred')
|
12
|
+
"
|
13
|
+
>
|
14
|
+
<ul v-if="allSystemMessages.length > 1">
|
15
|
+
<li v-for="(systemMessage, index) in allSystemMessages" :key="index">
|
16
|
+
{{ systemMessage }}
|
17
|
+
</li>
|
18
|
+
</ul>
|
45
19
|
</CmdSystemMessage>
|
20
|
+
<div :class="['box', { 'allow-drop': allowDrop }]" v-on="dragAndDropHandler">
|
21
|
+
<template v-if="!listOfFiles.length">
|
22
|
+
<h4 v-if="allowMultipleFileUploads">
|
23
|
+
{{ getMessage("cmduploadform.no_files_to_upload") }}
|
24
|
+
</h4>
|
25
|
+
<h4 v-else>
|
26
|
+
{{ getMessage("cmduploadform.no_file_to_upload") }}
|
27
|
+
</h4>
|
28
|
+
</template>
|
29
|
+
|
30
|
+
<!-- begin total-upload information -->
|
31
|
+
<template v-else>
|
32
|
+
<template v-if="showTotalUpload && listOfFiles.length !== 1">
|
33
|
+
<h4>{{ getMessage("cmduploadform.headline.summary_of_all_files") }}</h4>
|
34
|
+
<ul v-if="showTotalUpload && listOfFiles.length !== 1" class="list-of-files">
|
35
|
+
<li class="flex-container no-flex">
|
36
|
+
<a
|
37
|
+
href="#"
|
38
|
+
:title="getMessage('cmduploadform.labeltext.remove_all_files_from_list')"
|
39
|
+
@click.prevent="cancelUpload"
|
40
|
+
>
|
41
|
+
<span :class="deleteIconClass"></span>
|
42
|
+
</a>
|
43
|
+
<span>
|
44
|
+
<strong>{{ listOfFiles.length }}
|
45
|
+
<template v-if="!allowMultipleFileUploads">
|
46
|
+
{{ getMessage("cmduploadform.labeltext.file_uploading") }}
|
47
|
+
</template>
|
48
|
+
<template v-else>
|
49
|
+
{{ getMessage("cmduploadform.labeltext.files_uploading") }}
|
50
|
+
</template>
|
51
|
+
<span
|
52
|
+
:class="[
|
53
|
+
'text-align-right',
|
54
|
+
{ error: maxTotalUploadSize > 0 && totalSize > maxTotalUploadSize }
|
55
|
+
]">({{ formatSize(totalSize) }})</span>
|
56
|
+
</strong>
|
57
|
+
</span>
|
58
|
+
<span class="progressbar" v-if="uploadInitiated">
|
59
|
+
<span>{{ getPercentage(totalUploadProgress) }}</span>
|
60
|
+
<progress
|
61
|
+
max="100"
|
62
|
+
:value="totalUploadProgress"
|
63
|
+
:title="totalBytesUploaded"
|
64
|
+
></progress>
|
65
|
+
</span>
|
66
|
+
</li>
|
67
|
+
</ul>
|
68
|
+
<hr/>
|
69
|
+
</template>
|
70
|
+
<!-- end total-upload information -->
|
71
|
+
|
72
|
+
<!-- begin list of selected files -->
|
73
|
+
<h4>{{ getMessage("cmduploadform.headline.list_of_selected_files") }}</h4>
|
74
|
+
<ul class="list-of-files">
|
75
|
+
<li
|
76
|
+
v-for="(uploadFile, index) in listOfFiles"
|
77
|
+
:key="index"
|
78
|
+
class="flex-container no-flex"
|
79
|
+
>
|
80
|
+
<a
|
81
|
+
href="#"
|
82
|
+
:title="getMessage('cmduploadform.labeltext.remove_file_from_list')"
|
83
|
+
@click.prevent="removeFile(index)"
|
84
|
+
><span :class="deleteIconClass"></span>
|
85
|
+
</a>
|
86
|
+
<span
|
87
|
+
:class="[
|
88
|
+
'text-align-right',
|
89
|
+
uploadFile.allowedType ? 'allowed' : 'not-allowed',
|
90
|
+
{ error: uploadFile.error }
|
91
|
+
]"
|
92
|
+
>
|
93
|
+
{{ uploadFile.file.name }} ({{ formatSize(uploadFile.file.size) }})
|
94
|
+
</span>
|
95
|
+
<template v-if="uploadInitiated && !uploadFile.error">
|
96
|
+
<span class="progressbar">
|
97
|
+
<span>{{ getPercentage(uploadFile.progress) }}</span>
|
98
|
+
<!-- do not place inside progress-tag (will not be displayed then) -->
|
99
|
+
<progress
|
100
|
+
max="100"
|
101
|
+
:value="uploadFile.progress"
|
102
|
+
:title="
|
103
|
+
formatSize(uploadFile.uploadedBytes) + '/' + formatSize(uploadFile.file.size)
|
104
|
+
"
|
105
|
+
></progress>
|
106
|
+
</span>
|
107
|
+
</template>
|
108
|
+
</li>
|
109
|
+
</ul>
|
110
|
+
<a
|
111
|
+
v-if="failedUpload"
|
112
|
+
href="#"
|
113
|
+
@click.prevent="cancel"
|
114
|
+
:title="getMessage('cmduploadform.all_files_will_be_removed')">
|
115
|
+
{{ getMessage("cmduploadform.reset_upload") }}
|
116
|
+
</a>
|
117
|
+
<hr/>
|
118
|
+
</template>
|
119
|
+
<!-- end list of selected files -->
|
120
|
+
|
121
|
+
<!-- begin upload conditions -->
|
122
|
+
<h4 v-if="allowMultipleFileUploads && listOfFiles.length">
|
123
|
+
{{ getMessage("cmduploadform.headline.select_additional_files") }}
|
124
|
+
</h4>
|
125
|
+
<h4 v-if="!allowMultipleFileUploads && listOfFiles.length">
|
126
|
+
{{ getMessage("cmduploadform.headline.select_new_file") }}
|
127
|
+
</h4>
|
128
|
+
<dl class="small">
|
129
|
+
<template v-if="maxTotalUploadSize > 0">
|
130
|
+
<dt :class="{ error: totalSize > maxTotalUploadSize }">
|
131
|
+
{{ getMessage("cmduploadform.max_total_upload_size") }}
|
132
|
+
</dt>
|
133
|
+
<dd :class="['text-align-right', { error: totalSize > maxTotalUploadSize }]">
|
134
|
+
{{ formatSize(this.maxTotalUploadSize) }}
|
135
|
+
</dd>
|
136
|
+
</template>
|
137
|
+
<dt :class="{ error: errors.fileSize }">
|
138
|
+
{{ getMessage("cmduploadform.max_file_upload_size") }}
|
139
|
+
</dt>
|
140
|
+
<dd :class="['text-align-right', { error: errors.fileSize }]">
|
141
|
+
{{ formatSize(this.maxFileUploadSize) }}
|
142
|
+
</dd>
|
143
|
+
<dt :class="{ error: errors.fileType }">
|
144
|
+
{{ getMessage("cmduploadform.allowed_file_types") }}
|
145
|
+
</dt>
|
146
|
+
<dd>
|
147
|
+
<a
|
148
|
+
:class="showListOfFileExtensions ? 'icon-not-visible' : 'icon-visible'"
|
149
|
+
href="#"
|
150
|
+
@click.prevent="showListOfFileExtensions = !showListOfFileExtensions"
|
151
|
+
:title="getMessage('cmduploadform.tooltip.toggle_list_of_allowed_file_types')"
|
152
|
+
></a>
|
153
|
+
<transition name="fade">
|
154
|
+
<ul v-if="showListOfFileExtensions" class="list-of-file-extensions">
|
155
|
+
<li
|
156
|
+
v-for="(fileExtension, index) in allowedFileExtensions"
|
157
|
+
:key="index"
|
158
|
+
:class="{ error: errors.fileType }"
|
159
|
+
>
|
160
|
+
{{ fileExtension }}
|
161
|
+
</li>
|
162
|
+
</ul>
|
163
|
+
</transition>
|
164
|
+
</dd>
|
165
|
+
</dl>
|
166
|
+
<!-- end upload conditions -->
|
167
|
+
<button
|
168
|
+
type="button"
|
169
|
+
:class="['button upload primary', { disabled: uploadInitiated }]"
|
170
|
+
:disabled="uploadInitiated"
|
171
|
+
@click="selectFiles()"
|
172
|
+
>
|
173
|
+
<span class="icon-file-upload"></span>
|
174
|
+
<span v-if="allowMultipleFileUploads">{{
|
175
|
+
getMessage("cmduploadform.labeltext.select_files")
|
176
|
+
}}</span>
|
177
|
+
<span v-else>{{ getMessage("cmduploadform.labeltext.select_file") }}</span>
|
178
|
+
</button>
|
179
|
+
<CmdFormElement
|
180
|
+
element="input"
|
181
|
+
type="file"
|
182
|
+
:labelText="getMessage('cmduploadform.labeltext.select_files')"
|
183
|
+
:disabled="uploadInitiated"
|
184
|
+
:multiple="allowMultipleFileUploads"
|
185
|
+
@change="filesSelected"
|
186
|
+
/>
|
187
|
+
<p v-if="enableDragAndDrop" :class="['text-drag-and-drop', { disabled: uploadInitiated }]">
|
188
|
+
<span>{{ getMessage("cmduploadform.or") }}</span>
|
189
|
+
<strong>
|
190
|
+
{{ getMessage("cmduploadform.drag_and_drop") }}
|
191
|
+
<template v-if="allowMultipleFileUploads && listOfFiles.length">
|
192
|
+
{{ getMessage("cmduploadform.additional") }}
|
193
|
+
</template
|
194
|
+
>
|
195
|
+
<template v-if="!allowMultipleFileUploads && listOfFiles.length">
|
196
|
+
{{ getMessage("cmduploadform.new") }}
|
197
|
+
</template
|
198
|
+
>
|
199
|
+
{{ getMessage("cmduploadform.files_to_this_area") }}
|
200
|
+
</strong>
|
201
|
+
</p>
|
202
|
+
</div>
|
46
203
|
<CmdFormElement
|
47
204
|
v-if="enableComment"
|
48
205
|
element="textarea"
|
49
|
-
labelText="
|
50
|
-
|
51
|
-
|
206
|
+
:labelText="getMessage('cmduploadform.labeltext.comment')"
|
207
|
+
v-model="comment"
|
208
|
+
:required="commentRequired"
|
209
|
+
:statusMessage="commentStatusMessage"
|
210
|
+
:placeholder="getMessage('cmduploadform.placeholder.comment')"
|
211
|
+
:status="commentStatusMessage ? 'error' : ''"
|
52
212
|
/>
|
53
213
|
<div class="button-wrapper no-flex">
|
54
|
-
<button
|
55
|
-
|
56
|
-
|
57
|
-
|
214
|
+
<button
|
215
|
+
:class="[
|
216
|
+
'button primary',
|
217
|
+
{
|
218
|
+
disabled:
|
219
|
+
listOfFiles.length === 0 ||
|
220
|
+
(maxTotalUploadSize > 0 && totalSize > maxTotalUploadSize) ||
|
221
|
+
uploadInitiated
|
222
|
+
}
|
223
|
+
]"
|
224
|
+
:disabled="
|
225
|
+
listOfFiles.length === 0 ||
|
226
|
+
(maxTotalUploadSize > 0 && totalSize > maxTotalUploadSize) ||
|
227
|
+
uploadInitiated
|
228
|
+
"
|
229
|
+
@click="uploadFiles"
|
230
|
+
>
|
231
|
+
<span class="icon-upload"></span
|
232
|
+
><span v-if="listOfFiles.length === 1 || !allowMultipleFileUploads">{{
|
233
|
+
getMessage("cmduploadform.buttontext.upload_file")
|
234
|
+
}}</span>
|
235
|
+
<span v-else>{{ getMessage("cmduploadform.buttontext.upload_files") }}</span>
|
58
236
|
</button>
|
59
|
-
<button class="button" @click="
|
60
|
-
|
237
|
+
<button :class="['button', { disabled: listOfFiles.length === 0 }]" @click="cancel">
|
238
|
+
<span class="icon-cancel"></span
|
239
|
+
><span>{{ getMessage("cmduploadform.buttontext.cancel") }}</span>
|
61
240
|
</button>
|
62
241
|
</div>
|
63
242
|
</fieldset>
|
64
243
|
</template>
|
65
244
|
|
66
245
|
<script>
|
67
|
-
|
246
|
+
import I18n from "../mixins/I18n"
|
247
|
+
import DefaultMessageProperties from "../mixins/CmdUploadForm/DefaultMessageProperties"
|
248
|
+
import {getFileExtension} from "../utils/GetFileExtension.js"
|
249
|
+
import axios from "axios"
|
250
|
+
|
68
251
|
import CmdFormElement from "./CmdFormElement"
|
69
252
|
import CmdSystemMessage from "./CmdSystemMessage"
|
70
253
|
|
254
|
+
|
71
255
|
export default {
|
72
256
|
name: "CmdUploadForm",
|
73
|
-
emits: ["click"],
|
74
257
|
data() {
|
75
258
|
return {
|
76
259
|
comment: "",
|
77
260
|
allowDrop: false,
|
78
|
-
listOfFiles: []
|
261
|
+
listOfFiles: [],
|
262
|
+
systemMessages: [],
|
263
|
+
defaultSystemMessageStatus: "",
|
264
|
+
showListOfFileExtensions: false,
|
265
|
+
resetForm: {},
|
266
|
+
uploadInitiated: false,
|
267
|
+
errors: {}
|
79
268
|
}
|
80
269
|
},
|
270
|
+
mixins: [I18n, DefaultMessageProperties],
|
81
271
|
components: {
|
82
|
-
|
83
|
-
|
272
|
+
CmdSystemMessage,
|
273
|
+
CmdFormElement
|
274
|
+
},
|
275
|
+
created() {
|
276
|
+
// Set initial data for resetForm.
|
277
|
+
this.resetForm.comment = this.presetComment
|
278
|
+
this.resetForm.allowDrop = this.allowDrop
|
279
|
+
this.resetForm.listOfFiles = JSON.parse(JSON.stringify(this.listOfFiles))
|
280
|
+
this.resetForm.systemMessages = this.systemMessages
|
281
|
+
this.resetForm.systemMessageStatus = this.systemMessageStatus
|
84
282
|
},
|
85
283
|
props: {
|
86
284
|
/**
|
87
|
-
*
|
285
|
+
* set icon class for delete-icons
|
88
286
|
*/
|
89
|
-
|
287
|
+
deleteIconClass: {
|
90
288
|
type: String,
|
91
|
-
|
289
|
+
default: "icon-delete"
|
92
290
|
},
|
93
291
|
/**
|
94
|
-
*
|
292
|
+
* toggle visibility of total upload (number of files, total size, total progress
|
95
293
|
*/
|
96
|
-
|
97
|
-
type:
|
98
|
-
|
294
|
+
showTotalUpload: {
|
295
|
+
type: Boolean,
|
296
|
+
default: true
|
99
297
|
},
|
100
298
|
/**
|
101
|
-
*
|
299
|
+
* toggle if upload is handled by component itself or by outer component
|
102
300
|
*/
|
103
|
-
|
301
|
+
componentHandlesUpload: {
|
104
302
|
type: Boolean,
|
105
303
|
default: true
|
106
304
|
},
|
107
305
|
/**
|
108
|
-
*
|
306
|
+
* list of allowed file extensions to upload (all can be selected)
|
109
307
|
*/
|
110
|
-
|
308
|
+
allowedFileExtensions: {
|
309
|
+
type: Array,
|
310
|
+
required: true
|
311
|
+
},
|
312
|
+
commentRequired: {
|
111
313
|
type: Boolean,
|
112
314
|
default: true
|
113
315
|
},
|
316
|
+
commentStatusMessage: {
|
317
|
+
type: String,
|
318
|
+
default: ""
|
319
|
+
},
|
320
|
+
/**
|
321
|
+
* set an optional headlien for the fieldset
|
322
|
+
*/
|
323
|
+
headline: {
|
324
|
+
type: String,
|
325
|
+
required: false
|
326
|
+
},
|
114
327
|
/**
|
115
|
-
* enable
|
328
|
+
* enable if files can also be dragged (and dropped) into upload-area
|
116
329
|
*/
|
330
|
+
enableDragAndDrop: {
|
331
|
+
type: Boolean,
|
332
|
+
default: false
|
333
|
+
},
|
117
334
|
enableComment: {
|
118
335
|
type: Boolean,
|
119
336
|
default: true
|
120
337
|
},
|
121
338
|
/**
|
122
|
-
* set upload
|
339
|
+
* set to 0 if no maximum for total upload size should be set
|
123
340
|
*/
|
124
|
-
|
125
|
-
type:
|
126
|
-
|
341
|
+
maxTotalUploadSize: {
|
342
|
+
type: Number,
|
343
|
+
default: 5242880
|
127
344
|
},
|
128
345
|
/**
|
129
|
-
*
|
346
|
+
* max file size (in bytes) for each single file
|
130
347
|
*/
|
131
|
-
|
132
|
-
type:
|
133
|
-
|
348
|
+
maxFileUploadSize: {
|
349
|
+
type: Number,
|
350
|
+
default: 10485760
|
351
|
+
},
|
352
|
+
allowMultipleFileUploads: {
|
353
|
+
type: Boolean,
|
354
|
+
default: false
|
355
|
+
},
|
356
|
+
presetComment: {
|
357
|
+
type: String,
|
358
|
+
default: ""
|
134
359
|
},
|
135
360
|
/**
|
136
|
-
*
|
361
|
+
* defines upload options if component handles upload itself
|
362
|
+
* (componentHandlesUpload-property must be true)
|
363
|
+
*
|
364
|
+
* Example:
|
365
|
+
* <pre>
|
366
|
+
* url: String,
|
367
|
+
* filesParam: String,
|
368
|
+
* additionalParams: {}
|
369
|
+
* </pre>
|
137
370
|
*/
|
138
|
-
|
139
|
-
type:
|
140
|
-
|
371
|
+
uploadOptions: {
|
372
|
+
type: Object,
|
373
|
+
required: false
|
141
374
|
}
|
142
375
|
},
|
143
376
|
computed: {
|
144
|
-
|
145
|
-
|
377
|
+
failedUpload() {
|
378
|
+
return this.listOfFiles.some(file => file.error)
|
379
|
+
},
|
380
|
+
totalBytesUploaded() {
|
381
|
+
const bytes = this.listOfFiles
|
382
|
+
.map(uploadFile => [
|
383
|
+
uploadFile.file.size,
|
384
|
+
((uploadFile.progress || 0) * uploadFile.file.size) / 100
|
385
|
+
])
|
386
|
+
.reduce((a, b) => [a[0] + b[0], a[1] + b[1]])
|
387
|
+
return this.formatSize(bytes[1]) + "/" + this.formatSize(bytes[0])
|
388
|
+
},
|
389
|
+
totalSize() {
|
390
|
+
let totalSize = 0
|
146
391
|
for (let i = 0; i < this.listOfFiles.length; i++) {
|
147
|
-
|
148
|
-
uploadSize = uploadSize + this.listOfFiles[i].file.size
|
149
|
-
}
|
392
|
+
totalSize = totalSize + this.listOfFiles[i].file.size
|
150
393
|
}
|
151
|
-
return
|
394
|
+
return totalSize
|
152
395
|
},
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
396
|
+
allSystemMessages() {
|
397
|
+
if (this.maxTotalUploadSize > 0 && this.totalSize > this.maxTotalUploadSize) {
|
398
|
+
return [
|
399
|
+
this.getMessage("cmduploadform.system_message_total_size_of_files_too_large"),
|
400
|
+
...this.systemMessages
|
401
|
+
]
|
402
|
+
}
|
403
|
+
return this.systemMessages
|
404
|
+
},
|
405
|
+
systemMessageStatus() {
|
406
|
+
return this.defaultSystemMessageStatus || (this.allSystemMessages.length ? "error" : "")
|
407
|
+
},
|
408
|
+
dragAndDropHandler() {
|
409
|
+
// register handlers only if drag-and-drop is enabled
|
410
|
+
if (this.enableDragAndDrop) {
|
411
|
+
return {
|
412
|
+
dragenter: this.dragEnter,
|
413
|
+
dragover: this.dragOver,
|
414
|
+
dragleave: this.dragLeave,
|
415
|
+
drop: this.drop
|
158
416
|
}
|
159
417
|
}
|
160
|
-
return
|
418
|
+
return {}
|
419
|
+
},
|
420
|
+
totalUploadProgress() {
|
421
|
+
const progress = this.listOfFiles
|
422
|
+
.map(uploadFile => [
|
423
|
+
uploadFile.file.size,
|
424
|
+
((uploadFile.progress || 0) * uploadFile.file.size) / 100
|
425
|
+
])
|
426
|
+
.reduce((a, b) => [a[0] + b[0], a[1] + b[1]])
|
427
|
+
return (progress[1] / progress[0]) * 100
|
428
|
+
}
|
429
|
+
},
|
430
|
+
watch: {
|
431
|
+
presetComment: {
|
432
|
+
handler(newValue) {
|
433
|
+
this.comment = newValue
|
434
|
+
},
|
435
|
+
immediate: true
|
161
436
|
}
|
162
437
|
},
|
163
438
|
methods: {
|
439
|
+
getPercentage(percentage) {
|
440
|
+
if (percentage) {
|
441
|
+
return percentage.toFixed(2) + "%"
|
442
|
+
}
|
443
|
+
return "0.00%"
|
444
|
+
},
|
445
|
+
// use imported function as method (to use in template)
|
446
|
+
getFileExtension(filename) {
|
447
|
+
return getFileExtension(filename)
|
448
|
+
},
|
449
|
+
selectFiles() {
|
450
|
+
let inputFile = this.$el.querySelector('input[type="file"]')
|
451
|
+
inputFile.click()
|
452
|
+
},
|
164
453
|
dragEnter(event) {
|
165
454
|
this.dragOver(event)
|
166
455
|
},
|
@@ -187,11 +476,11 @@ export default {
|
|
187
476
|
this.allowDrop = false
|
188
477
|
},
|
189
478
|
/*
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
479
|
+
drag(event) {
|
480
|
+
alert("dropped")
|
481
|
+
event.dataTransfer.setData("text", event.target.id)
|
482
|
+
},
|
483
|
+
*/
|
195
484
|
drop(event) {
|
196
485
|
this.allowDrop = false
|
197
486
|
if (event.dataTransfer && event.dataTransfer.files && event.dataTransfer.files.length > 0) {
|
@@ -200,144 +489,403 @@ export default {
|
|
200
489
|
}
|
201
490
|
},
|
202
491
|
cancelUpload() {
|
492
|
+
// cancel upload for each file
|
493
|
+
this.listOfFiles.forEach(file => {
|
494
|
+
if (file.abortController) {
|
495
|
+
file.abortController.abort()
|
496
|
+
}
|
497
|
+
})
|
498
|
+
|
499
|
+
// clear list of files, remove error-highlighting and hide all system-messages afterwards
|
500
|
+
this.errors = {}
|
203
501
|
this.listOfFiles = []
|
204
|
-
this.
|
205
|
-
|
502
|
+
this.hideAllSystemMessages()
|
503
|
+
|
504
|
+
// set uploadInitiated to false to enable all disabled buttons
|
505
|
+
this.uploadInitiated = false
|
506
|
+
},
|
507
|
+
cancel() {
|
508
|
+
this.cancelUpload()
|
509
|
+
|
510
|
+
// emit click event with argument "cancel" to react in outer component
|
511
|
+
this.$emit("click", "cancel")
|
206
512
|
},
|
207
513
|
filesSelected(event) {
|
208
514
|
this.checkFiles(event.target.files)
|
209
515
|
},
|
210
516
|
checkFiles(files) {
|
517
|
+
this.defaultSystemMessageStatus = "" // hide systemMessage if already is shown
|
518
|
+
this.systemMessages = [] // hide systemMessage if already is shown
|
519
|
+
this.errors = {}
|
520
|
+
|
211
521
|
for (let i = 0; i < files.length; i++) {
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
522
|
+
// define file-object which will be pushed in listOfFiles
|
523
|
+
const uploadFile = {
|
524
|
+
file: files[i],
|
525
|
+
progress: null,
|
526
|
+
uploadedBytes: 0
|
217
527
|
}
|
218
528
|
|
219
|
-
|
220
|
-
|
529
|
+
// check size for current file
|
530
|
+
if (files[i].size > this.maxFileUploadSize) {
|
531
|
+
this.errors.fileSize = true
|
532
|
+
this.systemMessages.push(
|
533
|
+
this.getMessage(
|
534
|
+
"cmduploadform.system_message.file_size_too_large",
|
535
|
+
files[i].name,
|
536
|
+
files[i].size
|
537
|
+
)
|
538
|
+
)
|
539
|
+
continue
|
221
540
|
}
|
222
541
|
|
223
|
-
if
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
542
|
+
// check if current file has allowed file-type (else continue with next file)
|
543
|
+
if (!this.allowedFileExtensions.includes(getFileExtension(files[i].name))) {
|
544
|
+
this.showListOfFileExtensions = true
|
545
|
+
this.errors.fileType = true
|
546
|
+
this.systemMessages.push(
|
547
|
+
this.getMessage(
|
548
|
+
"cmduploadform.system_message.not_allowed_file_type",
|
549
|
+
files[i].name,
|
550
|
+
getFileExtension(files[i].name)
|
551
|
+
)
|
552
|
+
)
|
553
|
+
continue
|
554
|
+
}
|
555
|
+
|
556
|
+
// check (if multiple files can be selected) if current file already exists in listOfFiles
|
557
|
+
if (
|
558
|
+
this.allowMultipleFileUploads &&
|
559
|
+
this.listOfFiles.some(listOfFilesEntry =>
|
560
|
+
this.compareFiles(listOfFilesEntry.file, files[i])
|
561
|
+
)
|
562
|
+
) {
|
563
|
+
this.systemMessages.push(
|
564
|
+
this.getMessage(
|
565
|
+
"cmduploadform.system_message.duplicate_file",
|
566
|
+
files[i].name,
|
567
|
+
getFileExtension(files[i].name)
|
568
|
+
)
|
569
|
+
)
|
570
|
+
continue
|
571
|
+
}
|
572
|
+
|
573
|
+
if (this.allowMultipleFileUploads) {
|
574
|
+
// push file-object (for each valid file) to listOfFiles-array
|
575
|
+
this.listOfFiles.push(uploadFile)
|
576
|
+
} else {
|
577
|
+
if (files.length > 1) {
|
578
|
+
this.systemMessages.push(
|
579
|
+
this.getMessage("cmduploadform.system_message.only_one_file_allowed")
|
580
|
+
)
|
581
|
+
}
|
582
|
+
// assign uploadFile-object (which contains current (and valid) file to listOfFiles-array
|
583
|
+
this.listOfFiles = [uploadFile]
|
584
|
+
break
|
236
585
|
}
|
237
|
-
this.listOfFiles.push(uploadFile)
|
238
586
|
}
|
239
587
|
},
|
588
|
+
compareFiles(file1, file2) {
|
589
|
+
return (
|
590
|
+
file1.name === file2.name &&
|
591
|
+
file1.lastModified === file2.lastModified &&
|
592
|
+
file1.size === file2.size
|
593
|
+
)
|
594
|
+
},
|
240
595
|
removeFile(index) {
|
241
|
-
/* remove file from list */
|
596
|
+
/* remove specific file from list */
|
242
597
|
this.listOfFiles.splice(index, 1)
|
598
|
+
if (!this.listOfFiles.length) {
|
599
|
+
this.uploadInitiated = false
|
600
|
+
}
|
601
|
+
this.hideAllSystemMessages()
|
602
|
+
},
|
603
|
+
hideAllSystemMessages() {
|
604
|
+
// hide all system-messages if all files are removed from list
|
605
|
+
if (!this.listOfFiles.length) {
|
606
|
+
this.systemMessages = []
|
607
|
+
}
|
243
608
|
},
|
244
609
|
formatSize(size) {
|
245
610
|
if (size < 1024) {
|
246
611
|
return size
|
247
612
|
} else if (size < 1048576) {
|
248
|
-
return size =
|
613
|
+
return (size = Math.round((size / 1024) * 100) / 100 + " KB")
|
249
614
|
} else {
|
250
|
-
return size =
|
615
|
+
return (size = Math.round((size / 1048576) * 100) / 100 + " MB")
|
251
616
|
}
|
252
617
|
},
|
253
618
|
uploadFiles() {
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
619
|
+
this.systemMessages = []
|
620
|
+
this.errors = {}
|
621
|
+
this.defaultSystemMessageStatus = ""
|
622
|
+
|
623
|
+
if (
|
624
|
+
this.enableComment &&
|
625
|
+
!this.comment &&
|
626
|
+
this.commentRequired &&
|
627
|
+
this.commentStatusMessage
|
628
|
+
) {
|
629
|
+
this.defaultSystemMessageStatus = "error"
|
630
|
+
this.systemMessages.push(this.getMessage("cmduploadform.system_message.fill_required"))
|
631
|
+
} else {
|
632
|
+
this.uploadInitiated = true
|
633
|
+
|
634
|
+
if (this.componentHandlesUpload && this.uploadOptions && this.uploadOptions.url) {
|
635
|
+
const url = new URL(this.uploadOptions.url, location.href)
|
636
|
+
const formData = new FormData()
|
637
|
+
|
638
|
+
// // append information about files to formData-object
|
639
|
+
// formData.append(
|
640
|
+
// this.uploadOptions.filesParam ? this.uploadOptions.filesParam : "files",
|
641
|
+
// this.listOfFiles.map(uploadFile => uploadFile.file)
|
642
|
+
// )
|
643
|
+
|
644
|
+
// iterate over additionalParams-object and append to formData-object
|
645
|
+
Object.entries(this.uploadOptions.additionalParams || {}).forEach(([key, value]) =>
|
646
|
+
formData.append(key, value)
|
647
|
+
)
|
648
|
+
|
649
|
+
// append comment to formData-object
|
650
|
+
if (this.enableComment) {
|
651
|
+
formData.append("comment", this.comment)
|
259
652
|
}
|
260
|
-
}
|
261
653
|
|
262
|
-
|
263
|
-
|
654
|
+
const requests = []
|
655
|
+
|
656
|
+
// iterate over list-of-files to send upload request for each file individually
|
657
|
+
this.listOfFiles.forEach(file => {
|
658
|
+
requests.push(this.uploadSingleFile(url, file, formData))
|
659
|
+
})
|
660
|
+
|
661
|
+
// check upload-status for each file individually
|
662
|
+
Promise.allSettled(requests).then(results => {
|
663
|
+
// if status equals "rejected" the upload was not successful and the file will not be deleted from list
|
664
|
+
const rejectedFiles = results.filter(result => result.status === "rejected")
|
665
|
+
this.uploadInitiated = false
|
666
|
+
|
667
|
+
if (rejectedFiles.length) {
|
668
|
+
this.defaultSystemMessageStatus = "error"
|
669
|
+
this.systemMessages.push(
|
670
|
+
this.getMessage(
|
671
|
+
"cmduploadform.system_message.some_files_are_not_uploaded_successfully"
|
672
|
+
)
|
673
|
+
)
|
674
|
+
} else {
|
675
|
+
this.defaultSystemMessageStatus = "success"
|
676
|
+
this.systemMessages.push(
|
677
|
+
this.getMessage("cmduploadform.system_message.all_files_are_uploaded_successfully")
|
678
|
+
)
|
679
|
+
}
|
680
|
+
|
681
|
+
this.$emit("upload-complete", {success: !rejectedFiles.length})
|
682
|
+
})
|
683
|
+
} else {
|
684
|
+
let uploadObj = {}
|
685
|
+
uploadObj.listOfFiles = this.listOfFiles
|
686
|
+
uploadObj.type = "upload"
|
687
|
+
uploadObj.comment = this.comment
|
688
|
+
|
689
|
+
// emit uploadObj to handle upload by outer component
|
690
|
+
this.$emit("click", uploadObj, this.showMessage)
|
264
691
|
}
|
692
|
+
}
|
693
|
+
},
|
694
|
+
onUploadProgress(event, uploadFile) {
|
695
|
+
if (event.lengthComputable) {
|
696
|
+
uploadFile.progress = (event.loaded / event.total) * 100
|
697
|
+
uploadFile.uploadedBytes = event.loaded
|
265
698
|
} else {
|
266
|
-
|
699
|
+
uploadFile.progress = null
|
700
|
+
uploadFile.uploadedBytes = 0
|
267
701
|
}
|
702
|
+
this.$forceUpdate()
|
703
|
+
},
|
704
|
+
uploadSingleFile(url, file, formData) {
|
705
|
+
file.abortController = new AbortController()
|
706
|
+
|
707
|
+
// append information about given file to formData-object
|
708
|
+
formData.set(
|
709
|
+
this.uploadOptions.filesParam ? this.uploadOptions.filesParam : "files",
|
710
|
+
file.file
|
711
|
+
)
|
712
|
+
|
713
|
+
return (
|
714
|
+
axios
|
715
|
+
.post(url, formData, {
|
716
|
+
signal: file.abortController.signal,
|
717
|
+
onUploadProgress: event => this.onUploadProgress(event, file)
|
718
|
+
})
|
719
|
+
// emit information about successful-upload + file
|
720
|
+
.then(response => {
|
721
|
+
this.$emit("upload-file-success", file)
|
722
|
+
return response
|
723
|
+
})
|
724
|
+
// delete uploaded file from list-of-files-array
|
725
|
+
.then(response => {
|
726
|
+
const positionOfFile = this.listOfFiles.indexOf(file)
|
727
|
+
if (positionOfFile !== -1) {
|
728
|
+
this.listOfFiles.splice(positionOfFile, 1)
|
729
|
+
}
|
730
|
+
return response
|
731
|
+
})
|
732
|
+
.catch(error => {
|
733
|
+
this.defaultSystemMessageStatus = "error"
|
734
|
+
this.systemMessages.push(error)
|
735
|
+
file.error = true
|
736
|
+
throw new Error()
|
737
|
+
})
|
738
|
+
)
|
268
739
|
},
|
269
|
-
|
270
|
-
if (
|
271
|
-
|
740
|
+
showMessage(result) {
|
741
|
+
if (result === true) {
|
742
|
+
this.defaultSystemMessageStatus = "success"
|
743
|
+
this.systemMessages.push(this.getMessage("cmduploadform.system_message.upload_success"))
|
744
|
+
} else if (result === false) {
|
745
|
+
this.defaultSystemMessageStatus = "error"
|
746
|
+
this.systemMessages.push(this.getMessage("cmduploadform.system_message.upload_failed"))
|
272
747
|
}
|
273
|
-
return "success"
|
274
748
|
},
|
275
|
-
|
276
|
-
this
|
749
|
+
resetFields() {
|
750
|
+
if (typeof this.resetForm === "object") {
|
751
|
+
const resetArr = Object.keys(this.resetForm)
|
752
|
+
for (let key of resetArr) {
|
753
|
+
if (typeof this.resetForm[key] === "object") {
|
754
|
+
this[key] = JSON.parse(JSON.stringify(this.resetForm[key]))
|
755
|
+
} else {
|
756
|
+
this[key] = this.resetForm[key]
|
757
|
+
}
|
758
|
+
}
|
759
|
+
}
|
277
760
|
}
|
278
761
|
}
|
279
762
|
}
|
280
763
|
</script>
|
281
764
|
|
282
765
|
<style lang="scss">
|
283
|
-
/* begin
|
284
|
-
.
|
766
|
+
/* begin my-sp-upload-form -------------------------------------------------------------------------------------------- */
|
767
|
+
.my-sp-upload-form {
|
285
768
|
.box {
|
286
|
-
padding:
|
769
|
+
padding: (var(--default-padding));
|
287
770
|
text-align: center;
|
288
|
-
background: rgba(255, 255, 255, .5);
|
289
771
|
border-style: dashed;
|
772
|
+
background: var(--pure-white-reduced-opacity);
|
290
773
|
|
291
774
|
&.allow-drop {
|
292
775
|
border-style: solid;
|
293
|
-
background:
|
776
|
+
background: var(--pure-white);
|
777
|
+
}
|
778
|
+
|
779
|
+
dl {
|
780
|
+
justify-content: center;
|
781
|
+
text-align: left;
|
782
|
+
|
783
|
+
.list-of-file-extensions {
|
784
|
+
display: table;
|
785
|
+
|
786
|
+
> li:only-child {
|
787
|
+
list-style-type: none;
|
788
|
+
margin: 0;
|
789
|
+
}
|
790
|
+
}
|
791
|
+
}
|
792
|
+
}
|
793
|
+
|
794
|
+
[class*="list-of-file"] {
|
795
|
+
max-height: 10rem;
|
796
|
+
overflow-x: hidden;
|
797
|
+
overflow-y: auto;
|
798
|
+
border: var(--default-border);
|
799
|
+
padding: calc(var(--default-padding) / 2);
|
800
|
+
margin: 0;
|
801
|
+
|
802
|
+
> li {
|
803
|
+
flex-wrap: nowrap;
|
804
|
+
margin-right: var(--default-margin); /* avoids text to be placed below scrollbar */
|
805
|
+
|
806
|
+
.progressbar {
|
807
|
+
display: table;
|
808
|
+
align-self: center;
|
809
|
+
|
810
|
+
progress {
|
811
|
+
&[value] {
|
812
|
+
background: var(--pure-white);
|
813
|
+
|
814
|
+
&::-moz-progress-bar {
|
815
|
+
border-top-left-radius: var(--border-radius);
|
816
|
+
border-bottom-left-radius: var(--border-radius);
|
817
|
+
background: var(--primary-color);
|
818
|
+
}
|
819
|
+
}
|
820
|
+
}
|
821
|
+
|
822
|
+
& > span {
|
823
|
+
position: absolute;
|
824
|
+
left: 50%;
|
825
|
+
transform: translateX(-50%);
|
826
|
+
z-index: 1;
|
827
|
+
font-size: 1.2rem;
|
828
|
+
display: table;
|
829
|
+
top: 0.2rem;
|
830
|
+
padding: 0.1rem 0.2rem;
|
831
|
+
line-height: 100%;
|
832
|
+
background: var(--pure-white);
|
833
|
+
}
|
834
|
+
}
|
294
835
|
}
|
295
836
|
}
|
296
837
|
|
297
838
|
.list-of-files {
|
298
|
-
|
839
|
+
display: inline-flex;
|
840
|
+
flex-direction: column;
|
841
|
+
gap: calc(var(--default-gap) / 2);
|
842
|
+
|
843
|
+
&:last-of-type {
|
844
|
+
margin-bottom: var(--default-margin);
|
845
|
+
}
|
299
846
|
|
300
847
|
li {
|
301
848
|
list-style-type: none;
|
302
849
|
margin-left: 0;
|
850
|
+
gap: calc(var(--default-gap) / 2);
|
851
|
+
}
|
303
852
|
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
padding: calc(var(--default-padding) / 2);
|
310
|
-
}
|
853
|
+
& + a {
|
854
|
+
display: table;
|
855
|
+
margin: 0 auto;
|
856
|
+
}
|
857
|
+
}
|
311
858
|
|
312
|
-
|
313
|
-
|
859
|
+
p {
|
860
|
+
&.text-drag-and-drop {
|
861
|
+
&.disabled {
|
862
|
+
background: none !important; /* overwrite default styling for .disabled */
|
863
|
+
}
|
864
|
+
}
|
865
|
+
}
|
314
866
|
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
}
|
319
|
-
}
|
867
|
+
textarea {
|
868
|
+
min-height: 0;
|
869
|
+
}
|
320
870
|
|
321
|
-
|
322
|
-
|
871
|
+
.button.upload {
|
872
|
+
align-self: center;
|
323
873
|
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
}
|
328
|
-
}
|
874
|
+
& + .cmd-form-element {
|
875
|
+
display: none;
|
876
|
+
}
|
329
877
|
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
font-size: 1rem;
|
334
|
-
font-weight: bold;
|
335
|
-
color: var(--pure-white);
|
336
|
-
}
|
878
|
+
& ~ p {
|
879
|
+
& > * {
|
880
|
+
display: block;
|
337
881
|
}
|
338
882
|
}
|
339
883
|
}
|
884
|
+
|
885
|
+
.error {
|
886
|
+
color: var(--error-color);
|
887
|
+
}
|
340
888
|
}
|
341
889
|
|
342
|
-
/* end
|
890
|
+
/* end my-sp-upload-form ---------------------------------------------------------------------------------------------- */
|
343
891
|
</style>
|