@xuda.io/xuda-widget-plugin-xuda-drive 1.0.142 → 1.0.144
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/index.mjs +5 -0
- package/dist/runtime.mjs +11004 -11018
- package/package.json +1 -1
- package/src/index.mjs +5 -0
- package/src/runtime.mjs +233 -338
package/src/runtime.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import 'filepond/dist/filepond.min.css';
|
|
|
3
3
|
|
|
4
4
|
window.FilePond = FilePond;
|
|
5
5
|
|
|
6
|
+
|
|
6
7
|
import FilePondPluginFileValidateType from 'filepond-plugin-file-validate-type';
|
|
7
8
|
|
|
8
9
|
import * as pintura from './vendors/pintura/pintura/pintura.js';
|
|
@@ -26,6 +27,11 @@ import * as fs from './vendors/fs-lightbox.js';
|
|
|
26
27
|
import { useDropZone } from '@vueuse/core';
|
|
27
28
|
|
|
28
29
|
export async function upload(fields, e) {
|
|
30
|
+
const container = e.container_node;
|
|
31
|
+
if (!container) {
|
|
32
|
+
e.report_error?.('xuda-drive: upload skipped — host provided no container_node (element not yet in DOM, e.g. a collapsed section)', true);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
29
35
|
return new Promise(async (resolve, reject) => {
|
|
30
36
|
var overrideAction = 'replace';
|
|
31
37
|
var existingFiles = {};
|
|
@@ -39,7 +45,7 @@ export async function upload(fields, e) {
|
|
|
39
45
|
var pond = undefined;
|
|
40
46
|
|
|
41
47
|
var init_files_fn = (files) => {
|
|
42
|
-
|
|
48
|
+
Object.entries(files || {}).forEach(function ([key, val]) {
|
|
43
49
|
if (val?.server_fileName) val = val.server_fileName;
|
|
44
50
|
val = func.utils.get_drive_url(e.SESSION_ID, val).value;
|
|
45
51
|
|
|
@@ -119,12 +125,15 @@ export async function upload(fields, e) {
|
|
|
119
125
|
}, 100);
|
|
120
126
|
};
|
|
121
127
|
|
|
122
|
-
var $pallet_wrapper = $(`<div class="image_wrapper" style="border: none !important;">`);
|
|
123
|
-
|
|
124
128
|
var input_id = new Date().valueOf();
|
|
125
|
-
var
|
|
126
|
-
|
|
127
|
-
|
|
129
|
+
var pallet_wrapper = document.createElement('div');
|
|
130
|
+
pallet_wrapper.className = 'image_wrapper';
|
|
131
|
+
pallet_wrapper.style.setProperty('border', 'none', 'important');
|
|
132
|
+
var imgInp = document.createElement('input');
|
|
133
|
+
imgInp.className = `filepond ${widget_cssClass || ''}`.trim();
|
|
134
|
+
imgInp.id = String(input_id);
|
|
135
|
+
pallet_wrapper.appendChild(imgInp);
|
|
136
|
+
container.replaceChildren(pallet_wrapper);
|
|
128
137
|
|
|
129
138
|
FilePond.registerPlugin(FilePondPluginImageEditor);
|
|
130
139
|
FilePond.registerPlugin(FilePondPluginFileValidateType);
|
|
@@ -144,15 +153,15 @@ export async function upload(fields, e) {
|
|
|
144
153
|
};
|
|
145
154
|
|
|
146
155
|
const axios_ajax = function (url, data) {
|
|
147
|
-
|
|
156
|
+
const body = new URLSearchParams();
|
|
148
157
|
|
|
149
|
-
|
|
150
|
-
|
|
158
|
+
Object.entries(data || {}).forEach(function ([key, val]) {
|
|
159
|
+
body.append(key, val);
|
|
151
160
|
});
|
|
152
161
|
|
|
153
162
|
fetch(url, {
|
|
154
163
|
method: 'POST',
|
|
155
|
-
body
|
|
164
|
+
body,
|
|
156
165
|
}).then((res) => {
|
|
157
166
|
console.log('Request complete! response:', res);
|
|
158
167
|
});
|
|
@@ -162,7 +171,7 @@ export async function upload(fields, e) {
|
|
|
162
171
|
allowProcess: true,
|
|
163
172
|
allowRemove: true,
|
|
164
173
|
allowRevert: true,
|
|
165
|
-
maxParallelUploads:
|
|
174
|
+
maxParallelUploads: 25,
|
|
166
175
|
|
|
167
176
|
imageCropAspectRatio: 1,
|
|
168
177
|
imageResizeTargetWidth: 200,
|
|
@@ -407,7 +416,7 @@ export async function upload(fields, e) {
|
|
|
407
416
|
opt.styleButtonProcessItemPosition = 'right bottom';
|
|
408
417
|
}
|
|
409
418
|
|
|
410
|
-
pond = FilePond.create(
|
|
419
|
+
pond = FilePond.create(imgInp, opt);
|
|
411
420
|
|
|
412
421
|
pond.on('processfiles', (e) => {
|
|
413
422
|
do_callback();
|
|
@@ -433,6 +442,10 @@ export async function upload(fields, e) {
|
|
|
433
442
|
|
|
434
443
|
export async function viewer(fields, e) {
|
|
435
444
|
const { app_id, gtp_token, app_token, api_key } = e._session;
|
|
445
|
+
if (!e.container_node) {
|
|
446
|
+
e.report_error?.('xuda-drive: viewer skipped — host provided no container_node (element not yet in DOM, e.g. a collapsed section)', true);
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
436
449
|
|
|
437
450
|
var sepcialLoader = {
|
|
438
451
|
template: `
|
|
@@ -633,7 +646,7 @@ export async function viewer(fields, e) {
|
|
|
633
646
|
|
|
634
647
|
<div>
|
|
635
648
|
<button
|
|
636
|
-
@click="
|
|
649
|
+
@click="uploadModal = true"
|
|
637
650
|
type="button"
|
|
638
651
|
class="inline-flex items-center justify-center px-4 py-2 text-sm font-medium tracking-wide text-white transition-colors duration-200 rounded-md bg-neutral-950 hover:bg-neutral-900 focus:ring-2 focus:ring-offset-2 focus:ring-neutral-900 focus:shadow-outline focus:outline-none"
|
|
639
652
|
>
|
|
@@ -751,137 +764,129 @@ export async function viewer(fields, e) {
|
|
|
751
764
|
</div>
|
|
752
765
|
</div>
|
|
753
766
|
|
|
754
|
-
<!-- Hidden native file input -->
|
|
755
|
-
<input type="file" ref="fileInput" :multiple="options.file_upload_allow_multiple_files" :accept="acceptTypes" class="hidden" @change="onFilesSelected" />
|
|
756
|
-
|
|
757
|
-
<!-- Consent override modal -->
|
|
758
767
|
<teleport to="body">
|
|
759
|
-
<div @keydown.escape.window="
|
|
760
|
-
<div v-show="
|
|
768
|
+
<div @keydown.escape.window="uploadModal = false" v-show="uploadModal" class="fixed top-0 left-0 z-[99] w-screen h-screen" x-cloak>
|
|
769
|
+
<div v-show="uploadModal" @click="uploadModal=false" class="absolute inset-0 w-full h-full bg-white backdrop-blur-sm bg-opacity-70"></div>
|
|
770
|
+
|
|
761
771
|
<transition key="wrapper" enter-from-class="opacity-0" enter-active-class="ease-out duration-300" enter-to-class="opacity-100" leave-from-class="opacity-100" leave-active-class="ease-in duration-300" leave-to-class="opacity-0">
|
|
762
|
-
<div class="flex items-center justify-center size-full" v-show="
|
|
763
|
-
<transition
|
|
764
|
-
|
|
772
|
+
<div class="flex items-center justify-center size-full" v-show="uploadModal">
|
|
773
|
+
<transition
|
|
774
|
+
key="inner"
|
|
775
|
+
enter-active-class="ease-out duration-300"
|
|
776
|
+
enter-from-class="opacity-0 -translate-y-2 sm:scale-95"
|
|
777
|
+
enter-to-class="opacity-100 translate-y-0 sm:scale-100"
|
|
778
|
+
leave-active-class="ease-in duration-200"
|
|
779
|
+
leave-from-class="opacity-100 translate-y-0 sm:scale-100"
|
|
780
|
+
leave-to-class="opacity-0 -translate-y-2 sm:scale-95"
|
|
781
|
+
>
|
|
782
|
+
<div v-show="uploadModal" class="relative w-full py-6 bg-white border shadow-lg px-7 border-neutral-200 sm:max-w-lg sm:rounded-lg overflow-hidden">
|
|
765
783
|
<div class="flex items-center justify-between pb-3">
|
|
766
|
-
<h3 class="text-lg font-semibold"
|
|
767
|
-
<button @click="
|
|
784
|
+
<h3 class="text-lg font-semibold" v-text="'Upload file' + (options.file_upload_allow_multiple_files ? 's' : '')"></h3>
|
|
785
|
+
<button @click="uploadModal=false" class="absolute top-0 right-0 flex items-center justify-center w-8 h-8 mt-5 mr-5 text-gray-600 rounded-full hover:text-gray-800 hover:bg-gray-50">
|
|
768
786
|
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /></svg>
|
|
769
787
|
</button>
|
|
770
788
|
</div>
|
|
771
|
-
<div class="
|
|
772
|
-
|
|
773
|
-
<
|
|
774
|
-
<div class="mt-6 space-y-5 pl-2">
|
|
775
|
-
<div v-for="option in replaceOptions" :key="option.value">
|
|
776
|
-
<label :for="option.value" class="flex items-center cursor-pointer">
|
|
777
|
-
<input :id="option.value" name="notification-method" type="radio" :value="option.value" v-model="overrideAction" :checked="option.value === overrideAction" class="option-radio peer" />
|
|
778
|
-
<div class="ml-3 block font-medium peer-checked:text-black text-light-700 cursor-pointer">{{ option.label }}</div>
|
|
779
|
-
</label>
|
|
780
|
-
</div>
|
|
781
|
-
</div>
|
|
782
|
-
</fieldset>
|
|
783
|
-
</div>
|
|
784
|
-
<div class="flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2 mt-6">
|
|
785
|
-
<button @click="showExistModal=false" type="button" class="inline-flex gap-2 items-center justify-center h-10 px-4 py-2 text-sm font-medium transition-colors border rounded-md focus:outline-none focus:ring-2 focus:ring-neutral-100 focus:ring-offset-2">
|
|
789
|
+
<div id="imageUploaderComponent" class="relative w-auto pb-8 h-48 [&>div]:h-full [&>div>div]:h-full"></div>
|
|
790
|
+
<div class="flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2">
|
|
791
|
+
<button @click="uploadModal=false" type="button" class="inline-flex gap-2 items-center justify-center h-10 px-4 py-2 text-sm font-medium transition-colors border rounded-md focus:outline-none focus:ring-2 focus:ring-neutral-100 focus:ring-offset-2">
|
|
786
792
|
<div>Dismiss</div>
|
|
787
793
|
</button>
|
|
788
|
-
<button
|
|
789
|
-
|
|
794
|
+
<button
|
|
795
|
+
@click="uploadFiles(false)"
|
|
796
|
+
:disabled="uploaderLoading || checkingFiles"
|
|
797
|
+
type="button"
|
|
798
|
+
class="inline-flex items-center gap-2 justify-center h-10 px-4 py-2 text-sm font-medium text-white transition-colors border border-transparent rounded-md focus:outline-none focus:ring-2 focus:ring-neutral-900 focus:ring-offset-2 bg-neutral-950 hover:bg-neutral-900 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
799
|
+
>
|
|
800
|
+
<template v-if="checkingFiles">
|
|
801
|
+
<sepcial-loader class="size-5" />
|
|
802
|
+
Checking files...
|
|
803
|
+
</template>
|
|
804
|
+
|
|
805
|
+
<template v-else-if="uploaderLoading">
|
|
806
|
+
<sepcial-loader class="size-5" />
|
|
807
|
+
Uploading
|
|
808
|
+
</template>
|
|
809
|
+
|
|
810
|
+
<template v-else>Upload</template>
|
|
790
811
|
</button>
|
|
791
812
|
</div>
|
|
813
|
+
|
|
814
|
+
<div v-show="showExistModal" class="absolute inset-0 bg-white py-6 px-7 flex flex-col">
|
|
815
|
+
<div class="flex items-center justify-between pb-3">
|
|
816
|
+
<h3 class="text-lg font-semibold">Upload options</h3>
|
|
817
|
+
<button @click="showExistModal=false" class="absolute top-0 right-0 flex items-center justify-center w-8 h-8 mt-5 mr-5 text-gray-600 rounded-full hover:text-gray-800 hover:bg-gray-50">
|
|
818
|
+
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /></svg>
|
|
819
|
+
</button>
|
|
820
|
+
</div>
|
|
821
|
+
|
|
822
|
+
<div class="flex-1">
|
|
823
|
+
<p class="text-gray-800">One or more items already exists in this location. Do you want to replace the existing items with a new version or keep both items? Replacing the items won't change sharing settings.</p>
|
|
824
|
+
|
|
825
|
+
<fieldset>
|
|
826
|
+
<div class="mt-6 space-y-5 pl-2">
|
|
827
|
+
<div v-for="option in replaceOptions" :key="option.value">
|
|
828
|
+
<label :for="option.value" class="flex items-center cursor-pointer">
|
|
829
|
+
<input :id="option.value" name="notification-method" type="radio" :value="option.value" v-model="overrideAction" :checked="option.value === overrideAction" class="option-radio peer" />
|
|
830
|
+
<div class="ml-3 block font-medium peer-checked:text-black text-light-700 cursor-pointer">{{ option.label }}</div>
|
|
831
|
+
</label>
|
|
832
|
+
</div>
|
|
833
|
+
</div>
|
|
834
|
+
</fieldset>
|
|
835
|
+
</div>
|
|
836
|
+
|
|
837
|
+
<div class="flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2">
|
|
838
|
+
<button @click="showExistModal=false" type="button" class="inline-flex gap-2 items-center justify-center h-10 px-4 py-2 text-sm font-medium transition-colors border rounded-md focus:outline-none focus:ring-2 focus:ring-neutral-100 focus:ring-offset-2">
|
|
839
|
+
<div>Dismiss</div>
|
|
840
|
+
</button>
|
|
841
|
+
<button
|
|
842
|
+
@click="uploadFiles(true)"
|
|
843
|
+
:disabled="uploaderLoading"
|
|
844
|
+
type="button"
|
|
845
|
+
class="inline-flex items-center gap-2 justify-center h-10 px-4 py-2 text-sm font-medium text-white transition-colors border border-transparent rounded-md focus:outline-none focus:ring-2 focus:ring-neutral-900 focus:ring-offset-2 bg-neutral-950 hover:bg-neutral-900 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
846
|
+
>
|
|
847
|
+
Upload
|
|
848
|
+
</button>
|
|
849
|
+
</div>
|
|
850
|
+
</div>
|
|
851
|
+
|
|
852
|
+
<div v-show="checkerFailedModal" class="absolute inset-0 bg-white py-6 px-7 flex flex-col">
|
|
853
|
+
<div class="flex items-center justify-between pb-3">
|
|
854
|
+
<h3 class="text-lg font-semibold">Checker failed</h3>
|
|
855
|
+
<button @click="checkerFailedModal=false" class="absolute top-0 right-0 flex items-center justify-center w-8 h-8 mt-5 mr-5 text-gray-600 rounded-full hover:text-gray-800 hover:bg-gray-50">
|
|
856
|
+
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /></svg>
|
|
857
|
+
</button>
|
|
858
|
+
</div>
|
|
859
|
+
|
|
860
|
+
<div class="flex-1">
|
|
861
|
+
<p class="text-gray-800">We couldn't complete your file upload due to a network error. This might be due to a temporary connection issue.
|
|
862
|
+
|
|
863
|
+
</p>
|
|
864
|
+
|
|
865
|
+
|
|
866
|
+
</div>
|
|
867
|
+
|
|
868
|
+
<div class="flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2">
|
|
869
|
+
<button @click="checkerFailedModal=false" type="button" class="inline-flex gap-2 items-center justify-center h-10 px-4 py-2 text-sm font-medium transition-colors border rounded-md focus:outline-none focus:ring-2 focus:ring-neutral-100 focus:ring-offset-2">
|
|
870
|
+
<div>Dismiss</div>
|
|
871
|
+
</button>
|
|
872
|
+
<button
|
|
873
|
+
@click="uploadFiles(false), (checkerFailedModal = false)"
|
|
874
|
+
:disabled="uploaderLoading"
|
|
875
|
+
type="button"
|
|
876
|
+
class="inline-flex items-center gap-2 justify-center h-10 px-4 py-2 text-sm font-medium text-white transition-colors border border-transparent rounded-md focus:outline-none focus:ring-2 focus:ring-neutral-900 focus:ring-offset-2 bg-neutral-950 hover:bg-neutral-900 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
877
|
+
>
|
|
878
|
+
Retry
|
|
879
|
+
</button>
|
|
880
|
+
</div>
|
|
881
|
+
</div>
|
|
882
|
+
|
|
883
|
+
|
|
884
|
+
|
|
792
885
|
</div>
|
|
793
886
|
</transition>
|
|
794
887
|
</div>
|
|
795
888
|
</transition>
|
|
796
889
|
</div>
|
|
797
|
-
|
|
798
|
-
<!-- Upload job window (Google Drive style) -->
|
|
799
|
-
<div v-if="uploadJobs.length || checkingFiles" class="fixed bottom-4 right-4 z-[100] w-96 bg-white rounded-lg shadow-2xl border border-neutral-200 overflow-hidden" style="font-family: system-ui, -apple-system, sans-serif;">
|
|
800
|
-
<!-- Header -->
|
|
801
|
-
<div class="flex items-center justify-between px-4 py-3 bg-neutral-900 text-white cursor-pointer" @click="jobWindowCollapsed = !jobWindowCollapsed">
|
|
802
|
-
<div class="flex items-center gap-2.5 font-medium text-sm">
|
|
803
|
-
<!-- Circular progress -->
|
|
804
|
-
<svg v-if="uploadJobs.length && !checkingFiles && uploadsInProgress > 0" class="w-5 h-5 -rotate-90 shrink-0" viewBox="0 0 20 20">
|
|
805
|
-
<circle cx="10" cy="10" r="8" fill="none" stroke="currentColor" stroke-width="2.5" opacity="0.2" />
|
|
806
|
-
<circle cx="10" cy="10" r="8" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" :stroke-dasharray="50.265" :stroke-dashoffset="50.265 - (50.265 * overallProgress / 100)" class="transition-all duration-300" />
|
|
807
|
-
</svg>
|
|
808
|
-
<div>
|
|
809
|
-
<template v-if="checkingFiles">Checking {{ checkingFilesCount }} file{{ checkingFilesCount > 1 ? 's' : '' }}</template>
|
|
810
|
-
<template v-else-if="uploadsInProgress > 0">Uploading {{ uploadsInProgress }} item{{ uploadsInProgress > 1 ? 's' : '' }}<template v-if="uploadsFailed">, {{ uploadsFailed }} failed</template></template>
|
|
811
|
-
<template v-else>
|
|
812
|
-
{{ uploadsDone }} uploaded<template v-if="uploadsFailed">, {{ uploadsFailed }} failed</template>
|
|
813
|
-
</template>
|
|
814
|
-
</div>
|
|
815
|
-
</div>
|
|
816
|
-
<div class="flex items-center gap-2">
|
|
817
|
-
<button @click.stop="jobWindowCollapsed = !jobWindowCollapsed" class="text-white/70 hover:text-white">
|
|
818
|
-
<svg :class="{ 'rotate-180': jobWindowCollapsed }" class="w-5 h-5 transition-transform" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5" /></svg>
|
|
819
|
-
</button>
|
|
820
|
-
<button v-if="uploadsInProgress === 0 && !checkingFiles" @click.stop="uploadJobs = []" class="text-white/70 hover:text-white">
|
|
821
|
-
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /></svg>
|
|
822
|
-
</button>
|
|
823
|
-
</div>
|
|
824
|
-
</div>
|
|
825
|
-
<!-- Failed summary bar -->
|
|
826
|
-
<div v-if="uploadsFailed > 0 && uploadsInProgress === 0 && !jobWindowCollapsed" class="flex items-center justify-between px-4 py-2 bg-red-50 border-b border-red-100">
|
|
827
|
-
<div class="flex items-center gap-2 text-red-600 text-sm font-medium">
|
|
828
|
-
<svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path fill-rule="evenodd" d="M12 2.25c-5.385 0-9.75 4.365-9.75 9.75s4.365 9.75 9.75 9.75 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25zm-1.72 6.97a.75.75 0 10-1.06 1.06L10.94 12l-1.72 1.72a.75.75 0 101.06 1.06L12 13.06l1.72 1.72a.75.75 0 101.06-1.06L13.06 12l1.72-1.72a.75.75 0 10-1.06-1.06L12 10.94l-1.72-1.72z" clip-rule="evenodd" /></svg>
|
|
829
|
-
{{ uploadsFailed }} failed
|
|
830
|
-
</div>
|
|
831
|
-
<div class="flex items-center gap-1.5">
|
|
832
|
-
<!-- Navigate up/down between failed items -->
|
|
833
|
-
<button @click.stop="navigateFailed(-1)" class="p-0.5 rounded hover:bg-red-100 text-red-500 transition-colors">
|
|
834
|
-
<svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M4.5 15.75l7.5-7.5 7.5 7.5" /></svg>
|
|
835
|
-
</button>
|
|
836
|
-
<button @click.stop="navigateFailed(1)" class="p-0.5 rounded hover:bg-red-100 text-red-500 transition-colors">
|
|
837
|
-
<svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5" /></svg>
|
|
838
|
-
</button>
|
|
839
|
-
<!-- Retry all failed -->
|
|
840
|
-
<button @click.stop="retryAllFailed" class="text-xs text-red-600 hover:text-red-800 font-medium px-2 py-1 rounded hover:bg-red-100 transition-colors">
|
|
841
|
-
Retry all
|
|
842
|
-
</button>
|
|
843
|
-
</div>
|
|
844
|
-
</div>
|
|
845
|
-
<!-- File list -->
|
|
846
|
-
<div v-show="!jobWindowCollapsed" ref="jobListRef" class="max-h-64 overflow-y-auto divide-y divide-neutral-100">
|
|
847
|
-
<!-- Checking files row -->
|
|
848
|
-
<div v-if="checkingFiles" class="flex items-center gap-3 px-4 py-2.5">
|
|
849
|
-
<div class="shrink-0">
|
|
850
|
-
<sepcial-loader class="size-5 text-neutral-500" />
|
|
851
|
-
</div>
|
|
852
|
-
<div class="flex-1 min-w-0">
|
|
853
|
-
<div class="text-sm">Checking {{ checkingFilesCount }} file{{ checkingFilesCount > 1 ? 's' : '' }} for duplicates...</div>
|
|
854
|
-
</div>
|
|
855
|
-
</div>
|
|
856
|
-
<!-- Upload jobs -->
|
|
857
|
-
<div v-for="job in uploadJobs" :key="job.id" :ref="'job-' + job.id" class="flex items-center gap-3 px-4 py-2.5">
|
|
858
|
-
<div class="shrink-0">
|
|
859
|
-
<!-- Pending (queued) -->
|
|
860
|
-
<svg v-if="job.status === 'pending'" class="w-5 h-5 text-neutral-300" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" /></svg>
|
|
861
|
-
<!-- Uploading spinner -->
|
|
862
|
-
<svg v-else-if="job.status === 'uploading'" class="w-5 h-5 text-neutral-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5m-13.5-9L12 3m0 0l4.5 4.5M12 3v13.5" /></svg>
|
|
863
|
-
<!-- Success icon -->
|
|
864
|
-
<svg v-else-if="job.status === 'done'" class="w-5 h-5 text-green-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path fill-rule="evenodd" d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12zm13.36-1.814a.75.75 0 10-1.22-.872l-3.236 4.53L9.53 12.22a.75.75 0 00-1.06 1.06l2.25 2.25a.75.75 0 001.14-.094l3.75-5.25z" clip-rule="evenodd" /></svg>
|
|
865
|
-
<!-- Error icon -->
|
|
866
|
-
<svg v-else-if="job.status === 'error'" class="w-5 h-5 text-red-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path fill-rule="evenodd" d="M12 2.25c-5.385 0-9.75 4.365-9.75 9.75s4.365 9.75 9.75 9.75 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25zm-1.72 6.97a.75.75 0 10-1.06 1.06L10.94 12l-1.72 1.72a.75.75 0 101.06 1.06L12 13.06l1.72 1.72a.75.75 0 101.06-1.06L13.06 12l1.72-1.72a.75.75 0 10-1.06-1.06L12 10.94l-1.72-1.72z" clip-rule="evenodd" /></svg>
|
|
867
|
-
</div>
|
|
868
|
-
<div class="flex-1 min-w-0">
|
|
869
|
-
<div class="text-sm truncate" v-text="job.name"></div>
|
|
870
|
-
<!-- Progress bar -->
|
|
871
|
-
<div v-if="job.status === 'uploading'" class="mt-1 h-1 bg-neutral-100 rounded-full overflow-hidden">
|
|
872
|
-
<div class="h-full bg-blue-500 rounded-full transition-all duration-300" :style="{ width: job.progress + '%' }"></div>
|
|
873
|
-
</div>
|
|
874
|
-
</div>
|
|
875
|
-
<div class="shrink-0 flex items-center gap-1">
|
|
876
|
-
<span v-if="job.status === 'uploading'" class="text-xs text-neutral-400">{{ Math.round(job.progress) }}%</span>
|
|
877
|
-
<!-- Retry button for failed uploads -->
|
|
878
|
-
<button v-if="job.status === 'error'" @click.stop="retryUpload(job)" class="text-xs text-blue-500 hover:text-blue-700 font-medium px-1.5 py-0.5 rounded hover:bg-blue-50 transition-colors">
|
|
879
|
-
Retry
|
|
880
|
-
</button>
|
|
881
|
-
</div>
|
|
882
|
-
</div>
|
|
883
|
-
</div>
|
|
884
|
-
</div>
|
|
885
890
|
</teleport>
|
|
886
891
|
</div>
|
|
887
892
|
|
|
@@ -929,7 +934,7 @@ export async function viewer(fields, e) {
|
|
|
929
934
|
};
|
|
930
935
|
|
|
931
936
|
var containerId = crypto.randomUUID();
|
|
932
|
-
e
|
|
937
|
+
e.container_node.setAttribute('widget-id', containerId);
|
|
933
938
|
|
|
934
939
|
var app = createApp({
|
|
935
940
|
template: html,
|
|
@@ -1000,21 +1005,18 @@ export async function viewer(fields, e) {
|
|
|
1000
1005
|
folders: [],
|
|
1001
1006
|
files: [],
|
|
1002
1007
|
view: fields.default_view_type || 'gallery',
|
|
1008
|
+
uploadModal: false,
|
|
1009
|
+
uploaderLoading: false,
|
|
1010
|
+
checkingFiles: false,
|
|
1003
1011
|
loadingSearch: false,
|
|
1004
1012
|
fetcher,
|
|
1005
1013
|
formatBytes,
|
|
1014
|
+
initUploader: upload,
|
|
1006
1015
|
swiper: null,
|
|
1007
1016
|
existingTags: '',
|
|
1008
1017
|
showSearchPopper: false,
|
|
1009
1018
|
isOverDropZone: false,
|
|
1010
1019
|
removeChipCount: 0,
|
|
1011
|
-
// Upload job window state
|
|
1012
|
-
uploadJobs: [],
|
|
1013
|
-
jobWindowCollapsed: false,
|
|
1014
|
-
pendingFiles: [],
|
|
1015
|
-
checkingFiles: false,
|
|
1016
|
-
checkingFilesCount: 0,
|
|
1017
|
-
failedNavIndex: -1,
|
|
1018
1020
|
searchOptions: [
|
|
1019
1021
|
{
|
|
1020
1022
|
label: 'Filename',
|
|
@@ -1046,7 +1048,10 @@ export async function viewer(fields, e) {
|
|
|
1046
1048
|
existingFiles: {},
|
|
1047
1049
|
allLoading: false,
|
|
1048
1050
|
showExistModal: false,
|
|
1051
|
+
checkerFailedModal: false,
|
|
1049
1052
|
overrideAction: 'replace',
|
|
1053
|
+
pond: null,
|
|
1054
|
+
processFiles: null,
|
|
1050
1055
|
};
|
|
1051
1056
|
},
|
|
1052
1057
|
methods: {
|
|
@@ -1093,189 +1098,67 @@ export async function viewer(fields, e) {
|
|
|
1093
1098
|
refreshFsLightbox();
|
|
1094
1099
|
});
|
|
1095
1100
|
},
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
if (!files.length) return;
|
|
1099
|
-
this.pendingFiles = files;
|
|
1100
|
-
// Reset the input so re-selecting the same files works
|
|
1101
|
-
event.target.value = '';
|
|
1102
|
-
this.checkAndUpload();
|
|
1103
|
-
},
|
|
1104
|
-
checkAndUpload() {
|
|
1105
|
-
this.existingFiles = {};
|
|
1106
|
-
this.checkingFiles = true;
|
|
1107
|
-
this.checkingFilesCount = this.pendingFiles.length;
|
|
1108
|
-
this.jobWindowCollapsed = false;
|
|
1109
|
-
var file_paths = this.pendingFiles.map((f) => '/' + f.name);
|
|
1110
|
-
|
|
1111
|
-
this.fetcher('check_drive_files_workspace', {
|
|
1112
|
-
file_paths,
|
|
1113
|
-
type: 'file',
|
|
1114
|
-
})
|
|
1115
|
-
.then(() => {
|
|
1116
|
-
this.checkingFiles = false;
|
|
1117
|
-
// No conflicts — upload all
|
|
1118
|
-
this.startUpload(false);
|
|
1119
|
-
})
|
|
1120
|
-
.catch((err) => {
|
|
1121
|
-
this.checkingFiles = false;
|
|
1122
|
-
if (err.code === -764) {
|
|
1123
|
-
(err.files || []).forEach((file, index) => {
|
|
1124
|
-
if (file.code === -764) {
|
|
1125
|
-
this.existingFiles[this.pendingFiles[index].name] = file.duplicates;
|
|
1126
|
-
}
|
|
1127
|
-
});
|
|
1101
|
+
uploadFiles(is_after_confirm) {
|
|
1102
|
+
this.uploaderLoading = true;
|
|
1128
1103
|
|
|
1129
|
-
|
|
1130
|
-
this.showExistModal = true;
|
|
1131
|
-
} else {
|
|
1132
|
-
this.startUpload(false);
|
|
1133
|
-
}
|
|
1134
|
-
} else {
|
|
1135
|
-
// Network error — upload anyway, server will handle it
|
|
1136
|
-
this.startUpload(false);
|
|
1137
|
-
}
|
|
1138
|
-
});
|
|
1139
|
-
},
|
|
1140
|
-
startUpload(isAfterConsent) {
|
|
1141
|
-
if (isAfterConsent) {
|
|
1104
|
+
if (is_after_confirm) {
|
|
1142
1105
|
this.showExistModal = false;
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
this.jobWindowCollapsed = false;
|
|
1148
|
-
|
|
1149
|
-
filesToUpload.forEach((file) => {
|
|
1150
|
-
const jobId = crypto.randomUUID();
|
|
1151
|
-
const isUpdate = this.overrideAction === 'replace' && file.name in this.existingFiles;
|
|
1152
|
-
this.uploadJobs.push({
|
|
1153
|
-
id: jobId,
|
|
1154
|
-
name: file.name,
|
|
1155
|
-
progress: 0,
|
|
1156
|
-
status: 'pending',
|
|
1157
|
-
file: file,
|
|
1158
|
-
isUpdate: isUpdate,
|
|
1106
|
+
// this.processCb();
|
|
1107
|
+
this.processFiles(null, {
|
|
1108
|
+
overrideAction: this.overrideAction,
|
|
1109
|
+
existingFiles: this.existingFiles,
|
|
1159
1110
|
});
|
|
1160
|
-
});
|
|
1161
1111
|
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
const active = this.uploadJobs.filter(j => j.status === 'uploading').length;
|
|
1167
|
-
const pending = this.uploadJobs.filter(j => j.status === 'pending');
|
|
1168
|
-
const slotsAvailable = MAX_CONCURRENT - active;
|
|
1169
|
-
|
|
1170
|
-
for (let i = 0; i < Math.min(slotsAvailable, pending.length); i++) {
|
|
1171
|
-
const job = pending[i];
|
|
1172
|
-
job.status = 'uploading';
|
|
1173
|
-
this.uploadSingleFile(job.file, job.id, job.isUpdate);
|
|
1174
|
-
}
|
|
1175
|
-
},
|
|
1176
|
-
uploadSingleFile(file, jobId, isUpdate) {
|
|
1177
|
-
let formData = new FormData();
|
|
1178
|
-
formData.append('file', file, file.name);
|
|
1179
|
-
|
|
1180
|
-
const { app_id: appId, gtp_token, app_token, api_key } = e._session;
|
|
1181
|
-
formData.append('app_id_reference', APP_OBJ?.[appId]?.app_id_reference);
|
|
1182
|
-
formData.append('app_id', appId);
|
|
1183
|
-
formData.append('gtp_token', gtp_token);
|
|
1184
|
-
formData.append('app_token', app_token);
|
|
1185
|
-
formData.append('drive_type', 'workspace');
|
|
1186
|
-
if (api_key) formData.append('api_key', api_key);
|
|
1187
|
-
|
|
1188
|
-
if (fields.file_upload_folder) formData.append('folder', fields.file_upload_folder);
|
|
1189
|
-
if (fields.public_file) formData.append('make_public', fields.public_file);
|
|
1190
|
-
|
|
1191
|
-
let tags = fields.assign_file_tags || [];
|
|
1192
|
-
let file_tags = [];
|
|
1193
|
-
if (fields.auto_tag_generator) {
|
|
1194
|
-
let identifier = fields.auto_tag_identifier || ' ';
|
|
1195
|
-
const filename = file.name.split('.').slice(0, -1).join('.');
|
|
1196
|
-
file_tags = filename.split(identifier);
|
|
1197
|
-
}
|
|
1198
|
-
formData.append('tags', [...tags, ...file_tags]);
|
|
1199
|
-
|
|
1200
|
-
const request = new XMLHttpRequest();
|
|
1201
|
-
|
|
1202
|
-
let _domain = e._session.domain;
|
|
1203
|
-
if (e._session.is_deployment) {
|
|
1204
|
-
_domain = e._session.opt.regional_server === 'dev.xuda.ai' ? 'dev.xuda.ai' : 'xuda.ai';
|
|
1205
|
-
}
|
|
1112
|
+
// this.pond.on("processfile", (e) => {
|
|
1113
|
+
// this.uploaderLoading = false;
|
|
1114
|
+
// this.uploadModal = false;
|
|
1115
|
+
// this.pond.removeFiles();
|
|
1206
1116
|
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
formData.append('file_name', file.name);
|
|
1210
|
-
request.open('POST', 'https://' + _domain + '/cpi/update_drive_file_workspace');
|
|
1117
|
+
// this.refreshDirectory();
|
|
1118
|
+
// });
|
|
1211
1119
|
} else {
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
request.setRequestHeader('xu-gtp-token', gtp_token);
|
|
1216
|
-
request.setRequestHeader('xu-app-token', app_token);
|
|
1120
|
+
this.existingFiles = {};
|
|
1121
|
+
this.checkingFiles = true;
|
|
1217
1122
|
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
const job = this.uploadJobs.find((j) => j.id === jobId);
|
|
1221
|
-
if (job) job.progress = (ev.loaded / ev.total) * 100;
|
|
1222
|
-
}
|
|
1223
|
-
};
|
|
1224
|
-
|
|
1225
|
-
const onComplete = () => {
|
|
1226
|
-
this.processQueue();
|
|
1227
|
-
const hasPending = this.uploadJobs.some(j => j.status === 'pending');
|
|
1228
|
-
if (this.uploadsInProgress === 0 && !hasPending) {
|
|
1229
|
-
this.refreshDirectory(false, true);
|
|
1230
|
-
}
|
|
1231
|
-
};
|
|
1123
|
+
var filesMapped = this.pond.getFiles().map((e) => ({ id: e.id, filename: e.file.name }));
|
|
1124
|
+
var file_paths = filesMapped.map(({ filename }) => '/' + filename);
|
|
1232
1125
|
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
} else {
|
|
1240
|
-
if (job) job.status = 'error';
|
|
1241
|
-
}
|
|
1242
|
-
} catch (_e) {
|
|
1243
|
-
if (job) job.status = 'error';
|
|
1244
|
-
}
|
|
1245
|
-
onComplete();
|
|
1246
|
-
};
|
|
1126
|
+
this.fetcher(`check_drive_files_workspace`, {
|
|
1127
|
+
file_paths,
|
|
1128
|
+
type: 'file',
|
|
1129
|
+
})
|
|
1130
|
+
.then(() => {
|
|
1131
|
+
this.checkingFiles = false;
|
|
1247
1132
|
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1133
|
+
// No conflicts — process all files
|
|
1134
|
+
filesMapped.forEach((e) => {
|
|
1135
|
+
this.processFiles(e.id);
|
|
1136
|
+
});
|
|
1137
|
+
})
|
|
1138
|
+
.catch((err) => {
|
|
1139
|
+
this.checkingFiles = false;
|
|
1140
|
+
|
|
1141
|
+
if (err.code === -764) {
|
|
1142
|
+
(err.files || []).forEach((file, index) => {
|
|
1143
|
+
if (file.code === -764) {
|
|
1144
|
+
this.existingFiles[filesMapped[index].filename] = file.duplicates;
|
|
1145
|
+
}
|
|
1146
|
+
});
|
|
1147
|
+
|
|
1148
|
+
if (Object.keys(this.existingFiles).length) {
|
|
1149
|
+
this.showExistModal = true;
|
|
1150
|
+
}
|
|
1253
1151
|
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
job.progress = 0;
|
|
1265
|
-
});
|
|
1266
|
-
this.processQueue();
|
|
1267
|
-
},
|
|
1268
|
-
navigateFailed(direction) {
|
|
1269
|
-
const failedJobs = this.uploadJobs.filter(j => j.status === 'error');
|
|
1270
|
-
if (!failedJobs.length) return;
|
|
1271
|
-
this.failedNavIndex = (this.failedNavIndex + direction + failedJobs.length) % failedJobs.length;
|
|
1272
|
-
const targetJob = failedJobs[this.failedNavIndex];
|
|
1273
|
-
const refEl = this.$refs['job-' + targetJob.id];
|
|
1274
|
-
const el = Array.isArray(refEl) ? refEl[0] : refEl;
|
|
1275
|
-
if (el && this.$refs.jobListRef) {
|
|
1276
|
-
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
1277
|
-
el.classList.add('bg-red-50');
|
|
1278
|
-
setTimeout(() => el.classList.remove('bg-red-50'), 1500);
|
|
1152
|
+
// Process non-conflicting files
|
|
1153
|
+
filesMapped
|
|
1154
|
+
.filter((e) => !Object.keys(this.existingFiles).includes(e.filename))
|
|
1155
|
+
.forEach((e) => {
|
|
1156
|
+
this.processFiles(e.id);
|
|
1157
|
+
});
|
|
1158
|
+
} else {
|
|
1159
|
+
this.checkerFailedModal = true;
|
|
1160
|
+
}
|
|
1161
|
+
});
|
|
1279
1162
|
}
|
|
1280
1163
|
},
|
|
1281
1164
|
|
|
@@ -1308,10 +1191,10 @@ export async function viewer(fields, e) {
|
|
|
1308
1191
|
moment,
|
|
1309
1192
|
},
|
|
1310
1193
|
mounted() {
|
|
1311
|
-
var onDrop = (
|
|
1194
|
+
var onDrop = (files) => {
|
|
1312
1195
|
if (this.view === 'slider') return;
|
|
1313
|
-
this.
|
|
1314
|
-
this.
|
|
1196
|
+
this.uploadModal = true;
|
|
1197
|
+
this.pond.addFiles(files);
|
|
1315
1198
|
};
|
|
1316
1199
|
|
|
1317
1200
|
const { isOverDropZone } = useDropZone(this.$refs.dropZoneRef, {
|
|
@@ -1321,31 +1204,6 @@ export async function viewer(fields, e) {
|
|
|
1321
1204
|
this.isOverDropZone = isOverDropZone;
|
|
1322
1205
|
},
|
|
1323
1206
|
computed: {
|
|
1324
|
-
uploadsInProgress() {
|
|
1325
|
-
return this.uploadJobs.filter((j) => j.status === 'uploading' || j.status === 'pending').length;
|
|
1326
|
-
},
|
|
1327
|
-
uploadsDone() {
|
|
1328
|
-
return this.uploadJobs.filter(j => j.status === 'done').length;
|
|
1329
|
-
},
|
|
1330
|
-
uploadsFailed() {
|
|
1331
|
-
return this.uploadJobs.filter(j => j.status === 'error').length;
|
|
1332
|
-
},
|
|
1333
|
-
overallProgress() {
|
|
1334
|
-
if (!this.uploadJobs.length) return 0;
|
|
1335
|
-
return this.uploadJobs.reduce((sum, j) => sum + j.progress, 0) / this.uploadJobs.length;
|
|
1336
|
-
},
|
|
1337
|
-
acceptTypes() {
|
|
1338
|
-
if (fields.file_upload_mime_type_preset) {
|
|
1339
|
-
switch (fields.file_upload_mime_type_preset) {
|
|
1340
|
-
case 'image': return 'image/*';
|
|
1341
|
-
case 'video': return 'video/*';
|
|
1342
|
-
case 'audio': return 'audio/*';
|
|
1343
|
-
default: return 'image/*';
|
|
1344
|
-
}
|
|
1345
|
-
}
|
|
1346
|
-
if (fields.file_upload_custom_mime_types) return fields.file_upload_custom_mime_types;
|
|
1347
|
-
return null;
|
|
1348
|
-
},
|
|
1349
1207
|
searchTags() {
|
|
1350
1208
|
return this.opts.search_string.split(' ').filter((e) => e.includes(':'));
|
|
1351
1209
|
},
|
|
@@ -1424,6 +1282,43 @@ export async function viewer(fields, e) {
|
|
|
1424
1282
|
async created() {
|
|
1425
1283
|
this.refreshDirectory();
|
|
1426
1284
|
|
|
1285
|
+
this.$nextTick(async () => {
|
|
1286
|
+
// debugger;
|
|
1287
|
+
const uploaderContainer = e.container_node.querySelector('#imageUploaderComponent');
|
|
1288
|
+
if (!uploaderContainer) return; // viewer-only (e.g. a read-only diagram) — no uploader element to init
|
|
1289
|
+
var { pond, processFiles } = await this.initUploader(
|
|
1290
|
+
{ ...fields, instant_file_upload: 'N' },
|
|
1291
|
+
{
|
|
1292
|
+
...e,
|
|
1293
|
+
container_node: uploaderContainer,
|
|
1294
|
+
container_data: uploaderContainer ? func.runtime.ui.get_data(uploaderContainer) : {},
|
|
1295
|
+
},
|
|
1296
|
+
);
|
|
1297
|
+
|
|
1298
|
+
this.pond = pond;
|
|
1299
|
+
this.processFiles = processFiles;
|
|
1300
|
+
|
|
1301
|
+
// Avoid per-file DOM removal — let processfiles handle cleanup in bulk
|
|
1302
|
+
this.pond.on('processfile', (e, { id }) => {
|
|
1303
|
+
// if (!e?.type !== 'error') {
|
|
1304
|
+
// this.pond.removeFiles(id);
|
|
1305
|
+
// }
|
|
1306
|
+
|
|
1307
|
+
// if (!this.pond.getFiles().length) {
|
|
1308
|
+
// this.uploaderLoading = false;
|
|
1309
|
+
// this.uploadModal = false;
|
|
1310
|
+
// this.pond.removeFiles();
|
|
1311
|
+
// }
|
|
1312
|
+
});
|
|
1313
|
+
|
|
1314
|
+
this.pond.on('processfiles', () => {
|
|
1315
|
+
this.uploaderLoading = false;
|
|
1316
|
+
this.uploadModal = false;
|
|
1317
|
+
this.pond.removeFiles();
|
|
1318
|
+
this.refreshDirectory(false, true);
|
|
1319
|
+
});
|
|
1320
|
+
});
|
|
1321
|
+
|
|
1427
1322
|
this.$watch('opts', (value) => {
|
|
1428
1323
|
this.refreshDirectory();
|
|
1429
1324
|
});
|