index.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. import _extends from "@babel/runtime/helpers/esm/extends";
  2. // Utils
  3. import { createNamespace, addUnit, noop, isPromise, isDef } from '../utils';
  4. import { toArray, readFile as _readFile, isOversize, isImageFile } from './utils'; // Mixins
  5. import { FieldMixin } from '../mixins/field'; // Components
  6. import Icon from '../icon';
  7. import Image from '../image';
  8. import Loading from '../loading';
  9. import ImagePreview from '../image-preview';
  10. var _createNamespace = createNamespace('uploader'),
  11. createComponent = _createNamespace[0],
  12. bem = _createNamespace[1];
  13. export default createComponent({
  14. inheritAttrs: false,
  15. mixins: [FieldMixin],
  16. model: {
  17. prop: 'fileList'
  18. },
  19. props: {
  20. disabled: Boolean,
  21. readonly: Boolean,
  22. lazyLoad: Boolean,
  23. uploadText: String,
  24. afterRead: Function,
  25. beforeRead: Function,
  26. beforeDelete: Function,
  27. previewSize: [Number, String],
  28. previewOptions: Object,
  29. name: {
  30. type: [Number, String],
  31. default: ''
  32. },
  33. accept: {
  34. type: String,
  35. default: 'image/*'
  36. },
  37. fileList: {
  38. type: Array,
  39. default: function _default() {
  40. return [];
  41. }
  42. },
  43. maxSize: {
  44. type: [Number, String, Function],
  45. default: Number.MAX_VALUE
  46. },
  47. maxCount: {
  48. type: [Number, String],
  49. default: Number.MAX_VALUE
  50. },
  51. deletable: {
  52. type: Boolean,
  53. default: true
  54. },
  55. showUpload: {
  56. type: Boolean,
  57. default: true
  58. },
  59. previewImage: {
  60. type: Boolean,
  61. default: true
  62. },
  63. previewFullImage: {
  64. type: Boolean,
  65. default: true
  66. },
  67. imageFit: {
  68. type: String,
  69. default: 'cover'
  70. },
  71. resultType: {
  72. type: String,
  73. default: 'dataUrl'
  74. },
  75. uploadIcon: {
  76. type: String,
  77. default: 'photograph'
  78. }
  79. },
  80. computed: {
  81. previewSizeWithUnit: function previewSizeWithUnit() {
  82. return addUnit(this.previewSize);
  83. },
  84. // for form
  85. value: function value() {
  86. return this.fileList;
  87. }
  88. },
  89. methods: {
  90. getDetail: function getDetail(index) {
  91. if (index === void 0) {
  92. index = this.fileList.length;
  93. }
  94. return {
  95. name: this.name,
  96. index: index
  97. };
  98. },
  99. onChange: function onChange(event) {
  100. var _this = this;
  101. var files = event.target.files;
  102. if (this.disabled || !files.length) {
  103. return;
  104. }
  105. files = files.length === 1 ? files[0] : [].slice.call(files);
  106. if (this.beforeRead) {
  107. var response = this.beforeRead(files, this.getDetail());
  108. if (!response) {
  109. this.resetInput();
  110. return;
  111. }
  112. if (isPromise(response)) {
  113. response.then(function (data) {
  114. if (data) {
  115. _this.readFile(data);
  116. } else {
  117. _this.readFile(files);
  118. }
  119. }).catch(this.resetInput);
  120. return;
  121. }
  122. }
  123. this.readFile(files);
  124. },
  125. readFile: function readFile(files) {
  126. var _this2 = this;
  127. var oversize = isOversize(files, this.maxSize);
  128. if (Array.isArray(files)) {
  129. var maxCount = this.maxCount - this.fileList.length;
  130. if (files.length > maxCount) {
  131. files = files.slice(0, maxCount);
  132. }
  133. Promise.all(files.map(function (file) {
  134. return _readFile(file, _this2.resultType);
  135. })).then(function (contents) {
  136. var fileList = files.map(function (file, index) {
  137. var result = {
  138. file: file,
  139. status: '',
  140. message: ''
  141. };
  142. if (contents[index]) {
  143. result.content = contents[index];
  144. }
  145. return result;
  146. });
  147. _this2.onAfterRead(fileList, oversize);
  148. });
  149. } else {
  150. _readFile(files, this.resultType).then(function (content) {
  151. var result = {
  152. file: files,
  153. status: '',
  154. message: ''
  155. };
  156. if (content) {
  157. result.content = content;
  158. }
  159. _this2.onAfterRead(result, oversize);
  160. });
  161. }
  162. },
  163. onAfterRead: function onAfterRead(files, oversize) {
  164. var _this3 = this;
  165. this.resetInput();
  166. var validFiles = files;
  167. if (oversize) {
  168. var oversizeFiles = files;
  169. if (Array.isArray(files)) {
  170. oversizeFiles = [];
  171. validFiles = [];
  172. files.forEach(function (item) {
  173. if (item.file) {
  174. if (isOversize(item.file, _this3.maxSize)) {
  175. oversizeFiles.push(item);
  176. } else {
  177. validFiles.push(item);
  178. }
  179. }
  180. });
  181. } else {
  182. validFiles = null;
  183. }
  184. this.$emit('oversize', oversizeFiles, this.getDetail());
  185. }
  186. var isValidFiles = Array.isArray(validFiles) ? Boolean(validFiles.length) : Boolean(validFiles);
  187. if (isValidFiles) {
  188. this.$emit('input', [].concat(this.fileList, toArray(validFiles)));
  189. if (this.afterRead) {
  190. this.afterRead(validFiles, this.getDetail());
  191. }
  192. }
  193. },
  194. onDelete: function onDelete(file, index) {
  195. var _file$beforeDelete,
  196. _this4 = this;
  197. var beforeDelete = (_file$beforeDelete = file.beforeDelete) != null ? _file$beforeDelete : this.beforeDelete;
  198. if (beforeDelete) {
  199. var response = beforeDelete(file, this.getDetail(index));
  200. if (!response) {
  201. return;
  202. }
  203. if (isPromise(response)) {
  204. response.then(function () {
  205. _this4.deleteFile(file, index);
  206. }).catch(noop);
  207. return;
  208. }
  209. }
  210. this.deleteFile(file, index);
  211. },
  212. deleteFile: function deleteFile(file, index) {
  213. var fileList = this.fileList.slice(0);
  214. fileList.splice(index, 1);
  215. this.$emit('input', fileList);
  216. this.$emit('delete', file, this.getDetail(index));
  217. },
  218. resetInput: function resetInput() {
  219. /* istanbul ignore else */
  220. if (this.$refs.input) {
  221. this.$refs.input.value = '';
  222. }
  223. },
  224. onClickUpload: function onClickUpload(event) {
  225. this.$emit('click-upload', event);
  226. },
  227. onPreviewImage: function onPreviewImage(item) {
  228. var _this5 = this;
  229. if (!this.previewFullImage) {
  230. return;
  231. }
  232. var imageFiles = this.fileList.filter(function (item) {
  233. return isImageFile(item);
  234. });
  235. var imageContents = imageFiles.map(function (item) {
  236. return item.content || item.url;
  237. });
  238. this.imagePreview = ImagePreview(_extends({
  239. images: imageContents,
  240. startPosition: imageFiles.indexOf(item),
  241. onClose: function onClose() {
  242. _this5.$emit('close-preview');
  243. }
  244. }, this.previewOptions));
  245. },
  246. // @exposed-api
  247. closeImagePreview: function closeImagePreview() {
  248. if (this.imagePreview) {
  249. this.imagePreview.close();
  250. }
  251. },
  252. // @exposed-api
  253. chooseFile: function chooseFile() {
  254. if (this.disabled) {
  255. return;
  256. }
  257. /* istanbul ignore else */
  258. if (this.$refs.input) {
  259. this.$refs.input.click();
  260. }
  261. },
  262. genPreviewMask: function genPreviewMask(item) {
  263. var h = this.$createElement;
  264. var status = item.status,
  265. message = item.message;
  266. if (status === 'uploading' || status === 'failed') {
  267. var MaskIcon = status === 'failed' ? h(Icon, {
  268. "attrs": {
  269. "name": "close"
  270. },
  271. "class": bem('mask-icon')
  272. }) : h(Loading, {
  273. "class": bem('loading')
  274. });
  275. var showMessage = isDef(message) && message !== '';
  276. return h("div", {
  277. "class": bem('mask')
  278. }, [MaskIcon, showMessage && h("div", {
  279. "class": bem('mask-message')
  280. }, [message])]);
  281. }
  282. },
  283. genPreviewItem: function genPreviewItem(item, index) {
  284. var _item$deletable,
  285. _this6 = this,
  286. _item$previewSize,
  287. _item$imageFit;
  288. var h = this.$createElement;
  289. var deleteAble = (_item$deletable = item.deletable) != null ? _item$deletable : this.deletable;
  290. var showDelete = item.status !== 'uploading' && deleteAble;
  291. var DeleteIcon = showDelete && h("div", {
  292. "class": bem('preview-delete'),
  293. "on": {
  294. "click": function click(event) {
  295. event.stopPropagation();
  296. _this6.onDelete(item, index);
  297. }
  298. }
  299. }, [h(Icon, {
  300. "attrs": {
  301. "name": "cross"
  302. },
  303. "class": bem('preview-delete-icon')
  304. })]);
  305. var PreviewCoverContent = this.slots('preview-cover', _extends({
  306. index: index
  307. }, item));
  308. var PreviewCover = PreviewCoverContent && h("div", {
  309. "class": bem('preview-cover')
  310. }, [PreviewCoverContent]);
  311. var previewSize = (_item$previewSize = item.previewSize) != null ? _item$previewSize : this.previewSize;
  312. var imageFit = (_item$imageFit = item.imageFit) != null ? _item$imageFit : this.imageFit;
  313. var Preview = isImageFile(item) ? h(Image, {
  314. "attrs": {
  315. "fit": imageFit,
  316. "src": item.content || item.url,
  317. "width": previewSize,
  318. "height": previewSize,
  319. "lazyLoad": this.lazyLoad
  320. },
  321. "class": bem('preview-image'),
  322. "on": {
  323. "click": function click() {
  324. _this6.onPreviewImage(item);
  325. }
  326. }
  327. }, [PreviewCover]) : h("div", {
  328. "class": bem('file'),
  329. "style": {
  330. width: this.previewSizeWithUnit,
  331. height: this.previewSizeWithUnit
  332. }
  333. }, [h(Icon, {
  334. "class": bem('file-icon'),
  335. "attrs": {
  336. "name": "description"
  337. }
  338. }), h("div", {
  339. "class": [bem('file-name'), 'van-ellipsis']
  340. }, [item.file ? item.file.name : item.url]), PreviewCover]);
  341. return h("div", {
  342. "class": bem('preview'),
  343. "on": {
  344. "click": function click() {
  345. _this6.$emit('click-preview', item, _this6.getDetail(index));
  346. }
  347. }
  348. }, [Preview, this.genPreviewMask(item), DeleteIcon]);
  349. },
  350. genPreviewList: function genPreviewList() {
  351. if (this.previewImage) {
  352. return this.fileList.map(this.genPreviewItem);
  353. }
  354. },
  355. genUpload: function genUpload() {
  356. var h = this.$createElement;
  357. if (this.fileList.length >= this.maxCount || !this.showUpload) {
  358. return;
  359. }
  360. var slot = this.slots();
  361. var Input = this.readonly ? null : h("input", {
  362. "attrs": _extends({}, this.$attrs, {
  363. "type": "file",
  364. "accept": this.accept,
  365. "disabled": this.disabled
  366. }),
  367. "ref": "input",
  368. "class": bem('input'),
  369. "on": {
  370. "change": this.onChange
  371. }
  372. });
  373. if (slot) {
  374. return h("div", {
  375. "class": bem('input-wrapper'),
  376. "key": "input-wrapper",
  377. "on": {
  378. "click": this.onClickUpload
  379. }
  380. }, [slot, Input]);
  381. }
  382. var style;
  383. if (this.previewSize) {
  384. var size = this.previewSizeWithUnit;
  385. style = {
  386. width: size,
  387. height: size
  388. };
  389. }
  390. return h("div", {
  391. "class": bem('upload', {
  392. readonly: this.readonly
  393. }),
  394. "style": style,
  395. "on": {
  396. "click": this.onClickUpload
  397. }
  398. }, [h(Icon, {
  399. "attrs": {
  400. "name": this.uploadIcon
  401. },
  402. "class": bem('upload-icon')
  403. }), this.uploadText && h("span", {
  404. "class": bem('upload-text')
  405. }, [this.uploadText]), Input]);
  406. }
  407. },
  408. render: function render() {
  409. var h = arguments[0];
  410. return h("div", {
  411. "class": bem()
  412. }, [h("div", {
  413. "class": bem('wrapper', {
  414. disabled: this.disabled
  415. })
  416. }, [this.genPreviewList(), this.genUpload()])]);
  417. }
  418. });