import classNames from '../_util/classNames'; import uniqBy from 'lodash-es/uniqBy'; import findIndex from 'lodash-es/findIndex'; import VcUpload from '../vc-upload'; import BaseMixin from '../_util/BaseMixin'; import { getOptionProps, hasProp, getSlot } from '../_util/props-util'; import initDefaultProps from '../_util/props-util/initDefaultProps'; import LocaleReceiver from '../locale-provider/LocaleReceiver'; import defaultLocale from '../locale-provider/default'; import { defaultConfigProvider } from '../config-provider'; import Dragger from './Dragger'; import UploadList from './UploadList'; import { UploadProps } from './interface'; import { T, fileToObject, genPercentAdd, getFileItem, removeFileItem } from './utils'; import { defineComponent, inject } from 'vue'; import { getDataAndAriaProps } from '../_util/util'; import { useInjectFormItemContext } from '../form/FormItemContext'; export type UploadFileStatus = 'error' | 'success' | 'done' | 'uploading' | 'removed'; export interface UploadFile { uid: string; size?: number; name: string; fileName?: string; lastModified?: number; lastModifiedDate?: Date; url?: string; status?: UploadFileStatus; percent?: number; thumbUrl?: string; originFileObj?: any; response?: T; error?: any; linkProps?: any; type?: string; xhr?: T; preview?: string; } export default defineComponent({ name: 'AUpload', mixins: [BaseMixin], inheritAttrs: false, Dragger, props: initDefaultProps(UploadProps, { type: 'select', multiple: false, action: '', data: {}, accept: '', beforeUpload: T, showUploadList: true, listType: 'text', // or pictrue disabled: false, supportServerRender: true, }), setup() { const formItemContext = useInjectFormItemContext(); return { upload: null, progressTimer: null, configProvider: inject('configProvider', defaultConfigProvider), formItemContext, }; }, // recentUploadStatus: boolean | PromiseLike; data() { return { sFileList: this.fileList || this.defaultFileList || [], dragState: 'drop', }; }, watch: { fileList(val) { this.sFileList = val || []; }, }, beforeUnmount() { this.clearProgressTimer(); }, methods: { onStart(file) { const targetItem = fileToObject(file); targetItem.status = 'uploading'; const nextFileList = this.sFileList.concat(); const fileIndex = findIndex(nextFileList, ({ uid }) => uid === targetItem.uid); if (fileIndex === -1) { nextFileList.push(targetItem); } else { nextFileList[fileIndex] = targetItem; } this.handleChange({ file: targetItem, fileList: nextFileList, }); // fix ie progress if (!window.File || (typeof process === 'object' && process.env.TEST_IE)) { this.autoUpdateProgress(0, targetItem); } }, onSuccess(response, file, xhr) { this.clearProgressTimer(); try { if (typeof response === 'string') { response = JSON.parse(response); } } catch (e) { /* do nothing */ } const fileList = this.sFileList; const targetItem = getFileItem(file, fileList); // removed if (!targetItem) { return; } targetItem.status = 'done'; targetItem.response = response; targetItem.xhr = xhr; this.handleChange({ file: { ...targetItem }, fileList, }); }, onProgress(e, file) { const fileList = this.sFileList; const targetItem = getFileItem(file, fileList); // removed if (!targetItem) { return; } targetItem.percent = e.percent; this.handleChange({ event: e, file: { ...targetItem }, fileList: this.sFileList, }); }, onError(error, response, file) { this.clearProgressTimer(); const fileList = this.sFileList; const targetItem = getFileItem(file, fileList); // removed if (!targetItem) { return; } targetItem.error = error; targetItem.response = response; targetItem.status = 'error'; this.handleChange({ file: { ...targetItem }, fileList, }); }, onReject(fileList) { this.$emit('reject', fileList); }, handleRemove(file) { const { remove: onRemove } = this; const { sFileList: fileList } = this.$data; Promise.resolve(typeof onRemove === 'function' ? onRemove(file) : onRemove).then(ret => { // Prevent removing file if (ret === false) { return; } const removedFileList = removeFileItem(file, fileList); if (removedFileList) { file.status = 'removed'; // eslint-disable-line if (this.upload) { this.upload.abort(file); } this.handleChange({ file, fileList: removedFileList, }); } }); }, handleManualRemove(file) { if (this.$refs.uploadRef) { (this.$refs.uploadRef as any).abort(file); } this.handleRemove(file); }, handleChange(info) { if (!hasProp(this, 'fileList')) { this.setState({ sFileList: info.fileList }); } this.$emit('update:fileList', info.fileList); this.$emit('change', info); this.formItemContext.onFieldChange(); }, onFileDrop(e) { this.setState({ dragState: e.type, }); }, reBeforeUpload(file, fileList) { const { beforeUpload } = this.$props; const { sFileList: stateFileList } = this.$data; if (!beforeUpload) { return true; } const result = beforeUpload(file, fileList); if (result === false) { this.handleChange({ file, fileList: uniqBy( stateFileList.concat(fileList.map(fileToObject)), (item: UploadFile) => item.uid, ), }); return false; } if (result && result.then) { return result; } return true; }, clearProgressTimer() { clearInterval(this.progressTimer); }, autoUpdateProgress(_, file) { const getPercent = genPercentAdd(); let curPercent = 0; this.clearProgressTimer(); this.progressTimer = setInterval(() => { curPercent = getPercent(curPercent); this.onProgress( { percent: curPercent * 100, }, file, ); }, 200); }, renderUploadList(locale) { const { showUploadList = {}, listType, previewFile, disabled, locale: propLocale, } = getOptionProps(this); const { showRemoveIcon, showPreviewIcon, showDownloadIcon } = showUploadList; const { sFileList: fileList } = this.$data; const { onDownload, onPreview } = this.$props; const uploadListProps = { listType, items: fileList, previewFile, showRemoveIcon: !disabled && showRemoveIcon, showPreviewIcon, showDownloadIcon, locale: { ...locale, ...propLocale }, onRemove: this.handleManualRemove, onDownload, onPreview, }; return ; }, }, render() { const { prefixCls: customizePrefixCls, showUploadList, listType, type, disabled, } = getOptionProps(this); const { sFileList: fileList, dragState } = this.$data; const { class: className, style } = this.$attrs; const getPrefixCls = this.configProvider.getPrefixCls; const prefixCls = getPrefixCls('upload', customizePrefixCls); const vcUploadProps = { ...this.$props, id: this.$props.id ?? this.formItemContext.id.value, prefixCls, beforeUpload: this.reBeforeUpload, onStart: this.onStart, onError: this.onError, onProgress: this.onProgress, onSuccess: this.onSuccess, onReject: this.onReject, ref: 'uploadRef', }; const uploadList = showUploadList ? ( ) : null; const children = getSlot(this); if (type === 'drag') { const dragCls = classNames(prefixCls, { [`${prefixCls}-drag`]: true, [`${prefixCls}-drag-uploading`]: fileList.some((file: any) => file.status === 'uploading'), [`${prefixCls}-drag-hover`]: dragState === 'dragover', [`${prefixCls}-disabled`]: disabled, }); return (
{children}
{uploadList}
); } const uploadButtonCls = classNames(prefixCls, { [`${prefixCls}-select`]: true, [`${prefixCls}-select-${listType}`]: true, [`${prefixCls}-disabled`]: disabled, }); // Remove id to avoid open by label when trigger is hidden // https://github.com/ant-design/ant-design/issues/14298 if (!children.length || disabled) { delete vcUploadProps.id; } const uploadButton = (
{children}
); if (listType === 'picture-card') { return ( {uploadList} {uploadButton} ); } return ( {uploadButton} {uploadList} ); }, });