ant-design-vue/components/upload/Upload.jsx

304 lines
7.7 KiB
Vue

import classNames from 'classnames'
import uniqBy from 'lodash/uniqBy'
import VcUpload from '../vc-upload'
import BaseMixin from '../_util/BaseMixin'
import { getOptionProps, initDefaultProps, hasProp } from '../_util/props-util'
import LocaleReceiver from '../locale-provider/LocaleReceiver'
import defaultLocale from '../locale-provider/default'
import Dragger from './Dragger'
import UploadList from './UploadList'
import { UploadProps } from './interface'
import { T, fileToObject, genPercentAdd, getFileItem, removeFileItem } from './utils'
export { UploadProps }
export default {
inheritAttrs: false,
name: 'AUpload',
Dragger: Dragger,
mixins: [BaseMixin],
props: initDefaultProps(UploadProps, {
prefixCls: 'ant-upload',
type: 'select',
multiple: false,
action: '',
data: {},
accept: '',
beforeUpload: T,
showUploadList: true,
listType: 'text', // or pictrue
disabled: false,
supportServerRender: true,
}),
// recentUploadStatus: boolean | PromiseLike<any>;
data () {
this.progressTimer = null
return {
sFileList: this.fileList || this.defaultFileList || [],
dragState: 'drop',
}
},
beforeDestroy () {
this.clearProgressTimer()
},
watch: {
fileList (val) {
this.sFileList = val
},
},
methods: {
onStart (file) {
const targetItem = fileToObject(file)
targetItem.status = 'uploading'
const nextFileList = this.sFileList.concat()
const fileIndex = nextFileList.findIndex(({ uid }) => uid === targetItem.uid)
if (fileIndex === -1) {
nextFileList.push(targetItem)
} else {
nextFileList[fileIndex] = targetItem
}
this.onChange({
file: targetItem,
fileList: nextFileList,
})
// fix ie progress
if (!window.FormData) {
this.autoUpdateProgress(0, targetItem)
}
},
autoUpdateProgress (_, file) {
const getPercent = genPercentAdd()
let curPercent = 0
this.clearProgressTimer()
this.progressTimer = setInterval(() => {
curPercent = getPercent(curPercent)
this.onProgress({
percent: curPercent * 100,
}, file)
}, 200)
},
onSuccess (response, file) {
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
this.onChange({
file: { ...targetItem },
fileList,
})
},
onProgress (e, file) {
const fileList = this.sFileList
const targetItem = getFileItem(file, fileList)
// removed
if (!targetItem) {
return
}
targetItem.percent = e.percent
this.onChange({
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.onChange({
file: { ...targetItem },
fileList,
})
},
handleRemove (file) {
const { remove } = getOptionProps(this)
Promise.resolve(typeof remove === 'function' ? remove(file) : remove).then(ret => {
// Prevent removing file
if (ret === false) {
return
}
const removedFileList = removeFileItem(file, this.sFileList)
if (removedFileList) {
this.onChange({
file,
fileList: removedFileList,
})
}
})
},
handleManualRemove (file) {
this.$refs.uploadRef.abort(file)
file.status = 'removed' // eslint-disable-line
this.handleRemove(file)
},
onChange (info) {
if (!hasProp(this, 'fileList')) {
this.setState({ sFileList: info.fileList })
}
this.$emit('change', info)
},
onFileDrop (e) {
this.setState({
dragState: e.type,
})
},
reBeforeUpload (file, fileList) {
if (!this.beforeUpload) {
return true
}
const result = this.beforeUpload(file, fileList)
if (result === false) {
this.onChange({
file,
fileList: uniqBy(
this.sFileList.concat(fileList.map(fileToObject)),
(item) => item.uid,
),
})
return false
} else if (result && result.then) {
return result
}
return true
},
clearProgressTimer () {
clearInterval(this.progressTimer)
},
renderUploadList (locale) {
const { showUploadList = {}, listType } = getOptionProps(this)
const { showRemoveIcon, showPreviewIcon } = showUploadList
const uploadListProps = {
props: {
listType,
items: this.sFileList,
showRemoveIcon,
showPreviewIcon,
locale: { ...locale, ...this.$props.locale },
},
on: {
remove: this.handleManualRemove,
},
}
if (this.$listeners.preview) {
uploadListProps.on.preview = this.$listeners.preview
}
return (
<UploadList
{...uploadListProps}
/>
)
},
},
render () {
const {
prefixCls = '',
showUploadList,
listType,
type,
disabled,
} = getOptionProps(this)
const vcUploadProps = {
props: {
...this.$props,
beforeUpload: this.reBeforeUpload,
},
on: {
// ...this.$listeners,
start: this.onStart,
error: this.onError,
progress: this.onProgress,
success: this.onSuccess,
},
ref: 'uploadRef',
class: `${prefixCls}-btn`,
attrs: this.$attrs,
}
const uploadList = showUploadList ? (
<LocaleReceiver
componentName='Upload'
defaultLocale={defaultLocale.Upload}
scopedSlots={
{ default: this.renderUploadList }
}
>
</LocaleReceiver>
) : null
const children = this.$slots.default
if (type === 'drag') {
const dragCls = classNames(prefixCls, {
[`${prefixCls}-drag`]: true,
[`${prefixCls}-drag-uploading`]: this.sFileList.some(file => file.status === 'uploading'),
[`${prefixCls}-drag-hover`]: this.dragState === 'dragover',
[`${prefixCls}-disabled`]: disabled,
})
return (
<span>
<div
class={dragCls}
onDrop={this.onFileDrop}
onDragover={this.onFileDrop}
onDragleave={this.onFileDrop}
>
<VcUpload {...vcUploadProps}>
<div class={`${prefixCls}-drag-container`}>
{children}
</div>
</VcUpload>
</div>
{uploadList}
</span>
)
}
const uploadButtonCls = classNames(prefixCls, {
[`${prefixCls}-select`]: true,
[`${prefixCls}-select-${listType}`]: true,
[`${prefixCls}-disabled`]: disabled,
})
const uploadButton = (
<div class={uploadButtonCls} style={{ display: children ? '' : 'none' }}>
<VcUpload {...vcUploadProps} >{children}</VcUpload>
</div>
)
if (listType === 'picture-card') {
return (
<span>
{uploadList}
{uploadButton}
</span>
)
}
return (
<span>
{uploadButton}
{uploadList}
</span>
)
},
}