update the master branch to the latest
This commit is contained in:
parent
67d534df27
commit
23a0ba9831
611 changed files with 122648 additions and 0 deletions
82
src/components/upload/ajax.js
Executable file
82
src/components/upload/ajax.js
Executable file
|
@ -0,0 +1,82 @@
|
|||
// https://github.com/ElemeFE/element/blob/dev/packages/upload/src/ajax.js
|
||||
|
||||
function getError(action, option, xhr) {
|
||||
const msg = `fail to post ${action} ${xhr.status}'`;
|
||||
const err = new Error(msg);
|
||||
err.status = xhr.status;
|
||||
err.method = 'post';
|
||||
err.url = action;
|
||||
return err;
|
||||
}
|
||||
|
||||
function getBody(xhr) {
|
||||
const text = xhr.responseText || xhr.response;
|
||||
if (!text) {
|
||||
return text;
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(text);
|
||||
} catch (e) {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
export default function upload(option) {
|
||||
if (typeof XMLHttpRequest === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
const xhr = new XMLHttpRequest();
|
||||
const action = option.action;
|
||||
|
||||
if (xhr.upload) {
|
||||
xhr.upload.onprogress = function progress(e) {
|
||||
if (e.total > 0) {
|
||||
e.percent = e.loaded / e.total * 100;
|
||||
}
|
||||
option.onProgress(e);
|
||||
};
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
|
||||
if (option.data) {
|
||||
Object.keys(option.data).map(key => {
|
||||
formData.append(key, option.data[key]);
|
||||
});
|
||||
}
|
||||
|
||||
formData.append(option.filename, option.file);
|
||||
|
||||
xhr.onerror = function error(e) {
|
||||
option.onError(e);
|
||||
};
|
||||
|
||||
xhr.onload = function onload() {
|
||||
if (xhr.status < 200 || xhr.status >= 300) {
|
||||
return option.onError(getError(action, option, xhr), getBody(xhr));
|
||||
}
|
||||
|
||||
option.onSuccess(getBody(xhr));
|
||||
};
|
||||
|
||||
xhr.open('post', action, true);
|
||||
|
||||
if (option.withCredentials && 'withCredentials' in xhr) {
|
||||
xhr.withCredentials = true;
|
||||
}
|
||||
|
||||
const headers = option.headers || {};
|
||||
|
||||
// if (headers['X-Requested-With'] !== null) {
|
||||
// xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
|
||||
// }
|
||||
|
||||
for (let item in headers) {
|
||||
if (headers.hasOwnProperty(item) && headers[item] !== null) {
|
||||
xhr.setRequestHeader(item, headers[item]);
|
||||
}
|
||||
}
|
||||
xhr.send(formData);
|
||||
}
|
3
src/components/upload/index.js
Normal file
3
src/components/upload/index.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
import Upload from './upload.vue';
|
||||
|
||||
export default Upload;
|
94
src/components/upload/upload-list.vue
Normal file
94
src/components/upload/upload-list.vue
Normal file
|
@ -0,0 +1,94 @@
|
|||
<template>
|
||||
<ul :class="[prefixCls + '-list']">
|
||||
<li
|
||||
v-for="file in files"
|
||||
:class="fileCls(file)"
|
||||
@click="handleClick(file)">
|
||||
<span @click="handlePreview(file)">
|
||||
<Icon :type="format(file)"></Icon> {{ file.name }}
|
||||
</span>
|
||||
<Icon
|
||||
type="ios-close"
|
||||
:class="[prefixCls + '-list-remove']"
|
||||
v-show="file.status === 'finished'"
|
||||
@click.native="handleRemove(file)"></Icon>
|
||||
<transition name="fade">
|
||||
<i-progress
|
||||
v-if="file.showProgress"
|
||||
:stroke-width="2"
|
||||
:percent="parsePercentage(file.percentage)"
|
||||
:status="file.status === 'finished' && file.showProgress ? 'success' : 'normal'"></i-progress>
|
||||
</transition>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
<script>
|
||||
import Icon from '../icon/icon.vue';
|
||||
import iProgress from '../progress/progress.vue';
|
||||
const prefixCls = 'ivu-upload';
|
||||
|
||||
export default {
|
||||
name: 'UploadList',
|
||||
components: { Icon, iProgress },
|
||||
props: {
|
||||
files: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
prefixCls: prefixCls
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
fileCls (file) {
|
||||
return [
|
||||
`${prefixCls}-list-file`,
|
||||
{
|
||||
[`${prefixCls}-list-file-finish`]: file.status === 'finished'
|
||||
}
|
||||
];
|
||||
},
|
||||
handleClick (file) {
|
||||
this.$emit('on-file-click', file);
|
||||
},
|
||||
handlePreview (file) {
|
||||
this.$emit('on-file-preview', file);
|
||||
},
|
||||
handleRemove (file) {
|
||||
this.$emit('on-file-remove', file);
|
||||
},
|
||||
format (file) {
|
||||
const format = file.name.split('.').pop().toLocaleLowerCase() || '';
|
||||
let type = 'ios-document-outline';
|
||||
|
||||
if (['gif','jpg','jpeg','png','bmp','webp'].indexOf(format) > -1) {
|
||||
type = 'ios-image';
|
||||
}
|
||||
if (['mp4','m3u8','rmvb','avi','swf','3gp','mkv','flv'].indexOf(format) > -1) {
|
||||
type = 'ios-film';
|
||||
}
|
||||
if (['mp3','wav','wma','ogg','aac','flac'].indexOf(format) > -1) {
|
||||
type = 'ios-musical-notes';
|
||||
}
|
||||
if (['doc','txt','docx','pages','epub','pdf'].indexOf(format) > -1) {
|
||||
type = 'md-document';
|
||||
}
|
||||
if (['numbers','csv','xls','xlsx'].indexOf(format) > -1) {
|
||||
type = 'ios-stats';
|
||||
}
|
||||
if (['keynote','ppt','pptx'].indexOf(format) > -1) {
|
||||
type = 'ios-videocam';
|
||||
}
|
||||
|
||||
return type;
|
||||
},
|
||||
parsePercentage (val) {
|
||||
return parseInt(val, 10);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
345
src/components/upload/upload.vue
Normal file
345
src/components/upload/upload.vue
Normal file
|
@ -0,0 +1,345 @@
|
|||
<template>
|
||||
<div :class="[prefixCls]">
|
||||
<div
|
||||
:class="classes"
|
||||
@click="handleClick"
|
||||
@drop.prevent="onDrop"
|
||||
@paste="handlePaste"
|
||||
@dragover.prevent="dragOver = true"
|
||||
@dragleave.prevent="dragOver = false">
|
||||
<input
|
||||
ref="input"
|
||||
type="file"
|
||||
:class="[prefixCls + '-input']"
|
||||
@change="handleChange"
|
||||
:multiple="multiple"
|
||||
:accept="accept">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<slot name="tip"></slot>
|
||||
<upload-list
|
||||
v-if="showUploadList"
|
||||
:files="fileList"
|
||||
@on-file-remove="handleRemove"
|
||||
@on-file-preview="handlePreview"></upload-list>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import UploadList from './upload-list.vue';
|
||||
import ajax from './ajax';
|
||||
import { oneOf } from '../../utils/assist';
|
||||
import Emitter from '../../mixins/emitter';
|
||||
|
||||
const prefixCls = 'ivu-upload';
|
||||
|
||||
export default {
|
||||
name: 'Upload',
|
||||
mixins: [ Emitter ],
|
||||
components: { UploadList },
|
||||
props: {
|
||||
action: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
headers: {
|
||||
type: Object,
|
||||
default () {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
data: {
|
||||
type: Object
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: 'file'
|
||||
},
|
||||
withCredentials: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
showUploadList: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
validator (value) {
|
||||
return oneOf(value, ['select', 'drag']);
|
||||
},
|
||||
default: 'select'
|
||||
},
|
||||
format: {
|
||||
type: Array,
|
||||
default () {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
accept: {
|
||||
type: String
|
||||
},
|
||||
maxSize: {
|
||||
type: Number
|
||||
},
|
||||
beforeUpload: Function,
|
||||
onProgress: {
|
||||
type: Function,
|
||||
default () {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
onSuccess: {
|
||||
type: Function,
|
||||
default () {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
onError: {
|
||||
type: Function,
|
||||
default () {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
onRemove: {
|
||||
type: Function,
|
||||
default () {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
onPreview: {
|
||||
type: Function,
|
||||
default () {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
onExceededSize: {
|
||||
type: Function,
|
||||
default () {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
onFormatError: {
|
||||
type: Function,
|
||||
default () {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
defaultFileList: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
paste: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
prefixCls: prefixCls,
|
||||
dragOver: false,
|
||||
fileList: [],
|
||||
tempIndex: 1
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
classes () {
|
||||
return [
|
||||
`${prefixCls}`,
|
||||
{
|
||||
[`${prefixCls}-select`]: this.type === 'select',
|
||||
[`${prefixCls}-drag`]: this.type === 'drag',
|
||||
[`${prefixCls}-dragOver`]: this.type === 'drag' && this.dragOver
|
||||
}
|
||||
];
|
||||
},
|
||||
|
||||
},
|
||||
methods: {
|
||||
handleClick () {
|
||||
if (this.disabled) return;
|
||||
this.$refs.input.click();
|
||||
},
|
||||
handleChange (e) {
|
||||
const files = e.target.files;
|
||||
|
||||
if (!files) {
|
||||
return;
|
||||
}
|
||||
this.uploadFiles(files);
|
||||
this.$refs.input.value = null;
|
||||
},
|
||||
onDrop (e) {
|
||||
this.dragOver = false;
|
||||
if (this.disabled) return;
|
||||
this.uploadFiles(e.dataTransfer.files);
|
||||
},
|
||||
handlePaste (e) {
|
||||
if (this.disabled) return;
|
||||
if (this.paste) {
|
||||
this.uploadFiles(e.clipboardData.files);
|
||||
}
|
||||
},
|
||||
uploadFiles (files) {
|
||||
let postFiles = Array.prototype.slice.call(files);
|
||||
if (!this.multiple) postFiles = postFiles.slice(0, 1);
|
||||
|
||||
if (postFiles.length === 0) return;
|
||||
|
||||
postFiles.forEach(file => {
|
||||
this.upload(file);
|
||||
});
|
||||
},
|
||||
upload (file) {
|
||||
if (!this.beforeUpload) {
|
||||
return this.post(file);
|
||||
}
|
||||
|
||||
const before = this.beforeUpload(file);
|
||||
if (before && before.then) {
|
||||
before.then(processedFile => {
|
||||
if (Object.prototype.toString.call(processedFile) === '[object File]') {
|
||||
this.post(processedFile);
|
||||
} else {
|
||||
this.post(file);
|
||||
}
|
||||
}, () => {
|
||||
// this.$emit('cancel', file);
|
||||
});
|
||||
} else if (before !== false) {
|
||||
this.post(file);
|
||||
} else {
|
||||
// this.$emit('cancel', file);
|
||||
}
|
||||
},
|
||||
post (file) {
|
||||
// check format
|
||||
if (this.format.length) {
|
||||
const _file_format = file.name.split('.').pop().toLocaleLowerCase();
|
||||
const checked = this.format.some(item => item.toLocaleLowerCase() === _file_format);
|
||||
if (!checked) {
|
||||
this.onFormatError(file, this.fileList);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// check maxSize
|
||||
if (this.maxSize) {
|
||||
if (file.size > this.maxSize * 1024) {
|
||||
this.onExceededSize(file, this.fileList);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
this.handleStart(file);
|
||||
let formData = new FormData();
|
||||
formData.append(this.name, file);
|
||||
|
||||
ajax({
|
||||
headers: this.headers,
|
||||
withCredentials: this.withCredentials,
|
||||
file: file,
|
||||
data: this.data,
|
||||
filename: this.name,
|
||||
action: this.action,
|
||||
onProgress: e => {
|
||||
this.handleProgress(e, file);
|
||||
},
|
||||
onSuccess: res => {
|
||||
this.handleSuccess(res, file);
|
||||
},
|
||||
onError: (err, response) => {
|
||||
this.handleError(err, response, file);
|
||||
}
|
||||
});
|
||||
},
|
||||
handleStart (file) {
|
||||
file.uid = Date.now() + this.tempIndex++;
|
||||
const _file = {
|
||||
status: 'uploading',
|
||||
name: file.name,
|
||||
size: file.size,
|
||||
percentage: 0,
|
||||
uid: file.uid,
|
||||
showProgress: true
|
||||
};
|
||||
|
||||
this.fileList.push(_file);
|
||||
},
|
||||
getFile (file) {
|
||||
const fileList = this.fileList;
|
||||
let target;
|
||||
fileList.every(item => {
|
||||
target = file.uid === item.uid ? item : null;
|
||||
return !target;
|
||||
});
|
||||
return target;
|
||||
},
|
||||
handleProgress (e, file) {
|
||||
const _file = this.getFile(file);
|
||||
this.onProgress(e, _file, this.fileList);
|
||||
_file.percentage = e.percent || 0;
|
||||
},
|
||||
handleSuccess (res, file) {
|
||||
const _file = this.getFile(file);
|
||||
|
||||
if (_file) {
|
||||
_file.status = 'finished';
|
||||
_file.response = res;
|
||||
|
||||
this.onSuccess(res, _file, this.fileList);
|
||||
this.dispatch('FormItem', 'on-form-change', _file);
|
||||
|
||||
setTimeout(() => {
|
||||
_file.showProgress = false;
|
||||
}, 1000);
|
||||
}
|
||||
},
|
||||
handleError (err, response, file) {
|
||||
const _file = this.getFile(file);
|
||||
const fileList = this.fileList;
|
||||
|
||||
_file.status = 'fail';
|
||||
|
||||
fileList.splice(fileList.indexOf(_file), 1);
|
||||
|
||||
this.onError(err, response, file);
|
||||
},
|
||||
handleRemove(file) {
|
||||
const fileList = this.fileList;
|
||||
fileList.splice(fileList.indexOf(file), 1);
|
||||
this.onRemove(file, fileList);
|
||||
},
|
||||
handlePreview(file) {
|
||||
if (file.status === 'finished') {
|
||||
this.onPreview(file);
|
||||
}
|
||||
},
|
||||
clearFiles() {
|
||||
this.fileList = [];
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
defaultFileList: {
|
||||
immediate: true,
|
||||
handler(fileList) {
|
||||
this.fileList = fileList.map(item => {
|
||||
item.status = 'finished';
|
||||
item.percentage = 100;
|
||||
item.uid = Date.now() + this.tempIndex++;
|
||||
return item;
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
Loading…
Add table
Add a link
Reference in a new issue