@xuda.io/xuda-widget-plugin-xuda-drive 1.0.136 → 1.0.138
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/runtime.mjs +4229 -4166
- package/package.json +1 -1
- package/src/runtime.mjs +92 -12
package/package.json
CHANGED
package/src/runtime.mjs
CHANGED
|
@@ -796,27 +796,60 @@ export async function viewer(fields, e) {
|
|
|
796
796
|
</div>
|
|
797
797
|
|
|
798
798
|
<!-- Upload job window (Google Drive style) -->
|
|
799
|
-
<div v-if="uploadJobs.length" 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;">
|
|
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
800
|
<!-- Header -->
|
|
801
801
|
<div class="flex items-center justify-between px-4 py-3 bg-neutral-900 text-white cursor-pointer" @click="jobWindowCollapsed = !jobWindowCollapsed">
|
|
802
802
|
<div class="font-medium text-sm">
|
|
803
|
-
<template v-if="
|
|
804
|
-
<template v-else>{{
|
|
803
|
+
<template v-if="checkingFiles">Checking {{ checkingFilesCount }} file{{ checkingFilesCount > 1 ? 's' : '' }}</template>
|
|
804
|
+
<template v-else-if="uploadsInProgress > 0">Uploading {{ uploadsInProgress }} item{{ uploadsInProgress > 1 ? 's' : '' }}</template>
|
|
805
|
+
<template v-else>
|
|
806
|
+
{{ uploadsDone }} uploaded<template v-if="uploadsFailed">, {{ uploadsFailed }} failed</template>
|
|
807
|
+
</template>
|
|
805
808
|
</div>
|
|
806
809
|
<div class="flex items-center gap-2">
|
|
807
810
|
<button @click.stop="jobWindowCollapsed = !jobWindowCollapsed" class="text-white/70 hover:text-white">
|
|
808
811
|
<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>
|
|
809
812
|
</button>
|
|
810
|
-
<button v-if="uploadsInProgress === 0" @click.stop="uploadJobs = []" class="text-white/70 hover:text-white">
|
|
813
|
+
<button v-if="uploadsInProgress === 0 && !checkingFiles" @click.stop="uploadJobs = []" class="text-white/70 hover:text-white">
|
|
811
814
|
<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>
|
|
812
815
|
</button>
|
|
813
816
|
</div>
|
|
814
817
|
</div>
|
|
818
|
+
<!-- Failed summary bar -->
|
|
819
|
+
<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">
|
|
820
|
+
<div class="flex items-center gap-2 text-red-600 text-sm font-medium">
|
|
821
|
+
<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>
|
|
822
|
+
{{ uploadsFailed }} failed
|
|
823
|
+
</div>
|
|
824
|
+
<div class="flex items-center gap-1.5">
|
|
825
|
+
<!-- Navigate up/down between failed items -->
|
|
826
|
+
<button @click.stop="navigateFailed(-1)" class="p-0.5 rounded hover:bg-red-100 text-red-500 transition-colors">
|
|
827
|
+
<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>
|
|
828
|
+
</button>
|
|
829
|
+
<button @click.stop="navigateFailed(1)" class="p-0.5 rounded hover:bg-red-100 text-red-500 transition-colors">
|
|
830
|
+
<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>
|
|
831
|
+
</button>
|
|
832
|
+
<!-- Retry all failed -->
|
|
833
|
+
<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">
|
|
834
|
+
Retry all
|
|
835
|
+
</button>
|
|
836
|
+
</div>
|
|
837
|
+
</div>
|
|
815
838
|
<!-- File list -->
|
|
816
|
-
<div v-show="!jobWindowCollapsed" class="max-h-64 overflow-y-auto divide-y divide-neutral-100">
|
|
817
|
-
|
|
839
|
+
<div v-show="!jobWindowCollapsed" ref="jobListRef" class="max-h-64 overflow-y-auto divide-y divide-neutral-100">
|
|
840
|
+
<!-- Checking files row -->
|
|
841
|
+
<div v-if="checkingFiles" class="flex items-center gap-3 px-4 py-2.5">
|
|
842
|
+
<div class="shrink-0">
|
|
843
|
+
<sepcial-loader class="size-5 text-neutral-500" />
|
|
844
|
+
</div>
|
|
845
|
+
<div class="flex-1 min-w-0">
|
|
846
|
+
<div class="text-sm">Checking {{ checkingFilesCount }} file{{ checkingFilesCount > 1 ? 's' : '' }} for duplicates...</div>
|
|
847
|
+
</div>
|
|
848
|
+
</div>
|
|
849
|
+
<!-- Upload jobs -->
|
|
850
|
+
<div v-for="job in uploadJobs" :key="job.id" :ref="'job-' + job.id" class="flex items-center gap-3 px-4 py-2.5">
|
|
818
851
|
<div class="shrink-0">
|
|
819
|
-
<!--
|
|
852
|
+
<!-- Uploading spinner -->
|
|
820
853
|
<svg v-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>
|
|
821
854
|
<!-- Success icon -->
|
|
822
855
|
<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>
|
|
@@ -830,8 +863,12 @@ export async function viewer(fields, e) {
|
|
|
830
863
|
<div class="h-full bg-blue-500 rounded-full transition-all duration-300" :style="{ width: job.progress + '%' }"></div>
|
|
831
864
|
</div>
|
|
832
865
|
</div>
|
|
833
|
-
<div class="shrink-0
|
|
834
|
-
<span v-if="job.status === 'uploading'">{{ Math.round(job.progress) }}%</span>
|
|
866
|
+
<div class="shrink-0 flex items-center gap-1">
|
|
867
|
+
<span v-if="job.status === 'uploading'" class="text-xs text-neutral-400">{{ Math.round(job.progress) }}%</span>
|
|
868
|
+
<!-- Retry button for failed uploads -->
|
|
869
|
+
<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">
|
|
870
|
+
Retry
|
|
871
|
+
</button>
|
|
835
872
|
</div>
|
|
836
873
|
</div>
|
|
837
874
|
</div>
|
|
@@ -966,6 +1003,9 @@ export async function viewer(fields, e) {
|
|
|
966
1003
|
uploadJobs: [],
|
|
967
1004
|
jobWindowCollapsed: false,
|
|
968
1005
|
pendingFiles: [],
|
|
1006
|
+
checkingFiles: false,
|
|
1007
|
+
checkingFilesCount: 0,
|
|
1008
|
+
failedNavIndex: -1,
|
|
969
1009
|
searchOptions: [
|
|
970
1010
|
{
|
|
971
1011
|
label: 'Filename',
|
|
@@ -1054,6 +1094,9 @@ export async function viewer(fields, e) {
|
|
|
1054
1094
|
},
|
|
1055
1095
|
checkAndUpload() {
|
|
1056
1096
|
this.existingFiles = {};
|
|
1097
|
+
this.checkingFiles = true;
|
|
1098
|
+
this.checkingFilesCount = this.pendingFiles.length;
|
|
1099
|
+
this.jobWindowCollapsed = false;
|
|
1057
1100
|
var file_paths = this.pendingFiles.map((f) => '/' + f.name);
|
|
1058
1101
|
|
|
1059
1102
|
this.fetcher('check_drive_files_workspace', {
|
|
@@ -1061,10 +1104,12 @@ export async function viewer(fields, e) {
|
|
|
1061
1104
|
type: 'file',
|
|
1062
1105
|
})
|
|
1063
1106
|
.then(() => {
|
|
1107
|
+
this.checkingFiles = false;
|
|
1064
1108
|
// No conflicts — upload all
|
|
1065
1109
|
this.startUpload(false);
|
|
1066
1110
|
})
|
|
1067
1111
|
.catch((err) => {
|
|
1112
|
+
this.checkingFiles = false;
|
|
1068
1113
|
if (err.code === -764) {
|
|
1069
1114
|
(err.files || []).forEach((file, index) => {
|
|
1070
1115
|
if (file.code === -764) {
|
|
@@ -1094,16 +1139,19 @@ export async function viewer(fields, e) {
|
|
|
1094
1139
|
|
|
1095
1140
|
filesToUpload.forEach((file) => {
|
|
1096
1141
|
const jobId = crypto.randomUUID();
|
|
1142
|
+
const isUpdate = this.overrideAction === 'replace' && file.name in this.existingFiles;
|
|
1097
1143
|
this.uploadJobs.push({
|
|
1098
1144
|
id: jobId,
|
|
1099
1145
|
name: file.name,
|
|
1100
1146
|
progress: 0,
|
|
1101
1147
|
status: 'uploading',
|
|
1148
|
+
file: file,
|
|
1149
|
+
isUpdate: isUpdate,
|
|
1102
1150
|
});
|
|
1103
|
-
this.uploadSingleFile(file, jobId);
|
|
1151
|
+
this.uploadSingleFile(file, jobId, isUpdate);
|
|
1104
1152
|
});
|
|
1105
1153
|
},
|
|
1106
|
-
uploadSingleFile(file, jobId) {
|
|
1154
|
+
uploadSingleFile(file, jobId, isUpdate) {
|
|
1107
1155
|
let formData = new FormData();
|
|
1108
1156
|
formData.append('file', file, file.name);
|
|
1109
1157
|
|
|
@@ -1134,7 +1182,7 @@ export async function viewer(fields, e) {
|
|
|
1134
1182
|
_domain = e._session.opt.regional_server === 'dev.xuda.ai' ? 'dev.xuda.ai' : 'xuda.ai';
|
|
1135
1183
|
}
|
|
1136
1184
|
|
|
1137
|
-
if (
|
|
1185
|
+
if (isUpdate) {
|
|
1138
1186
|
formData.append('file_path', '/' + file.name);
|
|
1139
1187
|
formData.append('file_name', file.name);
|
|
1140
1188
|
request.open('POST', 'https://' + _domain + '/cpi/update_drive_file_workspace');
|
|
@@ -1174,10 +1222,36 @@ export async function viewer(fields, e) {
|
|
|
1174
1222
|
request.onerror = () => {
|
|
1175
1223
|
const job = this.uploadJobs.find((j) => j.id === jobId);
|
|
1176
1224
|
if (job) job.status = 'error';
|
|
1225
|
+
if (this.uploadsInProgress === 0) {
|
|
1226
|
+
this.refreshDirectory(false, true);
|
|
1227
|
+
}
|
|
1177
1228
|
};
|
|
1178
1229
|
|
|
1179
1230
|
request.send(formData);
|
|
1180
1231
|
},
|
|
1232
|
+
retryUpload(job) {
|
|
1233
|
+
job.status = 'uploading';
|
|
1234
|
+
job.progress = 0;
|
|
1235
|
+
this.uploadSingleFile(job.file, job.id, job.isUpdate);
|
|
1236
|
+
},
|
|
1237
|
+
retryAllFailed() {
|
|
1238
|
+
this.uploadJobs.filter(j => j.status === 'error').forEach(job => {
|
|
1239
|
+
this.retryUpload(job);
|
|
1240
|
+
});
|
|
1241
|
+
},
|
|
1242
|
+
navigateFailed(direction) {
|
|
1243
|
+
const failedJobs = this.uploadJobs.filter(j => j.status === 'error');
|
|
1244
|
+
if (!failedJobs.length) return;
|
|
1245
|
+
this.failedNavIndex = (this.failedNavIndex + direction + failedJobs.length) % failedJobs.length;
|
|
1246
|
+
const targetJob = failedJobs[this.failedNavIndex];
|
|
1247
|
+
const refEl = this.$refs['job-' + targetJob.id];
|
|
1248
|
+
const el = Array.isArray(refEl) ? refEl[0] : refEl;
|
|
1249
|
+
if (el && this.$refs.jobListRef) {
|
|
1250
|
+
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
1251
|
+
el.classList.add('bg-red-50');
|
|
1252
|
+
setTimeout(() => el.classList.remove('bg-red-50'), 1500);
|
|
1253
|
+
}
|
|
1254
|
+
},
|
|
1181
1255
|
|
|
1182
1256
|
removeLastSearch($event) {
|
|
1183
1257
|
if (!$event.target.value) {
|
|
@@ -1224,6 +1298,12 @@ export async function viewer(fields, e) {
|
|
|
1224
1298
|
uploadsInProgress() {
|
|
1225
1299
|
return this.uploadJobs.filter((j) => j.status === 'uploading').length;
|
|
1226
1300
|
},
|
|
1301
|
+
uploadsDone() {
|
|
1302
|
+
return this.uploadJobs.filter(j => j.status === 'done').length;
|
|
1303
|
+
},
|
|
1304
|
+
uploadsFailed() {
|
|
1305
|
+
return this.uploadJobs.filter(j => j.status === 'error').length;
|
|
1306
|
+
},
|
|
1227
1307
|
acceptTypes() {
|
|
1228
1308
|
if (fields.file_upload_mime_type_preset) {
|
|
1229
1309
|
switch (fields.file_upload_mime_type_preset) {
|