update upload

pull/2/head
baiyaaaaa 2016-08-20 18:27:37 +08:00
parent 2ceff8709e
commit 0aaf2df436
8 changed files with 530 additions and 274 deletions

View File

@ -7,33 +7,6 @@
.demo-box {
margin-bottom: 24px;
}
.el-draggeer__uploaded-image__btns {
margin-top: 45px;
color: #fff;
font-size: 14px;
& .btn {
display: inline-block;
& span {
opacity: 0;
transition: opacity .15s linear;
}
&:not(:first-child) {
margin-left: 35px;
}
&:hover span {
opacity: 1;
}
}
& i {
display: block;
font-size: 26px;
margin-bottom: 5px;
}
}
</style>
<script>
export default {
@ -43,6 +16,16 @@
},
handleRemove(file, fileList) {
console.log(file, fileList);
},
beforeUpload(file) {
if (file.size > 40000000) {
console.warn(file.name + ' is too large!');
return false;
}
return true;
},
handlePreview(file) {
console.log(file);
}
}
}
@ -51,31 +34,66 @@
## 基础使用
<div class="demo-box">
<el-upload action="http://127.0.0.1:9000/upload" @filechange="handleChange" @fileremove="handleRemove">
<el-upload action="http://element.alpha.elenet.me/upload" :on-preview="handlePreview" :on-remove="handleRemove">
<el-button size="small" type="primary">点击上传</el-button>
<div class="el-upload__tip" slot="tip">只能上传jpg/png文件且不超过500kb</div>
</el-upload>
</div>
```html
<el-upload action="http://127.0.0.1:9000/upload" @filechange="handleChange" @fileremove="handleRemove">
<el-upload action="http://element.alpha.elenet.me/upload" :on-preview="handlePreview" :on-remove="handleRemove">
<el-button size="small" type="primary">点击上传</el-button>
<div class="el-upload__tip" slot="tip">只能上传jpg/png文件且不超过500kb</div>
</el-upload>
<script>
export default {
methods: {
handleRemove(file, fileList) {
console.log(file, fileList);
},
handlePreview(file) {
console.log(file);
}
}
}
</script>
```
## 拖拽文件上传
<div class="demo-box">
<el-upload action="http://127.0.0.1:9000/upload" type="drag" :multiple="true">
<el-upload
action="http://element.alpha.elenet.me/upload"
type="drag"
:multiple="true"
:on-preview="handlePreview"
:on-remove="handleRemove">
<div class="el-upload__tip" slot="tip">只能上传jpg/png文件且不超过500kb</div>
</el-upload>
</div>
```html
<el-upload action="http://127.0.0.1:9000/upload" type="drag" :multiple="true">
<el-upload
action="http://element.alpha.elenet.me/upload"
type="drag"
:multiple="true"
:on-preview="handlePreview"
:on-remove="handleRemove">
<div class="el-upload__tip" slot="tip">只能上传jpg/png文件且不超过500kb</div>
</el-upload>
<script>
export default {
methods: {
handleRemove(file, fileList) {
console.log(file, fileList);
},
handlePreview(file) {
console.log(file);
}
}
}
</script>
```
## 图片缩略图模式
@ -83,23 +101,39 @@
上传文件类型限制为只能上传图片,并可展示本地缩略图,该模式暂不支持多选
<div class="demo-box">
<el-upload action="http://127.0.0.1:9000/upload" type="drag" mode="image">
<div class="el-draggeer__uploaded-image__btns" slot="interact">
<span class="btn"><i class="el-icon-share"></i><span>分享图片</span></span>
<span class="btn"><i class="el-icon-delete"></i><span>删除</span></span>
</div>
<el-upload
action="http://element.alpha.elenet.me/upload"
type="drag"
:thumbnail-mode="true"
:on-preview="handlePreview"
:on-remove="handleRemove"
>
<div class="el-upload__tip" slot="tip">只能上传jpg/png文件且不超过500kb</div>
</el-upload>
</div>
```html
<el-upload action="http://127.0.0.1:9000/upload" type="drag" mode="image">
<div class="el-draggeer__uploaded-image__btns" slot="interact">
<span class="btn"><i class="el-icon-share"></i><span>分享图片</span></span>
<span class="btn"><i class="el-icon-delete"></i><span>删除</span></span>
</div>
<el-upload
action="http://element.alpha.elenet.me/upload"
type="drag"
:thumbnail-mode="true"
:on-preview="handlePreview"
:on-remove="handleRemove"
>
<div class="el-upload__tip" slot="tip">只能上传jpg/png文件且不超过500kb</div>
</el-upload>
<script>
export default {
methods: {
handleRemove(file, fileList) {
console.log(file, fileList);
},
handlePreview(file) {
console.log(file);
}
}
}
</script>
```
## API
@ -113,6 +147,8 @@
| showUploadList | 是否显示已上传文件列表 | boolean | | true |
| type | 上传控件类型 | string | select,drag | select |
| accept | 可选参数, 接受上传的[文件类型](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-accept), 拖拽文件上传时不受此参数影响 | string | | |
| filechange | 可选参数, 上传文件改变时的回调 | function(file, fileList, event) | | |
| fileremove | 可选参数, 文件列表移除文件时的回调 | function(file, fileList) | | |
| onPreview | 可选参数, 点击已上传的文件链接时的钩子 | function(file) | | |
| onRemove | 可选参数, 文件列表移除文件时的钩子 | function(file, fileList) | | |
| beforeUpload | 可选参数, 上传文件之前的钩子,参数为上传的文件,若返回 false 或者 Promise 则停止上传。 | function(file) | | |
| thumbnailMode | 是否设置为图片模式,该模式下会显示图片缩略图 | boolean | | false |
| type | 上传控件类型 | string | select,drag | select |

