vue3-chunked-upload 1.0.4

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.
@@ -0,0 +1,766 @@
1
+ (function(){"use strict";try{if(typeof document<"u"){var t=document.createElement("style");t.appendChild(document.createTextNode('*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.pointer-events-none{pointer-events:none}.absolute{position:absolute}.relative{position:relative}.mb-3{margin-bottom:.75rem}.mt-0\\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.flex{display:flex}.hidden{display:none}.h-1\\.5{height:.375rem}.h-10{height:2.5rem}.h-14{height:3.5rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-full{height:100%}.w-10{width:2.5rem}.w-14{width:3.5rem}.w-4{width:1rem}.w-5{width:1.25rem}.min-w-0{min-width:0px}.flex-1{flex:1 1 0%}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-center{justify-content:center}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.overflow-hidden{overflow:hidden}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.border{border-width:1px}.border-2{border-width:2px}.border-dashed{border-style:dashed}.border-blue-500{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1))}.border-gray-200{--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity, 1))}.border-gray-300{--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity, 1))}.bg-blue-50{--tw-bg-opacity: 1;background-color:rgb(239 246 255 / var(--tw-bg-opacity, 1))}.bg-blue-500{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}.bg-gray-100{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1))}.bg-gray-200{--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity, 1))}.bg-gray-50{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity, 1))}.bg-indigo-50{--tw-bg-opacity: 1;background-color:rgb(238 242 255 / var(--tw-bg-opacity, 1))}.bg-red-500{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity, 1))}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-yellow-500{--tw-bg-opacity: 1;background-color:rgb(234 179 8 / var(--tw-bg-opacity, 1))}.p-10{padding:2.5rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.text-center{text-align:center}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-medium{font-weight:500}.text-blue-600{--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity, 1))}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.text-gray-700{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity, 1))}.text-gray-800{--tw-text-opacity: 1;color:rgb(31 41 55 / var(--tw-text-opacity, 1))}.text-green-600{--tw-text-opacity: 1;color:rgb(22 163 74 / var(--tw-text-opacity, 1))}.text-indigo-600{--tw-text-opacity: 1;color:rgb(79 70 229 / var(--tw-text-opacity, 1))}.text-red-600{--tw-text-opacity: 1;color:rgb(220 38 38 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-yellow-600{--tw-text-opacity: 1;color:rgb(202 138 4 / var(--tw-text-opacity, 1))}.opacity-50{opacity:.5}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-300{transition-duration:.3s}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.hover\\:border-blue-400:hover{--tw-border-opacity: 1;border-color:rgb(96 165 250 / var(--tw-border-opacity, 1))}.hover\\:border-gray-300:hover{--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity, 1))}.hover\\:bg-blue-600:hover{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity, 1))}.hover\\:bg-gray-100:hover{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1))}.hover\\:bg-gray-50:hover{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity, 1))}.hover\\:bg-red-50:hover{--tw-bg-opacity: 1;background-color:rgb(254 242 242 / var(--tw-bg-opacity, 1))}.hover\\:text-gray-600:hover{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.hover\\:text-red-500:hover{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.hover\\:underline:hover{text-decoration-line:underline}.disabled\\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\\:bg-gray-400:disabled{--tw-bg-opacity: 1;background-color:rgb(156 163 175 / var(--tw-bg-opacity, 1))}.task-enter-active[data-v-372cc334],.task-leave-active[data-v-372cc334]{transition:all .3s ease}.task-enter-from[data-v-372cc334]{opacity:0;transform:translateY(-10px)}.task-leave-to[data-v-372cc334]{opacity:0;transform:translateY(10px)}.task-move[data-v-372cc334]{transition:transform .3s ease}.progress-bar[data-v-372cc334]{position:relative}.progress-bar-shimmer[data-v-372cc334]{position:absolute;top:0;left:0;right:0;bottom:0;background:linear-gradient(90deg,transparent 0%,rgba(255,255,255,.4) 50%,transparent 100%);animation:shimmer-372cc334 1.5s infinite}@keyframes shimmer-372cc334{0%{transform:translate(-100%)}to{transform:translate(100%)}}')),document.head.appendChild(t)}}catch(r){console.error("vite-plugin-css-injected-by-js",r)}})();
2
+ import { ref as j, computed as V, onUnmounted as re, nextTick as le, defineComponent as ie, onMounted as ae, watch as oe, openBlock as p, createElementBlock as h, normalizeClass as W, createElementVNode as l, renderSlot as ue, toDisplayString as U, unref as v, withModifiers as ee, createTextVNode as de, createVNode as ce, TransitionGroup as pe, withCtx as he, Fragment as fe, renderList as ge, createCommentVNode as b, normalizeStyle as me } from "vue";
3
+ function ve() {
4
+ return Date.now().toString(36) + Math.random().toString(36).substr(2);
5
+ }
6
+ function ye(u) {
7
+ return `${u.name}_${u.size}_${u.lastModified}`;
8
+ }
9
+ function se(u) {
10
+ if (u === 0) return "0 B";
11
+ const k = 1024, i = ["B", "KB", "MB", "GB", "TB"], r = Math.floor(Math.log(u) / Math.log(k));
12
+ return parseFloat((u / Math.pow(k, r)).toFixed(2)) + " " + i[r];
13
+ }
14
+ function we(u) {
15
+ return new Promise((k) => setTimeout(k, u));
16
+ }
17
+ function xe(u, k) {
18
+ const i = k.chunkSize || 10485760, r = Math.ceil(u.size / i);
19
+ return {
20
+ id: ve(),
21
+ file: u,
22
+ status: "pending",
23
+ progress: 0,
24
+ chunkSize: i,
25
+ totalChunks: r,
26
+ uploadedChunks: /* @__PURE__ */ new Set(),
27
+ currentChunk: 0,
28
+ fileId: ye(u),
29
+ xhrList: [],
30
+ retryCount: 0,
31
+ chunkStatus: /* @__PURE__ */ new Map()
32
+ };
33
+ }
34
+ function Ce(u = {}) {
35
+ const i = { ...{
36
+ chunkSize: 10485760,
37
+ maxConcurrent: 3,
38
+ uploadUrl: "/api/chunkUpload/chunk",
39
+ mergeUrl: "/api/chunkUpload/complete",
40
+ maxFileSize: 10737418240,
41
+ autoStart: !0,
42
+ retryTimes: 3,
43
+ retryDelay: 1e3,
44
+ draggable: !0,
45
+ headers: {},
46
+ accept: "",
47
+ limit: 0,
48
+ onBeforeUpload: () => !0,
49
+ onProgress: () => {
50
+ },
51
+ onSuccess: () => {
52
+ },
53
+ onError: () => {
54
+ },
55
+ onTaskAdd: () => {
56
+ },
57
+ onTaskRemove: () => {
58
+ },
59
+ onNotice: () => {
60
+ },
61
+ onAllComplete: () => {
62
+ }
63
+ }, ...u }, r = j([]), d = j(!1), x = /* @__PURE__ */ new Map(), S = j(0), G = V(() => r.value.length), J = V(() => r.value.filter((e) => e.status === "completed").length), K = V(() => {
64
+ if (r.value.length === 0) return 0;
65
+ const e = r.value.reduce((t, s) => t + s.progress, 0);
66
+ return Math.round(e / r.value.length);
67
+ }), L = V(() => r.value.reduce((e, t) => e + t.file.size, 0)), X = V(() => {
68
+ let e = 0;
69
+ return r.value.forEach((t) => {
70
+ e += t.uploadedChunks.size * t.chunkSize;
71
+ }), Math.min(e, L.value);
72
+ }), M = () => {
73
+ S.value++, r.value = [...r.value];
74
+ }, T = (e, t) => {
75
+ const s = x.get(e);
76
+ s && (t(s), r.value = [...r.value]);
77
+ }, z = (e) => x.get(e), f = () => {
78
+ if (r.value.length === 0) return;
79
+ if (r.value.every((t) => t.status === "completed")) {
80
+ const t = r.value.filter((s) => s.status === "completed");
81
+ i.onAllComplete(t);
82
+ }
83
+ }, C = (e, t) => new Promise((s, o) => {
84
+ const n = t * e.chunkSize, a = Math.min(n + e.chunkSize, e.file.size), g = e.file.slice(n, a), y = {
85
+ fileId: e.fileId,
86
+ fileName: e.file.name,
87
+ chunkIndex: t,
88
+ totalChunks: e.totalChunks,
89
+ chunk: g
90
+ }, w = new FormData();
91
+ w.append("fileId", y.fileId), w.append("fileName", y.fileName), w.append("chunkIndex", String(y.chunkIndex)), w.append("totalChunks", String(y.totalChunks)), w.append("chunk", y.chunk);
92
+ const c = new XMLHttpRequest();
93
+ e.xhrList.push(c), c.open("POST", i.uploadUrl, !0), c.timeout = 6e4, i.headers && Object.entries(i.headers).forEach(([m, _]) => {
94
+ c.setRequestHeader(m, _);
95
+ }), c.onload = () => {
96
+ if (c.status === 200)
97
+ try {
98
+ const m = JSON.parse(c.responseText);
99
+ m.success ? (e.uploadedChunks.add(t), e.chunkStatus.set(t, "completed"), s()) : o(new Error(m.message || `上传分片 ${t} 失败`));
100
+ } catch {
101
+ e.uploadedChunks.add(t), e.chunkStatus.set(t, "completed"), s();
102
+ }
103
+ else
104
+ o(new Error(`上传分片 ${t} 失败: ${c.status}`));
105
+ }, c.onerror = () => {
106
+ o(new Error(`上传分片 ${t} 网络错误`));
107
+ }, c.ontimeout = () => {
108
+ o(new Error(`上传分片 ${t} 超时`));
109
+ }, c.upload.onprogress = (m) => {
110
+ m.lengthComputable && e.status === "uploading" && e.chunkStatus.set(t, "uploading");
111
+ }, c.send(w);
112
+ }), A = async (e) => {
113
+ const t = e.totalChunks;
114
+ let o = (() => {
115
+ const m = [];
116
+ for (let _ = 0; _ < t; _++)
117
+ e.uploadedChunks.has(_) || m.push(_);
118
+ return m;
119
+ })();
120
+ if (o.length === 0) {
121
+ await $(e);
122
+ return;
123
+ }
124
+ const n = [...o];
125
+ let a = !1;
126
+ const g = async (m) => {
127
+ let _ = null;
128
+ for (let I = 0; I < i.retryTimes; I++)
129
+ try {
130
+ if (e.status === "paused" || e.status === "completed")
131
+ return;
132
+ await C(e, m);
133
+ const q = e.uploadedChunks.size;
134
+ if (e.progress = Math.round(q / t * 100), e.startTime) {
135
+ const te = (Date.now() - e.startTime) / 1e3;
136
+ if (te > 0) {
137
+ const ne = q * e.chunkSize;
138
+ e.speed = Math.round(ne / te / 1024);
139
+ }
140
+ }
141
+ if (i.onProgress(e), T(e.id, () => {
142
+ }), e.uploadedChunks.size === t) {
143
+ a || (a = !0, await $(e));
144
+ return;
145
+ }
146
+ await y();
147
+ return;
148
+ } catch (q) {
149
+ _ = q, e.retryCount++, I < i.retryTimes - 1 && await we(i.retryDelay * (I + 1));
150
+ }
151
+ throw _ || new Error(`上传分片 ${m} 失败`);
152
+ }, y = async () => {
153
+ if (n.length === 0 || e.status === "paused" || e.status === "completed")
154
+ return;
155
+ const m = n.shift();
156
+ await g(m);
157
+ }, w = Math.min(i.maxConcurrent, n.length), c = [];
158
+ for (let m = 0; m < w; m++)
159
+ c.push(y());
160
+ await Promise.all(c);
161
+ }, $ = async (e) => {
162
+ try {
163
+ const t = {
164
+ "Content-Type": "application/json",
165
+ ...i.headers
166
+ }, s = {
167
+ fileId: e.fileId,
168
+ fileName: e.file.name,
169
+ totalChunks: e.totalChunks,
170
+ fileSize: e.file.size
171
+ }, o = await fetch(i.mergeUrl, {
172
+ method: "POST",
173
+ headers: t,
174
+ body: JSON.stringify(s)
175
+ }), n = await o.json();
176
+ if (o.ok && n.success)
177
+ e.status = "completed", e.progress = 100, e.endTime = Date.now(), n.data && n.data.path && (e.path = n.data.path), i.onSuccess(e), i.onNotice("success", `文件 "${e.file.name}" 上传成功`), T(e.id, () => {
178
+ }), f();
179
+ else
180
+ throw new Error(n.message || "合并文件失败");
181
+ } catch (t) {
182
+ throw e.status = "error", e.errorMessage = t instanceof Error ? t.message : "合并文件失败", i.onError(e, t), t;
183
+ }
184
+ }, E = async (e) => {
185
+ const t = z(e);
186
+ if (!t)
187
+ throw new Error(`任务 ${e} 不存在`);
188
+ if (t.status === "completed") {
189
+ i.onNotice("info", "文件已上传完成");
190
+ return;
191
+ }
192
+ if (t.status !== "uploading") {
193
+ t.retryCount = 0, t.status = "uploading", t.startTime = Date.now(), d.value = !0;
194
+ try {
195
+ await A(t);
196
+ } catch (s) {
197
+ console.error("Upload error:", s), t.status = "error", t.errorMessage = s instanceof Error ? s.message : "上传失败", i.onError(t, s), i.onNotice("error", `上传失败: ${t.errorMessage}`), T(t.id, () => {
198
+ });
199
+ } finally {
200
+ d.value = !1;
201
+ }
202
+ }
203
+ }, P = (e) => {
204
+ const t = [], s = Array.from(e);
205
+ for (const o of s) {
206
+ if (o.size > i.maxFileSize) {
207
+ i.onNotice(
208
+ "error",
209
+ `文件 "${o.name}" 超过 ${se(i.maxFileSize)} 限制`
210
+ );
211
+ continue;
212
+ }
213
+ if (r.value.some(
214
+ (g) => g.file.name === o.name && g.file.size === o.size && g.file.lastModified === o.lastModified
215
+ )) {
216
+ i.onNotice("warning", `文件 "${o.name}" 已存在`);
217
+ continue;
218
+ }
219
+ const a = xe(o, i);
220
+ r.value.push(a), x.set(a.id, a), t.push(a.id), i.onTaskAdd(a), i.autoStart && le(() => {
221
+ E(a.id);
222
+ });
223
+ }
224
+ return M(), t;
225
+ }, O = (e) => {
226
+ for (const t of e) {
227
+ if (r.value.some((w) => {
228
+ var c;
229
+ return w.id === t.id || ((c = w.file) == null ? void 0 : c.name) === t.name;
230
+ })) continue;
231
+ const o = i.chunkSize || 10 * 1024 * 1024, n = Math.ceil(t.size / o), a = new Blob([], { type: "" });
232
+ Object.defineProperty(a, "size", { value: t.size });
233
+ const g = a;
234
+ Object.defineProperty(g, "name", { value: t.name });
235
+ const y = {
236
+ id: t.id,
237
+ file: g,
238
+ status: "completed",
239
+ progress: 100,
240
+ chunkSize: o,
241
+ totalChunks: n,
242
+ uploadedChunks: new Set(Array.from({ length: n }, (w, c) => c)),
243
+ currentChunk: n - 1,
244
+ fileId: `${t.name}_${t.size}`,
245
+ xhrList: [],
246
+ retryCount: 0,
247
+ chunkStatus: /* @__PURE__ */ new Map(),
248
+ path: t.path
249
+ };
250
+ r.value.push(y), x.set(y.id, y);
251
+ }
252
+ M();
253
+ }, F = (e) => {
254
+ const t = z(e);
255
+ if (!t || t.status !== "uploading") return;
256
+ t.status = "paused", t.xhrList.forEach((o) => o.abort()), t.xhrList = [], T(e, () => {
257
+ }), r.value.some((o) => o.status === "uploading") || (d.value = !1);
258
+ }, N = (e) => {
259
+ const t = z(e);
260
+ !t || t.status !== "paused" && t.status !== "pending" || E(e);
261
+ }, B = (e) => {
262
+ const t = z(e);
263
+ return t ? (t.xhrList.forEach((s) => s.abort()), x.delete(e), r.value = r.value.filter((s) => s.id !== e), i.onTaskRemove(t), M(), !0) : !1;
264
+ }, Q = () => {
265
+ r.value.forEach((e) => {
266
+ e.xhrList.forEach((t) => t.abort());
267
+ }), r.value = [], x.clear(), d.value = !1, M();
268
+ }, R = (e) => {
269
+ const t = z(e);
270
+ t && (t.status === "error" || t.status === "pending") && (t.retryCount = 0, t.errorMessage = void 0, t.status = "pending", t.chunkStatus.forEach((s, o) => {
271
+ s === "error" && (t.chunkStatus.delete(o), t.uploadedChunks.delete(o));
272
+ }), T(e, () => {
273
+ }), E(e));
274
+ }, H = () => {
275
+ r.value.forEach((e) => {
276
+ e.status === "uploading" && F(e.id);
277
+ });
278
+ }, Y = () => {
279
+ r.value.forEach((e) => {
280
+ e.status === "paused" && N(e.id);
281
+ });
282
+ }, Z = () => {
283
+ const e = {
284
+ pending: 0,
285
+ uploading: 0,
286
+ paused: 0,
287
+ completed: 0,
288
+ error: 0,
289
+ total: r.value.length
290
+ };
291
+ return r.value.forEach((t) => {
292
+ e[t.status] = (e[t.status] || 0) + 1;
293
+ }), e;
294
+ }, D = (e) => {
295
+ const t = z(e);
296
+ return t ? Array.from(t.uploadedChunks).sort((s, o) => s - o) : [];
297
+ };
298
+ return re(() => {
299
+ r.value.forEach((e) => {
300
+ e.xhrList.forEach((t) => t.abort());
301
+ }), r.value = [], x.clear(), d.value = !1;
302
+ }), {
303
+ config: i,
304
+ // 状态
305
+ tasks: r,
306
+ isUploading: d,
307
+ totalCount: G,
308
+ completedCount: J,
309
+ totalProgress: K,
310
+ totalSize: L,
311
+ uploadedSize: X,
312
+ updateCounter: S,
313
+ // 配置
314
+ maxFileSize: i.maxFileSize,
315
+ // 核心方法
316
+ addFiles: P,
317
+ addExistingFiles: O,
318
+ startUpload: E,
319
+ pauseUpload: F,
320
+ resumeUpload: N,
321
+ removeTask: B,
322
+ clearAllTasks: Q,
323
+ retryTask: R,
324
+ pauseAll: H,
325
+ resumeAll: Y,
326
+ getTask: z,
327
+ getTaskStats: Z,
328
+ getUploadedChunks: D,
329
+ forceUpdate: M,
330
+ // 工具函数
331
+ formatFileSize: se
332
+ };
333
+ }
334
+ const ke = { key: 0 }, be = { class: "text-xs text-gray-400 mt-2" }, ze = { class: "flex flex-col items-center gap-3" }, _e = { class: "text-xs text-gray-400 mt-1" }, Se = ["accept"], Me = {
335
+ key: 2,
336
+ class: "upload-button"
337
+ }, Te = ["disabled"], Ee = { class: "text-xs text-gray-400 mt-2" }, Ue = ["accept"], $e = {
338
+ key: 3,
339
+ class: "mt-3"
340
+ }, Be = { class: "space-y-2" }, Ae = {
341
+ key: 0,
342
+ class: "w-5 h-5 text-indigo-600",
343
+ fill: "none",
344
+ stroke: "currentColor",
345
+ viewBox: "0 0 24 24"
346
+ }, Fe = {
347
+ key: 1,
348
+ class: "w-5 h-5 text-indigo-600",
349
+ fill: "none",
350
+ stroke: "currentColor",
351
+ viewBox: "0 0 24 24"
352
+ }, Ne = {
353
+ key: 2,
354
+ class: "w-5 h-5 text-gray-500",
355
+ fill: "none",
356
+ stroke: "currentColor",
357
+ viewBox: "0 0 24 24"
358
+ }, De = { class: "flex-1 min-w-0" }, Ve = { class: "text-sm font-medium text-gray-800 truncate" }, je = { class: "flex items-center gap-3 mt-0.5" }, Le = { class: "text-xs text-gray-500" }, Pe = {
359
+ key: 0,
360
+ class: "text-xs text-blue-600"
361
+ }, Oe = {
362
+ key: 1,
363
+ class: "text-xs text-green-600"
364
+ }, Re = {
365
+ key: 2,
366
+ class: "text-xs text-yellow-600"
367
+ }, He = {
368
+ key: 3,
369
+ class: "text-xs text-red-600"
370
+ }, Ie = {
371
+ key: 0,
372
+ class: "mt-2"
373
+ }, qe = { class: "progress-bar-container h-1.5 bg-gray-200 rounded-full overflow-hidden" }, We = {
374
+ key: 0,
375
+ class: "progress-bar-shimmer"
376
+ }, Ge = { class: "flex items-center gap-2" }, Je = ["onClick"], Ke = ["onClick"], Xe = ["onClick"], Qe = ["onClick"], Ye = /* @__PURE__ */ ie({
377
+ __name: "ChunkUpload",
378
+ props: {
379
+ modelValue: { default: () => [] },
380
+ options: {},
381
+ disabled: { type: Boolean, default: !1 }
382
+ },
383
+ emits: ["update:modelValue", "success", "error", "progress", "notice", "allComplete", "taskAdd", "taskRemove"],
384
+ setup(u, { expose: k, emit: i }) {
385
+ const r = u, d = i, x = j(), S = j(!1), G = (s, o) => {
386
+ d("notice", s, o);
387
+ }, J = (s) => {
388
+ d("success", s);
389
+ }, K = (s, o) => {
390
+ d("error", s, o);
391
+ }, L = (s) => {
392
+ d("progress", s);
393
+ }, X = (s) => {
394
+ d("allComplete", s);
395
+ }, M = (s) => {
396
+ d("taskAdd", s);
397
+ }, T = (s) => {
398
+ d("taskRemove", s);
399
+ }, z = {
400
+ ...r.options,
401
+ onNotice: G,
402
+ onSuccess: J,
403
+ onError: K,
404
+ onProgress: L,
405
+ onAllComplete: X,
406
+ onTaskAdd: M,
407
+ onTaskRemove: T
408
+ }, {
409
+ config: f,
410
+ tasks: C,
411
+ maxFileSize: A,
412
+ addFiles: $,
413
+ addExistingFiles: E,
414
+ pauseUpload: P,
415
+ resumeUpload: O,
416
+ removeTask: F,
417
+ retryTask: N,
418
+ formatFileSize: B
419
+ } = Ce(z);
420
+ ae(() => {
421
+ r.modelValue && r.modelValue.length > 0 && E(r.modelValue);
422
+ }), oe(
423
+ () => r.modelValue,
424
+ (s) => {
425
+ if (s && s.length > 0) {
426
+ const o = C.value.map((a) => a.id), n = s.filter((a) => !o.includes(a.id));
427
+ n.length > 0 && E(n);
428
+ }
429
+ },
430
+ { immediate: !0, deep: !0 }
431
+ );
432
+ const Q = (s) => {
433
+ if (!s.speed || s.speed === 0) return "";
434
+ const o = s.file.size - s.uploadedChunks.size * s.chunkSize, n = Math.ceil(o / (s.speed * 1024));
435
+ if (n < 60)
436
+ return `剩余 ${n} 秒`;
437
+ const a = Math.floor(n / 60), g = n % 60;
438
+ return `剩余 ${a} 分 ${g} 秒`;
439
+ }, R = (s) => [".mp4", ".webm", ".ogg", ".mov", ".avi", ".wmv"].some((n) => s.toLowerCase().endsWith(n)), H = (s) => [".mp3", ".wav", ".ogg", ".flac", ".aac", ".m4a"].some((n) => s.toLowerCase().endsWith(n)), Y = (s) => R(s) || H(s) ? "bg-indigo-50" : "bg-gray-100", Z = () => {
440
+ const s = C.value.filter((o) => o.status === "completed").map((o) => ({
441
+ id: o.id,
442
+ name: o.file.name,
443
+ size: o.file.size,
444
+ path: o.path,
445
+ status: o.status
446
+ }));
447
+ d("update:modelValue", s);
448
+ };
449
+ oe(
450
+ () => C.value,
451
+ () => {
452
+ Z();
453
+ },
454
+ { deep: !0 }
455
+ );
456
+ const D = () => {
457
+ var s;
458
+ r.disabled || (s = x.value) == null || s.click();
459
+ }, e = async (s) => {
460
+ if (r.disabled) return;
461
+ const o = s.target;
462
+ if (o.files && o.files.length > 0) {
463
+ const n = o.files.length;
464
+ if (f.limit && C.value.length + n > f.limit) {
465
+ const g = f.limit - C.value.length;
466
+ d(
467
+ "notice",
468
+ "warning",
469
+ `最多只能上传 ${f.limit} 个文件,当前最多还能添加 ${g} 个`
470
+ ), o.value = "";
471
+ return;
472
+ }
473
+ const a = Array.from(o.files);
474
+ if (f.onBeforeUpload && !await f.onBeforeUpload(a)) {
475
+ o.value = "";
476
+ return;
477
+ }
478
+ $(o.files);
479
+ }
480
+ o.value = "";
481
+ }, t = async (s) => {
482
+ var n;
483
+ if (r.disabled) return;
484
+ S.value = !1;
485
+ const o = (n = s.dataTransfer) == null ? void 0 : n.files;
486
+ if (o && o.length > 0) {
487
+ const a = o.length;
488
+ if (f.limit && C.value.length + a > f.limit) {
489
+ const w = f.limit - C.value.length;
490
+ d(
491
+ "notice",
492
+ "warning",
493
+ `最多只能上传 ${f.limit} 个文件,当前最多还能添加 ${w} 个`
494
+ );
495
+ return;
496
+ }
497
+ const g = Array.from(o);
498
+ if (f.onBeforeUpload && !await f.onBeforeUpload(g))
499
+ return;
500
+ const y = $(o);
501
+ y.length > 0 && d("notice", "success", `已添加 ${y.length} 个文件`);
502
+ }
503
+ };
504
+ return k({
505
+ tasks: C,
506
+ addFiles: $,
507
+ pauseUpload: P,
508
+ resumeUpload: O,
509
+ removeTask: F,
510
+ retryTask: N
511
+ }), (s, o) => (p(), h("div", {
512
+ class: W(["chunk-uploader", { "opacity-50 pointer-events-none": r.disabled }])
513
+ }, [
514
+ s.$slots.trigger ? (p(), h("div", ke, [
515
+ l("div", {
516
+ onClick: o[0] || (o[0] = (n) => !r.disabled && D())
517
+ }, [
518
+ ue(s.$slots, "trigger", {}, void 0, !0)
519
+ ]),
520
+ l("p", be, "最大文件大小 " + U(v(B)(v(A))), 1),
521
+ l("input", {
522
+ ref_key: "fileInput",
523
+ ref: x,
524
+ type: "file",
525
+ multiple: "",
526
+ class: "hidden",
527
+ onChange: e
528
+ }, null, 544)
529
+ ])) : v(f).draggable ? (p(), h("div", {
530
+ key: 1,
531
+ class: W(["upload-zone border-2 border-dashed rounded-lg p-10 text-center cursor-pointer transition-all duration-300", {
532
+ "border-blue-500 bg-blue-50": S.value && !r.disabled,
533
+ "border-gray-300 hover:border-blue-400 hover:bg-gray-50": !S.value && !r.disabled,
534
+ "border-gray-200 bg-gray-50 cursor-not-allowed": r.disabled
535
+ }]),
536
+ onClick: o[1] || (o[1] = (n) => !r.disabled && D()),
537
+ onDragover: o[2] || (o[2] = ee((n) => !r.disabled && (S.value = !0), ["prevent"])),
538
+ onDragleave: o[3] || (o[3] = ee((n) => !r.disabled && (S.value = !1), ["prevent"])),
539
+ onDrop: ee(t, ["prevent"])
540
+ }, [
541
+ l("div", ze, [
542
+ o[5] || (o[5] = l("div", { class: "w-14 h-14 flex items-center justify-center" }, [
543
+ l("svg", {
544
+ class: "w-14 h-14",
545
+ viewBox: "0 0 24 24",
546
+ fill: "none",
547
+ xmlns: "http://www.w3.org/2000/svg"
548
+ }, [
549
+ l("path", {
550
+ d: "M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8l-6-6z",
551
+ fill: "#DBEAFE",
552
+ stroke: "#3B82F6",
553
+ "stroke-width": "2",
554
+ "stroke-linecap": "round",
555
+ "stroke-linejoin": "round"
556
+ }),
557
+ l("path", {
558
+ d: "M14 2v6h6M16 13H8M16 17H8M10 9H8",
559
+ stroke: "#3B82F6",
560
+ "stroke-width": "2",
561
+ "stroke-linecap": "round",
562
+ "stroke-linejoin": "round"
563
+ })
564
+ ])
565
+ ], -1)),
566
+ l("div", null, [
567
+ o[4] || (o[4] = l("p", { class: "text-sm font-medium text-gray-700" }, [
568
+ de(" 将文件拖放到此处或 "),
569
+ l("span", { class: "text-blue-600 cursor-pointer hover:underline" }, "选择文件")
570
+ ], -1)),
571
+ l("p", _e, "最大文件大小 " + U(v(B)(v(A))) + "。", 1)
572
+ ])
573
+ ]),
574
+ l("input", {
575
+ ref_key: "fileInput",
576
+ ref: x,
577
+ type: "file",
578
+ multiple: "",
579
+ accept: v(f).accept,
580
+ class: "hidden",
581
+ onChange: e
582
+ }, null, 40, Se)
583
+ ], 34)) : (p(), h("div", Me, [
584
+ l("button", {
585
+ type: "button",
586
+ class: "px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors disabled:bg-gray-400 disabled:cursor-not-allowed",
587
+ disabled: r.disabled,
588
+ onClick: D
589
+ }, " 选择文件 ", 8, Te),
590
+ l("p", Ee, "最大文件大小 " + U(v(B)(v(A))), 1),
591
+ l("input", {
592
+ ref_key: "fileInput",
593
+ ref: x,
594
+ type: "file",
595
+ multiple: "",
596
+ accept: v(f).accept,
597
+ class: "hidden",
598
+ onChange: e
599
+ }, null, 40, Ue)
600
+ ])),
601
+ v(C).length > 0 ? (p(), h("div", $e, [
602
+ l("div", Be, [
603
+ ce(pe, { name: "task" }, {
604
+ default: he(() => [
605
+ (p(!0), h(fe, null, ge(v(C), (n) => (p(), h("div", {
606
+ key: n.id,
607
+ class: "file-item bg-white border border-gray-200 rounded-lg p-3 flex items-center gap-3 hover:border-gray-300 transition-colors"
608
+ }, [
609
+ l("div", {
610
+ class: W(["w-10 h-10 rounded-lg flex items-center justify-center", Y(n.file.name)])
611
+ }, [
612
+ R(n.file.name) ? (p(), h("svg", Ae, [...o[6] || (o[6] = [
613
+ l("path", {
614
+ "stroke-linecap": "round",
615
+ "stroke-linejoin": "round",
616
+ "stroke-width": "2",
617
+ d: "M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"
618
+ }, null, -1),
619
+ l("path", {
620
+ "stroke-linecap": "round",
621
+ "stroke-linejoin": "round",
622
+ "stroke-width": "2",
623
+ d: "M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
624
+ }, null, -1)
625
+ ])])) : H(n.file.name) ? (p(), h("svg", Fe, [...o[7] || (o[7] = [
626
+ l("path", {
627
+ "stroke-linecap": "round",
628
+ "stroke-linejoin": "round",
629
+ "stroke-width": "2",
630
+ d: "M9 19V6l12-3v13M9 19c0 1.105-1.343 2-3 2s-3-.895-3-2 1.343-2 3-2 3 .895 3 2zm12-3c0 1.105-1.343 2-3 2s-3-.895-3-2 1.343-2 3-2 3 .895 3 2zM9 10l12-3"
631
+ }, null, -1)
632
+ ])])) : (p(), h("svg", Ne, [...o[8] || (o[8] = [
633
+ l("path", {
634
+ "stroke-linecap": "round",
635
+ "stroke-linejoin": "round",
636
+ "stroke-width": "2",
637
+ d: "M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
638
+ }, null, -1)
639
+ ])]))
640
+ ], 2),
641
+ l("div", De, [
642
+ l("p", Ve, U(n.file.name), 1),
643
+ l("div", je, [
644
+ l("span", Le, U(v(B)(n.file.size)), 1),
645
+ n.status === "uploading" ? (p(), h("span", Pe, U(n.progress) + "% · " + U(Q(n)), 1)) : b("", !0),
646
+ n.status === "completed" ? (p(), h("span", Oe, " 已完成 ")) : b("", !0),
647
+ n.status === "paused" ? (p(), h("span", Re, " 已暂停 ")) : b("", !0),
648
+ n.status === "error" ? (p(), h("span", He, " 失败 ")) : b("", !0)
649
+ ]),
650
+ n.status !== "completed" ? (p(), h("div", Ie, [
651
+ l("div", qe, [
652
+ l("div", {
653
+ class: W(["progress-bar h-full rounded-full transition-all duration-300 ease-out", {
654
+ "bg-blue-500": n.status === "uploading" || n.status === "pending",
655
+ "bg-yellow-500": n.status === "paused",
656
+ "bg-red-500": n.status === "error"
657
+ }]),
658
+ style: me({ width: `${n.progress}%` })
659
+ }, [
660
+ n.status === "uploading" ? (p(), h("div", We)) : b("", !0)
661
+ ], 6)
662
+ ])
663
+ ])) : b("", !0)
664
+ ]),
665
+ l("div", Ge, [
666
+ n.status === "uploading" ? (p(), h("button", {
667
+ key: 0,
668
+ onClick: (a) => v(P)(n.id),
669
+ class: "p-2 text-gray-400 hover:text-gray-600 hover:bg-gray-100 rounded-lg transition-colors"
670
+ }, [...o[9] || (o[9] = [
671
+ l("svg", {
672
+ class: "w-4 h-4",
673
+ fill: "none",
674
+ stroke: "currentColor",
675
+ viewBox: "0 0 24 24"
676
+ }, [
677
+ l("path", {
678
+ "stroke-linecap": "round",
679
+ "stroke-linejoin": "round",
680
+ "stroke-width": "2",
681
+ d: "M10 9v6m4-6v6m7-3a9 9 0 11-18 0 9 9 0 0118 0z"
682
+ })
683
+ ], -1)
684
+ ])], 8, Je)) : b("", !0),
685
+ n.status === "paused" || n.status === "pending" ? (p(), h("button", {
686
+ key: 1,
687
+ onClick: (a) => v(O)(n.id),
688
+ class: "p-2 text-gray-400 hover:text-gray-600 hover:bg-gray-100 rounded-lg transition-colors"
689
+ }, [...o[10] || (o[10] = [
690
+ l("svg", {
691
+ class: "w-4 h-4",
692
+ fill: "none",
693
+ stroke: "currentColor",
694
+ viewBox: "0 0 24 24"
695
+ }, [
696
+ l("path", {
697
+ "stroke-linecap": "round",
698
+ "stroke-linejoin": "round",
699
+ "stroke-width": "2",
700
+ d: "M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"
701
+ }),
702
+ l("path", {
703
+ "stroke-linecap": "round",
704
+ "stroke-linejoin": "round",
705
+ "stroke-width": "2",
706
+ d: "M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
707
+ })
708
+ ], -1)
709
+ ])], 8, Ke)) : b("", !0),
710
+ n.status === "error" ? (p(), h("button", {
711
+ key: 2,
712
+ onClick: (a) => v(N)(n.id),
713
+ class: "p-2 text-gray-400 hover:text-gray-600 hover:bg-gray-100 rounded-lg transition-colors"
714
+ }, [...o[11] || (o[11] = [
715
+ l("svg", {
716
+ class: "w-4 h-4",
717
+ fill: "none",
718
+ stroke: "currentColor",
719
+ viewBox: "0 0 24 24"
720
+ }, [
721
+ l("path", {
722
+ "stroke-linecap": "round",
723
+ "stroke-linejoin": "round",
724
+ "stroke-width": "2",
725
+ d: "M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
726
+ })
727
+ ], -1)
728
+ ])], 8, Xe)) : b("", !0),
729
+ l("button", {
730
+ onClick: (a) => v(F)(n.id),
731
+ class: "p-2 text-gray-400 hover:text-red-500 hover:bg-red-50 rounded-lg transition-colors"
732
+ }, [...o[12] || (o[12] = [
733
+ l("svg", {
734
+ class: "w-4 h-4",
735
+ fill: "none",
736
+ stroke: "currentColor",
737
+ viewBox: "0 0 24 24"
738
+ }, [
739
+ l("path", {
740
+ "stroke-linecap": "round",
741
+ "stroke-linejoin": "round",
742
+ "stroke-width": "2",
743
+ d: "M6 18L18 6M6 6l12 12"
744
+ })
745
+ ], -1)
746
+ ])], 8, Qe)
747
+ ])
748
+ ]))), 128))
749
+ ]),
750
+ _: 1
751
+ })
752
+ ])
753
+ ])) : b("", !0)
754
+ ], 2));
755
+ }
756
+ }), Ze = (u, k) => {
757
+ const i = u.__vccOpts || u;
758
+ for (const [r, d] of k)
759
+ i[r] = d;
760
+ return i;
761
+ }, tt = /* @__PURE__ */ Ze(Ye, [["__scopeId", "data-v-372cc334"]]);
762
+ export {
763
+ tt as Vue3ChunkedUpload,
764
+ se as formatFileSize,
765
+ Ce as useChunkUpload
766
+ };