Compare commits

..

221 Commits
1.0 ... master

Author SHA1 Message Date
lianyue 209a5725ba fix #294
7 years ago
lianyue cb7a75e4ad build
7 years ago
lianyue 1f440b74e1 fix #283
7 years ago
lianyue 0d307c175a build
7 years ago
lianyue 8c2eba8d9a Merge branch 'master' of github.com:lian-yue/vue-upload-component
7 years ago
lianyue 5a7201e3d7 build
7 years ago
lianyue 60d1064205 fix #264
7 years ago
LianYue 2d946569d1
Merge pull request #204 from ahDDD/dev
7 years ago
LianYue 53db208074
Merge pull request #261 from bieli/update-docs-for-chunked-upload
7 years ago
lianyue 6e44782f93 build
7 years ago
lianyue 7e8f23204f fix
7 years ago
lianyue 454109129d build
7 years ago
lianyue aad7d18ab3 Merge branch 'master' of github.com:lian-yue/vue-upload-component
7 years ago
lianyue 3145c88d68 build
7 years ago
lianyue b6e5bc3919 fix #257
7 years ago
Marcin Bielak fde412ee1a adding real examples intot docs with HTTP examples for chunked upload
7 years ago
LianYue 8ad138d108
Merge pull request #252 from getsetbro/patch-1
7 years ago
getsetbro 098cc958ee
Update Drag.vue
7 years ago
lianyue 4b58d015e8 build
7 years ago
lianyue c687bb14d8 add prop disabled #245
7 years ago
lianyue 48a3a876b8 build
7 years ago
lianyue 6bbd4deb73 fix #236
7 years ago
lianyue f82e00393d build
7 years ago
lianyue 229c5830f4 remove config host 0.0.0.0
7 years ago
lianyue a969e16bc0 fix #219
7 years ago
lianyue ba5274c0a2 build
7 years ago
lianyue 0850fc0411 fix ie dragleave
7 years ago
lianyue b39e76d0f3 build
7 years ago
lianyue d75ffa6369 fix #226
7 years ago
lianyue a194f9c9c8 fix #225 #210
7 years ago
lianyue 3940224a72 build
8 years ago
lianyue a0ceec86d9 fix #212
8 years ago
lianyue 47497baabe update package
8 years ago
lianyue 644872010b fix #213
8 years ago
d 8a8c8c1cae some docs supplements
8 years ago
lianyue f45a552a33 build
8 years ago
lianyue 9ae5172387 fix config sourcemap -> sourceMap
8 years ago
LianYue d1dc4baf80
Merge pull request #202 from ahDDD/master
8 years ago
d c507c32135 Merge remote-tracking branch 'origin/master' into dev
8 years ago
d 70264884fa fix a key error in docs
8 years ago
lianyue 6a7eac5c12 build
8 years ago
lianyue f4df58000f fix #196
8 years ago
lianyue 059ff1a15f Update devDependencies
8 years ago
LianYue 011d74de62
Merge pull request #195 from mikemcbride/fix-173
8 years ago
Mike McBride 49845c13bd fixes #173
8 years ago
lianyue 1a365fefdc Update version
8 years ago
LianYue fe9dedbff6
Merge pull request #192 from HKskn/master
8 years ago
Haluk Keskin ea3767df64 fixed ie11 upload error
8 years ago
lianyue 453a1a574d update version
8 years ago
LianYue 41562987af
Merge pull request #167 from DrSensor/patch-2
8 years ago
Fahmi Akbar Wildana 28c993544d
Fix missing declare and upload.file doesn't exist
8 years ago
LianYue 5740af9718 update version
8 years ago
LianYue 32d87b1527 Merge branch 'ts'
8 years ago
LianYue 560d99c8bc Merge branch 'patch-1' of https://github.com/DrSensor/vue-upload-component into ts
8 years ago
LianYue 43540ea35c confi file
8 years ago
Fahmi Akbar Wildana 1ea52e241f
Support `this.$refs.upload.___` using `as`
8 years ago
Fahmi Akbar Wildana 631ad4b16b
Make it import-able
8 years ago
Fahmi Akbar Wildana 38f3da2996
Add typescript support
8 years ago
git bd3a5072ae build
8 years ago
git c4f6a597db Merge branch 'chunk'
8 years ago
git a6bc6b58f3 Merge branch 'master' of https://github.com/coosto/vue-upload-component into chunk
8 years ago
Wesley 4d1a87991b
Merge pull request #3 from coosto/task/chunk-upload-functionality
8 years ago
José Cámara 8417e11b3a Chunk upload fix for `multipart/form-data` form request.
8 years ago
git bca5bab289 build
8 years ago
git 4e2530e492 Add props.capture
8 years ago
git e291993e44 build
8 years ago
git 29b541bfeb fix await
8 years ago
git 586c2bb72b add custom-action
8 years ago
git 3285dd30e6 add props.customAction
8 years ago
git 5a7a4fcea9 Merge branch 'chunk'
8 years ago
git 8e557d0670 Merge branch 'master' of https://github.com/coosto/vue-upload-component into chunk
8 years ago
Wesley a218cffefd
Merge pull request #2 from coosto/task/chunk-upload-functionality
8 years ago
José Cámara 78ca7c569f Merge branch 'master' into task/chunk-upload-functionality
8 years ago
José Cámara 699c7476bf * Fixing `Chunk upload` problems:
8 years ago
git ab1612d4cf merge chunk uploads
8 years ago
git 86b5b713e6 build
8 years ago
git 48ce45257d fix build
8 years ago
git c01a3abb92 Add Special thanks
8 years ago
José Cámara bdf52d66ed * Some minor changes in the documentation
8 years ago
José Cámara b81ab35d45 * Moved `bodyParser` require to the top of the document
8 years ago
José Cámara 54bf33da4a * Some changes in the documentation
8 years ago
José Cámara 902b3164f8 * Moved `chunkUpload` backend code to utils file
8 years ago
José Cámara 961cc35f23 * Changes in `FileUload` to use `chunk upload` in some cases
8 years ago
José Cámara 5604efe979 * Some minor changes in the documentation
8 years ago
José Cámara dfc1e65475 * Moved `bodyParser` require to the top of the document
8 years ago
José Cámara 28df5d3125 * Some changes in the documentation
8 years ago
José Cámara 2cfb93c04b * Moved `chunkUpload` backend code to utils file
8 years ago
José Cámara 872c984eb2 * Changes in `FileUload` to use `chunk upload` in some cases
8 years ago
git 7d88edc716 build
8 years ago
git 6af5af4af2 fix #129
8 years ago
git fa5fe89c13 build
8 years ago
git dacb0cfdc3 fix addDataTransfer getAsFile
8 years ago
git cbb8fa574d build
8 years ago
git 1d0e93a23e fix #120
8 years ago
git b7530b811e build
8 years ago
git 9c9d8aafbc fix #114 upload a folder of more then 100 files
8 years ago
git b5743ad79f build
8 years ago
git bf34813501 update version
8 years ago
git 8f83f2bbe9 Add doc maximum
8 years ago
git 1155bbe995 fix props.maximum
8 years ago
git ad06881ae2 Add props.maximum
8 years ago
LianYue 76c890de6c
Merge pull request #111 from vmartis/dropfix
8 years ago
vmartis fb0b1d3223 fix cancel drop with ESC
8 years ago
git e11c4e44d8 Build v2.6.3
8 years ago
LianYue 828b5b4eb2 Merge pull request #104 from victorzhuk/master
8 years ago
Victor Zhuk 09ec1eb755 fix. additional file upload with html5 post method
8 years ago
git 57cd9c4425 Build
8 years ago
git d07f4bf5f2 Auto scrollTo hash add decode
8 years ago
git 94a09ce045 Auto scrollTo hash
8 years ago
git 815500dbd6 Build
8 years ago
git d4c117f01b Auto scrollTo hash
8 years ago
git c0d5c65cf8 Fix put-action
8 years ago
git 9307218ecb Add image-compressor script
8 years ago
git 396e269034 build
8 years ago
git 50f5785920 fix line feed
8 years ago
git b9534be58d build
8 years ago
git bb4cc184ed fix line feed
8 years ago
git 081b2aca10 build
8 years ago
git d4c1a49402 add ImageCompressor
8 years ago
git e1251ba4e5 add automatic compression
8 years ago
git d4a85ca90b update docs, add automatic compression
8 years ago
git be060d2b72 window.File -> window.Blob
8 years ago
git 28bb0768ee build
8 years ago
git ff2ecdafaf Update md
8 years ago
git b794ecb808 build
8 years ago
git 17369cd847 Add, props add-index, method replace. Modify method add(files, index)
8 years ago
git 9493161003 fix to.hash
8 years ago
git 88047f60b0 fix to.hash
8 years ago
git 207a000970 Merge branch 'master' of github.com:lian-yue/vue-upload-component
8 years ago
git adfea6e8b1 build
8 years ago
git 778122a9e5 fix #94
8 years ago
git 40790e8c7f build
8 years ago
git d25e9e2a3d fix className
8 years ago
git 657fd2fb4e fix toBlob
8 years ago
LianYue bbba94e20b Delete CNAME
8 years ago
LianYue a44e89a52a Create CNAME
8 years ago
git f65a220435 build
8 years ago
git 49eb5f6459 update Source code
8 years ago
git 27dca17f14 Add viewport meta
8 years ago
git f25f126bd1 Add examples
8 years ago
git 5ce75ac4e8 Add gzip icon
8 years ago
git ef614e59ad fix Source code link
8 years ago
git 9a5562e4ad fix Source code link
8 years ago
git 5ef5575450 flx regeneratorRuntime is not defined
8 years ago
git c6b1011615 Fix H2
8 years ago
git 4207dc0665 v2.6.0-beta.1
8 years ago
git 24bb304a8f Fix `newFile.name`
8 years ago
git 3a5a235bbd `vue-upload-component\/src`
8 years ago
git 71d98e0101 Update docs
8 years ago
git 6f3379f1ed Update version
8 years ago
git 7a32970db7 Update docs
8 years ago
git 14cec0d533 Remove .babelrc
8 years ago
git 8b847bb50f fix dataTransfer `kind=string`
8 years ago
git f3867fb8ea fix dataTransfer `kind=string`
8 years ago
git 9a4f07f8d2 Fix `firefox` upload folder prompt window
8 years ago
git 9615613a4c update README
8 years ago
git 9429278b65 fix dropDirectory
8 years ago
git 87ff993153 Update README
8 years ago
git 104dbebc58 Fix drag upload
8 years ago
git c933a66773 Fix wrap
8 years ago
git 058c83858d update version
8 years ago
git 7b9dac4faa Fix es6-promise
8 years ago
git 7a3af5e0e6 v2.5.0-beta.1
8 years ago
git a5a7a7d694 Add unpkg
8 years ago
git 062b6fd547 Fix #77
8 years ago
git 993b56aca7 Fix example blob
8 years ago
git 5202df85ce Fix #60, Remove props.filter
8 years ago
git f0972e48b3 Detailed event type judgment
8 years ago
git f6e09f2141 Add props inputId
8 years ago
git c70dc2f347 Fix the demo filter, Add post name
8 years ago
git 3d92f7730a Fix #71
8 years ago
git dae502d4ae fix libraryTarget = 'umd';
8 years ago
git 2738d7a90d Add script to import:
8 years ago
git 5aae7d586d Add upload directory
9 years ago
git 4d43a30f5c Add Drop folder #32
9 years ago
git 5b26b07b2d Add v-model
9 years ago
git 05d6158671 V2.4.0-beta.1
9 years ago
git e2f7e18b6b Merge branch '2.0' into gh-pages
9 years ago
git cc404ad3bc fix #56
9 years ago
git 5be0900fdb fix #52
9 years ago
git 5076e381b2 Update md
9 years ago
git 75fd49b119 Add SSR, props.title support HTML
9 years ago
LianYue 6cf882bf1e Merge pull request #50 from docnoe/docnoe-patch-1
9 years ago
Johannes Noe e9f72b0e7b Merge pull request #3 from docnoe/develop
9 years ago
Johannes Noe 7e8caa8861 fix: InputFile component does not bind props
9 years ago
Johannes Noe cab0135ed0 2.3.0
9 years ago
Johannes Noe 44e033f466 build
9 years ago
Johannes Noe a61c424d99 Switching from render function to template
9 years ago
Johannes Noe decbd7bc33 Merge pull request #1 from docnoe/docnoe-patch-1
9 years ago
Johannes Noe cdfaf3f238 Adds slot for inner html
9 years ago
git bfa1949ded Merge branch '2.0' into gh-pages
9 years ago
git fb6a9409e2 Fix thread
9 years ago
git 5e80c29ee4 Merge branch 'gh-pages' of github.com:lian-yue/vue-upload-component into gh-pages
9 years ago
git 77e13e0daf Update
9 years ago
git 0071bd56fd Add props thread
9 years ago
git cb7d7bc864 Add Features #44
9 years ago
git 656a9b6509 fix example url
9 years ago
git 25d906a858 Merge remote-tracking branch 'github/2.0' into 2.0
9 years ago
git e4287f7e1f fix devtool
9 years ago
LianYue a59a2f854a Merge pull request #45 from lukeb/2.0
9 years ago
Luke Brookhart b3da037d54 fix 2 errors in example.
9 years ago
git 7be691ba23 .
9 years ago
git 26f530541d Update devtool
9 years ago
git b782af2df7 Add example
9 years ago
git e4705d3820 version
9 years ago
LianYue 0195eca288 Merge pull request #39 from xengulai/2.0
9 years ago
Jaysen Nuttall 176ff0a8d8 Allow non-string elements in data object
9 years ago
git 8a48f091e4 fix #35 #37
9 years ago
git 2905fab59a update version
9 years ago
LianYue a179fda708 Merge pull request #34 from vetalball/fix-for-33
9 years ago
Vitalii Barkatov dffce36537 Fix 33. run build.
9 years ago
Vitalii Barkatov ea290eab1d Fix #33. Move the file field to the end of form-data
9 years ago
git 9f5babc692 fix #28
9 years ago
git 796cc72c6b fix #29
9 years ago
git 1ef765d7f4 fix #26
9 years ago
git ae6907a892 fix #24 vue 2.1.x
9 years ago
git b9dd840a35 fix #20
9 years ago
git 6929d3b50f fix file response
9 years ago
LianYue 511e31c40b Merge pull request #11 from ilvsx/2.0
9 years ago
ilvsx f401e1e4c8 fix Comment
9 years ago
git bc24562a12 v2.0.0-beta
9 years ago

