Upload.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
  2. import _extends from "@babel/runtime/helpers/esm/extends";
  3. import { createVNode as _createVNode, resolveDirective as _resolveDirective } from "vue";
  4. var __awaiter = this && this.__awaiter || function (thisArg, _arguments, P, generator) {
  5. function adopt(value) {
  6. return value instanceof P ? value : new P(function (resolve) {
  7. resolve(value);
  8. });
  9. }
  10. return new (P || (P = Promise))(function (resolve, reject) {
  11. function fulfilled(value) {
  12. try {
  13. step(generator.next(value));
  14. } catch (e) {
  15. reject(e);
  16. }
  17. }
  18. function rejected(value) {
  19. try {
  20. step(generator["throw"](value));
  21. } catch (e) {
  22. reject(e);
  23. }
  24. }
  25. function step(result) {
  26. result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
  27. }
  28. step((generator = generator.apply(thisArg, _arguments || [])).next());
  29. });
  30. };
  31. var __rest = this && this.__rest || function (s, e) {
  32. var t = {};
  33. for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p];
  34. if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
  35. if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]];
  36. }
  37. return t;
  38. };
  39. import VcUpload from '../vc-upload';
  40. import UploadList from './UploadList';
  41. import { uploadProps } from './interface';
  42. import { file2Obj, getFileItem, removeFileItem, updateFileList } from './utils';
  43. import { useLocaleReceiver } from '../locale-provider/LocaleReceiver';
  44. import defaultLocale from '../locale/en_US';
  45. import { computed, defineComponent, onMounted, ref, toRef } from 'vue';
  46. import { flattenChildren, initDefaultProps } from '../_util/props-util';
  47. import useMergedState from '../_util/hooks/useMergedState';
  48. import devWarning from '../vc-util/devWarning';
  49. import useConfigInject from '../config-provider/hooks/useConfigInject';
  50. import classNames from '../_util/classNames';
  51. import { useInjectFormItemContext } from '../form';
  52. // CSSINJS
  53. import useStyle from './style';
  54. import { useInjectDisabled } from '../config-provider/DisabledContext';
  55. export const LIST_IGNORE = `__LIST_IGNORE_${Date.now()}__`;
  56. export default defineComponent({
  57. compatConfig: {
  58. MODE: 3
  59. },
  60. name: 'AUpload',
  61. inheritAttrs: false,
  62. props: initDefaultProps(uploadProps(), {
  63. type: 'select',
  64. multiple: false,
  65. action: '',
  66. data: {},
  67. accept: '',
  68. showUploadList: true,
  69. listType: 'text',
  70. supportServerRender: true
  71. }),
  72. setup(props, _ref) {
  73. let {
  74. slots,
  75. attrs,
  76. expose
  77. } = _ref;
  78. const formItemContext = useInjectFormItemContext();
  79. const {
  80. prefixCls,
  81. direction,
  82. disabled
  83. } = useConfigInject('upload', props);
  84. // style
  85. const [wrapSSR, hashId] = useStyle(prefixCls);
  86. const disabledContext = useInjectDisabled();
  87. const mergedDisabled = computed(() => {
  88. var _a;
  89. return (_a = disabled.value) !== null && _a !== void 0 ? _a : disabledContext.value;
  90. });
  91. const [mergedFileList, setMergedFileList] = useMergedState(props.defaultFileList || [], {
  92. value: toRef(props, 'fileList'),
  93. postState: list => {
  94. const timestamp = Date.now();
  95. return (list !== null && list !== void 0 ? list : []).map((file, index) => {
  96. if (!file.uid && !Object.isFrozen(file)) {
  97. file.uid = `__AUTO__${timestamp}_${index}__`;
  98. }
  99. return file;
  100. });
  101. }
  102. });
  103. const dragState = ref('drop');
  104. const upload = ref(null);
  105. onMounted(() => {
  106. devWarning(props.fileList !== undefined || attrs.value === undefined, 'Upload', '`value` is not a valid prop, do you mean `fileList`?');
  107. devWarning(props.transformFile === undefined, 'Upload', '`transformFile` is deprecated. Please use `beforeUpload` directly.');
  108. devWarning(props.remove === undefined, 'Upload', '`remove` props is deprecated. Please use `remove` event.');
  109. });
  110. const onInternalChange = (file, changedFileList, event) => {
  111. var _a, _b;
  112. let cloneList = [...changedFileList];
  113. // Cut to match count
  114. if (props.maxCount === 1) {
  115. cloneList = cloneList.slice(-1);
  116. } else if (props.maxCount) {
  117. cloneList = cloneList.slice(0, props.maxCount);
  118. }
  119. setMergedFileList(cloneList);
  120. const changeInfo = {
  121. file: file,
  122. fileList: cloneList
  123. };
  124. if (event) {
  125. changeInfo.event = event;
  126. }
  127. (_a = props['onUpdate:fileList']) === null || _a === void 0 ? void 0 : _a.call(props, changeInfo.fileList);
  128. (_b = props.onChange) === null || _b === void 0 ? void 0 : _b.call(props, changeInfo);
  129. formItemContext.onFieldChange();
  130. };
  131. const mergedBeforeUpload = (file, fileListArgs) => __awaiter(this, void 0, void 0, function* () {
  132. const {
  133. beforeUpload,
  134. transformFile
  135. } = props;
  136. let parsedFile = file;
  137. if (beforeUpload) {
  138. const result = yield beforeUpload(file, fileListArgs);
  139. if (result === false) {
  140. return false;
  141. }
  142. // Hack for LIST_IGNORE, we add additional info to remove from the list
  143. delete file[LIST_IGNORE];
  144. if (result === LIST_IGNORE) {
  145. Object.defineProperty(file, LIST_IGNORE, {
  146. value: true,
  147. configurable: true
  148. });
  149. return false;
  150. }
  151. if (typeof result === 'object' && result) {
  152. parsedFile = result;
  153. }
  154. }
  155. if (transformFile) {
  156. parsedFile = yield transformFile(parsedFile);
  157. }
  158. return parsedFile;
  159. });
  160. const onBatchStart = batchFileInfoList => {
  161. // Skip file which marked as `LIST_IGNORE`, these file will not add to file list
  162. const filteredFileInfoList = batchFileInfoList.filter(info => !info.file[LIST_IGNORE]);
  163. // Nothing to do since no file need upload
  164. if (!filteredFileInfoList.length) {
  165. return;
  166. }
  167. const objectFileList = filteredFileInfoList.map(info => file2Obj(info.file));
  168. // Concat new files with prev files
  169. let newFileList = [...mergedFileList.value];
  170. objectFileList.forEach(fileObj => {
  171. // Replace file if exist
  172. newFileList = updateFileList(fileObj, newFileList);
  173. });
  174. objectFileList.forEach((fileObj, index) => {
  175. // Repeat trigger `onChange` event for compatible
  176. let triggerFileObj = fileObj;
  177. if (!filteredFileInfoList[index].parsedFile) {
  178. // `beforeUpload` return false
  179. const {
  180. originFileObj
  181. } = fileObj;
  182. let clone;
  183. try {
  184. clone = new File([originFileObj], originFileObj.name, {
  185. type: originFileObj.type
  186. });
  187. } catch (e) {
  188. clone = new Blob([originFileObj], {
  189. type: originFileObj.type
  190. });
  191. clone.name = originFileObj.name;
  192. clone.lastModifiedDate = new Date();
  193. clone.lastModified = new Date().getTime();
  194. }
  195. clone.uid = fileObj.uid;
  196. triggerFileObj = clone;
  197. } else {
  198. // Inject `uploading` status
  199. fileObj.status = 'uploading';
  200. }
  201. onInternalChange(triggerFileObj, newFileList);
  202. });
  203. };
  204. const onSuccess = (response, file, xhr) => {
  205. try {
  206. if (typeof response === 'string') {
  207. response = JSON.parse(response);
  208. }
  209. } catch (e) {
  210. /* do nothing */
  211. }
  212. // removed
  213. if (!getFileItem(file, mergedFileList.value)) {
  214. return;
  215. }
  216. const targetItem = file2Obj(file);
  217. targetItem.status = 'done';
  218. targetItem.percent = 100;
  219. targetItem.response = response;
  220. targetItem.xhr = xhr;
  221. const nextFileList = updateFileList(targetItem, mergedFileList.value);
  222. onInternalChange(targetItem, nextFileList);
  223. };
  224. const onProgress = (e, file) => {
  225. // removed
  226. if (!getFileItem(file, mergedFileList.value)) {
  227. return;
  228. }
  229. const targetItem = file2Obj(file);
  230. targetItem.status = 'uploading';
  231. targetItem.percent = e.percent;
  232. const nextFileList = updateFileList(targetItem, mergedFileList.value);
  233. onInternalChange(targetItem, nextFileList, e);
  234. };
  235. const onError = (error, response, file) => {
  236. // removed
  237. if (!getFileItem(file, mergedFileList.value)) {
  238. return;
  239. }
  240. const targetItem = file2Obj(file);
  241. targetItem.error = error;
  242. targetItem.response = response;
  243. targetItem.status = 'error';
  244. const nextFileList = updateFileList(targetItem, mergedFileList.value);
  245. onInternalChange(targetItem, nextFileList);
  246. };
  247. const handleRemove = file => {
  248. let currentFile;
  249. const mergedRemove = props.onRemove || props.remove;
  250. Promise.resolve(typeof mergedRemove === 'function' ? mergedRemove(file) : mergedRemove).then(ret => {
  251. var _a, _b;
  252. // Prevent removing file
  253. if (ret === false) {
  254. return;
  255. }
  256. const removedFileList = removeFileItem(file, mergedFileList.value);
  257. if (removedFileList) {
  258. currentFile = _extends(_extends({}, file), {
  259. status: 'removed'
  260. });
  261. (_a = mergedFileList.value) === null || _a === void 0 ? void 0 : _a.forEach(item => {
  262. const matchKey = currentFile.uid !== undefined ? 'uid' : 'name';
  263. if (item[matchKey] === currentFile[matchKey] && !Object.isFrozen(item)) {
  264. item.status = 'removed';
  265. }
  266. });
  267. (_b = upload.value) === null || _b === void 0 ? void 0 : _b.abort(currentFile);
  268. onInternalChange(currentFile, removedFileList);
  269. }
  270. });
  271. };
  272. const onFileDrop = e => {
  273. var _a;
  274. dragState.value = e.type;
  275. if (e.type === 'drop') {
  276. (_a = props.onDrop) === null || _a === void 0 ? void 0 : _a.call(props, e);
  277. }
  278. };
  279. expose({
  280. onBatchStart,
  281. onSuccess,
  282. onProgress,
  283. onError,
  284. fileList: mergedFileList,
  285. upload
  286. });
  287. const [locale] = useLocaleReceiver('Upload', defaultLocale.Upload, computed(() => props.locale));
  288. const renderUploadList = (button, buttonVisible) => {
  289. const {
  290. removeIcon,
  291. previewIcon,
  292. downloadIcon,
  293. previewFile,
  294. onPreview,
  295. onDownload,
  296. isImageUrl,
  297. progress,
  298. itemRender,
  299. iconRender,
  300. showUploadList
  301. } = props;
  302. const {
  303. showDownloadIcon,
  304. showPreviewIcon,
  305. showRemoveIcon
  306. } = typeof showUploadList === 'boolean' ? {} : showUploadList;
  307. return showUploadList ? _createVNode(UploadList, {
  308. "prefixCls": prefixCls.value,
  309. "listType": props.listType,
  310. "items": mergedFileList.value,
  311. "previewFile": previewFile,
  312. "onPreview": onPreview,
  313. "onDownload": onDownload,
  314. "onRemove": handleRemove,
  315. "showRemoveIcon": !mergedDisabled.value && showRemoveIcon,
  316. "showPreviewIcon": showPreviewIcon,
  317. "showDownloadIcon": showDownloadIcon,
  318. "removeIcon": removeIcon,
  319. "previewIcon": previewIcon,
  320. "downloadIcon": downloadIcon,
  321. "iconRender": iconRender,
  322. "locale": locale.value,
  323. "isImageUrl": isImageUrl,
  324. "progress": progress,
  325. "itemRender": itemRender,
  326. "appendActionVisible": buttonVisible,
  327. "appendAction": button
  328. }, _extends({}, slots)) : button === null || button === void 0 ? void 0 : button();
  329. };
  330. return () => {
  331. var _a, _b, _c;
  332. const {
  333. listType,
  334. type
  335. } = props;
  336. const {
  337. class: className,
  338. style: styleName
  339. } = attrs,
  340. transAttrs = __rest(attrs, ["class", "style"]);
  341. const rcUploadProps = _extends(_extends(_extends({
  342. onBatchStart,
  343. onError,
  344. onProgress,
  345. onSuccess
  346. }, transAttrs), props), {
  347. id: (_a = props.id) !== null && _a !== void 0 ? _a : formItemContext.id.value,
  348. prefixCls: prefixCls.value,
  349. beforeUpload: mergedBeforeUpload,
  350. onChange: undefined,
  351. disabled: mergedDisabled.value
  352. });
  353. delete rcUploadProps.remove;
  354. // Remove id to avoid open by label when trigger is hidden
  355. // !children: https://github.com/ant-design/ant-design/issues/14298
  356. // disabled: https://github.com/ant-design/ant-design/issues/16478
  357. // https://github.com/ant-design/ant-design/issues/24197
  358. if (!slots.default || mergedDisabled.value) {
  359. delete rcUploadProps.id;
  360. }
  361. const rtlCls = {
  362. [`${prefixCls.value}-rtl`]: direction.value === 'rtl'
  363. };
  364. if (type === 'drag') {
  365. const dragCls = classNames(prefixCls.value, {
  366. [`${prefixCls.value}-drag`]: true,
  367. [`${prefixCls.value}-drag-uploading`]: mergedFileList.value.some(file => file.status === 'uploading'),
  368. [`${prefixCls.value}-drag-hover`]: dragState.value === 'dragover',
  369. [`${prefixCls.value}-disabled`]: mergedDisabled.value,
  370. [`${prefixCls.value}-rtl`]: direction.value === 'rtl'
  371. }, attrs.class, hashId.value);
  372. return wrapSSR(_createVNode("span", _objectSpread(_objectSpread({}, attrs), {}, {
  373. "class": classNames(`${prefixCls.value}-wrapper`, rtlCls, className, hashId.value)
  374. }), [_createVNode("div", {
  375. "class": dragCls,
  376. "onDrop": onFileDrop,
  377. "onDragover": onFileDrop,
  378. "onDragleave": onFileDrop,
  379. "style": attrs.style
  380. }, [_createVNode(VcUpload, _objectSpread(_objectSpread({}, rcUploadProps), {}, {
  381. "ref": upload,
  382. "class": `${prefixCls.value}-btn`
  383. }), _objectSpread({
  384. default: () => [_createVNode("div", {
  385. "class": `${prefixCls.value}-drag-container`
  386. }, [(_b = slots.default) === null || _b === void 0 ? void 0 : _b.call(slots)])]
  387. }, slots))]), renderUploadList()]));
  388. }
  389. const uploadButtonCls = classNames(prefixCls.value, {
  390. [`${prefixCls.value}-select`]: true,
  391. [`${prefixCls.value}-select-${listType}`]: true,
  392. [`${prefixCls.value}-disabled`]: mergedDisabled.value,
  393. [`${prefixCls.value}-rtl`]: direction.value === 'rtl'
  394. });
  395. const children = flattenChildren((_c = slots.default) === null || _c === void 0 ? void 0 : _c.call(slots));
  396. const renderUploadButton = uploadButtonStyle => _createVNode("div", {
  397. "class": uploadButtonCls,
  398. "style": uploadButtonStyle
  399. }, [_createVNode(VcUpload, _objectSpread(_objectSpread({}, rcUploadProps), {}, {
  400. "ref": upload
  401. }), slots)]);
  402. if (listType === 'picture-card') {
  403. return wrapSSR(_createVNode("span", _objectSpread(_objectSpread({}, attrs), {}, {
  404. "class": classNames(`${prefixCls.value}-wrapper`, `${prefixCls.value}-picture-card-wrapper`, rtlCls, attrs.class, hashId.value)
  405. }), [renderUploadList(renderUploadButton, !!(children && children.length))]));
  406. }
  407. return wrapSSR(_createVNode("span", _objectSpread(_objectSpread({}, attrs), {}, {
  408. "class": classNames(`${prefixCls.value}-wrapper`, rtlCls, attrs.class, hashId.value)
  409. }), [renderUploadButton(children && children.length ? undefined : {
  410. display: 'none'
  411. }), renderUploadList()]));
  412. };
  413. }
  414. });