index.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  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. created: function created() {
  90. this.urls = [];
  91. },
  92. beforeDestroy: function beforeDestroy() {
  93. this.urls.forEach(function (url) {
  94. return URL.revokeObjectURL(url);
  95. });
  96. },
  97. methods: {
  98. getDetail: function getDetail(index) {
  99. if (index === void 0) {
  100. index = this.fileList.length;
  101. }
  102. return {
  103. name: this.name,
  104. index: index
  105. };
  106. },
  107. onChange: function onChange(event) {
  108. var _this = this;
  109. var files = event.target.files;
  110. if (this.disabled || !files.length) {
  111. return;
  112. }
  113. files = files.length === 1 ? files[0] : [].slice.call(files);
  114. if (this.beforeRead) {
  115. var response = this.beforeRead(files, this.getDetail());
  116. if (!response) {
  117. this.resetInput();
  118. return;
  119. }
  120. if (isPromise(response)) {
  121. response.then(function (data) {
  122. if (data) {
  123. _this.readFile(data);
  124. } else {
  125. _this.readFile(files);
  126. }
  127. }).catch(this.resetInput);
  128. return;
  129. }
  130. }
  131. this.readFile(files);
  132. },
  133. readFile: function readFile(files) {
  134. var _this2 = this;
  135. var oversize = isOversize(files, this.maxSize);
  136. if (Array.isArray(files)) {
  137. var maxCount = this.maxCount - this.fileList.length;
  138. if (files.length > maxCount) {
  139. files = files.slice(0, maxCount);
  140. }
  141. Promise.all(files.map(function (file) {
  142. return _readFile(file, _this2.resultType);
  143. })).then(function (contents) {
  144. var fileList = files.map(function (file, index) {
  145. var result = {
  146. file: file,
  147. status: '',
  148. message: ''
  149. };
  150. if (contents[index]) {
  151. result.content = contents[index];
  152. }
  153. return result;
  154. });
  155. _this2.onAfterRead(fileList, oversize);
  156. });
  157. } else {
  158. _readFile(files, this.resultType).then(function (content) {
  159. var result = {
  160. file: files,
  161. status: '',
  162. message: ''
  163. };
  164. if (content) {
  165. result.content = content;
  166. }
  167. _this2.onAfterRead(result, oversize);
  168. });
  169. }
  170. },
  171. onAfterRead: function onAfterRead(files, oversize) {
  172. var _this3 = this;
  173. this.resetInput();
  174. var validFiles = files;
  175. if (oversize) {
  176. var oversizeFiles = files;
  177. if (Array.isArray(files)) {
  178. oversizeFiles = [];
  179. validFiles = [];
  180. files.forEach(function (item) {
  181. if (item.file) {
  182. if (isOversize(item.file, _this3.maxSize)) {
  183. oversizeFiles.push(item);
  184. } else {
  185. validFiles.push(item);
  186. }
  187. }
  188. });
  189. } else {
  190. validFiles = null;
  191. }
  192. this.$emit('oversize', oversizeFiles, this.getDetail());
  193. }
  194. var isValidFiles = Array.isArray(validFiles) ? Boolean(validFiles.length) : Boolean(validFiles);
  195. if (isValidFiles) {
  196. this.$emit('input', [].concat(this.fileList, toArray(validFiles)));
  197. if (this.afterRead) {
  198. this.afterRead(validFiles, this.getDetail());
  199. }
  200. }
  201. },
  202. onDelete: function onDelete(file, index) {
  203. var _file$beforeDelete,
  204. _this4 = this;
  205. var beforeDelete = (_file$beforeDelete = file.beforeDelete) != null ? _file$beforeDelete : this.beforeDelete;
  206. if (beforeDelete) {
  207. var response = beforeDelete(file, this.getDetail(index));
  208. if (!response) {
  209. return;
  210. }
  211. if (isPromise(response)) {
  212. response.then(function () {
  213. _this4.deleteFile(file, index);
  214. }).catch(noop);
  215. return;
  216. }
  217. }
  218. this.deleteFile(file, index);
  219. },
  220. deleteFile: function deleteFile(file, index) {
  221. var fileList = this.fileList.slice(0);
  222. fileList.splice(index, 1);
  223. this.$emit('input', fileList);
  224. this.$emit('delete', file, this.getDetail(index));
  225. },
  226. resetInput: function resetInput() {
  227. /* istanbul ignore else */
  228. if (this.$refs.input) {
  229. this.$refs.input.value = '';
  230. }
  231. },
  232. onClickUpload: function onClickUpload(event) {
  233. this.$emit('click-upload', event);
  234. },
  235. onPreviewImage: function onPreviewImage(item) {
  236. var _this5 = this;
  237. if (!this.previewFullImage) {
  238. return;
  239. }
  240. var imageFiles = this.fileList.filter(function (item) {
  241. return isImageFile(item);
  242. });
  243. var imageContents = imageFiles.map(function (item) {
  244. if (item.file && !item.url && item.status !== 'failed') {
  245. item.url = URL.createObjectURL(item.file);
  246. _this5.urls.push(item.url);
  247. }
  248. return item.url;
  249. });
  250. this.imagePreview = ImagePreview(_extends({
  251. images: imageContents,
  252. startPosition: imageFiles.indexOf(item),
  253. onClose: function onClose() {
  254. _this5.$emit('close-preview');
  255. }
  256. }, this.previewOptions));
  257. },
  258. // @exposed-api
  259. closeImagePreview: function closeImagePreview() {
  260. if (this.imagePreview) {
  261. this.imagePreview.close();
  262. }
  263. },
  264. // @exposed-api
  265. chooseFile: function chooseFile() {
  266. if (this.disabled) {
  267. return;
  268. }
  269. /* istanbul ignore else */
  270. if (this.$refs.input) {
  271. this.$refs.input.click();
  272. }
  273. },
  274. genPreviewMask: function genPreviewMask(item) {
  275. var h = this.$createElement;
  276. var status = item.status,
  277. message = item.message;
  278. if (status === 'uploading' || status === 'failed') {
  279. var MaskIcon = status === 'failed' ? h(Icon, {
  280. "attrs": {
  281. "name": "close"
  282. },
  283. "class": bem('mask-icon')
  284. }) : h(Loading, {
  285. "class": bem('loading')
  286. });
  287. var showMessage = isDef(message) && message !== '';
  288. return h("div", {
  289. "class": bem('mask')
  290. }, [MaskIcon, showMessage && h("div", {
  291. "class": bem('mask-message')
  292. }, [message])]);
  293. }
  294. },
  295. genPreviewItem: function genPreviewItem(item, index) {
  296. var _item$deletable,
  297. _this6 = this,
  298. _item$previewSize,
  299. _item$imageFit;
  300. var h = this.$createElement;
  301. var deleteAble = (_item$deletable = item.deletable) != null ? _item$deletable : this.deletable;
  302. var showDelete = item.status !== 'uploading' && deleteAble;
  303. var DeleteIcon = showDelete && h("div", {
  304. "class": bem('preview-delete'),
  305. "on": {
  306. "click": function click(event) {
  307. event.stopPropagation();
  308. _this6.onDelete(item, index);
  309. }
  310. }
  311. }, [h(Icon, {
  312. "attrs": {
  313. "name": "cross"
  314. },
  315. "class": bem('preview-delete-icon')
  316. })]);
  317. var PreviewCoverContent = this.slots('preview-cover', _extends({
  318. index: index
  319. }, item));
  320. var PreviewCover = PreviewCoverContent && h("div", {
  321. "class": bem('preview-cover')
  322. }, [PreviewCoverContent]);
  323. var previewSize = (_item$previewSize = item.previewSize) != null ? _item$previewSize : this.previewSize;
  324. var imageFit = (_item$imageFit = item.imageFit) != null ? _item$imageFit : this.imageFit;
  325. var Preview = isImageFile(item) ? h(Image, {
  326. "attrs": {
  327. "fit": imageFit,
  328. "src": item.content || item.url,
  329. "width": previewSize,
  330. "height": previewSize,
  331. "lazyLoad": this.lazyLoad
  332. },
  333. "class": bem('preview-image'),
  334. "on": {
  335. "click": function click() {
  336. _this6.onPreviewImage(item);
  337. }
  338. }
  339. }, [PreviewCover]) : h("div", {
  340. "class": bem('file'),
  341. "style": {
  342. width: this.previewSizeWithUnit,
  343. height: this.previewSizeWithUnit
  344. }
  345. }, [h(Icon, {
  346. "class": bem('file-icon'),
  347. "attrs": {
  348. "name": "description"
  349. }
  350. }), h("div", {
  351. "class": [bem('file-name'), 'van-ellipsis']
  352. }, [item.file ? item.file.name : item.url]), PreviewCover]);
  353. return h("div", {
  354. "class": bem('preview'),
  355. "on": {
  356. "click": function click() {
  357. _this6.$emit('click-preview', item, _this6.getDetail(index));
  358. }
  359. }
  360. }, [Preview, this.genPreviewMask(item), DeleteIcon]);
  361. },
  362. genPreviewList: function genPreviewList() {
  363. if (this.previewImage) {
  364. return this.fileList.map(this.genPreviewItem);
  365. }
  366. },
  367. genUpload: function genUpload() {
  368. var h = this.$createElement;
  369. if (this.fileList.length >= this.maxCount) {
  370. return;
  371. }
  372. var slot = this.slots();
  373. var Input = this.readonly ? null : h("input", {
  374. "attrs": _extends({}, this.$attrs, {
  375. "type": "file",
  376. "accept": this.accept,
  377. "disabled": this.disabled
  378. }),
  379. "ref": "input",
  380. "class": bem('input'),
  381. "on": {
  382. "change": this.onChange
  383. }
  384. });
  385. if (slot) {
  386. return h("div", {
  387. "class": bem('input-wrapper'),
  388. "key": "input-wrapper",
  389. "on": {
  390. "click": this.onClickUpload
  391. }
  392. }, [slot, Input]);
  393. }
  394. var style;
  395. if (this.previewSize) {
  396. var size = this.previewSizeWithUnit;
  397. style = {
  398. width: size,
  399. height: size
  400. };
  401. }
  402. return h("div", {
  403. "directives": [{
  404. name: "show",
  405. value: this.showUpload
  406. }],
  407. "class": bem('upload', {
  408. readonly: this.readonly
  409. }),
  410. "style": style,
  411. "on": {
  412. "click": this.onClickUpload
  413. }
  414. }, [h(Icon, {
  415. "attrs": {
  416. "name": this.uploadIcon
  417. },
  418. "class": bem('upload-icon')
  419. }), this.uploadText && h("span", {
  420. "class": bem('upload-text')
  421. }, [this.uploadText]), Input]);
  422. }
  423. },
  424. render: function render() {
  425. var h = arguments[0];
  426. return h("div", {
  427. "class": bem()
  428. }, [h("div", {
  429. "class": bem('wrapper', {
  430. disabled: this.disabled
  431. })
  432. }, [this.genPreviewList(), this.genUpload()])]);
  433. }
  434. });