@robot-admin/naive-ui-components 0.3.0

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.
Files changed (352) hide show
  1. package/README.md +257 -0
  2. package/dist/C_ActionBar-DWN-woTc.css.map +1 -0
  3. package/dist/C_ActionBar.cjs +5 -0
  4. package/dist/C_ActionBar.d.cts +2 -0
  5. package/dist/C_ActionBar.d.ts +2 -0
  6. package/dist/C_ActionBar.js +4 -0
  7. package/dist/C_ActionBar2.js +196 -0
  8. package/dist/C_ActionBar2.js.map +1 -0
  9. package/dist/C_AntV-AFKyK6hH.css.map +1 -0
  10. package/dist/C_AntV.cjs +8 -0
  11. package/dist/C_AntV.d.cts +2 -0
  12. package/dist/C_AntV.d.ts +2 -0
  13. package/dist/C_AntV.js +4 -0
  14. package/dist/C_AntV2.js +3150 -0
  15. package/dist/C_AntV2.js.map +1 -0
  16. package/dist/C_Barcode-P_EFj8dC.css.map +1 -0
  17. package/dist/C_Barcode.cjs +4 -0
  18. package/dist/C_Barcode.d.cts +2 -0
  19. package/dist/C_Barcode.d.ts +2 -0
  20. package/dist/C_Barcode.js +3 -0
  21. package/dist/C_Barcode2.js +68 -0
  22. package/dist/C_Barcode2.js.map +1 -0
  23. package/dist/C_Captcha-C-ef41xw.css.map +1 -0
  24. package/dist/C_Captcha.cjs +4 -0
  25. package/dist/C_Captcha.d.cts +2 -0
  26. package/dist/C_Captcha.d.ts +2 -0
  27. package/dist/C_Captcha.js +3 -0
  28. package/dist/C_Captcha2.js +155 -0
  29. package/dist/C_Captcha2.js.map +1 -0
  30. package/dist/C_Cascade-D9kNsjsV.css.map +1 -0
  31. package/dist/C_Cascade.cjs +4 -0
  32. package/dist/C_Cascade.d.cts +2 -0
  33. package/dist/C_Cascade.d.ts +2 -0
  34. package/dist/C_Cascade.js +3 -0
  35. package/dist/C_Cascade2.js +103 -0
  36. package/dist/C_Cascade2.js.map +1 -0
  37. package/dist/C_City-BCQ4ipiK.css.map +1 -0
  38. package/dist/C_City.cjs +4 -0
  39. package/dist/C_City.d.cts +2 -0
  40. package/dist/C_City.d.ts +2 -0
  41. package/dist/C_City.js +3 -0
  42. package/dist/C_City2.js +841 -0
  43. package/dist/C_City2.js.map +1 -0
  44. package/dist/C_Code-C9kvvEmO.css.map +1 -0
  45. package/dist/C_Code.cjs +5 -0
  46. package/dist/C_Code.d.cts +2 -0
  47. package/dist/C_Code.d.ts +2 -0
  48. package/dist/C_Code.js +4 -0
  49. package/dist/C_Code2.js +346 -0
  50. package/dist/C_Code2.js.map +1 -0
  51. package/dist/C_CollapsePanel-BUJHuYcU.css.map +1 -0
  52. package/dist/C_CollapsePanel.cjs +6 -0
  53. package/dist/C_CollapsePanel.d.cts +2 -0
  54. package/dist/C_CollapsePanel.d.ts +2 -0
  55. package/dist/C_CollapsePanel.js +4 -0
  56. package/dist/C_CollapsePanel2.js +319 -0
  57. package/dist/C_CollapsePanel2.js.map +1 -0
  58. package/dist/C_Cron-yx2Ob4Jl.css.map +1 -0
  59. package/dist/C_Cron.cjs +15 -0
  60. package/dist/C_Cron.d.cts +2 -0
  61. package/dist/C_Cron.d.ts +2 -0
  62. package/dist/C_Cron.js +4 -0
  63. package/dist/C_Cron2.js +1209 -0
  64. package/dist/C_Cron2.js.map +1 -0
  65. package/dist/C_Date.cjs +4 -0
  66. package/dist/C_Date.d.cts +2 -0
  67. package/dist/C_Date.d.ts +2 -0
  68. package/dist/C_Date.js +3 -0
  69. package/dist/C_Date2.js +219 -0
  70. package/dist/C_Date2.js.map +1 -0
  71. package/dist/C_Draggable-C483syRC.css.map +1 -0
  72. package/dist/C_Draggable.cjs +5 -0
  73. package/dist/C_Draggable.d.cts +2 -0
  74. package/dist/C_Draggable.d.ts +2 -0
  75. package/dist/C_Draggable.js +3 -0
  76. package/dist/C_Draggable2.js +295 -0
  77. package/dist/C_Draggable2.js.map +1 -0
  78. package/dist/C_Editor-Bp0SyIEw.css.map +1 -0
  79. package/dist/C_Editor.cjs +4 -0
  80. package/dist/C_Editor.d.cts +2 -0
  81. package/dist/C_Editor.d.ts +2 -0
  82. package/dist/C_Editor.js +3 -0
  83. package/dist/C_Editor2.js +160 -0
  84. package/dist/C_Editor2.js.map +1 -0
  85. package/dist/C_FilePreview-CPqvhoCy.css.map +1 -0
  86. package/dist/C_FilePreview.cjs +6 -0
  87. package/dist/C_FilePreview.d.cts +2 -0
  88. package/dist/C_FilePreview.d.ts +2 -0
  89. package/dist/C_FilePreview.js +3 -0
  90. package/dist/C_FilePreview2.js +1031 -0
  91. package/dist/C_FilePreview2.js.map +1 -0
  92. package/dist/C_Form-Jx7PY3sT.css.map +1 -0
  93. package/dist/C_Form.cjs +15 -0
  94. package/dist/C_Form.d.cts +2 -0
  95. package/dist/C_Form.d.ts +2 -0
  96. package/dist/C_Form.js +4 -0
  97. package/dist/C_Form2.js +2510 -0
  98. package/dist/C_Form2.js.map +1 -0
  99. package/dist/C_FormSearch-DvRgxlRn.css.map +1 -0
  100. package/dist/C_FormSearch.cjs +6 -0
  101. package/dist/C_FormSearch.d.cts +2 -0
  102. package/dist/C_FormSearch.d.ts +2 -0
  103. package/dist/C_FormSearch.js +3 -0
  104. package/dist/C_FormSearch2.js +356 -0
  105. package/dist/C_FormSearch2.js.map +1 -0
  106. package/dist/C_FormulaEditor-DtGkt4T_.css.map +1 -0
  107. package/dist/C_FormulaEditor.cjs +13 -0
  108. package/dist/C_FormulaEditor.d.cts +2 -0
  109. package/dist/C_FormulaEditor.d.ts +2 -0
  110. package/dist/C_FormulaEditor.js +4 -0
  111. package/dist/C_FormulaEditor2.js +1433 -0
  112. package/dist/C_FormulaEditor2.js.map +1 -0
  113. package/dist/C_FullCalendar-BF7H0YIx.css.map +1 -0
  114. package/dist/C_FullCalendar.cjs +9 -0
  115. package/dist/C_FullCalendar.d.cts +2 -0
  116. package/dist/C_FullCalendar.d.ts +2 -0
  117. package/dist/C_FullCalendar.js +3 -0
  118. package/dist/C_FullCalendar2.js +377 -0
  119. package/dist/C_FullCalendar2.js.map +1 -0
  120. package/dist/C_Guide.cjs +4 -0
  121. package/dist/C_Guide.d.cts +2 -0
  122. package/dist/C_Guide.d.ts +2 -0
  123. package/dist/C_Guide.js +3 -0
  124. package/dist/C_Guide2.js +58 -0
  125. package/dist/C_Guide2.js.map +1 -0
  126. package/dist/C_Icon.cjs +4 -0
  127. package/dist/C_Icon.d.cts +2 -0
  128. package/dist/C_Icon.d.ts +2 -0
  129. package/dist/C_Icon.js +3 -0
  130. package/dist/C_Icon2.js +286 -0
  131. package/dist/C_Icon2.js.map +1 -0
  132. package/dist/C_ImageCropper-BVJfUufl.css.map +1 -0
  133. package/dist/C_ImageCropper.cjs +6 -0
  134. package/dist/C_ImageCropper.d.cts +2 -0
  135. package/dist/C_ImageCropper.d.ts +2 -0
  136. package/dist/C_ImageCropper.js +4 -0
  137. package/dist/C_ImageCropper2.js +723 -0
  138. package/dist/C_ImageCropper2.js.map +1 -0
  139. package/dist/C_Language.cjs +4 -0
  140. package/dist/C_Language.d.cts +2 -0
  141. package/dist/C_Language.d.ts +2 -0
  142. package/dist/C_Language.js +3 -0
  143. package/dist/C_Language2.js +72 -0
  144. package/dist/C_Language2.js.map +1 -0
  145. package/dist/C_Map-DpzeuWdX.css.map +1 -0
  146. package/dist/C_Map.cjs +7 -0
  147. package/dist/C_Map.d.cts +2 -0
  148. package/dist/C_Map.d.ts +2 -0
  149. package/dist/C_Map.js +3 -0
  150. package/dist/C_Map2.js +199 -0
  151. package/dist/C_Map2.js.map +1 -0
  152. package/dist/C_Markdown-BEjxknqd.css.map +1 -0
  153. package/dist/C_Markdown.cjs +4 -0
  154. package/dist/C_Markdown.d.cts +2 -0
  155. package/dist/C_Markdown.d.ts +2 -0
  156. package/dist/C_Markdown.js +3 -0
  157. package/dist/C_Markdown2.js +186 -0
  158. package/dist/C_Markdown2.js.map +1 -0
  159. package/dist/C_NotificationCenter-0l3TY2Gn.css.map +1 -0
  160. package/dist/C_NotificationCenter.cjs +20 -0
  161. package/dist/C_NotificationCenter.d.cts +2 -0
  162. package/dist/C_NotificationCenter.d.ts +2 -0
  163. package/dist/C_NotificationCenter.js +4 -0
  164. package/dist/C_NotificationCenter2.js +1383 -0
  165. package/dist/C_NotificationCenter2.js.map +1 -0
  166. package/dist/C_Progress.cjs +4 -0
  167. package/dist/C_Progress.d.cts +2 -0
  168. package/dist/C_Progress.d.ts +2 -0
  169. package/dist/C_Progress.js +3 -0
  170. package/dist/C_Progress2.js +103 -0
  171. package/dist/C_Progress2.js.map +1 -0
  172. package/dist/C_QRCode-DbdiAIPg.css.map +1 -0
  173. package/dist/C_QRCode.cjs +5 -0
  174. package/dist/C_QRCode.d.cts +2 -0
  175. package/dist/C_QRCode.d.ts +2 -0
  176. package/dist/C_QRCode.js +3 -0
  177. package/dist/C_QRCode2.js +218 -0
  178. package/dist/C_QRCode2.js.map +1 -0
  179. package/dist/C_Signature-zhHCbra9.css.map +1 -0
  180. package/dist/C_Signature.cjs +8 -0
  181. package/dist/C_Signature.d.cts +2 -0
  182. package/dist/C_Signature.d.ts +2 -0
  183. package/dist/C_Signature.js +4 -0
  184. package/dist/C_Signature2.js +618 -0
  185. package/dist/C_Signature2.js.map +1 -0
  186. package/dist/C_SplitPane-C6sBsfKY.css.map +1 -0
  187. package/dist/C_SplitPane.cjs +6 -0
  188. package/dist/C_SplitPane.d.cts +2 -0
  189. package/dist/C_SplitPane.d.ts +2 -0
  190. package/dist/C_SplitPane.js +4 -0
  191. package/dist/C_SplitPane2.js +356 -0
  192. package/dist/C_SplitPane2.js.map +1 -0
  193. package/dist/C_Steps-CODHN5Hs.css.map +1 -0
  194. package/dist/C_Steps.cjs +4 -0
  195. package/dist/C_Steps.d.cts +2 -0
  196. package/dist/C_Steps.d.ts +2 -0
  197. package/dist/C_Steps.js +3 -0
  198. package/dist/C_Steps2.js +82 -0
  199. package/dist/C_Steps2.js.map +1 -0
  200. package/dist/C_Table-DSNsntmT.css.map +1 -0
  201. package/dist/C_Table.cjs +19 -0
  202. package/dist/C_Table.d.cts +2 -0
  203. package/dist/C_Table.d.ts +2 -0
  204. package/dist/C_Table.js +5 -0
  205. package/dist/C_Table2.js +3009 -0
  206. package/dist/C_Table2.js.map +1 -0
  207. package/dist/C_Theme.cjs +4 -0
  208. package/dist/C_Theme.d.cts +2 -0
  209. package/dist/C_Theme.d.ts +2 -0
  210. package/dist/C_Theme.js +3 -0
  211. package/dist/C_Theme2.js +60 -0
  212. package/dist/C_Theme2.js.map +1 -0
  213. package/dist/C_Time-BvZLYraL.css.map +1 -0
  214. package/dist/C_Time.cjs +5 -0
  215. package/dist/C_Time.d.cts +2 -0
  216. package/dist/C_Time.d.ts +2 -0
  217. package/dist/C_Time.js +3 -0
  218. package/dist/C_Time2.js +199 -0
  219. package/dist/C_Time2.js.map +1 -0
  220. package/dist/C_Tree-0GDv--jX.css.map +1 -0
  221. package/dist/C_Tree.cjs +7 -0
  222. package/dist/C_Tree.d.cts +2 -0
  223. package/dist/C_Tree.d.ts +2 -0
  224. package/dist/C_Tree.js +4 -0
  225. package/dist/C_Tree2.js +441 -0
  226. package/dist/C_Tree2.js.map +1 -0
  227. package/dist/C_Upload-BXd3YYLx.css.map +1 -0
  228. package/dist/C_Upload.cjs +12 -0
  229. package/dist/C_Upload.d.cts +2 -0
  230. package/dist/C_Upload.d.ts +2 -0
  231. package/dist/C_Upload.js +4 -0
  232. package/dist/C_Upload2.js +1388 -0
  233. package/dist/C_Upload2.js.map +1 -0
  234. package/dist/C_VideoPlayer-DYG3RL0Q.css.map +1 -0
  235. package/dist/C_VideoPlayer.cjs +23 -0
  236. package/dist/C_VideoPlayer.d.cts +2 -0
  237. package/dist/C_VideoPlayer.d.ts +2 -0
  238. package/dist/C_VideoPlayer.js +3 -0
  239. package/dist/C_VideoPlayer2.js +1932 -0
  240. package/dist/C_VideoPlayer2.js.map +1 -0
  241. package/dist/C_VtableGantt-fhItIiHE.css.map +1 -0
  242. package/dist/C_VtableGantt.cjs +6 -0
  243. package/dist/C_VtableGantt.d.cts +2 -0
  244. package/dist/C_VtableGantt.d.ts +2 -0
  245. package/dist/C_VtableGantt.js +4 -0
  246. package/dist/C_VtableGantt2.js +873 -0
  247. package/dist/C_VtableGantt2.js.map +1 -0
  248. package/dist/C_WaterFall-8sQDFXKg.css.map +1 -0
  249. package/dist/C_WaterFall.cjs +13 -0
  250. package/dist/C_WaterFall.d.cts +2 -0
  251. package/dist/C_WaterFall.d.ts +2 -0
  252. package/dist/C_WaterFall.js +3 -0
  253. package/dist/C_WaterFall2.js +365 -0
  254. package/dist/C_WaterFall2.js.map +1 -0
  255. package/dist/C_WorkFlow-J-dyIuh9.css.map +1 -0
  256. package/dist/C_WorkFlow.cjs +8 -0
  257. package/dist/C_WorkFlow.d.cts +2 -0
  258. package/dist/C_WorkFlow.d.ts +2 -0
  259. package/dist/C_WorkFlow.js +4 -0
  260. package/dist/C_WorkFlow2.js +1984 -0
  261. package/dist/C_WorkFlow2.js.map +1 -0
  262. package/dist/chunk.js +22 -0
  263. package/dist/city.js +4817 -0
  264. package/dist/city.js.map +1 -0
  265. package/dist/constants.d.ts +273 -0
  266. package/dist/constants.d.ts.map +1 -0
  267. package/dist/constants2.d.ts +178 -0
  268. package/dist/constants2.d.ts.map +1 -0
  269. package/dist/constants3.d.ts +475 -0
  270. package/dist/constants3.d.ts.map +1 -0
  271. package/dist/constants4.d.ts +430 -0
  272. package/dist/constants4.d.ts.map +1 -0
  273. package/dist/constants5.d.ts +4283 -0
  274. package/dist/constants5.d.ts.map +1 -0
  275. package/dist/data.d.ts +67 -0
  276. package/dist/data.d.ts.map +1 -0
  277. package/dist/export-helper.js +9 -0
  278. package/dist/index.cjs +409 -0
  279. package/dist/index.d.cts +96 -0
  280. package/dist/index.d.cts.map +1 -0
  281. package/dist/index.d.ts +103 -0
  282. package/dist/index.d.ts.map +1 -0
  283. package/dist/index.js +230 -0
  284. package/dist/index.js.map +1 -0
  285. package/dist/index.vue.d.ts +80 -0
  286. package/dist/index.vue.d.ts.map +1 -0
  287. package/dist/index10.vue.d.ts +72 -0
  288. package/dist/index10.vue.d.ts.map +1 -0
  289. package/dist/index11.vue.d.ts +26 -0
  290. package/dist/index11.vue.d.ts.map +1 -0
  291. package/dist/index12.vue.d.ts +81 -0
  292. package/dist/index12.vue.d.ts.map +1 -0
  293. package/dist/index13.vue.d.ts +55 -0
  294. package/dist/index13.vue.d.ts.map +1 -0
  295. package/dist/index14.vue.d.ts +33 -0
  296. package/dist/index14.vue.d.ts.map +1 -0
  297. package/dist/index15.vue.d.ts +18 -0
  298. package/dist/index15.vue.d.ts.map +1 -0
  299. package/dist/index16.vue.d.ts +662 -0
  300. package/dist/index16.vue.d.ts.map +1 -0
  301. package/dist/index2.vue.d.ts +38 -0
  302. package/dist/index2.vue.d.ts.map +1 -0
  303. package/dist/index3.vue.d.ts +45 -0
  304. package/dist/index3.vue.d.ts.map +1 -0
  305. package/dist/index4.vue.d.ts +31 -0
  306. package/dist/index4.vue.d.ts.map +1 -0
  307. package/dist/index5.vue.d.ts +35 -0
  308. package/dist/index5.vue.d.ts.map +1 -0
  309. package/dist/index6.vue.d.ts +48 -0
  310. package/dist/index6.vue.d.ts.map +1 -0
  311. package/dist/index7.vue.d.ts +56 -0
  312. package/dist/index7.vue.d.ts.map +1 -0
  313. package/dist/index8.vue.d.ts +41 -0
  314. package/dist/index8.vue.d.ts.map +1 -0
  315. package/dist/index9.vue.d.ts +30 -0
  316. package/dist/index9.vue.d.ts.map +1 -0
  317. package/dist/storage.js +31 -0
  318. package/dist/storage.js.map +1 -0
  319. package/dist/style.css +7725 -0
  320. package/dist/useCalendarEvents.d.ts +148 -0
  321. package/dist/useCalendarEvents.d.ts.map +1 -0
  322. package/dist/useCollapsePanel.d.ts +132 -0
  323. package/dist/useCollapsePanel.d.ts.map +1 -0
  324. package/dist/useCropperCore.d.ts +102 -0
  325. package/dist/useCropperCore.d.ts.map +1 -0
  326. package/dist/useDraggableLayout.d.ts +194 -0
  327. package/dist/useDraggableLayout.d.ts.map +1 -0
  328. package/dist/useDynamicFormState.d.ts +4248 -0
  329. package/dist/useDynamicFormState.d.ts.map +1 -0
  330. package/dist/useEdgeInteraction.d.ts +7614 -0
  331. package/dist/useEdgeInteraction.d.ts.map +1 -0
  332. package/dist/useFullscreen.d.ts +166 -0
  333. package/dist/useFullscreen.d.ts.map +1 -0
  334. package/dist/useInfiniteScroll.d.ts +169 -0
  335. package/dist/useInfiniteScroll.d.ts.map +1 -0
  336. package/dist/useModalEdit.d.ts +960 -0
  337. package/dist/useModalEdit.d.ts.map +1 -0
  338. package/dist/useQRCode.d.ts +87 -0
  339. package/dist/useQRCode.d.ts.map +1 -0
  340. package/dist/useSearchState.d.ts +180 -0
  341. package/dist/useSearchState.d.ts.map +1 -0
  342. package/dist/useSignatureHistory.d.ts +189 -0
  343. package/dist/useSignatureHistory.d.ts.map +1 -0
  344. package/dist/useSplitResize.d.ts +158 -0
  345. package/dist/useSplitResize.d.ts.map +1 -0
  346. package/dist/useTimeSelection.d.ts +105 -0
  347. package/dist/useTimeSelection.d.ts.map +1 -0
  348. package/dist/useTreeOperations.d.ts +183 -0
  349. package/dist/useTreeOperations.d.ts.map +1 -0
  350. package/dist/useWorkflowValidation.d.ts +1052 -0
  351. package/dist/useWorkflowValidation.d.ts.map +1 -0
  352. package/package.json +342 -0
