v2.6.0-beta.1

master
git 8 years ago
parent 24bb304a8f
commit 4207dc0665

@ -1,6 +1,6 @@
# vue-upload-component # vue-upload-component
[![npm](https://img.shields.io/npm/dm/vue-upload-component.svg?style=flat-square)](https://www.npmjs.com/package/vue-upload-component) [![npm](https://img.shields.io/npm/v/vue-upload-component.svg?style=flat-square)](https://www.npmjs.com/package/vue-upload-component) [![license](https://img.shields.io/github/license/lian-yue/vue-upload-component.svg?style=flat-square)](https://www.npmjs.com/package/vue-upload-component)
>
> Vue.js file upload component > Vue.js file upload component
> The component is just a button > The component is just a button
@ -16,6 +16,29 @@
- [x] Customize the filter - [x] Customize the filter
- [x] thumbnails - [x] thumbnails
# Example
https://lian-yue.github.io/vue-upload-component/
# Installation
``` bash
npm install vue-upload-component --save
```
# Documentation
https://lian-yue.github.io/vue-upload-component/#/documents
---
> Vue.js 文件上传组建 > Vue.js 文件上传组建
> 组件只是一个按钮 > 组件只是一个按钮
@ -29,13 +52,18 @@
- [x] 自定义过滤器 - [x] 自定义过滤器
- [x] 缩略图 - [x] 缩略图
# 演示 (Example) # 演示
https://lian-yue.github.io/vue-upload-component/#/zh-cn/
https://lian-yue.github.io/vue-upload-component/
## 安装
# 文档 (Documents) ``` bash
npm install vue-upload-component --save
```
**[中文文档](https://github.com/lian-yue/vue-upload-component/tree/2.0/docs/zh-cn/README.md)** # 文档
**[English document](https://github.com/lian-yue/vue-upload-component/tree/2.0/docs/en/README.md)** https://lian-yue.github.io/vue-upload-component/#/zh-cn/documents

4298
dist/example.js vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,34 @@
.file-uploads {
overflow: hidden;
position: relative;
text-align: center;
display: inline-block;
}
.file-uploads.file-uploads-html4 input[type="file"] {
opacity: 0;
font-size: 20em;
z-index: 1;
top: 0;
left: 0;
right: 0;
bottom: 0;
position: absolute;
width: 100%;
height: 100%;
}
.file-uploads.file-uploads-html5 input[type="file"] {
overflow: hidden;
position: fixed;
width: 1px;
height: 1px;
z-index: -1;
opacity: 0;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

3867
docs/dist/index.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,30 @@
export default {
header: {
logo: 'Vuejs',
home: 'Home',
examples: 'Examples',
documents: 'Documentation',
blog: 'Blog',
locale: 'Language(语言)',
issues: 'Issues',
github: 'Github',
},
locale: {
en: 'English',
'zh-cn': '中文(简体)',
},
document: {
title: 'Documentation',
},
example: {
full: 'Full',
simple: 'Simple',
avatar: 'Avatar',
drag: 'Drag',
multiple: 'Multiple',
vuex: 'Vuex',
}
}

@ -0,0 +1,14 @@
// import Vue from 'vue'
import VueI18n from 'vue-i18n'
import en from './en'
import zhCN from './zh-cn'
// Vue.use(VueI18n)
export default new VueI18n({
locale: 'en',
messages: {
'zh-cn': zhCN,
en,
}
})

@ -0,0 +1,31 @@
export default {
header: {
logo: 'Vuejs',
home: '首页',
examples: '演示',
documents: '文档',
blog: 'Blog',
locale: 'Language(语言)',
issues: 'Issues',
github: 'Github',
},
locale: {
en: 'English',
'zh-cn': '中文(简体)',
},
document: {
title: '文档',
},
example: {
full: '完整例子',
simple: '简单例子',
avatar: '头像例子',
drag: '拖拽例子',
multiple: '多实例',
vuex: 'Vuex',
}
}

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>vue-upload-component- Upload Component - Uploader</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0-beta/dist/css/bootstrap.min.css" rel="stylesheet" />
<link href="https://cdn.jsdelivr.net/npm/font-awesome/css/font-awesome.min.css" rel="stylesheet" />
<link href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release/build/styles/atom-one-light.min.css" rel="stylesheet" />
<link href="https://cdn.jsdelivr.net/npm/cropperjs/dist/cropper.css" rel="stylesheet" />
</head>
<body>
<div id="app"></div>
<script src="https://cdn.jsdelivr.net/npm/es6-promise/dist/es6-promise.auto.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuex/dist/vuex.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue-router/dist/vue-router.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue-i18n/dist/vue-i18n.js"></script>
<script src="https://cdn.jsdelivr.net/npm/marked/lib/marked.js"></script>
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release/build/highlight.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/cropperjs/dist/cropper.js"></script>
<script src="./dist/index.js"></script>
</body>
</html>

@ -0,0 +1,131 @@
import Vue from 'vue'
import marked from 'marked'
import highlightjs from 'highlight.js'
import store from './store'
import router from './router'
import i18n from './i18n'
import App from './views/App'
Vue.config.silent = false
Vue.config.devtools = true
class Renderer extends marked.Renderer {
heading(text, level, raw) {
let rawName = raw.toLowerCase().replace(/([\u0000-\u002F\u003A-\u0060\u007B-\u007F]+)/g, '-').replace(/^\-+|\-+$/, '')
if (!this.options.headers) {
this.options.headers = []
}
while (this.options.headers.length >= level) {
this.options.headers.pop()
}
let parent = this.options.headers.filter(value => !!value).join('-')
if (parent) {
parent = parent + '-'
}
while (this.options.headers.length < (level - 1)) {
this.options.headers.push('')
}
this.options.headers.push(rawName)
return '<h'
+ level
+ ' id="'
+ this.options.headerPrefix
+ parent
+ rawName
+ '">'
+ text
+ '</h'
+ level
+ '>\n'
}
}
marked.setOptions({
renderer: new Renderer(),
gfm: true,
tables: true,
breaks: false,
pedantic: false,
sanitize: false,
smartLists: true,
smartypants: false,
highlight(code, lang) {
if (lang) {
return highlightjs.highlight(lang, code).value
} else {
return highlightjs.highlightAuto(code).value
}
}
})
Vue.directive('markdown', function (el, binding, vnode) {
if (!el.className || !/vue-markdown/.test(el.className)) {
el.className += ' vue-markdown'
}
let text = ''
for (let i = 0; i < vnode.children.length; i++) {
text += vnode.children[i].text || ''
}
el.innerHTML = marked(text)
let selectorList = el.querySelectorAll('a')
for (let i = 0; i < selectorList.length; i++) {
selectorList[i].onclick = function (e) {
if (e.metaKey || e.ctrlKey || e.shiftKey) {
return
}
if (e.defaultPrevented) {
return
}
if (e.button !== undefined && e.button !== 0) {
return
}
if (this.host !== window.location.host) {
return
}
let href = this.getAttribute('href')
if (!href) {
return
}
if (href.charAt(0) !== '#') {
return
}
e.preventDefault()
router.push(href)
}
}
})
Vue.filter('formatSize', function (size) {
if (size > 1024 * 1024 * 1024 * 1024) {
return (size / 1024 / 1024 / 1024 / 1024).toFixed(2) + ' TB'
} else if (size > 1024 * 1024 * 1024) {
return (size / 1024 / 1024 / 1024).toFixed(2) + ' GB'
} else if (size > 1024 * 1024) {
return (size / 1024 / 1024).toFixed(2) + ' MB'
} else if (size > 1024) {
return (size / 1024).toFixed(2) + ' KB'
}
return size.toString() + ' B'
})
Vue.filter('toLocale', function (to) {
return '/' + i18n.locale + to
})
new Vue({
store,
router,
i18n,
...App
}).$mount('#app')

@ -0,0 +1,83 @@
// import Vue from 'vue'
import VueRouter from 'vue-router'
import i18n from './i18n'
import RouterComponent from './views/Router'
import DocumentComponent from './views/Document'
import ExampleComponent from './views/Example'
import FullExampleComponent from './views/examples/Full'
import SimpleExampleComponent from './views/examples/Simple'
import AvatarExampleComponent from './views/examples/Avatar'
import DragExampleComponent from './views/examples/Drag'
import MultipleExampleComponent from './views/examples/Multiple'
import VuexExampleComponent from './views/examples/Vuex'
// Vue.use(VueRouter)
let examples = [
{
path: '',
component: FullExampleComponent,
},
{
path: 'full',
component: FullExampleComponent,
},
{
path: 'simple',
component: SimpleExampleComponent,
},
{
path: 'avatar',
component: AvatarExampleComponent,
},
{
path: 'drag',
component: DragExampleComponent,
},
{
path: 'multiple',
component: MultipleExampleComponent,
},
{
path: 'vuex',
component: VuexExampleComponent,
},
]
const router = new VueRouter({
mode: 'hash',
fallback: false,
scrollBehavior() {
return { y: 0 }
},
routes: [
{
path: '/:locale(' + Object.keys(i18n.messages).join('|') + ')?',
component: RouterComponent,
children: [
{
path: 'documents',
component: DocumentComponent,
},
{
path: 'examples',
component: ExampleComponent,
children: examples,
},
{
path: '',
component: ExampleComponent,
children: examples,
},
]
},
]
})
export default router

@ -1,7 +1,6 @@
import Vue from 'vue' // import Vue from 'vue'
import Vuex from 'vuex' import Vuex from 'vuex'
//
// Vue.use(Vuex) // Vue.use(Vuex)

@ -0,0 +1,192 @@
<template>
<div>
<header id="header" class="navbar navbar-expand-lg navbar-dark bg-dark">
<router-link :exact="true" class="navbar-brand" :to="'/' | toLocale">{{$t('header.logo')}}</router-link>
<button class="navbar-toggler" type="button" @click.prevent="showNav = !showNav">
<span class="navbar-toggler-icon"></span>
</button>
<nav :class="{collapse: true, 'navbar-collapse': true, show: showNav}" id="navbar">
<ul class="navbar-nav">
<li class="nav-item">
<router-link active-class="active" :exact="true" :class="'nav-link' + ($route.path === '/' ? ' active' : '')" :to="'/' | toLocale">{{$t('header.home')}}</router-link>
</li>
<li class="nav-item">
<router-link active-class="active" class="nav-link" :to="'/documents' | toLocale">{{$t('header.documents')}}</router-link>
</li>
<li class="nav-item">
<router-link active-class="active" class="nav-link" :to="'/examples' | toLocale">{{$t('header.examples')}}</router-link>
</li>
<li class="nav-item">
<a rel="license noopener" class="nav-link" href="https://www.lianyue.org" target="_blank">{{$t('header.blog')}}</a>
</li>
</ul>
<ul class="navbar-nav ml-md-auto">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" @click.prevent="onLocale(true)" @focus="onLocale(true)" @blur="onLocale(false)">
{{$t('header.locale')}}
</a>
<div :class="{'dropdown-menu': true, show: showLocale}" @blur="onLocale(false)">
<router-link class="dropdown-item" :to="'/' + name + ($route.params.locale ? $route.fullPath.substr($route.params.locale.length + 1) : $route.fullPath)" v-for="(value, name) in locale" :key="name">{{value}}</router-link>
</div>
</li>
<li class="nav-item">
<a class="nav-link" href="https://github.com/lian-yue/vue-upload-component/issues">
{{$t('header.issues')}}
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="https://github.com/lian-yue/vue-upload-component">
{{$t('header.github')}}
</a>
</li>
</ul>
</nav>
</header>
<router-view></router-view>
</div>
</template>
<style>
#header {
position: -webkit-sticky;
position: sticky;
top: 0;
z-index: 1071;
}
#sidebar {
background: #fff;
border-right: 1px solid #e5e5e5;
border-bottom: 1px solid #e5e5e5;
}
@media (min-width: 768px) {
#sidebar {
position: -webkit-sticky;
position: sticky;
top: 3.5rem;
z-index: 1000;
max-height: calc(100vh - 3.5rem);
border-right: 1px solid #e5e5e5;
border-bottom: 1px solid #e5e5e5;
}
}
#sidebar-nav {
padding-top: 1rem;
padding-bottom: 1rem;
margin-right: -15px;
margin-left: -15px;
max-height: 100%;
overflow-y: auto;
}
#sidebar-nav .nav {
display: block;
}
#sidebar-nav .nav .nav-item .nav {
display: none;
margin-bottom: 1rem;
}
#sidebar-nav .nav .nav-item .nav {
display: none;
margin-bottom: 1rem;
}
#sidebar-nav .nav .nav-item.active .nav, #sidebar-nav .nav .active + .nav {
display: block;
}
@media (min-width: 768px) {
#sidebar-nav .nav .nav-item .nav {
display: block;
}
}
#sidebar-nav .nav .nav-link.active, #sidebar-nav .nav .active > .nav-link{
color: #262626;
font-weight: 500;
}
#sidebar-nav .nav-item .nav-link {
padding: .25rem 1rem;
font-weight: 500;
color: #666
}
#sidebar-nav .nav-item .nav-item .nav-link {
font-weight: 400;
font-size: 85%;
margin-left: 1rem
}
#main {
padding-top: 1rem;
margin-bottom: 2rem
}
blockquote {
margin-bottom: 1rem;
font-size: 1.25rem;
padding: 0 1em;
color: #6a737d;
border-left: 0.25em solid #dfe2e5;
}
pre {
padding: 16px;
overflow: auto;
font-size: 85%;
line-height: 1.45;
background-color: #f6f8fa;
border-radius: 3px;
}
.modal-backdrop.fade {
visibility: hidden;
}
.modal-backdrop.fade.show {
visibility: visible;
}
.fade.show {
display: block;
z-index: 1072;
}
</style>
<script>
export default {
data() {
return {
showLocale: false,
showNav: false,
}
},
beforeCreate() {
if (this.$route.params.locale && this.$route.params.locale !== this.$i18n.locale) {
this.$i18n.locale = this.$route.params.locale
}
},
beforeUpdate() {
if (this.$route.params.locale && this.$route.params.locale !== this.$i18n.locale) {
this.$i18n.locale = this.$route.params.locale
}
},
computed: {
locale() {
let i18n = this.$i18n
return i18n.messages[i18n.locale].locale
},
},
methods: {
onLocale(show) {
if (show) {
this.showLocale = show
} else {
setTimeout(() => {
this.showLocale = show
}, 128)
}
}
}
}
</script>

@ -0,0 +1,119 @@
<template>
<div class="container-fluid">
<div class="row flex-xl-nowrap">
<div class="col-12 col-md-3 col-xl-2" id="sidebar">
<nav id="sidebar-nav" class="collapse show">
<ul class="nav">
<li :class="{'nav-item': true, active: (!$route.hash && !index) || $route.hash.indexOf(group.hash) === 1}" v-for="(group, index) in navs">
<router-link active-class="active" :class="{'nav-link': true, active: $route.hash.indexOf(group.hash) === 1}" :to="'#' + group.hash">{{group.name}}</router-link>
<ul class="nav" v-if="group.children.length">
<li class="nav-item" v-for="child in group.children">
<router-link active-class="active" class="nav-link" :to="'#' + group.hash + '-' + child.hash">{{child.name}}</router-link>
</li>
</ul>
</li>
</ul>
</nav>
</div>
<main class="col-12 col-md-9 col-xl-10 py-md-3 pr-md-5 pl-md-5" id="main" role="main">
<h1 class="document-title" id="document-title">{{$t('document.title')}}</h1>
<div class="document-content" v-markdown>{{document}}</div>
</main>
</div>
</div>
</div>
</template>
<style>
.document-title {
margin-bottom: 2rem;
padding-bottom: 1rem;
border-bottom: 1px solid #ddd;
}
.document-content h2 {
padding-top: 1rem;
padding-bottom: 1rem;
margin-top: 4rem;
border-bottom: 1px solid #eaecef;
}
.document-content h2:first-child {
margin-top: 0;
}
.document-content h2 + h3 {
margin-top: 0rem;
}
.document-content h3 {
margin-top: 1.5rem;
padding-top: 1rem;
margin-bottom: 1rem;
}
</style>
<script>
import marked from 'marked'
export default {
computed: {
document() {
return require('../docs/' + this.$i18n.locale)
},
navs() {
let tokens = marked.lexer(this.document)
let rootNode = {
name: '',
children: [],
level: 1,
}
let parent = rootNode
let navPrev
for (let i = 0; i < tokens.length; i++) {
let token = tokens[i]
if (token.type !== 'heading') {
continue
}
let nav = {
name: token.text,
hash: token.text.toLowerCase().replace(/([\u0000-\u002F\u003A-\u0060\u007B-\u007F]+)/g, '-').replace(/^\-+|\-+$/, ''),
level: token.depth,
children: [],
}
if (!navPrev || nav.level === navPrev.level) {
// empty
} else if (nav.level > navPrev.level) {
// next
parent = navPrev
} else {
while (nav.level < navPrev.level && navPrev.parent) {
navPrev = navPrev.parent
parent = navPrev.parent
}
}
nav.parent = parent
parent.children.push(nav)
navPrev = nav
}
return rootNode.children
},
},
watch: {
/**
* [$route description]
* @param {[type]} route [description]
* @return {[type]} [description]
*/
$route(route) {
if (route.hash) {
let el = document.querySelector(route.hash)
window.scrollTo(0, el ? el.offsetTop : 0)
} else {
window.scrollTo(0, 0)
}
}
},
}
</script>

@ -0,0 +1,31 @@
<template>
<div class="container-fluid">
<div class="row flex-xl-nowrap">
<div class="col-12 col-md-3 col-xl-2" id="sidebar">
<nav id="sidebar-nav" class="collapse show">
<ul class="nav">
<li class="nav-item">
<router-link active-class="active" class="nav-link" :to="'/examples/full' | toLocale">{{$t('example.full')}}</router-link>
</li>
<li class="nav-item">
<router-link active-class="active" class="nav-link" :to="'/examples/simple' | toLocale">{{$t('example.simple')}}</router-link>
</li>
<li class="nav-item">
<router-link active-class="active" class="nav-link" :to="'/examples/avatar' | toLocale">{{$t('example.avatar')}}</router-link>
</li>
<li class="nav-item">
<router-link active-class="active" class="nav-link" :to="'/examples/drag' | toLocale">{{$t('example.drag')}}</router-link>
</li>
<li class="nav-item">
<router-link active-class="active" class="nav-link" :to="'/examples/multiple' | toLocale">{{$t('example.multiple')}}</router-link>
</li>
<li class="nav-item">
<router-link active-class="active" class="nav-link" :to="'/examples/vuex' | toLocale">{{$t('example.vuex')}}</router-link>
</li>
</ul>
</nav>
</div>
<main class="col-12 col-md-9 col-xl-10 py-md-3 pr-md-5 pl-md-5" id="main" role="main"><router-view></router-view></main>
</div>
</div>
</template>

@ -0,0 +1,3 @@
<template>
<router-view></router-view>
</template>

@ -0,0 +1,7 @@
<template>
<div class="example-avatar">
No content
</div>
</template>
<style>
</style>

@ -0,0 +1,7 @@
<template>
<div class="example-drag">
No content
</div>
</template>
<style>
</style>

@ -0,0 +1,602 @@
<template>
<div class="example-full">
<button type="button" class="btn btn-danger float-right btn-is-option" @click.prevent="isOption = !isOption">
<i class="fa fa-cog" aria-hidden="true"></i>
Options
</button>
<h1 id="document-title" class="document-title">Full Example</h1>
<div v-show="$refs.upload && $refs.upload.dropActive" class="drop-active">
<h3>Drop files to upload</h3>
</div>
<div class="upload" v-show="!isOption">
<table class="table table-hover">
<thead>
<tr>
<th>#</th>
<th>Thumb</th>
<th>Name</th>
<th>Size</th>
<th>Speed</th>
<th>Status</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr v-if="!files.length">
<td colspan="7">
<div class="text-center p-5">
<h4>Drop files anywhere to upload<br/>or</h4>
<label :for="name" class="btn btn-lg btn-primary">Select Files</label>
</div>
</td>
</tr>
<tr v-for="(file, index) in files" :key="file.id">
<td>{{index}}</td>
<td>
<img v-if="file.thumb" :src="file.thumb" width="40" height="auto" />
<span v-else>No Image</span>
</td>
<td>
<div class="filename">
{{file.name}}
</div>
<div class="progress" v-if="file.active || file.progress !== '0.00'">
<div :class="{'progress-bar': true, 'progress-bar-striped': true, 'bg-danger': file.error, 'progress-bar-animated': file.active}" role="progressbar" :style="{width: file.progress + '%'}">{{file.progress}}%</div>
</div>
</td>
<td>{{file.size | formatSize}}</td>
<td>{{file.speed | formatSize}}</td>
<td v-if="file.error">{{file.error}}</td>
<td v-else-if="file.success">success</td>
<td v-else-if="file.active">active</td>
<td v-else></td>
<td>
<div class="btn-group">
<button class="btn btn-secondary btn-sm dropdown-toggle" type="button">
Action
</button>
<div class="dropdown-menu">
<a :class="{'dropdown-item': true, disabled: file.active || file.success}" href="#" @click.prevent="file.active || file.success ? false : onEditFileShow(file)">Edit</a>
<a :class="{'dropdown-item': true, disabled: !file.active}" href="#" @click.prevent="file.active ? $refs.upload.update(file, {error: 'cancel'}) : false">Cancel</a>
<a class="dropdown-item" href="#" v-if="file.active" @click.prevent="$refs.upload.update(file, {active: false})">Abort</a>
<a class="dropdown-item" href="#" v-else-if="file.error && $refs.upload.features.html5" @click.prevent="$refs.upload.update(file, {active: true, error: '', progress: '0.00'})">Retry upload</a>
<a :class="{'dropdown-item': true, disabled: file.success}" href="#" v-else @click.prevent="file.success ? false : $refs.upload.update(file, {active: true})">Upload</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#" @click.prevent="$refs.upload.remove(file)">Remove</a>
</div>
</div>
</td>
</tr>
</tbody>
</table>
<div class="example-foorer">
<div class="footer-status float-right">
Drop: {{$refs.upload ? $refs.upload.drop : false}},
Active: {{$refs.upload ? $refs.upload.active : false}},
Uploaded: {{$refs.upload ? $refs.upload.uploaded : true}},
Drop active: {{$refs.upload ? $refs.upload.dropActive : false}}
</div>
<div class="btn-group">
<file-upload class="btn btn-primary dropdown-toggle"
:post-action="postAction"
:put-action="putAction"
:extensions="extensions"
:accept="accept"
:multiple="multiple"
:directory="directory"
:size="size || 0"
:thread="thread < 1 ? 1 : (thread > 5 ? 5 : thread)"
:headers="headers"
:data="data"
:drop="drop"
:drop-directory="dropDirectory"
v-model="files"
@input-filter="inputFilter"
@input-file="inputFile"
ref="upload">
<i class="fa fa-plus"></i>
Select
</file-upload>
<div class="dropdown-menu">
<label class="dropdown-item" :for="name">Add files</label>
<a class="dropdown-item" href="#" @click="onAddFolader">Add folder</a>
<a class="dropdown-item" href="#" @click.prevent="addData.show = true">Add data</a>
</div>
</div>
<button type="button" class="btn btn-success" v-if="!$refs.upload || !$refs.upload.active" @click.prevent="$refs.upload.active = true">
<i class="fa fa-arrow-up" aria-hidden="true"></i>
Start Upload
</button>
<button type="button" class="btn btn-danger" v-else @click.prevent="$refs.upload.active = false">
<i class="fa fa-stop" aria-hidden="true"></i>
Stop Upload
</button>
</div>
</div>
<div class="option" v-show="isOption">
<div class="form-group">
<label for="accept">Accept:</label>
<input type="text" id="accept" class="form-control" v-model="accept">
<small class="form-text text-muted">Allow upload mime type</small>
</div>
<div class="form-group">
<label for="extensions">Extensions:</label>
<input type="text" id="extensions" class="form-control" v-model="extensions">
<small class="form-text text-muted">Allow upload file extension</small>
</div>
<div class="form-group">
<label>PUT Upload:</label>
<div class="form-check">
<label class="form-check-label">
<input class="form-check-input" type="radio" name="put-action" id="put-action" value="" v-model="putAction"> Off
</label>
</div>
<div class="form-check">
<label class="form-check-label">
<input class="form-check-input" type="radio" name="put-action" id="put-action" value="/upload/put" v-model="putAction"> On
</label>
</div>
<small class="form-text text-muted">After the shutdown, use the POST method to upload</small>
</div>
<div class="form-group">
<label for="thread">Thread:</label>
<input type="number" max="5" min="1" id="thread" class="form-control" v-model.number="thread">
<small class="form-text text-muted">Also upload the number of files at the same time (number of threads)</small>
</div>
<div class="form-group">
<label for="size">Max size:</label>
<input type="number" min="0" id="size" class="form-control" v-model.number="size">
</div>
<div class="form-group">
<label for="minSize">Min size:</label>
<input type="number" min="0" id="minSize" class="form-control" v-model.number="minSize">
</div>
<div class="form-group">
<div class="form-check">
<label class="form-check-label">
<input type="checkbox" id="drop" class="form-check-input" v-model="drop"> Drop
</label>
</div>
<small class="form-text text-muted">Drag and drop upload</small>
</div>
<div class="form-group">
<div class="form-check">
<label class="form-check-label">
<input type="checkbox" id="drop-directory" class="form-check-input" v-model="dropDirectory"> Drop directory
</label>
</div>
<small class="form-text text-muted">Not checked, filter the dragged folder</small>
</div>
<div class="form-group">
<div class="form-check">
<label class="form-check-label">
<input type="checkbox" id="upload-auto" class="form-check-input" v-model="uploadAuto"> Auto start
</label>
</div>
<small class="form-text text-muted">Automatically activate upload</small>
</div>
<div class="form-group">
<button type="button" class="btn btn-primary btn-lg btn-block" @click.prevent="isOption = !isOption">Confirm</button>
</div>
</div>
<div :class="{'modal-backdrop': true, 'fade': true, show: addData.show}"></div>
<div :class="{modal: true, fade: true, show: addData.show}" id="modal-add-data" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Add data</h5>
<button type="button" class="close" @click.prevent="addData.show = false">
<span>&times;</span>
</button>
</div>
<form @submit.prevent="onAddData">
<div class="modal-body">
<div class="form-group">
<label for="name">Name:</label>
<input type="text" class="form-control" required id="name" placeholder="Please enter a file name" v-model="addData.name">
<small class="form-text text-muted">Such as <code>filename.txt</code></small>
</div>
<div class="form-group">
<label for="type">Type:</label>
<input type="text" class="form-control" required id="type" placeholder="Please enter the MIME type" v-model="addData.type">
<small class="form-text text-muted">Such as <code>text/plain</code></small>
</div>
<div class="form-group">
<label for="content">Content:</label>
<textarea class="form-control" required id="content" rows="3" placeholder="Please enter the file contents" v-model="addData.content"></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" @click.prevent="addData.show = false">Close</button>
<button type="submit" class="btn btn-primary">Save</button>
</div>
</form>
</div>
</div>
</div>
<div :class="{'modal-backdrop': true, 'fade': true, show: editFile.show}"></div>
<div :class="{modal: true, fade: true, show: editFile.show}" id="modal-edit-file" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Edit file</h5>
<button type="button" class="close" @click.prevent="editFile.show = false">
<span>&times;</span>
</button>
</div>
<form @submit.prevent="onEditorFile">
<div class="modal-body">
<div class="form-group">
<label for="name">Name:</label>
<input type="text" class="form-control" required id="name" placeholder="Please enter a file name" v-model="editFile.name">
</div>
<div class="form-group" v-if="editFile.show && editFile.blob && editFile.type && editFile.type.substr(0, 6) === 'image/'">
<label>Image: </label>
<div class="edit-image">
<img :src="editFile.blob" ref="editImage" />
</div>
<div class="edit-image-tool">
<div class="btn-group" role="group">
<button type="button" class="btn btn-primary" @click="editFile.cropper.rotate(-90)" title="cropper.rotate(-90)"><i class="fa fa-undo" aria-hidden="true"></i></button>
<button type="button" class="btn btn-primary" @click="editFile.cropper.rotate(90)" title="cropper.rotate(90)"><i class="fa fa-repeat" aria-hidden="true"></i></button>
</div>
<div class="btn-group" role="group">
<button type="button" class="btn btn-primary" @click="editFile.cropper.crop()" title="cropper.crop()"><i class="fa fa-check" aria-hidden="true"></i></button>
<button type="button" class="btn btn-primary" @click="editFile.cropper.clear()" title="cropper.clear()"><i class="fa fa-remove" aria-hidden="true"></i></button>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" @click.prevent="editFile.show = false">Close</button>
<button type="submit" class="btn btn-primary">Save</button>
</div>
</form>
</div>
</div>
</div>
<div class="pt-5">
Source code: <a href="https://github.com/lian-yue/vue-upload-component/tree/2.0/docs/views/examples/Full.vue">https://github.com/lian-yue/vue-upload-component/tree/2.0/docs/views/examples/Full.vue</a>
</div>
</div>
</template>
<style>
.example-full .btn-group .dropdown-menu {
display: block;
visibility: hidden;
transition: all .2s
}
.example-full .btn-group:hover > .dropdown-menu {
visibility: visible;
}
.example-full label.dropdown-item {
margin-bottom: 0;
}
.example-full .btn-group .dropdown-toggle {
margin-right: .6rem
}
.example-full .filename {
margin-bottom: .3rem
}
.example-full .btn-is-option {
margin-top: 0.25rem;
}
.example-full .example-foorer {
padding: .5rem 0;
border-top: 1px solid #e9ecef;
border-bottom: 1px solid #e9ecef;
}
.example-full .edit-image img {
max-width: 100%;
}
.example-full .edit-image-tool {
margin-top: .6rem;
}
.example-full .edit-image-tool .btn-group{
margin-right: .6rem;
}
.example-full .footer-status {
padding-top: .4rem;
}
.example-full .drop-active {
top: 0;
bottom: 0;
right: 0;
left: 0;
position: fixed;
z-index: 9999;
opacity: .6;
text-align: center;
background: #000;
}
.example-full .drop-active h3 {
margin: -.5em 0 0;
position: absolute;
top: 50%;
left: 0;
right: 0;
-webkit-transform: translateY(-50%);
-ms-transform: translateY(-50%);
transform: translateY(-50%);
font-size: 40px;
color: #fff;
padding: 0;
}
</style>
<script>
import Cropper from 'cropperjs'
import FileUpload from 'vue-upload-component'
export default {
components: {
FileUpload,
},
data() {
return {
files: [],
accept: 'image/png,image/gif,image/jpeg,image/webp',
extensions: 'gif,jpg,jpeg,png,webp',
// extensions: ['gif', 'jpg', 'jpeg','png', 'webp'],
// extensions: /\.(gif|jpe?g|png|webp)$/i,
minSize: 1024,
size: 1024 * 1024 * 10,
multiple: true,
directory: false,
drop: true,
dropDirectory: true,
thread: 3,
name: 'file',
postAction: '/upload/post',
putAction: '/upload/put',
headers: {
'X-Csrf-Token': 'xxxx',
},
data: {
'_csrf_token': 'xxxxxx',
},
uploadAuto: false,
isOption: false,
addData: {
show: false,
name: '',
type: '',
content: '',
},
editFile: {
show: false,
name: '',
}
}
},
watch: {
'editFile.show'(newValue, oldValue) {
// error
if (!newValue && oldValue) {
this.$refs.upload.update(this.editFile.id, { error: this.editFile.error || '' })
}
if (newValue) {
this.$nextTick(function () {
if (!this.$refs.editImage) {
return
}
let cropper = new Cropper(this.$refs.editImage, {
autoCrop: false,
})
this.editFile = {
...this.editFile,
cropper
}
})
}
},
'addData.show'(show) {
if (show) {
this.addData.name = ''
this.addData.type = ''
this.addData.content = ''
}
},
},
methods: {
inputFilter(newFile, oldFile, prevent) {
if (newFile && !oldFile) {
// Before adding a file
//
// Filter system files or hide files
//
if (/(\/|^)(Thumbs\.db|desktop\.ini|\..+)$/.test(newFile.name)) {
return prevent()
}
// Filter php html js file
// php html js
if (/\.(php5?|html?|jsx?)$/i.test(newFile.name)) {
return prevent()
}
}
if (newFile && (!oldFile || newFile.file !== oldFile.file)) {
// Create a blob field
// blob
newFile.blob = ''
let URL = window.URL || window.webkitURL
if (URL && URL.createObjectURL) {
newFile.blob = URL.createObjectURL(newFile.file)
}
// Thumbnails
//
newFile.thumb = ''
if (newFile.blob && newFile.type.substr(0, 6) === 'image/') {
newFile.thumb = newFile.blob
}
}
},
// add, update, remove File Event
inputFile(newFile, oldFile) {
if (newFile && oldFile) {
// update
if (newFile.active && !oldFile.active) {
// beforeSend
// min size
if (newFile.size >= 0 && this.minSize > 0 && newFile.size < this.minSize) {
this.$refs.upload.update(newFile, { error: 'size' })
}
}
if (newFile.progress !== oldFile.progress) {
// progress
}
if (newFile.error && !oldFile.error) {
// error
}
if (newFile.success && !oldFile.success) {
// success
}
}
if (!newFile && oldFile) {
// remove
if (oldFile.success && oldFile.response.id) {
// $.ajax({
// type: 'DELETE',
// url: '/upload/delete?id=' + oldFile.response.id,
// })
}
}
// Automatically activate upload
if (newFile && !oldFile && this.uploadAuto && !this.$refs.upload.active) {
this.$refs.upload.active = true
}
},
alert(message) {
alert(message)
},
onEditFileShow(file) {
this.editFile = { ...file, show: true }
this.$refs.upload.update(file, { error: 'edit' })
},
async onEditorFile() {
if (!this.$refs.upload.features.html5) {
this.alert('Your browser does not support')
this.editFile.show = false
return
}
let data = {
name: this.editFile.name,
}
if (this.editFile.cropper) {
let blob = await new Promise((resolve, reject) => {
this.editFile.cropper.getCroppedCanvas().toBlob(function (value) {
resolve(value)
})
})
data.file = new File([blob], data.name, { type: blob.type })
data.type = blob.type
data.size = blob.size
}
this.$refs.upload.update(this.editFile.id, data)
this.editFile.error = ''
this.editFile.show = false
},
// add folader
onAddFolader() {
if (!this.$refs.upload.features.directory) {
this.alert('Your browser does not support')
return
}
let input = this.$refs.upload.$el.querySelector('input')
input.directory = true
input.webkitdirectory = true
this.directory = true
input.onclick = null
input.click()
input.onclick = (e) => {
this.directory = false
input.directory = false
input.webkitdirectory = false
}
},
onAddData() {
this.addData.show = false
if (!this.$refs.upload.features.html5) {
this.alert('Your browser does not support')
return
}
let file = new window.File([this.addData.content], this.addData.name, {
type: this.addData.type,
})
this.$refs.upload.add(file)
}
}
}
</script>

@ -0,0 +1,7 @@
<template>
<div class="example-multiple">
No content
</div>
</template>
<style>
</style>

@ -0,0 +1,7 @@
<template>
<div class="example-simple">
No content
</div>
</template>
<style>
</style>

@ -0,0 +1,7 @@
<template>
<div class="example-vuex">
No content
</div>
</template>
<style>
</style>

File diff suppressed because it is too large Load Diff

@ -1,344 +0,0 @@
<style>
.home {
position: relative;
}
.file-uploads {
font-size: 18px;
padding: .6em;
font-weight: bold;
border: 1px solid #888;
background: #f3f3f3
}
.drop-active {
top: 0;
bottom: 0;
right: 0;
left: 0;
position: absolute;
opacity: .4;
background: #000;
}
button {
padding: .6em;
}
table {
margin-bottom: 2em
}
table th,table td {
padding: .4em;
border: 1px solid #ddd
}
</style>
<template>
<main class="home">
<div id="lists">
<table>
<thead>
<tr>
<th>Index</th>
<th>ID</th>
<th>Image</th>
<th>Name</th>
<th>Size</th>
<th>Progress</th>
<th>Speed</th>
<th>Active</th>
<th>Error</th>
<th>Success</th>
<th>Abort</th>
<th>customError</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
<tr v-for="(file, index) in files" :key="file.id">
<td>{{index}}</td>
<td>{{file.id}}</td>
<td v-if="file.type.substr(0, 6) == 'image/' && file.blob">
<img :src="file.blob" width="50" height="auto" />
</td>
<td v-else></td>
<td>{{file.name}}</td>
<td>{{file.size | formatSize}}</td>
<td>{{file.progress}}</td>
<td>{{file.speed | formatSize}}</td>
<td>{{file.active}}</td>
<td>{{file.error}}</td>
<td>{{file.success}}</td>
<td><button type="button" @click.prevent="abort(file)">Abort</button></td>
<td><button type="button" @click.prevent="customError(file)">custom error</button></td>
<td><button type="button" @click.prevent="remove(file)">x</button></td>
</tr>
</tbody>
</table>
</div>
<div id="options">
<table>
<tbody>
<tr>
<td>
<file-upload
:post-action="postAction"
:put-action="putAction"
:extensions="extensions"
:accept="accept"
:multiple="multiple"
:directory="directory"
:size="size || 0"
:thread="thread < 1 ? 1 : (thread > 5 ? 5 : thread)"
:headers="headers"
:data="data"
:drop="drop"
:drop-directory="dropDirectory"
v-model="files"
@input-filter="inputFilter"
@input-file="inputFile"
ref="upload">
Add upload files
</file-upload>
</td>
<td>
<button @click.prevent="addDirectory">Add upload directory</button>
<br/>
<span v-show="$refs.upload && !$refs.upload.features.directory">Your browser does not support</span>
</td>
<td>
<button v-show="!$refs.upload || !$refs.upload.active" @click.prevent="$refs.upload.active = true" type="button">Start upload</button>
<button v-show="$refs.upload && $refs.upload.active" @click.prevent="$refs.upload.active = false" type="button">Stop upload</button>
</td>
<td>
<label>
Auto start: <input type="checkbox" id="checkbox" v-model="auto">
</label>
</td>
<td>
<label>
Accept: <input type="text" v-model="accept">
</label>
</td>
<td>
<label>
Extensions: <input type="text" v-model="extensions">
</label>
</td>
<td>
<label>
Drop: <input type="checkbox" id="checkbox" v-model="drop">
</label>
</td>
<td>
<label>
Max file size: <input type="text" v-model.number="size">
</label>
</td>
<td>
<label>
Multiple: <input type="checkbox" id="checkbox" v-model="multiple">
</label>
</td>
<td>
<label>
Thread: <input type="text" v-model.number="thread">
</label>
</td>
</tr>
<tr>
<td>
Auto start: {{auto}}
</td>
<td>
Active: {{$refs.upload ? $refs.upload.active : false}}
</td>
<td>
Uploaded: {{$refs.upload ? $refs.upload.uploaded : true}}
</td>
<td>
Drop active: {{$refs.upload ? $refs.upload.dropActive : false}}
</td>
<td>
<label :for="name">Click</label>
</td>
</tr>
</tbody>
</table>
</div>
<h1>Allow to drag and drop</h1>
<div v-show="$refs.upload && $refs.upload.dropActive" class="drop-active">
Drop ing
</div>
<br/>
<br/>
<button type="button" @click.prevent="files = []">Test overwrite files</button>
</main>
</template>
<script>
import FileUpload from '../src'
export default {
components: {
FileUpload,
},
data() {
return {
files: [],
accept: 'image/png,image/gif,image/jpeg,image/webp',
size: 1024 * 1024 * 10,
extensions: 'gif,jpg,jpeg,png,webp',
// extensions: ['gif', 'jpg', 'jpeg','png', 'webp'],
// extensions: /\.(gif|jpe?g|png|webp)$/i,
multiple: true,
directory: false,
drop: true,
dropDirectory: true,
thread: 3,
name: 'file',
postAction: './post.php',
putAction: './put.php',
headers: {
"X-Csrf-Token": "xxxx",
},
data: {
"_csrf_token": "xxxxxx",
},
auto: false,
// headers: {},
// putAction: '',
// postAction: 'http://upload.qiniu.com/',
// accept: 'image/png,image/gif,image/jpeg,image/webp',
// extensions: '',
// size: 0,
}
},
watch: {
auto(auto) {
if (auto && !this.$refs.upload.uploaded && !this.$refs.upload.active) {
this.$refs.upload.active = true
}
}
},
methods: {
// add Directory
addDirectory() {
if (!this.$refs.upload.features.directory) {
return
}
let input = this.$refs.upload.$el.querySelector('input')
input.directory = true
input.webkitdirectory = true
this.directory = true
input.onclick = null
input.click()
input.onclick = (e) => {
this.directory = false
input.directory = false
input.webkitdirectory = false
}
},
// event Filter
inputFilter(newFile, oldFile, prevent) {
if (newFile && !oldFile) {
// or
if (/(\/|^)(Thumbs\.db|desktop\.ini|\..+)$/.test(newFile.name)) {
return prevent()
}
// php html js
if (/\.(php5?|html?|jsx?)$/i.test(newFile.name)) {
return prevent()
}
// blob
newFile.blob = ''
var URL = window.URL || window.webkitURL
if (URL && URL.createObjectURL) {
newFile.blob = URL.createObjectURL(newFile.file)
}
}
},
// add, update, remove File Event
inputFile(newFile, oldFile) {
if (newFile && oldFile) {
// console.log('update', newFile, oldFile)
if (newFile.active && !oldFile.active) {
// this.beforeSend(newFile)
// min size
if (newFile.size >= 0 && newFile.size < 100 * 1024) {
// newFile = this.$refs.upload.update(newFile, {error: 'size'})
}
}
if (newFile.progress != oldFile.progress) {
// this.progress(newFile)
// console.log('progress', newFile.progress)
}
if (newFile.error && !oldFile.error) {
// this.error(newFile)
// console.log('error', newFile.error, newFile.response)
}
if (newFile.success && !oldFile.success) {
// this.success(newFile)
// console.log('success', newFile.response)
}
}
if (!newFile && oldFile) {
// this.remove(oldFile)
// console.log('remove', oldFile)
}
//
if (newFile && !oldFile && this.auto && !this.$refs.upload.active) {
this.$refs.upload.active = true
}
},
abort(file) {
this.$refs.upload.update(file, {active: false})
// or
// this.$refs.upload.update(file, {error: 'abort'})
},
customError(file) {
this.$refs.upload.update(file, {error: 'custom'})
},
remove(file) {
this.$refs.upload.remove(file)
},
},
}
</script>

@ -1,32 +0,0 @@
import Vue from 'vue'
import Home from './Home'
import store from './store'
Vue.config.silent = false;
Vue.config.devtools = true;
Vue.filter('formatSize', function(size) {
if (size > 1024 * 1024 * 1024 * 1024) {
return (size / 1024 / 1024 / 1024 / 1024).toFixed(2) + ' TB';
} else if (size > 1024 * 1024 * 1024) {
return (size / 1024 / 1024 / 1024).toFixed(2) + ' GB';
} else if (size > 1024 * 1024) {
return (size / 1024 / 1024).toFixed(2) + ' MB';
} else if (size > 1024) {
return (size / 1024).toFixed(2) + ' KB';
}
return size.toString() + ' B';
});
new Vue({
store,
...Home
}).$mount('#app')

@ -2,13 +2,23 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>vue-upload-component</title> <title>vue-upload-component- Upload Component - Uploader</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0-beta/dist/css/bootstrap.min.css" rel="stylesheet" />
<link href="https://cdn.jsdelivr.net/npm/font-awesome/css/font-awesome.min.css" rel="stylesheet" />
<link href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release/build/styles/atom-one-light.min.css" rel="stylesheet" />
<link href="https://cdn.jsdelivr.net/npm/cropperjs/dist/cropper.css" rel="stylesheet" />
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>
<script src="https://unpkg.com/es6-promise/dist/es6-promise.auto.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/es6-promise/dist/es6-promise.auto.min.js"></script>
<script src="https://unpkg.com/vue"></script> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vuex"></script> <script src="https://cdn.jsdelivr.net/npm/vuex/dist/vuex.js"></script>
<script src="./dist/example.js"></script> <script src="https://cdn.jsdelivr.net/npm/vue-router/dist/vue-router.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue-i18n/dist/vue-i18n.js"></script>
<script src="https://cdn.jsdelivr.net/npm/marked/lib/marked.js"></script>
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release/build/highlight.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/cropperjs/dist/cropper.js"></script>
<script src="./docs/dist/index.js"></script>
</body> </body>
</html> </html>

@ -1,30 +1,38 @@
{ {
"name": "vue-upload-component", "name": "vue-upload-component",
"description": "Vue.js file upload component, Multi-file upload, Upload directory, Drag upload, Drag the directory, Upload multiple files at the same time, html4 (IE 9), `PUT` method, Customize the filter", "description": "Vue.js file upload component, Multi-file upload, Upload directory, Drag upload, Drag the directory, Upload multiple files at the same time, html4 (IE 9), `PUT` method, Customize the filter",
"version": "2.5.1-beta.3", "version": "2.6.0-beta.1",
"author": "LianYue", "author": "LianYue",
"scripts": { "scripts": {
"dev": "webpack-dev-server --inline --hot", "dev": "cross-env NODE_ENV=development webpack-dev-server",
"build": "cross-env NODE_ENV=production webpack --progress --hide-modules --config webpack.config.build.min.js && cross-env NODE_ENV=production webpack --progress --hide-modules --config webpack.config.build.js && cross-env NODE_ENV=production webpack --progress --hide-modules --config webpack.config.js" "build": "npm run build-webpack && npm run build-rollup",
"build-webpack": "cross-env NODE_ENV=production webpack --progress --hide-modules",
"build-rollup": "cross-env NODE_ENV=production rollup --config"
}, },
"main": "dist/vue-upload-component.js", "main": "dist/vue-upload-component.js",
"module": "dist/vue-upload-component.js",
"unpkg": "dist/vue-upload-component.js", "unpkg": "dist/vue-upload-component.js",
"jsdelivr": "dist/vue-upload-component.js",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/lian-yue/vue-upload-component.git" "url": "git+https://github.com/lian-yue/vue-upload-component.git"
}, },
"keywords": [ "keywords": [
"javascript",
"vue",
"vue.js", "vue.js",
"component",
"file", "file",
"put", "uploader",
"upload", "directory",
"uploads",
"multiple", "multiple",
"component", "upload",
"upload-directory",
"drag",
"drag-directory",
"upload-directory",
"vue-component", "vue-component",
"vue-upload-component", "vue-upload-component"
"vue-file-upload",
"vue-file-upload-component"
], ],
"license": "Apache-2.0", "license": "Apache-2.0",
"bugs": { "bugs": {
@ -33,22 +41,33 @@
"homepage": "https://github.com/lian-yue/vue-upload-component#readme", "homepage": "https://github.com/lian-yue/vue-upload-component#readme",
"dependencies": {}, "dependencies": {},
"devDependencies": { "devDependencies": {
"babel-core": "^6.0.0", "babel-core": "^6.26.0",
"babel-loader": "^6.0.0", "babel-eslint": "^8.0.1",
"babel-plugin-transform-runtime": "^6.0.0", "babel-loader": "^7.1.2",
"babel-preset-es2015": "^6.0.0", "babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-stage-0": "^6.5.0", "babel-preset-env": "^1.6.0",
"babel-runtime": "^6.0.0", "babel-preset-es2015": "^6.24.1",
"babel-preset-es2017": "^6.24.1",
"babel-preset-stage-0": "^6.24.1",
"babel-runtime": "^6.26.0",
"cross-env": "^1.0.6", "cross-env": "^1.0.6",
"css-loader": "^0.23.0", "css-loader": "^0.28.7",
"expose-loader": "^0.7.1", "eslint": "^4.8.0",
"file-loader": "^0.8.4", "eslint-loader": "^1.9.0",
"json-loader": "^0.5.4", "eslint-plugin-html": "^3.2.2",
"url-loader": "^0.5.7", "eslint-plugin-vue": "^3.13.0",
"vue-hot-reload-api": "^1.3.3", "raw-loader": "^0.5.1",
"vue-loader": "^11.1.4", "rollup": "^0.50.0",
"vue-template-compiler": "^2.2.6", "rollup-plugin-babel": "^3.0.2",
"webpack": "^1.12.2", "rollup-plugin-commonjs": "^8.2.1",
"webpack-dev-server": "^1.12.0" "rollup-plugin-node-resolve": "^3.0.0",
"rollup-plugin-uglify": "^2.0.1",
"rollup-plugin-vue": "^2.5.2",
"vue-hot-reload-api": "^2.1.1",
"vue-loader": "^13.0.5",
"vue-template-compiler": "^2.4.4",
"webpack": "^3.6.0",
"webpack-dev-server": "^2.9.1",
"webpack-merge": "^4.1.0"
} }
} }

@ -0,0 +1,85 @@
import resolve from 'rollup-plugin-node-resolve'
import commonjs from 'rollup-plugin-commonjs'
import babel from 'rollup-plugin-babel'
import uglify from 'rollup-plugin-uglify'
import vue from 'rollup-plugin-vue'
import packageInfo from './package.json'
// const isDev = process.env.NODE_ENV === 'development'
function baseConfig() {
return {
output: {
format: 'umd',
},
plugins: [
resolve({
jsnext: true,
main: true,
browser: true,
}),
commonjs({
extensions: [
'.js',
'.jsx',
'.json',
'.vue'
],
}),
],
banner: `/*!\n * Name: ${packageInfo.name}\n * Version: ${packageInfo.version}\n * Author: ${packageInfo.author}\n */`,
sourcemap: true,
}
}
let config = baseConfig()
config.input = 'src/index.js'
config.output.file = 'dist/vue-upload-component.js'
config.output.name = 'VueUploadComponent'
config.plugins.push(
vue({
autoStyles: false,
css: true,
}),
babel()
)
let configMin = baseConfig()
configMin.input = 'src/index.js'
configMin.output.file = 'dist/vue-upload-component.min.js'
configMin.output.name = 'VueUploadComponent'
configMin.plugins.push(
vue({
autoStyles: false,
css: true,
}),
babel(),
uglify({
output: {
comments: /^!/,
}
})
)
let configPart = baseConfig()
configPart.input = 'src/index.js'
configPart.output.file = 'dist/vue-upload-component.part.js'
configPart.output.name = 'VueUploadComponent'
configPart.plugins.push(
vue({
autoStyles: false,
css: 'dist/vue-upload-component.part.css',
}),
babel()
)
module.exports = [
config,
configMin,
configPart,
]

@ -1,7 +1,7 @@
<template> <template>
<label :class="className"> <label :class="className">
<input-file></input-file>
<slot></slot> <slot></slot>
<input-file></input-file>
</label> </label>
</template> </template>
<style> <style>
@ -160,7 +160,7 @@ export default {
// files // files
this.maps = {} this.maps = {}
this.$nextTick(function() { this.$nextTick(function () {
// //
if (this.$parent) { if (this.$parent) {
@ -189,7 +189,8 @@ export default {
* uploading 正在上传的线程 * uploading 正在上传的线程
* @return {[type]} [description] * @return {[type]} [description]
*/ */
/*uploading() { /*
uploading() {
let uploading = 0 let uploading = 0
for (var i = 0; i < this.files.length; i++) { for (var i = 0; i < this.files.length; i++) {
if (this.files[i].active) { if (this.files[i].active) {
@ -197,7 +198,8 @@ export default {
} }
} }
return uploading return uploading
},*/ },
*/
/** /**
* uploaded 文件列表是否全部已上传 * uploaded 文件列表是否全部已上传
* @return {[type]} [description] * @return {[type]} [description]
@ -206,7 +208,7 @@ export default {
let file let file
for (let i = 0; i < this.files.length; i++) { for (let i = 0; i < this.files.length; i++) {
file = this.files[i] file = this.files[i]
if (!file.error && !file.success) { if (file.fileObject && !file.error && !file.success) {
return false return false
} }
} }
@ -244,11 +246,8 @@ export default {
if (this.files === files) { if (this.files === files) {
return return
} }
let oldFiles = this.files
this.files = files this.files = files
let oldMaps = this.maps let oldMaps = this.maps
// maps // maps
@ -303,14 +302,15 @@ export default {
} }
if (typeof id === 'object') { if (typeof id === 'object') {
id = id.id return this.maps[id.id] || false
} }
return this.maps[id] || false return this.maps[id] || false
}, },
// //
add(files, start) { add(_files, start) {
let files = _files
let isArray = files instanceof Array let isArray = files instanceof Array
// //
@ -326,40 +326,53 @@ export default {
file = { file = {
file, file,
size: file.size, size: file.size,
name: file.webkitRelativePath || file.relativePath || file.name, name: file.webkitRelativePath || file.relativePath || file.name || 'unknown',
type: file.type, type: file.type,
} }
} }
file = { let fileObject = false
size: -1, if (file.fileObject === false) {
name: 'Filename', // false
type: '', } else if (file.fileObject) {
active: false, fileObject = true
error: '', } else if (typeof Element !== 'undefined' && file.el instanceof Element) {
success: false, fileObject = true
putAction: this.putAction, } else if (typeof File !== 'undefined' && file.file instanceof File) {
postAction: this.postAction, fileObject = true
timeout: this.timeout,
...file,
response: {},
progress: '0.00', //
speed: 0, //
// xhr: false, //
// iframe: false, //
} }
if (fileObject) {
file = {
fileObject: true,
size: -1,
name: 'Filename',
type: '',
active: false,
error: '',
success: false,
putAction: this.putAction,
postAction: this.postAction,
timeout: this.timeout,
...file,
response: {},
progress: '0.00', //
speed: 0, //
// xhr: false, //
// iframe: false, //
}
file.data = { file.data = {
...this.data, ...this.data,
...file.data ? file.data : {}, ...file.data ? file.data : {},
} }
file.headers = { file.headers = {
...this.headers, ...this.headers,
...file.headers ? file.headers : {}, ...file.headers ? file.headers : {},
}
} }
// id
if (!file.id) { if (!file.id) {
file.id = Math.random().toString(36).substr(2) file.id = Math.random().toString(36).substr(2)
} }
@ -465,13 +478,13 @@ export default {
if (!item || (!this.multiple && files.length)) { if (!item || (!this.multiple && files.length)) {
return resolve(this.add(files)) return resolve(this.add(files))
} }
this.getEntry(item).then(function(results) { this.getEntry(item).then(function (results) {
files.push(...results) files.push(...results)
forEach(i + 1) forEach(i + 1)
}) })
} }
forEach(0) forEach(0)
}); })
} }
if (dataTransfer.files.length) { if (dataTransfer.files.length) {
@ -492,7 +505,7 @@ export default {
getEntry(entry, path = '') { getEntry(entry, path = '') {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (entry.isFile) { if (entry.isFile) {
entry.file(function(file) { entry.file(function (file) {
resolve([ resolve([
{ {
size: file.size, size: file.size,
@ -524,8 +537,8 @@ export default {
// //
remove(file) { remove(id) {
file = this.get(file) let file = this.get(id)
if (file) { if (file) {
if (this.emitFilter(undefined, file)) { if (this.emitFilter(undefined, file)) {
return false return false
@ -550,12 +563,15 @@ export default {
}, },
// //
update(file, data) { update(id, data) {
file = this.get(file) let file = this.get(id)
if (file) { if (file) {
let newFile = {...file, ...data} let newFile = {
...file,
...data
}
// //
if (file.active && !newFile.active && !newFile.error && !newFile.success) { if (file.fileObject && file.active && !newFile.active && !newFile.error && !newFile.success) {
newFile.error = 'abort' newFile.error = 'abort'
} }
@ -572,8 +588,9 @@ export default {
files.splice(index, 1, newFile) files.splice(index, 1, newFile)
this.files = files this.files = files
// // 便id)
this.maps[file.id] = newFile delete this.maps[file.id]
this.maps[newFile.id] = newFile
// //
this.emitInput() this.emitInput()
@ -587,7 +604,7 @@ export default {
// //
emitFilter(newFile, oldFile) { emitFilter(newFile, oldFile) {
let isPrevent = false let isPrevent = false
this.$emit('input-filter', newFile, oldFile, function() { this.$emit('input-filter', newFile, oldFile, function () {
isPrevent = true isPrevent = true
return isPrevent return isPrevent
}) })
@ -597,22 +614,30 @@ export default {
// //
emitFile(newFile, oldFile) { emitFile(newFile, oldFile) {
this.$emit('input-file', newFile, oldFile) this.$emit('input-file', newFile, oldFile)
if (newFile && newFile.active && (!oldFile || !oldFile.active)) { if (newFile && newFile.fileObject && newFile.active && (!oldFile || !oldFile.active)) {
this.uploading++ this.uploading++
// //
this.$nextTick(function() { this.$nextTick(function () {
setTimeout(() => { setTimeout(() => {
this.upload(newFile).then(() => { this.upload(newFile).then(() => {
// eslint-disable-next-line
newFile = this.get(newFile) newFile = this.get(newFile)
if (newFile) { if (newFile && newFile.fileObject) {
this.update(newFile, {active: false, success: !newFile.error}) this.update(newFile, {
active: false,
success: !newFile.error
})
} }
}).catch((e) => { }).catch((e) => {
this.update(newFile, {active: false, success: false, error: e.code || e.error || e.message || e}) this.update(newFile, {
active: false,
success: false,
error: e.code || e.error || e.message || e
})
}) })
}, parseInt(Math.random() * 50 + 50)) }, parseInt(Math.random() * 50 + 50, 10))
}) })
} else if ((!newFile || !newFile.active) && oldFile && oldFile.active) { } else if ((!newFile || !newFile.fileObject || !newFile.active) && oldFile && oldFile.fileObject && oldFile.active) {
// //
this.uploading-- this.uploading--
} }
@ -622,19 +647,26 @@ export default {
this.watchActive(true) this.watchActive(true)
} }
}, },
emitInput() { emitInput() {
this.$emit('input', this.files) this.$emit('input', this.files)
}, },
// //
upload(file) { upload(id) {
let file = this.get(id)
// //
if (!(file = this.get(file))) { if (!file) {
return Promise.reject('not_exists') return Promise.reject('not_exists')
} }
//
if (!file.fileObject) {
return Promise.reject('file_object')
}
// //
if (file.error) { if (file.error) {
return Promise.reject(file.error) return Promise.reject(file.error)
@ -646,13 +678,13 @@ export default {
} }
// //
var extensions = this.extensions let extensions = this.extensions
if (extensions && (extensions.length || typeof extensions.length == 'undefined')) { if (extensions && (extensions.length || typeof extensions.length === 'undefined')) {
if (typeof extensions != 'object' || !(extensions instanceof RegExp)) { if (typeof extensions !== 'object' || !(extensions instanceof RegExp)) {
if (typeof extensions == 'string') { if (typeof extensions === 'string') {
extensions = extensions.split(',').map(value => value.trim()).filter(value => value) extensions = extensions.split(',').map(value => value.trim()).filter(value => value)
} }
extensions = new RegExp('\\.('+ extensions.join('|').replace(/\./g, '\\.') +')$', 'i') extensions = new RegExp('\\.(' + extensions.join('|').replace(/\./g, '\\.') + ')$', 'i')
} }
if (file.name.search(extensions) === -1) { if (file.name.search(extensions) === -1) {
return Promise.reject('extension') return Promise.reject('extension')
@ -680,10 +712,10 @@ export default {
for (let key in file.data) { for (let key in file.data) {
value = file.data[key] value = file.data[key]
if (value !== null && value !== undefined) { if (value !== null && value !== undefined) {
querys.push(encodeURIComponent(key) + '=' + encodeURIComponent(value)) querys.push(encodeURIComponent(key) + '=' + encodeURIComponent(value))
} }
} }
let queryString = querys.length ? (file.putAction.indexOf('?') == -1 ? '?' : '&') + querys.join('&') : '' let queryString = querys.length ? (file.putAction.indexOf('?') === -1 ? '?' : '&') + querys.join('&') : ''
let xhr = new XMLHttpRequest() let xhr = new XMLHttpRequest()
xhr.open('PUT', file.putAction + queryString) xhr.open('PUT', file.putAction + queryString)
return this.uploadXhr(xhr, file, file.file) return this.uploadXhr(xhr, file, file.file)
@ -706,14 +738,16 @@ export default {
return this.uploadXhr(xhr, file, form) return this.uploadXhr(xhr, file, form)
}, },
uploadXhr(xhr, file, data) { uploadXhr(xhr, _file, body) {
let file = _file
let speedTime = 0 let speedTime = 0
let speedLoaded = 0 let speedLoaded = 0
// //
xhr.upload.onprogress = (e) => { xhr.upload.onprogress = (e) => {
// //
if (!e.lengthComputable || !(file = this.get(file)) || !file.active) { file = this.get(file)
if (!e.lengthComputable || !file || !file.fileObject || !file.active) {
return return
} }
@ -734,7 +768,7 @@ export default {
// //
let interval = setInterval(() => { let interval = setInterval(() => {
file = this.get(file) file = this.get(file)
if (file && !file.success && !file.error && file.active) { if (file && file.fileObject && !file.success && !file.error && file.active) {
return return
} }
@ -745,7 +779,7 @@ export default {
try { try {
xhr.abort() xhr.abort()
xhr.timeout =1 xhr.timeout = 1
} catch (e) { } catch (e) {
} }
}, 100) }, 100)
@ -770,6 +804,11 @@ export default {
return reject('not_exists') return reject('not_exists')
} }
//
if (!file.fileObject) {
return reject('file_object')
}
// //
if (file.error) { if (file.error) {
return reject(file.error) return reject(file.error)
@ -796,14 +835,14 @@ export default {
case 'error': case 'error':
if (!xhr.status) { if (!xhr.status) {
data.error = 'network' data.error = 'network'
} else if(xhr.status >= 500) { } else if (xhr.status >= 500) {
data.error = 'server' data.error = 'server'
} else if (xhr.status >= 400) { } else if (xhr.status >= 400) {
data.error = 'denied' data.error = 'denied'
} }
break break
default: default:
if(xhr.status >= 500) { if (xhr.status >= 500) {
data.error = 'server' data.error = 'server'
} else if (xhr.status >= 400) { } else if (xhr.status >= 400) {
data.error = 'denied' data.error = 'denied'
@ -813,8 +852,8 @@ export default {
} }
if (xhr.responseText) { if (xhr.responseText) {
var contentType = xhr.getResponseHeader('Content-Type') let contentType = xhr.getResponseHeader('Content-Type')
if (contentType && contentType.indexOf('/json') != -1) { if (contentType && contentType.indexOf('/json') !== -1) {
data.response = JSON.parse(xhr.responseText) data.response = JSON.parse(xhr.responseText)
} else { } else {
data.response = xhr.responseText data.response = xhr.responseText
@ -850,31 +889,32 @@ export default {
} }
// xhr // xhr
file = this.update(file, {xhr}) file = this.update(file, { xhr })
// //
xhr.send(data) xhr.send(body)
}) })
}, },
uploadHtml4(file) { uploadHtml4(_file) {
var onKeydown = function(e) { let file = _file
if (e.keyCode == 27) { let onKeydown = function (e) {
if (e.keyCode === 27) {
e.preventDefault() e.preventDefault()
} }
} }
var iframe = document.createElement('iframe') let iframe = document.createElement('iframe')
iframe.id = 'upload-iframe-' + file.id iframe.id = 'upload-iframe-' + file.id
iframe.name = 'upload-iframe-' + file.id iframe.name = 'upload-iframe-' + file.id
iframe.src = 'about:blank' iframe.src = 'about:blank'
iframe.setAttribute('style', 'width:1px;height:1px;top:-999em;position:absolute; margin-top:-999em;') iframe.setAttribute('style', 'width:1px;height:1px;top:-999em;position:absolute; margin-top:-999em;')
var form = document.createElement('form') let form = document.createElement('form')
form.action = file.postAction form.action = file.postAction
@ -885,17 +925,17 @@ export default {
form.setAttribute('target', 'upload-iframe-' + file.id) form.setAttribute('target', 'upload-iframe-' + file.id)
form.setAttribute('enctype', 'multipart/form-data') form.setAttribute('enctype', 'multipart/form-data')
var value let value
var input let input
for (var key in file.data) { for (let key in file.data) {
value = file.data[key] value = file.data[key]
if (value && typeof value == 'object' && typeof value.toString != 'function') { if (value && typeof value === 'object' && typeof value.toString !== 'function') {
value = JSON.stringify(value) value = JSON.stringify(value)
} }
if (value !== null && value !== undefined) { if (value !== null && value !== undefined) {
input = document.createElement('input') input = document.createElement('input')
input.type = 'hidden' input.type = 'hidden'
input.name = key input.name = key
form.appendChild(input) form.appendChild(input)
} }
} }
@ -906,18 +946,18 @@ export default {
var getResponseData = function() { let getResponseData = function () {
let doc let doc
try { try {
if (iframe.contentWindow) { if (iframe.contentWindow) {
doc = iframe.contentWindow.document doc = iframe.contentWindow.document
} }
} catch(err) { } catch (err) {
} }
if (!doc) { if (!doc) {
try { try {
doc = iframe.contentDocument ? iframe.contentDocument : iframe.document doc = iframe.contentDocument ? iframe.contentDocument : iframe.document
} catch(err) { } catch (err) {
doc = iframe.document doc = iframe.document
} }
} }
@ -930,31 +970,31 @@ export default {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
setTimeout(() => { setTimeout(() => {
file = this.update(file, { iframe })
// //
if (!(file = this.update(file, {iframe}))) { if (!file) {
return reject('not_exists') return reject('not_exists')
} }
// //
var interval = setInterval(() => { let interval = setInterval(() => {
file = this.get(file) file = this.get(file)
if (file && !file.success && !file.error && file.active) { if (file && file.fileObject && !file.success && !file.error && file.active) {
return return
} }
if (interval) { if (interval) {
clearInterval(interval) clearInterval(interval)
interval = false interval = false
} }
if (!file || file.error) { iframe.onabort({ type: file ? 'abort' : 'not_exists' })
iframe.onabort({type: file ? 'abort' : 'not_exists'})
}
}, 100) }, 100)
var complete let complete
var fn = (e) => { let fn = (e) => {
// //
if (complete) { if (complete) {
return return
@ -977,6 +1017,11 @@ export default {
return reject('not_exists') return reject('not_exists')
} }
//
if (!file.fileObject) {
return reject('file_object')
}
// //
if (file.error) { if (file.error) {
return reject(file.error) return reject(file.error)
@ -992,8 +1037,8 @@ export default {
return resolve(file) return resolve(file)
} }
var response = getResponseData() let response = getResponseData()
var data = {} let data = {}
switch (e.type) { switch (e.type) {
case 'abort': case 'abort':
data.error = 'abort' data.error = 'abort'
@ -1018,7 +1063,7 @@ export default {
} }
if (response !== null) { if (response !== null) {
if (response && response.substr(0, 1) == '{' && response.substr(response.length - 1, 1) == '}') { if (response && response.substr(0, 1) === '{' && response.substr(response.length - 1, 1) === '}') {
try { try {
response = JSON.parse(response) response = JSON.parse(response)
} catch (err) { } catch (err) {
@ -1039,8 +1084,6 @@ export default {
} }
// //
iframe.onload = fn iframe.onload = fn
iframe.onerror = fn iframe.onerror = fn
@ -1052,13 +1095,11 @@ export default {
// //
form.submit() form.submit()
}, 50) }, 50)
}).then(function(res) { }).then(function (res) {
iframe.parentNode && iframe.parentNode.removeChild(iframe) iframe.parentNode && iframe.parentNode.removeChild(iframe)
return res return res
}).catch(function(res) { }).catch(function (res) {
iframe.parentNode && iframe.parentNode.removeChild(iframe) iframe.parentNode && iframe.parentNode.removeChild(iframe)
return res return res
}) })
@ -1067,20 +1108,22 @@ export default {
watchActive(active) { watchActive(active) {
var file let file
var index = 0 let index = 0
while (file = this.files[index]) { while ((file = this.files[index])) {
index++ index++
if (active && !this.destroy) { if (!file.fileObject) {
//
} else if (active && !this.destroy) {
if (this.uploading >= this.thread || (this.uploading && !this.features.html5)) { if (this.uploading >= this.thread || (this.uploading && !this.features.html5)) {
break break
} }
if (!file.active && !file.error && !file.success) { if (!file.active && !file.error && !file.success) {
this.update(file, {active: true}) this.update(file, { active: true })
} }
} else { } else {
if (file.active) { if (file.active) {
this.update(file, {active: false}) this.update(file, { active: false })
} }
} }
} }
@ -1090,7 +1133,8 @@ export default {
}, },
watchDrop(el) { watchDrop(_el) {
let el = _el
if (!this.features.drop) { if (!this.features.drop) {
return return
} }
@ -1098,8 +1142,8 @@ export default {
// //
if (this.dropElement) { if (this.dropElement) {
try { try {
window.document.removeEventListener('dragenter', this.onDragenter, false) document.removeEventListener('dragenter', this.onDragenter, false)
window.document.removeEventListener('dragleave', this.onDragleave, false) document.removeEventListener('dragleave', this.onDragleave, false)
this.dropElement.removeEventListener('dragover', this.onDragover, false) this.dropElement.removeEventListener('dragover', this.onDragover, false)
this.dropElement.removeEventListener('drop', this.onDrop, false) this.dropElement.removeEventListener('drop', this.onDrop, false)
} catch (e) { } catch (e) {
@ -1108,7 +1152,7 @@ export default {
if (!el) { if (!el) {
el = false el = false
} else if (typeof el == 'string') { } else if (typeof el === 'string') {
el = document.querySelector(el) || this.$root.$el.querySelector(el) el = document.querySelector(el) || this.$root.$el.querySelector(el)
} else if (el === true) { } else if (el === true) {
el = this.$parent.$el el = this.$parent.$el
@ -1117,8 +1161,8 @@ export default {
this.dropElement = el this.dropElement = el
if (this.dropElement) { if (this.dropElement) {
window.document.addEventListener('dragenter', this.onDragenter, false) document.addEventListener('dragenter', this.onDragenter, false)
window.document.addEventListener('dragleave', this.onDragleave, false) document.addEventListener('dragleave', this.onDragleave, false)
this.dropElement.addEventListener('dragover', this.onDragover, false) this.dropElement.addEventListener('dragover', this.onDragover, false)
this.dropElement.addEventListener('drop', this.onDrop, false) this.dropElement.addEventListener('drop', this.onDrop, false)
} }
@ -1134,7 +1178,7 @@ export default {
onDragleave(e) { onDragleave(e) {
e.preventDefault() e.preventDefault()
if (e.target.nodeName === 'HTML' || (e.screenX == 0 && e.screenY == 0 && e.screenY == 0 && !e.fromElement && e.offsetX < 0)) { if (e.target.nodeName === 'HTML' || (e.screenX === 0 && e.screenY === 0 && e.screenY === 0 && !e.fromElement && e.offsetX < 0)) {
this.dropActive = false this.dropActive = false
} }
}, },

@ -7,7 +7,6 @@
@change="change" @change="change"
:webkitdirectory="$parent.directory && $parent.features.directory" :webkitdirectory="$parent.directory && $parent.features.directory"
:directory="$parent.directory && $parent.features.directory" :directory="$parent.directory && $parent.features.directory"
:allowdir="$parent.directory && $parent.features.directory"
:multiple="$parent.multiple && $parent.features.html5" :multiple="$parent.multiple && $parent.features.html5"
/> />
</template> </template>
@ -17,6 +16,7 @@ export default {
change(e) { change(e) {
this.$destroy() this.$destroy()
this.$parent.addInputFile(e.target) this.$parent.addInputFile(e.target)
// eslint-disable-next-line
new this.constructor({ new this.constructor({
parent: this.$parent, parent: this.$parent,
el: this.$el, el: this.$el,

@ -1 +1 @@
module.exports = require('./FileUpload.vue'); module.exports = require('./FileUpload.vue')

@ -1,12 +0,0 @@
var webpack = require('webpack');
module.exports = require('./webpack.config.js');
module.exports.entry = {
'vue-upload-component': './src',
}
module.exports.output.library = 'VueUploadComponent';
module.exports.output.libraryTarget = 'umd';

@ -1,11 +0,0 @@
var webpack = require('webpack');
module.exports = require('./webpack.config.build.js');
module.exports.output.filename = "[name].min.js";
module.exports.plugins.push(new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false,
}
}));

@ -1,82 +1,195 @@
var path = require('path') const path = require('path')
var webpack = require('webpack') const merge = require('webpack-merge')
const webpack = require('webpack')
module.exports = { const packageInfo = require('./package')
entry: {
example: ['./example'],
},
output: { process.env.NODE_ENV = process.env.NODE_ENV || 'production'
path: './dist',
publicPath: '/dist/',
filename: "[name].js",
// target: 'node',
},
const isDev = process.env.NODE_ENV === 'development'
resolve: {
root: path.join(__dirname, 'node_modules'),
extensions: ['', '.js', '.vue', '.json'],
},
externals: { function baseConfig() {
vue: 'Vue', let config = {
vuex: 'Vuex', output: {
}, path: path.join(__dirname, 'dist'),
publicPath: '/dist',
filename: '[name].js',
chunkFilename: '[chunkhash:8].[name].chunk.js',
},
module: { resolve: {
loaders: [ modules: [
{ path.join(__dirname, 'node_modules'),
test: /\.vue$/, ],
loader: 'vue'
}, alias: {
{ 'vue-upload-component': path.join(__dirname, 'src'),
test: /\.js$/,
loader: 'babel',
exclude: /node_modules/
},
{
test: /\.json$/,
loader: 'json'
},
{
test: /\.html$/,
loader: 'vue-html'
}, },
{
test: /\.(png|jpg|gif|svg)$/, extensions: [
loader: 'url', '.js',
query: { '.jsx',
limit: 10000, '.json',
name: '[name].[ext]?[hash]' '.vue',
'.md',
],
},
externals: {
vue: 'Vue',
vuex: 'Vuex',
'vue-router': 'VueRouter',
'vue-i18n': 'VueI18n',
'marked': 'marked',
'highlight.js': 'hljs',
'cropperjs': 'Cropper',
},
module: {
rules: [
{
test: /\.jsx?$/,
use: [
{
loader: 'babel-loader',
options: {
presets: [
[
'env',
{
modules: false
}
],
'stage-0',
],
plugins: [
[
'transform-runtime',
{
helpers: false,
polyfill: false,
regenerator: true,
moduleName: 'babel-runtime'
}
]
],
cacheDirectory: isDev
},
},
{
loader: 'eslint-loader',
},
],
},
{
test: /\.(md|txt)$/,
use: [
{
loader: 'raw-loader',
},
]
},
{
test: /\.vue$/,
use: [
{
loader: 'vue-loader',
options: {
preserveWhitespace: false,
loaders: {
js: [
{
loader: 'babel-loader',
options: {
presets: [
[
'env',
{
modules: false
}
],
'stage-0'
],
cacheDirectory: isDev
}
},
],
}
},
},
{
loader: 'eslint-loader',
},
]
} }
} ]
] },
plugins: [
new webpack.BannerPlugin(`Name: ${packageInfo.name}\nVersion: ${packageInfo.version}\nAuthor: ${packageInfo.author}`),
],
devtool: isDev ? 'eval-source-map' : 'source-map'
}
if (isDev) {
config.plugins.push(new webpack.HotModuleReplacementPlugin())
}
return config
}
module.exports = merge(baseConfig(), {
entry: {
index: [
path.join(__dirname, 'docs/index.js'),
],
}, },
babel: { output: {
presets: ['es2015', 'stage-0'], path: path.join(__dirname, 'docs/dist'),
plugins: ['transform-runtime'],
}, },
devServer: { devServer: {
// host: '172.16.23.1', before(app) {
let id = 1000000
let put = function (req, res) {
setTimeout(function () {
let rand = Math.random()
if (rand <= 0.1) {
res.status(500)
res.json({ error: 'server', success: false })
} else if (rand <= 0.25) {
res.status(403)
res.json({ error: 'failure', success: false })
} else {
res.json({ name: 'filename.ext', id: id++, success: true })
}
}, 200 + parseInt(Math.random() * 4000, 10))
}
let del = function (req, res) {
res.json({ success: true })
}
app.post('/upload/post', put)
app.put('/upload/put', put)
app.post('/upload/delete', del)
app.delete('/upload/delete', del)
},
hot: true,
contentBase: path.join(__dirname, 'docs'),
clientLogLevel: 'error',
noInfo: true,
publicPath: '/dist',
inline: true,
historyApiFallback: true, historyApiFallback: true,
noInfo: true overlay: {
warnings: true,
errors: true
},
// host: '172.16.23.1'
}, },
devtool: 'eval-source-map' })
}
if (process.env.NODE_ENV === 'production') {
module.exports.devtool = 'source-map'
// http://vuejs.github.io/vue-loader/workflow/production.html
module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new webpack.optimize.OccurenceOrderPlugin()
])
}

Loading…
Cancel
Save