AjaxUploader.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
  2. import { createVNode as _createVNode } from "vue";
  3. var __awaiter = this && this.__awaiter || function (thisArg, _arguments, P, generator) {
  4. function adopt(value) {
  5. return value instanceof P ? value : new P(function (resolve) {
  6. resolve(value);
  7. });
  8. }
  9. return new (P || (P = Promise))(function (resolve, reject) {
  10. function fulfilled(value) {
  11. try {
  12. step(generator.next(value));
  13. } catch (e) {
  14. reject(e);
  15. }
  16. }
  17. function rejected(value) {
  18. try {
  19. step(generator["throw"](value));
  20. } catch (e) {
  21. reject(e);
  22. }
  23. }
  24. function step(result) {
  25. result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
  26. }
  27. step((generator = generator.apply(thisArg, _arguments || [])).next());
  28. });
  29. };
  30. var __rest = this && this.__rest || function (s, e) {
  31. var t = {};
  32. for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p];
  33. if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
  34. if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]];
  35. }
  36. return t;
  37. };
  38. import defaultRequest from './request';
  39. import getUid from './uid';
  40. import attrAccept from './attr-accept';
  41. import traverseFileTree from './traverseFileTree';
  42. import { uploadProps } from './interface';
  43. import { defineComponent, onBeforeUnmount, onMounted, ref } from 'vue';
  44. import pickAttrs from '../_util/pickAttrs';
  45. import partition from 'lodash-es/partition';
  46. export default defineComponent({
  47. compatConfig: {
  48. MODE: 3
  49. },
  50. name: 'AjaxUploader',
  51. inheritAttrs: false,
  52. props: uploadProps(),
  53. setup(props, _ref) {
  54. let {
  55. slots,
  56. attrs,
  57. expose
  58. } = _ref;
  59. const uid = ref(getUid());
  60. const reqs = {};
  61. const fileInput = ref();
  62. let isMounted = false;
  63. /**
  64. * Process file before upload. When all the file is ready, we start upload.
  65. */
  66. const processFile = (file, fileList) => __awaiter(this, void 0, void 0, function* () {
  67. const {
  68. beforeUpload
  69. } = props;
  70. let transformedFile = file;
  71. if (beforeUpload) {
  72. try {
  73. transformedFile = yield beforeUpload(file, fileList);
  74. } catch (e) {
  75. // Rejection will also trade as false
  76. transformedFile = false;
  77. }
  78. if (transformedFile === false) {
  79. return {
  80. origin: file,
  81. parsedFile: null,
  82. action: null,
  83. data: null
  84. };
  85. }
  86. }
  87. // Get latest action
  88. const {
  89. action
  90. } = props;
  91. let mergedAction;
  92. if (typeof action === 'function') {
  93. mergedAction = yield action(file);
  94. } else {
  95. mergedAction = action;
  96. }
  97. // Get latest data
  98. const {
  99. data
  100. } = props;
  101. let mergedData;
  102. if (typeof data === 'function') {
  103. mergedData = yield data(file);
  104. } else {
  105. mergedData = data;
  106. }
  107. const parsedData =
  108. // string type is from legacy `transformFile`.
  109. // Not sure if this will work since no related test case works with it
  110. (typeof transformedFile === 'object' || typeof transformedFile === 'string') && transformedFile ? transformedFile : file;
  111. let parsedFile;
  112. if (parsedData instanceof File) {
  113. parsedFile = parsedData;
  114. } else {
  115. parsedFile = new File([parsedData], file.name, {
  116. type: file.type
  117. });
  118. }
  119. const mergedParsedFile = parsedFile;
  120. mergedParsedFile.uid = file.uid;
  121. return {
  122. origin: file,
  123. data: mergedData,
  124. parsedFile: mergedParsedFile,
  125. action: mergedAction
  126. };
  127. });
  128. const post = _ref2 => {
  129. let {
  130. data,
  131. origin,
  132. action,
  133. parsedFile
  134. } = _ref2;
  135. if (!isMounted) {
  136. return;
  137. }
  138. const {
  139. onStart,
  140. customRequest,
  141. name,
  142. headers,
  143. withCredentials,
  144. method
  145. } = props;
  146. const {
  147. uid
  148. } = origin;
  149. const request = customRequest || defaultRequest;
  150. const requestOption = {
  151. action,
  152. filename: name,
  153. data,
  154. file: parsedFile,
  155. headers,
  156. withCredentials,
  157. method: method || 'post',
  158. onProgress: e => {
  159. const {
  160. onProgress
  161. } = props;
  162. onProgress === null || onProgress === void 0 ? void 0 : onProgress(e, parsedFile);
  163. },
  164. onSuccess: (ret, xhr) => {
  165. const {
  166. onSuccess
  167. } = props;
  168. onSuccess === null || onSuccess === void 0 ? void 0 : onSuccess(ret, parsedFile, xhr);
  169. delete reqs[uid];
  170. },
  171. onError: (err, ret) => {
  172. const {
  173. onError
  174. } = props;
  175. onError === null || onError === void 0 ? void 0 : onError(err, ret, parsedFile);
  176. delete reqs[uid];
  177. }
  178. };
  179. onStart(origin);
  180. reqs[uid] = request(requestOption);
  181. };
  182. const reset = () => {
  183. uid.value = getUid();
  184. };
  185. const abort = file => {
  186. if (file) {
  187. const uid = file.uid ? file.uid : file;
  188. if (reqs[uid] && reqs[uid].abort) {
  189. reqs[uid].abort();
  190. }
  191. delete reqs[uid];
  192. } else {
  193. Object.keys(reqs).forEach(uid => {
  194. if (reqs[uid] && reqs[uid].abort) {
  195. reqs[uid].abort();
  196. }
  197. delete reqs[uid];
  198. });
  199. }
  200. };
  201. onMounted(() => {
  202. isMounted = true;
  203. });
  204. onBeforeUnmount(() => {
  205. isMounted = false;
  206. abort();
  207. });
  208. const uploadFiles = files => {
  209. const originFiles = [...files];
  210. const postFiles = originFiles.map(file => {
  211. // eslint-disable-next-line no-param-reassign
  212. file.uid = getUid();
  213. return processFile(file, originFiles);
  214. });
  215. // Batch upload files
  216. Promise.all(postFiles).then(fileList => {
  217. const {
  218. onBatchStart
  219. } = props;
  220. onBatchStart === null || onBatchStart === void 0 ? void 0 : onBatchStart(fileList.map(_ref3 => {
  221. let {
  222. origin,
  223. parsedFile
  224. } = _ref3;
  225. return {
  226. file: origin,
  227. parsedFile
  228. };
  229. }));
  230. fileList.filter(file => file.parsedFile !== null).forEach(file => {
  231. post(file);
  232. });
  233. });
  234. };
  235. const onChange = e => {
  236. const {
  237. accept,
  238. directory
  239. } = props;
  240. const {
  241. files
  242. } = e.target;
  243. const acceptedFiles = [...files].filter(file => !directory || attrAccept(file, accept));
  244. uploadFiles(acceptedFiles);
  245. reset();
  246. };
  247. const onClick = e => {
  248. const el = fileInput.value;
  249. if (!el) {
  250. return;
  251. }
  252. const {
  253. onClick
  254. } = props;
  255. // TODO
  256. // if (children && (children as any).type === 'button') {
  257. // const parent = el.parentNode as HTMLInputElement;
  258. // parent.focus();
  259. // parent.querySelector('button').blur();
  260. // }
  261. el.click();
  262. if (onClick) {
  263. onClick(e);
  264. }
  265. };
  266. const onKeyDown = e => {
  267. if (e.key === 'Enter') {
  268. onClick(e);
  269. }
  270. };
  271. const onFileDrop = e => {
  272. const {
  273. multiple
  274. } = props;
  275. e.preventDefault();
  276. if (e.type === 'dragover') {
  277. return;
  278. }
  279. if (props.directory) {
  280. traverseFileTree(Array.prototype.slice.call(e.dataTransfer.items), uploadFiles, _file => attrAccept(_file, props.accept));
  281. } else {
  282. const files = partition(Array.prototype.slice.call(e.dataTransfer.files), file => attrAccept(file, props.accept));
  283. let successFiles = files[0];
  284. const errorFiles = files[1];
  285. if (multiple === false) {
  286. successFiles = successFiles.slice(0, 1);
  287. }
  288. uploadFiles(successFiles);
  289. if (errorFiles.length && props.onReject) props.onReject(errorFiles);
  290. }
  291. };
  292. expose({
  293. abort
  294. });
  295. return () => {
  296. var _a;
  297. const {
  298. componentTag: Tag,
  299. prefixCls,
  300. disabled,
  301. id,
  302. multiple,
  303. accept,
  304. capture,
  305. directory,
  306. openFileDialogOnClick,
  307. onMouseenter,
  308. onMouseleave
  309. } = props,
  310. otherProps = __rest(props, ["componentTag", "prefixCls", "disabled", "id", "multiple", "accept", "capture", "directory", "openFileDialogOnClick", "onMouseenter", "onMouseleave"]);
  311. const cls = {
  312. [prefixCls]: true,
  313. [`${prefixCls}-disabled`]: disabled,
  314. [attrs.class]: !!attrs.class
  315. };
  316. // because input don't have directory/webkitdirectory type declaration
  317. const dirProps = directory ? {
  318. directory: 'directory',
  319. webkitdirectory: 'webkitdirectory'
  320. } : {};
  321. const events = disabled ? {} : {
  322. onClick: openFileDialogOnClick ? onClick : () => {},
  323. onKeydown: openFileDialogOnClick ? onKeyDown : () => {},
  324. onMouseenter,
  325. onMouseleave,
  326. onDrop: onFileDrop,
  327. onDragover: onFileDrop,
  328. tabindex: '0'
  329. };
  330. return _createVNode(Tag, _objectSpread(_objectSpread({}, events), {}, {
  331. "class": cls,
  332. "role": "button",
  333. "style": attrs.style
  334. }), {
  335. default: () => [_createVNode("input", _objectSpread(_objectSpread(_objectSpread({}, pickAttrs(otherProps, {
  336. aria: true,
  337. data: true
  338. })), {}, {
  339. "id": id,
  340. "type": "file",
  341. "ref": fileInput,
  342. "onClick": e => e.stopPropagation(),
  343. "onCancel": e => e.stopPropagation(),
  344. "key": uid.value,
  345. "style": {
  346. display: 'none'
  347. },
  348. "accept": accept
  349. }, dirProps), {}, {
  350. "multiple": multiple,
  351. "onChange": onChange
  352. }, capture != null ? {
  353. capture
  354. } : {}), null), (_a = slots.default) === null || _a === void 0 ? void 0 : _a.call(slots)]
  355. });
  356. };
  357. }
  358. });