@ -1,5 +1,11 @@
{
"presets": ["es2015", "stage-2"],
"plugins": ["transform-runtime"],
"comments": false
"presets": [
[
"env",
{
"modules": false
}
],
"stage-0"
]
}

@ -0,0 +1,2 @@
node_modules/*
dist/*

@ -0,0 +1,39 @@
// http://eslint.org/docs/user-guide/configuring
module.exports = {
root: true,
parser: 'babel-eslint',
parserOptions: {
sourceType: 'module'
},
env: {
browser: true,
jest: true
},
// https://github.com/standard/standard/blob/master/docs/RULES-en.md
extends: "standard",
// required to lint *.vue files
plugins: [
'html'
],
// add your custom rules here
'rules': {
// allow paren-less arrow functions
'arrow-parens': 0,
// allow async-await
'generator-star-spacing': 0,
// allow debugger during development
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
'no-control-regex': 0,
'no-useless-escape': 0,
'comma-dangle': 0,
'space-before-function-paren': 0,
'no-multiple-empty-lines': 0,
'no-multi-spaces': 0,
'padded-blocks': 0,
'prefer-promise-reject-errors': 0,
'operator-linebreak': 0
}
}

1
.gitignore vendored

@ -1,3 +1,4 @@
.DS_Store
node_modules/
npm-debug.log
.idea

@ -1,174 +1,75 @@
# 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) [![gzip](http://img.badgesize.io/lian-yue/vue-upload-component/master/dist/vue-upload-component.min.js.svg?compression=gzip&style=flat-square)](https://github.com/lian-yue/vue-upload-component)
> Vue.js file upload component, Support for multiple file uploads, progress, html4, ie9
**Html4 does not support the progress bar, file size, accept, timeout, headers, response status code error of judgment**
> Vue.js file upload component
> The component is just a button
## Install
- [x] Multi-file upload
- [x] Upload directory
- [x] Drag upload
- [x] Drag the directory
- [x] Upload multiple files at the same time
- [x] html4 (IE 9)
- [x] `PUT` method
- [x] Customize the filter
- [x] thumbnails
- [x] Chunk upload
``` bash
npm install vue-upload-component --save
```
### Vue 2.x
https://github.com/lian-yue/vue-upload-component/tree/2.0
``` bash
npm install vue-upload-component@next --save
```
### CommonJS
```js
var FileUpload = require('vue-upload-component');
new Vue({
template: '<file-upload post-action="/post.method" put-action="/put.method"></file-upload>',
components: {
FileUpload: FileUpload
}
})
```
### ES6
```js
import FileUpload from 'vue-upload-component'
new Vue({
template: '<file-upload post-action="/post.method" put-action="/put.method"></file-upload>',
components: {
FileUpload
}
})
```
## Example
# Example
https://lian-yue.github.io/vue-upload-component/
``` html
<!-- Example file ./index.html -->
<!-- Example file ./src/example.js -->
<div id="app">
<file-upload title="Add upload files"></file-upload>
</div>
<script type="text/javascript">
var FileUpload = require('vue-upload-component');
new Vue({
el:'#app',
components: {
FileUpload:FileUpload,
},
});
</script>
```
## Build Setup
# Installation
``` bash
# install dependencies
npm install
# serve with hot reload at localhost:8080
npm run dev
# build for production with minification
npm run build
npm install vue-upload-component --save
```
# Documentation
## $dispatch, methods
addFileUpload
removeFileUpload
https://lian-yue.github.io/vue-upload-component/#/documents
fileUploadProgress
beforeFileUpload
afterFileUpload
## Setting
> Vue.js 文件上传组建
> 组件只是一个按钮
### Data
``` js
{
files: [
{
id: 'String', // Read only
name: 'filename String',
size: 'filesize Number',
progress: 'progress String', // Read only
speed: "Speed Number", // Read only
active: 'active Boolean',
error: 'error String',
errno: 'errno String',
success: 'success Boolean', // Read only
data: 'Response data Object or String', // Read only
request: {
headers: {
"X-Csrf-Token": "xxxx",
},
data: {
"_csrf_token": "xxxxxx",
},
},
}
],
- [x] 上传多文件
- [x] 上传目录
- [x] 拖拽
- [x] 拖拽目录
- [x] 多线程
- [x] html4(IE 9)
- [x] `PUT` 方法
- [x] 自定义过滤器
- [x] 缩略图
# 演示
// Global
request: {
headers: {
"X-Csrf-Token": "xxxx",
},
data: {
"_csrf_token": "xxxxxx",
},
},
https://lian-yue.github.io/vue-upload-component/#/zh-cn/
active: false,
uploaded: true, // Read only
dropActive: false, // Read only
}
```
### Props
``` html
<file-upload :title="Add upload files" :name="file" :drop="Boolean (true = $parent) or Element or Css Selector" :extensions="Array or String or Regular" :post-action="./post.method" :put-action="./put.method" :accept="accept" :multiple="true" :size="size" :timeout="3600000"></file-upload>
```
# 安装
``` bash
npm install vue-upload-component --save
```
title="Add upload files"
name="post file name"
drop="Boolean (true = $parent) or Element or Css Selector"
extensions="Array or String or Regular" :post-action="./post.method"
# 文档
post-action="./post.method"
https://lian-yue.github.io/vue-upload-component/#/zh-cn/documents
put-action="./put.method"
accept="accept"
multiple="true or false"
# Special thanks (特别感谢)
size="max Size"
timeout="3600000"
```
- [@josec89](https://github.com/josec89)

12647
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,7 @@
.file-uploads{overflow:hidden;position:relative;text-align:center;display:inline-block
}
.file-uploads.file-uploads-html4 input,.file-uploads.file-uploads-html5 label{background:#fff;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-html4 label,.file-uploads.file-uploads-html5 input{background:rgba(255,255,255,0);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

@ -0,0 +1,31 @@
<template>
<file-upload
name="file"
post-action="/post"
put-action="/put"
:value="files"
@input="input"
ref="upload">
Add upload files
</file-upload>
</template>
<script>
import FileUpload from '../src'
import { mapState } from 'vuex'
export default {
components: {
FileUpload,
},
computed: mapState({
files: state => state.files
}),
methods: {
// Files Event
input(files) {
this.$store.commit('updateFiles', files)
},
},
}
</script>

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,31 @@
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 Example',
simple: 'Simple',
avatar: 'Upload avatar',
drag: 'Drag and drop',
multiple: 'Multiple instances',
chunk: 'Chunk upload',
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,30 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="Cache-Control" content="no-siteapp">
<meta http-equiv="Cache-Control" content="no-transform">
<meta http-equiv="X-UA-Compatible" content="IE=Edge, chrome=1">
<meta name="renderer" content="webkit">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<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/gh/xkeshi/image-compressor/dist/image-compressor.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,136 @@
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 || ''
}
if (el.markdown === text) {
return
}
el.markdown = 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,95 @@
// 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 ChunkExampleComponent from './views/examples/Chunk'
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: 'chunk',
component: ChunkExampleComponent,
},
{
path: 'vuex',
component: VuexExampleComponent,
},
]
const router = new VueRouter({
mode: 'hash',
fallback: false,
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else if (to.hash) {
let el = document.querySelector(to.hash)
return { x: 0, y: el ? el.offsetTop : 0 }
} else {
return { x: 0, 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

@ -0,0 +1,20 @@
// import Vue from 'vue'
import Vuex from 'vuex'
//
// Vue.use(Vuex)
const state = {
files: [],
}
const mutations = {
updateFiles(state, files) {
state.files = files
}
}
export default new Vuex.Store({
strict: true,
state,
mutations
})

@ -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,115 @@
<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 {
mounted() {
// auto scrollTo hash
if (this.$route.hash) {
let el = document.querySelector(decodeURIComponent(this.$route.hash))
if (el) {
window.scrollTo(0, el.offsetTop)
}
}
},
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
},
},
}
</script>

@ -0,0 +1,34 @@
<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/chunk' | toLocale">{{$t('example.chunk')}}</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,179 @@
<template>
<div class="example-avatar">
<div v-show="$refs.upload && $refs.upload.dropActive" class="drop-active">
<h3>Drop files to upload</h3>
</div>
<div class="avatar-upload" v-show="!edit">
<div class="text-center p-2">
<label for="avatar">
<img :src="files.length ? files[0].url : 'https://www.gravatar.com/avatar/default?s=200&r=pg&d=mm'" class="rounded-circle" />
<h4 class="pt-2">or<br/>Drop files anywhere to upload</h4>
</label>
</div>
<div class="text-center p-2">
<file-upload
extensions="gif,jpg,jpeg,png,webp"
accept="image/png,image/gif,image/jpeg,image/webp"
name="avatar"
class="btn btn-primary"
post-action="/upload/post"
:drop="!edit"
v-model="files"
@input-filter="inputFilter"
@input-file="inputFile"
ref="upload">
Upload avatar
</file-upload>
</div>
</div>
<div class="avatar-edit" v-show="files.length && edit">
<div class="avatar-edit-image" v-if="files.length">
<img ref="editImage" :src="files[0].url" />
</div>
<div class="text-center p-4">
<button type="button" class="btn btn-secondary" @click.prevent="$refs.upload.clear">Cancel</button>
<button type="submit" class="btn btn-primary" @click.prevent="editSave">Save</button>
</div>
</div>
<div class="pt-5">
Source code: <a href="https://github.com/lian-yue/vue-upload-component/blob/master/docs/views/examples/Avatar.vue">/docs/views/examples/Avatar.vue</a>
</div>
</div>
</template>
<style>
.example-avatar .avatar-upload .rounded-circle {
width: 200px;
height: 200px;
}
.example-avatar .text-center .btn {
margin: 0 .5rem
}
.example-avatar .avatar-edit-image {
max-width: 100%
}
.example-avatar .drop-active {
top: 0;
bottom: 0;
right: 0;
left: 0;
position: fixed;
z-index: 9999;
opacity: .6;
text-align: center;
background: #000;
}
.example-avatar .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: [],
edit: false,
cropper: false,
}
},
watch: {
edit(value) {
if (value) {
this.$nextTick(function () {
if (!this.$refs.editImage) {
return
}
let cropper = new Cropper(this.$refs.editImage, {
aspectRatio: 1 / 1,
viewMode: 1,
})
this.cropper = cropper
})
} else {
if (this.cropper) {
this.cropper.destroy()
this.cropper = false
}
}
}
},
methods: {
editSave() {
this.edit = false
let oldFile = this.files[0]
let binStr = atob(this.cropper.getCroppedCanvas().toDataURL(oldFile.type).split(',')[1])
let arr = new Uint8Array(binStr.length)
for (let i = 0; i < binStr.length; i++) {
arr[i] = binStr.charCodeAt(i)
}
let file = new File([arr], oldFile.name, { type: oldFile.type })
this.$refs.upload.update(oldFile.id, {
file,
type: file.type,
size: file.size,
active: true,
})
},
alert(message) {
alert(message)
},
inputFile(newFile, oldFile, prevent) {
if (newFile && !oldFile) {
this.$nextTick(function () {
this.edit = true
})
}
if (!newFile && oldFile) {
this.edit = false
}
},
inputFilter(newFile, oldFile, prevent) {
if (newFile && !oldFile) {
if (!/\.(gif|jpg|jpeg|png|webp)$/i.test(newFile.name)) {
this.alert('Your choice is not a picture')
return prevent()
}
}
if (newFile && (!oldFile || newFile.file !== oldFile.file)) {
newFile.url = ''
let URL = window.URL || window.webkitURL
if (URL && URL.createObjectURL) {
newFile.url = URL.createObjectURL(newFile.file)
}
}
}
}
}
</script>

@ -0,0 +1,245 @@
<template>
<div class="example-simple">
<h1 id="example-title" class="example-title">Chunk Upload Example</h1>
<p>When using chunk uploads, the file will be uploaded in different parts (or chunks). All the files with a size higher than the set in the input will be uploaded using this method.</p>
<p>You will be able to see the different parts being uploaded individually. Some parts might fail, and the package is prepared to <em>retry</em> up to a certain amount of times.</p>
<p>You can also pause / resume the upload process.</p>
<div class="upload">
<div class="form-horizontal">
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<div class="checkbox">
<label>
<input v-model="chunkEnabled" type="checkbox"> Use chunk upload
</label>
</div>
</div>
</div>
<div class="form-group">
<label for="inputMinSize" class="col-sm-2 control-label">Min Size</label>
<div class="col-sm-10">
<div class="input-group">
<input id="inputMinSize" v-model="chunkMinSize" type="number" class="form-control">
<span class="input-group-addon">MB</span>
</div>
</div>
</div>
<div class="form-group">
<label for="inputMaxActive" class="col-sm-2 control-label">Max Active Chunks</label>
<div class="col-sm-10">
<input id="inputMaxActive" v-model="chunkMaxActive" type="number" class="form-control">
</div>
</div>
<div class="form-group">
<label for="inputMaxRetries" class="col-sm-2 control-label">Max Chunk Retries</label>
<div class="col-sm-10">
<input id="inputMaxRetries" v-model="chunkMaxRetries" type="number" class="form-control">
</div>
</div>
</div>
<table class="table table-striped table-condensed">
<thead class="thead-dark">
<tr>
<th>Name</th>
<th class="text-right">Size</th>
<th class="text-right">Progress</th>
<th>Status</th>
<th>Pause</th>
<th colspan="3" class="text-center">Chunks</th>
</tr>
<tr>
<th colspan="5"></th>
<th class="text-right">Total</th>
<th class="text-right">Active</th>
<th class="text-right">Completed</th>
</tr>
</thead>
<tbody>
<template v-for="file in files">
<tr :key="file.id + '-info'">
<td>{{ file.name }}</td>
<td class="text-right">{{ file.size | formatSize }}</td>
<td class="text-right">{{ file.progress }}%</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>
<template v-if="file.chunk">
<button
class="btn btn-sm btn-danger"
v-if="file.active"
@click="file.chunk.pause()"
>
<i class="fa fa-pause"/>
</button>
<button
class="btn btn-sm btn-primary"
v-if="!file.active && file.chunk.hasChunksToUpload"
@click="file.chunk.resume()"
>
<i class="fa fa-play"/>
</button>
</template>
</td>
<template v-if="file.chunk">
<td class="text-right">{{ file.chunk.chunks.length }}</td>
<td class="text-right">{{ file.chunk.chunksUploading.length }}</td>
<td class="text-right">{{ file.chunk.chunksUploaded.length }}</td>
</template>
<template v-else>
<td class="text-right"> - </td>
<td class="text-right"> - </td>
<td class="text-right"> - </td>
</template>
</tr>
<tr :key="file.id + '-loading'">
<td colspan="8">
<div class="chunk-loading" v-if="file.chunk">
<span
v-for="(chunk, index) in file.chunk.chunks"
:key="index"
class="chunk-loading-part"
:class="{'chunk-loading-part__uploaded': chunk.uploaded}"
>
<template v-if="chunk.retries != file.chunk.maxRetries">
{{ file.chunk.maxRetries - chunk.retries }} error(s)
</template>
</span>
</div>
</td>
</tr>
</template>
</tbody>
</table>
<div class="example-btn">
<file-upload
class="btn btn-primary"
post-action="/upload/post"
:chunk-enabled="chunkEnabled"
:chunk="{
action: '/upload/chunk',
minSize: chunkMinSize * 1048576,
maxActive: chunkMaxActive,
maxRetries: chunkMaxRetries
}"
extensions="gif,jpg,jpeg,png,webp"
accept="image/png,image/gif,image/jpeg,image/webp"
:multiple="true"
:size="1024 * 1024 * 10"
v-model="files"
@input-filter="inputFilter"
@input-file="inputFile"
ref="upload">
<i class="fa fa-plus"></i>
Select files
</file-upload>
</div>
</div>
<div class="pt-5">
Source code: <a href="https://github.com/lian-yue/vue-upload-component/blob/master/docs/views/examples/Chunk.vue">/docs/views/examples/Chunk.vue</a>
</div>
</div>
</template>
<style>
.example-simple label.btn {
margin-bottom: 0;
margin-right: 1rem;
}
</style>
<script>
import FileUpload from 'vue-upload-component'
export default {
components: {
FileUpload,
},
data() {
return {
files: [],
chunkEnabled: true,
// 1MB by default
chunkMinSize: 1,
chunkMaxActive: 3,
chunkMaxRetries: 5
}
},
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()
}
}
},
inputFile(newFile, oldFile) {
if (newFile && !oldFile) {
// add
console.log('add', newFile)
this.$refs.upload.active = true
}
if (newFile && oldFile) {
// update
console.log('update', newFile)
}
if (!newFile && oldFile) {
// remove
console.log('remove', oldFile)
}
}
}
}
</script>
<style scoped>
.chunk-loading {
margin: -12px;
display: flex;
width: calc(100% + 24px);
}
.chunk-loading .chunk-loading-part {
height: 25px;
line-height: 25px;
flex: 1;
background: #ccc;
font-size: 14px;
color: white;
text-align: center;
}
.chunk-loading .chunk-loading-part.chunk-loading-part__uploaded {
background: #28A745;
}
</style>

@ -0,0 +1,102 @@
<template>
<div class="example-drag">
<div class="upload">
<ul v-if="files.length">
<li v-for="(file, index) in files" :key="file.id">
<span>{{file.name}}</span> -
<span>{{file.size | formatSize}}</span> -
<span v-if="file.error">{{file.error}}</span>
<span v-else-if="file.success">success</span>
<span v-else-if="file.active">active</span>
<span v-else></span>
</li>
</ul>
<ul v-else>
<td colspan="7">
<div class="text-center p-5">
<h4>Drop files anywhere to upload<br/>or</h4>
<label for="file" class="btn btn-lg btn-primary">Select Files</label>
</div>
</td>
</ul>
<div v-show="$refs.upload && $refs.upload.dropActive" class="drop-active">
<h3>Drop files to upload</h3>
</div>
<div class="example-btn">
<file-upload
class="btn btn-primary"
post-action="/upload/post"
:multiple="true"
:drop="true"
:drop-directory="true"
v-model="files"
ref="upload">
<i class="fa fa-plus"></i>
Select files
</file-upload>
<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="pt-5">
Source code: <a href="https://github.com/lian-yue/vue-upload-component/blob/master/docs/views/examples/Drag.vue">/docs/views/examples/Drag.vue</a>
</div>
</div>
</template>
<style>
.example-drag label.btn {
margin-bottom: 0;
margin-right: 1rem;
}
.example-drag .drop-active {
top: 0;
bottom: 0;
right: 0;
left: 0;
position: fixed;
z-index: 9999;
opacity: .6;
text-align: center;
background: #000;
}
.example-drag .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 FileUpload from 'vue-upload-component'
export default {
components: {
FileUpload,
},
data() {
return {
files: [],
}
}
}
</script>

@ -0,0 +1,642 @@
<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="example-title" class="example-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">
<div class="table-responsive">
<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 || file.error === 'compressing'}" href="#" @click.prevent="file.active || file.success || file.error === 'compressing' ? 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 && file.error !== 'compressing' && $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 || file.error === 'compressing'}" href="#" v-else @click.prevent="file.success || file.error === 'compressing' ? 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>
<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"
:add-index="addIndex"
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">
<label for="autoCompress">Automatically compress:</label>
<input type="number" min="0" id="autoCompress" class="form-control" v-model.number="autoCompress">
<small class="form-text text-muted" v-if="autoCompress > 0">More than {{autoCompress | formatSize}} files are automatically compressed</small>
<small class="form-text text-muted" v-else>Set up automatic compression</small>
</div>
<div class="form-group">
<div class="form-check">
<label class="form-check-label">
<input type="checkbox" id="add-index" class="form-check-input" v-model="addIndex"> Start position to add
</label>
</div>
<small class="form-text text-muted">Add a file list to start the location to add</small>
</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/blob/master/docs/views/examples/Full.vue">/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 ImageCompressor from '@xkeshi/image-compressor'
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,
addIndex: false,
thread: 3,
name: 'file',
postAction: '/upload/post',
putAction: '/upload/put',
headers: {
'X-Csrf-Token': 'xxxx',
},
data: {
'_csrf_token': 'xxxxxx',
},
autoCompress: 1024 * 1024,
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()
}
// Automatic compression
//
if (newFile.file && newFile.type.substr(0, 6) === 'image/' && this.autoCompress > 0 && this.autoCompress < newFile.size) {
newFile.error = 'compressing'
const imageCompressor = new ImageCompressor(null, {
convertSize: Infinity,
maxWidth: 512,
maxHeight: 512,
})
imageCompressor.compress(newFile.file)
.then((file) => {
this.$refs.upload.update(newFile, { error: '', file, size: file.size, type: file.type })
})
.catch((err) => {
this.$refs.upload.update(newFile, { error: err.message || 'compress' })
})
}
}
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 (Boolean(newFile) !== Boolean(oldFile) || oldFile.error !== newFile.error) {
if (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' })
},
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 binStr = atob(this.editFile.cropper.getCroppedCanvas().toDataURL(this.editFile.type).split(',')[1])
let arr = new Uint8Array(binStr.length)
for (let i = 0; i < binStr.length; i++) {
arr[i] = binStr.charCodeAt(i)
}
data.file = new File([arr], data.name, { type: this.editFile.type })
data.size = data.file.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,101 @@
<template>
<div class="example-multiple">
<h1 id="example-title" class="example-title">Multiple instances</h1>
<div class="upload">
<ul>
<li v-for="(file, index) in files1" :key="file.id">
<span>{{file.name}}</span> -
<span>{{file.size | formatSize}}</span> -
<span v-if="file.error">{{file.error}}</span>
<span v-else-if="file.success">success</span>
<span v-else-if="file.active">active</span>
<span v-else-if="file.active">active</span>
<span v-else></span>
</li>
</ul>
<div class="example-btn">
<file-upload
class="btn btn-primary"
input-id="file1"
post-action="/upload/post"
v-model="files1"
ref="upload1">
<i class="fa fa-plus"></i>
Select files
</file-upload>
<label for="file1" class="btn btn-primary">Label Select files</label>
<button type="button" class="btn btn-success" v-if="!$refs.upload1 || !$refs.upload1.active" @click.prevent="$refs.upload1.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.upload1.active = false">
<i class="fa fa-stop" aria-hidden="true"></i>
Stop Upload
</button>
</div>
</div>
<div class="upload">
<ul>
<li v-for="(file, index) in files2" :key="file.id">
<span>{{file.name}}</span> -
<span>{{file.size | formatSize}}</span> -
<span v-if="file.error">{{file.error}}</span>
<span v-else-if="file.success">success</span>
<span v-else-if="file.active">active</span>
<span v-else-if="file.active">active</span>
<span v-else></span>
</li>
</ul>
<div class="example-btn">
<file-upload
class="btn btn-primary"
input-id="file2"
post-action="/upload/post"
v-model="files2"
ref="upload2">
<i class="fa fa-plus"></i>
Select files
</file-upload>
<label for="file2" class="btn btn-primary">Label Select files</label>
<button type="button" class="btn btn-success" v-if="!$refs.upload2 || !$refs.upload2.active" @click.prevent="$refs.upload2.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.upload2.active = false">
<i class="fa fa-stop" aria-hidden="true"></i>
Stop Upload
</button>
</div>
</div>
<div class="pt-5">
Source code: <a href="https://github.com/lian-yue/vue-upload-component/blob/master/docs/views/examples/Multiple.vue">/docs/views/examples/Multiple.vue</a>
</div>
</div>
</template>
<style>
.example-multiple label.btn {
margin-bottom: 0;
margin-right: 1rem;
}
.example-multiple .upload {
margin-bottom: 1rem;
}
</style>
<script>
import FileUpload from 'vue-upload-component'
export default {
components: {
FileUpload,
},
data() {
return {
files1: [],
files2: [],
}
},
}
</script>

@ -0,0 +1,103 @@
<template>
<div class="example-simple">
<h1 id="example-title" class="example-title">Simple Example</h1>
<div class="upload">
<ul>
<li v-for="(file, index) in files" :key="file.id">
<span>{{file.name}}</span> -
<span>{{file.size | formatSize}}</span> -
<span v-if="file.error">{{file.error}}</span>
<span v-else-if="file.success">success</span>
<span v-else-if="file.active">active</span>
<span v-else-if="file.active">active</span>
<span v-else></span>
</li>
</ul>
<div class="example-btn">
<file-upload
class="btn btn-primary"
post-action="/upload/post"
extensions="gif,jpg,jpeg,png,webp"
accept="image/png,image/gif,image/jpeg,image/webp"
:multiple="true"
:size="1024 * 1024 * 10"
v-model="files"
@input-filter="inputFilter"
@input-file="inputFile"
ref="upload">
<i class="fa fa-plus"></i>
Select files
</file-upload>
<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="pt-5">
Source code: <a href="https://github.com/lian-yue/vue-upload-component/blob/master/docs/views/examples/Simple.vue">/docs/views/examples/Simple.vue</a>
</div>
</div>
</template>
<style>
.example-simple label.btn {
margin-bottom: 0;
margin-right: 1rem;
}
</style>
<script>
import FileUpload from 'vue-upload-component'
export default {
components: {
FileUpload,
},
data() {
return {
files: [],
}
},
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()
}
}
},
inputFile(newFile, oldFile) {
if (newFile && !oldFile) {
// add
console.log('add', newFile)
}
if (newFile && oldFile) {
// update
console.log('update', newFile)
}
if (!newFile && oldFile) {
// remove
console.log('remove', oldFile)
}
}
}
}
</script>

@ -0,0 +1,72 @@
<template>
<div class="example-vuex">
<h1 id="example-title" class="example-title">Vuex Example</h1>
<div class="upload">
<ul>
<li v-for="(file, index) in files" :key="file.id">
<span>{{file.name}}</span> -
<span>{{file.size | formatSize}}</span> -
<span v-if="file.error">{{file.error}}</span>
<span v-else-if="file.success">success</span>
<span v-else-if="file.active">active</span>
<span v-else-if="file.active">active</span>
<span v-else></span>
</li>
</ul>
<div class="example-btn">
<file-upload
class="btn btn-primary"
post-action="/upload/post"
extensions="gif,jpg,jpeg,png,webp"
accept="image/png,image/gif,image/jpeg,image/webp"
:multiple="true"
:size="1024 * 1024 * 10"
:value="files"
@input="inputUpdate"
ref="upload">
<i class="fa fa-plus"></i>
Select files
</file-upload>
<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="pt-5">
Source code: <a href="https://github.com/lian-yue/vue-upload-component/blob/master/docs/views/examples/Vuex.vue">/docs/views/examples/Vuex.vue</a>, <a href="https://github.com/lian-yue/vue-upload-component/blob/master/docs/store.js">/docs/store.js</a>
</div>
</div>
</template>
<style>
.example-vuex label.btn {
margin-bottom: 0;
margin-right: 1rem;
}
</style>
<script>
import { mapState } from 'vuex'
import FileUpload from 'vue-upload-component'
export default {
components: {
FileUpload,
},
computed: {
...mapState([
'files',
])
},
methods: {
inputUpdate(files) {
this.$store.commit('updateFiles', files)
},
}
}
</script>

54
index.d.ts vendored

@ -0,0 +1,54 @@
import Vue from 'vue'
// Instance / File
declare global {
interface VUFile {
file: File
readonly fileObject: boolean
id: string | number
size: number
name: string
type: string
active: boolean
error: string
success: boolean
putAction: string
postAction: string
headers: object
data: object
timeout: number
response: object | string
progress: string
speed: number
xhr: XMLHttpRequest
iframe: Element
}
}
declare class _ extends Vue {
// Instance / Methods
get(id: VUFile | object | string): VUFile | object | boolean
add(files: Array<VUFile | File | object> | VUFile | File | object): object | Array<VUFile | object> | boolean
addInputFile(el: HTMLInputElement): Array<VUFile>
addDataTransfer(dataTransfer: DataTransfer): Promise<Array<VUFile>>
update(id: VUFile | object | string, data: object): object | boolean
remove(id: VUFile | object | string): object | boolean
replace(id1: VUFile | object | string, id2: VUFile | object | string): boolean
clear(): boolean
// Instance / Data
readonly files: Array<VUFile>
readonly features: { html5?: boolean; directory?: boolean; drag?: boolean }
active: boolean
readonly dropActive: true
readonly uploaded: true
}
// module 'vue/types/vue' {
// https://stackoverflow.com/a/41286276/5221998
// interface Vue {
// readonly $refs: { [key: string]: VueUploadComponent };
// }
// }
export default _

@ -2,131 +2,29 @@
<html lang="en">
<head>
<meta charset="utf-8">
<title>vue-upload-component</title>
<style type="text/css">
#app {
position: relative;
}
.file-upload {
display: block;
}
.file-upload span{
display: block;
font-size: 18px;
padding: 1em;
font-weight: bold;
border: 1px solid #888;
}
.drop-active {
top: 0;
bottom: 0;
right: 0;
left: 0;
position: absolute;
opacity: .4;
background: #000;
}
</style>
<meta http-equiv="Cache-Control" content="no-siteapp">
<meta http-equiv="Cache-Control" content="no-transform">
<meta http-equiv="X-UA-Compatible" content="IE=Edge, chrome=1">
<meta name="renderer" content="webkit">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<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">
<table style="width: 100%; border: 1px solid #ddd;">
<thead>
<tr>
<th>Index</th>
<th>ID</th>
<th>Name</th>
<th>Size</th>
<th>Progress</th>
<th>Speed</th>
<th>Active</th>
<th>Error</th>
<th>Errno</th>
<th>Success</th>
<th>Abort</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
<tr v-for="file in files">
<td>{{$index}}</td>
<td>{{file.id}}</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.errno}}</td>
<td>{{file.success}}</td>
<td @click="file.active = false">Abort</td>
<td @click="remove(file)">x</td>
</tr>
</tbody>
</table>
<br/>
<br/>
<table style="width: 100%; border: 1px solid #ddd;">
<tbody>
<tr>
<td>
<file-upload title="<strong>Add upload files</strong>" class="file-upload" name="file" post-action="./post.php" put-action="./put.php" :extensions="extensions" :accept="accept" :multiple="multiple" :size="size" v-ref:upload :drop="drop"></file-upload>
</td>
<td>
<button v-if="upload.active" type="submit" @click.prevent="$refs.upload.active = !$refs.upload.active">Stop upload</button>
<button v-else type="submit" @click.prevent="$refs.upload.active = !$refs.upload.active">Start 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="size">
</label>
</td>
<td>
<label>
Multiple: <input type="checkbox" id="checkbox" v-model="multiple">
</label>
</td>
</tr>
<tr>
<td>
Auto start: {{auto}}
</td>
<td>
Active: {{$refs.upload.active}}
</td>
<td>
Uploaded: {{$refs.upload.uploaded}}
</td>
<td>
Drop active: {{$refs.upload.dropActive}}
</td>
</tr>
</tbody>
</table>
<div v-show="$refs.upload.dropActive" class="drop-active">
Drop ing
</div>
</div>
<script src="./dist/example.js"></script>
<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/gh/xkeshi/image-compressor/dist/image-compressor.js"></script>
<script src="https://cdn.jsdelivr.net/npm/cropperjs/dist/cropper.js"></script>
<script src="./docs/dist/index.js"></script>
</body>
</html>

10498
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -1,55 +1,84 @@
{
"name": "vue-upload-component",
"description": "Vue.js file upload component, Support for multiple file uploads, progress, html5, html4, support ie9",
"version": "0.3.9",
"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.8.20",
"author": "LianYue",
"scripts": {
"dev": "webpack-dev-server --inline --hot",
"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"
"dev": "cross-env NODE_ENV=development webpack-dev-server",
"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",
"module": "dist/vue-upload-component.js",
"unpkg": "dist/vue-upload-component.js",
"jsdelivr": "dist/vue-upload-component.js",
"typings": "index.d.ts",
"repository": {
"type": "git",
"url": "git+https://github.com/lian-yue/vue-upload-component.git"
},
"keywords": [
"javascript",
"vue",
"vue.js",
"component",
"file",
"put",
"upload",
"uploads",
"uploader",
"directory",
"multiple",
"component",
"upload",
"upload-directory",
"chunk",
"drag",
"drag-directory",
"upload-directory",
"vue-component",
"vue-upload-component",
"vue-file-upload",
"vue-file-upload-component"
"vue-upload-component"
],
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/lian-yue/vue-upload-component/issues"
},
"homepage": "https://github.com/lian-yue/vue-upload-component#readme",
"dependencies": {
},
"dependencies": {},
"devDependencies": {
"babel-core": "^6.0.0",
"babel-loader": "^6.0.0",
"babel-plugin-transform-runtime": "^6.0.0",
"babel-preset-es2015": "^6.0.0",
"babel-preset-stage-2": "^6.0.0",
"babel-runtime": "^6.0.0",
"cross-env": "^1.0.6",
"css-loader": "^0.23.0",
"file-loader": "^0.8.4",
"json-loader": "^0.5.4",
"url-loader": "^0.5.7",
"vue": "^1.0.26",
"vue-hot-reload-api": "^1.2.0",
"vue-html-loader": "^1.0.0",
"vue-loader": "^8.2.1",
"vue-style-loader": "^1.0.0",
"webpack": "^1.12.2",
"webpack-dev-server": "^1.12.0"
"babel-core": "^6.26.3",
"babel-eslint": "^8.2.6",
"babel-loader": "^7.1.5",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-env": "^1.7.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": "^5.2.0",
"css-loader": "^1.0.0",
"eslint": "^5.1.0",
"eslint-config-standard": "^11.0.0",
"eslint-loader": "^2.0.0",
"eslint-plugin-html": "^4.0.5",
"eslint-plugin-import": "^2.13.0",
"eslint-plugin-node": "^7.0.1",
"eslint-plugin-promise": "^3.8.0",
"eslint-plugin-standard": "^3.1.0",
"eslint-plugin-vue": "^4.7.0",
"postcss": "^7.0.0",
"raw-loader": "^0.5.1",
"rollup": "^0.63.0",
"rollup-plugin-babel": "^3.0.7",
"rollup-plugin-commonjs": "^9.1.3",
"rollup-plugin-css-only": "^0.4.0",
"rollup-plugin-node-resolve": "^3.3.0",
"rollup-plugin-uglify": "^4.0.0",
"rollup-plugin-vue": "^4.3.1",
"vue-hot-reload-api": "^2.3.0",
"vue-loader": "^15.2.5",
"vue-template-compiler": "^2.5.16",
"webpack": "^4.16.1",
"webpack-body-parser": "^1.11.110",
"webpack-cli": "^3.0.8",
"webpack-dev-server": "^3.1.4",
"webpack-merge": "^4.1.3"
}
}

@ -0,0 +1,98 @@
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 pluginCSS = require('rollup-plugin-css-only')
// const isDev = process.env.NODE_ENV === 'development'
function baseConfig() {
return {
output: {
format: 'umd',
sourcemap: true,
banner: `/*!\n * Name: ${packageInfo.name}\n * Version: ${packageInfo.version}\n * Author: ${packageInfo.author}\n */`,
},
plugins: [
resolve({
jsnext: true,
main: true,
browser: true,
}),
commonjs({
extensions: [
'.js',
'.jsx',
'.json',
// '.vue'
],
}),
],
}
}
let config = baseConfig()
config.input = 'src/index.js'
config.output.file = 'dist/vue-upload-component.js'
config.output.name = 'VueUploadComponent'
config.plugins.push(
vue({
template: {
isProduction: true,
},
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({
style: {
trim: true,
},
template: {
isProduction: true,
},
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(
pluginCSS(),
vue({
style: {
trim: true,
},
template: {
isProduction: true,
},
css: false,
}),
babel()
)
module.exports = [
config,
configMin,
configPart,
]

File diff suppressed because it is too large Load Diff

@ -0,0 +1,38 @@
<template>
<input
type="file"
:name="$parent.name"
:id="$parent.inputId || $parent.name"
:accept="$parent.accept"
:capture="$parent.capture"
:disabled="$parent.disabled"
@change="change"
:webkitdirectory="$parent.directory && $parent.features.directory"
:directory="$parent.directory && $parent.features.directory"
:multiple="$parent.multiple && $parent.features.html5"
/>
</template>
<script>
export default {
methods: {
change(e) {
this.$parent.addInputFile(e.target)
if (e.target.files) {
e.target.value = ''
if (e.target.files.length && !/safari/i.test(navigator.userAgent)) {
e.target.type = ''
e.target.type = 'file'
}
} else {
// ie9 fix #219
this.$destroy()
// eslint-disable-next-line
new this.constructor({
parent: this.$parent,
el: this.$el,
})
}
}
}
}
</script>

@ -0,0 +1,360 @@
import {
default as request,
createRequest,
sendFormRequest
} from '../utils/request'
export default class ChunkUploadHandler {
/**
* Constructor
*
* @param {File} file
* @param {Object} options
*/
constructor (file, options) {
this.file = file
this.options = options
}
/**
* Gets the max retries from options
*/
get maxRetries () {
return parseInt(this.options.maxRetries)
}
/**
* Gets the max number of active chunks being uploaded at once from options
*/
get maxActiveChunks () {
return parseInt(this.options.maxActive)
}
/**
* Gets the file type
*/
get fileType () {
return this.file.type
}
/**
* Gets the file size
*/
get fileSize () {
return this.file.size
}
/**
* Gets the file name
*/
get fileName () {
return this.file.name
}
/**
* Gets action (url) to upload the file
*/
get action () {
return this.options.action || null
}
/**
* Gets the body to be merged when sending the request in start phase
*/
get startBody () {
return this.options.startBody || {}
}
/**
* Gets the body to be merged when sending the request in upload phase
*/
get uploadBody () {
return this.options.uploadBody || {}
}
/**
* Gets the body to be merged when sending the request in finish phase
*/
get finishBody () {
return this.options.finishBody || {}
}
/**
* Gets the headers of the requests from options
*/
get headers () {
return this.options.headers || {}
}
/**
* Whether it's ready to upload files or not
*/
get readyToUpload () {
return !!this.chunks
}
/**
* Gets the progress of the chunk upload
* - Gets all the completed chunks
* - Gets the progress of all the chunks that are being uploaded
*/
get progress () {
const completedProgress = (this.chunksUploaded.length / this.chunks.length) * 100
const uploadingProgress = this.chunksUploading.reduce((progress, chunk) => {
return progress + ((chunk.progress | 0) / this.chunks.length)
}, 0)
return Math.min(completedProgress + uploadingProgress, 100)
}
/**
* Gets all the chunks that are pending to be uploaded
*/
get chunksToUpload () {
return this.chunks.filter(chunk => {
return !chunk.active && !chunk.uploaded
})
}
/**
* Whether there are chunks to upload or not
*/
get hasChunksToUpload () {
return this.chunksToUpload.length > 0
}
/**
* Gets all the chunks that are uploading
*/
get chunksUploading () {
return this.chunks.filter(chunk => {
return !!chunk.xhr && !!chunk.active
})
}
/**
* Gets all the chunks that have finished uploading
*/
get chunksUploaded () {
return this.chunks.filter(chunk => {
return !!chunk.uploaded
})
}
/**
* Creates all the chunks in the initial state
*/
createChunks () {
this.chunks = []
let start = 0
let end = this.chunkSize
while (start < this.fileSize) {
this.chunks.push({
blob: this.file.file.slice(start, end),
startOffset: start,
active: false,
retries: this.maxRetries
})
start = end
end = start + this.chunkSize
}
}
/**
* Updates the progress of the file with the handler's progress
*/
updateFileProgress () {
this.file.progress = this.progress
}
/**
* Paues the upload process
* - Stops all active requests
* - Sets the file not active
*/
pause () {
this.file.active = false
this.stopChunks()
}
/**
* Stops all the current chunks
*/
stopChunks () {
this.chunksUploading.forEach(chunk => {
chunk.xhr.abort()
chunk.active = false
})
}
/**
* Resumes the file upload
* - Sets the file active
* - Starts the following chunks
*/
resume () {
this.file.active = true
this.startChunking()
}
/**
* Starts the file upload
*
* @returns Promise
* - resolve The file was uploaded
* - reject The file upload failed
*/
upload () {
this.promise = new Promise((resolve, reject) => {
this.resolve = resolve
this.reject = reject
})
this.start()
return this.promise
}
/**
* Start phase
* Sends a request to the backend to initialise the chunks
*/
start () {
request({
method: 'POST',
headers: Object.assign({}, this.headers, {
'Content-Type': 'application/json'
}),
url: this.action,
body: Object.assign(this.startBody, {
phase: 'start',
mime_type: this.fileType,
size: this.fileSize,
name: this.fileName
})
}).then(res => {
if (res.status !== 'success') {
this.file.response = res
return this.reject('server')
}
this.sessionId = res.data.session_id
this.chunkSize = res.data.end_offset
this.createChunks()
this.startChunking()
}).catch(res => {
this.file.response = res
this.reject('server')
})
}
/**
* Starts to upload chunks
*/
startChunking () {
for (let i = 0; i < this.maxActiveChunks; i++) {
this.uploadNextChunk()
}
}
/**
* Uploads the next chunk
* - Won't do anything if the process is paused
* - Will start finish phase if there are no more chunks to upload
*/
uploadNextChunk () {
if (this.file.active) {
if (this.hasChunksToUpload) {
return this.uploadChunk(this.chunksToUpload[0])
}
if (this.chunksUploading.length === 0) {
return this.finish()
}
}
}
/**
* Uploads a chunk
* - Sends the chunk to the backend
* - Sets the chunk as uploaded if everything went well
* - Decreases the number of retries if anything went wrong
* - Fails if there are no more retries
*
* @param {Object} chunk
*/
uploadChunk (chunk) {
chunk.progress = 0
chunk.active = true
this.updateFileProgress()
chunk.xhr = createRequest({
method: 'POST',
headers: this.headers,
url: this.action
})
chunk.xhr.upload.addEventListener('progress', function (evt) {
if (evt.lengthComputable) {
chunk.progress = Math.round(evt.loaded / evt.total * 100)
}
}, false)
sendFormRequest(chunk.xhr, Object.assign(this.uploadBody, {
phase: 'upload',
session_id: this.sessionId,
start_offset: chunk.startOffset,
chunk: chunk.blob
})).then(res => {
chunk.active = false
if (res.status === 'success') {
chunk.uploaded = true
} else {
if (chunk.retries-- <= 0) {
this.stopChunks()
return this.reject('upload')
}
}
this.uploadNextChunk()
}).catch(() => {
chunk.active = false
if (chunk.retries-- <= 0) {
this.stopChunks()
return this.reject('upload')
}
this.uploadNextChunk()
})
}
/**
* Finish phase
* Sends a request to the backend to finish the process
*/
finish () {
this.updateFileProgress()
request({
method: 'POST',
headers: Object.assign({}, this.headers, {
'Content-Type': 'application/json'
}),
url: this.action,
body: Object.assign(this.finishBody, {
phase: 'finish',
session_id: this.sessionId
})
}).then(res => {
this.file.response = res
if (res.status !== 'success') {
return this.reject('server')
}
this.resolve(res)
}).catch(res => {
this.file.response = res
this.reject('server')
})
}
}

@ -1,77 +0,0 @@
import Vue from 'vue';
// var FileUpload = require('./FileUpload.vue');
import FileUpload from './FileUpload.vue';
Vue.config.debug = true;
Vue.config.silent = false;
Vue.config.async = 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({
el:'#app',
components: {
FileUpload:FileUpload,
},
data: {
accept: 'image/*',
size: 1024 * 1024 * 10,
multiple: true,
extensions: 'gif,jpg,png',
// extensions: ['gif','jpg','png'],
// extensions: /\.(gif|png|jpg)$/i,
files: [],
upload: {},
drop: true,
auto: false,
},
compiled() {
this.upload = this.$refs.upload;
this.upload.request = {
headers: {
"X-Csrf-Token": "xxxx",
},
data: {
"_csrf_token": "xxxxxx",
},
};
this.files = this.$refs.upload.files;
},
methods: {
remove(file) {
this.$refs.upload.files.$remove(file);
},
},
events: {
addFileUpload(file, component) {
console.log('addFileUpload');
if (this.auto) {
component.active = true;
}
},
fileUploadProgress(file, component) {
console.log('fileUploadProgress ' + file.progress);
},
afterFileUpload(file, component) {
console.log('afterFileUpload');
},
beforeFileUpload(file, component) {
console.log('beforeFileUpload');
},
}
});

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

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

@ -0,0 +1,59 @@
const CHUNK_SIZE = 1048576
const ChunkActiveUploads = {}
const chunkUploadStart = (req, res) => {
const uuid = Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1)
ChunkActiveUploads[uuid] = {}
return res.json({
status: 'success',
data: {
session_id: uuid,
start_offset: 0,
end_offset: CHUNK_SIZE
}
})
}
const chunkUploadPart = (req, res) => {
setTimeout(() => {
const rand = Math.random()
if (rand <= 0.25) {
res.status(500)
res.json({ status: 'error', error: 'server' })
} else {
res.send({ status: 'success' })
}
}, 100 + parseInt(Math.random() * 2000, 10))
}
const chunkUploadFinish = (req, res) => {
setTimeout(() => {
const rand = Math.random()
if (rand <= 0.25) {
res.status(500)
res.json({ status: 'error', error: 'server' })
} else {
res.send({ status: 'success' })
}
}, 100 + parseInt(Math.random() * 2000, 10))
}
module.exports = (req, res) => {
if (!req.body.phase) {
return chunkUploadPart(req, res)
}
switch (req.body.phase) {
case 'start':
return chunkUploadStart(req, res)
case 'upload':
return chunkUploadPart(req, res)
case 'finish':
return chunkUploadFinish(req, res)
}
}

@ -0,0 +1,87 @@
/**
* Creates a XHR request
*
* @param {Object} options
*/
export const createRequest = (options) => {
const xhr = new XMLHttpRequest()
xhr.open(options.method || 'GET', options.url)
xhr.responseType = 'json'
if (options.headers) {
Object.keys(options.headers).forEach(key => {
xhr.setRequestHeader(key, options.headers[key])
})
}
return xhr
}
/**
* Sends a XHR request with certain body
*
* @param {XMLHttpRequest} xhr
* @param {Object} body
*/
export const sendRequest = (xhr, body) => {
return new Promise((resolve, reject) => {
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) {
var response
try {
response = JSON.parse(xhr.response)
} catch (err) {
response = xhr.response
}
resolve(response)
} else {
reject(xhr.response)
}
}
xhr.onerror = () => reject(xhr.response)
xhr.send(JSON.stringify(body))
})
}
/**
* Sends a XHR request with certain form data
*
* @param {XMLHttpRequest} xhr
* @param {Object} data
*/
export const sendFormRequest = (xhr, data) => {
const body = new FormData()
for (var name in data) {
body.append(name, data[name])
}
return new Promise((resolve, reject) => {
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) {
var response
try {
response = JSON.parse(xhr.response)
} catch (err) {
response = xhr.response
}
resolve(response)
} else {
reject(xhr.response)
}
}
xhr.onerror = () => reject(xhr.response)
xhr.send(body)
})
}
/**
* Creates and sends XHR request
*
* @param {Object} options
*
* @returns Promise
*/
export default function (options) {
const xhr = createRequest(options)
return sendRequest(xhr, options.body)
}

@ -1,12 +0,0 @@
var webpack = require('webpack');
module.exports = require('./webpack.config.js');
module.exports.entry = {
'vue-upload-component': './src/main.js',
}
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,66 +1,247 @@
var path = require('path')
var webpack = require('webpack')
const path = require('path')
const merge = require('webpack-merge')
const webpack = require('webpack')
module.exports = {
entry: {
example: ['vue', './src/example.js'],
},
const packageInfo = require('./package')
const bodyParser = require('webpack-body-parser')
const chunkUpload = require('./src/utils/chunkUpload')
const { VueLoaderPlugin } = require('vue-loader')
process.env.NODE_ENV = process.env.NODE_ENV || 'production'
const isDev = process.env.NODE_ENV === 'development'
function baseConfig() {
let config = {
mode: process.env.NODE_ENV === 'development' ? 'development' : 'production',
output: {
path: './dist',
publicPath: '/dist/',
filename: "[name].js",
path: path.join(__dirname, 'dist'),
publicPath: '/dist',
filename: '[name].js',
chunkFilename: '[chunkhash:8].[name].chunk.js',
},
resolveLoader: {
root: path.join(__dirname, 'node_modules'),
},
module: {
loaders: [
{
test: /\.vue$/,
loader: 'vue'
},
{
test: /\.js$/,
loader: 'babel',
exclude: /node_modules/
},
{
test: /\.json$/,
loader: 'json'
},
{
test: /\.html$/,
loader: 'vue-html'
resolve: {
modules: [
path.join(__dirname, 'node_modules'),
],
alias: {
'vue-upload-component': path.join(__dirname, 'src'),
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: 'url',
query: {
limit: 10000,
name: '[name].[ext]?[hash]'
extensions: [
'.js',
'.jsx',
'.json',
'.vue',
'.md',
],
},
externals: {
vue: 'Vue',
vuex: 'Vuex',
'vue-router': 'VueRouter',
'vue-i18n': 'VueI18n',
'marked': 'marked',
'highlight.js': 'hljs',
'cropperjs': 'Cropper',
'@xkeshi/image-compressor': 'ImageCompressor',
},
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: /\.css$/,
use: [
{
loader: 'vue-style-loader',
},
{
loader: 'css-loader',
options: {
minimize: !isDev,
},
},
]
},
{
test: /\.vue$/,
use: [
{
loader: 'vue-loader',
options: {
preserveWhitespace: false,
cssModules: {
localIdentName: '[hash:base64:8]',
camelCase: true,
},
},
},
],
},
/*
{
test: /\.vue$/,
use: [
{
loader: 'vue-loader',
options: {
preserveWhitespace: false,
loaders: {
js: [
{
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',
},
]
}
}
]
*/
]
},
plugins: [
new webpack.BannerPlugin(`Name: ${packageInfo.name}\nVersion: ${packageInfo.version}\nAuthor: ${packageInfo.author}`),
new VueLoaderPlugin(),
],
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'),
],
},
devServer: {
historyApiFallback: true,
noInfo: true
output: {
path: path.join(__dirname, 'docs/dist'),
},
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"'
devServer: {
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({ url: 'https://vuejs.org/images/logo.png?id=' + id, name: 'filename.ext', id: id++, success: true })
}
}, 200 + parseInt(Math.random() * 4000, 10))
}
let del = function (req, res) {
res.json({ success: true })
}
}),
new webpack.optimize.OccurenceOrderPlugin()
])
}
// Chunk upload
app.post('/upload/chunk', bodyParser.json(), chunkUpload)
app.post('/upload/post', put)
app.put('/upload/put', put)
app.post('/upload/delete', del)
app.delete('/upload/delete', del)
},
// host: '0.0.0.0',
hot: true,
contentBase: path.join(__dirname, 'docs'),
clientLogLevel: 'error',
noInfo: true,
publicPath: '/dist',
inline: true,
historyApiFallback: true,
overlay: {
warnings: true,
errors: true
},
// host: '172.16.23.1'
},
})

Loading…
Cancel
Save