kui-vue
Version:
A high quality UI Toolkit built on Vue.js 2.0
371 lines (357 loc) • 11.7 kB
JSX
import Icon from '../icon'
import { t } from '../locale'
import Progress from '../progress'
import Tooltip from '../tooltip'
import { DocumentTextOutline, Close, AlertCircle, Add } from 'kui-icons'
let count = 0
const timestamp = Date.now()
function getUuid() {
return `k-upload-${timestamp}-${count++}`
}
export default {
name: "Upload",
props: {
method: { type: String, default: "post" },
name: { type: String, default: 'file' }, //提交的 name值
action: { type: String, required: true },
type: { type: String, default: "list", validator: (val) => ['list', 'picture'].indexOf(val) >= 0 },
data: { type: Object, default: () => { } },
disabled: Boolean,
directory: Boolean,
multiple: Boolean,
accept: String,
headers: Object,
showUploadList: { type: Boolean, default: true },
transformFile: Function,
fileList: Array,
autoTrigger: { type: Boolean, default: true },
limit: Number,
minSize: Number,
maxSize: Number,
uploadText: String,
uploadSubText: String,
uploadIcon: [String, Array],
draggable: Boolean,
},
watch: {
fileList(v) {
this.defaultFileList = v || []
}
},
data() {
return {
defaultFileList: this.fileList || [],
count: 0,
uploadTemp: {},
dragOver: false
};
},
mounted() {
// if (this.draggable && this.$isServer) {
// window.addEventListener("dragover", function (e) {
// e = e || event;
// e.preventDefault();
// }, false);
// window.addEventListener("drop", function (e) {
// e = e || event;
// e.preventDefault();
// }, false);
// }
},
methods: {
formatFileSize(fileSize) {
var temp = ''
if (fileSize < 1024) {
return fileSize + 'B';
} else if (fileSize < (1024 * 1024)) {
temp = fileSize / 1024;
temp = temp.toFixed(2);
return temp + 'KB';
} else if (fileSize < (1024 * 1024 * 1024)) {
temp = fileSize / (1024 * 1024);
temp = temp.toFixed(2);
return temp + 'MB';
} else {
temp = fileSize / (1024 * 1024 * 1024);
temp = temp.toFixed(2);
return temp + 'GB';
}
},
triggerSelect(e) {
e.cancelBubble = true;
if (this.disabled) return false;
this.$refs["k-upload-file"].click();
return false;
},
remove(i) {
if (this.disabled) return false;
let item = this.defaultFileList[i]
this.defaultFileList.splice(i, 1)
delete this.uploadTemp[item.uid]
item.xhr && item.xhr.abort()
this.$emit('remove', {
file: item,
fileList: this.defaultFileList
})
},
upload() {
if (!this.autoTrigger && !this.disabled) {
let files = this.uploadTemp
for (let k in files) {
let item = this.defaultFileList.filter(x => x.uid == k)[0]
item && this.uploadFile(item, files[k])
}
}
},
selectFiles(e) {
let { limit, minSize, maxSize, autoTrigger } = this
let files = e.dataTransfer ? e.dataTransfer.files : e.target.files
for (let i = 0; i < files.length; i++) {
let { size, type } = files[i]
if (files[i].name == '.DS_Store') {
continue;
}
let item = {
uid: getUuid(),
filename: files[i].name, size: this.formatFileSize(size),
status: 'wait', percent: 0, preview: false
}
this.uploadTemp[item.uid] = files[i]
if (limit && i > limit - 1) {
this.$emit('exceed')
return;
}
if ((type || '').indexOf('image') >= 0) {
item.preview = window.URL.createObjectURL(files[i])
}
let error = false
if ((minSize !== undefined && minSize >= 0 && size / 1024 < minSize) ||
(maxSize !== undefined && maxSize >= 0 && size / 1024 > maxSize)
) {
error = true
item.errorText = t('k.upload.errorFileSize')
item.status = 'error'
}
// if (multiple) {
this.defaultFileList.push(item)
this.$emit('before-upload', {
file: item,
fileList: this.defaultFileList,
})
if (error) {
this.$emit('change', {
file: item,
fileList: this.defaultFileList,
})
this.$emit('size-error', {
file: item,
fileList: this.defaultFileList,
})
continue;
}
// } else {
// this.defaultFileList = [item]
// }
if (autoTrigger) {
this.uploadFile(item, files[i])
}
}
e.target.value = ''
},
async uploadFile(item, file) {
if (this.transformFile) {
this.transformFile(file).then(f => {
this.toUpload(item, f)
})
} else {
this.toUpload(item, file)
}
},
toUpload(item, file) {
let { action, name, headers, data } = this
var formdata = new FormData();
formdata.append(name, file);
if (data) {
for (let k in data) {
formdata.append(k, data[k]);
}
}
//创建xhr,使用ajax进行文件上传
var xhr = new XMLHttpRequest();
item.xhr = xhr
xhr.open("post", action);
if (headers) {
for (let k in headers) {
xhr.setRequestHeader(k, headers[k])
}
}
//回调
xhr.onreadystatechange = event => {
if (xhr.readyState == 4 && xhr.status == 200) {
item.status = 'success'
delete this.uploadTemp[item.uid]
this.$emit('change', {
file: Object.assign(item, { response: JSON.parse(xhr.responseText) }),
fileList: this.defaultFileList,
event
})
}
}
xhr.upload.onloadstart = () => item.status = 'uploading'
//获取上传的进度
xhr.upload.onprogress = (event) => {
// console.log(event)
if (event.lengthComputable) {
var percent = (event.loaded / event.total) * 100;
// console.log(percent)
item.percent = percent
}
this.$emit('change', {
file: Object.assign(item, { response: xhr.responseText }),
fileList: this.defaultFileList,
event
})
}
xhr.onload = (e) => {
if (xhr.status != 200) {
item.status = 'error'
delete this.uploadTemp[item.uid]
this.$emit('change', {
file: Object.assign(item, { response: xhr.responseText }),
fileList: this.defaultFileList,
event: event
})
}
}
xhr.onerror = event => {
item.status = 'error'
delete this.uploadTemp[item.uid]
this.$emit('change', {
file: Object.assign(item, { response: xhr.responseText }),
fileList: this.defaultFileList,
event: event
})
}
//将formdata上传
xhr.send(formdata);
},
getPreview(item) {
if (item.preview == true && item.url) {
return <img src={item.url} />
}
if (item.preview) {
return <img src={item.preview} />
} else if (item.url) {
return <img src={item.url} />
} else {
return null
}
},
onDrop(e) {
// var files = e.dataTransfer.files;
this.selectFiles(e)
e.preventDefault()
this.dragOver = false
return false
},
onDragEnter(e) {
this.dragOver = true
e.preventDefault()
return false
},
onDragOver(e) {
e.stopPropagation();
e.preventDefault();
}
},
render() {
let { name, accept, multiple, directory, type, showUploadList, uploadIcon,
defaultFileList, limit, uploadText, uploadSubText, draggable } = this
let isPicture = type == 'picture'
if (uploadIcon === undefined) {
uploadIcon = Add
}
const props = {
class: [
"k-upload",
{
["k-upload-disabled"]: this.disabled,
["k-upload-picture"]: isPicture,
["k-upload-drag"]: draggable
}
],
}
// let list
// let childs = getChild(this.$slots.default)
// let child = childs.map(child => {
// return cloneVNode(child, { on: { click: this.triggerSelect } })
// })
let addProps = {
attrs: {
drag: draggable && this.dragOver ? 'over' : null
},
on: {
dragenter: this.onDragEnter,
drop: this.onDrop,
dragover: this.onDragOver,
dragleave: () => this.dragOver = false,
click: this.triggerSelect
}
}
let showSelector = (isPicture && limit && limit > defaultFileList.length) || !isPicture || !limit
const selector = showSelector ? <div class='k-upload-select'>
<div class="k-upload-add" {...addProps}>
<input type="file" class="k-upload-file"
webkitdirectory={directory}
name={name} accept={accept} multiple={multiple}
onChange={this.selectFiles} ref="k-upload-file" />
{(isPicture || draggable) ? <Icon type={uploadIcon} /> : this.$slots.default}
{(isPicture || draggable && uploadText) ? <span class="k-upload-text">{uploadText}</span> : null}
{(draggable && uploadSubText) ? <span class="k-upload-sub-text">{this.dragOver ? '松手开始上传' : uploadSubText}</span> : null}
</div>
</div> : null
const filsList = () => {
return (showUploadList && !isPicture) || isPicture ? <div class={`k-upload-${isPicture ? 'picture' : 'file'}-list`}>
{
defaultFileList.map((item, i) => {
let statusText = item.status == 'success' ? t('k.upload.successful') : (item.errorText || t('k.upload.failed'))
delete item.errorText
item.uid = item.uid || getUuid()
return (
<div class={[`k-upload-file-${type}-item`, `k-upload-file-status-${item.status}`]} key={item.uid}>
<div class={`k-upload-${isPicture ? 'picture' : 'file'}-preview`}>
{
this.getPreview(item) || <Icon type={DocumentTextOutline} />
}
</div>
<div class="k-upload-file-item-info">
{!isPicture ? <div class="k-upload-file-main">
<span class="k-upload-file-name">{item.filename}</span>
<span class="k-upload-file-size">{item.size}</span>
</div> : null}
{
(item.status != 'wait') ?
<div class="k-upload-file-status">
{item.status == 'uploading' ?
<Progress percent={item.percent} type={`${isPicture ? 'circle' : 'line'}`} size="small" showInfo={false} status="active" strokeWidth={15} />
:
statusText && !isPicture ? <div class="k-upload-file-status-text"><Icon type={AlertCircle} />{statusText}</div> : null
}
{isPicture && item.status == 'error' ? <Tooltip title={statusText} placement="bottom"><Icon type={AlertCircle} /></Tooltip> : null}
</div> : null}
</div>
<Icon type={Close} class={`k-upload-file-${isPicture ? 'picture' : 'item'}-remove`} onClick={() => this.remove(i)} />
</div>
)
})
}
{isPicture && selector}
</div> : null
}
return (
<div {...props} >
{!isPicture ? [selector, filsList()] : filsList(selector)}
</div >
)
}
}