View File

@ -1,25 +1,24 @@
@charset "UTF-8";
@import './var.css';
.fade-in-transition {
opacity: 1;
transition: var(--fade-transition);
}
.fade-in-linear-enter-active {
opacity: 1;
.fade-in-linear-enter-active,
.fade-in-linear-leave-active {
transition: var(--fade-linear-transition);
}
.fade-in-linear-enter,
.fade-in-linear-leave,
.fade-in-linear-leave-active {
opacity: 0;
transition: var(--fade-linear-transition);
}
.fade-in-enter-active,
.fade-in-leave-active {
transition: all .3s cubic-bezier(.55,0,.1,1);
}
.fade-in-enter,
.fade-in-leave,
.fade-in-linear-enter,
.fade-in-linear-leave {
.fade-in-leave-active {
opacity: 0;
}
@ -96,28 +95,19 @@
transition: opacity .3s cubic-bezier(.645,.045,.355,1);
}
.fade-enter,
.fade-leave,
.fade-leave-active {
opacity: 0;
}
.slide-in-bottom-enter {
animation: slideInBottomEnter .3s;
.list-move, .list-enter-active, .list-leave-active {
transition: all .5s cubic-bezier(.55,0,.1,1);
}
.slide-in-bottom-leave {
animation: slideInBottomLeave .3s;
}
@keyframes slideInBottomEnter {
0% {
opacity: 0;
transform: translate3d(0,50%,0);
}
}
@keyframes slideInBottomLeave {
to {
opacity: 0;
transform: translate3d(0,50%,0);
}
.list-enter, .list-leave-active {
opacity: 0;
transform: translate(0, -30px);
}
/*.list-leave-active {
position: absolute;
opacity: 0;
transform: scaleY(0.01) translate(30px, 0);
}*/

View File

@ -23,6 +23,11 @@
--color-black: #000;
--color-grey: #C0CCDA;
/* Link
-------------------------- */
--link-color: #475669;
--link-hover-color: var(--color-primary);
/* Border
-------------------------- */
--border-width-base: 1px;

View File

@ -19,6 +19,7 @@
margin-bottom: 10px;
}
@e file {
transition: all .5s cubic-bezier(.55,0,.1,1);
font-size: 14px;
color: #475669;
line-height: 32px;
@ -33,21 +34,16 @@
text-overflow: ellipsis;
position: relative;
a {
color: #475669;
transition: color .3s;
}
[class^="el-icon"] {
color: #99a9bf;
margin-right: 7px;
height: 100%;
line-height: inherit;
}
&:hover {
background-color: #eff2f7;
.el-upload__btn-delete {
display: block;
cursor: pointer;
}
}
& .el-progress {
position: absolute;
bottom: 0;
@ -61,6 +57,21 @@
top: 0;
height: 100%;
}
&:hover {
background-color: #eff2f7;
}
@when finished {
& a:hover {
color: var(--link-hover-color);
cursor: pointer;
}
&:hover {
.el-upload__btn-delete {
display: block;
cursor: pointer;
}
}
}
}
@e tip {
font-size: 12px;
@ -125,6 +136,7 @@
height: 100%;
overflow: hidden;
z-index: 10;
cursor: default;
& img {
display: block;
@ -140,6 +152,45 @@
height: 100%;
background-color: rgba(#000, .72);
text-align: center;
& .btn {
display: inline-block;
color: #fff;
font-size: 14px;
cursor: pointer;
vertical-align: middle;
transition: var(--md-fade-transition);
margin-top: 60px;
& i {
margin-top: 0;
}
& span {
opacity: 0;
transition: opacity .15s linear;
}
&:not(:first-child) {
margin-left: 35px;
}
&:hover {
transform: translateY(-13px);
& span {
opacity: 1;
}
}
& i {
color: #fff;
display: block;
font-size: 24px;
line-height: inherit;
margin: 0 auto 5px;
}
}
}
@e title {

View File

@ -1,4 +1,4 @@
const Upload = require('./src/upload');
const Upload = require('./src/index');
Upload.install = function(Vue) {
Vue.component(Upload.name, Upload);

View File

@ -0,0 +1,204 @@
<script>
import UploadList from './upload-list';
import Upload from './upload';
import ElProgress from 'packages/progress/index.js';
function noop() {
}
export default {
name: 'el-upload',
// extends: typeof FormData !== 'undefined' ? ajaxUpload : iframeUpload,
// extends: iframeUpload,
components: {
ElProgress,
UploadList,
Upload
},
props: {
action: {
type: String,
required: true
},
headers: {
type: Object,
default() {
return {
// 'Access-Control-Request-Methods': 'GET, PUT, POST, DELETE, OPTIONS',
// 'Access-Control-Request-Headers': 'Content-Type, Content-Range, Content-Disposition, Content-Description'
};
}
},
multiple: {
type: Boolean,
default: false
},
name: {
type: String,
default: 'file'
},
withCredentials: {
type: Boolean,
default: false
},
thumbnailMode: Boolean,
showUploadList: {
type: Boolean,
default: true
},
accept: String,
type: {
type: String,
default: 'select'
},
beforeUpload: Function,
onRemove: {
type: Function,
default: noop
},
onChange: {
type: Function,
default: noop
},
onPreview: {
type: Function,
default: noop
}
},
data() {
return {
uploadedFiles: [],
dragOver: false,
draging: false,
tempIndex: 1
};
},
methods: {
onStart(file) {
file.uid = Date.now() + this.tempIndex++;
let _file = {
status: 'uploading',
name: file.name,
size: file.size,
percentage: 0,
uid: file.uid,
showProgress: true
};
if (this.thumbnailMode) {
try {
_file.url = URL.createObjectURL(file);
} catch (err) {
console.log(err);
return;
}
}
this.uploadedFiles.push(_file);
},
onProgress(ev, file) {
var _file = this.getFile(file);
_file.percentage = ev.percent;
},
onSuccess(res, file) {
var _file = this.getFile(file);
_file.status = 'finished';
_file.response = res;
setTimeout(() => {
_file.showProgress = false;
}, 1000);
},
onError(err, file) {
var _file = this.getFile(file);
var fileList = this.uploadedFiles;
_file.status = 'fail';
fileList.splice(fileList.indexOf(_file), 1);
this.$emit('error', _file, fileList, err);
},
handleRemove(file) {
var fileList = this.uploadedFiles;
fileList.splice(fileList.indexOf(file), 1);
this.onRemove(file, fileList);
},
getFile(file) {
var fileList = this.uploadedFiles;
var target;
fileList.every(item => {
target = file.uid === item.uid ? item : null;
return !target;
});
return target;
},
handlePreview(file) {
if (file.status === 'finished') {
this.onPreview(file);
}
}
},
render(h) {
var uploadList;
if (this.showUploadList && !this.thumbnailMode) {
uploadList = (
<UploadList
files={this.uploadedFiles}
on-remove={this.handleRemove}
on-preview={this.handlePreview}>
</UploadList>
);
}
var props = {
props: {
action: this.action,
multiple: this.multiple,
'before-upload': this.beforeUpload,
'with-credentials': this.withCredentials,
name: this.name,
accept: this.thumbnailMode ? 'image/*' : this.accept,
'on-start': this.onStart,
'on-progress': this.onProgress,
'on-success': this.onSuccess,
'on-error': this.onError,
'on-preview': this.handlePreview,
'on-remove': this.handleRemove
}
};
if (this.type === 'select') {
return (
<div class="el-upload">
{uploadList}
<upload {...props}>
{this.$slots.default}
</upload>
{this.$slots.tip}
</div>
);
}
if (this.type === 'drag') {
props.props.type = 'drag';
return (
<div class="el-upload">
<upload {...props}>
{this.$slots.default}
</upload>
{this.$slots.tip}
{uploadList}
</div>
);
}
}
};
</script>

View File

@ -0,0 +1,37 @@
<template>
<transition-group tag="ul" class="el-upload__files" name="list">
<li
v-for="file in files"
class="el-upload__file"
:class="{
'is-finished': file.status === 'finished'
}"
:key="file"
@click="$emit('clickFile', file)"
>
<a class="el-upload__file__name" @click="$emit('preview', file)">
<i class="el-icon-document"></i>{{file.name}}
</a>
<i class="el-icon-check" v-if="file.status === 'finished' && file.showProgress"></i>
<span class="el-upload__btn-delete" @click="$emit('remove', file)" v-show="file.status === 'finished'"></span>
<el-progress
v-if="file.showProgress"
size="small"
:percentage="file.percentage"
:type="file.status === 'finished' ? 'green' : 'blue'">
</el-progress>
</li>
</transition-group>
</template>
<script>
export default {
props: {
files: {
type: Array,
default() {
return [];
}
}
}
};
</script>

View File

@ -1,187 +1,103 @@
<template>
<div class="el-upload">
<!-- 选择类型 -->
<template v-if="type === 'select'">
<ul class="el-upload__files" v-show="showUploadList && uploadedFiles.length > 0" transition="slide-in-bottom">
<li class="el-upload__file" v-for="file in uploadedFiles" transition="slide-in-bottom">
<i class="el-icon-document"></i>{{file.name}}
<i class="el-icon-check" v-show="file.status === 'success'"></i>
<span class="el-upload__btn-delete" @click="removeFile(file)" v-show="file.status === 'finished'"></span>
<el-progress
v-if="file.status === 'success' || file.status === 'uploading'"
size="small"
:percentage="file.percentage"
:type="file.status === 'success' ? 'green' : 'blue'">
</el-progress>
</li>
</ul>
<component :is="uploadComponent"
:action="action",
:multiple="multiple",
:with-credentials="withCredentials",
:name="name",
:accept="accept",
:on-start="onStart",
:on-progress="onProgress",
:on-success="onSuccess",
:on-error="onError"
>
<slot></slot>
</component>
<slot name="tip"></slot>
<div class="el-upload__inner"
:class="{
'el-dragger': type === 'drag',
'is-dragOver': dragOver,
'is-hover': mouseover,
'is-showImage': showThumbnail
}"
@click="$refs.input.click()"
@drop.prevent="onDrop"
@dragover.prevent="dragOver = true"
@dragleave.prevent="dragOver = false"
@mouseenter="mouseover = true"
@mouseleave="mouseover = false"
>
<slot></slot>
<template v-if="type === 'drag' && !showThumbnail">
<i class="el-icon-upload"></i>
<div class="el-dragger__text">将文件拖到此处<em>点击上传</em></div>
</template>
<!-- 拖拽类型 -->
<template v-if="type === 'drag'">
<div class="el-dragger"
:class="{
'is-dragOver': dragOver,
'is-draging': draging,
'is-hover': mouseover,
'is-showImage': showImageBlock
}"
@drop.prevent="dragOver = false"
@dragOver.prevent="dragOver = true"
@dragLeave.prevent="dragOver = false"
@mouseenter="mouseover = true"
@mouseleave="mouseover = false"
>
<template v-if="thumbnailMode">
<transition name="fade-in">
<el-progress
class="el-dragger__progress"
v-if="mode === 'image' && (image.status === 'success' || image.status === 'uploading')"
v-if="lastestFile.showProgress"
size="large"
:percentage="image.percentage"
:type="image.status === 'success' ? 'green' : 'blue'">
:percentage="lastestFile.percentage"
:type="lastestFile.status === 'finished' ? 'green' : 'blue'">
</el-progress>
<div class="el-dragger__uploaded-image"
v-if="mode === 'image' && image.status === 'finished'"
transition="slide-in-bottom"
>
<img :src="image.url">
<div v-show="mouseover" class="el-dragger__uploaded-image__interact" transition="fade-in">
<slot name="interact"></slot>
</transition>
<div class="el-dragger__uploaded-image" v-if="lastestFile.status === 'finished'" @click.stop>
<img :src="lastestFile.url">
<transition name="fade-in">
<div v-show="mouseover" class="el-dragger__uploaded-image__interact">
<div class="el-draggeer__uploaded-image__btns">
<span class="btn" @click="$refs.input.click()"><i class="el-icon-upload"></i><span>继续上传</span></span>
<span class="btn" @click="onPreview(lastestFile)"><i class="el-icon-search"></i><span>查看图片</span></span>
<span class="btn" @click="onRemove(lastestFile)"><i class="el-icon-delete"></i><span>删除</span></span>
</div>
</div>
<h4 v-show="mouseover" class="el-dragger__uploaded-image__title" transition="slide-in-bottom">{{image.name}}</h4>
</div>
<component :is="uploadComponent"
:action="action",
:multiple="multiple",
:with-credentials="withCredentials",
:name="name",
:accept="accept",
:on-start="onStart",
:on-progress="onProgress",
:on-success="onSuccess",
:on-error="onError"
>
<slot></slot>
</component>
</transition>
<transition name="md-fade-top">
<h4 v-show="mouseover" class="el-dragger__uploaded-image__title">{{lastestFile.name}}</h4>
</transition>
</div>
<slot name="tip" class="el-dragger__tip"></slot>
<ul class="el-upload__files"
v-if="mode !== 'image' && showUploadList"
v-show="uploadedFiles.length > 0"
transition="slide-in-bottom"
>
<li class="el-upload__file" v-for="file in uploadedFiles" transition="slide-in-bottom">
<i class="el-icon-document"></i>{{file.name}}
<i class="el-icon-check" v-show="file.status === 'success'"></i>
<span class="el-upload__btn-delete" @click="removeFile(file)" v-show="file.status === 'finished'"></span>
<el-progress
v-if="file.status === 'success' || file.status === 'uploading'"
size="small"
:percentage="file.percentage"
:type="file.status === 'success' ? 'green' : 'blue'">
</el-progress>
</li>
</ul>
</template>
<input class="el-upload__input" type="file" ref="input" @change="handleChange" :multiple="multiple" :accept="accept">
</div>
</template>
<script>
import AjaxUpload from './ajax-upload';
import IframeUpload from './iframe-upload';
import ElProgress from 'packages/progress/index.js';
import ajax from './ajax';
export default {
name: 'el-upload',
// extends: typeof FormData !== 'undefined' ? ajaxUpload : iframeUpload,
// extends: iframeUpload,
components: {
ElProgress,
AjaxUpload,
IframeUpload
},
props: {
type: String,
action: {
type: String,
required: true
},
headers: {
type: Object,
default() {
return {
// 'Access-Control-Request-Methods': 'GET, PUT, POST, DELETE, OPTIONS',
// 'Access-Control-Request-Headers': 'Content-Type, Content-Range, Content-Disposition, Content-Description'
};
}
},
multiple: false,
name: {
type: String,
default: 'file'
},
withCredentials: {
type: Boolean,
default: false
},
showUploadList: {
type: Boolean,
default: true
},
withCredentials: Boolean,
multiple: Boolean,
accept: String,
type: {
type: String,
default: 'select'
onStart: Function,
onProgress: Function,
onSuccess: Function,
onError: Function,
beforeUpload: Function,
onPreview: {
type: Function,
default: function() {}
},
mode: String
onRemove: {
type: Function,
default: function() {}
}
},
data() {
return {
uploading: false,
percentage: 0,
uploadedFiles: [],
filename: '',
success: false,
dragOver: false,
draging: false,
mouseover: false,
tempIndex: 1
mouseover: false
};
},
computed: {
uploadComponent() {
return typeof FormData !== 'undefined' ? 'AjaxUpload' : 'IframeUpload';
lastestFile() {
var uploadedFiles = this.$parent.uploadedFiles;
return uploadedFiles.length > 0 ? uploadedFiles[uploadedFiles.length - 1] : {};
},
image() {
return this.uploadedFiles.length > 0 ? this.uploadedFiles[this.uploadedFiles.length - 1] : {};
showThumbnail() {
var file = this.lastestFile;
return this.thumbnailMode && file.status && file.status !== 'fail';
},
showImageBlock() {
return this.mode === 'image' && this.image.status && this.image.status !== 'fail';
}
},
ready() {
if (this.mode === 'image') {
this.accept = 'image/*';
thumbnailMode() {
return this.$parent.thumbnailMode;
}
},
@ -189,59 +105,76 @@ export default {
isImage(str) {
return str.indexOf('image') !== -1;
},
removeFile(file) {
this.uploadedFiles.$remove(file);
this.$dispatch('fileremove', file, this.uploadedFiles);
handleChange(ev) {
const files = ev.target.files;
if (!files) {
return;
}
this.uploadFiles(files);
},
onStart(files) {
files.forEach(file => {
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 => {
let isImage = this.isImage(file.type);
let uid = Date.now() + this.tempIndex++;
let _file = {
status: 'uploading',
name: file.name,
size: file.size,
percentage: 0,
uid: uid
};
if (this.mode === 'image') {
if (!isImage) {
this.tempIndex--;
return;
} else {
_file.url = URL.createObjectURL(file);
}
if (this.thumbnailMode && !isImage) {
return;
} else {
this.upload(file);
}
this.uploadedFiles.push(_file);
file.index = this.uploadedFiles.length - 1;
});
},
onProgress(ev, file) {
this.uploadedFiles[file.index].percentage = ev.percent;
},
onSuccess(res, file) {
var _file = this.uploadedFiles[file.index];
upload(file) {
if (!this.beforeUpload) {
return this.post(file);
}
_file.status = 'success';
setTimeout(() => {
_file.status = 'finished';
this.reset();
}, 1000);
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);
}
},
onError(err, file) {
var _file = this.uploadedFiles[file.index];
post(file) {
this.onStart(file);
let formData = new FormData();
formData.append(this.name, file);
_file.status = 'finished';
this.uploadedFiles.$remove(_file);
this.reset();
console.log(err);
ajax(this.action, {
headers: this.headers,
withCredentials: this.withCredentials,
file: file,
filename: this.name,
onProgress: e => {
this.onProgress(e, file);
},
onSuccess: res => {
this.onSuccess(res, file);
},
onError: err => {
this.onError(err, file);
}
});
},
reset() {
this.uploading = false;
this.percent = 0;
this.filename = '';
onDrop(e) {
this.dragOver = false;
this.uploadFiles(e.dataTransfer.files);
}
}
};