@@ -0,0 +1,1388 @@
1
+ import { t as C_Icon_default } from "./C_Icon2.js";
2
+ import { t as export_helper_default } from "./export-helper.js";
3
+ import { Fragment, TransitionGroup, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, normalizeClass, onBeforeUnmount, onMounted, openBlock, reactive, readonly, ref, renderList, renderSlot, toDisplayString, unref, watch, withCtx } from "vue";
4
+ import { NButton, NEllipsis, NProgress, NTag } from "naive-ui";
5
+
6
+ //#region src/components/C_Upload/constants.ts
7
+ /**
8
+ * C_Upload 组件常量
9
+ */
10
+ /** 默认分片大小 2MB */
11
+ const DEFAULT_CHUNK_SIZE = 2 * 1024 * 1024;
12
+ /** 默认最大并发数 */
13
+ const DEFAULT_CONCURRENCY = 3;
14
+ /** 大文件阈值(超过此大小自动启用分片) */
15
+ const LARGE_FILE_THRESHOLD = 10 * 1024 * 1024;
16
+ /** 状态文案映射 */
17
+ const STATUS_TEXT = {
18
+ pending: "等待上传",
19
+ hashing: "计算校验…",
20
+ uploading: "上传中…",
21
+ success: "上传成功",
22
+ error: "上传失败",
23
+ paused: "已暂停",
24
+ instant: "秒传成功"
25
+ };
26
+ /** 状态颜色映射(Naive UI NTag type) */
27
+ const STATUS_TYPE = {
28
+ pending: "default",
29
+ hashing: "info",
30
+ uploading: "info",
31
+ success: "success",
32
+ error: "error",
33
+ paused: "warning",
34
+ instant: "success"
35
+ };
36
+ /** 文件图标映射 */
37
+ const FILE_ICON_MAP = {
38
+ "image/": "mdi:file-image-outline",
39
+ "video/": "mdi:file-video-outline",
40
+ "audio/": "mdi:file-music-outline",
41
+ "application/pdf": "mdi:file-pdf-box",
42
+ "application/zip": "mdi:folder-zip-outline",
43
+ "application/x-rar": "mdi:folder-zip-outline",
44
+ "application/vnd.ms-excel": "mdi:file-excel-outline",
45
+ "application/vnd.openxmlformats-officedocument.spreadsheetml": "mdi:file-excel-outline",
46
+ "application/msword": "mdi:file-word-outline",
47
+ "application/vnd.openxmlformats-officedocument.wordprocessingml": "mdi:file-word-outline",
48
+ "text/": "mdi:file-document-outline",
49
+ default: "mdi:file-outline"
50
+ };
51
+ /** 根据 MIME 类型获取图标 */
52
+ function getFileIcon(type) {
53
+ for (const [key, icon] of Object.entries(FILE_ICON_MAP)) if (key !== "default" && type.startsWith(key)) return icon;
54
+ return FILE_ICON_MAP.default;
55
+ }
56
+ /** 格式化文件大小 */
57
+ function formatFileSize(bytes) {
58
+ if (bytes === 0) return "0 B";
59
+ const units = [
60
+ "B",
61
+ "KB",
62
+ "MB",
63
+ "GB",
64
+ "TB"
65
+ ];
66
+ const i = Math.floor(Math.log(bytes) / Math.log(1024));
67
+ return `${(bytes / 1024 ** i).toFixed(i > 0 ? 1 : 0)} ${units[i]}`;
68
+ }
69
+
70
+ //#endregion
71
+ //#region src/components/C_Upload/composables/useFileHash.ts
72
+ /**
73
+ * 文件哈希计算(Web Worker + spark-md5)
74
+ */
75
+ /**
76
+ * 文件哈希计算
77
+ *
78
+ * 使用 spark-md5 在 Web Worker 中对文件分片计算 MD5 hash,
79
+ * 不阻塞主线程,支持进度回调。
80
+ *
81
+ * @param chunkSize 分片大小(bytes),也用于哈希分块读取
82
+ */
83
+ function useFileHash(chunkSize) {
84
+ const hashing = ref(false);
85
+ const hashProgress = ref(0);
86
+ /**
87
+ * 计算文件 hash
88
+ * @param file 原生 File 对象
89
+ * @returns MD5 hash 字符串
90
+ */
91
+ async function calculateHash(file) {
92
+ hashing.value = true;
93
+ hashProgress.value = 0;
94
+ return new Promise((resolve, reject) => {
95
+ const workerCode = `
96
+ self.importScripts('https://cdn.jsdelivr.net/npm/spark-md5@3.0.2/spark-md5.min.js');
97
+
98
+ self.onmessage = function(e) {
99
+ const { chunks } = e.data;
100
+ const spark = new self.SparkMD5.ArrayBuffer();
101
+ let current = 0;
102
+
103
+ function processNext() {
104
+ if (current >= chunks.length) {
105
+ self.postMessage({ type: 'done', hash: spark.end() });
106
+ return;
107
+ }
108
+ const reader = new FileReaderSync();
109
+ const buffer = reader.readAsArrayBuffer(chunks[current]);
110
+ spark.append(buffer);
111
+ current++;
112
+ self.postMessage({ type: 'progress', percent: Math.round((current / chunks.length) * 100) });
113
+ processNext();
114
+ }
115
+
116
+ processNext();
117
+ };
118
+ `;
119
+ const size = chunkSize.value;
120
+ const chunks = [];
121
+ let offset = 0;
122
+ while (offset < file.size) {
123
+ chunks.push(file.slice(offset, offset + size));
124
+ offset += size;
125
+ }
126
+ if (typeof Worker === "undefined") {
127
+ computeHashMainThread(file, chunks).then(resolve).catch(reject).finally(() => {
128
+ hashing.value = false;
129
+ });
130
+ return;
131
+ }
132
+ try {
133
+ const blob = new Blob([workerCode], { type: "application/javascript" });
134
+ const workerUrl = URL.createObjectURL(blob);
135
+ const worker = new Worker(workerUrl);
136
+ worker.onmessage = (e) => {
137
+ const { type, hash, percent } = e.data;
138
+ if (type === "progress") hashProgress.value = percent;
139
+ else if (type === "done") {
140
+ hashing.value = false;
141
+ worker.terminate();
142
+ URL.revokeObjectURL(workerUrl);
143
+ resolve(hash);
144
+ }
145
+ };
146
+ worker.onerror = () => {
147
+ hashing.value = false;
148
+ worker.terminate();
149
+ URL.revokeObjectURL(workerUrl);
150
+ computeHashMainThread(file, chunks).then(resolve).catch(reject);
151
+ };
152
+ worker.postMessage({ chunks });
153
+ } catch {
154
+ computeHashMainThread(file, chunks).then(resolve).catch(reject).finally(() => {
155
+ hashing.value = false;
156
+ });
157
+ }
158
+ });
159
+ }
160
+ /** 主线程降级计算 */
161
+ async function computeHashMainThread(_file, chunks) {
162
+ const spark = new (await (import("spark-md5"))).default.ArrayBuffer();
163
+ for (let i = 0; i < chunks.length; i++) {
164
+ const buffer = await chunks[i].arrayBuffer();
165
+ spark.append(buffer);
166
+ hashProgress.value = Math.round((i + 1) / chunks.length * 100);
167
+ }
168
+ hashing.value = false;
169
+ return spark.end();
170
+ }
171
+ return {
172
+ hashing: readonly(hashing),
173
+ hashProgress: readonly(hashProgress),
174
+ calculateHash
175
+ };
176
+ }
177
+
178
+ //#endregion
179
+ //#region src/components/C_Upload/composables/useChunkUpload.ts
180
+ /**
181
+ * 分片上传引擎
182
+ *
183
+ * 处理大文件分片切割、并发上传、断点续传、分片合并。
184
+ */
185
+ function useChunkUpload(options) {
186
+ /** 正在进行的上传中止控制器映射 uid → abort[] */
187
+ const abortMap = /* @__PURE__ */ new Map();
188
+ /**
189
+ * 将文件切割为分片
190
+ */
191
+ function createChunks(file) {
192
+ const size = options.chunkSize.value;
193
+ const chunks = [];
194
+ let index = 0;
195
+ let offset = 0;
196
+ while (offset < file.size) {
197
+ const end = Math.min(offset + size, file.size);
198
+ chunks.push({
199
+ index,
200
+ blob: file.slice(offset, end),
201
+ size: end - offset,
202
+ uploaded: false
203
+ });
204
+ offset = end;
205
+ index++;
206
+ }
207
+ return chunks;
208
+ }
209
+ /**
210
+ * 查询已上传的分片(断点续传)
211
+ */
212
+ async function queryExistingChunks(hash, chunks) {
213
+ if (!options.uploadedChunksQuery?.value) return;
214
+ try {
215
+ const uploaded = await options.uploadedChunksQuery.value(hash);
216
+ for (const idx of uploaded) {
217
+ const chunk = chunks[idx];
218
+ if (chunk) chunk.uploaded = true;
219
+ }
220
+ } catch {}
221
+ }
222
+ /**
223
+ * 上传单个分片
224
+ */
225
+ function uploadSingleChunk(ctx) {
226
+ return new Promise((resolve, reject) => {
227
+ const requestOptions = {
228
+ action: options.action.value,
229
+ headers: options.headers.value,
230
+ data: {
231
+ ...options.data.value,
232
+ hash: ctx.hash,
233
+ chunkIndex: ctx.chunk.index,
234
+ totalChunks: ctx.totalChunks,
235
+ filename: ctx.file.name
236
+ },
237
+ file: ctx.chunk.blob,
238
+ filename: ctx.file.name,
239
+ hash: ctx.hash,
240
+ chunkIndex: ctx.chunk.index,
241
+ totalChunks: ctx.totalChunks,
242
+ onProgress: () => {},
243
+ onSuccess: () => {
244
+ ctx.chunk.uploaded = true;
245
+ ctx.setUploadedBytes(ctx.chunk.size);
246
+ ctx.onProgress({
247
+ uploadedChunks: ctx.chunks.filter((c) => c.uploaded).length,
248
+ totalChunks: ctx.totalChunks,
249
+ uploadedBytes: ctx.chunks.filter((c) => c.uploaded).reduce((sum, c) => sum + c.size, 0),
250
+ totalBytes: ctx.totalBytes
251
+ });
252
+ resolve();
253
+ },
254
+ onError: (err) => {
255
+ ctx.setError();
256
+ reject(err);
257
+ }
258
+ };
259
+ const req = options.customRequest?.value ? options.customRequest.value(requestOptions) : defaultUploadRequest(requestOptions);
260
+ ctx.abortControllers.push(req.abort);
261
+ });
262
+ }
263
+ /**
264
+ * 执行分片上传
265
+ */
266
+ async function uploadChunks(params) {
267
+ const { uid, file, hash, onProgress, onSuccess, onError, isPaused } = params;
268
+ const chunks = createChunks(file);
269
+ const totalChunks = chunks.length;
270
+ const totalBytes = file.size;
271
+ let uploadedBytes = 0;
272
+ await queryExistingChunks(hash, chunks);
273
+ uploadedBytes = chunks.filter((c) => c.uploaded).reduce((sum, c) => sum + c.size, 0);
274
+ onProgress({
275
+ uploadedChunks: chunks.filter((c) => c.uploaded).length,
276
+ totalChunks,
277
+ uploadedBytes,
278
+ totalBytes
279
+ });
280
+ const pendingChunks = chunks.filter((c) => !c.uploaded);
281
+ if (pendingChunks.length === 0) {
282
+ await mergeAndFinish(hash, file.name, totalChunks, onSuccess, onError);
283
+ return;
284
+ }
285
+ const abortControllers = [];
286
+ abortMap.set(uid, abortControllers);
287
+ const concurrency = options.concurrency.value;
288
+ let current = 0;
289
+ let hasError = false;
290
+ /** 上传下一个分片 */
291
+ async function uploadNext() {
292
+ while (current < pendingChunks.length && !hasError && !isPaused()) {
293
+ const chunk = pendingChunks[current++];
294
+ await uploadSingleChunk({
295
+ chunk,
296
+ hash,
297
+ file,
298
+ totalChunks,
299
+ chunks,
300
+ totalBytes,
301
+ abortControllers,
302
+ onProgress,
303
+ setUploadedBytes: (bytes) => {
304
+ uploadedBytes += bytes;
305
+ },
306
+ setError: () => {
307
+ hasError = true;
308
+ }
309
+ });
310
+ }
311
+ }
312
+ const pool = Array.from({ length: Math.min(concurrency, pendingChunks.length) }, () => uploadNext());
313
+ try {
314
+ await Promise.all(pool);
315
+ if (!hasError && !isPaused()) await mergeAndFinish(hash, file.name, totalChunks, onSuccess, onError);
316
+ } catch (err) {
317
+ onError(err instanceof Error ? err : new Error(String(err)));
318
+ } finally {
319
+ abortMap.delete(uid);
320
+ }
321
+ }
322
+ /** 合并分片 */
323
+ async function mergeAndFinish(hash, filename, totalChunks, onSuccess, onError) {
324
+ if (options.mergeChunks?.value) try {
325
+ onSuccess(await options.mergeChunks.value(hash, filename, totalChunks));
326
+ } catch (err) {
327
+ onError(err instanceof Error ? err : /* @__PURE__ */ new Error("分片合并失败"));
328
+ }
329
+ else onSuccess({ message: "分片上传完成(未配置合并函数)" });
330
+ }
331
+ /** 中止指定文件的分片上传 */
332
+ function abortUpload(uid) {
333
+ abortMap.get(uid)?.forEach((abort) => abort());
334
+ abortMap.delete(uid);
335
+ }
336
+ /** 中止所有 */
337
+ function abortAll() {
338
+ abortMap.forEach((controllers) => {
339
+ controllers.forEach((abort) => abort());
340
+ });
341
+ abortMap.clear();
342
+ }
343
+ return {
344
+ createChunks,
345
+ uploadChunks,
346
+ abortUpload,
347
+ abortAll
348
+ };
349
+ }
350
+ /** 默认 XMLHttpRequest 上传实现 */
351
+ function defaultUploadRequest(options) {
352
+ const xhr = new XMLHttpRequest();
353
+ xhr.open("POST", options.action);
354
+ if (options.headers) Object.entries(options.headers).forEach(([key, value]) => {
355
+ xhr.setRequestHeader(key, value);
356
+ });
357
+ xhr.upload.addEventListener("progress", (e) => {
358
+ if (e.lengthComputable) options.onProgress?.(Math.round(e.loaded / e.total * 100));
359
+ });
360
+ xhr.addEventListener("load", () => {
361
+ if (xhr.status >= 200 && xhr.status < 300) {
362
+ let response;
363
+ try {
364
+ response = JSON.parse(xhr.responseText);
365
+ } catch {
366
+ response = xhr.responseText;
367
+ }
368
+ options.onSuccess?.(response);
369
+ } else options.onError?.(/* @__PURE__ */ new Error(`上传失败: HTTP ${xhr.status}`));
370
+ });
371
+ xhr.addEventListener("error", () => {
372
+ options.onError?.(/* @__PURE__ */ new Error("网络错误"));
373
+ });
374
+ const formData = new FormData();
375
+ formData.append("file", options.file, options.filename);
376
+ if (options.data) Object.entries(options.data).forEach(([key, value]) => {
377
+ formData.append(key, String(value));
378
+ });
379
+ xhr.send(formData);
380
+ return { abort: () => xhr.abort() };
381
+ }
382
+
383
+ //#endregion
384
+ //#region src/components/C_Upload/composables/useUploadQueue.ts
385
+ /**
386
+ * 并发队列管理
387
+ */
388
+ /**
389
+ * 并发队列管理
390
+ *
391
+ * 控制普通文件(非分片)的并发上传数量,
392
+ * 先进先出排队,自动从队列中取出执行。
393
+ */
394
+ function useUploadQueue(options) {
395
+ /** 等待队列 */
396
+ const queue = [];
397
+ /** 正在上传的数量 */
398
+ const activeCount = ref(0);
399
+ /** uid → abort */
400
+ const abortMap = /* @__PURE__ */ new Map();
401
+ /**
402
+ * 添加文件到队列
403
+ */
404
+ function enqueue(file, onProgress, onSuccess, onError) {
405
+ queue.push(file);
406
+ processQueue(onProgress, onSuccess, onError);
407
+ }
408
+ /**
409
+ * 处理队列
410
+ */
411
+ function processQueue(onProgress, onSuccess, onError) {
412
+ while (activeCount.value < options.concurrency.value && queue.length > 0) {
413
+ const file = queue.shift();
414
+ if (!file.raw) continue;
415
+ activeCount.value++;
416
+ const requestOptions = {
417
+ action: options.action.value,
418
+ headers: options.headers.value,
419
+ data: options.data.value,
420
+ file: file.raw,
421
+ filename: file.name,
422
+ onProgress: (percent) => {
423
+ onProgress(file.uid, percent);
424
+ },
425
+ onSuccess: (response) => {
426
+ activeCount.value--;
427
+ abortMap.delete(file.uid);
428
+ onSuccess(file.uid, response);
429
+ processQueue(onProgress, onSuccess, onError);
430
+ },
431
+ onError: (error) => {
432
+ activeCount.value--;
433
+ abortMap.delete(file.uid);
434
+ onError(file.uid, error);
435
+ processQueue(onProgress, onSuccess, onError);
436
+ }
437
+ };
438
+ if (options.customRequest?.value) {
439
+ const { abort } = options.customRequest.value(requestOptions);
440
+ abortMap.set(file.uid, abort);
441
+ } else {
442
+ const { abort } = defaultRequest(requestOptions);
443
+ abortMap.set(file.uid, abort);
444
+ }
445
+ }
446
+ }
447
+ /** 中止指定文件 */
448
+ function abort(uid) {
449
+ abortMap.get(uid)?.();
450
+ abortMap.delete(uid);
451
+ const idx = queue.findIndex((f) => f.uid === uid);
452
+ if (idx !== -1) queue.splice(idx, 1);
453
+ }
454
+ /** 中止所有 */
455
+ function abortAll() {
456
+ abortMap.forEach((fn) => fn());
457
+ abortMap.clear();
458
+ queue.length = 0;
459
+ activeCount.value = 0;
460
+ }
461
+ return {
462
+ activeCount: readonly(activeCount),
463
+ enqueue,
464
+ abort,
465
+ abortAll
466
+ };
467
+ }
468
+ /** 默认 XHR 上传 */
469
+ function defaultRequest(options) {
470
+ const xhr = new XMLHttpRequest();
471
+ xhr.open("POST", options.action);
472
+ if (options.headers) Object.entries(options.headers).forEach(([key, value]) => {
473
+ xhr.setRequestHeader(key, value);
474
+ });
475
+ xhr.upload.addEventListener("progress", (e) => {
476
+ if (e.lengthComputable) options.onProgress?.(Math.round(e.loaded / e.total * 100));
477
+ });
478
+ xhr.addEventListener("load", () => {
479
+ if (xhr.status >= 200 && xhr.status < 300) {
480
+ let response;
481
+ try {
482
+ response = JSON.parse(xhr.responseText);
483
+ } catch {
484
+ response = xhr.responseText;
485
+ }
486
+ options.onSuccess?.(response);
487
+ } else options.onError?.(/* @__PURE__ */ new Error(`上传失败: HTTP ${xhr.status}`));
488
+ });
489
+ xhr.addEventListener("error", () => {
490
+ options.onError?.(/* @__PURE__ */ new Error("网络错误"));
491
+ });
492
+ const formData = new FormData();
493
+ formData.append("file", options.file, options.filename);
494
+ if (options.data) Object.entries(options.data).forEach(([key, value]) => {
495
+ formData.append(key, String(value));
496
+ });
497
+ xhr.send(formData);
498
+ return { abort: () => xhr.abort() };
499
+ }
500
+
501
+ //#endregion
502
+ //#region src/components/C_Upload/composables/useUploadCore.ts
503
+ /**
504
+ * 核心上传逻辑
505
+ */
506
+ /**
507
+ * 核心上传逻辑
508
+ *
509
+ * 统一调度文件校验、hash 计算、秒传检查、分片/普通上传、
510
+ * 进度汇总和状态管理。
511
+ */
512
+ function useUploadCore(props) {
513
+ const action = computed(() => props.action ?? "");
514
+ const headers = computed(() => props.headers ?? {});
515
+ const data = computed(() => props.data ?? {});
516
+ const chunkSize = computed(() => props.chunkSize ?? DEFAULT_CHUNK_SIZE);
517
+ const concurrency = computed(() => props.concurrency ?? DEFAULT_CONCURRENCY);
518
+ const customRequest = computed(() => props.customRequest);
519
+ const instantCheck = computed(() => props.instantCheck);
520
+ const uploadedChunksQuery = computed(() => props.uploadedChunksQuery);
521
+ const mergeChunks = computed(() => props.mergeChunks);
522
+ const fileList = ref([...props.defaultFileList ?? []]);
523
+ const pausedSet = reactive(/* @__PURE__ */ new Set());
524
+ const { hashing, hashProgress, calculateHash } = useFileHash(chunkSize);
525
+ const chunkUploader = useChunkUpload({
526
+ chunkSize,
527
+ concurrency,
528
+ action,
529
+ headers,
530
+ data,
531
+ customRequest,
532
+ uploadedChunksQuery,
533
+ mergeChunks
534
+ });
535
+ const uploadQueue = useUploadQueue({
536
+ concurrency,
537
+ action,
538
+ headers,
539
+ data,
540
+ customRequest
541
+ });
542
+ const totalPercent = computed(() => {
543
+ const list = fileList.value;
544
+ if (list.length === 0) return 0;
545
+ const total = list.reduce((sum, f) => sum + f.percent, 0);
546
+ return Math.round(total / list.length);
547
+ });
548
+ /** 生成唯一 ID */
549
+ function generateUid() {
550
+ return `upload-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
551
+ }
552
+ /** 创建缩略图 */
553
+ function createThumbnail(file) {
554
+ if (file.type.startsWith("image/")) return URL.createObjectURL(file);
555
+ }
556
+ /** 添加文件到列表 */
557
+ function addFiles(files) {
558
+ const items = [];
559
+ for (const file of files) {
560
+ if (props.maxCount && fileList.value.length + items.length >= props.maxCount) break;
561
+ const item = {
562
+ uid: generateUid(),
563
+ name: file.name,
564
+ size: file.size,
565
+ type: file.type,
566
+ status: "pending",
567
+ percent: 0,
568
+ raw: file,
569
+ thumbUrl: createThumbnail(file)
570
+ };
571
+ items.push(item);
572
+ }
573
+ fileList.value.push(...items);
574
+ return items;
575
+ }
576
+ /** 更新文件状态 */
577
+ function updateFile(uid, patch) {
578
+ const file = fileList.value.find((f) => f.uid === uid);
579
+ if (file) Object.assign(file, patch);
580
+ }
581
+ /** 移除文件 */
582
+ function removeFile(uid) {
583
+ chunkUploader.abortUpload(uid);
584
+ uploadQueue.abort(uid);
585
+ pausedSet.delete(uid);
586
+ const idx = fileList.value.findIndex((f) => f.uid === uid);
587
+ if (idx !== -1) {
588
+ const file = fileList.value[idx];
589
+ if (file.thumbUrl) URL.revokeObjectURL(file.thumbUrl);
590
+ fileList.value.splice(idx, 1);
591
+ }
592
+ }
593
+ /** 清空所有 */
594
+ function clearAll() {
595
+ chunkUploader.abortAll();
596
+ uploadQueue.abortAll();
597
+ pausedSet.clear();
598
+ fileList.value.forEach((f) => {
599
+ if (f.thumbUrl) URL.revokeObjectURL(f.thumbUrl);
600
+ });
601
+ fileList.value = [];
602
+ }
603
+ /** 校验单文件 */
604
+ async function validateFile(file) {
605
+ if (props.maxSize && file.size > props.maxSize) return false;
606
+ if (props.beforeUpload) return await props.beforeUpload(file) !== false;
607
+ return true;
608
+ }
609
+ /** 上传单个文件 */
610
+ async function processFile(item) {
611
+ if (!item.raw) return;
612
+ if (!await validateFile(item.raw)) {
613
+ updateFile(item.uid, {
614
+ status: "error",
615
+ error: "文件校验未通过"
616
+ });
617
+ return;
618
+ }
619
+ if (props.chunked && item.raw.size > chunkSize.value) await processChunkedUpload(item);
620
+ else processNormalUpload(item);
621
+ }
622
+ /** 分片上传流程 */
623
+ async function processChunkedUpload(item) {
624
+ const file = item.raw;
625
+ updateFile(item.uid, { status: "hashing" });
626
+ let hash;
627
+ try {
628
+ hash = await calculateHash(file);
629
+ updateFile(item.uid, { hash });
630
+ } catch {
631
+ updateFile(item.uid, {
632
+ status: "error",
633
+ error: "Hash 计算失败"
634
+ });
635
+ return;
636
+ }
637
+ if (instantCheck.value) try {
638
+ const result = await instantCheck.value(hash, file.name);
639
+ if (result.exists) {
640
+ updateFile(item.uid, {
641
+ status: "instant",
642
+ percent: 100,
643
+ url: result.url
644
+ });
645
+ return;
646
+ }
647
+ } catch {}
648
+ updateFile(item.uid, { status: "uploading" });
649
+ await chunkUploader.uploadChunks({
650
+ uid: item.uid,
651
+ file,
652
+ hash,
653
+ onProgress: (progress) => {
654
+ const percent = Math.round(progress.uploadedBytes / progress.totalBytes * 100);
655
+ updateFile(item.uid, {
656
+ percent,
657
+ chunkProgress: progress
658
+ });
659
+ },
660
+ onSuccess: (response) => {
661
+ updateFile(item.uid, {
662
+ status: "success",
663
+ percent: 100,
664
+ response
665
+ });
666
+ },
667
+ onError: (error) => {
668
+ updateFile(item.uid, {
669
+ status: "error",
670
+ error: error.message
671
+ });
672
+ },
673
+ isPaused: () => pausedSet.has(item.uid)
674
+ });
675
+ }
676
+ /** 普通上传 */
677
+ function processNormalUpload(item) {
678
+ updateFile(item.uid, { status: "uploading" });
679
+ uploadQueue.enqueue(item, (uid, percent) => {
680
+ updateFile(uid, { percent });
681
+ }, (uid, response) => {
682
+ updateFile(uid, {
683
+ status: "success",
684
+ percent: 100,
685
+ response
686
+ });
687
+ }, (uid, error) => {
688
+ updateFile(uid, {
689
+ status: "error",
690
+ error: error.message
691
+ });
692
+ });
693
+ }
694
+ /** 开始上传(上传所有 pending 文件) */
695
+ function startUpload() {
696
+ fileList.value.filter((f) => f.status === "pending").forEach(processFile);
697
+ }
698
+ /** 暂停所有 */
699
+ function pauseAll() {
700
+ fileList.value.forEach((f) => {
701
+ if (f.status === "uploading" || f.status === "hashing") {
702
+ pausedSet.add(f.uid);
703
+ updateFile(f.uid, { status: "paused" });
704
+ }
705
+ });
706
+ }
707
+ /** 恢复所有 */
708
+ function resumeAll() {
709
+ fileList.value.forEach((f) => {
710
+ if (f.status === "paused") {
711
+ pausedSet.delete(f.uid);
712
+ updateFile(f.uid, { status: "pending" });
713
+ }
714
+ });
715
+ startUpload();
716
+ }
717
+ /** 重试单个文件 */
718
+ function retryFile(uid) {
719
+ const file = fileList.value.find((f) => f.uid === uid);
720
+ if (file && file.status === "error") {
721
+ updateFile(uid, {
722
+ status: "pending",
723
+ percent: 0,
724
+ error: void 0
725
+ });
726
+ processFile(file);
727
+ }
728
+ }
729
+ /** 获取成功列表 */
730
+ function getSuccessList() {
731
+ return fileList.value.filter((f) => f.status === "success" || f.status === "instant");
732
+ }
733
+ onBeforeUnmount(() => {
734
+ fileList.value.forEach((f) => {
735
+ if (f.thumbUrl) URL.revokeObjectURL(f.thumbUrl);
736
+ });
737
+ });
738
+ return {
739
+ fileList,
740
+ totalPercent,
741
+ hashing,
742
+ hashProgress,
743
+ addFiles,
744
+ removeFile,
745
+ clearAll,
746
+ startUpload,
747
+ pauseAll,
748
+ resumeAll,
749
+ retryFile,
750
+ processFile,
751
+ getSuccessList
752
+ };
753
+ }
754
+
755
+ //#endregion
756
+ //#region src/components/C_Upload/composables/useDragDrop.ts
757
+ /**
758
+ * 拖拽 & 粘贴上传
759
+ */
760
+ /**
761
+ * 拖拽 & 粘贴上传
762
+ *
763
+ * 监听容器的 dragover / drop / paste 事件,
764
+ * 提取文件(含目录遍历)后回调给上层。
765
+ *
766
+ * @param containerRef 容器 DOM
767
+ * @param enabled 是否启用拖拽
768
+ * @param pasteable 是否启用粘贴
769
+ * @param accept 文件类型限制
770
+ * @param onFiles 文件回调
771
+ */
772
+ function useDragDrop(containerRef, enabled, pasteable, accept, onFiles) {
773
+ /** 是否正在拖拽悬停 */
774
+ const isDragOver = ref(false);
775
+ /** 处理拖入事件 */
776
+ function handleDragEnter(e) {
777
+ if (!enabled.value) return;
778
+ e.preventDefault();
779
+ isDragOver.value = true;
780
+ }
781
+ /** 处理拖拽经过事件 */
782
+ function handleDragOver(e) {
783
+ if (!enabled.value) return;
784
+ e.preventDefault();
785
+ isDragOver.value = true;
786
+ }
787
+ /** 处理拖离事件 */
788
+ function handleDragLeave(e) {
789
+ if (!enabled.value) return;
790
+ e.preventDefault();
791
+ const container = containerRef.value;
792
+ if (container && !container.contains(e.relatedTarget)) isDragOver.value = false;
793
+ }
794
+ /** 处理放置事件 */
795
+ async function handleDrop(e) {
796
+ if (!enabled.value) return;
797
+ e.preventDefault();
798
+ isDragOver.value = false;
799
+ const items = e.dataTransfer?.items;
800
+ if (!items) return;
801
+ const filtered = filterByAccept(await collectDropFiles(items, e.dataTransfer), accept.value);
802
+ if (filtered.length > 0) onFiles(filtered);
803
+ }
804
+ /** 粘贴事件 */
805
+ function handlePaste(e) {
806
+ if (!pasteable.value) return;
807
+ const items = e.clipboardData?.items;
808
+ if (!items) return;
809
+ const files = [];
810
+ for (let i = 0; i < items.length; i++) {
811
+ const item = items[i];
812
+ if (item.kind === "file") {
813
+ const file = item.getAsFile();
814
+ if (file) files.push(file);
815
+ }
816
+ }
817
+ const filtered = filterByAccept(files, accept.value);
818
+ if (filtered.length > 0) {
819
+ e.preventDefault();
820
+ onFiles(filtered);
821
+ }
822
+ }
823
+ /** 绑定事件 */
824
+ function bindEvents() {
825
+ const el = containerRef.value;
826
+ if (!el) return;
827
+ el.addEventListener("dragenter", handleDragEnter);
828
+ el.addEventListener("dragover", handleDragOver);
829
+ el.addEventListener("dragleave", handleDragLeave);
830
+ el.addEventListener("drop", handleDrop);
831
+ if (pasteable.value) document.addEventListener("paste", handlePaste);
832
+ }
833
+ /** 解绑事件 */
834
+ function unbindEvents() {
835
+ const el = containerRef.value;
836
+ if (el) {
837
+ el.removeEventListener("dragenter", handleDragEnter);
838
+ el.removeEventListener("dragover", handleDragOver);
839
+ el.removeEventListener("dragleave", handleDragLeave);
840
+ el.removeEventListener("drop", handleDrop);
841
+ }
842
+ document.removeEventListener("paste", handlePaste);
843
+ }
844
+ onMounted(bindEvents);
845
+ onBeforeUnmount(unbindEvents);
846
+ watch([
847
+ containerRef,
848
+ enabled,
849
+ pasteable
850
+ ], () => {
851
+ unbindEvents();
852
+ bindEvents();
853
+ });
854
+ return { isDragOver: readonly(isDragOver) };
855
+ }
856
+ /** 从 drop 事件收集文件(含目录遍历) */
857
+ async function collectDropFiles(items, dataTransfer) {
858
+ const entries = [];
859
+ for (let i = 0; i < items.length; i++) {
860
+ const entry = items[i].webkitGetAsEntry?.();
861
+ if (entry) entries.push(entry);
862
+ }
863
+ if (entries.length > 0) return (await Promise.all(entries.map(readEntry))).flat();
864
+ const files = [];
865
+ const dtFiles = dataTransfer?.files;
866
+ if (dtFiles) for (let i = 0; i < dtFiles.length; i++) files.push(dtFiles[i]);
867
+ return files;
868
+ }
869
+ /** 递归读取 FileSystemEntry */
870
+ async function readEntry(entry) {
871
+ if (entry.isFile) return new Promise((resolve) => {
872
+ entry.file((file) => resolve([file]), () => resolve([]));
873
+ });
874
+ if (entry.isDirectory) {
875
+ const reader = entry.createReader();
876
+ const entries = await new Promise((resolve) => {
877
+ reader.readEntries((result) => resolve(result), () => resolve([]));
878
+ });
879
+ return (await Promise.all(entries.map(readEntry))).flat();
880
+ }
881
+ return [];
882
+ }
883
+ /** 根据 accept 过滤文件 */
884
+ function filterByAccept(files, accept) {
885
+ if (!accept) return files;
886
+ const acceptTypes = accept.split(",").map((s) => s.trim().toLowerCase());
887
+ return files.filter((file) => {
888
+ return acceptTypes.some((type) => {
889
+ if (type.startsWith(".")) return file.name.toLowerCase().endsWith(type);
890
+ if (type.endsWith("/*")) {
891
+ const prefix = type.replace("/*", "");
892
+ return file.type.toLowerCase().startsWith(prefix);
893
+ }
894
+ return file.type.toLowerCase() === type;
895
+ });
896
+ });
897
+ }
898
+
899
+ //#endregion
900
+ //#region src/components/C_Upload/components/UploadArea.vue?vue&type=script&setup=true&lang.ts
901
+ const _hoisted_1$3 = [
902
+ "accept",
903
+ "multiple",
904
+ "webkitdirectory"
905
+ ];
906
+ const _hoisted_2$3 = { class: "upload-area__default" };
907
+ const _hoisted_3$3 = { class: "upload-area__text" };
908
+ const _hoisted_4$2 = { key: 0 };
909
+ const _hoisted_5$2 = { key: 1 };
910
+ const _hoisted_6$2 = {
911
+ key: 0,
912
+ class: "upload-area__tip"
913
+ };
914
+ var UploadArea_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
915
+ __name: "UploadArea",
916
+ props: {
917
+ accept: { default: "" },
918
+ multiple: {
919
+ type: Boolean,
920
+ default: false
921
+ },
922
+ directory: {
923
+ type: Boolean,
924
+ default: false
925
+ },
926
+ disabled: {
927
+ type: Boolean,
928
+ default: false
929
+ },
930
+ draggable: {
931
+ type: Boolean,
932
+ default: true
933
+ },
934
+ pasteable: {
935
+ type: Boolean,
936
+ default: false
937
+ },
938
+ tip: { default: "" }
939
+ },
940
+ emits: ["files"],
941
+ setup(__props, { expose: __expose, emit: __emit }) {
942
+ const props = __props;
943
+ const emit = __emit;
944
+ const areaRef = ref();
945
+ const inputRef = ref();
946
+ const { isDragOver } = useDragDrop(areaRef, computed(() => props.draggable && !props.disabled), computed(() => props.pasteable && !props.disabled), computed(() => props.accept), (files) => emit("files", files));
947
+ /** 点击触发文件选择 */
948
+ function handleClick() {
949
+ if (props.disabled) return;
950
+ inputRef.value?.click();
951
+ }
952
+ /** input change */
953
+ function handleInputChange(e) {
954
+ const input = e.target;
955
+ const files = Array.from(input.files ?? []);
956
+ if (files.length > 0) emit("files", files);
957
+ input.value = "";
958
+ }
959
+ /** 暴露触发方法 */
960
+ __expose({ triggerSelect: handleClick });
961
+ return (_ctx, _cache) => {
962
+ return openBlock(), createElementBlock("div", {
963
+ ref_key: "areaRef",
964
+ ref: areaRef,
965
+ class: normalizeClass(["upload-area", {
966
+ "upload-area--drag-over": unref(isDragOver),
967
+ "upload-area--disabled": _ctx.disabled
968
+ }]),
969
+ onClick: handleClick
970
+ }, [createElementVNode("input", {
971
+ ref_key: "inputRef",
972
+ ref: inputRef,
973
+ type: "file",
974
+ class: "upload-area__input",
975
+ accept: _ctx.accept,
976
+ multiple: _ctx.multiple,
977
+ webkitdirectory: _ctx.directory || void 0,
978
+ onChange: handleInputChange
979
+ }, null, 40, _hoisted_1$3), renderSlot(_ctx.$slots, "default", {}, () => [createElementVNode("div", _hoisted_2$3, [
980
+ createVNode(C_Icon_default, {
981
+ name: unref(isDragOver) ? "mdi:cloud-download-outline" : "mdi:cloud-upload-outline",
982
+ class: "upload-area__icon"
983
+ }, null, 8, ["name"]),
984
+ createElementVNode("div", _hoisted_3$3, [unref(isDragOver) ? (openBlock(), createElementBlock("span", _hoisted_4$2, "释放文件到此处")) : (openBlock(), createElementBlock("span", _hoisted_5$2, [_cache[3] || (_cache[3] = createTextVNode(" 点击或拖拽文件到此区域上传 ", -1)), _ctx.pasteable ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
985
+ _cache[0] || (_cache[0] = createTextVNode(" ,支持 ", -1)),
986
+ _cache[1] || (_cache[1] = createElementVNode("kbd", null, "Ctrl+V", -1)),
987
+ _cache[2] || (_cache[2] = createTextVNode(" 粘贴 ", -1))
988
+ ], 64)) : createCommentVNode("v-if", true)]))]),
989
+ _ctx.tip ? (openBlock(), createElementBlock("div", _hoisted_6$2, toDisplayString(_ctx.tip), 1)) : createCommentVNode("v-if", true)
990
+ ])], true)], 2);
991
+ };
992
+ }
993
+ });
994
+
995
+ //#endregion
996
+ //#region src/components/C_Upload/components/UploadArea.vue
997
+ var UploadArea_default = /* @__PURE__ */ export_helper_default(UploadArea_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-2e419e04"]]);
998
+
999
+ //#endregion
1000
+ //#region src/components/C_Upload/components/FileList.vue?vue&type=script&setup=true&lang.ts
1001
+ const _hoisted_1$2 = { class: "file-list__thumb" };
1002
+ const _hoisted_2$2 = ["src", "alt"];
1003
+ const _hoisted_3$2 = { class: "file-list__info" };
1004
+ const _hoisted_4$1 = { class: "file-list__name" };
1005
+ const _hoisted_5$1 = { class: "file-list__meta" };
1006
+ const _hoisted_6$1 = { class: "file-list__size" };
1007
+ const _hoisted_7 = {
1008
+ key: 0,
1009
+ class: "file-list__instant"
1010
+ };
1011
+ const _hoisted_8 = {
1012
+ key: 1,
1013
+ class: "file-list__chunk-info"
1014
+ };
1015
+ const _hoisted_9 = {
1016
+ key: 2,
1017
+ class: "file-list__error"
1018
+ };
1019
+ const _hoisted_10 = { class: "file-list__actions" };
1020
+ var FileList_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
1021
+ __name: "FileList",
1022
+ props: {
1023
+ fileList: {},
1024
+ showThumbnail: { type: Boolean },
1025
+ hashProgress: {}
1026
+ },
1027
+ emits: ["remove", "retry"],
1028
+ setup(__props, { emit: __emit }) {
1029
+ const emit = __emit;
1030
+ return (_ctx, _cache) => {
1031
+ return openBlock(), createBlock(TransitionGroup, {
1032
+ name: "file-list",
1033
+ tag: "div",
1034
+ class: "file-list"
1035
+ }, {
1036
+ default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.fileList, (file) => {
1037
+ return openBlock(), createElementBlock("div", {
1038
+ key: file.uid,
1039
+ class: normalizeClass(["file-list__item", `file-list__item--${file.status}`])
1040
+ }, [
1041
+ createCommentVNode(" 缩略图 / 图标 "),
1042
+ createElementVNode("div", _hoisted_1$2, [_ctx.showThumbnail && file.thumbUrl ? (openBlock(), createElementBlock("img", {
1043
+ key: 0,
1044
+ src: file.thumbUrl,
1045
+ alt: file.name,
1046
+ class: "file-list__thumb-img"
1047
+ }, null, 8, _hoisted_2$2)) : (openBlock(), createBlock(C_Icon_default, {
1048
+ key: 1,
1049
+ name: unref(getFileIcon)(file.type),
1050
+ class: "file-list__thumb-icon"
1051
+ }, null, 8, ["name"]))]),
1052
+ createCommentVNode(" 文件信息 "),
1053
+ createElementVNode("div", _hoisted_3$2, [
1054
+ createElementVNode("div", _hoisted_4$1, [createVNode(unref(NEllipsis), { "line-clamp": 1 }, {
1055
+ default: withCtx(() => [createTextVNode(toDisplayString(file.name), 1)]),
1056
+ _: 2
1057
+ }, 1024)]),
1058
+ createElementVNode("div", _hoisted_5$1, [
1059
+ createElementVNode("span", _hoisted_6$1, toDisplayString(unref(formatFileSize)(file.size)), 1),
1060
+ createVNode(unref(NTag), {
1061
+ type: unref(STATUS_TYPE)[file.status] || "default",
1062
+ size: "tiny",
1063
+ bordered: false
1064
+ }, {
1065
+ default: withCtx(() => [createTextVNode(toDisplayString(unref(STATUS_TEXT)[file.status] || file.status), 1)]),
1066
+ _: 2
1067
+ }, 1032, ["type"]),
1068
+ file.status === "instant" ? (openBlock(), createElementBlock("span", _hoisted_7, " ⚡ ")) : createCommentVNode("v-if", true)
1069
+ ]),
1070
+ createCommentVNode(" 进度条 "),
1071
+ file.status === "uploading" || file.status === "hashing" ? (openBlock(), createBlock(unref(NProgress), {
1072
+ key: 0,
1073
+ percentage: file.status === "hashing" ? _ctx.hashProgress : file.percent,
1074
+ height: 4,
1075
+ "border-radius": 2,
1076
+ "show-indicator": false,
1077
+ status: file.status === "hashing" ? "info" : "success"
1078
+ }, null, 8, ["percentage", "status"])) : createCommentVNode("v-if", true),
1079
+ createCommentVNode(" 分片进度描述 "),
1080
+ file.chunkProgress && file.status === "uploading" ? (openBlock(), createElementBlock("div", _hoisted_8, " 分片 " + toDisplayString(file.chunkProgress.uploadedChunks) + "/" + toDisplayString(file.chunkProgress.totalChunks), 1)) : createCommentVNode("v-if", true),
1081
+ createCommentVNode(" 错误信息 "),
1082
+ file.status === "error" && file.error ? (openBlock(), createElementBlock("div", _hoisted_9, toDisplayString(file.error), 1)) : createCommentVNode("v-if", true)
1083
+ ]),
1084
+ createCommentVNode(" 操作按钮 "),
1085
+ createElementVNode("div", _hoisted_10, [file.status === "error" ? (openBlock(), createBlock(unref(NButton), {
1086
+ key: 0,
1087
+ text: "",
1088
+ type: "warning",
1089
+ size: "tiny",
1090
+ onClick: ($event) => emit("retry", file.uid)
1091
+ }, {
1092
+ icon: withCtx(() => [createVNode(C_Icon_default, { name: "mdi:refresh" })]),
1093
+ _: 2
1094
+ }, 1032, ["onClick"])) : createCommentVNode("v-if", true), createVNode(unref(NButton), {
1095
+ text: "",
1096
+ type: "error",
1097
+ size: "tiny",
1098
+ onClick: ($event) => emit("remove", file.uid)
1099
+ }, {
1100
+ icon: withCtx(() => [createVNode(C_Icon_default, { name: "mdi:close" })]),
1101
+ _: 2
1102
+ }, 1032, ["onClick"])])
1103
+ ], 2);
1104
+ }), 128))]),
1105
+ _: 1
1106
+ });
1107
+ };
1108
+ }
1109
+ });
1110
+
1111
+ //#endregion
1112
+ //#region src/components/C_Upload/components/FileList.vue
1113
+ var FileList_default = /* @__PURE__ */ export_helper_default(FileList_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-8e4ad054"]]);
1114
+
1115
+ //#endregion
1116
+ //#region src/components/C_Upload/components/ImagePreview.vue?vue&type=script&setup=true&lang.ts
1117
+ const _hoisted_1$1 = {
1118
+ key: 0,
1119
+ class: "image-preview"
1120
+ };
1121
+ const _hoisted_2$1 = ["src", "alt"];
1122
+ const _hoisted_3$1 = { class: "image-preview__overlay" };
1123
+ const _hoisted_4 = {
1124
+ key: 0,
1125
+ class: "image-preview__progress"
1126
+ };
1127
+ const _hoisted_5 = { class: "image-preview__actions" };
1128
+ const _hoisted_6 = { class: "image-preview__name" };
1129
+ var ImagePreview_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
1130
+ __name: "ImagePreview",
1131
+ props: { fileList: {} },
1132
+ emits: ["remove", "retry"],
1133
+ setup(__props, { emit: __emit }) {
1134
+ const props = __props;
1135
+ const emit = __emit;
1136
+ /** 过滤出图片文件 */
1137
+ const imageFiles = computed(() => props.fileList.filter((f) => f.type?.startsWith("image/")));
1138
+ return (_ctx, _cache) => {
1139
+ return imageFiles.value.length > 0 ? (openBlock(), createElementBlock("div", _hoisted_1$1, [(openBlock(true), createElementBlock(Fragment, null, renderList(imageFiles.value, (file) => {
1140
+ return openBlock(), createElementBlock("div", {
1141
+ key: file.uid,
1142
+ class: normalizeClass(["image-preview__item", `image-preview__item--${file.status}`])
1143
+ }, [
1144
+ file.thumbUrl ? (openBlock(), createElementBlock("img", {
1145
+ key: 0,
1146
+ src: file.thumbUrl,
1147
+ alt: file.name,
1148
+ class: "image-preview__img"
1149
+ }, null, 8, _hoisted_2$1)) : createCommentVNode("v-if", true),
1150
+ createCommentVNode(" 遮罩层 "),
1151
+ createElementVNode("div", _hoisted_3$1, [
1152
+ createCommentVNode(" 上传中进度 "),
1153
+ file.status === "uploading" || file.status === "hashing" ? (openBlock(), createElementBlock("div", _hoisted_4, [createVNode(unref(NProgress), {
1154
+ type: "circle",
1155
+ percentage: file.percent,
1156
+ "stroke-width": 3,
1157
+ style: { "width": "40px" }
1158
+ }, null, 8, ["percentage"])])) : file.status === "success" || file.status === "instant" ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [createCommentVNode(" 状态图标 "), createVNode(C_Icon_default, {
1159
+ name: "mdi:check-circle",
1160
+ class: "image-preview__status-icon image-preview__status-icon--success"
1161
+ })], 2112)) : file.status === "error" ? (openBlock(), createBlock(C_Icon_default, {
1162
+ key: 2,
1163
+ name: "mdi:alert-circle",
1164
+ class: "image-preview__status-icon image-preview__status-icon--error"
1165
+ })) : createCommentVNode("v-if", true),
1166
+ createCommentVNode(" 操作 "),
1167
+ createElementVNode("div", _hoisted_5, [file.status === "error" ? (openBlock(), createBlock(unref(NButton), {
1168
+ key: 0,
1169
+ circle: "",
1170
+ size: "tiny",
1171
+ type: "warning",
1172
+ onClick: ($event) => emit("retry", file.uid)
1173
+ }, {
1174
+ icon: withCtx(() => [createVNode(C_Icon_default, { name: "mdi:refresh" })]),
1175
+ _: 2
1176
+ }, 1032, ["onClick"])) : createCommentVNode("v-if", true), createVNode(unref(NButton), {
1177
+ circle: "",
1178
+ size: "tiny",
1179
+ type: "error",
1180
+ onClick: ($event) => emit("remove", file.uid)
1181
+ }, {
1182
+ icon: withCtx(() => [createVNode(C_Icon_default, { name: "mdi:close" })]),
1183
+ _: 2
1184
+ }, 1032, ["onClick"])])
1185
+ ]),
1186
+ createCommentVNode(" 文件名 "),
1187
+ createElementVNode("div", _hoisted_6, [createVNode(unref(NEllipsis), { "line-clamp": 1 }, {
1188
+ default: withCtx(() => [createTextVNode(toDisplayString(file.name), 1)]),
1189
+ _: 2
1190
+ }, 1024)])
1191
+ ], 2);
1192
+ }), 128))])) : createCommentVNode("v-if", true);
1193
+ };
1194
+ }
1195
+ });
1196
+
1197
+ //#endregion
1198
+ //#region src/components/C_Upload/components/ImagePreview.vue
1199
+ var ImagePreview_default = /* @__PURE__ */ export_helper_default(ImagePreview_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-76efbe9c"]]);
1200
+
1201
+ //#endregion
1202
+ //#region src/components/C_Upload/index.vue?vue&type=script&setup=true&lang.ts
1203
+ const _hoisted_1 = { class: "c-upload" };
1204
+ const _hoisted_2 = {
1205
+ key: 0,
1206
+ class: "c-upload__total-progress"
1207
+ };
1208
+ const _hoisted_3 = { class: "c-upload__total-label" };
1209
+ var index_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
1210
+ __name: "index",
1211
+ props: {
1212
+ action: { default: "" },
1213
+ headers: { default: () => ({}) },
1214
+ data: { default: () => ({}) },
1215
+ multiple: {
1216
+ type: Boolean,
1217
+ default: false
1218
+ },
1219
+ directory: {
1220
+ type: Boolean,
1221
+ default: false
1222
+ },
1223
+ accept: { default: "" },
1224
+ maxSize: { default: 0 },
1225
+ maxCount: { default: 0 },
1226
+ disabled: {
1227
+ type: Boolean,
1228
+ default: false
1229
+ },
1230
+ draggable: {
1231
+ type: Boolean,
1232
+ default: true
1233
+ },
1234
+ pasteable: {
1235
+ type: Boolean,
1236
+ default: false
1237
+ },
1238
+ chunked: {
1239
+ type: Boolean,
1240
+ default: false
1241
+ },
1242
+ chunkSize: { default: DEFAULT_CHUNK_SIZE },
1243
+ concurrency: { default: DEFAULT_CONCURRENCY },
1244
+ showFileList: {
1245
+ type: Boolean,
1246
+ default: true
1247
+ },
1248
+ listType: { default: "text" },
1249
+ showThumbnail: {
1250
+ type: Boolean,
1251
+ default: true
1252
+ },
1253
+ customRequest: {},
1254
+ instantCheck: {},
1255
+ uploadedChunksQuery: {},
1256
+ mergeChunks: {},
1257
+ beforeUpload: {},
1258
+ tip: { default: "" },
1259
+ defaultFileList: { default: () => [] }
1260
+ },
1261
+ emits: [
1262
+ "change",
1263
+ "success",
1264
+ "error",
1265
+ "progress",
1266
+ "remove",
1267
+ "finish",
1268
+ "exceed"
1269
+ ],
1270
+ setup(__props, { expose: __expose, emit: __emit }) {
1271
+ const props = __props;
1272
+ const emit = __emit;
1273
+ const uploadAreaRef = ref();
1274
+ const { fileList, totalPercent, hashProgress, addFiles, removeFile, clearAll, startUpload, pauseAll, resumeAll, retryFile, processFile, getSuccessList } = useUploadCore(props);
1275
+ const showTotalProgress = computed(() => {
1276
+ return fileList.value.some((f) => f.status === "uploading" || f.status === "hashing");
1277
+ });
1278
+ /** 处理接收到的文件 */
1279
+ function handleFiles(files) {
1280
+ if (props.maxCount && fileList.value.length + files.length > props.maxCount) {
1281
+ emit("exceed", files, fileList.value);
1282
+ const available = props.maxCount - fileList.value.length;
1283
+ if (available <= 0) return;
1284
+ files = files.slice(0, available);
1285
+ }
1286
+ if (props.maxSize) files = files.filter((f) => f.size <= props.maxSize);
1287
+ const items = addFiles(files);
1288
+ emit("change", fileList.value);
1289
+ items.forEach(processFile);
1290
+ }
1291
+ /** 移除文件 */
1292
+ function handleRemove(uid) {
1293
+ const file = fileList.value.find((f) => f.uid === uid);
1294
+ if (file) {
1295
+ emit("remove", file);
1296
+ removeFile(uid);
1297
+ emit("change", fileList.value);
1298
+ }
1299
+ }
1300
+ /** 重试上传 */
1301
+ function handleRetry(uid) {
1302
+ retryFile(uid);
1303
+ }
1304
+ watch(fileList, (list) => {
1305
+ if (list.length > 0 && list.every((f) => f.status === "success" || f.status === "instant" || f.status === "error")) emit("finish", list);
1306
+ }, { deep: true });
1307
+ __expose({
1308
+ selectFiles: () => uploadAreaRef.value?.triggerSelect(),
1309
+ startUpload,
1310
+ pauseAll,
1311
+ resumeAll,
1312
+ clearAll: () => {
1313
+ clearAll();
1314
+ emit("change", fileList.value);
1315
+ },
1316
+ removeFile: (uid) => {
1317
+ handleRemove(uid);
1318
+ },
1319
+ retryFile,
1320
+ getFileList: () => fileList.value,
1321
+ getSuccessList,
1322
+ get totalPercent() {
1323
+ return totalPercent.value;
1324
+ }
1325
+ });
1326
+ return (_ctx, _cache) => {
1327
+ return openBlock(), createElementBlock("div", _hoisted_1, [
1328
+ createCommentVNode(" 上传区域 "),
1329
+ createVNode(UploadArea_default, {
1330
+ ref_key: "uploadAreaRef",
1331
+ ref: uploadAreaRef,
1332
+ accept: props.accept,
1333
+ multiple: props.multiple,
1334
+ directory: props.directory,
1335
+ disabled: props.disabled,
1336
+ draggable: props.draggable,
1337
+ pasteable: props.pasteable,
1338
+ tip: props.tip,
1339
+ onFiles: handleFiles
1340
+ }, {
1341
+ default: withCtx(() => [renderSlot(_ctx.$slots, "area", {}, void 0, true)]),
1342
+ _: 3
1343
+ }, 8, [
1344
+ "accept",
1345
+ "multiple",
1346
+ "directory",
1347
+ "disabled",
1348
+ "draggable",
1349
+ "pasteable",
1350
+ "tip"
1351
+ ]),
1352
+ createCommentVNode(" 总进度条 "),
1353
+ showTotalProgress.value ? (openBlock(), createElementBlock("div", _hoisted_2, [createElementVNode("div", _hoisted_3, [_cache[0] || (_cache[0] = createElementVNode("span", null, "总进度", -1)), createElementVNode("span", null, toDisplayString(unref(totalPercent)) + "%", 1)]), createVNode(unref(NProgress), {
1354
+ percentage: unref(totalPercent),
1355
+ height: 6,
1356
+ "border-radius": 3,
1357
+ "show-indicator": false,
1358
+ status: "success"
1359
+ }, null, 8, ["percentage"])])) : createCommentVNode("v-if", true),
1360
+ createCommentVNode(" 图片预览模式 "),
1361
+ props.listType === "image" ? (openBlock(), createBlock(ImagePreview_default, {
1362
+ key: 1,
1363
+ "file-list": unref(fileList),
1364
+ onRemove: handleRemove,
1365
+ onRetry: handleRetry
1366
+ }, null, 8, ["file-list"])) : props.showFileList && unref(fileList).length > 0 ? (openBlock(), createElementBlock(Fragment, { key: 2 }, [createCommentVNode(" 文件列表模式 "), createVNode(FileList_default, {
1367
+ "file-list": unref(fileList),
1368
+ "show-thumbnail": props.showThumbnail,
1369
+ "hash-progress": unref(hashProgress),
1370
+ onRemove: handleRemove,
1371
+ onRetry: handleRetry
1372
+ }, null, 8, [
1373
+ "file-list",
1374
+ "show-thumbnail",
1375
+ "hash-progress"
1376
+ ])], 2112)) : createCommentVNode("v-if", true)
1377
+ ]);
1378
+ };
1379
+ }
1380
+ });
1381
+
1382
+ //#endregion
1383
+ //#region src/components/C_Upload/index.vue
1384
+ var C_Upload_default = /* @__PURE__ */ export_helper_default(index_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-adade9f9"]]);
1385
+
1386
+ //#endregion
1387
+ export { useChunkUpload as a, getFileIcon as c, useUploadQueue as i, useDragDrop as n, useFileHash as o, useUploadCore as r, formatFileSize as s, C_Upload_default as t };
1388
+ //# sourceMappingURL=C_Upload2.js.map