tremble 4 years ago
parent
commit
c7e1c95461
100 changed files with 17878 additions and 3 deletions
  1. 25 0
      .gitignore
  2. 1 2
      backstage/package.json
  3. 1 1
      backstage/src/pages/content/Dynamic.vue
  4. 404 0
      backstage/src/pages/content/Upload.vue
  5. 1 0
      backstage/src/pages/layout/aside.vue
  6. 7 0
      backstage/src/router/index.js
  7. 43 0
      node_modules/_simple-uploader.js@0.5.6@simple-uploader.js/LICENSE
  8. 267 0
      node_modules/_simple-uploader.js@0.5.6@simple-uploader.js/README.md
  9. BIN
      node_modules/_simple-uploader.js@0.5.6@simple-uploader.js/assets/simple-uploader-QQ-2.png
  10. BIN
      node_modules/_simple-uploader.js@0.5.6@simple-uploader.js/assets/simple-uploader-QQ.jpg
  11. 1610 0
      node_modules/_simple-uploader.js@0.5.6@simple-uploader.js/dist/uploader.js
  12. 9 0
      node_modules/_simple-uploader.js@0.5.6@simple-uploader.js/dist/uploader.min.js
  13. 70 0
      node_modules/_simple-uploader.js@0.5.6@simple-uploader.js/package.json
  14. 315 0
      node_modules/_simple-uploader.js@0.5.6@simple-uploader.js/src/chunk.js
  15. 49 0
      node_modules/_simple-uploader.js@0.5.6@simple-uploader.js/src/event.js
  16. 541 0
      node_modules/_simple-uploader.js@0.5.6@simple-uploader.js/src/file.js
  17. 514 0
      node_modules/_simple-uploader.js@0.5.6@simple-uploader.js/src/uploader.js
  18. 171 0
      node_modules/_simple-uploader.js@0.5.6@simple-uploader.js/src/utils.js
  19. 14 0
      node_modules/_simple-uploader.js@0.5.6@simple-uploader.js/test.sh
  20. 18 0
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/.babelrc
  21. 9 0
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/.editorconfig
  22. 2 0
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/.eslintignore
  23. 27 0
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/.eslintrc.js
  24. 8 0
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/.postcssrc.js
  25. 21 0
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/LICENSE
  26. 400 0
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/README.md
  27. 405 0
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/README_zh-CN.md
  28. 2 0
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/dist/vue-uploader.js
  29. 1 0
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/dist/vue-uploader.js.map
  30. 59 0
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/example/App.vue
  31. 11 0
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/example/index.html
  32. 12 0
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/example/main.js
  33. BIN
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/example/simple-uploader.gif
  34. 43 0
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/node_modules/simple-uploader.js/LICENSE
  35. 267 0
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/node_modules/simple-uploader.js/README.md
  36. BIN
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/node_modules/simple-uploader.js/assets/simple-uploader-QQ-2.png
  37. BIN
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/node_modules/simple-uploader.js/assets/simple-uploader-QQ.jpg
  38. 1610 0
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/node_modules/simple-uploader.js/dist/uploader.js
  39. 9 0
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/node_modules/simple-uploader.js/dist/uploader.min.js
  40. 70 0
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/node_modules/simple-uploader.js/package.json
  41. 315 0
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/node_modules/simple-uploader.js/src/chunk.js
  42. 49 0
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/node_modules/simple-uploader.js/src/event.js
  43. 541 0
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/node_modules/simple-uploader.js/src/file.js
  44. 514 0
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/node_modules/simple-uploader.js/src/uploader.js
  45. 171 0
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/node_modules/simple-uploader.js/src/utils.js
  46. 14 0
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/node_modules/simple-uploader.js/test.sh
  47. 86 0
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/package.json
  48. 3 0
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/src/common/file-events.js
  49. 14 0
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/src/common/mixins.js
  50. 27 0
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/src/common/utils.js
  51. 56 0
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/src/components/btn.vue
  52. 64 0
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/src/components/drop.vue
  53. 441 0
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/src/components/file.vue
  54. 42 0
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/src/components/files.vue
  55. 42 0
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/src/components/list.vue
  56. 28 0
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/src/components/unsupport.vue
  57. 150 0
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/src/components/uploader.vue
  58. 38 0
      node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/src/index.js
  59. 43 0
      node_modules/simple-uploader.js/LICENSE
  60. 267 0
      node_modules/simple-uploader.js/README.md
  61. BIN
      node_modules/simple-uploader.js/assets/simple-uploader-QQ-2.png
  62. BIN
      node_modules/simple-uploader.js/assets/simple-uploader-QQ.jpg
  63. 1610 0
      node_modules/simple-uploader.js/dist/uploader.js
  64. 9 0
      node_modules/simple-uploader.js/dist/uploader.min.js
  65. 70 0
      node_modules/simple-uploader.js/package.json
  66. 315 0
      node_modules/simple-uploader.js/src/chunk.js
  67. 49 0
      node_modules/simple-uploader.js/src/event.js
  68. 541 0
      node_modules/simple-uploader.js/src/file.js
  69. 514 0
      node_modules/simple-uploader.js/src/uploader.js
  70. 171 0
      node_modules/simple-uploader.js/src/utils.js
  71. 14 0
      node_modules/simple-uploader.js/test.sh
  72. 18 0
      node_modules/vue-simple-uploader/.babelrc
  73. 9 0
      node_modules/vue-simple-uploader/.editorconfig
  74. 2 0
      node_modules/vue-simple-uploader/.eslintignore
  75. 27 0
      node_modules/vue-simple-uploader/.eslintrc.js
  76. 8 0
      node_modules/vue-simple-uploader/.postcssrc.js
  77. 21 0
      node_modules/vue-simple-uploader/LICENSE
  78. 400 0
      node_modules/vue-simple-uploader/README.md
  79. 405 0
      node_modules/vue-simple-uploader/README_zh-CN.md
  80. 2 0
      node_modules/vue-simple-uploader/dist/vue-uploader.js
  81. 1 0
      node_modules/vue-simple-uploader/dist/vue-uploader.js.map
  82. 59 0
      node_modules/vue-simple-uploader/example/App.vue
  83. 11 0
      node_modules/vue-simple-uploader/example/index.html
  84. 12 0
      node_modules/vue-simple-uploader/example/main.js
  85. BIN
      node_modules/vue-simple-uploader/example/simple-uploader.gif
  86. 43 0
      node_modules/vue-simple-uploader/node_modules/simple-uploader.js/LICENSE
  87. 267 0
      node_modules/vue-simple-uploader/node_modules/simple-uploader.js/README.md
  88. BIN
      node_modules/vue-simple-uploader/node_modules/simple-uploader.js/assets/simple-uploader-QQ-2.png
  89. BIN
      node_modules/vue-simple-uploader/node_modules/simple-uploader.js/assets/simple-uploader-QQ.jpg
  90. 1610 0
      node_modules/vue-simple-uploader/node_modules/simple-uploader.js/dist/uploader.js
  91. 9 0
      node_modules/vue-simple-uploader/node_modules/simple-uploader.js/dist/uploader.min.js
  92. 70 0
      node_modules/vue-simple-uploader/node_modules/simple-uploader.js/package.json
  93. 315 0
      node_modules/vue-simple-uploader/node_modules/simple-uploader.js/src/chunk.js
  94. 49 0
      node_modules/vue-simple-uploader/node_modules/simple-uploader.js/src/event.js
  95. 541 0
      node_modules/vue-simple-uploader/node_modules/simple-uploader.js/src/file.js
  96. 514 0
      node_modules/vue-simple-uploader/node_modules/simple-uploader.js/src/uploader.js
  97. 171 0
      node_modules/vue-simple-uploader/node_modules/simple-uploader.js/src/utils.js
  98. 14 0
      node_modules/vue-simple-uploader/node_modules/simple-uploader.js/test.sh
  99. 86 0
      node_modules/vue-simple-uploader/package.json
  100. 0 0
      node_modules/vue-simple-uploader/src/common/file-events.js

+ 25 - 0
.gitignore

@@ -0,0 +1,25 @@
+.DS_Store
+node_modules
+/dist
+public/data
+
+
+
+# local env files
+.env.local
+.env.*.local
+
+# Log files
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+
+# Editor directories and files
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?

+ 1 - 2
backstage/package.json

@@ -13,8 +13,7 @@
     "element-ui": "^2.13.2",
     "js-base64": "^2.6.2",
     "vue": "^2.6.11",
-    "vue-router": "^3.1.6",
-    "vue2-editor": "^2.10.2"
+    "vue-router": "^3.1.6"
   },
   "devDependencies": {
     "@vue/cli-plugin-babel": "~4.3.0",

+ 1 - 1
backstage/src/pages/content/Dynamic.vue

@@ -350,7 +350,7 @@ export default {
     },
 
     gotoEdit(item) {
-      window.open(`/edit-backstage/edit.html?m=${item.sceneCode}`, "_blank");
+      window.open(`/edit-backstage2/edit.html?m=${item.sceneCode}`, "_blank");
     },
 
     del(item) {

+ 404 - 0
backstage/src/pages/content/Upload.vue

@@ -0,0 +1,404 @@
+<!--  -->
+<template>
+  <div>
+    <main-top :crumb="crumbData"></main-top>
+    <div class="table-interface">
+      <div class="top-body">
+        <div class="info-top">
+          <div class="info-left">
+            <el-input class="elInput" :maxlength="50"
+                show-word-limit v-model="inputKey" placeholder="请输入关键字"></el-input>
+            <el-button type="primary" @click="getInformation">查询</el-button>
+            <el-button @click="reset">重置</el-button>
+          </div>
+          <div class="info-right"></div>
+        </div>
+        <el-table :data="tableData" height="68vh" class="collection-con" style="width: 100%">
+          <el-table-column
+            v-for="(item, idx) in data"
+            :key="idx"
+            :prop="item.prop"
+            :label="item.label"
+          >
+            <template slot-scope="scope" >
+              <span v-html="scope.row[item.prop]||'-'"></span>
+            </template>
+          </el-table-column>
+          <el-table-column label="操作">
+            <template slot-scope="scope" >
+              <span class="o-span" @click="dowload(scope.row)">下载附件</span>
+              <span class="o-span" @click="del(scope.row)">删除</span>
+            </template>
+          </el-table-column>
+        </el-table>
+        <div class="e-pagination">
+          <el-pagination
+            @current-change="handleCurrentChange"
+            :current-page.sync="currentPage"
+            @size-change="handleSizeChange"
+            :page-size="size"
+            :page-sizes="PAGESIZES"
+            layout="total, sizes, prev, pager, next, jumper"
+            :total="total"
+          ></el-pagination>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+
+import MainTop from '@/components/main-top'
+const crumbData = [
+  {
+    name: '用户上传管理',
+    id: 4
+  }
+]
+
+
+let data = [
+      {
+        prop: "idx",
+        label: "编号"
+      },
+      {
+        prop: "description",
+        label: "史料说明"
+      },
+      {
+        prop: "phone",
+        label: "联系方式"
+      },
+      {
+        prop: "updateTime",
+        label: "修改时间"
+      }
+    ];
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  components: {
+    MainTop,
+  },
+  data () {
+    return {
+      data,
+      crumbData,
+      tableData: [],
+      currentPage: 1,
+      size: 25,
+      total: 0,
+      loading: false,
+      inputKey:''
+    }
+  },
+  watch: {
+    currentPage () {
+      this.refresh()
+    },
+    size () {
+      this.refresh()
+    }
+  },
+  mounted () {
+    this.refresh()
+  },
+ 
+  methods: {
+    reset(){
+      this.inputKey=''
+      this.time=''
+      this.type=''
+      this.refresh()
+    },
+    del(item) {
+      this.$confirm("删除后,文件将无法恢复,是否继续?", "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning",
+      })
+        .then(() => {
+          this.$http
+            .get(`/manage/material/remove/${item.id}`, {
+              headers: {
+                token: window.localStorage.getItem("token"),
+              },
+            })
+            .then((res) => {
+              if (res.code === 0) {
+                this.$alert("删除成功", "提示", {
+                  confirmButtonText: "确定",
+                  callback: () => {
+                    this.refresh();
+                  },
+                });
+              } else {
+                this.$notify.error({
+                  title: "错误",
+                  message: res.msg,
+                });
+              }
+            });
+        })
+        .catch(() => {
+          this.$message({
+            type: "info",
+            message: "已取消删除",
+          });
+        });
+    },
+    _browserDownload (url) {
+      let a = document.createElement('a')
+      let urlArr = url.split('/')
+      let fileName = urlArr[urlArr.length - 1]
+      a.href = url
+      a.download = fileName
+      a.style.display = 'none'
+      document.body.appendChild(a)
+      a.click()
+      document.body.removeChild(a)
+    },
+    async dowload(item){
+      let result = await this.$http({
+            method: 'get',
+            headers: {
+              token: window.localStorage.getItem('token')
+            },
+            url: `/manage/material/download/${item.id}`
+        })
+        if (result.code != 0) {
+          return this.$alert("获取下载链接失败", "提示", {
+            confirmButtonText: "确定",
+            callback: () => {
+              this.refresh();
+            },
+          });
+        }
+       this._browserDownload(result.data)
+    },
+    refresh () {
+      this.loading = true
+      this.getInformation()
+      this.loading = false
+    },
+    handleCurrentChange (val) {
+      this.currentPage = val
+    },
+    handleSizeChange(val){
+      this.size = val
+    },
+    async getInformation () {
+      let params = {
+          pageNum:this.currentPage,
+          pageSize: this.size,
+          searchKey: this.inputKey
+      }
+
+      let result = await this.$http({
+        method: 'post',
+        data: params,
+        headers: {
+          token: window.localStorage.getItem('token')
+        },
+        url: '/manage/material/list'
+      })
+
+      if (result.code !== 0) {
+        return
+      }
+      this.tableData = result.data.list
+      this.total = result.data.total
+      this.tableData.forEach((item, i) => {
+        item["idx"] = i + 1;
+      });
+    }
+  }
+}
+</script>
+
+
+<style lang="less" scoped>
+.top-body{
+  border-top: .0625rem solid #e6e6e6;
+  line-height: 1.5;
+  padding: 0 1.25rem 1.25rem;
+  align-items: center;
+  box-sizing: border-box;
+  background: #fff;
+  margin: 1rem 0;
+}
+
+.top-body .top-con{
+  font-weight: bold;
+}
+.table-title{
+  padding: 1rem 1rem 1rem 0 ;
+  font-size: 1rem;
+  color: #2d2d2d;
+  border-bottom: .0625rem solid #ccc;
+  display: flex;
+  justify-content: space-between;
+}
+
+.more{
+  color: #ec652d;
+  cursor: pointer;
+}
+
+.top-right{
+  background-color: #ec652d;
+  height: 3.5625rem;
+  line-height: 3.5625rem;
+  color: #fff;
+  padding: 0 1.875rem;
+  border-radius: .3125rem;
+  font-size: 1.125rem;
+  font-weight: bold;
+}
+
+.search-body{
+  background-color: #fff;
+  margin: 1.25rem 0;
+}
+
+.interface-table{
+  height: calc(100% - 17.1875rem);
+  overflow-x: hidden;
+  background-color: #fff;
+  padding: 0 1.125rem 2.375rem;
+  box-sizing: border-box;
+}
+
+.zan-con{
+  display: flex;
+  width: 100%;
+  justify-content: space-around;
+  margin-top: 1.5rem;
+  text-align: center;
+  font-size: 1.125rem;
+}
+
+.zan-con .line{
+  height: 8rem;
+  width: .0625rem;
+  background: #ccc;
+}
+
+.zan-con .zan-contain{
+  display: flex;
+  justify-content: space-between;
+  flex-direction: column;
+}
+
+.zan-con .zan-contain p{
+  font-size: 2.25rem;
+}
+
+.zan-con .zan-contain p:first-child{
+  font-size: .875rem;
+  line-height: 1.5;
+}
+
+.zan-sub{
+  text-align: right;
+  margin-top: 1.25rem;
+  color: #a5a5a5;
+  font-size: .875rem;
+}
+
+.table-interface{
+  height: calc(100% - 3rem);
+}
+
+
+.info-top{
+  padding: 1.25rem 0;
+  display: flex;
+  justify-content: space-between;
+  border-bottom: .0625rem #a5a5a5 solid;
+}
+
+.o-span{
+  cursor: pointer;
+  color: rgb(7, 152, 244);
+}
+
+.collection-con{
+  width: 100%;
+  overflow-x: hidden;
+  overflow-y: auto;
+  height: 68vh;
+}
+
+
+
+.collection-con ul{
+  display: flex;
+  flex-wrap: wrap;
+  margin-top: 1.25rem;
+}
+
+.collection-con ul li {
+  width: 24%;
+  margin-right: 1%;
+  cursor: pointer;
+  font-size: .8rem;
+  color: #2d2d2d;
+  margin-bottom: 1.25rem;
+}
+
+.collection-con ul .li-img {
+  position: relative;
+  
+}
+.collection-con ul .li-img div{
+  position: absolute;
+  left: .625rem;
+  bottom: .625rem;
+  color: #fff;
+}
+
+.collection-con ul .li-img div span{
+  margin-right: .625rem;
+}
+
+
+.collection-con ul img {
+  width: 100%;
+}
+
+.collection-con{
+  .li-txt,.li-name{
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+  }
+  .li-name{
+    margin-top: .5rem;
+    div{
+      font-size: 1rem;
+      font-weight: bold;
+      color: #532F1C;
+      &:last-of-type{
+        color: #707070;
+        font-size: .875rem;
+        font-weight: normal;
+        span{
+          margin-left: .30rem;
+          &:last-of-type{
+            &:hover{
+              color: #409EFF;
+            }
+          }
+          &:hover{
+            color: #c56351;
+          }
+        }
+      }
+    }
+  }
+}
+</style>

+ 1 - 0
backstage/src/pages/layout/aside.vue

@@ -6,6 +6,7 @@
       <div :class="{active:activeIdx>=2&&activeIdx<=6}"><i class="iconfont iconsys_nav_work"></i>内容管理</div>
       <div @click="go(2,'/dynamic')" :class="{activeFont:activeIdx === 2}">场景管理</div>
       <div @click="go(3,'/spirit')" :class="{activeFont:activeIdx === 3}">文物管理</div>
+      <div @click="go(4,'/upload')" :class="{activeFont:activeIdx === 4}">用户上传</div>
     </div>
     <div class="aside-item">
       <div :class="{active:activeIdx>=7&&activeIdx<=11}"><i class="iconfont iconsys_nav_system"></i>系统管理</div>

+ 7 - 0
backstage/src/router/index.js

@@ -62,6 +62,13 @@ const routes = [
         component:  () => import( "../pages/system/Menu.vue"),
         meta: {index: 7,role:'sys_admin'}
       },
+
+      {
+        path:'/upload',
+        name: 'upload',
+        component:  () => import( "../pages/content/Upload.vue"),
+        meta: {index: 4,role:'sys_admin'}
+      },
       {
         path:'/parameter',
         name: 'parameter',

+ 43 - 0
node_modules/_simple-uploader.js@0.5.6@simple-uploader.js/LICENSE

@@ -0,0 +1,43 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 doly mood
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+The MIT License (MIT)
+
+Copyright (c) 2011, 23, http://www.23developer.com
+              2013, Aidas Klimas
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

File diff suppressed because it is too large
+ 267 - 0
node_modules/_simple-uploader.js@0.5.6@simple-uploader.js/README.md


BIN
node_modules/_simple-uploader.js@0.5.6@simple-uploader.js/assets/simple-uploader-QQ-2.png


BIN
node_modules/_simple-uploader.js@0.5.6@simple-uploader.js/assets/simple-uploader-QQ.jpg


File diff suppressed because it is too large
+ 1610 - 0
node_modules/_simple-uploader.js@0.5.6@simple-uploader.js/dist/uploader.js


File diff suppressed because it is too large
+ 9 - 0
node_modules/_simple-uploader.js@0.5.6@simple-uploader.js/dist/uploader.min.js


+ 70 - 0
node_modules/_simple-uploader.js@0.5.6@simple-uploader.js/package.json

@@ -0,0 +1,70 @@
+{
+  "name": "simple-uploader.js",
+  "version": "0.5.6",
+  "author": "dolymood <dolymood@gmail.com>",
+  "license": "MIT",
+  "description": "Uploader library implements html5 file upload and provides multiple simultaneous, stable, fault tolerant and resumable uploads",
+  "keywords": [
+    "simple-uploader",
+    "simple-uploader.js",
+    "uploader",
+    "uploader.js",
+    "resumable.js",
+    "flow.js",
+    "file upload",
+    "resumable upload",
+    "chunk upload",
+    "html5 upload",
+    "javascript upload",
+    "upload"
+  ],
+  "main": "src/uploader.js",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/simple-uploader/Uploader.git"
+  },
+  "bugs": "https://github.com/simple-uploader/Uploader/issues",
+  "homepage": "https://github.com/simple-uploader/Uploader",
+  "scripts": {
+    "dev": "gulp watch",
+    "build": "gulp build",
+    "codecov": "codecov",
+    "test": "gulp",
+    "test:unit": "gulp test",
+    "test:cover": "gulp cover",
+    "test:ci": "gulp ci",
+    "release": "gulp release"
+  },
+  "devDependencies": {
+    "browserify-versionify": "^1.0.6",
+    "codecov": "^3.6.5",
+    "eslint": "^4.2.0",
+    "eslint-config-standard": "^6.1.0",
+    "eslint-plugin-promise": "^3.5.0",
+    "eslint-plugin-standard": "^3.0.1",
+    "gulp": "^3.9.1",
+    "gulp-browserify": "^0.5.1",
+    "gulp-concat": "^2.6.1",
+    "gulp-eslint": "^4.0.0",
+    "gulp-git": "^2.4.1",
+    "gulp-header": "^1.8.8",
+    "gulp-sourcemaps": "^2.6.0",
+    "gulp-tag-version": "^1.3.0",
+    "gulp-uglify": "^3.0.0",
+    "jasmine": "^2.6.0",
+    "jasmine-core": "^2.6.4",
+    "karma": "^1.7.0",
+    "karma-chrome-launcher": "^2.1.1",
+    "karma-commonjs": "^1.0.0",
+    "karma-coverage": "^1.1.1",
+    "karma-firefox-launcher": "^1.0.1",
+    "karma-jasmine": "^1.1.0",
+    "karma-phantomjs-launcher": "^1.0.4",
+    "karma-sauce-launcher": "^1.1.0",
+    "pump": "^1.0.2",
+    "sinon": "1.7.3"
+  },
+  "__npminstall_done": "Wed Jun 02 2021 14:28:26 GMT+0800 (中国标准时间)",
+  "_from": "simple-uploader.js@0.5.6",
+  "_resolved": "https://registry.npm.taobao.org/simple-uploader.js/download/simple-uploader.js-0.5.6.tgz"
+}

+ 315 - 0
node_modules/_simple-uploader.js@0.5.6@simple-uploader.js/src/chunk.js

@@ -0,0 +1,315 @@
+var utils = require('./utils')
+
+function Chunk (uploader, file, offset) {
+  utils.defineNonEnumerable(this, 'uploader', uploader)
+  utils.defineNonEnumerable(this, 'file', file)
+  utils.defineNonEnumerable(this, 'bytes', null)
+  this.offset = offset
+  this.tested = false
+  this.retries = 0
+  this.pendingRetry = false
+  this.preprocessState = 0
+  this.readState = 0
+  this.loaded = 0
+  this.total = 0
+  this.chunkSize = this.uploader.opts.chunkSize
+  this.startByte = this.offset * this.chunkSize
+  this.endByte = this.computeEndByte()
+  this.xhr = null
+}
+
+var STATUS = Chunk.STATUS = {
+  PENDING: 'pending',
+  UPLOADING: 'uploading',
+  READING: 'reading',
+  SUCCESS: 'success',
+  ERROR: 'error',
+  COMPLETE: 'complete',
+  PROGRESS: 'progress',
+  RETRY: 'retry'
+}
+
+utils.extend(Chunk.prototype, {
+
+  _event: function (evt, args) {
+    args = utils.toArray(arguments)
+    args.unshift(this)
+    this.file._chunkEvent.apply(this.file, args)
+  },
+
+  computeEndByte: function () {
+    var endByte = Math.min(this.file.size, (this.offset + 1) * this.chunkSize)
+    if (this.file.size - endByte < this.chunkSize && !this.uploader.opts.forceChunkSize) {
+      // The last chunk will be bigger than the chunk size,
+      // but less than 2 * this.chunkSize
+      endByte = this.file.size
+    }
+    return endByte
+  },
+
+  getParams: function () {
+    return {
+      chunkNumber: this.offset + 1,
+      chunkSize: this.uploader.opts.chunkSize,
+      currentChunkSize: this.endByte - this.startByte,
+      totalSize: this.file.size,
+      identifier: this.file.uniqueIdentifier,
+      filename: this.file.name,
+      relativePath: this.file.relativePath,
+      totalChunks: this.file.chunks.length
+    }
+  },
+
+  getTarget: function (target, params) {
+    if (!params.length) {
+      return target
+    }
+    if (target.indexOf('?') < 0) {
+      target += '?'
+    } else {
+      target += '&'
+    }
+    return target + params.join('&')
+  },
+
+  test: function () {
+    this.xhr = new XMLHttpRequest()
+    this.xhr.addEventListener('load', testHandler, false)
+    this.xhr.addEventListener('error', testHandler, false)
+    var testMethod = utils.evalOpts(this.uploader.opts.testMethod, this.file, this)
+    var data = this.prepareXhrRequest(testMethod, true)
+    this.xhr.send(data)
+
+    var $ = this
+    function testHandler (event) {
+      var status = $.status(true)
+      if (status === STATUS.ERROR) {
+        $._event(status, $.message())
+        $.uploader.uploadNextChunk()
+      } else if (status === STATUS.SUCCESS) {
+        $._event(status, $.message())
+        $.tested = true
+      } else if (!$.file.paused) {
+        // Error might be caused by file pause method
+        // Chunks does not exist on the server side
+        $.tested = true
+        $.send()
+      }
+    }
+  },
+
+  preprocessFinished: function () {
+    // Compute the endByte after the preprocess function to allow an
+    // implementer of preprocess to set the fileObj size
+    this.endByte = this.computeEndByte()
+    this.preprocessState = 2
+    this.send()
+  },
+
+  readFinished: function (bytes) {
+    this.readState = 2
+    this.bytes = bytes
+    this.send()
+  },
+
+  send: function () {
+    var preprocess = this.uploader.opts.preprocess
+    var read = this.uploader.opts.readFileFn
+    if (utils.isFunction(preprocess)) {
+      switch (this.preprocessState) {
+        case 0:
+          this.preprocessState = 1
+          preprocess(this)
+          return
+        case 1:
+          return
+      }
+    }
+    switch (this.readState) {
+      case 0:
+        this.readState = 1
+        read(this.file, this.file.fileType, this.startByte, this.endByte, this)
+        return
+      case 1:
+        return
+    }
+    if (this.uploader.opts.testChunks && !this.tested) {
+      this.test()
+      return
+    }
+
+    this.loaded = 0
+    this.total = 0
+    this.pendingRetry = false
+
+    // Set up request and listen for event
+    this.xhr = new XMLHttpRequest()
+    this.xhr.upload.addEventListener('progress', progressHandler, false)
+    this.xhr.addEventListener('load', doneHandler, false)
+    this.xhr.addEventListener('error', doneHandler, false)
+
+    var uploadMethod = utils.evalOpts(this.uploader.opts.uploadMethod, this.file, this)
+    var data = this.prepareXhrRequest(uploadMethod, false, this.uploader.opts.method, this.bytes)
+    this.xhr.send(data)
+
+    var $ = this
+    function progressHandler (event) {
+      if (event.lengthComputable) {
+        $.loaded = event.loaded
+        $.total = event.total
+      }
+      $._event(STATUS.PROGRESS, event)
+    }
+
+    function doneHandler (event) {
+      var msg = $.message()
+      $.processingResponse = true
+      $.uploader.opts.processResponse(msg, function (err, res) {
+        $.processingResponse = false
+        if (!$.xhr) {
+          return
+        }
+        $.processedState = {
+          err: err,
+          res: res
+        }
+        var status = $.status()
+        if (status === STATUS.SUCCESS || status === STATUS.ERROR) {
+          // delete this.data
+          $._event(status, res)
+          status === STATUS.ERROR && $.uploader.uploadNextChunk()
+        } else {
+          $._event(STATUS.RETRY, res)
+          $.pendingRetry = true
+          $.abort()
+          $.retries++
+          var retryInterval = $.uploader.opts.chunkRetryInterval
+          if (retryInterval !== null) {
+            setTimeout(function () {
+              $.send()
+            }, retryInterval)
+          } else {
+            $.send()
+          }
+        }
+      }, $.file, $)
+    }
+  },
+
+  abort: function () {
+    var xhr = this.xhr
+    this.xhr = null
+    this.processingResponse = false
+    this.processedState = null
+    if (xhr) {
+      xhr.abort()
+    }
+  },
+
+  status: function (isTest) {
+    if (this.readState === 1) {
+      return STATUS.READING
+    } else if (this.pendingRetry || this.preprocessState === 1) {
+      // if pending retry then that's effectively the same as actively uploading,
+      // there might just be a slight delay before the retry starts
+      return STATUS.UPLOADING
+    } else if (!this.xhr) {
+      return STATUS.PENDING
+    } else if (this.xhr.readyState < 4 || this.processingResponse) {
+      // Status is really 'OPENED', 'HEADERS_RECEIVED'
+      // or 'LOADING' - meaning that stuff is happening
+      return STATUS.UPLOADING
+    } else {
+      var _status
+      if (this.uploader.opts.successStatuses.indexOf(this.xhr.status) > -1) {
+        // HTTP 200, perfect
+        // HTTP 202 Accepted - The request has been accepted for processing, but the processing has not been completed.
+        _status = STATUS.SUCCESS
+      } else if (this.uploader.opts.permanentErrors.indexOf(this.xhr.status) > -1 ||
+          !isTest && this.retries >= this.uploader.opts.maxChunkRetries) {
+        // HTTP 415/500/501, permanent error
+        _status = STATUS.ERROR
+      } else {
+        // this should never happen, but we'll reset and queue a retry
+        // a likely case for this would be 503 service unavailable
+        this.abort()
+        _status = STATUS.PENDING
+      }
+      var processedState = this.processedState
+      if (processedState && processedState.err) {
+        _status = STATUS.ERROR
+      }
+      return _status
+    }
+  },
+
+  message: function () {
+    return this.xhr ? this.xhr.responseText : ''
+  },
+
+  progress: function () {
+    if (this.pendingRetry) {
+      return 0
+    }
+    var s = this.status()
+    if (s === STATUS.SUCCESS || s === STATUS.ERROR) {
+      return 1
+    } else if (s === STATUS.PENDING) {
+      return 0
+    } else {
+      return this.total > 0 ? this.loaded / this.total : 0
+    }
+  },
+
+  sizeUploaded: function () {
+    var size = this.endByte - this.startByte
+    // can't return only chunk.loaded value, because it is bigger than chunk size
+    if (this.status() !== STATUS.SUCCESS) {
+      size = this.progress() * size
+    }
+    return size
+  },
+
+  prepareXhrRequest: function (method, isTest, paramsMethod, blob) {
+    // Add data from the query options
+    var query = utils.evalOpts(this.uploader.opts.query, this.file, this, isTest)
+    query = utils.extend(this.getParams(), query)
+
+    // processParams
+    query = this.uploader.opts.processParams(query, this.file, this, isTest)
+
+    var target = utils.evalOpts(this.uploader.opts.target, this.file, this, isTest)
+    var data = null
+    if (method === 'GET' || paramsMethod === 'octet') {
+      // Add data from the query options
+      var params = []
+      utils.each(query, function (v, k) {
+        params.push([encodeURIComponent(k), encodeURIComponent(v)].join('='))
+      })
+      target = this.getTarget(target, params)
+      data = blob || null
+    } else {
+      // Add data from the query options
+      data = new FormData()
+      utils.each(query, function (v, k) {
+        data.append(k, v)
+      })
+      if (typeof blob !== 'undefined') {
+        data.append(this.uploader.opts.fileParameterName, blob, this.file.name)
+      }
+    }
+
+    this.xhr.open(method, target, true)
+    this.xhr.withCredentials = this.uploader.opts.withCredentials
+
+    // Add data from header options
+    utils.each(utils.evalOpts(this.uploader.opts.headers, this.file, this, isTest), function (v, k) {
+      this.xhr.setRequestHeader(k, v)
+    }, this)
+
+    return data
+  }
+
+})
+
+module.exports = Chunk

+ 49 - 0
node_modules/_simple-uploader.js@0.5.6@simple-uploader.js/src/event.js

@@ -0,0 +1,49 @@
+var each = require('./utils').each
+
+var event = {
+
+  _eventData: null,
+
+  on: function (name, func) {
+    if (!this._eventData) this._eventData = {}
+    if (!this._eventData[name]) this._eventData[name] = []
+    var listened = false
+    each(this._eventData[name], function (fuc) {
+      if (fuc === func) {
+        listened = true
+        return false
+      }
+    })
+    if (!listened) {
+      this._eventData[name].push(func)
+    }
+  },
+
+  off: function (name, func) {
+    if (!this._eventData) this._eventData = {}
+    if (!this._eventData[name] || !this._eventData[name].length) return
+    if (func) {
+      each(this._eventData[name], function (fuc, i) {
+        if (fuc === func) {
+          this._eventData[name].splice(i, 1)
+          return false
+        }
+      }, this)
+    } else {
+      this._eventData[name] = []
+    }
+  },
+
+  trigger: function (name) {
+    if (!this._eventData) this._eventData = {}
+    if (!this._eventData[name]) return true
+    var args = this._eventData[name].slice.call(arguments, 1)
+    var preventDefault = false
+    each(this._eventData[name], function (fuc) {
+      preventDefault = fuc.apply(this, args) === false || preventDefault
+    }, this)
+    return !preventDefault
+  }
+}
+
+module.exports = event

+ 541 - 0
node_modules/_simple-uploader.js@0.5.6@simple-uploader.js/src/file.js

@@ -0,0 +1,541 @@
+var utils = require('./utils')
+var Chunk = require('./chunk')
+
+function File (uploader, file, parent) {
+  utils.defineNonEnumerable(this, 'uploader', uploader)
+  this.isRoot = this.isFolder = uploader === this
+  utils.defineNonEnumerable(this, 'parent', parent || null)
+  utils.defineNonEnumerable(this, 'files', [])
+  utils.defineNonEnumerable(this, 'fileList', [])
+  utils.defineNonEnumerable(this, 'chunks', [])
+  utils.defineNonEnumerable(this, '_errorFiles', [])
+  utils.defineNonEnumerable(this, 'file', null)
+  this.id = utils.uid()
+
+  if (this.isRoot || !file) {
+    this.file = null
+  } else {
+    if (utils.isString(file)) {
+      // folder
+      this.isFolder = true
+      this.file = null
+      this.path = file
+      if (this.parent.path) {
+        file = file.substr(this.parent.path.length)
+      }
+      this.name = file.charAt(file.length - 1) === '/' ? file.substr(0, file.length - 1) : file
+    } else {
+      this.file = file
+      this.fileType = this.file.type
+      this.name = file.fileName || file.name
+      this.size = file.size
+      this.relativePath = file.relativePath || file.webkitRelativePath || this.name
+      this._parseFile()
+    }
+  }
+
+  this.paused = uploader.opts.initialPaused
+  this.error = false
+  this.allError = false
+  this.aborted = false
+  this.completed = false
+  this.averageSpeed = 0
+  this.currentSpeed = 0
+  this._lastProgressCallback = Date.now()
+  this._prevUploadedSize = 0
+  this._prevProgress = 0
+
+  this.bootstrap()
+}
+
+utils.extend(File.prototype, {
+
+  _parseFile: function () {
+    var ppaths = parsePaths(this.relativePath)
+    if (ppaths.length) {
+      var filePaths = this.uploader.filePaths
+      utils.each(ppaths, function (path, i) {
+        var folderFile = filePaths[path]
+        if (!folderFile) {
+          folderFile = new File(this.uploader, path, this.parent)
+          filePaths[path] = folderFile
+          this._updateParentFileList(folderFile)
+        }
+        this.parent = folderFile
+        folderFile.files.push(this)
+        if (!ppaths[i + 1]) {
+          folderFile.fileList.push(this)
+        }
+      }, this)
+    } else {
+      this._updateParentFileList()
+    }
+  },
+
+  _updateParentFileList: function (file) {
+    if (!file) {
+      file = this
+    }
+    var p = this.parent
+    if (p) {
+      p.fileList.push(file)
+    }
+  },
+
+  _eachAccess: function (eachFn, fileFn) {
+    if (this.isFolder) {
+      utils.each(this.files, function (f, i) {
+        return eachFn.call(this, f, i)
+      }, this)
+      return
+    }
+    fileFn.call(this, this)
+  },
+
+  bootstrap: function () {
+    if (this.isFolder) return
+    var opts = this.uploader.opts
+    if (utils.isFunction(opts.initFileFn)) {
+      opts.initFileFn.call(this, this)
+    }
+
+    this.abort(true)
+    this._resetError()
+    // Rebuild stack of chunks from file
+    this._prevProgress = 0
+    var round = opts.forceChunkSize ? Math.ceil : Math.floor
+    var chunks = Math.max(round(this.size / opts.chunkSize), 1)
+    for (var offset = 0; offset < chunks; offset++) {
+      this.chunks.push(new Chunk(this.uploader, this, offset))
+    }
+  },
+
+  _measureSpeed: function () {
+    var smoothingFactor = this.uploader.opts.speedSmoothingFactor
+    var timeSpan = Date.now() - this._lastProgressCallback
+    if (!timeSpan) {
+      return
+    }
+    var uploaded = this.sizeUploaded()
+    // Prevent negative upload speed after file upload resume
+    this.currentSpeed = Math.max((uploaded - this._prevUploadedSize) / timeSpan * 1000, 0)
+    this.averageSpeed = smoothingFactor * this.currentSpeed + (1 - smoothingFactor) * this.averageSpeed
+    this._prevUploadedSize = uploaded
+    if (this.parent && this.parent._checkProgress()) {
+      this.parent._measureSpeed()
+    }
+  },
+
+  _checkProgress: function (file) {
+    return Date.now() - this._lastProgressCallback >= this.uploader.opts.progressCallbacksInterval
+  },
+
+  _chunkEvent: function (chunk, evt, message) {
+    var uploader = this.uploader
+    var STATUS = Chunk.STATUS
+    var that = this
+    var rootFile = this.getRoot()
+    var triggerProgress = function () {
+      that._measureSpeed()
+      uploader._trigger('fileProgress', rootFile, that, chunk)
+      that._lastProgressCallback = Date.now()
+    }
+    switch (evt) {
+      case STATUS.PROGRESS:
+        if (this._checkProgress()) {
+          triggerProgress()
+        }
+        break
+      case STATUS.ERROR:
+        this._error()
+        this.abort(true)
+        uploader._trigger('fileError', rootFile, this, message, chunk)
+        break
+      case STATUS.SUCCESS:
+        this._updateUploadedChunks(message, chunk)
+        if (this.error) {
+          return
+        }
+        clearTimeout(this._progeressId)
+        this._progeressId = 0
+        var timeDiff = Date.now() - this._lastProgressCallback
+        if (timeDiff < uploader.opts.progressCallbacksInterval) {
+          this._progeressId = setTimeout(triggerProgress, uploader.opts.progressCallbacksInterval - timeDiff)
+        }
+        if (this.isComplete()) {
+          clearTimeout(this._progeressId)
+          triggerProgress()
+          this.currentSpeed = 0
+          this.averageSpeed = 0
+          uploader._trigger('fileSuccess', rootFile, this, message, chunk)
+          if (rootFile.isComplete()) {
+            uploader._trigger('fileComplete', rootFile, this)
+          }
+        } else if (!this._progeressId) {
+          triggerProgress()
+        }
+        break
+      case STATUS.RETRY:
+        uploader._trigger('fileRetry', rootFile, this, chunk)
+        break
+    }
+  },
+
+  _updateUploadedChunks: function (message, chunk) {
+    var checkChunkUploaded = this.uploader.opts.checkChunkUploadedByResponse
+    if (checkChunkUploaded) {
+      var xhr = chunk.xhr
+      utils.each(this.chunks, function (_chunk) {
+        if (!_chunk.tested) {
+          var uploaded = checkChunkUploaded.call(this, _chunk, message)
+          if (_chunk === chunk && !uploaded) {
+            // fix the first chunk xhr status
+            // treated as success but checkChunkUploaded is false
+            // so the current chunk should be uploaded again
+            _chunk.xhr = null
+          }
+          if (uploaded) {
+            // first success and other chunks are uploaded
+            // then set xhr, so the uploaded chunks
+            // will be treated as success too
+            _chunk.xhr = xhr
+          }
+          _chunk.tested = true
+        }
+      }, this)
+      if (!this._firstResponse) {
+        this._firstResponse = true
+        this.uploader.upload(true)
+      } else {
+        this.uploader.uploadNextChunk()
+      }
+    } else {
+      this.uploader.uploadNextChunk()
+    }
+  },
+
+  _error: function () {
+    this.error = this.allError = true
+    var parent = this.parent
+    while (parent && parent !== this.uploader) {
+      parent._errorFiles.push(this)
+      parent.error = true
+      if (parent._errorFiles.length === parent.files.length) {
+        parent.allError = true
+      }
+      parent = parent.parent
+    }
+  },
+
+  _resetError: function () {
+    this.error = this.allError = false
+    var parent = this.parent
+    var index = -1
+    while (parent && parent !== this.uploader) {
+      index = parent._errorFiles.indexOf(this)
+      parent._errorFiles.splice(index, 1)
+      parent.allError = false
+      if (!parent._errorFiles.length) {
+        parent.error = false
+      }
+      parent = parent.parent
+    }
+  },
+
+  isComplete: function () {
+    if (!this.completed) {
+      var outstanding = false
+      this._eachAccess(function (file) {
+        if (!file.isComplete()) {
+          outstanding = true
+          return false
+        }
+      }, function () {
+        if (this.error) {
+          outstanding = true
+        } else {
+          var STATUS = Chunk.STATUS
+          utils.each(this.chunks, function (chunk) {
+            var status = chunk.status()
+            if (status === STATUS.ERROR || status === STATUS.PENDING || status === STATUS.UPLOADING || status === STATUS.READING || chunk.preprocessState === 1 || chunk.readState === 1) {
+              outstanding = true
+              return false
+            }
+          })
+        }
+      })
+      this.completed = !outstanding
+    }
+    return this.completed
+  },
+
+  isUploading: function () {
+    var uploading = false
+    this._eachAccess(function (file) {
+      if (file.isUploading()) {
+        uploading = true
+        return false
+      }
+    }, function () {
+      var uploadingStatus = Chunk.STATUS.UPLOADING
+      utils.each(this.chunks, function (chunk) {
+        if (chunk.status() === uploadingStatus) {
+          uploading = true
+          return false
+        }
+      })
+    })
+    return uploading
+  },
+
+  resume: function () {
+    this._eachAccess(function (f) {
+      f.resume()
+    }, function () {
+      this.paused = false
+      this.aborted = false
+      this.uploader.upload()
+    })
+    this.paused = false
+    this.aborted = false
+  },
+
+  pause: function () {
+    this._eachAccess(function (f) {
+      f.pause()
+    }, function () {
+      this.paused = true
+      this.abort()
+    })
+    this.paused = true
+  },
+
+  cancel: function () {
+    this.uploader.removeFile(this)
+  },
+
+  retry: function (file) {
+    var fileRetry = function (file) {
+      if (file.error) {
+        file.bootstrap()
+      }
+    }
+    if (file) {
+      file.bootstrap()
+    } else {
+      this._eachAccess(fileRetry, function () {
+        this.bootstrap()
+      })
+    }
+    this.uploader.upload()
+  },
+
+  abort: function (reset) {
+    if (this.aborted) {
+      return
+    }
+    this.currentSpeed = 0
+    this.averageSpeed = 0
+    this.aborted = !reset
+    var chunks = this.chunks
+    if (reset) {
+      this.chunks = []
+    }
+    var uploadingStatus = Chunk.STATUS.UPLOADING
+    utils.each(chunks, function (c) {
+      if (c.status() === uploadingStatus) {
+        c.abort()
+        this.uploader.uploadNextChunk()
+      }
+    }, this)
+  },
+
+  progress: function () {
+    var totalDone = 0
+    var totalSize = 0
+    var ret = 0
+    this._eachAccess(function (file, index) {
+      totalDone += file.progress() * file.size
+      totalSize += file.size
+      if (index === this.files.length - 1) {
+        ret = totalSize > 0 ? totalDone / totalSize : this.isComplete() ? 1 : 0
+      }
+    }, function () {
+      if (this.error) {
+        ret = 1
+        return
+      }
+      if (this.chunks.length === 1) {
+        this._prevProgress = Math.max(this._prevProgress, this.chunks[0].progress())
+        ret = this._prevProgress
+        return
+      }
+      // Sum up progress across everything
+      var bytesLoaded = 0
+      utils.each(this.chunks, function (c) {
+        // get chunk progress relative to entire file
+        bytesLoaded += c.progress() * (c.endByte - c.startByte)
+      })
+      var percent = bytesLoaded / this.size
+      // We don't want to lose percentages when an upload is paused
+      this._prevProgress = Math.max(this._prevProgress, percent > 0.9999 ? 1 : percent)
+      ret = this._prevProgress
+    })
+    return ret
+  },
+
+  getSize: function () {
+    var size = 0
+    this._eachAccess(function (file) {
+      size += file.size
+    }, function () {
+      size += this.size
+    })
+    return size
+  },
+
+  getFormatSize: function () {
+    var size = this.getSize()
+    return utils.formatSize(size)
+  },
+
+  getRoot: function () {
+    if (this.isRoot) {
+      return this
+    }
+    var parent = this.parent
+    while (parent) {
+      if (parent.parent === this.uploader) {
+        // find it
+        return parent
+      }
+      parent = parent.parent
+    }
+    return this
+  },
+
+  sizeUploaded: function () {
+    var size = 0
+    this._eachAccess(function (file) {
+      size += file.sizeUploaded()
+    }, function () {
+      utils.each(this.chunks, function (chunk) {
+        size += chunk.sizeUploaded()
+      })
+    })
+    return size
+  },
+
+  timeRemaining: function () {
+    var ret = 0
+    var sizeDelta = 0
+    var averageSpeed = 0
+    this._eachAccess(function (file, i) {
+      if (!file.paused && !file.error) {
+        sizeDelta += file.size - file.sizeUploaded()
+        averageSpeed += file.averageSpeed
+      }
+      if (i === this.files.length - 1) {
+        ret = calRet(sizeDelta, averageSpeed)
+      }
+    }, function () {
+      if (this.paused || this.error) {
+        ret = 0
+        return
+      }
+      var delta = this.size - this.sizeUploaded()
+      ret = calRet(delta, this.averageSpeed)
+    })
+    return ret
+    function calRet (delta, averageSpeed) {
+      if (delta && !averageSpeed) {
+        return Number.POSITIVE_INFINITY
+      }
+      if (!delta && !averageSpeed) {
+        return 0
+      }
+      return Math.floor(delta / averageSpeed)
+    }
+  },
+
+  removeFile: function (file) {
+    if (file.isFolder) {
+      while (file.files.length) {
+        var f = file.files[file.files.length - 1]
+        this._removeFile(f)
+      }
+    }
+    this._removeFile(file)
+  },
+
+  _delFilePath: function (file) {
+    if (file.path && this.filePaths) {
+      delete this.filePaths[file.path]
+    }
+    utils.each(file.fileList, function (file) {
+      this._delFilePath(file)
+    }, this)
+  },
+
+  _removeFile: function (file) {
+    if (!file.isFolder) {
+      utils.each(this.files, function (f, i) {
+        if (f === file) {
+          this.files.splice(i, 1)
+          return false
+        }
+      }, this)
+      file.abort()
+      var parent = file.parent
+      var newParent
+      while (parent && parent !== this) {
+        newParent = parent.parent
+        parent._removeFile(file)
+        parent = newParent
+      }
+    }
+    file.parent === this && utils.each(this.fileList, function (f, i) {
+      if (f === file) {
+        this.fileList.splice(i, 1)
+        return false
+      }
+    }, this)
+    if (!this.isRoot && this.isFolder && !this.files.length) {
+      this.parent._removeFile(this)
+      this.uploader._delFilePath(this)
+    }
+    file.parent = null
+  },
+
+  getType: function () {
+    if (this.isFolder) {
+      return 'folder'
+    }
+    return this.file.type && this.file.type.split('/')[1]
+  },
+
+  getExtension: function () {
+    if (this.isFolder) {
+      return ''
+    }
+    return this.name.substr((~-this.name.lastIndexOf('.') >>> 0) + 2).toLowerCase()
+  }
+
+})
+
+module.exports = File
+
+function parsePaths (path) {
+  var ret = []
+  var paths = path.split('/')
+  var len = paths.length
+  var i = 1
+  paths.splice(len - 1, 1)
+  len--
+  if (paths.length) {
+    while (i <= len) {
+      ret.push(paths.slice(0, i++).join('/') + '/')
+    }
+  }
+  return ret
+}

+ 514 - 0
node_modules/_simple-uploader.js@0.5.6@simple-uploader.js/src/uploader.js

@@ -0,0 +1,514 @@
+var utils = require('./utils')
+var event = require('./event')
+var File = require('./file')
+var Chunk = require('./chunk')
+
+var version = '__VERSION__'
+
+var isServer = typeof window === 'undefined'
+
+// ie10+
+var ie10plus = isServer ? false : window.navigator.msPointerEnabled
+var support = (function () {
+  if (isServer) {
+    return false
+  }
+  var sliceName = 'slice'
+  var _support = utils.isDefined(window.File) && utils.isDefined(window.Blob) &&
+                utils.isDefined(window.FileList)
+  var bproto = null
+  if (_support) {
+    bproto = window.Blob.prototype
+    utils.each(['slice', 'webkitSlice', 'mozSlice'], function (n) {
+      if (bproto[n]) {
+        sliceName = n
+        return false
+      }
+    })
+    _support = !!bproto[sliceName]
+  }
+  if (_support) Uploader.sliceName = sliceName
+  bproto = null
+  return _support
+})()
+
+var supportDirectory = (function () {
+  if (isServer) {
+    return false
+  }
+  var input = window.document.createElement('input')
+  input.type = 'file'
+  var sd = 'webkitdirectory' in input || 'directory' in input
+  input = null
+  return sd
+})()
+
+function Uploader (opts) {
+  this.support = support
+  /* istanbul ignore if */
+  if (!this.support) {
+    return
+  }
+  this.supportDirectory = supportDirectory
+  utils.defineNonEnumerable(this, 'filePaths', {})
+  this.opts = utils.extend({}, Uploader.defaults, opts || {})
+
+  this.preventEvent = utils.bind(this._preventEvent, this)
+
+  File.call(this, this)
+}
+
+/**
+ * Default read function using the webAPI
+ *
+ * @function webAPIFileRead(fileObj, fileType, startByte, endByte, chunk)
+ *
+ */
+var webAPIFileRead = function (fileObj, fileType, startByte, endByte, chunk) {
+  chunk.readFinished(fileObj.file[Uploader.sliceName](startByte, endByte, fileType))
+}
+
+Uploader.version = version
+
+Uploader.defaults = {
+  chunkSize: 1024 * 1024,
+  forceChunkSize: false,
+  simultaneousUploads: 3,
+  singleFile: false,
+  fileParameterName: 'file',
+  progressCallbacksInterval: 500,
+  speedSmoothingFactor: 0.1,
+  query: {},
+  headers: {},
+  withCredentials: false,
+  preprocess: null,
+  method: 'multipart',
+  testMethod: 'GET',
+  uploadMethod: 'POST',
+  prioritizeFirstAndLastChunk: false,
+  allowDuplicateUploads: false,
+  target: '/',
+  testChunks: true,
+  generateUniqueIdentifier: null,
+  maxChunkRetries: 0,
+  chunkRetryInterval: null,
+  permanentErrors: [404, 415, 500, 501],
+  successStatuses: [200, 201, 202],
+  onDropStopPropagation: false,
+  initFileFn: null,
+  readFileFn: webAPIFileRead,
+  checkChunkUploadedByResponse: null,
+  initialPaused: false,
+  processResponse: function (response, cb) {
+    cb(null, response)
+  },
+  processParams: function (params) {
+    return params
+  }
+}
+
+Uploader.utils = utils
+Uploader.event = event
+Uploader.File = File
+Uploader.Chunk = Chunk
+
+// inherit file
+Uploader.prototype = utils.extend({}, File.prototype)
+// inherit event
+utils.extend(Uploader.prototype, event)
+utils.extend(Uploader.prototype, {
+
+  constructor: Uploader,
+
+  _trigger: function (name) {
+    var args = utils.toArray(arguments)
+    var preventDefault = !this.trigger.apply(this, arguments)
+    if (name !== 'catchAll') {
+      args.unshift('catchAll')
+      preventDefault = !this.trigger.apply(this, args) || preventDefault
+    }
+    return !preventDefault
+  },
+
+  _triggerAsync: function () {
+    var args = arguments
+    utils.nextTick(function () {
+      this._trigger.apply(this, args)
+    }, this)
+  },
+
+  addFiles: function (files, evt) {
+    var _files = []
+    var oldFileListLen = this.fileList.length
+    utils.each(files, function (file) {
+      // Uploading empty file IE10/IE11 hangs indefinitely
+      // Directories have size `0` and name `.`
+      // Ignore already added files if opts.allowDuplicateUploads is set to false
+      if ((!ie10plus || ie10plus && file.size > 0) && !(file.size % 4096 === 0 && (file.name === '.' || file.fileName === '.'))) {
+        var uniqueIdentifier = this.generateUniqueIdentifier(file)
+        if (this.opts.allowDuplicateUploads || !this.getFromUniqueIdentifier(uniqueIdentifier)) {
+          var _file = new File(this, file, this)
+          _file.uniqueIdentifier = uniqueIdentifier
+          if (this._trigger('fileAdded', _file, evt)) {
+            _files.push(_file)
+          } else {
+            File.prototype.removeFile.call(this, _file)
+          }
+        }
+      }
+    }, this)
+    // get new fileList
+    var newFileList = this.fileList.slice(oldFileListLen)
+    if (this._trigger('filesAdded', _files, newFileList, evt)) {
+      utils.each(_files, function (file) {
+        if (this.opts.singleFile && this.files.length > 0) {
+          this.removeFile(this.files[0])
+        }
+        this.files.push(file)
+      }, this)
+      this._trigger('filesSubmitted', _files, newFileList, evt)
+    } else {
+      utils.each(newFileList, function (file) {
+        File.prototype.removeFile.call(this, file)
+      }, this)
+    }
+  },
+
+  addFile: function (file, evt) {
+    this.addFiles([file], evt)
+  },
+
+  cancel: function () {
+    for (var i = this.fileList.length - 1; i >= 0; i--) {
+      this.fileList[i].cancel()
+    }
+  },
+
+  removeFile: function (file) {
+    File.prototype.removeFile.call(this, file)
+    this._trigger('fileRemoved', file)
+  },
+
+  generateUniqueIdentifier: function (file) {
+    var custom = this.opts.generateUniqueIdentifier
+    if (utils.isFunction(custom)) {
+      return custom(file)
+    }
+    /* istanbul ignore next */
+    // Some confusion in different versions of Firefox
+    var relativePath = file.relativePath || file.webkitRelativePath || file.fileName || file.name
+    /* istanbul ignore next */
+    return file.size + '-' + relativePath.replace(/[^0-9a-zA-Z_-]/img, '')
+  },
+
+  getFromUniqueIdentifier: function (uniqueIdentifier) {
+    var ret = false
+    utils.each(this.files, function (file) {
+      if (file.uniqueIdentifier === uniqueIdentifier) {
+        ret = file
+        return false
+      }
+    })
+    return ret
+  },
+
+  uploadNextChunk: function (preventEvents) {
+    var found = false
+    var pendingStatus = Chunk.STATUS.PENDING
+    var checkChunkUploaded = this.uploader.opts.checkChunkUploadedByResponse
+    if (this.opts.prioritizeFirstAndLastChunk) {
+      utils.each(this.files, function (file) {
+        if (file.paused) {
+          return
+        }
+        if (checkChunkUploaded && !file._firstResponse && file.isUploading()) {
+          // waiting for current file's first chunk response
+          return
+        }
+        if (file.chunks.length && file.chunks[0].status() === pendingStatus) {
+          file.chunks[0].send()
+          found = true
+          return false
+        }
+        if (file.chunks.length > 1 && file.chunks[file.chunks.length - 1].status() === pendingStatus) {
+          file.chunks[file.chunks.length - 1].send()
+          found = true
+          return false
+        }
+      })
+      if (found) {
+        return found
+      }
+    }
+
+    // Now, simply look for the next, best thing to upload
+    utils.each(this.files, function (file) {
+      if (!file.paused) {
+        if (checkChunkUploaded && !file._firstResponse && file.isUploading()) {
+          // waiting for current file's first chunk response
+          return
+        }
+        utils.each(file.chunks, function (chunk) {
+          if (chunk.status() === pendingStatus) {
+            chunk.send()
+            found = true
+            return false
+          }
+        })
+      }
+      if (found) {
+        return false
+      }
+    })
+    if (found) {
+      return true
+    }
+
+    // The are no more outstanding chunks to upload, check is everything is done
+    var outstanding = false
+    utils.each(this.files, function (file) {
+      if (!file.isComplete()) {
+        outstanding = true
+        return false
+      }
+    })
+    // should check files now
+    // if now files in list
+    // should not trigger complete event
+    if (!outstanding && !preventEvents && this.files.length) {
+      // All chunks have been uploaded, complete
+      this._triggerAsync('complete')
+    }
+    return outstanding
+  },
+
+  upload: function (preventEvents) {
+    // Make sure we don't start too many uploads at once
+    var ret = this._shouldUploadNext()
+    if (ret === false) {
+      return
+    }
+    !preventEvents && this._trigger('uploadStart')
+    var started = false
+    for (var num = 1; num <= this.opts.simultaneousUploads - ret; num++) {
+      started = this.uploadNextChunk(!preventEvents) || started
+      if (!started && preventEvents) {
+        // completed
+        break
+      }
+    }
+    if (!started && !preventEvents) {
+      this._triggerAsync('complete')
+    }
+  },
+
+  /**
+   * should upload next chunk
+   * @function
+   * @returns {Boolean|Number}
+   */
+  _shouldUploadNext: function () {
+    var num = 0
+    var should = true
+    var simultaneousUploads = this.opts.simultaneousUploads
+    var uploadingStatus = Chunk.STATUS.UPLOADING
+    utils.each(this.files, function (file) {
+      utils.each(file.chunks, function (chunk) {
+        if (chunk.status() === uploadingStatus) {
+          num++
+          if (num >= simultaneousUploads) {
+            should = false
+            return false
+          }
+        }
+      })
+      return should
+    })
+    // if should is true then return uploading chunks's length
+    return should && num
+  },
+
+  /**
+   * Assign a browse action to one or more DOM nodes.
+   * @function
+   * @param {Element|Array.<Element>} domNodes
+   * @param {boolean} isDirectory Pass in true to allow directories to
+   * @param {boolean} singleFile prevent multi file upload
+   * @param {Object} attributes set custom attributes:
+   *  http://www.w3.org/TR/html-markup/input.file.html#input.file-attributes
+   *  eg: accept: 'image/*'
+   * be selected (Chrome only).
+   */
+  assignBrowse: function (domNodes, isDirectory, singleFile, attributes) {
+    if (typeof domNodes.length === 'undefined') {
+      domNodes = [domNodes]
+    }
+
+    utils.each(domNodes, function (domNode) {
+      var input
+      if (domNode.tagName === 'INPUT' && domNode.type === 'file') {
+        input = domNode
+      } else {
+        input = document.createElement('input')
+        input.setAttribute('type', 'file')
+        // display:none - not working in opera 12
+        utils.extend(input.style, {
+          visibility: 'hidden',
+          position: 'absolute',
+          width: '1px',
+          height: '1px'
+        })
+        // for opera 12 browser, input must be assigned to a document
+        domNode.appendChild(input)
+        // https://developer.mozilla.org/en/using_files_from_web_applications)
+        // event listener is executed two times
+        // first one - original mouse click event
+        // second - input.click(), input is inside domNode
+        domNode.addEventListener('click', function (e) {
+          if (domNode.tagName.toLowerCase() === 'label') {
+            return
+          }
+          input.click()
+        }, false)
+      }
+      if (!this.opts.singleFile && !singleFile) {
+        input.setAttribute('multiple', 'multiple')
+      }
+      if (isDirectory) {
+        input.setAttribute('webkitdirectory', 'webkitdirectory')
+      }
+      attributes && utils.each(attributes, function (value, key) {
+        input.setAttribute(key, value)
+      })
+      // When new files are added, simply append them to the overall list
+      var that = this
+      input.addEventListener('change', function (e) {
+        that._trigger(e.type, e)
+        if (e.target.value) {
+          that.addFiles(e.target.files, e)
+          e.target.value = ''
+        }
+      }, false)
+    }, this)
+  },
+
+  onDrop: function (evt) {
+    this._trigger(evt.type, evt)
+    if (this.opts.onDropStopPropagation) {
+      evt.stopPropagation()
+    }
+    evt.preventDefault()
+    this._parseDataTransfer(evt.dataTransfer, evt)
+  },
+
+  _parseDataTransfer: function (dataTransfer, evt) {
+    if (dataTransfer.items && dataTransfer.items[0] &&
+      dataTransfer.items[0].webkitGetAsEntry) {
+      this.webkitReadDataTransfer(dataTransfer, evt)
+    } else {
+      this.addFiles(dataTransfer.files, evt)
+    }
+  },
+
+  webkitReadDataTransfer: function (dataTransfer, evt) {
+    var self = this
+    var queue = dataTransfer.items.length
+    var files = []
+    utils.each(dataTransfer.items, function (item) {
+      var entry = item.webkitGetAsEntry()
+      if (!entry) {
+        decrement()
+        return
+      }
+      if (entry.isFile) {
+        // due to a bug in Chrome's File System API impl - #149735
+        fileReadSuccess(item.getAsFile(), entry.fullPath)
+      } else {
+        readDirectory(entry.createReader())
+      }
+    })
+    function readDirectory (reader) {
+      reader.readEntries(function (entries) {
+        if (entries.length) {
+          queue += entries.length
+          utils.each(entries, function (entry) {
+            if (entry.isFile) {
+              var fullPath = entry.fullPath
+              entry.file(function (file) {
+                fileReadSuccess(file, fullPath)
+              }, readError)
+            } else if (entry.isDirectory) {
+              readDirectory(entry.createReader())
+            }
+          })
+          readDirectory(reader)
+        } else {
+          decrement()
+        }
+      }, readError)
+    }
+    function fileReadSuccess (file, fullPath) {
+      // relative path should not start with "/"
+      file.relativePath = fullPath.substring(1)
+      files.push(file)
+      decrement()
+    }
+    function readError (fileError) {
+      throw fileError
+    }
+    function decrement () {
+      if (--queue === 0) {
+        self.addFiles(files, evt)
+      }
+    }
+  },
+
+  _assignHelper: function (domNodes, handles, remove) {
+    if (typeof domNodes.length === 'undefined') {
+      domNodes = [domNodes]
+    }
+    var evtMethod = remove ? 'removeEventListener' : 'addEventListener'
+    utils.each(domNodes, function (domNode) {
+      utils.each(handles, function (handler, name) {
+        domNode[evtMethod](name, handler, false)
+      }, this)
+    }, this)
+  },
+
+  _preventEvent: function (e) {
+    utils.preventEvent(e)
+    this._trigger(e.type, e)
+  },
+
+  /**
+   * Assign one or more DOM nodes as a drop target.
+   * @function
+   * @param {Element|Array.<Element>} domNodes
+   */
+  assignDrop: function (domNodes) {
+    this._onDrop = utils.bind(this.onDrop, this)
+    this._assignHelper(domNodes, {
+      dragover: this.preventEvent,
+      dragenter: this.preventEvent,
+      dragleave: this.preventEvent,
+      drop: this._onDrop
+    })
+  },
+
+  /**
+   * Un-assign drop event from DOM nodes
+   * @function
+   * @param domNodes
+   */
+  unAssignDrop: function (domNodes) {
+    this._assignHelper(domNodes, {
+      dragover: this.preventEvent,
+      dragenter: this.preventEvent,
+      dragleave: this.preventEvent,
+      drop: this._onDrop
+    }, true)
+    this._onDrop = null
+  }
+})
+
+module.exports = Uploader

+ 171 - 0
node_modules/_simple-uploader.js@0.5.6@simple-uploader.js/src/utils.js

@@ -0,0 +1,171 @@
+var oproto = Object.prototype
+var aproto = Array.prototype
+var serialize = oproto.toString
+
+var isFunction = function (fn) {
+  return serialize.call(fn) === '[object Function]'
+}
+
+var isArray = Array.isArray || /* istanbul ignore next */ function (ary) {
+  return serialize.call(ary) === '[object Array]'
+}
+
+var isPlainObject = function (obj) {
+  return serialize.call(obj) === '[object Object]' && Object.getPrototypeOf(obj) === oproto
+}
+
+var i = 0
+var utils = {
+  uid: function () {
+    return ++i
+  },
+  noop: function () {},
+  bind: function (fn, context) {
+    return function () {
+      return fn.apply(context, arguments)
+    }
+  },
+  preventEvent: function (evt) {
+    evt.preventDefault()
+  },
+  stop: function (evt) {
+    evt.preventDefault()
+    evt.stopPropagation()
+  },
+  nextTick: function (fn, context) {
+    setTimeout(utils.bind(fn, context), 0)
+  },
+  toArray: function (ary, start, end) {
+    if (start === undefined) start = 0
+    if (end === undefined) end = ary.length
+    return aproto.slice.call(ary, start, end)
+  },
+
+  isPlainObject: isPlainObject,
+  isFunction: isFunction,
+  isArray: isArray,
+  isObject: function (obj) {
+    return Object(obj) === obj
+  },
+  isString: function (s) {
+    return typeof s === 'string'
+  },
+  isUndefined: function (a) {
+    return typeof a === 'undefined'
+  },
+  isDefined: function (a) {
+    return typeof a !== 'undefined'
+  },
+
+  each: function (ary, func, context) {
+    if (utils.isDefined(ary.length)) {
+      for (var i = 0, len = ary.length; i < len; i++) {
+        if (func.call(context, ary[i], i, ary) === false) {
+          break
+        }
+      }
+    } else {
+      for (var k in ary) {
+        if (func.call(context, ary[k], k, ary) === false) {
+          break
+        }
+      }
+    }
+  },
+
+  /**
+   * If option is a function, evaluate it with given params
+   * @param {*} data
+   * @param {...} args arguments of a callback
+   * @returns {*}
+   */
+  evalOpts: function (data, args) {
+    if (utils.isFunction(data)) {
+      // `arguments` is an object, not array, in FF, so:
+      args = utils.toArray(arguments)
+      data = data.apply(null, args.slice(1))
+    }
+    return data
+  },
+
+  extend: function () {
+    var options
+    var name
+    var src
+    var copy
+    var copyIsArray
+    var clone
+    var target = arguments[0] || {}
+    var i = 1
+    var length = arguments.length
+    var force = false
+
+    // 如果第一个参数为布尔,判定是否深拷贝
+    if (typeof target === 'boolean') {
+      force = target
+      target = arguments[1] || {}
+      i++
+    }
+
+    // 确保接受方为一个复杂的数据类型
+    if (typeof target !== 'object' && !isFunction(target)) {
+      target = {}
+    }
+
+    // 如果只有一个参数,那么新成员添加于 extend 所在的对象上
+    if (i === length) {
+      target = this
+      i--
+    }
+
+    for (; i < length; i++) {
+      // 只处理非空参数
+      if ((options = arguments[i]) != null) {
+        for (name in options) {
+          src = target[name]
+          copy = options[name]
+
+          // 防止环引用
+          if (target === copy) {
+            continue
+          }
+          if (force && copy && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) {
+            if (copyIsArray) {
+              copyIsArray = false
+              clone = src && isArray(src) ? src : []
+            } else {
+              clone = src && isPlainObject(src) ? src : {}
+            }
+            target[name] = utils.extend(force, clone, copy)
+          } else if (copy !== undefined) {
+            target[name] = copy
+          }
+        }
+      }
+    }
+    return target
+  },
+
+  formatSize: function (size) {
+    if (size < 1024) {
+      return size.toFixed(0) + ' bytes'
+    } else if (size < 1024 * 1024) {
+      return (size / 1024.0).toFixed(0) + ' KB'
+    } else if (size < 1024 * 1024 * 1024) {
+      return (size / 1024.0 / 1024.0).toFixed(1) + ' MB'
+    } else {
+      return (size / 1024.0 / 1024.0 / 1024.0).toFixed(1) + ' GB'
+    }
+  },
+
+  defineNonEnumerable: function (target, key, value) {
+    Object.defineProperty(target, key, {
+      enumerable: false,
+      configurable: true,
+      writable: true,
+      value: value
+    })
+  }
+}
+
+module.exports = utils

+ 14 - 0
node_modules/_simple-uploader.js@0.5.6@simple-uploader.js/test.sh

@@ -0,0 +1,14 @@
+#!/bin/bash
+set -e
+
+if [ $TESTNAME = "unit-tests" ]; then
+  echo "Running unit-tests"
+  export DISPLAY=:99.0
+  sh -e /etc/init.d/xvfb start
+  sleep 1
+  npm run test
+  npm run codecov
+elif [ $TESTNAME = "browser-tests" ]; then
+  echo "Running browser-tests"
+  npm run test:ci
+fi

+ 18 - 0
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/.babelrc

@@ -0,0 +1,18 @@
+{
+  "presets": [
+    ["env", {
+      "modules": false,
+      "targets": {
+        "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
+      }
+    }],
+    "stage-2"
+  ],
+  "plugins": ["transform-runtime", "add-module-exports", "transform-es2015-modules-umd"],
+  "env": {
+    "test": {
+      "presets": ["env", "stage-2"],
+      "plugins": ["istanbul"]
+    }
+  }
+}

+ 9 - 0
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/.editorconfig

@@ -0,0 +1,9 @@
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true

+ 2 - 0
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/.eslintignore

@@ -0,0 +1,2 @@
+build/*.js
+config/*.js

+ 27 - 0
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/.eslintrc.js

@@ -0,0 +1,27 @@
+// http://eslint.org/docs/user-guide/configuring
+
+module.exports = {
+  root: true,
+  parser: 'babel-eslint',
+  parserOptions: {
+    sourceType: 'module'
+  },
+  env: {
+    browser: true,
+  },
+  // https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style
+  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
+  }
+}

+ 8 - 0
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/.postcssrc.js

@@ -0,0 +1,8 @@
+// https://github.com/michael-ciniawsky/postcss-load-config
+
+module.exports = {
+  "plugins": {
+    // to edit target browsers: use "browserslist" field in package.json
+    "autoprefixer": {}
+  }
+}

+ 21 - 0
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 simple-uploader
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 400 - 0
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/README.md

@@ -0,0 +1,400 @@
+# vue-simple-uploader  [![NPM Version][npm-image]][npm-url] [![NPM Downloads][downloads-image]][downloads-url] [![juejin likes][juejin-image]](juejin-url)
+
+> A Vue.js upload component powered by [simple-uploader.js](https://github.com/simple-uploader/Uploader)
+
+![example](https://github.com/simple-uploader/vue-uploader/blob/master/example/simple-uploader.gif)
+
+![QQ](https://github.com/simple-uploader/Uploader/blob/develop/assets/simple-uploader-QQ-2.png?raw=true)
+
+[中文](./README_zh-CN.md)
+
+## Features
+
+* Treat Folder and File as `File`
+* Pause/Resume upload
+* Recover upload
+* Error handling
+* Drag and Drop with folder reader
+* Custom upload buttons
+* Folder Upload
+* Queue management
+* File validation
+* Upload progress
+* Time remaining
+* Chunk uploads
+
+## Install
+
+``` bash
+npm install vue-simple-uploader --save
+```
+
+## Notes
+
+- https://www.cnblogs.com/xiahj/p/vue-simple-uploader.html
+- https://github.com/LuoLiangDSGA/spring-learning/tree/master/boot-uploader
+- http://www.smarthu.club
+
+## Usage
+
+### init
+
+``` js
+import Vue from 'vue'
+import uploader from 'vue-simple-uploader'
+import App from './App.vue'
+
+Vue.use(uploader)
+
+/* eslint-disable no-new */
+new Vue({
+  render(createElement) {
+    return createElement(App)
+  }
+}).$mount('#app')
+```
+
+### App.vue
+
+``` vue
+<template>
+  <uploader :options="options" class="uploader-example">
+    <uploader-unsupport></uploader-unsupport>
+    <uploader-drop>
+      <p>Drop files here to upload or</p>
+      <uploader-btn>select files</uploader-btn>
+      <uploader-btn :attrs="attrs">select images</uploader-btn>
+      <uploader-btn :directory="true">select folder</uploader-btn>
+    </uploader-drop>
+    <uploader-list></uploader-list>
+  </uploader>
+</template>
+
+<script>
+  export default {
+    data () {
+      return {
+        options: {
+          // https://github.com/simple-uploader/Uploader/tree/develop/samples/Node.js
+          target: '//localhost:3000/upload',
+          testChunks: false
+        },
+        attrs: {
+          accept: 'image/*'
+        }
+      }
+    }
+  }
+</script>
+
+<style>
+  .uploader-example {
+    width: 880px;
+    padding: 15px;
+    margin: 40px auto 0;
+    font-size: 12px;
+    box-shadow: 0 0 10px rgba(0, 0, 0, .4);
+  }
+  .uploader-example .uploader-btn {
+    margin-right: 4px;
+  }
+  .uploader-example .uploader-list {
+    max-height: 440px;
+    overflow: auto;
+    overflow-x: hidden;
+    overflow-y: auto;
+  }
+</style>
+```
+
+## Components
+
+### Uploader
+
+Root component.
+
+#### Props
+
+* `options {Object}`
+
+  See [simple-uploader.js options](https://github.com/simple-uploader/Uploader#configuration).
+
+  Besides, some other options are avaliable too:
+
+  - `parseTimeRemaining(timeRemaining, parsedTimeRemaining) {Function}`
+
+    this function option to format the current file's time remaining value(seconds, number), you can return your language time remaining text, params:
+
+    - `timeRemaining{Number}`, time remaining seconds
+
+    - `parsedTimeRemaining{String}`, default shown time remaining text, you can use it like this:
+
+      ```js
+      parseTimeRemaining: function (timeRemaining, parsedTimeRemaining) {
+        return parsedTimeRemaining
+          .replace(/\syears?/, '年')
+          .replace(/\days?/, '天')
+          .replace(/\shours?/, '小时')
+          .replace(/\sminutes?/, '分钟')
+          .replace(/\sseconds?/, '秒')
+      }
+      ```
+  - `categoryMap {Object}`
+
+    File category map, default:
+
+    ```js
+    {
+      image: ['gif', 'jpg', 'jpeg', 'png', 'bmp', 'webp'],
+      video: ['mp4', 'm3u8', 'rmvb', 'avi', 'swf', '3gp', 'mkv', 'flv'],
+      audio: ['mp3', 'wav', 'wma', 'ogg', 'aac', 'flac'],
+      document: ['doc', 'txt', 'docx', 'pages', 'epub', 'pdf', 'numbers', 'csv', 'xls', 'xlsx', 'keynote', 'ppt', 'pptx']
+    }
+    ```
+
+* `autoStart {Boolean}`
+
+  Default `true`, Whether the file will be start uploading after it is added.
+
+* `fileStatusText {Object}`
+
+  Default:
+  ```js
+  {
+    success: 'success',
+    error: 'error',
+    uploading: 'uploading',
+    paused: 'paused',
+    waiting: 'waiting'
+  }
+  ```
+  An object map for file status text.
+
+  After 0.6.0, `fileStatusText` can be a function with params `(status, response = null)`, you can control the status text more flexible:
+
+  ```js
+  fileStatusText(status, response) {
+    const statusTextMap = {
+      uploading: 'uploading',
+      paused: 'paused',
+      waiting: 'waiting'
+    }
+    if (status === 'success' || status === 'error') {
+      // only use response when status is success or error
+
+      // eg:
+      // return response data ?
+      return response.data
+    } else {
+      return statusTextMap[status]
+    }
+  }
+  ```
+
+#### Events
+
+See [simple-uploader.js uploader/events](https://github.com/simple-uploader/Uploader#events)
+
+**Note:**
+
+* All events name will be transformed by [lodash.kebabCase](https://github.com/lodash/lodash/blob/master/kebabCase.js), eg: `fileSuccess` will be transformed to `file-success`
+
+* `catchAll` event will not be emited.
+
+* `file-added(file)`, file added event, this event is used for file validation. To reject this file you should set `file.ignored = true`.
+
+* `files-added(files, fileList)`, files added event, this event is used for files validation. To reject these files you should set `files.ignored = true` or `fileList.ignored = true`.
+
+#### Scoped Slots
+
+* `files {Array}`
+
+  An array of files (no folders).
+
+* `fileList {Array}`
+
+  An array of files and folders.
+
+* `started`
+
+  Started uploading or not.
+
+#### Get `Uploader` instance
+
+You can get it like this:
+
+```js
+const uploaderInstance = this.$refs.uploader.uploader
+// now you can call all uploader methods
+// https://github.com/simple-uploader/Uploader#methods
+uploaderInstance.cancel()
+```
+
+### UploaderBtn
+
+Select files button.
+
+#### Props
+
+* `directory {Boolean}`
+
+  Default `false`, Support selecting Folder
+
+* `single {Boolean}`
+
+  Default `false`, To prevent multiple file uploads if it is `true`.
+
+* `attrs {Object}`
+
+  Default `{}`, Pass object to set custom attributes on input element.
+
+### UploaderDrop
+
+Droped files area.
+
+### UploaderList
+
+An array of `Uploader.File` file(folder) objects added by the user, but it treated Folder as `Uploader.File` Object.
+
+#### Scoped Slots
+
+* `fileList {Array}`
+
+  An array of files and folders.
+
+### UploaderFiles
+
+An array of `Uploader.File` file objects added by the user.
+
+#### Scoped Slots
+
+* `files {Array}`
+
+  An array of files (no folders).
+
+### UploaderUnsupport
+
+It will be shown if the current browser do not support HTML5 File API.
+
+### UploaderFile
+
+File item component.
+
+#### Props
+
+* `file {Uploader.File}`
+
+  `Uploader.File` instance.
+
+* `list {Boolean}`
+
+  It should be `true` if it is puted in `UploaderList`
+
+#### Scoped Slots
+
+* `file {Uploader.File}`
+
+  `Uploader.File` instance.
+
+* `list {Boolean}`
+
+  In `UploaderList` component or not.
+
+* `status {String}`
+
+  Current status, the values is one of `success`, `error`, `uploading`, `paused`, `waiting`
+
+* `paused {Boolean}`
+
+  Indicated if the file is paused.
+
+* `error {Boolean}`
+
+  Indicated if the file has encountered an error.
+
+* `averageSpeed {Number}`
+
+  Average upload speed, bytes per second.
+
+* `formatedAverageSpeed {String}`
+
+  Formated average upload speed, eg: `3 KB / S`
+
+* `currentSpeed {Number}`
+
+  Current upload speed, bytes per second.
+
+* `isComplete {Boolean}`
+
+  Indicated whether the file has completed uploading and received a server response.
+
+* `isUploading {Boolean}`
+
+  Indicated whether file chunks is uploading.
+
+* `size {Number}`
+
+  Size in bytes of the file.
+
+* `formatedSize {Number}`
+
+  Formated file size, eg: `10 KB`.
+
+* `uploadedSize {Number}`
+
+  Size uploaded in bytes.
+
+* `progress {Number}`
+
+  A number between 0 and 1 indicating the current upload progress of the file.
+
+* `progressStyle {String}`
+
+  The file progress element's transform style, eg: `{transform: '-50%'}`.
+
+* `progressingClass {String}`
+
+  The value will be `uploader-file-progressing` if the file is uploading.
+
+* `timeRemaining {Number}`
+
+  Remaining time to finish upload file in seconds.
+
+* `formatedTimeRemaining {String}`
+
+  Formated remaining time, eg: `3 miniutes`.
+
+* `type {String}`
+
+  File type.
+
+* `extension {String}`
+
+  File extension in lowercase.
+
+* `fileCategory {String}`
+
+  File category, one of `folder`, `document`, `video`, `audio`, `image`, `unknown`.
+
+## Development
+
+``` bash
+# install dependencies
+npm install
+
+# serve with hot reload at localhost:8080
+npm run dev
+
+# build for production with minification
+npm run build
+
+# build for production and view the bundle analyzer report
+npm run build --report
+```
+
+[npm-image]: https://img.shields.io/npm/v/vue-simple-uploader.svg?style=flat
+[npm-url]: https://npmjs.org/package/vue-simple-uploader
+[downloads-image]: https://img.shields.io/npm/dm/vue-simple-uploader.svg?style=flat
+[downloads-url]: https://npmjs.org/package/vue-simple-uploader
+[juejin-image]: https://badge.juejin.im/entry/599dad0ff265da248b04d7b8/likes.svg?style=flat
+[juejin-url]: https://juejin.im/entry/599dad0ff265da248b04d7b8/detail

+ 405 - 0
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/README_zh-CN.md

@@ -0,0 +1,405 @@
+# vue-simple-uploader  [![NPM Version][npm-image]][npm-url] [![NPM Downloads][downloads-image]][downloads-url] [![juejin likes][juejin-image]](juejin-url)
+
+> 一个基于 [simple-uploader.js](https://github.com/simple-uploader/Uploader) 的 Vue 上传组件
+
+![example](https://github.com/simple-uploader/vue-uploader/blob/master/example/simple-uploader.gif)
+
+![QQ](https://github.com/simple-uploader/Uploader/blob/develop/assets/simple-uploader-QQ-2.png?raw=true)
+
+## 特性
+
+* 支持文件、多文件、文件夹上传
+
+* 支持拖拽文件、文件夹上传
+
+* 统一对待文件和文件夹,方便操作管理
+
+* 可暂停、继续上传
+
+* 错误处理
+
+* 支持“快传”,通过文件判断服务端是否已存在从而实现“快传”
+
+* 上传队列管理,支持最大并发上传
+
+* 分块上传
+
+* 支持进度、预估剩余时间、出错自动重试、重传等操作
+
+## 安装
+
+``` bash
+npm install vue-simple-uploader --save
+```
+
+## 笔记周边
+
+- https://www.cnblogs.com/xiahj/p/vue-simple-uploader.html
+- https://github.com/LuoLiangDSGA/spring-learning/tree/master/boot-uploader
+- http://www.smarthu.club
+
+## 使用
+
+### 初始化
+
+``` js
+import Vue from 'vue'
+import uploader from 'vue-simple-uploader'
+import App from './App.vue'
+
+Vue.use(uploader)
+
+/* eslint-disable no-new */
+new Vue({
+  render(createElement) {
+    return createElement(App)
+  }
+}).$mount('#app')
+```
+
+### App.vue
+
+``` vue
+<template>
+  <uploader :options="options" class="uploader-example">
+    <uploader-unsupport></uploader-unsupport>
+    <uploader-drop>
+      <p>Drop files here to upload or</p>
+      <uploader-btn>select files</uploader-btn>
+      <uploader-btn :attrs="attrs">select images</uploader-btn>
+      <uploader-btn :directory="true">select folder</uploader-btn>
+    </uploader-drop>
+    <uploader-list></uploader-list>
+  </uploader>
+</template>
+
+<script>
+  export default {
+    data () {
+      return {
+        options: {
+          // https://github.com/simple-uploader/Uploader/tree/develop/samples/Node.js
+          target: '//localhost:3000/upload',
+          testChunks: false
+        },
+        attrs: {
+          accept: 'image/*'
+        }
+      }
+    }
+  }
+</script>
+
+<style>
+  .uploader-example {
+    width: 880px;
+    padding: 15px;
+    margin: 40px auto 0;
+    font-size: 12px;
+    box-shadow: 0 0 10px rgba(0, 0, 0, .4);
+  }
+  .uploader-example .uploader-btn {
+    margin-right: 4px;
+  }
+  .uploader-example .uploader-list {
+    max-height: 440px;
+    overflow: auto;
+    overflow-x: hidden;
+    overflow-y: auto;
+  }
+</style>
+```
+
+## 组件
+
+### Uploader
+
+上传根组件,可理解为一个上传器。
+
+#### Props
+
+* `options {Object}`
+
+  参考 [simple-uploader.js 配置](https://github.com/simple-uploader/Uploader/blob/develop/README_zh-CN.md#配置)。
+
+  此外,你可以有如下配置项可选:
+
+  - `parseTimeRemaining(timeRemaining, parsedTimeRemaining) {Function}`
+
+    用于格式化你想要剩余时间,一般可以用来做多语言。参数:
+
+    - `timeRemaining{Number}`, 剩余时间,秒为单位
+
+    - `parsedTimeRemaining{String}`, 默认展示的剩余时间内容,你也可以这样做替换使用:
+
+      ```js
+      parseTimeRemaining: function (timeRemaining, parsedTimeRemaining) {
+        return parsedTimeRemaining
+          .replace(/\syears?/, '年')
+          .replace(/\days?/, '天')
+          .replace(/\shours?/, '小时')
+          .replace(/\sminutes?/, '分钟')
+          .replace(/\sseconds?/, '秒')
+      }
+      ```
+
+  - `categoryMap {Object}`
+
+    文件类型 map,默认:
+
+    ```js
+    {
+      image: ['gif', 'jpg', 'jpeg', 'png', 'bmp', 'webp'],
+      video: ['mp4', 'm3u8', 'rmvb', 'avi', 'swf', '3gp', 'mkv', 'flv'],
+      audio: ['mp3', 'wav', 'wma', 'ogg', 'aac', 'flac'],
+      document: ['doc', 'txt', 'docx', 'pages', 'epub', 'pdf', 'numbers', 'csv', 'xls', 'xlsx', 'keynote', 'ppt', 'pptx']
+    }
+    ```
+
+* `autoStart {Boolean}`
+
+  默认 `true`, 是否选择文件后自动开始上传。
+
+* `fileStatusText {Object}`
+
+  默认:
+  ```js
+  {
+    success: 'success',
+    error: 'error',
+    uploading: 'uploading',
+    paused: 'paused',
+    waiting: 'waiting'
+  }
+  ```
+  用于转换文件上传状态文本映射对象。
+
+  0.6.0 版本之后,`fileStatusText` 可以设置为一个函数,参数为 `(status, response = null)`, 第一个 status 为状态,第二个为响应内容,默认 null,示例:
+
+  ```js
+  fileStatusText(status, response) {
+    const statusTextMap = {
+      uploading: 'uploading',
+      paused: 'paused',
+      waiting: 'waiting'
+    }
+    if (status === 'success' || status === 'error') {
+      // 只有status为success或者error的时候可以使用 response
+
+      // eg:
+      // return response data ?
+      return response.data
+    } else {
+      return statusTextMap[status]
+    }
+  }
+  ```
+
+#### 事件
+
+参见 [simple-uploader.js uploader 事件](https://github.com/simple-uploader/Uploader/blob/develop/README_zh-CN.md#事件)
+
+**注意:**
+
+* 所有的事件都会通过 [lodash.kebabCase](https://github.com/lodash/lodash/blob/master/kebabCase.js) 做转换,例如 `fileSuccess` 就会变成 `file-success`。
+
+* `catch-all` 这个事件是不会触发的。
+
+* `file-added(file)`, 添加了一个文件事件,一般用做文件校验,如果设置 `file.ignored = true` 的话这个文件就会被过滤掉。
+
+* `files-added(files, fileList)`, 添加了一批文件事件,一般用做一次选择的多个文件进行校验,如果设置 `files.ignored = true` 或者 ``fileList.ignored = true`` 的话本次选择的文件就会被过滤掉。
+
+#### 作用域插槽
+
+* `files {Array}`
+
+  纯文件列表,没有文件夹概念。
+
+* `fileList {Array}`
+
+  统一对待文件、文件夹列表。
+
+* `started`
+
+  是否开始上传了。
+
+#### 得到 `Uploader` 实例
+
+可以通过如下方式获得:
+
+```js
+// 在 uploader 组件上会有 uploader 属性 指向的就是 Uploader 实例
+const uploaderInstance = this.$refs.uploader.uploader
+// 这里可以调用实例方法
+// https://github.com/simple-uploader/Uploader/blob/develop/README_zh-CN.md#方法
+uploaderInstance.cancel()
+```
+
+### UploaderBtn
+
+点选上传文件按钮。
+
+#### Props
+
+* `directory {Boolean}`
+
+  默认 `false`, 是否是文件夹上传。
+
+* `single {Boolean}`
+
+  默认 `false`, 如果设为 `true`,则代表一次只能选择一个文件。
+
+* `attrs {Object}`
+
+  默认 `{}`, 添加到 input 元素上的额外属性。
+
+### UploaderDrop
+
+拖拽上传区域。
+
+### UploaderList
+
+文件、文件夹列表,同等对待。
+
+#### 作用域插槽
+
+* `fileList {Array}`
+
+  文件、文件夹组成数组。
+
+### UploaderFiles
+
+文件列表,没有文件夹概念,纯文件列表。
+
+#### 作用域插槽
+
+* `files {Array}`
+
+  文件列表。
+
+### UploaderUnsupport
+
+不支持 HTML5 File API 的时候会显示。
+
+### UploaderFile
+
+文件、文件夹单个组件。
+
+#### Props
+
+* `file {Uploader.File}`
+
+  封装的文件实例。
+
+* `list {Boolean}`
+
+  如果是在 `UploaderList` 组件中使用的话,请设置为 `true`。
+
+#### 作用域插槽
+
+* `file {Uploader.File}`
+
+  文件实例。
+
+* `list {Boolean}`
+
+  是否在 `UploaderList` 组件中使用。
+
+* `status {String}`
+
+  当前状态,可能是:`success`, `error`, `uploading`, `paused`, `waiting`
+
+* `paused {Boolean}`
+
+  是否暂停了。
+
+* `error {Boolean}`
+
+  是否出错了。
+
+* `averageSpeed {Number}`
+
+  平均上传速度,单位字节每秒。
+
+* `formatedAverageSpeed {String}`
+
+  格式化后的平均上传速度,类似:`3 KB / S`。
+
+* `currentSpeed {Number}`
+
+  当前上传速度,单位字节每秒。
+
+* `isComplete {Boolean}`
+
+  是否已经上传完成。
+
+* `isUploading {Boolean}`
+
+  是否在上传中。
+
+* `size {Number}`
+
+  文件或者文件夹大小。
+
+* `formatedSize {Number}`
+
+  格式化后文件或者文件夹大小,类似:`10 KB`.
+
+* `uploadedSize {Number}`
+
+  已经上传大小,单位字节。
+
+* `progress {Number}`
+
+  介于 0 到 1 之间的小数,上传进度。
+
+* `progressStyle {String}`
+
+  进度样式,transform 属性,类似:`{transform: '-50%'}`.
+
+* `progressingClass {String}`
+
+  正在上传中的时候值为:`uploader-file-progressing`。
+
+* `timeRemaining {Number}`
+
+  预估剩余时间,单位秒。
+
+* `formatedTimeRemaining {String}`
+
+  格式化后剩余时间,类似:`3 miniutes`.
+
+* `type {String}`
+
+  文件类型。
+
+* `extension {String}`
+
+  文件名后缀,小写。
+
+* `fileCategory {String}`
+
+  文件分类,其中之一:`folder`, `document`, `video`, `audio`, `image`, `unknown`。
+
+## Development
+
+``` bash
+# install dependencies
+npm install
+
+# serve with hot reload at localhost:8080
+npm run dev
+
+# build for production with minification
+npm run build
+
+# build for production and view the bundle analyzer report
+npm run build --report
+```
+
+[npm-image]: https://img.shields.io/npm/v/vue-simple-uploader.svg?style=flat
+[npm-url]: https://npmjs.org/package/vue-simple-uploader
+[downloads-image]: https://img.shields.io/npm/dm/vue-simple-uploader.svg?style=flat
+[downloads-url]: https://npmjs.org/package/vue-simple-uploader
+[juejin-image]: https://badge.juejin.im/entry/599dad0ff265da248b04d7b8/likes.svg?style=flat
+[juejin-url]: https://juejin.im/entry/599dad0ff265da248b04d7b8/detail

File diff suppressed because it is too large
+ 2 - 0
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/dist/vue-uploader.js


File diff suppressed because it is too large
+ 1 - 0
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/dist/vue-uploader.js.map


+ 59 - 0
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/example/App.vue

@@ -0,0 +1,59 @@
+<template>
+  <uploader :options="options" :file-status-text="statusText" class="uploader-example" ref="uploader" @file-complete="fileComplete" @complete="complete"></uploader>
+</template>
+
+<script>
+  export default {
+    data () {
+      return {
+        options: {
+          target: '//localhost:3000/upload', // '//jsonplaceholder.typicode.com/posts/',
+          testChunks: false
+        },
+        attrs: {
+          accept: 'image/*'
+        },
+        statusText: {
+          success: '成功了',
+          error: '出错了',
+          uploading: '上传中',
+          paused: '暂停中',
+          waiting: '等待中'
+        }
+      }
+    },
+    methods: {
+      complete () {
+        debugger
+        console.log('complete', arguments)
+      },
+      fileComplete () {
+        console.log('file complete', arguments)
+      }
+    },
+    mounted () {
+      this.$nextTick(() => {
+        window.uploader = this.$refs.uploader.uploader
+      })
+    }
+  }
+</script>
+
+<style>
+  .uploader-example {
+    width: 880px;
+    padding: 15px;
+    margin: 40px auto 0;
+    font-size: 12px;
+    box-shadow: 0 0 10px rgba(0, 0, 0, .4);
+  }
+  .uploader-example .uploader-btn {
+    margin-right: 4px;
+  }
+  .uploader-example .uploader-list {
+    max-height: 440px;
+    overflow: auto;
+    overflow-x: hidden;
+    overflow-y: auto;
+  }
+</style>

+ 11 - 0
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/example/index.html

@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>vue-uploader</title>
+  </head>
+  <body>
+    <div id="app"></div>
+    <!-- built files will be auto injected -->
+  </body>
+</html>

+ 12 - 0
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/example/main.js

@@ -0,0 +1,12 @@
+import Vue from 'vue'
+import uploader from '../src'
+import App from './App.vue'
+
+Vue.use(uploader)
+
+/* eslint-disable no-new */
+new Vue({
+  render(createElement) {
+    return createElement(App)
+  }
+}).$mount('#app')

BIN
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/example/simple-uploader.gif


+ 43 - 0
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/node_modules/simple-uploader.js/LICENSE

@@ -0,0 +1,43 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 doly mood
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+The MIT License (MIT)
+
+Copyright (c) 2011, 23, http://www.23developer.com
+              2013, Aidas Klimas
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

File diff suppressed because it is too large
+ 267 - 0
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/node_modules/simple-uploader.js/README.md


BIN
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/node_modules/simple-uploader.js/assets/simple-uploader-QQ-2.png


BIN
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/node_modules/simple-uploader.js/assets/simple-uploader-QQ.jpg


File diff suppressed because it is too large
+ 1610 - 0
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/node_modules/simple-uploader.js/dist/uploader.js


File diff suppressed because it is too large
+ 9 - 0
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/node_modules/simple-uploader.js/dist/uploader.min.js


+ 70 - 0
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/node_modules/simple-uploader.js/package.json

@@ -0,0 +1,70 @@
+{
+  "name": "simple-uploader.js",
+  "version": "0.5.6",
+  "author": "dolymood <dolymood@gmail.com>",
+  "license": "MIT",
+  "description": "Uploader library implements html5 file upload and provides multiple simultaneous, stable, fault tolerant and resumable uploads",
+  "keywords": [
+    "simple-uploader",
+    "simple-uploader.js",
+    "uploader",
+    "uploader.js",
+    "resumable.js",
+    "flow.js",
+    "file upload",
+    "resumable upload",
+    "chunk upload",
+    "html5 upload",
+    "javascript upload",
+    "upload"
+  ],
+  "main": "src/uploader.js",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/simple-uploader/Uploader.git"
+  },
+  "bugs": "https://github.com/simple-uploader/Uploader/issues",
+  "homepage": "https://github.com/simple-uploader/Uploader",
+  "scripts": {
+    "dev": "gulp watch",
+    "build": "gulp build",
+    "codecov": "codecov",
+    "test": "gulp",
+    "test:unit": "gulp test",
+    "test:cover": "gulp cover",
+    "test:ci": "gulp ci",
+    "release": "gulp release"
+  },
+  "devDependencies": {
+    "browserify-versionify": "^1.0.6",
+    "codecov": "^3.6.5",
+    "eslint": "^4.2.0",
+    "eslint-config-standard": "^6.1.0",
+    "eslint-plugin-promise": "^3.5.0",
+    "eslint-plugin-standard": "^3.0.1",
+    "gulp": "^3.9.1",
+    "gulp-browserify": "^0.5.1",
+    "gulp-concat": "^2.6.1",
+    "gulp-eslint": "^4.0.0",
+    "gulp-git": "^2.4.1",
+    "gulp-header": "^1.8.8",
+    "gulp-sourcemaps": "^2.6.0",
+    "gulp-tag-version": "^1.3.0",
+    "gulp-uglify": "^3.0.0",
+    "jasmine": "^2.6.0",
+    "jasmine-core": "^2.6.4",
+    "karma": "^1.7.0",
+    "karma-chrome-launcher": "^2.1.1",
+    "karma-commonjs": "^1.0.0",
+    "karma-coverage": "^1.1.1",
+    "karma-firefox-launcher": "^1.0.1",
+    "karma-jasmine": "^1.1.0",
+    "karma-phantomjs-launcher": "^1.0.4",
+    "karma-sauce-launcher": "^1.1.0",
+    "pump": "^1.0.2",
+    "sinon": "1.7.3"
+  },
+  "__npminstall_done": "Wed Jun 02 2021 14:28:26 GMT+0800 (中国标准时间)",
+  "_from": "simple-uploader.js@0.5.6",
+  "_resolved": "https://registry.npm.taobao.org/simple-uploader.js/download/simple-uploader.js-0.5.6.tgz"
+}

+ 315 - 0
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/node_modules/simple-uploader.js/src/chunk.js

@@ -0,0 +1,315 @@
+var utils = require('./utils')
+
+function Chunk (uploader, file, offset) {
+  utils.defineNonEnumerable(this, 'uploader', uploader)
+  utils.defineNonEnumerable(this, 'file', file)
+  utils.defineNonEnumerable(this, 'bytes', null)
+  this.offset = offset
+  this.tested = false
+  this.retries = 0
+  this.pendingRetry = false
+  this.preprocessState = 0
+  this.readState = 0
+  this.loaded = 0
+  this.total = 0
+  this.chunkSize = this.uploader.opts.chunkSize
+  this.startByte = this.offset * this.chunkSize
+  this.endByte = this.computeEndByte()
+  this.xhr = null
+}
+
+var STATUS = Chunk.STATUS = {
+  PENDING: 'pending',
+  UPLOADING: 'uploading',
+  READING: 'reading',
+  SUCCESS: 'success',
+  ERROR: 'error',
+  COMPLETE: 'complete',
+  PROGRESS: 'progress',
+  RETRY: 'retry'
+}
+
+utils.extend(Chunk.prototype, {
+
+  _event: function (evt, args) {
+    args = utils.toArray(arguments)
+    args.unshift(this)
+    this.file._chunkEvent.apply(this.file, args)
+  },
+
+  computeEndByte: function () {
+    var endByte = Math.min(this.file.size, (this.offset + 1) * this.chunkSize)
+    if (this.file.size - endByte < this.chunkSize && !this.uploader.opts.forceChunkSize) {
+      // The last chunk will be bigger than the chunk size,
+      // but less than 2 * this.chunkSize
+      endByte = this.file.size
+    }
+    return endByte
+  },
+
+  getParams: function () {
+    return {
+      chunkNumber: this.offset + 1,
+      chunkSize: this.uploader.opts.chunkSize,
+      currentChunkSize: this.endByte - this.startByte,
+      totalSize: this.file.size,
+      identifier: this.file.uniqueIdentifier,
+      filename: this.file.name,
+      relativePath: this.file.relativePath,
+      totalChunks: this.file.chunks.length
+    }
+  },
+
+  getTarget: function (target, params) {
+    if (!params.length) {
+      return target
+    }
+    if (target.indexOf('?') < 0) {
+      target += '?'
+    } else {
+      target += '&'
+    }
+    return target + params.join('&')
+  },
+
+  test: function () {
+    this.xhr = new XMLHttpRequest()
+    this.xhr.addEventListener('load', testHandler, false)
+    this.xhr.addEventListener('error', testHandler, false)
+    var testMethod = utils.evalOpts(this.uploader.opts.testMethod, this.file, this)
+    var data = this.prepareXhrRequest(testMethod, true)
+    this.xhr.send(data)
+
+    var $ = this
+    function testHandler (event) {
+      var status = $.status(true)
+      if (status === STATUS.ERROR) {
+        $._event(status, $.message())
+        $.uploader.uploadNextChunk()
+      } else if (status === STATUS.SUCCESS) {
+        $._event(status, $.message())
+        $.tested = true
+      } else if (!$.file.paused) {
+        // Error might be caused by file pause method
+        // Chunks does not exist on the server side
+        $.tested = true
+        $.send()
+      }
+    }
+  },
+
+  preprocessFinished: function () {
+    // Compute the endByte after the preprocess function to allow an
+    // implementer of preprocess to set the fileObj size
+    this.endByte = this.computeEndByte()
+    this.preprocessState = 2
+    this.send()
+  },
+
+  readFinished: function (bytes) {
+    this.readState = 2
+    this.bytes = bytes
+    this.send()
+  },
+
+  send: function () {
+    var preprocess = this.uploader.opts.preprocess
+    var read = this.uploader.opts.readFileFn
+    if (utils.isFunction(preprocess)) {
+      switch (this.preprocessState) {
+        case 0:
+          this.preprocessState = 1
+          preprocess(this)
+          return
+        case 1:
+          return
+      }
+    }
+    switch (this.readState) {
+      case 0:
+        this.readState = 1
+        read(this.file, this.file.fileType, this.startByte, this.endByte, this)
+        return
+      case 1:
+        return
+    }
+    if (this.uploader.opts.testChunks && !this.tested) {
+      this.test()
+      return
+    }
+
+    this.loaded = 0
+    this.total = 0
+    this.pendingRetry = false
+
+    // Set up request and listen for event
+    this.xhr = new XMLHttpRequest()
+    this.xhr.upload.addEventListener('progress', progressHandler, false)
+    this.xhr.addEventListener('load', doneHandler, false)
+    this.xhr.addEventListener('error', doneHandler, false)
+
+    var uploadMethod = utils.evalOpts(this.uploader.opts.uploadMethod, this.file, this)
+    var data = this.prepareXhrRequest(uploadMethod, false, this.uploader.opts.method, this.bytes)
+    this.xhr.send(data)
+
+    var $ = this
+    function progressHandler (event) {
+      if (event.lengthComputable) {
+        $.loaded = event.loaded
+        $.total = event.total
+      }
+      $._event(STATUS.PROGRESS, event)
+    }
+
+    function doneHandler (event) {
+      var msg = $.message()
+      $.processingResponse = true
+      $.uploader.opts.processResponse(msg, function (err, res) {
+        $.processingResponse = false
+        if (!$.xhr) {
+          return
+        }
+        $.processedState = {
+          err: err,
+          res: res
+        }
+        var status = $.status()
+        if (status === STATUS.SUCCESS || status === STATUS.ERROR) {
+          // delete this.data
+          $._event(status, res)
+          status === STATUS.ERROR && $.uploader.uploadNextChunk()
+        } else {
+          $._event(STATUS.RETRY, res)
+          $.pendingRetry = true
+          $.abort()
+          $.retries++
+          var retryInterval = $.uploader.opts.chunkRetryInterval
+          if (retryInterval !== null) {
+            setTimeout(function () {
+              $.send()
+            }, retryInterval)
+          } else {
+            $.send()
+          }
+        }
+      }, $.file, $)
+    }
+  },
+
+  abort: function () {
+    var xhr = this.xhr
+    this.xhr = null
+    this.processingResponse = false
+    this.processedState = null
+    if (xhr) {
+      xhr.abort()
+    }
+  },
+
+  status: function (isTest) {
+    if (this.readState === 1) {
+      return STATUS.READING
+    } else if (this.pendingRetry || this.preprocessState === 1) {
+      // if pending retry then that's effectively the same as actively uploading,
+      // there might just be a slight delay before the retry starts
+      return STATUS.UPLOADING
+    } else if (!this.xhr) {
+      return STATUS.PENDING
+    } else if (this.xhr.readyState < 4 || this.processingResponse) {
+      // Status is really 'OPENED', 'HEADERS_RECEIVED'
+      // or 'LOADING' - meaning that stuff is happening
+      return STATUS.UPLOADING
+    } else {
+      var _status
+      if (this.uploader.opts.successStatuses.indexOf(this.xhr.status) > -1) {
+        // HTTP 200, perfect
+        // HTTP 202 Accepted - The request has been accepted for processing, but the processing has not been completed.
+        _status = STATUS.SUCCESS
+      } else if (this.uploader.opts.permanentErrors.indexOf(this.xhr.status) > -1 ||
+          !isTest && this.retries >= this.uploader.opts.maxChunkRetries) {
+        // HTTP 415/500/501, permanent error
+        _status = STATUS.ERROR
+      } else {
+        // this should never happen, but we'll reset and queue a retry
+        // a likely case for this would be 503 service unavailable
+        this.abort()
+        _status = STATUS.PENDING
+      }
+      var processedState = this.processedState
+      if (processedState && processedState.err) {
+        _status = STATUS.ERROR
+      }
+      return _status
+    }
+  },
+
+  message: function () {
+    return this.xhr ? this.xhr.responseText : ''
+  },
+
+  progress: function () {
+    if (this.pendingRetry) {
+      return 0
+    }
+    var s = this.status()
+    if (s === STATUS.SUCCESS || s === STATUS.ERROR) {
+      return 1
+    } else if (s === STATUS.PENDING) {
+      return 0
+    } else {
+      return this.total > 0 ? this.loaded / this.total : 0
+    }
+  },
+
+  sizeUploaded: function () {
+    var size = this.endByte - this.startByte
+    // can't return only chunk.loaded value, because it is bigger than chunk size
+    if (this.status() !== STATUS.SUCCESS) {
+      size = this.progress() * size
+    }
+    return size
+  },
+
+  prepareXhrRequest: function (method, isTest, paramsMethod, blob) {
+    // Add data from the query options
+    var query = utils.evalOpts(this.uploader.opts.query, this.file, this, isTest)
+    query = utils.extend(this.getParams(), query)
+
+    // processParams
+    query = this.uploader.opts.processParams(query, this.file, this, isTest)
+
+    var target = utils.evalOpts(this.uploader.opts.target, this.file, this, isTest)
+    var data = null
+    if (method === 'GET' || paramsMethod === 'octet') {
+      // Add data from the query options
+      var params = []
+      utils.each(query, function (v, k) {
+        params.push([encodeURIComponent(k), encodeURIComponent(v)].join('='))
+      })
+      target = this.getTarget(target, params)
+      data = blob || null
+    } else {
+      // Add data from the query options
+      data = new FormData()
+      utils.each(query, function (v, k) {
+        data.append(k, v)
+      })
+      if (typeof blob !== 'undefined') {
+        data.append(this.uploader.opts.fileParameterName, blob, this.file.name)
+      }
+    }
+
+    this.xhr.open(method, target, true)
+    this.xhr.withCredentials = this.uploader.opts.withCredentials
+
+    // Add data from header options
+    utils.each(utils.evalOpts(this.uploader.opts.headers, this.file, this, isTest), function (v, k) {
+      this.xhr.setRequestHeader(k, v)
+    }, this)
+
+    return data
+  }
+
+})
+
+module.exports = Chunk

+ 49 - 0
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/node_modules/simple-uploader.js/src/event.js

@@ -0,0 +1,49 @@
+var each = require('./utils').each
+
+var event = {
+
+  _eventData: null,
+
+  on: function (name, func) {
+    if (!this._eventData) this._eventData = {}
+    if (!this._eventData[name]) this._eventData[name] = []
+    var listened = false
+    each(this._eventData[name], function (fuc) {
+      if (fuc === func) {
+        listened = true
+        return false
+      }
+    })
+    if (!listened) {
+      this._eventData[name].push(func)
+    }
+  },
+
+  off: function (name, func) {
+    if (!this._eventData) this._eventData = {}
+    if (!this._eventData[name] || !this._eventData[name].length) return
+    if (func) {
+      each(this._eventData[name], function (fuc, i) {
+        if (fuc === func) {
+          this._eventData[name].splice(i, 1)
+          return false
+        }
+      }, this)
+    } else {
+      this._eventData[name] = []
+    }
+  },
+
+  trigger: function (name) {
+    if (!this._eventData) this._eventData = {}
+    if (!this._eventData[name]) return true
+    var args = this._eventData[name].slice.call(arguments, 1)
+    var preventDefault = false
+    each(this._eventData[name], function (fuc) {
+      preventDefault = fuc.apply(this, args) === false || preventDefault
+    }, this)
+    return !preventDefault
+  }
+}
+
+module.exports = event

+ 541 - 0
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/node_modules/simple-uploader.js/src/file.js

@@ -0,0 +1,541 @@
+var utils = require('./utils')
+var Chunk = require('./chunk')
+
+function File (uploader, file, parent) {
+  utils.defineNonEnumerable(this, 'uploader', uploader)
+  this.isRoot = this.isFolder = uploader === this
+  utils.defineNonEnumerable(this, 'parent', parent || null)
+  utils.defineNonEnumerable(this, 'files', [])
+  utils.defineNonEnumerable(this, 'fileList', [])
+  utils.defineNonEnumerable(this, 'chunks', [])
+  utils.defineNonEnumerable(this, '_errorFiles', [])
+  utils.defineNonEnumerable(this, 'file', null)
+  this.id = utils.uid()
+
+  if (this.isRoot || !file) {
+    this.file = null
+  } else {
+    if (utils.isString(file)) {
+      // folder
+      this.isFolder = true
+      this.file = null
+      this.path = file
+      if (this.parent.path) {
+        file = file.substr(this.parent.path.length)
+      }
+      this.name = file.charAt(file.length - 1) === '/' ? file.substr(0, file.length - 1) : file
+    } else {
+      this.file = file
+      this.fileType = this.file.type
+      this.name = file.fileName || file.name
+      this.size = file.size
+      this.relativePath = file.relativePath || file.webkitRelativePath || this.name
+      this._parseFile()
+    }
+  }
+
+  this.paused = uploader.opts.initialPaused
+  this.error = false
+  this.allError = false
+  this.aborted = false
+  this.completed = false
+  this.averageSpeed = 0
+  this.currentSpeed = 0
+  this._lastProgressCallback = Date.now()
+  this._prevUploadedSize = 0
+  this._prevProgress = 0
+
+  this.bootstrap()
+}
+
+utils.extend(File.prototype, {
+
+  _parseFile: function () {
+    var ppaths = parsePaths(this.relativePath)
+    if (ppaths.length) {
+      var filePaths = this.uploader.filePaths
+      utils.each(ppaths, function (path, i) {
+        var folderFile = filePaths[path]
+        if (!folderFile) {
+          folderFile = new File(this.uploader, path, this.parent)
+          filePaths[path] = folderFile
+          this._updateParentFileList(folderFile)
+        }
+        this.parent = folderFile
+        folderFile.files.push(this)
+        if (!ppaths[i + 1]) {
+          folderFile.fileList.push(this)
+        }
+      }, this)
+    } else {
+      this._updateParentFileList()
+    }
+  },
+
+  _updateParentFileList: function (file) {
+    if (!file) {
+      file = this
+    }
+    var p = this.parent
+    if (p) {
+      p.fileList.push(file)
+    }
+  },
+
+  _eachAccess: function (eachFn, fileFn) {
+    if (this.isFolder) {
+      utils.each(this.files, function (f, i) {
+        return eachFn.call(this, f, i)
+      }, this)
+      return
+    }
+    fileFn.call(this, this)
+  },
+
+  bootstrap: function () {
+    if (this.isFolder) return
+    var opts = this.uploader.opts
+    if (utils.isFunction(opts.initFileFn)) {
+      opts.initFileFn.call(this, this)
+    }
+
+    this.abort(true)
+    this._resetError()
+    // Rebuild stack of chunks from file
+    this._prevProgress = 0
+    var round = opts.forceChunkSize ? Math.ceil : Math.floor
+    var chunks = Math.max(round(this.size / opts.chunkSize), 1)
+    for (var offset = 0; offset < chunks; offset++) {
+      this.chunks.push(new Chunk(this.uploader, this, offset))
+    }
+  },
+
+  _measureSpeed: function () {
+    var smoothingFactor = this.uploader.opts.speedSmoothingFactor
+    var timeSpan = Date.now() - this._lastProgressCallback
+    if (!timeSpan) {
+      return
+    }
+    var uploaded = this.sizeUploaded()
+    // Prevent negative upload speed after file upload resume
+    this.currentSpeed = Math.max((uploaded - this._prevUploadedSize) / timeSpan * 1000, 0)
+    this.averageSpeed = smoothingFactor * this.currentSpeed + (1 - smoothingFactor) * this.averageSpeed
+    this._prevUploadedSize = uploaded
+    if (this.parent && this.parent._checkProgress()) {
+      this.parent._measureSpeed()
+    }
+  },
+
+  _checkProgress: function (file) {
+    return Date.now() - this._lastProgressCallback >= this.uploader.opts.progressCallbacksInterval
+  },
+
+  _chunkEvent: function (chunk, evt, message) {
+    var uploader = this.uploader
+    var STATUS = Chunk.STATUS
+    var that = this
+    var rootFile = this.getRoot()
+    var triggerProgress = function () {
+      that._measureSpeed()
+      uploader._trigger('fileProgress', rootFile, that, chunk)
+      that._lastProgressCallback = Date.now()
+    }
+    switch (evt) {
+      case STATUS.PROGRESS:
+        if (this._checkProgress()) {
+          triggerProgress()
+        }
+        break
+      case STATUS.ERROR:
+        this._error()
+        this.abort(true)
+        uploader._trigger('fileError', rootFile, this, message, chunk)
+        break
+      case STATUS.SUCCESS:
+        this._updateUploadedChunks(message, chunk)
+        if (this.error) {
+          return
+        }
+        clearTimeout(this._progeressId)
+        this._progeressId = 0
+        var timeDiff = Date.now() - this._lastProgressCallback
+        if (timeDiff < uploader.opts.progressCallbacksInterval) {
+          this._progeressId = setTimeout(triggerProgress, uploader.opts.progressCallbacksInterval - timeDiff)
+        }
+        if (this.isComplete()) {
+          clearTimeout(this._progeressId)
+          triggerProgress()
+          this.currentSpeed = 0
+          this.averageSpeed = 0
+          uploader._trigger('fileSuccess', rootFile, this, message, chunk)
+          if (rootFile.isComplete()) {
+            uploader._trigger('fileComplete', rootFile, this)
+          }
+        } else if (!this._progeressId) {
+          triggerProgress()
+        }
+        break
+      case STATUS.RETRY:
+        uploader._trigger('fileRetry', rootFile, this, chunk)
+        break
+    }
+  },
+
+  _updateUploadedChunks: function (message, chunk) {
+    var checkChunkUploaded = this.uploader.opts.checkChunkUploadedByResponse
+    if (checkChunkUploaded) {
+      var xhr = chunk.xhr
+      utils.each(this.chunks, function (_chunk) {
+        if (!_chunk.tested) {
+          var uploaded = checkChunkUploaded.call(this, _chunk, message)
+          if (_chunk === chunk && !uploaded) {
+            // fix the first chunk xhr status
+            // treated as success but checkChunkUploaded is false
+            // so the current chunk should be uploaded again
+            _chunk.xhr = null
+          }
+          if (uploaded) {
+            // first success and other chunks are uploaded
+            // then set xhr, so the uploaded chunks
+            // will be treated as success too
+            _chunk.xhr = xhr
+          }
+          _chunk.tested = true
+        }
+      }, this)
+      if (!this._firstResponse) {
+        this._firstResponse = true
+        this.uploader.upload(true)
+      } else {
+        this.uploader.uploadNextChunk()
+      }
+    } else {
+      this.uploader.uploadNextChunk()
+    }
+  },
+
+  _error: function () {
+    this.error = this.allError = true
+    var parent = this.parent
+    while (parent && parent !== this.uploader) {
+      parent._errorFiles.push(this)
+      parent.error = true
+      if (parent._errorFiles.length === parent.files.length) {
+        parent.allError = true
+      }
+      parent = parent.parent
+    }
+  },
+
+  _resetError: function () {
+    this.error = this.allError = false
+    var parent = this.parent
+    var index = -1
+    while (parent && parent !== this.uploader) {
+      index = parent._errorFiles.indexOf(this)
+      parent._errorFiles.splice(index, 1)
+      parent.allError = false
+      if (!parent._errorFiles.length) {
+        parent.error = false
+      }
+      parent = parent.parent
+    }
+  },
+
+  isComplete: function () {
+    if (!this.completed) {
+      var outstanding = false
+      this._eachAccess(function (file) {
+        if (!file.isComplete()) {
+          outstanding = true
+          return false
+        }
+      }, function () {
+        if (this.error) {
+          outstanding = true
+        } else {
+          var STATUS = Chunk.STATUS
+          utils.each(this.chunks, function (chunk) {
+            var status = chunk.status()
+            if (status === STATUS.ERROR || status === STATUS.PENDING || status === STATUS.UPLOADING || status === STATUS.READING || chunk.preprocessState === 1 || chunk.readState === 1) {
+              outstanding = true
+              return false
+            }
+          })
+        }
+      })
+      this.completed = !outstanding
+    }
+    return this.completed
+  },
+
+  isUploading: function () {
+    var uploading = false
+    this._eachAccess(function (file) {
+      if (file.isUploading()) {
+        uploading = true
+        return false
+      }
+    }, function () {
+      var uploadingStatus = Chunk.STATUS.UPLOADING
+      utils.each(this.chunks, function (chunk) {
+        if (chunk.status() === uploadingStatus) {
+          uploading = true
+          return false
+        }
+      })
+    })
+    return uploading
+  },
+
+  resume: function () {
+    this._eachAccess(function (f) {
+      f.resume()
+    }, function () {
+      this.paused = false
+      this.aborted = false
+      this.uploader.upload()
+    })
+    this.paused = false
+    this.aborted = false
+  },
+
+  pause: function () {
+    this._eachAccess(function (f) {
+      f.pause()
+    }, function () {
+      this.paused = true
+      this.abort()
+    })
+    this.paused = true
+  },
+
+  cancel: function () {
+    this.uploader.removeFile(this)
+  },
+
+  retry: function (file) {
+    var fileRetry = function (file) {
+      if (file.error) {
+        file.bootstrap()
+      }
+    }
+    if (file) {
+      file.bootstrap()
+    } else {
+      this._eachAccess(fileRetry, function () {
+        this.bootstrap()
+      })
+    }
+    this.uploader.upload()
+  },
+
+  abort: function (reset) {
+    if (this.aborted) {
+      return
+    }
+    this.currentSpeed = 0
+    this.averageSpeed = 0
+    this.aborted = !reset
+    var chunks = this.chunks
+    if (reset) {
+      this.chunks = []
+    }
+    var uploadingStatus = Chunk.STATUS.UPLOADING
+    utils.each(chunks, function (c) {
+      if (c.status() === uploadingStatus) {
+        c.abort()
+        this.uploader.uploadNextChunk()
+      }
+    }, this)
+  },
+
+  progress: function () {
+    var totalDone = 0
+    var totalSize = 0
+    var ret = 0
+    this._eachAccess(function (file, index) {
+      totalDone += file.progress() * file.size
+      totalSize += file.size
+      if (index === this.files.length - 1) {
+        ret = totalSize > 0 ? totalDone / totalSize : this.isComplete() ? 1 : 0
+      }
+    }, function () {
+      if (this.error) {
+        ret = 1
+        return
+      }
+      if (this.chunks.length === 1) {
+        this._prevProgress = Math.max(this._prevProgress, this.chunks[0].progress())
+        ret = this._prevProgress
+        return
+      }
+      // Sum up progress across everything
+      var bytesLoaded = 0
+      utils.each(this.chunks, function (c) {
+        // get chunk progress relative to entire file
+        bytesLoaded += c.progress() * (c.endByte - c.startByte)
+      })
+      var percent = bytesLoaded / this.size
+      // We don't want to lose percentages when an upload is paused
+      this._prevProgress = Math.max(this._prevProgress, percent > 0.9999 ? 1 : percent)
+      ret = this._prevProgress
+    })
+    return ret
+  },
+
+  getSize: function () {
+    var size = 0
+    this._eachAccess(function (file) {
+      size += file.size
+    }, function () {
+      size += this.size
+    })
+    return size
+  },
+
+  getFormatSize: function () {
+    var size = this.getSize()
+    return utils.formatSize(size)
+  },
+
+  getRoot: function () {
+    if (this.isRoot) {
+      return this
+    }
+    var parent = this.parent
+    while (parent) {
+      if (parent.parent === this.uploader) {
+        // find it
+        return parent
+      }
+      parent = parent.parent
+    }
+    return this
+  },
+
+  sizeUploaded: function () {
+    var size = 0
+    this._eachAccess(function (file) {
+      size += file.sizeUploaded()
+    }, function () {
+      utils.each(this.chunks, function (chunk) {
+        size += chunk.sizeUploaded()
+      })
+    })
+    return size
+  },
+
+  timeRemaining: function () {
+    var ret = 0
+    var sizeDelta = 0
+    var averageSpeed = 0
+    this._eachAccess(function (file, i) {
+      if (!file.paused && !file.error) {
+        sizeDelta += file.size - file.sizeUploaded()
+        averageSpeed += file.averageSpeed
+      }
+      if (i === this.files.length - 1) {
+        ret = calRet(sizeDelta, averageSpeed)
+      }
+    }, function () {
+      if (this.paused || this.error) {
+        ret = 0
+        return
+      }
+      var delta = this.size - this.sizeUploaded()
+      ret = calRet(delta, this.averageSpeed)
+    })
+    return ret
+    function calRet (delta, averageSpeed) {
+      if (delta && !averageSpeed) {
+        return Number.POSITIVE_INFINITY
+      }
+      if (!delta && !averageSpeed) {
+        return 0
+      }
+      return Math.floor(delta / averageSpeed)
+    }
+  },
+
+  removeFile: function (file) {
+    if (file.isFolder) {
+      while (file.files.length) {
+        var f = file.files[file.files.length - 1]
+        this._removeFile(f)
+      }
+    }
+    this._removeFile(file)
+  },
+
+  _delFilePath: function (file) {
+    if (file.path && this.filePaths) {
+      delete this.filePaths[file.path]
+    }
+    utils.each(file.fileList, function (file) {
+      this._delFilePath(file)
+    }, this)
+  },
+
+  _removeFile: function (file) {
+    if (!file.isFolder) {
+      utils.each(this.files, function (f, i) {
+        if (f === file) {
+          this.files.splice(i, 1)
+          return false
+        }
+      }, this)
+      file.abort()
+      var parent = file.parent
+      var newParent
+      while (parent && parent !== this) {
+        newParent = parent.parent
+        parent._removeFile(file)
+        parent = newParent
+      }
+    }
+    file.parent === this && utils.each(this.fileList, function (f, i) {
+      if (f === file) {
+        this.fileList.splice(i, 1)
+        return false
+      }
+    }, this)
+    if (!this.isRoot && this.isFolder && !this.files.length) {
+      this.parent._removeFile(this)
+      this.uploader._delFilePath(this)
+    }
+    file.parent = null
+  },
+
+  getType: function () {
+    if (this.isFolder) {
+      return 'folder'
+    }
+    return this.file.type && this.file.type.split('/')[1]
+  },
+
+  getExtension: function () {
+    if (this.isFolder) {
+      return ''
+    }
+    return this.name.substr((~-this.name.lastIndexOf('.') >>> 0) + 2).toLowerCase()
+  }
+
+})
+
+module.exports = File
+
+function parsePaths (path) {
+  var ret = []
+  var paths = path.split('/')
+  var len = paths.length
+  var i = 1
+  paths.splice(len - 1, 1)
+  len--
+  if (paths.length) {
+    while (i <= len) {
+      ret.push(paths.slice(0, i++).join('/') + '/')
+    }
+  }
+  return ret
+}

+ 514 - 0
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/node_modules/simple-uploader.js/src/uploader.js

@@ -0,0 +1,514 @@
+var utils = require('./utils')
+var event = require('./event')
+var File = require('./file')
+var Chunk = require('./chunk')
+
+var version = '__VERSION__'
+
+var isServer = typeof window === 'undefined'
+
+// ie10+
+var ie10plus = isServer ? false : window.navigator.msPointerEnabled
+var support = (function () {
+  if (isServer) {
+    return false
+  }
+  var sliceName = 'slice'
+  var _support = utils.isDefined(window.File) && utils.isDefined(window.Blob) &&
+                utils.isDefined(window.FileList)
+  var bproto = null
+  if (_support) {
+    bproto = window.Blob.prototype
+    utils.each(['slice', 'webkitSlice', 'mozSlice'], function (n) {
+      if (bproto[n]) {
+        sliceName = n
+        return false
+      }
+    })
+    _support = !!bproto[sliceName]
+  }
+  if (_support) Uploader.sliceName = sliceName
+  bproto = null
+  return _support
+})()
+
+var supportDirectory = (function () {
+  if (isServer) {
+    return false
+  }
+  var input = window.document.createElement('input')
+  input.type = 'file'
+  var sd = 'webkitdirectory' in input || 'directory' in input
+  input = null
+  return sd
+})()
+
+function Uploader (opts) {
+  this.support = support
+  /* istanbul ignore if */
+  if (!this.support) {
+    return
+  }
+  this.supportDirectory = supportDirectory
+  utils.defineNonEnumerable(this, 'filePaths', {})
+  this.opts = utils.extend({}, Uploader.defaults, opts || {})
+
+  this.preventEvent = utils.bind(this._preventEvent, this)
+
+  File.call(this, this)
+}
+
+/**
+ * Default read function using the webAPI
+ *
+ * @function webAPIFileRead(fileObj, fileType, startByte, endByte, chunk)
+ *
+ */
+var webAPIFileRead = function (fileObj, fileType, startByte, endByte, chunk) {
+  chunk.readFinished(fileObj.file[Uploader.sliceName](startByte, endByte, fileType))
+}
+
+Uploader.version = version
+
+Uploader.defaults = {
+  chunkSize: 1024 * 1024,
+  forceChunkSize: false,
+  simultaneousUploads: 3,
+  singleFile: false,
+  fileParameterName: 'file',
+  progressCallbacksInterval: 500,
+  speedSmoothingFactor: 0.1,
+  query: {},
+  headers: {},
+  withCredentials: false,
+  preprocess: null,
+  method: 'multipart',
+  testMethod: 'GET',
+  uploadMethod: 'POST',
+  prioritizeFirstAndLastChunk: false,
+  allowDuplicateUploads: false,
+  target: '/',
+  testChunks: true,
+  generateUniqueIdentifier: null,
+  maxChunkRetries: 0,
+  chunkRetryInterval: null,
+  permanentErrors: [404, 415, 500, 501],
+  successStatuses: [200, 201, 202],
+  onDropStopPropagation: false,
+  initFileFn: null,
+  readFileFn: webAPIFileRead,
+  checkChunkUploadedByResponse: null,
+  initialPaused: false,
+  processResponse: function (response, cb) {
+    cb(null, response)
+  },
+  processParams: function (params) {
+    return params
+  }
+}
+
+Uploader.utils = utils
+Uploader.event = event
+Uploader.File = File
+Uploader.Chunk = Chunk
+
+// inherit file
+Uploader.prototype = utils.extend({}, File.prototype)
+// inherit event
+utils.extend(Uploader.prototype, event)
+utils.extend(Uploader.prototype, {
+
+  constructor: Uploader,
+
+  _trigger: function (name) {
+    var args = utils.toArray(arguments)
+    var preventDefault = !this.trigger.apply(this, arguments)
+    if (name !== 'catchAll') {
+      args.unshift('catchAll')
+      preventDefault = !this.trigger.apply(this, args) || preventDefault
+    }
+    return !preventDefault
+  },
+
+  _triggerAsync: function () {
+    var args = arguments
+    utils.nextTick(function () {
+      this._trigger.apply(this, args)
+    }, this)
+  },
+
+  addFiles: function (files, evt) {
+    var _files = []
+    var oldFileListLen = this.fileList.length
+    utils.each(files, function (file) {
+      // Uploading empty file IE10/IE11 hangs indefinitely
+      // Directories have size `0` and name `.`
+      // Ignore already added files if opts.allowDuplicateUploads is set to false
+      if ((!ie10plus || ie10plus && file.size > 0) && !(file.size % 4096 === 0 && (file.name === '.' || file.fileName === '.'))) {
+        var uniqueIdentifier = this.generateUniqueIdentifier(file)
+        if (this.opts.allowDuplicateUploads || !this.getFromUniqueIdentifier(uniqueIdentifier)) {
+          var _file = new File(this, file, this)
+          _file.uniqueIdentifier = uniqueIdentifier
+          if (this._trigger('fileAdded', _file, evt)) {
+            _files.push(_file)
+          } else {
+            File.prototype.removeFile.call(this, _file)
+          }
+        }
+      }
+    }, this)
+    // get new fileList
+    var newFileList = this.fileList.slice(oldFileListLen)
+    if (this._trigger('filesAdded', _files, newFileList, evt)) {
+      utils.each(_files, function (file) {
+        if (this.opts.singleFile && this.files.length > 0) {
+          this.removeFile(this.files[0])
+        }
+        this.files.push(file)
+      }, this)
+      this._trigger('filesSubmitted', _files, newFileList, evt)
+    } else {
+      utils.each(newFileList, function (file) {
+        File.prototype.removeFile.call(this, file)
+      }, this)
+    }
+  },
+
+  addFile: function (file, evt) {
+    this.addFiles([file], evt)
+  },
+
+  cancel: function () {
+    for (var i = this.fileList.length - 1; i >= 0; i--) {
+      this.fileList[i].cancel()
+    }
+  },
+
+  removeFile: function (file) {
+    File.prototype.removeFile.call(this, file)
+    this._trigger('fileRemoved', file)
+  },
+
+  generateUniqueIdentifier: function (file) {
+    var custom = this.opts.generateUniqueIdentifier
+    if (utils.isFunction(custom)) {
+      return custom(file)
+    }
+    /* istanbul ignore next */
+    // Some confusion in different versions of Firefox
+    var relativePath = file.relativePath || file.webkitRelativePath || file.fileName || file.name
+    /* istanbul ignore next */
+    return file.size + '-' + relativePath.replace(/[^0-9a-zA-Z_-]/img, '')
+  },
+
+  getFromUniqueIdentifier: function (uniqueIdentifier) {
+    var ret = false
+    utils.each(this.files, function (file) {
+      if (file.uniqueIdentifier === uniqueIdentifier) {
+        ret = file
+        return false
+      }
+    })
+    return ret
+  },
+
+  uploadNextChunk: function (preventEvents) {
+    var found = false
+    var pendingStatus = Chunk.STATUS.PENDING
+    var checkChunkUploaded = this.uploader.opts.checkChunkUploadedByResponse
+    if (this.opts.prioritizeFirstAndLastChunk) {
+      utils.each(this.files, function (file) {
+        if (file.paused) {
+          return
+        }
+        if (checkChunkUploaded && !file._firstResponse && file.isUploading()) {
+          // waiting for current file's first chunk response
+          return
+        }
+        if (file.chunks.length && file.chunks[0].status() === pendingStatus) {
+          file.chunks[0].send()
+          found = true
+          return false
+        }
+        if (file.chunks.length > 1 && file.chunks[file.chunks.length - 1].status() === pendingStatus) {
+          file.chunks[file.chunks.length - 1].send()
+          found = true
+          return false
+        }
+      })
+      if (found) {
+        return found
+      }
+    }
+
+    // Now, simply look for the next, best thing to upload
+    utils.each(this.files, function (file) {
+      if (!file.paused) {
+        if (checkChunkUploaded && !file._firstResponse && file.isUploading()) {
+          // waiting for current file's first chunk response
+          return
+        }
+        utils.each(file.chunks, function (chunk) {
+          if (chunk.status() === pendingStatus) {
+            chunk.send()
+            found = true
+            return false
+          }
+        })
+      }
+      if (found) {
+        return false
+      }
+    })
+    if (found) {
+      return true
+    }
+
+    // The are no more outstanding chunks to upload, check is everything is done
+    var outstanding = false
+    utils.each(this.files, function (file) {
+      if (!file.isComplete()) {
+        outstanding = true
+        return false
+      }
+    })
+    // should check files now
+    // if now files in list
+    // should not trigger complete event
+    if (!outstanding && !preventEvents && this.files.length) {
+      // All chunks have been uploaded, complete
+      this._triggerAsync('complete')
+    }
+    return outstanding
+  },
+
+  upload: function (preventEvents) {
+    // Make sure we don't start too many uploads at once
+    var ret = this._shouldUploadNext()
+    if (ret === false) {
+      return
+    }
+    !preventEvents && this._trigger('uploadStart')
+    var started = false
+    for (var num = 1; num <= this.opts.simultaneousUploads - ret; num++) {
+      started = this.uploadNextChunk(!preventEvents) || started
+      if (!started && preventEvents) {
+        // completed
+        break
+      }
+    }
+    if (!started && !preventEvents) {
+      this._triggerAsync('complete')
+    }
+  },
+
+  /**
+   * should upload next chunk
+   * @function
+   * @returns {Boolean|Number}
+   */
+  _shouldUploadNext: function () {
+    var num = 0
+    var should = true
+    var simultaneousUploads = this.opts.simultaneousUploads
+    var uploadingStatus = Chunk.STATUS.UPLOADING
+    utils.each(this.files, function (file) {
+      utils.each(file.chunks, function (chunk) {
+        if (chunk.status() === uploadingStatus) {
+          num++
+          if (num >= simultaneousUploads) {
+            should = false
+            return false
+          }
+        }
+      })
+      return should
+    })
+    // if should is true then return uploading chunks's length
+    return should && num
+  },
+
+  /**
+   * Assign a browse action to one or more DOM nodes.
+   * @function
+   * @param {Element|Array.<Element>} domNodes
+   * @param {boolean} isDirectory Pass in true to allow directories to
+   * @param {boolean} singleFile prevent multi file upload
+   * @param {Object} attributes set custom attributes:
+   *  http://www.w3.org/TR/html-markup/input.file.html#input.file-attributes
+   *  eg: accept: 'image/*'
+   * be selected (Chrome only).
+   */
+  assignBrowse: function (domNodes, isDirectory, singleFile, attributes) {
+    if (typeof domNodes.length === 'undefined') {
+      domNodes = [domNodes]
+    }
+
+    utils.each(domNodes, function (domNode) {
+      var input
+      if (domNode.tagName === 'INPUT' && domNode.type === 'file') {
+        input = domNode
+      } else {
+        input = document.createElement('input')
+        input.setAttribute('type', 'file')
+        // display:none - not working in opera 12
+        utils.extend(input.style, {
+          visibility: 'hidden',
+          position: 'absolute',
+          width: '1px',
+          height: '1px'
+        })
+        // for opera 12 browser, input must be assigned to a document
+        domNode.appendChild(input)
+        // https://developer.mozilla.org/en/using_files_from_web_applications)
+        // event listener is executed two times
+        // first one - original mouse click event
+        // second - input.click(), input is inside domNode
+        domNode.addEventListener('click', function (e) {
+          if (domNode.tagName.toLowerCase() === 'label') {
+            return
+          }
+          input.click()
+        }, false)
+      }
+      if (!this.opts.singleFile && !singleFile) {
+        input.setAttribute('multiple', 'multiple')
+      }
+      if (isDirectory) {
+        input.setAttribute('webkitdirectory', 'webkitdirectory')
+      }
+      attributes && utils.each(attributes, function (value, key) {
+        input.setAttribute(key, value)
+      })
+      // When new files are added, simply append them to the overall list
+      var that = this
+      input.addEventListener('change', function (e) {
+        that._trigger(e.type, e)
+        if (e.target.value) {
+          that.addFiles(e.target.files, e)
+          e.target.value = ''
+        }
+      }, false)
+    }, this)
+  },
+
+  onDrop: function (evt) {
+    this._trigger(evt.type, evt)
+    if (this.opts.onDropStopPropagation) {
+      evt.stopPropagation()
+    }
+    evt.preventDefault()
+    this._parseDataTransfer(evt.dataTransfer, evt)
+  },
+
+  _parseDataTransfer: function (dataTransfer, evt) {
+    if (dataTransfer.items && dataTransfer.items[0] &&
+      dataTransfer.items[0].webkitGetAsEntry) {
+      this.webkitReadDataTransfer(dataTransfer, evt)
+    } else {
+      this.addFiles(dataTransfer.files, evt)
+    }
+  },
+
+  webkitReadDataTransfer: function (dataTransfer, evt) {
+    var self = this
+    var queue = dataTransfer.items.length
+    var files = []
+    utils.each(dataTransfer.items, function (item) {
+      var entry = item.webkitGetAsEntry()
+      if (!entry) {
+        decrement()
+        return
+      }
+      if (entry.isFile) {
+        // due to a bug in Chrome's File System API impl - #149735
+        fileReadSuccess(item.getAsFile(), entry.fullPath)
+      } else {
+        readDirectory(entry.createReader())
+      }
+    })
+    function readDirectory (reader) {
+      reader.readEntries(function (entries) {
+        if (entries.length) {
+          queue += entries.length
+          utils.each(entries, function (entry) {
+            if (entry.isFile) {
+              var fullPath = entry.fullPath
+              entry.file(function (file) {
+                fileReadSuccess(file, fullPath)
+              }, readError)
+            } else if (entry.isDirectory) {
+              readDirectory(entry.createReader())
+            }
+          })
+          readDirectory(reader)
+        } else {
+          decrement()
+        }
+      }, readError)
+    }
+    function fileReadSuccess (file, fullPath) {
+      // relative path should not start with "/"
+      file.relativePath = fullPath.substring(1)
+      files.push(file)
+      decrement()
+    }
+    function readError (fileError) {
+      throw fileError
+    }
+    function decrement () {
+      if (--queue === 0) {
+        self.addFiles(files, evt)
+      }
+    }
+  },
+
+  _assignHelper: function (domNodes, handles, remove) {
+    if (typeof domNodes.length === 'undefined') {
+      domNodes = [domNodes]
+    }
+    var evtMethod = remove ? 'removeEventListener' : 'addEventListener'
+    utils.each(domNodes, function (domNode) {
+      utils.each(handles, function (handler, name) {
+        domNode[evtMethod](name, handler, false)
+      }, this)
+    }, this)
+  },
+
+  _preventEvent: function (e) {
+    utils.preventEvent(e)
+    this._trigger(e.type, e)
+  },
+
+  /**
+   * Assign one or more DOM nodes as a drop target.
+   * @function
+   * @param {Element|Array.<Element>} domNodes
+   */
+  assignDrop: function (domNodes) {
+    this._onDrop = utils.bind(this.onDrop, this)
+    this._assignHelper(domNodes, {
+      dragover: this.preventEvent,
+      dragenter: this.preventEvent,
+      dragleave: this.preventEvent,
+      drop: this._onDrop
+    })
+  },
+
+  /**
+   * Un-assign drop event from DOM nodes
+   * @function
+   * @param domNodes
+   */
+  unAssignDrop: function (domNodes) {
+    this._assignHelper(domNodes, {
+      dragover: this.preventEvent,
+      dragenter: this.preventEvent,
+      dragleave: this.preventEvent,
+      drop: this._onDrop
+    }, true)
+    this._onDrop = null
+  }
+})
+
+module.exports = Uploader

+ 171 - 0
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/node_modules/simple-uploader.js/src/utils.js

@@ -0,0 +1,171 @@
+var oproto = Object.prototype
+var aproto = Array.prototype
+var serialize = oproto.toString
+
+var isFunction = function (fn) {
+  return serialize.call(fn) === '[object Function]'
+}
+
+var isArray = Array.isArray || /* istanbul ignore next */ function (ary) {
+  return serialize.call(ary) === '[object Array]'
+}
+
+var isPlainObject = function (obj) {
+  return serialize.call(obj) === '[object Object]' && Object.getPrototypeOf(obj) === oproto
+}
+
+var i = 0
+var utils = {
+  uid: function () {
+    return ++i
+  },
+  noop: function () {},
+  bind: function (fn, context) {
+    return function () {
+      return fn.apply(context, arguments)
+    }
+  },
+  preventEvent: function (evt) {
+    evt.preventDefault()
+  },
+  stop: function (evt) {
+    evt.preventDefault()
+    evt.stopPropagation()
+  },
+  nextTick: function (fn, context) {
+    setTimeout(utils.bind(fn, context), 0)
+  },
+  toArray: function (ary, start, end) {
+    if (start === undefined) start = 0
+    if (end === undefined) end = ary.length
+    return aproto.slice.call(ary, start, end)
+  },
+
+  isPlainObject: isPlainObject,
+  isFunction: isFunction,
+  isArray: isArray,
+  isObject: function (obj) {
+    return Object(obj) === obj
+  },
+  isString: function (s) {
+    return typeof s === 'string'
+  },
+  isUndefined: function (a) {
+    return typeof a === 'undefined'
+  },
+  isDefined: function (a) {
+    return typeof a !== 'undefined'
+  },
+
+  each: function (ary, func, context) {
+    if (utils.isDefined(ary.length)) {
+      for (var i = 0, len = ary.length; i < len; i++) {
+        if (func.call(context, ary[i], i, ary) === false) {
+          break
+        }
+      }
+    } else {
+      for (var k in ary) {
+        if (func.call(context, ary[k], k, ary) === false) {
+          break
+        }
+      }
+    }
+  },
+
+  /**
+   * If option is a function, evaluate it with given params
+   * @param {*} data
+   * @param {...} args arguments of a callback
+   * @returns {*}
+   */
+  evalOpts: function (data, args) {
+    if (utils.isFunction(data)) {
+      // `arguments` is an object, not array, in FF, so:
+      args = utils.toArray(arguments)
+      data = data.apply(null, args.slice(1))
+    }
+    return data
+  },
+
+  extend: function () {
+    var options
+    var name
+    var src
+    var copy
+    var copyIsArray
+    var clone
+    var target = arguments[0] || {}
+    var i = 1
+    var length = arguments.length
+    var force = false
+
+    // 如果第一个参数为布尔,判定是否深拷贝
+    if (typeof target === 'boolean') {
+      force = target
+      target = arguments[1] || {}
+      i++
+    }
+
+    // 确保接受方为一个复杂的数据类型
+    if (typeof target !== 'object' && !isFunction(target)) {
+      target = {}
+    }
+
+    // 如果只有一个参数,那么新成员添加于 extend 所在的对象上
+    if (i === length) {
+      target = this
+      i--
+    }
+
+    for (; i < length; i++) {
+      // 只处理非空参数
+      if ((options = arguments[i]) != null) {
+        for (name in options) {
+          src = target[name]
+          copy = options[name]
+
+          // 防止环引用
+          if (target === copy) {
+            continue
+          }
+          if (force && copy && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) {
+            if (copyIsArray) {
+              copyIsArray = false
+              clone = src && isArray(src) ? src : []
+            } else {
+              clone = src && isPlainObject(src) ? src : {}
+            }
+            target[name] = utils.extend(force, clone, copy)
+          } else if (copy !== undefined) {
+            target[name] = copy
+          }
+        }
+      }
+    }
+    return target
+  },
+
+  formatSize: function (size) {
+    if (size < 1024) {
+      return size.toFixed(0) + ' bytes'
+    } else if (size < 1024 * 1024) {
+      return (size / 1024.0).toFixed(0) + ' KB'
+    } else if (size < 1024 * 1024 * 1024) {
+      return (size / 1024.0 / 1024.0).toFixed(1) + ' MB'
+    } else {
+      return (size / 1024.0 / 1024.0 / 1024.0).toFixed(1) + ' GB'
+    }
+  },
+
+  defineNonEnumerable: function (target, key, value) {
+    Object.defineProperty(target, key, {
+      enumerable: false,
+      configurable: true,
+      writable: true,
+      value: value
+    })
+  }
+}
+
+module.exports = utils

+ 14 - 0
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/node_modules/simple-uploader.js/test.sh

@@ -0,0 +1,14 @@
+#!/bin/bash
+set -e
+
+if [ $TESTNAME = "unit-tests" ]; then
+  echo "Running unit-tests"
+  export DISPLAY=:99.0
+  sh -e /etc/init.d/xvfb start
+  sleep 1
+  npm run test
+  npm run codecov
+elif [ $TESTNAME = "browser-tests" ]; then
+  echo "Running browser-tests"
+  npm run test:ci
+fi

+ 86 - 0
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/package.json

@@ -0,0 +1,86 @@
+{
+  "name": "vue-simple-uploader",
+  "version": "0.7.6",
+  "description": "A Vue.js upload component powered by simple-uploader.js",
+  "main": "dist/vue-uploader.js",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/simple-uploader/vue-uploader.git"
+  },
+  "bugs": "https://github.com/simple-uploader/vue-uploader/issues",
+  "homepage": "https://github.com/simple-uploader/vue-uploader",
+  "author": "dolymood@gmail.com",
+  "license": "MIT",
+  "scripts": {
+    "dev": "node build/dev-server.js",
+    "start": "node build/dev-server.js",
+    "build": "node build/build.js",
+    "lint": "eslint --ext .js,.vue src test/unit/specs"
+  },
+  "dependencies": {
+    "simple-uploader.js": "^0.5.6"
+  },
+  "peerDependencies": {
+    "vue": ">=2.2"
+  },
+  "devDependencies": {
+    "autoprefixer": "^7.1.2",
+    "babel-core": "^6.22.1",
+    "babel-eslint": "^7.1.1",
+    "babel-loader": "^7.1.1",
+    "babel-plugin-add-module-exports": "^0.2.1",
+    "babel-plugin-transform-es2015-modules-umd": "^6.24.1",
+    "babel-plugin-transform-runtime": "^6.22.0",
+    "babel-preset-env": "^1.3.2",
+    "babel-preset-stage-2": "^6.22.0",
+    "babel-register": "^6.22.0",
+    "chalk": "^2.0.1",
+    "connect-history-api-fallback": "^1.3.0",
+    "copy-webpack-plugin": "^4.0.1",
+    "css-loader": "^0.28.0",
+    "cssnano": "^3.10.0",
+    "eslint": "^4.18.2",
+    "eslint-config-standard": "^6.2.1",
+    "eslint-friendly-formatter": "^3.0.0",
+    "eslint-loader": "^1.7.1",
+    "eslint-plugin-html": "^3.0.0",
+    "eslint-plugin-promise": "^3.4.0",
+    "eslint-plugin-standard": "^2.0.1",
+    "eventsource-polyfill": "^0.9.6",
+    "express": "^4.14.1",
+    "extract-text-webpack-plugin": "^2.0.0",
+    "file-loader": "^0.11.1",
+    "friendly-errors-webpack-plugin": "^1.1.3",
+    "html-webpack-plugin": "^2.28.0",
+    "http-proxy-middleware": "^0.17.3",
+    "opn": "^5.1.0",
+    "optimize-css-assets-webpack-plugin": "^2.0.0",
+    "ora": "^1.2.0",
+    "rimraf": "^2.6.0",
+    "semver": "^5.3.0",
+    "shelljs": "^0.7.6",
+    "url-loader": "^0.5.8",
+    "vue": "^2.3.3",
+    "vue-loader": "^12.1.0",
+    "vue-style-loader": "^3.0.1",
+    "vue-template-compiler": "^2.3.3",
+    "webpack": "^2.6.1",
+    "webpack-bundle-analyzer": "^3.3.2",
+    "webpack-dev-middleware": "^1.10.0",
+    "webpack-hot-middleware": "^2.18.0",
+    "webpack-merge": "^4.1.0"
+  },
+  "engines": {
+    "node": ">= 4.0.0",
+    "npm": ">= 3.0.0"
+  },
+  "browserslist": [
+    "> 1%",
+    "not ie <= 8",
+    "Android >= 4.0",
+    "iOS >= 8"
+  ],
+  "__npminstall_done": "Wed Jun 02 2021 14:28:26 GMT+0800 (中国标准时间)",
+  "_from": "vue-simple-uploader@0.7.6",
+  "_resolved": "https://registry.npm.taobao.org/vue-simple-uploader/download/vue-simple-uploader-0.7.6.tgz?cache=0&sync_timestamp=1603790927320&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-simple-uploader%2Fdownload%2Fvue-simple-uploader-0.7.6.tgz"
+}

+ 3 - 0
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/src/common/file-events.js

@@ -0,0 +1,3 @@
+const events = ['fileProgress', 'fileSuccess', 'fileComplete', 'fileError']
+
+export default events

+ 14 - 0
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/src/common/mixins.js

@@ -0,0 +1,14 @@
+export const uploaderMixin = {
+  inject: ['uploader']
+}
+
+export const supportMixin = {
+  data () {
+    return {
+      support: true
+    }
+  },
+  mounted () {
+    this.support = this.uploader.uploader.support
+  }
+}

+ 27 - 0
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/src/common/utils.js

@@ -0,0 +1,27 @@
+export function secondsToStr (temp) {
+  const years = Math.floor(temp / 31536000)
+  if (years) {
+    return years + ' year' + numberEnding(years)
+  }
+  const days = Math.floor((temp %= 31536000) / 86400)
+  if (days) {
+    return days + ' day' + numberEnding(days)
+  }
+  const hours = Math.floor((temp %= 86400) / 3600)
+  if (hours) {
+    return hours + ' hour' + numberEnding(hours)
+  }
+  const minutes = Math.floor((temp %= 3600) / 60)
+  if (minutes) {
+    return minutes + ' minute' + numberEnding(minutes)
+  }
+  const seconds = temp % 60
+  return seconds + ' second' + numberEnding(seconds)
+  function numberEnding (number) {
+    return (number > 1) ? 's' : ''
+  }
+}
+
+export function kebabCase (s) {
+  return s.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`)
+}

+ 56 - 0
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/src/components/btn.vue

@@ -0,0 +1,56 @@
+<template>
+  <label class="uploader-btn" ref="btn" v-show="support">
+    <slot></slot>
+  </label>
+</template>
+
+<script>
+  import { uploaderMixin, supportMixin } from '../common/mixins'
+
+  const COMPONENT_NAME = 'uploader-btn'
+
+  export default {
+    name: COMPONENT_NAME,
+    mixins: [uploaderMixin, supportMixin],
+    props: {
+      directory: {
+        type: Boolean,
+        default: false
+      },
+      single: {
+        type: Boolean,
+        default: false
+      },
+      attrs: {
+        type: Object,
+        default () {
+          return {}
+        }
+      }
+    },
+    mounted () {
+      this.$nextTick(() => {
+        this.uploader.uploader.assignBrowse(this.$refs.btn, this.directory, this.single, this.attrs)
+      })
+    }
+  }
+</script>
+
+<style>
+  .uploader-btn {
+    display: inline-block;
+    position: relative;
+    padding: 4px 8px;
+    font-size: 100%;
+    line-height: 1.4;
+    color: #666;
+    border: 1px solid #666;
+    cursor: pointer;
+    border-radius: 2px;
+    background: none;
+    outline: none;
+  }
+  .uploader-btn:hover {
+    background-color: rgba(0, 0, 0, .08);
+  }
+</style>

+ 64 - 0
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/src/components/drop.vue

@@ -0,0 +1,64 @@
+<template>
+  <div class="uploader-drop" :class="dropClass" ref="drop" v-show="support">
+    <slot></slot>
+  </div>
+</template>
+
+<script>
+  import { uploaderMixin, supportMixin } from '../common/mixins'
+
+  const COMPONENT_NAME = 'uploader-drop'
+
+  export default {
+    name: COMPONENT_NAME,
+    mixins: [uploaderMixin, supportMixin],
+    data () {
+      return {
+        dropClass: ''
+      }
+    },
+    methods: {
+      onDragEnter () {
+        this.dropClass = 'uploader-dragover'
+      },
+      onDragLeave () {
+        this.dropClass = ''
+      },
+      onDrop () {
+        this.dropClass = 'uploader-droped'
+      }
+    },
+    mounted () {
+      this.$nextTick(() => {
+        const dropEle = this.$refs.drop
+        const uploader = this.uploader.uploader
+        uploader.assignDrop(dropEle)
+        uploader.on('dragenter', this.onDragEnter)
+        uploader.on('dragleave', this.onDragLeave)
+        uploader.on('drop', this.onDrop)
+      })
+    },
+    beforeDestroy () {
+      const dropEle = this.$refs.drop
+      const uploader = this.uploader.uploader
+      uploader.off('dragenter', this.onDragEnter)
+      uploader.off('dragleave', this.onDragLeave)
+      uploader.off('drop', this.onDrop)
+      uploader.unAssignDrop(dropEle)
+    }
+  }
+</script>
+
+<style>
+  .uploader-drop {
+    position: relative;
+    padding: 10px;
+    overflow: hidden;
+    border: 1px dashed #ccc;
+    background-color: #f5f5f5;
+  }
+  .uploader-dragover {
+    border-color: #999;
+    background-color: #f7f7f7;
+  }
+</style>

File diff suppressed because it is too large
+ 441 - 0
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/src/components/file.vue


+ 42 - 0
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/src/components/files.vue

@@ -0,0 +1,42 @@
+<template>
+  <div class="uploader-files">
+    <slot :files="files">
+      <ul>
+        <li v-for="file in files" :key="file.id">
+          <uploader-file :file="file"></uploader-file>
+        </li>
+      </ul>
+    </slot>
+  </div>
+</template>
+
+<script>
+  import { uploaderMixin } from '../common/mixins'
+  import UploaderFile from './file.vue'
+
+  const COMPONENT_NAME = 'uploader-files'
+
+  export default {
+    name: COMPONENT_NAME,
+    mixins: [uploaderMixin],
+    computed: {
+      files () {
+        return this.uploader.files
+      }
+    },
+    components: {
+      UploaderFile
+    }
+  }
+</script>
+
+<style>
+  .uploader-files {
+    position: relative;
+  }
+  .uploader-files > ul {
+    list-style: none;
+    margin: 0;
+    padding: 0
+  }
+</style>

+ 42 - 0
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/src/components/list.vue

@@ -0,0 +1,42 @@
+<template>
+  <div class="uploader-list">
+    <slot :file-list="fileList">
+      <ul>
+        <li v-for="file in fileList" :key="file.id">
+          <uploader-file :file="file" :list="true"></uploader-file>
+        </li>
+      </ul>
+    </slot>
+  </div>
+</template>
+
+<script>
+  import { uploaderMixin } from '../common/mixins'
+  import UploaderFile from './file.vue'
+
+  const COMPONENT_NAME = 'uploader-list'
+
+  export default {
+    name: COMPONENT_NAME,
+    mixins: [uploaderMixin],
+    computed: {
+      fileList () {
+        return this.uploader.fileList
+      }
+    },
+    components: {
+      UploaderFile
+    }
+  }
+</script>
+
+<style>
+  .uploader-list {
+    position: relative;
+  }
+  .uploader-list > ul {
+    list-style: none;
+    margin: 0;
+    padding: 0
+  }
+</style>

+ 28 - 0
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/src/components/unsupport.vue

@@ -0,0 +1,28 @@
+<template>
+  <div class="uploader-unsupport" v-show="!support">
+    <slot>
+      <p>
+        Your browser, unfortunately, is not supported by Uploader.js. The library requires support for <a href="http://www.w3.org/TR/FileAPI/">the HTML5 File API</a> along with <a href="http://www.w3.org/TR/FileAPI/#normalization-of-params">file slicing</a>.
+      </p>
+    </slot>
+  </div>
+</template>
+
+<script>
+  import { uploaderMixin, supportMixin } from '../common/mixins'
+
+  const COMPONENT_NAME = 'uploader-unsupport'
+
+  export default {
+    name: COMPONENT_NAME,
+    mixins: [uploaderMixin, supportMixin]
+  }
+</script>
+
+<style>
+  .uploader-unsupport {
+    position: relative;
+    z-index: 10;
+    overflow: hidden;
+  }
+</style>

+ 150 - 0
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/src/components/uploader.vue

@@ -0,0 +1,150 @@
+<template>
+  <div class="uploader">
+    <slot :files="files" :file-list="fileList" :started="started">
+      <uploader-unsupport></uploader-unsupport>
+      <uploader-drop>
+        <p>Drop files here to upload or</p>
+        <uploader-btn>select files</uploader-btn>
+        <uploader-btn :directory="true">select folder</uploader-btn>
+      </uploader-drop>
+      <uploader-list></uploader-list>
+    </slot>
+  </div>
+</template>
+
+<script>
+  import Uploader from 'simple-uploader.js'
+  import { kebabCase } from '../common/utils'
+  import UploaderBtn from './btn.vue'
+  import UploaderDrop from './drop.vue'
+  import UploaderUnsupport from './unsupport.vue'
+  import UploaderList from './list.vue'
+  import UploaderFiles from './files.vue'
+  import UploaderFile from './file.vue'
+
+  const COMPONENT_NAME = 'uploader'
+  const FILE_ADDED_EVENT = 'fileAdded'
+  const FILES_ADDED_EVENT = 'filesAdded'
+  const UPLOAD_START_EVENT = 'uploadStart'
+
+  export default {
+    name: COMPONENT_NAME,
+    provide () {
+      return {
+        uploader: this
+      }
+    },
+    props: {
+      options: {
+        type: Object,
+        default () {
+          return {}
+        }
+      },
+      autoStart: {
+        type: Boolean,
+        default: true
+      },
+      fileStatusText: {
+        type: [Object, Function],
+        default () {
+          return {
+            success: 'success',
+            error: 'error',
+            uploading: 'uploading',
+            paused: 'paused',
+            waiting: 'waiting'
+          }
+        }
+      }
+    },
+    data () {
+      return {
+        started: false,
+        files: [],
+        fileList: []
+      }
+    },
+    methods: {
+      uploadStart () {
+        this.started = true
+      },
+      fileAdded (file) {
+        this.$emit(kebabCase(FILE_ADDED_EVENT), file)
+        if (file.ignored) {
+          // is ignored, filter it
+          return false
+        }
+      },
+      filesAdded (files, fileList) {
+        this.$emit(kebabCase(FILES_ADDED_EVENT), files, fileList)
+        if (files.ignored || fileList.ignored) {
+          // is ignored, filter it
+          return false
+        }
+      },
+      fileRemoved (file) {
+        this.files = this.uploader.files
+        this.fileList = this.uploader.fileList
+      },
+      filesSubmitted (files, fileList) {
+        this.files = this.uploader.files
+        this.fileList = this.uploader.fileList
+        if (this.autoStart) {
+          this.uploader.upload()
+        }
+      },
+      allEvent (...args) {
+        const name = args[0]
+        const EVENTSMAP = {
+          [FILE_ADDED_EVENT]: true,
+          [FILES_ADDED_EVENT]: true,
+          [UPLOAD_START_EVENT]: 'uploadStart'
+        }
+        const handler = EVENTSMAP[name]
+        if (handler) {
+          if (handler === true) {
+            return
+          }
+          this[handler].apply(this, args.slice(1))
+        }
+        args[0] = kebabCase(name)
+        this.$emit.apply(this, args)
+      }
+    },
+    created () {
+      this.options.initialPaused = !this.autoStart
+      const uploader = new Uploader(this.options)
+      this.uploader = uploader
+      this.uploader.fileStatusText = this.fileStatusText
+      uploader.on('catchAll', this.allEvent)
+      uploader.on(FILE_ADDED_EVENT, this.fileAdded)
+      uploader.on(FILES_ADDED_EVENT, this.filesAdded)
+      uploader.on('fileRemoved', this.fileRemoved)
+      uploader.on('filesSubmitted', this.filesSubmitted)
+    },
+    destroyed () {
+      const uploader = this.uploader
+      uploader.off('catchAll', this.allEvent)
+      uploader.off(FILE_ADDED_EVENT, this.fileAdded)
+      uploader.off(FILES_ADDED_EVENT, this.filesAdded)
+      uploader.off('fileRemoved', this.fileRemoved)
+      uploader.off('filesSubmitted', this.filesSubmitted)
+      this.uploader = null
+    },
+    components: {
+      UploaderBtn,
+      UploaderDrop,
+      UploaderUnsupport,
+      UploaderList,
+      UploaderFiles,
+      UploaderFile
+    }
+  }
+</script>
+
+<style>
+  .uploader {
+    position: relative;
+  }
+</style>

+ 38 - 0
node_modules/_vue-simple-uploader@0.7.6@vue-simple-uploader/src/index.js

@@ -0,0 +1,38 @@
+import Uploader from './components/uploader.vue'
+import UploaderBtn from './components/btn.vue'
+import UploaderDrop from './components/drop.vue'
+import UploaderUnsupport from './components/unsupport.vue'
+import UploaderList from './components/list.vue'
+import UploaderFiles from './components/files.vue'
+import UploaderFile from './components/file.vue'
+
+const uploader = {
+  version: /* eslint-disable no-undef */ __VERSION__,
+  install,
+  Uploader,
+  UploaderBtn,
+  UploaderDrop,
+  UploaderUnsupport,
+  UploaderList,
+  UploaderFiles,
+  UploaderFile
+}
+
+if (typeof window !== 'undefined' && window.Vue) {
+  window.Vue.use(install)
+}
+
+export default uploader
+
+function install (Vue) {
+  if (install.installed) {
+    return
+  }
+  Vue.component(Uploader.name, Uploader)
+  Vue.component(UploaderBtn.name, UploaderBtn)
+  Vue.component(UploaderDrop.name, UploaderDrop)
+  Vue.component(UploaderUnsupport.name, UploaderUnsupport)
+  Vue.component(UploaderList.name, UploaderList)
+  Vue.component(UploaderFiles.name, UploaderFiles)
+  Vue.component(UploaderFile.name, UploaderFile)
+}

+ 43 - 0
node_modules/simple-uploader.js/LICENSE

@@ -0,0 +1,43 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 doly mood
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+The MIT License (MIT)
+
+Copyright (c) 2011, 23, http://www.23developer.com
+              2013, Aidas Klimas
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

File diff suppressed because it is too large
+ 267 - 0
node_modules/simple-uploader.js/README.md


BIN
node_modules/simple-uploader.js/assets/simple-uploader-QQ-2.png


BIN
node_modules/simple-uploader.js/assets/simple-uploader-QQ.jpg


File diff suppressed because it is too large
+ 1610 - 0
node_modules/simple-uploader.js/dist/uploader.js


File diff suppressed because it is too large
+ 9 - 0
node_modules/simple-uploader.js/dist/uploader.min.js


+ 70 - 0
node_modules/simple-uploader.js/package.json

@@ -0,0 +1,70 @@
+{
+  "name": "simple-uploader.js",
+  "version": "0.5.6",
+  "author": "dolymood <dolymood@gmail.com>",
+  "license": "MIT",
+  "description": "Uploader library implements html5 file upload and provides multiple simultaneous, stable, fault tolerant and resumable uploads",
+  "keywords": [
+    "simple-uploader",
+    "simple-uploader.js",
+    "uploader",
+    "uploader.js",
+    "resumable.js",
+    "flow.js",
+    "file upload",
+    "resumable upload",
+    "chunk upload",
+    "html5 upload",
+    "javascript upload",
+    "upload"
+  ],
+  "main": "src/uploader.js",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/simple-uploader/Uploader.git"
+  },
+  "bugs": "https://github.com/simple-uploader/Uploader/issues",
+  "homepage": "https://github.com/simple-uploader/Uploader",
+  "scripts": {
+    "dev": "gulp watch",
+    "build": "gulp build",
+    "codecov": "codecov",
+    "test": "gulp",
+    "test:unit": "gulp test",
+    "test:cover": "gulp cover",
+    "test:ci": "gulp ci",
+    "release": "gulp release"
+  },
+  "devDependencies": {
+    "browserify-versionify": "^1.0.6",
+    "codecov": "^3.6.5",
+    "eslint": "^4.2.0",
+    "eslint-config-standard": "^6.1.0",
+    "eslint-plugin-promise": "^3.5.0",
+    "eslint-plugin-standard": "^3.0.1",
+    "gulp": "^3.9.1",
+    "gulp-browserify": "^0.5.1",
+    "gulp-concat": "^2.6.1",
+    "gulp-eslint": "^4.0.0",
+    "gulp-git": "^2.4.1",
+    "gulp-header": "^1.8.8",
+    "gulp-sourcemaps": "^2.6.0",
+    "gulp-tag-version": "^1.3.0",
+    "gulp-uglify": "^3.0.0",
+    "jasmine": "^2.6.0",
+    "jasmine-core": "^2.6.4",
+    "karma": "^1.7.0",
+    "karma-chrome-launcher": "^2.1.1",
+    "karma-commonjs": "^1.0.0",
+    "karma-coverage": "^1.1.1",
+    "karma-firefox-launcher": "^1.0.1",
+    "karma-jasmine": "^1.1.0",
+    "karma-phantomjs-launcher": "^1.0.4",
+    "karma-sauce-launcher": "^1.1.0",
+    "pump": "^1.0.2",
+    "sinon": "1.7.3"
+  },
+  "__npminstall_done": "Wed Jun 02 2021 14:28:26 GMT+0800 (中国标准时间)",
+  "_from": "simple-uploader.js@0.5.6",
+  "_resolved": "https://registry.npm.taobao.org/simple-uploader.js/download/simple-uploader.js-0.5.6.tgz"
+}

+ 315 - 0
node_modules/simple-uploader.js/src/chunk.js

@@ -0,0 +1,315 @@
+var utils = require('./utils')
+
+function Chunk (uploader, file, offset) {
+  utils.defineNonEnumerable(this, 'uploader', uploader)
+  utils.defineNonEnumerable(this, 'file', file)
+  utils.defineNonEnumerable(this, 'bytes', null)
+  this.offset = offset
+  this.tested = false
+  this.retries = 0
+  this.pendingRetry = false
+  this.preprocessState = 0
+  this.readState = 0
+  this.loaded = 0
+  this.total = 0
+  this.chunkSize = this.uploader.opts.chunkSize
+  this.startByte = this.offset * this.chunkSize
+  this.endByte = this.computeEndByte()
+  this.xhr = null
+}
+
+var STATUS = Chunk.STATUS = {
+  PENDING: 'pending',
+  UPLOADING: 'uploading',
+  READING: 'reading',
+  SUCCESS: 'success',
+  ERROR: 'error',
+  COMPLETE: 'complete',
+  PROGRESS: 'progress',
+  RETRY: 'retry'
+}
+
+utils.extend(Chunk.prototype, {
+
+  _event: function (evt, args) {
+    args = utils.toArray(arguments)
+    args.unshift(this)
+    this.file._chunkEvent.apply(this.file, args)
+  },
+
+  computeEndByte: function () {
+    var endByte = Math.min(this.file.size, (this.offset + 1) * this.chunkSize)
+    if (this.file.size - endByte < this.chunkSize && !this.uploader.opts.forceChunkSize) {
+      // The last chunk will be bigger than the chunk size,
+      // but less than 2 * this.chunkSize
+      endByte = this.file.size
+    }
+    return endByte
+  },
+
+  getParams: function () {
+    return {
+      chunkNumber: this.offset + 1,
+      chunkSize: this.uploader.opts.chunkSize,
+      currentChunkSize: this.endByte - this.startByte,
+      totalSize: this.file.size,
+      identifier: this.file.uniqueIdentifier,
+      filename: this.file.name,
+      relativePath: this.file.relativePath,
+      totalChunks: this.file.chunks.length
+    }
+  },
+
+  getTarget: function (target, params) {
+    if (!params.length) {
+      return target
+    }
+    if (target.indexOf('?') < 0) {
+      target += '?'
+    } else {
+      target += '&'
+    }
+    return target + params.join('&')
+  },
+
+  test: function () {
+    this.xhr = new XMLHttpRequest()
+    this.xhr.addEventListener('load', testHandler, false)
+    this.xhr.addEventListener('error', testHandler, false)
+    var testMethod = utils.evalOpts(this.uploader.opts.testMethod, this.file, this)
+    var data = this.prepareXhrRequest(testMethod, true)
+    this.xhr.send(data)
+
+    var $ = this
+    function testHandler (event) {
+      var status = $.status(true)
+      if (status === STATUS.ERROR) {
+        $._event(status, $.message())
+        $.uploader.uploadNextChunk()
+      } else if (status === STATUS.SUCCESS) {
+        $._event(status, $.message())
+        $.tested = true
+      } else if (!$.file.paused) {
+        // Error might be caused by file pause method
+        // Chunks does not exist on the server side
+        $.tested = true
+        $.send()
+      }
+    }
+  },
+
+  preprocessFinished: function () {
+    // Compute the endByte after the preprocess function to allow an
+    // implementer of preprocess to set the fileObj size
+    this.endByte = this.computeEndByte()
+    this.preprocessState = 2
+    this.send()
+  },
+
+  readFinished: function (bytes) {
+    this.readState = 2
+    this.bytes = bytes
+    this.send()
+  },
+
+  send: function () {
+    var preprocess = this.uploader.opts.preprocess
+    var read = this.uploader.opts.readFileFn
+    if (utils.isFunction(preprocess)) {
+      switch (this.preprocessState) {
+        case 0:
+          this.preprocessState = 1
+          preprocess(this)
+          return
+        case 1:
+          return
+      }
+    }
+    switch (this.readState) {
+      case 0:
+        this.readState = 1
+        read(this.file, this.file.fileType, this.startByte, this.endByte, this)
+        return
+      case 1:
+        return
+    }
+    if (this.uploader.opts.testChunks && !this.tested) {
+      this.test()
+      return
+    }
+
+    this.loaded = 0
+    this.total = 0
+    this.pendingRetry = false
+
+    // Set up request and listen for event
+    this.xhr = new XMLHttpRequest()
+    this.xhr.upload.addEventListener('progress', progressHandler, false)
+    this.xhr.addEventListener('load', doneHandler, false)
+    this.xhr.addEventListener('error', doneHandler, false)
+
+    var uploadMethod = utils.evalOpts(this.uploader.opts.uploadMethod, this.file, this)
+    var data = this.prepareXhrRequest(uploadMethod, false, this.uploader.opts.method, this.bytes)
+    this.xhr.send(data)
+
+    var $ = this
+    function progressHandler (event) {
+      if (event.lengthComputable) {
+        $.loaded = event.loaded
+        $.total = event.total
+      }
+      $._event(STATUS.PROGRESS, event)
+    }
+
+    function doneHandler (event) {
+      var msg = $.message()
+      $.processingResponse = true
+      $.uploader.opts.processResponse(msg, function (err, res) {
+        $.processingResponse = false
+        if (!$.xhr) {
+          return
+        }
+        $.processedState = {
+          err: err,
+          res: res
+        }
+        var status = $.status()
+        if (status === STATUS.SUCCESS || status === STATUS.ERROR) {
+          // delete this.data
+          $._event(status, res)
+          status === STATUS.ERROR && $.uploader.uploadNextChunk()
+        } else {
+          $._event(STATUS.RETRY, res)
+          $.pendingRetry = true
+          $.abort()
+          $.retries++
+          var retryInterval = $.uploader.opts.chunkRetryInterval
+          if (retryInterval !== null) {
+            setTimeout(function () {
+              $.send()
+            }, retryInterval)
+          } else {
+            $.send()
+          }
+        }
+      }, $.file, $)
+    }
+  },
+
+  abort: function () {
+    var xhr = this.xhr
+    this.xhr = null
+    this.processingResponse = false
+    this.processedState = null
+    if (xhr) {
+      xhr.abort()
+    }
+  },
+
+  status: function (isTest) {
+    if (this.readState === 1) {
+      return STATUS.READING
+    } else if (this.pendingRetry || this.preprocessState === 1) {
+      // if pending retry then that's effectively the same as actively uploading,
+      // there might just be a slight delay before the retry starts
+      return STATUS.UPLOADING
+    } else if (!this.xhr) {
+      return STATUS.PENDING
+    } else if (this.xhr.readyState < 4 || this.processingResponse) {
+      // Status is really 'OPENED', 'HEADERS_RECEIVED'
+      // or 'LOADING' - meaning that stuff is happening
+      return STATUS.UPLOADING
+    } else {
+      var _status
+      if (this.uploader.opts.successStatuses.indexOf(this.xhr.status) > -1) {
+        // HTTP 200, perfect
+        // HTTP 202 Accepted - The request has been accepted for processing, but the processing has not been completed.
+        _status = STATUS.SUCCESS
+      } else if (this.uploader.opts.permanentErrors.indexOf(this.xhr.status) > -1 ||
+          !isTest && this.retries >= this.uploader.opts.maxChunkRetries) {
+        // HTTP 415/500/501, permanent error
+        _status = STATUS.ERROR
+      } else {
+        // this should never happen, but we'll reset and queue a retry
+        // a likely case for this would be 503 service unavailable
+        this.abort()
+        _status = STATUS.PENDING
+      }
+      var processedState = this.processedState
+      if (processedState && processedState.err) {
+        _status = STATUS.ERROR
+      }
+      return _status
+    }
+  },
+
+  message: function () {
+    return this.xhr ? this.xhr.responseText : ''
+  },
+
+  progress: function () {
+    if (this.pendingRetry) {
+      return 0
+    }
+    var s = this.status()
+    if (s === STATUS.SUCCESS || s === STATUS.ERROR) {
+      return 1
+    } else if (s === STATUS.PENDING) {
+      return 0
+    } else {
+      return this.total > 0 ? this.loaded / this.total : 0
+    }
+  },
+
+  sizeUploaded: function () {
+    var size = this.endByte - this.startByte
+    // can't return only chunk.loaded value, because it is bigger than chunk size
+    if (this.status() !== STATUS.SUCCESS) {
+      size = this.progress() * size
+    }
+    return size
+  },
+
+  prepareXhrRequest: function (method, isTest, paramsMethod, blob) {
+    // Add data from the query options
+    var query = utils.evalOpts(this.uploader.opts.query, this.file, this, isTest)
+    query = utils.extend(this.getParams(), query)
+
+    // processParams
+    query = this.uploader.opts.processParams(query, this.file, this, isTest)
+
+    var target = utils.evalOpts(this.uploader.opts.target, this.file, this, isTest)
+    var data = null
+    if (method === 'GET' || paramsMethod === 'octet') {
+      // Add data from the query options
+      var params = []
+      utils.each(query, function (v, k) {
+        params.push([encodeURIComponent(k), encodeURIComponent(v)].join('='))
+      })
+      target = this.getTarget(target, params)
+      data = blob || null
+    } else {
+      // Add data from the query options
+      data = new FormData()
+      utils.each(query, function (v, k) {
+        data.append(k, v)
+      })
+      if (typeof blob !== 'undefined') {
+        data.append(this.uploader.opts.fileParameterName, blob, this.file.name)
+      }
+    }
+
+    this.xhr.open(method, target, true)
+    this.xhr.withCredentials = this.uploader.opts.withCredentials
+
+    // Add data from header options
+    utils.each(utils.evalOpts(this.uploader.opts.headers, this.file, this, isTest), function (v, k) {
+      this.xhr.setRequestHeader(k, v)
+    }, this)
+
+    return data
+  }
+
+})
+
+module.exports = Chunk

+ 49 - 0
node_modules/simple-uploader.js/src/event.js

@@ -0,0 +1,49 @@
+var each = require('./utils').each
+
+var event = {
+
+  _eventData: null,
+
+  on: function (name, func) {
+    if (!this._eventData) this._eventData = {}
+    if (!this._eventData[name]) this._eventData[name] = []
+    var listened = false
+    each(this._eventData[name], function (fuc) {
+      if (fuc === func) {
+        listened = true
+        return false
+      }
+    })
+    if (!listened) {
+      this._eventData[name].push(func)
+    }
+  },
+
+  off: function (name, func) {
+    if (!this._eventData) this._eventData = {}
+    if (!this._eventData[name] || !this._eventData[name].length) return
+    if (func) {
+      each(this._eventData[name], function (fuc, i) {
+        if (fuc === func) {
+          this._eventData[name].splice(i, 1)
+          return false
+        }
+      }, this)
+    } else {
+      this._eventData[name] = []
+    }
+  },
+
+  trigger: function (name) {
+    if (!this._eventData) this._eventData = {}
+    if (!this._eventData[name]) return true
+    var args = this._eventData[name].slice.call(arguments, 1)
+    var preventDefault = false
+    each(this._eventData[name], function (fuc) {
+      preventDefault = fuc.apply(this, args) === false || preventDefault
+    }, this)
+    return !preventDefault
+  }
+}
+
+module.exports = event

+ 541 - 0
node_modules/simple-uploader.js/src/file.js

@@ -0,0 +1,541 @@
+var utils = require('./utils')
+var Chunk = require('./chunk')
+
+function File (uploader, file, parent) {
+  utils.defineNonEnumerable(this, 'uploader', uploader)
+  this.isRoot = this.isFolder = uploader === this
+  utils.defineNonEnumerable(this, 'parent', parent || null)
+  utils.defineNonEnumerable(this, 'files', [])
+  utils.defineNonEnumerable(this, 'fileList', [])
+  utils.defineNonEnumerable(this, 'chunks', [])
+  utils.defineNonEnumerable(this, '_errorFiles', [])
+  utils.defineNonEnumerable(this, 'file', null)
+  this.id = utils.uid()
+
+  if (this.isRoot || !file) {
+    this.file = null
+  } else {
+    if (utils.isString(file)) {
+      // folder
+      this.isFolder = true
+      this.file = null
+      this.path = file
+      if (this.parent.path) {
+        file = file.substr(this.parent.path.length)
+      }
+      this.name = file.charAt(file.length - 1) === '/' ? file.substr(0, file.length - 1) : file
+    } else {
+      this.file = file
+      this.fileType = this.file.type
+      this.name = file.fileName || file.name
+      this.size = file.size
+      this.relativePath = file.relativePath || file.webkitRelativePath || this.name
+      this._parseFile()
+    }
+  }
+
+  this.paused = uploader.opts.initialPaused
+  this.error = false
+  this.allError = false
+  this.aborted = false
+  this.completed = false
+  this.averageSpeed = 0
+  this.currentSpeed = 0
+  this._lastProgressCallback = Date.now()
+  this._prevUploadedSize = 0
+  this._prevProgress = 0
+
+  this.bootstrap()
+}
+
+utils.extend(File.prototype, {
+
+  _parseFile: function () {
+    var ppaths = parsePaths(this.relativePath)
+    if (ppaths.length) {
+      var filePaths = this.uploader.filePaths
+      utils.each(ppaths, function (path, i) {
+        var folderFile = filePaths[path]
+        if (!folderFile) {
+          folderFile = new File(this.uploader, path, this.parent)
+          filePaths[path] = folderFile
+          this._updateParentFileList(folderFile)
+        }
+        this.parent = folderFile
+        folderFile.files.push(this)
+        if (!ppaths[i + 1]) {
+          folderFile.fileList.push(this)
+        }
+      }, this)
+    } else {
+      this._updateParentFileList()
+    }
+  },
+
+  _updateParentFileList: function (file) {
+    if (!file) {
+      file = this
+    }
+    var p = this.parent
+    if (p) {
+      p.fileList.push(file)
+    }
+  },
+
+  _eachAccess: function (eachFn, fileFn) {
+    if (this.isFolder) {
+      utils.each(this.files, function (f, i) {
+        return eachFn.call(this, f, i)
+      }, this)
+      return
+    }
+    fileFn.call(this, this)
+  },
+
+  bootstrap: function () {
+    if (this.isFolder) return
+    var opts = this.uploader.opts
+    if (utils.isFunction(opts.initFileFn)) {
+      opts.initFileFn.call(this, this)
+    }
+
+    this.abort(true)
+    this._resetError()
+    // Rebuild stack of chunks from file
+    this._prevProgress = 0
+    var round = opts.forceChunkSize ? Math.ceil : Math.floor
+    var chunks = Math.max(round(this.size / opts.chunkSize), 1)
+    for (var offset = 0; offset < chunks; offset++) {
+      this.chunks.push(new Chunk(this.uploader, this, offset))
+    }
+  },
+
+  _measureSpeed: function () {
+    var smoothingFactor = this.uploader.opts.speedSmoothingFactor
+    var timeSpan = Date.now() - this._lastProgressCallback
+    if (!timeSpan) {
+      return
+    }
+    var uploaded = this.sizeUploaded()
+    // Prevent negative upload speed after file upload resume
+    this.currentSpeed = Math.max((uploaded - this._prevUploadedSize) / timeSpan * 1000, 0)
+    this.averageSpeed = smoothingFactor * this.currentSpeed + (1 - smoothingFactor) * this.averageSpeed
+    this._prevUploadedSize = uploaded
+    if (this.parent && this.parent._checkProgress()) {
+      this.parent._measureSpeed()
+    }
+  },
+
+  _checkProgress: function (file) {
+    return Date.now() - this._lastProgressCallback >= this.uploader.opts.progressCallbacksInterval
+  },
+
+  _chunkEvent: function (chunk, evt, message) {
+    var uploader = this.uploader
+    var STATUS = Chunk.STATUS
+    var that = this
+    var rootFile = this.getRoot()
+    var triggerProgress = function () {
+      that._measureSpeed()
+      uploader._trigger('fileProgress', rootFile, that, chunk)
+      that._lastProgressCallback = Date.now()
+    }
+    switch (evt) {
+      case STATUS.PROGRESS:
+        if (this._checkProgress()) {
+          triggerProgress()
+        }
+        break
+      case STATUS.ERROR:
+        this._error()
+        this.abort(true)
+        uploader._trigger('fileError', rootFile, this, message, chunk)
+        break
+      case STATUS.SUCCESS:
+        this._updateUploadedChunks(message, chunk)
+        if (this.error) {
+          return
+        }
+        clearTimeout(this._progeressId)
+        this._progeressId = 0
+        var timeDiff = Date.now() - this._lastProgressCallback
+        if (timeDiff < uploader.opts.progressCallbacksInterval) {
+          this._progeressId = setTimeout(triggerProgress, uploader.opts.progressCallbacksInterval - timeDiff)
+        }
+        if (this.isComplete()) {
+          clearTimeout(this._progeressId)
+          triggerProgress()
+          this.currentSpeed = 0
+          this.averageSpeed = 0
+          uploader._trigger('fileSuccess', rootFile, this, message, chunk)
+          if (rootFile.isComplete()) {
+            uploader._trigger('fileComplete', rootFile, this)
+          }
+        } else if (!this._progeressId) {
+          triggerProgress()
+        }
+        break
+      case STATUS.RETRY:
+        uploader._trigger('fileRetry', rootFile, this, chunk)
+        break
+    }
+  },
+
+  _updateUploadedChunks: function (message, chunk) {
+    var checkChunkUploaded = this.uploader.opts.checkChunkUploadedByResponse
+    if (checkChunkUploaded) {
+      var xhr = chunk.xhr
+      utils.each(this.chunks, function (_chunk) {
+        if (!_chunk.tested) {
+          var uploaded = checkChunkUploaded.call(this, _chunk, message)
+          if (_chunk === chunk && !uploaded) {
+            // fix the first chunk xhr status
+            // treated as success but checkChunkUploaded is false
+            // so the current chunk should be uploaded again
+            _chunk.xhr = null
+          }
+          if (uploaded) {
+            // first success and other chunks are uploaded
+            // then set xhr, so the uploaded chunks
+            // will be treated as success too
+            _chunk.xhr = xhr
+          }
+          _chunk.tested = true
+        }
+      }, this)
+      if (!this._firstResponse) {
+        this._firstResponse = true
+        this.uploader.upload(true)
+      } else {
+        this.uploader.uploadNextChunk()
+      }
+    } else {
+      this.uploader.uploadNextChunk()
+    }
+  },
+
+  _error: function () {
+    this.error = this.allError = true
+    var parent = this.parent
+    while (parent && parent !== this.uploader) {
+      parent._errorFiles.push(this)
+      parent.error = true
+      if (parent._errorFiles.length === parent.files.length) {
+        parent.allError = true
+      }
+      parent = parent.parent
+    }
+  },
+
+  _resetError: function () {
+    this.error = this.allError = false
+    var parent = this.parent
+    var index = -1
+    while (parent && parent !== this.uploader) {
+      index = parent._errorFiles.indexOf(this)
+      parent._errorFiles.splice(index, 1)
+      parent.allError = false
+      if (!parent._errorFiles.length) {
+        parent.error = false
+      }
+      parent = parent.parent
+    }
+  },
+
+  isComplete: function () {
+    if (!this.completed) {
+      var outstanding = false
+      this._eachAccess(function (file) {
+        if (!file.isComplete()) {
+          outstanding = true
+          return false
+        }
+      }, function () {
+        if (this.error) {
+          outstanding = true
+        } else {
+          var STATUS = Chunk.STATUS
+          utils.each(this.chunks, function (chunk) {
+            var status = chunk.status()
+            if (status === STATUS.ERROR || status === STATUS.PENDING || status === STATUS.UPLOADING || status === STATUS.READING || chunk.preprocessState === 1 || chunk.readState === 1) {
+              outstanding = true
+              return false
+            }
+          })
+        }
+      })
+      this.completed = !outstanding
+    }
+    return this.completed
+  },
+
+  isUploading: function () {
+    var uploading = false
+    this._eachAccess(function (file) {
+      if (file.isUploading()) {
+        uploading = true
+        return false
+      }
+    }, function () {
+      var uploadingStatus = Chunk.STATUS.UPLOADING
+      utils.each(this.chunks, function (chunk) {
+        if (chunk.status() === uploadingStatus) {
+          uploading = true
+          return false
+        }
+      })
+    })
+    return uploading
+  },
+
+  resume: function () {
+    this._eachAccess(function (f) {
+      f.resume()
+    }, function () {
+      this.paused = false
+      this.aborted = false
+      this.uploader.upload()
+    })
+    this.paused = false
+    this.aborted = false
+  },
+
+  pause: function () {
+    this._eachAccess(function (f) {
+      f.pause()
+    }, function () {
+      this.paused = true
+      this.abort()
+    })
+    this.paused = true
+  },
+
+  cancel: function () {
+    this.uploader.removeFile(this)
+  },
+
+  retry: function (file) {
+    var fileRetry = function (file) {
+      if (file.error) {
+        file.bootstrap()
+      }
+    }
+    if (file) {
+      file.bootstrap()
+    } else {
+      this._eachAccess(fileRetry, function () {
+        this.bootstrap()
+      })
+    }
+    this.uploader.upload()
+  },
+
+  abort: function (reset) {
+    if (this.aborted) {
+      return
+    }
+    this.currentSpeed = 0
+    this.averageSpeed = 0
+    this.aborted = !reset
+    var chunks = this.chunks
+    if (reset) {
+      this.chunks = []
+    }
+    var uploadingStatus = Chunk.STATUS.UPLOADING
+    utils.each(chunks, function (c) {
+      if (c.status() === uploadingStatus) {
+        c.abort()
+        this.uploader.uploadNextChunk()
+      }
+    }, this)
+  },
+
+  progress: function () {
+    var totalDone = 0
+    var totalSize = 0
+    var ret = 0
+    this._eachAccess(function (file, index) {
+      totalDone += file.progress() * file.size
+      totalSize += file.size
+      if (index === this.files.length - 1) {
+        ret = totalSize > 0 ? totalDone / totalSize : this.isComplete() ? 1 : 0
+      }
+    }, function () {
+      if (this.error) {
+        ret = 1
+        return
+      }
+      if (this.chunks.length === 1) {
+        this._prevProgress = Math.max(this._prevProgress, this.chunks[0].progress())
+        ret = this._prevProgress
+        return
+      }
+      // Sum up progress across everything
+      var bytesLoaded = 0
+      utils.each(this.chunks, function (c) {
+        // get chunk progress relative to entire file
+        bytesLoaded += c.progress() * (c.endByte - c.startByte)
+      })
+      var percent = bytesLoaded / this.size
+      // We don't want to lose percentages when an upload is paused
+      this._prevProgress = Math.max(this._prevProgress, percent > 0.9999 ? 1 : percent)
+      ret = this._prevProgress
+    })
+    return ret
+  },
+
+  getSize: function () {
+    var size = 0
+    this._eachAccess(function (file) {
+      size += file.size
+    }, function () {
+      size += this.size
+    })
+    return size
+  },
+
+  getFormatSize: function () {
+    var size = this.getSize()
+    return utils.formatSize(size)
+  },
+
+  getRoot: function () {
+    if (this.isRoot) {
+      return this
+    }
+    var parent = this.parent
+    while (parent) {
+      if (parent.parent === this.uploader) {
+        // find it
+        return parent
+      }
+      parent = parent.parent
+    }
+    return this
+  },
+
+  sizeUploaded: function () {
+    var size = 0
+    this._eachAccess(function (file) {
+      size += file.sizeUploaded()
+    }, function () {
+      utils.each(this.chunks, function (chunk) {
+        size += chunk.sizeUploaded()
+      })
+    })
+    return size
+  },
+
+  timeRemaining: function () {
+    var ret = 0
+    var sizeDelta = 0
+    var averageSpeed = 0
+    this._eachAccess(function (file, i) {
+      if (!file.paused && !file.error) {
+        sizeDelta += file.size - file.sizeUploaded()
+        averageSpeed += file.averageSpeed
+      }
+      if (i === this.files.length - 1) {
+        ret = calRet(sizeDelta, averageSpeed)
+      }
+    }, function () {
+      if (this.paused || this.error) {
+        ret = 0
+        return
+      }
+      var delta = this.size - this.sizeUploaded()
+      ret = calRet(delta, this.averageSpeed)
+    })
+    return ret
+    function calRet (delta, averageSpeed) {
+      if (delta && !averageSpeed) {
+        return Number.POSITIVE_INFINITY
+      }
+      if (!delta && !averageSpeed) {
+        return 0
+      }
+      return Math.floor(delta / averageSpeed)
+    }
+  },
+
+  removeFile: function (file) {
+    if (file.isFolder) {
+      while (file.files.length) {
+        var f = file.files[file.files.length - 1]
+        this._removeFile(f)
+      }
+    }
+    this._removeFile(file)
+  },
+
+  _delFilePath: function (file) {
+    if (file.path && this.filePaths) {
+      delete this.filePaths[file.path]
+    }
+    utils.each(file.fileList, function (file) {
+      this._delFilePath(file)
+    }, this)
+  },
+
+  _removeFile: function (file) {
+    if (!file.isFolder) {
+      utils.each(this.files, function (f, i) {
+        if (f === file) {
+          this.files.splice(i, 1)
+          return false
+        }
+      }, this)
+      file.abort()
+      var parent = file.parent
+      var newParent
+      while (parent && parent !== this) {
+        newParent = parent.parent
+        parent._removeFile(file)
+        parent = newParent
+      }
+    }
+    file.parent === this && utils.each(this.fileList, function (f, i) {
+      if (f === file) {
+        this.fileList.splice(i, 1)
+        return false
+      }
+    }, this)
+    if (!this.isRoot && this.isFolder && !this.files.length) {
+      this.parent._removeFile(this)
+      this.uploader._delFilePath(this)
+    }
+    file.parent = null
+  },
+
+  getType: function () {
+    if (this.isFolder) {
+      return 'folder'
+    }
+    return this.file.type && this.file.type.split('/')[1]
+  },
+
+  getExtension: function () {
+    if (this.isFolder) {
+      return ''
+    }
+    return this.name.substr((~-this.name.lastIndexOf('.') >>> 0) + 2).toLowerCase()
+  }
+
+})
+
+module.exports = File
+
+function parsePaths (path) {
+  var ret = []
+  var paths = path.split('/')
+  var len = paths.length
+  var i = 1
+  paths.splice(len - 1, 1)
+  len--
+  if (paths.length) {
+    while (i <= len) {
+      ret.push(paths.slice(0, i++).join('/') + '/')
+    }
+  }
+  return ret
+}

+ 514 - 0
node_modules/simple-uploader.js/src/uploader.js

@@ -0,0 +1,514 @@
+var utils = require('./utils')
+var event = require('./event')
+var File = require('./file')
+var Chunk = require('./chunk')
+
+var version = '__VERSION__'
+
+var isServer = typeof window === 'undefined'
+
+// ie10+
+var ie10plus = isServer ? false : window.navigator.msPointerEnabled
+var support = (function () {
+  if (isServer) {
+    return false
+  }
+  var sliceName = 'slice'
+  var _support = utils.isDefined(window.File) && utils.isDefined(window.Blob) &&
+                utils.isDefined(window.FileList)
+  var bproto = null
+  if (_support) {
+    bproto = window.Blob.prototype
+    utils.each(['slice', 'webkitSlice', 'mozSlice'], function (n) {
+      if (bproto[n]) {
+        sliceName = n
+        return false
+      }
+    })
+    _support = !!bproto[sliceName]
+  }
+  if (_support) Uploader.sliceName = sliceName
+  bproto = null
+  return _support
+})()
+
+var supportDirectory = (function () {
+  if (isServer) {
+    return false
+  }
+  var input = window.document.createElement('input')
+  input.type = 'file'
+  var sd = 'webkitdirectory' in input || 'directory' in input
+  input = null
+  return sd
+})()
+
+function Uploader (opts) {
+  this.support = support
+  /* istanbul ignore if */
+  if (!this.support) {
+    return
+  }
+  this.supportDirectory = supportDirectory
+  utils.defineNonEnumerable(this, 'filePaths', {})
+  this.opts = utils.extend({}, Uploader.defaults, opts || {})
+
+  this.preventEvent = utils.bind(this._preventEvent, this)
+
+  File.call(this, this)
+}
+
+/**
+ * Default read function using the webAPI
+ *
+ * @function webAPIFileRead(fileObj, fileType, startByte, endByte, chunk)
+ *
+ */
+var webAPIFileRead = function (fileObj, fileType, startByte, endByte, chunk) {
+  chunk.readFinished(fileObj.file[Uploader.sliceName](startByte, endByte, fileType))
+}
+
+Uploader.version = version
+
+Uploader.defaults = {
+  chunkSize: 1024 * 1024,
+  forceChunkSize: false,
+  simultaneousUploads: 3,
+  singleFile: false,
+  fileParameterName: 'file',
+  progressCallbacksInterval: 500,
+  speedSmoothingFactor: 0.1,
+  query: {},
+  headers: {},
+  withCredentials: false,
+  preprocess: null,
+  method: 'multipart',
+  testMethod: 'GET',
+  uploadMethod: 'POST',
+  prioritizeFirstAndLastChunk: false,
+  allowDuplicateUploads: false,
+  target: '/',
+  testChunks: true,
+  generateUniqueIdentifier: null,
+  maxChunkRetries: 0,
+  chunkRetryInterval: null,
+  permanentErrors: [404, 415, 500, 501],
+  successStatuses: [200, 201, 202],
+  onDropStopPropagation: false,
+  initFileFn: null,
+  readFileFn: webAPIFileRead,
+  checkChunkUploadedByResponse: null,
+  initialPaused: false,
+  processResponse: function (response, cb) {
+    cb(null, response)
+  },
+  processParams: function (params) {
+    return params
+  }
+}
+
+Uploader.utils = utils
+Uploader.event = event
+Uploader.File = File
+Uploader.Chunk = Chunk
+
+// inherit file
+Uploader.prototype = utils.extend({}, File.prototype)
+// inherit event
+utils.extend(Uploader.prototype, event)
+utils.extend(Uploader.prototype, {
+
+  constructor: Uploader,
+
+  _trigger: function (name) {
+    var args = utils.toArray(arguments)
+    var preventDefault = !this.trigger.apply(this, arguments)
+    if (name !== 'catchAll') {
+      args.unshift('catchAll')
+      preventDefault = !this.trigger.apply(this, args) || preventDefault
+    }
+    return !preventDefault
+  },
+
+  _triggerAsync: function () {
+    var args = arguments
+    utils.nextTick(function () {
+      this._trigger.apply(this, args)
+    }, this)
+  },
+
+  addFiles: function (files, evt) {
+    var _files = []
+    var oldFileListLen = this.fileList.length
+    utils.each(files, function (file) {
+      // Uploading empty file IE10/IE11 hangs indefinitely
+      // Directories have size `0` and name `.`
+      // Ignore already added files if opts.allowDuplicateUploads is set to false
+      if ((!ie10plus || ie10plus && file.size > 0) && !(file.size % 4096 === 0 && (file.name === '.' || file.fileName === '.'))) {
+        var uniqueIdentifier = this.generateUniqueIdentifier(file)
+        if (this.opts.allowDuplicateUploads || !this.getFromUniqueIdentifier(uniqueIdentifier)) {
+          var _file = new File(this, file, this)
+          _file.uniqueIdentifier = uniqueIdentifier
+          if (this._trigger('fileAdded', _file, evt)) {
+            _files.push(_file)
+          } else {
+            File.prototype.removeFile.call(this, _file)
+          }
+        }
+      }
+    }, this)
+    // get new fileList
+    var newFileList = this.fileList.slice(oldFileListLen)
+    if (this._trigger('filesAdded', _files, newFileList, evt)) {
+      utils.each(_files, function (file) {
+        if (this.opts.singleFile && this.files.length > 0) {
+          this.removeFile(this.files[0])
+        }
+        this.files.push(file)
+      }, this)
+      this._trigger('filesSubmitted', _files, newFileList, evt)
+    } else {
+      utils.each(newFileList, function (file) {
+        File.prototype.removeFile.call(this, file)
+      }, this)
+    }
+  },
+
+  addFile: function (file, evt) {
+    this.addFiles([file], evt)
+  },
+
+  cancel: function () {
+    for (var i = this.fileList.length - 1; i >= 0; i--) {
+      this.fileList[i].cancel()
+    }
+  },
+
+  removeFile: function (file) {
+    File.prototype.removeFile.call(this, file)
+    this._trigger('fileRemoved', file)
+  },
+
+  generateUniqueIdentifier: function (file) {
+    var custom = this.opts.generateUniqueIdentifier
+    if (utils.isFunction(custom)) {
+      return custom(file)
+    }
+    /* istanbul ignore next */
+    // Some confusion in different versions of Firefox
+    var relativePath = file.relativePath || file.webkitRelativePath || file.fileName || file.name
+    /* istanbul ignore next */
+    return file.size + '-' + relativePath.replace(/[^0-9a-zA-Z_-]/img, '')
+  },
+
+  getFromUniqueIdentifier: function (uniqueIdentifier) {
+    var ret = false
+    utils.each(this.files, function (file) {
+      if (file.uniqueIdentifier === uniqueIdentifier) {
+        ret = file
+        return false
+      }
+    })
+    return ret
+  },
+
+  uploadNextChunk: function (preventEvents) {
+    var found = false
+    var pendingStatus = Chunk.STATUS.PENDING
+    var checkChunkUploaded = this.uploader.opts.checkChunkUploadedByResponse
+    if (this.opts.prioritizeFirstAndLastChunk) {
+      utils.each(this.files, function (file) {
+        if (file.paused) {
+          return
+        }
+        if (checkChunkUploaded && !file._firstResponse && file.isUploading()) {
+          // waiting for current file's first chunk response
+          return
+        }
+        if (file.chunks.length && file.chunks[0].status() === pendingStatus) {
+          file.chunks[0].send()
+          found = true
+          return false
+        }
+        if (file.chunks.length > 1 && file.chunks[file.chunks.length - 1].status() === pendingStatus) {
+          file.chunks[file.chunks.length - 1].send()
+          found = true
+          return false
+        }
+      })
+      if (found) {
+        return found
+      }
+    }
+
+    // Now, simply look for the next, best thing to upload
+    utils.each(this.files, function (file) {
+      if (!file.paused) {
+        if (checkChunkUploaded && !file._firstResponse && file.isUploading()) {
+          // waiting for current file's first chunk response
+          return
+        }
+        utils.each(file.chunks, function (chunk) {
+          if (chunk.status() === pendingStatus) {
+            chunk.send()
+            found = true
+            return false
+          }
+        })
+      }
+      if (found) {
+        return false
+      }
+    })
+    if (found) {
+      return true
+    }
+
+    // The are no more outstanding chunks to upload, check is everything is done
+    var outstanding = false
+    utils.each(this.files, function (file) {
+      if (!file.isComplete()) {
+        outstanding = true
+        return false
+      }
+    })
+    // should check files now
+    // if now files in list
+    // should not trigger complete event
+    if (!outstanding && !preventEvents && this.files.length) {
+      // All chunks have been uploaded, complete
+      this._triggerAsync('complete')
+    }
+    return outstanding
+  },
+
+  upload: function (preventEvents) {
+    // Make sure we don't start too many uploads at once
+    var ret = this._shouldUploadNext()
+    if (ret === false) {
+      return
+    }
+    !preventEvents && this._trigger('uploadStart')
+    var started = false
+    for (var num = 1; num <= this.opts.simultaneousUploads - ret; num++) {
+      started = this.uploadNextChunk(!preventEvents) || started
+      if (!started && preventEvents) {
+        // completed
+        break
+      }
+    }
+    if (!started && !preventEvents) {
+      this._triggerAsync('complete')
+    }
+  },
+
+  /**
+   * should upload next chunk
+   * @function
+   * @returns {Boolean|Number}
+   */
+  _shouldUploadNext: function () {
+    var num = 0
+    var should = true
+    var simultaneousUploads = this.opts.simultaneousUploads
+    var uploadingStatus = Chunk.STATUS.UPLOADING
+    utils.each(this.files, function (file) {
+      utils.each(file.chunks, function (chunk) {
+        if (chunk.status() === uploadingStatus) {
+          num++
+          if (num >= simultaneousUploads) {
+            should = false
+            return false
+          }
+        }
+      })
+      return should
+    })
+    // if should is true then return uploading chunks's length
+    return should && num
+  },
+
+  /**
+   * Assign a browse action to one or more DOM nodes.
+   * @function
+   * @param {Element|Array.<Element>} domNodes
+   * @param {boolean} isDirectory Pass in true to allow directories to
+   * @param {boolean} singleFile prevent multi file upload
+   * @param {Object} attributes set custom attributes:
+   *  http://www.w3.org/TR/html-markup/input.file.html#input.file-attributes
+   *  eg: accept: 'image/*'
+   * be selected (Chrome only).
+   */
+  assignBrowse: function (domNodes, isDirectory, singleFile, attributes) {
+    if (typeof domNodes.length === 'undefined') {
+      domNodes = [domNodes]
+    }
+
+    utils.each(domNodes, function (domNode) {
+      var input
+      if (domNode.tagName === 'INPUT' && domNode.type === 'file') {
+        input = domNode
+      } else {
+        input = document.createElement('input')
+        input.setAttribute('type', 'file')
+        // display:none - not working in opera 12
+        utils.extend(input.style, {
+          visibility: 'hidden',
+          position: 'absolute',
+          width: '1px',
+          height: '1px'
+        })
+        // for opera 12 browser, input must be assigned to a document
+        domNode.appendChild(input)
+        // https://developer.mozilla.org/en/using_files_from_web_applications)
+        // event listener is executed two times
+        // first one - original mouse click event
+        // second - input.click(), input is inside domNode
+        domNode.addEventListener('click', function (e) {
+          if (domNode.tagName.toLowerCase() === 'label') {
+            return
+          }
+          input.click()
+        }, false)
+      }
+      if (!this.opts.singleFile && !singleFile) {
+        input.setAttribute('multiple', 'multiple')
+      }
+      if (isDirectory) {
+        input.setAttribute('webkitdirectory', 'webkitdirectory')
+      }
+      attributes && utils.each(attributes, function (value, key) {
+        input.setAttribute(key, value)
+      })
+      // When new files are added, simply append them to the overall list
+      var that = this
+      input.addEventListener('change', function (e) {
+        that._trigger(e.type, e)
+        if (e.target.value) {
+          that.addFiles(e.target.files, e)
+          e.target.value = ''
+        }
+      }, false)
+    }, this)
+  },
+
+  onDrop: function (evt) {
+    this._trigger(evt.type, evt)
+    if (this.opts.onDropStopPropagation) {
+      evt.stopPropagation()
+    }
+    evt.preventDefault()
+    this._parseDataTransfer(evt.dataTransfer, evt)
+  },
+
+  _parseDataTransfer: function (dataTransfer, evt) {
+    if (dataTransfer.items && dataTransfer.items[0] &&
+      dataTransfer.items[0].webkitGetAsEntry) {
+      this.webkitReadDataTransfer(dataTransfer, evt)
+    } else {
+      this.addFiles(dataTransfer.files, evt)
+    }
+  },
+
+  webkitReadDataTransfer: function (dataTransfer, evt) {
+    var self = this
+    var queue = dataTransfer.items.length
+    var files = []
+    utils.each(dataTransfer.items, function (item) {
+      var entry = item.webkitGetAsEntry()
+      if (!entry) {
+        decrement()
+        return
+      }
+      if (entry.isFile) {
+        // due to a bug in Chrome's File System API impl - #149735
+        fileReadSuccess(item.getAsFile(), entry.fullPath)
+      } else {
+        readDirectory(entry.createReader())
+      }
+    })
+    function readDirectory (reader) {
+      reader.readEntries(function (entries) {
+        if (entries.length) {
+          queue += entries.length
+          utils.each(entries, function (entry) {
+            if (entry.isFile) {
+              var fullPath = entry.fullPath
+              entry.file(function (file) {
+                fileReadSuccess(file, fullPath)
+              }, readError)
+            } else if (entry.isDirectory) {
+              readDirectory(entry.createReader())
+            }
+          })
+          readDirectory(reader)
+        } else {
+          decrement()
+        }
+      }, readError)
+    }
+    function fileReadSuccess (file, fullPath) {
+      // relative path should not start with "/"
+      file.relativePath = fullPath.substring(1)
+      files.push(file)
+      decrement()
+    }
+    function readError (fileError) {
+      throw fileError
+    }
+    function decrement () {
+      if (--queue === 0) {
+        self.addFiles(files, evt)
+      }
+    }
+  },
+
+  _assignHelper: function (domNodes, handles, remove) {
+    if (typeof domNodes.length === 'undefined') {
+      domNodes = [domNodes]
+    }
+    var evtMethod = remove ? 'removeEventListener' : 'addEventListener'
+    utils.each(domNodes, function (domNode) {
+      utils.each(handles, function (handler, name) {
+        domNode[evtMethod](name, handler, false)
+      }, this)
+    }, this)
+  },
+
+  _preventEvent: function (e) {
+    utils.preventEvent(e)
+    this._trigger(e.type, e)
+  },
+
+  /**
+   * Assign one or more DOM nodes as a drop target.
+   * @function
+   * @param {Element|Array.<Element>} domNodes
+   */
+  assignDrop: function (domNodes) {
+    this._onDrop = utils.bind(this.onDrop, this)
+    this._assignHelper(domNodes, {
+      dragover: this.preventEvent,
+      dragenter: this.preventEvent,
+      dragleave: this.preventEvent,
+      drop: this._onDrop
+    })
+  },
+
+  /**
+   * Un-assign drop event from DOM nodes
+   * @function
+   * @param domNodes
+   */
+  unAssignDrop: function (domNodes) {
+    this._assignHelper(domNodes, {
+      dragover: this.preventEvent,
+      dragenter: this.preventEvent,
+      dragleave: this.preventEvent,
+      drop: this._onDrop
+    }, true)
+    this._onDrop = null
+  }
+})
+
+module.exports = Uploader

+ 171 - 0
node_modules/simple-uploader.js/src/utils.js

@@ -0,0 +1,171 @@
+var oproto = Object.prototype
+var aproto = Array.prototype
+var serialize = oproto.toString
+
+var isFunction = function (fn) {
+  return serialize.call(fn) === '[object Function]'
+}
+
+var isArray = Array.isArray || /* istanbul ignore next */ function (ary) {
+  return serialize.call(ary) === '[object Array]'
+}
+
+var isPlainObject = function (obj) {
+  return serialize.call(obj) === '[object Object]' && Object.getPrototypeOf(obj) === oproto
+}
+
+var i = 0
+var utils = {
+  uid: function () {
+    return ++i
+  },
+  noop: function () {},
+  bind: function (fn, context) {
+    return function () {
+      return fn.apply(context, arguments)
+    }
+  },
+  preventEvent: function (evt) {
+    evt.preventDefault()
+  },
+  stop: function (evt) {
+    evt.preventDefault()
+    evt.stopPropagation()
+  },
+  nextTick: function (fn, context) {
+    setTimeout(utils.bind(fn, context), 0)
+  },
+  toArray: function (ary, start, end) {
+    if (start === undefined) start = 0
+    if (end === undefined) end = ary.length
+    return aproto.slice.call(ary, start, end)
+  },
+
+  isPlainObject: isPlainObject,
+  isFunction: isFunction,
+  isArray: isArray,
+  isObject: function (obj) {
+    return Object(obj) === obj
+  },
+  isString: function (s) {
+    return typeof s === 'string'
+  },
+  isUndefined: function (a) {
+    return typeof a === 'undefined'
+  },
+  isDefined: function (a) {
+    return typeof a !== 'undefined'
+  },
+
+  each: function (ary, func, context) {
+    if (utils.isDefined(ary.length)) {
+      for (var i = 0, len = ary.length; i < len; i++) {
+        if (func.call(context, ary[i], i, ary) === false) {
+          break
+        }
+      }
+    } else {
+      for (var k in ary) {
+        if (func.call(context, ary[k], k, ary) === false) {
+          break
+        }
+      }
+    }
+  },
+
+  /**
+   * If option is a function, evaluate it with given params
+   * @param {*} data
+   * @param {...} args arguments of a callback
+   * @returns {*}
+   */
+  evalOpts: function (data, args) {
+    if (utils.isFunction(data)) {
+      // `arguments` is an object, not array, in FF, so:
+      args = utils.toArray(arguments)
+      data = data.apply(null, args.slice(1))
+    }
+    return data
+  },
+
+  extend: function () {
+    var options
+    var name
+    var src
+    var copy
+    var copyIsArray
+    var clone
+    var target = arguments[0] || {}
+    var i = 1
+    var length = arguments.length
+    var force = false
+
+    // 如果第一个参数为布尔,判定是否深拷贝
+    if (typeof target === 'boolean') {
+      force = target
+      target = arguments[1] || {}
+      i++
+    }
+
+    // 确保接受方为一个复杂的数据类型
+    if (typeof target !== 'object' && !isFunction(target)) {
+      target = {}
+    }
+
+    // 如果只有一个参数,那么新成员添加于 extend 所在的对象上
+    if (i === length) {
+      target = this
+      i--
+    }
+
+    for (; i < length; i++) {
+      // 只处理非空参数
+      if ((options = arguments[i]) != null) {
+        for (name in options) {
+          src = target[name]
+          copy = options[name]
+
+          // 防止环引用
+          if (target === copy) {
+            continue
+          }
+          if (force && copy && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) {
+            if (copyIsArray) {
+              copyIsArray = false
+              clone = src && isArray(src) ? src : []
+            } else {
+              clone = src && isPlainObject(src) ? src : {}
+            }
+            target[name] = utils.extend(force, clone, copy)
+          } else if (copy !== undefined) {
+            target[name] = copy
+          }
+        }
+      }
+    }
+    return target
+  },
+
+  formatSize: function (size) {
+    if (size < 1024) {
+      return size.toFixed(0) + ' bytes'
+    } else if (size < 1024 * 1024) {
+      return (size / 1024.0).toFixed(0) + ' KB'
+    } else if (size < 1024 * 1024 * 1024) {
+      return (size / 1024.0 / 1024.0).toFixed(1) + ' MB'
+    } else {
+      return (size / 1024.0 / 1024.0 / 1024.0).toFixed(1) + ' GB'
+    }
+  },
+
+  defineNonEnumerable: function (target, key, value) {
+    Object.defineProperty(target, key, {
+      enumerable: false,
+      configurable: true,
+      writable: true,
+      value: value
+    })
+  }
+}
+
+module.exports = utils

+ 14 - 0
node_modules/simple-uploader.js/test.sh

@@ -0,0 +1,14 @@
+#!/bin/bash
+set -e
+
+if [ $TESTNAME = "unit-tests" ]; then
+  echo "Running unit-tests"
+  export DISPLAY=:99.0
+  sh -e /etc/init.d/xvfb start
+  sleep 1
+  npm run test
+  npm run codecov
+elif [ $TESTNAME = "browser-tests" ]; then
+  echo "Running browser-tests"
+  npm run test:ci
+fi

+ 18 - 0
node_modules/vue-simple-uploader/.babelrc

@@ -0,0 +1,18 @@
+{
+  "presets": [
+    ["env", {
+      "modules": false,
+      "targets": {
+        "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
+      }
+    }],
+    "stage-2"
+  ],
+  "plugins": ["transform-runtime", "add-module-exports", "transform-es2015-modules-umd"],
+  "env": {
+    "test": {
+      "presets": ["env", "stage-2"],
+      "plugins": ["istanbul"]
+    }
+  }
+}

+ 9 - 0
node_modules/vue-simple-uploader/.editorconfig

@@ -0,0 +1,9 @@
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true

+ 2 - 0
node_modules/vue-simple-uploader/.eslintignore

@@ -0,0 +1,2 @@
+build/*.js
+config/*.js

+ 27 - 0
node_modules/vue-simple-uploader/.eslintrc.js

@@ -0,0 +1,27 @@
+// http://eslint.org/docs/user-guide/configuring
+
+module.exports = {
+  root: true,
+  parser: 'babel-eslint',
+  parserOptions: {
+    sourceType: 'module'
+  },
+  env: {
+    browser: true,
+  },
+  // https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style
+  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
+  }
+}

+ 8 - 0
node_modules/vue-simple-uploader/.postcssrc.js

@@ -0,0 +1,8 @@
+// https://github.com/michael-ciniawsky/postcss-load-config
+
+module.exports = {
+  "plugins": {
+    // to edit target browsers: use "browserslist" field in package.json
+    "autoprefixer": {}
+  }
+}

+ 21 - 0
node_modules/vue-simple-uploader/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 simple-uploader
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 400 - 0
node_modules/vue-simple-uploader/README.md

@@ -0,0 +1,400 @@
+# vue-simple-uploader  [![NPM Version][npm-image]][npm-url] [![NPM Downloads][downloads-image]][downloads-url] [![juejin likes][juejin-image]](juejin-url)
+
+> A Vue.js upload component powered by [simple-uploader.js](https://github.com/simple-uploader/Uploader)
+
+![example](https://github.com/simple-uploader/vue-uploader/blob/master/example/simple-uploader.gif)
+
+![QQ](https://github.com/simple-uploader/Uploader/blob/develop/assets/simple-uploader-QQ-2.png?raw=true)
+
+[中文](./README_zh-CN.md)
+
+## Features
+
+* Treat Folder and File as `File`
+* Pause/Resume upload
+* Recover upload
+* Error handling
+* Drag and Drop with folder reader
+* Custom upload buttons
+* Folder Upload
+* Queue management
+* File validation
+* Upload progress
+* Time remaining
+* Chunk uploads
+
+## Install
+
+``` bash
+npm install vue-simple-uploader --save
+```
+
+## Notes
+
+- https://www.cnblogs.com/xiahj/p/vue-simple-uploader.html
+- https://github.com/LuoLiangDSGA/spring-learning/tree/master/boot-uploader
+- http://www.smarthu.club
+
+## Usage
+
+### init
+
+``` js
+import Vue from 'vue'
+import uploader from 'vue-simple-uploader'
+import App from './App.vue'
+
+Vue.use(uploader)
+
+/* eslint-disable no-new */
+new Vue({
+  render(createElement) {
+    return createElement(App)
+  }
+}).$mount('#app')
+```
+
+### App.vue
+
+``` vue
+<template>
+  <uploader :options="options" class="uploader-example">
+    <uploader-unsupport></uploader-unsupport>
+    <uploader-drop>
+      <p>Drop files here to upload or</p>
+      <uploader-btn>select files</uploader-btn>
+      <uploader-btn :attrs="attrs">select images</uploader-btn>
+      <uploader-btn :directory="true">select folder</uploader-btn>
+    </uploader-drop>
+    <uploader-list></uploader-list>
+  </uploader>
+</template>
+
+<script>
+  export default {
+    data () {
+      return {
+        options: {
+          // https://github.com/simple-uploader/Uploader/tree/develop/samples/Node.js
+          target: '//localhost:3000/upload',
+          testChunks: false
+        },
+        attrs: {
+          accept: 'image/*'
+        }
+      }
+    }
+  }
+</script>
+
+<style>
+  .uploader-example {
+    width: 880px;
+    padding: 15px;
+    margin: 40px auto 0;
+    font-size: 12px;
+    box-shadow: 0 0 10px rgba(0, 0, 0, .4);
+  }
+  .uploader-example .uploader-btn {
+    margin-right: 4px;
+  }
+  .uploader-example .uploader-list {
+    max-height: 440px;
+    overflow: auto;
+    overflow-x: hidden;
+    overflow-y: auto;
+  }
+</style>
+```
+
+## Components
+
+### Uploader
+
+Root component.
+
+#### Props
+
+* `options {Object}`
+
+  See [simple-uploader.js options](https://github.com/simple-uploader/Uploader#configuration).
+
+  Besides, some other options are avaliable too:
+
+  - `parseTimeRemaining(timeRemaining, parsedTimeRemaining) {Function}`
+
+    this function option to format the current file's time remaining value(seconds, number), you can return your language time remaining text, params:
+
+    - `timeRemaining{Number}`, time remaining seconds
+
+    - `parsedTimeRemaining{String}`, default shown time remaining text, you can use it like this:
+
+      ```js
+      parseTimeRemaining: function (timeRemaining, parsedTimeRemaining) {
+        return parsedTimeRemaining
+          .replace(/\syears?/, '年')
+          .replace(/\days?/, '天')
+          .replace(/\shours?/, '小时')
+          .replace(/\sminutes?/, '分钟')
+          .replace(/\sseconds?/, '秒')
+      }
+      ```
+  - `categoryMap {Object}`
+
+    File category map, default:
+
+    ```js
+    {
+      image: ['gif', 'jpg', 'jpeg', 'png', 'bmp', 'webp'],
+      video: ['mp4', 'm3u8', 'rmvb', 'avi', 'swf', '3gp', 'mkv', 'flv'],
+      audio: ['mp3', 'wav', 'wma', 'ogg', 'aac', 'flac'],
+      document: ['doc', 'txt', 'docx', 'pages', 'epub', 'pdf', 'numbers', 'csv', 'xls', 'xlsx', 'keynote', 'ppt', 'pptx']
+    }
+    ```
+
+* `autoStart {Boolean}`
+
+  Default `true`, Whether the file will be start uploading after it is added.
+
+* `fileStatusText {Object}`
+
+  Default:
+  ```js
+  {
+    success: 'success',
+    error: 'error',
+    uploading: 'uploading',
+    paused: 'paused',
+    waiting: 'waiting'
+  }
+  ```
+  An object map for file status text.
+
+  After 0.6.0, `fileStatusText` can be a function with params `(status, response = null)`, you can control the status text more flexible:
+
+  ```js
+  fileStatusText(status, response) {
+    const statusTextMap = {
+      uploading: 'uploading',
+      paused: 'paused',
+      waiting: 'waiting'
+    }
+    if (status === 'success' || status === 'error') {
+      // only use response when status is success or error
+
+      // eg:
+      // return response data ?
+      return response.data
+    } else {
+      return statusTextMap[status]
+    }
+  }
+  ```
+
+#### Events
+
+See [simple-uploader.js uploader/events](https://github.com/simple-uploader/Uploader#events)
+
+**Note:**
+
+* All events name will be transformed by [lodash.kebabCase](https://github.com/lodash/lodash/blob/master/kebabCase.js), eg: `fileSuccess` will be transformed to `file-success`
+
+* `catchAll` event will not be emited.
+
+* `file-added(file)`, file added event, this event is used for file validation. To reject this file you should set `file.ignored = true`.
+
+* `files-added(files, fileList)`, files added event, this event is used for files validation. To reject these files you should set `files.ignored = true` or `fileList.ignored = true`.
+
+#### Scoped Slots
+
+* `files {Array}`
+
+  An array of files (no folders).
+
+* `fileList {Array}`
+
+  An array of files and folders.
+
+* `started`
+
+  Started uploading or not.
+
+#### Get `Uploader` instance
+
+You can get it like this:
+
+```js
+const uploaderInstance = this.$refs.uploader.uploader
+// now you can call all uploader methods
+// https://github.com/simple-uploader/Uploader#methods
+uploaderInstance.cancel()
+```
+
+### UploaderBtn
+
+Select files button.
+
+#### Props
+
+* `directory {Boolean}`
+
+  Default `false`, Support selecting Folder
+
+* `single {Boolean}`
+
+  Default `false`, To prevent multiple file uploads if it is `true`.
+
+* `attrs {Object}`
+
+  Default `{}`, Pass object to set custom attributes on input element.
+
+### UploaderDrop
+
+Droped files area.
+
+### UploaderList
+
+An array of `Uploader.File` file(folder) objects added by the user, but it treated Folder as `Uploader.File` Object.
+
+#### Scoped Slots
+
+* `fileList {Array}`
+
+  An array of files and folders.
+
+### UploaderFiles
+
+An array of `Uploader.File` file objects added by the user.
+
+#### Scoped Slots
+
+* `files {Array}`
+
+  An array of files (no folders).
+
+### UploaderUnsupport
+
+It will be shown if the current browser do not support HTML5 File API.
+
+### UploaderFile
+
+File item component.
+
+#### Props
+
+* `file {Uploader.File}`
+
+  `Uploader.File` instance.
+
+* `list {Boolean}`
+
+  It should be `true` if it is puted in `UploaderList`
+
+#### Scoped Slots
+
+* `file {Uploader.File}`
+
+  `Uploader.File` instance.
+
+* `list {Boolean}`
+
+  In `UploaderList` component or not.
+
+* `status {String}`
+
+  Current status, the values is one of `success`, `error`, `uploading`, `paused`, `waiting`
+
+* `paused {Boolean}`
+
+  Indicated if the file is paused.
+
+* `error {Boolean}`
+
+  Indicated if the file has encountered an error.
+
+* `averageSpeed {Number}`
+
+  Average upload speed, bytes per second.
+
+* `formatedAverageSpeed {String}`
+
+  Formated average upload speed, eg: `3 KB / S`
+
+* `currentSpeed {Number}`
+
+  Current upload speed, bytes per second.
+
+* `isComplete {Boolean}`
+
+  Indicated whether the file has completed uploading and received a server response.
+
+* `isUploading {Boolean}`
+
+  Indicated whether file chunks is uploading.
+
+* `size {Number}`
+
+  Size in bytes of the file.
+
+* `formatedSize {Number}`
+
+  Formated file size, eg: `10 KB`.
+
+* `uploadedSize {Number}`
+
+  Size uploaded in bytes.
+
+* `progress {Number}`
+
+  A number between 0 and 1 indicating the current upload progress of the file.
+
+* `progressStyle {String}`
+
+  The file progress element's transform style, eg: `{transform: '-50%'}`.
+
+* `progressingClass {String}`
+
+  The value will be `uploader-file-progressing` if the file is uploading.
+
+* `timeRemaining {Number}`
+
+  Remaining time to finish upload file in seconds.
+
+* `formatedTimeRemaining {String}`
+
+  Formated remaining time, eg: `3 miniutes`.
+
+* `type {String}`
+
+  File type.
+
+* `extension {String}`
+
+  File extension in lowercase.
+
+* `fileCategory {String}`
+
+  File category, one of `folder`, `document`, `video`, `audio`, `image`, `unknown`.
+
+## Development
+
+``` bash
+# install dependencies
+npm install
+
+# serve with hot reload at localhost:8080
+npm run dev
+
+# build for production with minification
+npm run build
+
+# build for production and view the bundle analyzer report
+npm run build --report
+```
+
+[npm-image]: https://img.shields.io/npm/v/vue-simple-uploader.svg?style=flat
+[npm-url]: https://npmjs.org/package/vue-simple-uploader
+[downloads-image]: https://img.shields.io/npm/dm/vue-simple-uploader.svg?style=flat
+[downloads-url]: https://npmjs.org/package/vue-simple-uploader
+[juejin-image]: https://badge.juejin.im/entry/599dad0ff265da248b04d7b8/likes.svg?style=flat
+[juejin-url]: https://juejin.im/entry/599dad0ff265da248b04d7b8/detail

+ 405 - 0
node_modules/vue-simple-uploader/README_zh-CN.md

@@ -0,0 +1,405 @@
+# vue-simple-uploader  [![NPM Version][npm-image]][npm-url] [![NPM Downloads][downloads-image]][downloads-url] [![juejin likes][juejin-image]](juejin-url)
+
+> 一个基于 [simple-uploader.js](https://github.com/simple-uploader/Uploader) 的 Vue 上传组件
+
+![example](https://github.com/simple-uploader/vue-uploader/blob/master/example/simple-uploader.gif)
+
+![QQ](https://github.com/simple-uploader/Uploader/blob/develop/assets/simple-uploader-QQ-2.png?raw=true)
+
+## 特性
+
+* 支持文件、多文件、文件夹上传
+
+* 支持拖拽文件、文件夹上传
+
+* 统一对待文件和文件夹,方便操作管理
+
+* 可暂停、继续上传
+
+* 错误处理
+
+* 支持“快传”,通过文件判断服务端是否已存在从而实现“快传”
+
+* 上传队列管理,支持最大并发上传
+
+* 分块上传
+
+* 支持进度、预估剩余时间、出错自动重试、重传等操作
+
+## 安装
+
+``` bash
+npm install vue-simple-uploader --save
+```
+
+## 笔记周边
+
+- https://www.cnblogs.com/xiahj/p/vue-simple-uploader.html
+- https://github.com/LuoLiangDSGA/spring-learning/tree/master/boot-uploader
+- http://www.smarthu.club
+
+## 使用
+
+### 初始化
+
+``` js
+import Vue from 'vue'
+import uploader from 'vue-simple-uploader'
+import App from './App.vue'
+
+Vue.use(uploader)
+
+/* eslint-disable no-new */
+new Vue({
+  render(createElement) {
+    return createElement(App)
+  }
+}).$mount('#app')
+```
+
+### App.vue
+
+``` vue
+<template>
+  <uploader :options="options" class="uploader-example">
+    <uploader-unsupport></uploader-unsupport>
+    <uploader-drop>
+      <p>Drop files here to upload or</p>
+      <uploader-btn>select files</uploader-btn>
+      <uploader-btn :attrs="attrs">select images</uploader-btn>
+      <uploader-btn :directory="true">select folder</uploader-btn>
+    </uploader-drop>
+    <uploader-list></uploader-list>
+  </uploader>
+</template>
+
+<script>
+  export default {
+    data () {
+      return {
+        options: {
+          // https://github.com/simple-uploader/Uploader/tree/develop/samples/Node.js
+          target: '//localhost:3000/upload',
+          testChunks: false
+        },
+        attrs: {
+          accept: 'image/*'
+        }
+      }
+    }
+  }
+</script>
+
+<style>
+  .uploader-example {
+    width: 880px;
+    padding: 15px;
+    margin: 40px auto 0;
+    font-size: 12px;
+    box-shadow: 0 0 10px rgba(0, 0, 0, .4);
+  }
+  .uploader-example .uploader-btn {
+    margin-right: 4px;
+  }
+  .uploader-example .uploader-list {
+    max-height: 440px;
+    overflow: auto;
+    overflow-x: hidden;
+    overflow-y: auto;
+  }
+</style>
+```
+
+## 组件
+
+### Uploader
+
+上传根组件,可理解为一个上传器。
+
+#### Props
+
+* `options {Object}`
+
+  参考 [simple-uploader.js 配置](https://github.com/simple-uploader/Uploader/blob/develop/README_zh-CN.md#配置)。
+
+  此外,你可以有如下配置项可选:
+
+  - `parseTimeRemaining(timeRemaining, parsedTimeRemaining) {Function}`
+
+    用于格式化你想要剩余时间,一般可以用来做多语言。参数:
+
+    - `timeRemaining{Number}`, 剩余时间,秒为单位
+
+    - `parsedTimeRemaining{String}`, 默认展示的剩余时间内容,你也可以这样做替换使用:
+
+      ```js
+      parseTimeRemaining: function (timeRemaining, parsedTimeRemaining) {
+        return parsedTimeRemaining
+          .replace(/\syears?/, '年')
+          .replace(/\days?/, '天')
+          .replace(/\shours?/, '小时')
+          .replace(/\sminutes?/, '分钟')
+          .replace(/\sseconds?/, '秒')
+      }
+      ```
+
+  - `categoryMap {Object}`
+
+    文件类型 map,默认:
+
+    ```js
+    {
+      image: ['gif', 'jpg', 'jpeg', 'png', 'bmp', 'webp'],
+      video: ['mp4', 'm3u8', 'rmvb', 'avi', 'swf', '3gp', 'mkv', 'flv'],
+      audio: ['mp3', 'wav', 'wma', 'ogg', 'aac', 'flac'],
+      document: ['doc', 'txt', 'docx', 'pages', 'epub', 'pdf', 'numbers', 'csv', 'xls', 'xlsx', 'keynote', 'ppt', 'pptx']
+    }
+    ```
+
+* `autoStart {Boolean}`
+
+  默认 `true`, 是否选择文件后自动开始上传。
+
+* `fileStatusText {Object}`
+
+  默认:
+  ```js
+  {
+    success: 'success',
+    error: 'error',
+    uploading: 'uploading',
+    paused: 'paused',
+    waiting: 'waiting'
+  }
+  ```
+  用于转换文件上传状态文本映射对象。
+
+  0.6.0 版本之后,`fileStatusText` 可以设置为一个函数,参数为 `(status, response = null)`, 第一个 status 为状态,第二个为响应内容,默认 null,示例:
+
+  ```js
+  fileStatusText(status, response) {
+    const statusTextMap = {
+      uploading: 'uploading',
+      paused: 'paused',
+      waiting: 'waiting'
+    }
+    if (status === 'success' || status === 'error') {
+      // 只有status为success或者error的时候可以使用 response
+
+      // eg:
+      // return response data ?
+      return response.data
+    } else {
+      return statusTextMap[status]
+    }
+  }
+  ```
+
+#### 事件
+
+参见 [simple-uploader.js uploader 事件](https://github.com/simple-uploader/Uploader/blob/develop/README_zh-CN.md#事件)
+
+**注意:**
+
+* 所有的事件都会通过 [lodash.kebabCase](https://github.com/lodash/lodash/blob/master/kebabCase.js) 做转换,例如 `fileSuccess` 就会变成 `file-success`。
+
+* `catch-all` 这个事件是不会触发的。
+
+* `file-added(file)`, 添加了一个文件事件,一般用做文件校验,如果设置 `file.ignored = true` 的话这个文件就会被过滤掉。
+
+* `files-added(files, fileList)`, 添加了一批文件事件,一般用做一次选择的多个文件进行校验,如果设置 `files.ignored = true` 或者 ``fileList.ignored = true`` 的话本次选择的文件就会被过滤掉。
+
+#### 作用域插槽
+
+* `files {Array}`
+
+  纯文件列表,没有文件夹概念。
+
+* `fileList {Array}`
+
+  统一对待文件、文件夹列表。
+
+* `started`
+
+  是否开始上传了。
+
+#### 得到 `Uploader` 实例
+
+可以通过如下方式获得:
+
+```js
+// 在 uploader 组件上会有 uploader 属性 指向的就是 Uploader 实例
+const uploaderInstance = this.$refs.uploader.uploader
+// 这里可以调用实例方法
+// https://github.com/simple-uploader/Uploader/blob/develop/README_zh-CN.md#方法
+uploaderInstance.cancel()
+```
+
+### UploaderBtn
+
+点选上传文件按钮。
+
+#### Props
+
+* `directory {Boolean}`
+
+  默认 `false`, 是否是文件夹上传。
+
+* `single {Boolean}`
+
+  默认 `false`, 如果设为 `true`,则代表一次只能选择一个文件。
+
+* `attrs {Object}`
+
+  默认 `{}`, 添加到 input 元素上的额外属性。
+
+### UploaderDrop
+
+拖拽上传区域。
+
+### UploaderList
+
+文件、文件夹列表,同等对待。
+
+#### 作用域插槽
+
+* `fileList {Array}`
+
+  文件、文件夹组成数组。
+
+### UploaderFiles
+
+文件列表,没有文件夹概念,纯文件列表。
+
+#### 作用域插槽
+
+* `files {Array}`
+
+  文件列表。
+
+### UploaderUnsupport
+
+不支持 HTML5 File API 的时候会显示。
+
+### UploaderFile
+
+文件、文件夹单个组件。
+
+#### Props
+
+* `file {Uploader.File}`
+
+  封装的文件实例。
+
+* `list {Boolean}`
+
+  如果是在 `UploaderList` 组件中使用的话,请设置为 `true`。
+
+#### 作用域插槽
+
+* `file {Uploader.File}`
+
+  文件实例。
+
+* `list {Boolean}`
+
+  是否在 `UploaderList` 组件中使用。
+
+* `status {String}`
+
+  当前状态,可能是:`success`, `error`, `uploading`, `paused`, `waiting`
+
+* `paused {Boolean}`
+
+  是否暂停了。
+
+* `error {Boolean}`
+
+  是否出错了。
+
+* `averageSpeed {Number}`
+
+  平均上传速度,单位字节每秒。
+
+* `formatedAverageSpeed {String}`
+
+  格式化后的平均上传速度,类似:`3 KB / S`。
+
+* `currentSpeed {Number}`
+
+  当前上传速度,单位字节每秒。
+
+* `isComplete {Boolean}`
+
+  是否已经上传完成。
+
+* `isUploading {Boolean}`
+
+  是否在上传中。
+
+* `size {Number}`
+
+  文件或者文件夹大小。
+
+* `formatedSize {Number}`
+
+  格式化后文件或者文件夹大小,类似:`10 KB`.
+
+* `uploadedSize {Number}`
+
+  已经上传大小,单位字节。
+
+* `progress {Number}`
+
+  介于 0 到 1 之间的小数,上传进度。
+
+* `progressStyle {String}`
+
+  进度样式,transform 属性,类似:`{transform: '-50%'}`.
+
+* `progressingClass {String}`
+
+  正在上传中的时候值为:`uploader-file-progressing`。
+
+* `timeRemaining {Number}`
+
+  预估剩余时间,单位秒。
+
+* `formatedTimeRemaining {String}`
+
+  格式化后剩余时间,类似:`3 miniutes`.
+
+* `type {String}`
+
+  文件类型。
+
+* `extension {String}`
+
+  文件名后缀,小写。
+
+* `fileCategory {String}`
+
+  文件分类,其中之一:`folder`, `document`, `video`, `audio`, `image`, `unknown`。
+
+## Development
+
+``` bash
+# install dependencies
+npm install
+
+# serve with hot reload at localhost:8080
+npm run dev
+
+# build for production with minification
+npm run build
+
+# build for production and view the bundle analyzer report
+npm run build --report
+```
+
+[npm-image]: https://img.shields.io/npm/v/vue-simple-uploader.svg?style=flat
+[npm-url]: https://npmjs.org/package/vue-simple-uploader
+[downloads-image]: https://img.shields.io/npm/dm/vue-simple-uploader.svg?style=flat
+[downloads-url]: https://npmjs.org/package/vue-simple-uploader
+[juejin-image]: https://badge.juejin.im/entry/599dad0ff265da248b04d7b8/likes.svg?style=flat
+[juejin-url]: https://juejin.im/entry/599dad0ff265da248b04d7b8/detail

File diff suppressed because it is too large
+ 2 - 0
node_modules/vue-simple-uploader/dist/vue-uploader.js


File diff suppressed because it is too large
+ 1 - 0
node_modules/vue-simple-uploader/dist/vue-uploader.js.map


+ 59 - 0
node_modules/vue-simple-uploader/example/App.vue

@@ -0,0 +1,59 @@
+<template>
+  <uploader :options="options" :file-status-text="statusText" class="uploader-example" ref="uploader" @file-complete="fileComplete" @complete="complete"></uploader>
+</template>
+
+<script>
+  export default {
+    data () {
+      return {
+        options: {
+          target: '//localhost:3000/upload', // '//jsonplaceholder.typicode.com/posts/',
+          testChunks: false
+        },
+        attrs: {
+          accept: 'image/*'
+        },
+        statusText: {
+          success: '成功了',
+          error: '出错了',
+          uploading: '上传中',
+          paused: '暂停中',
+          waiting: '等待中'
+        }
+      }
+    },
+    methods: {
+      complete () {
+        debugger
+        console.log('complete', arguments)
+      },
+      fileComplete () {
+        console.log('file complete', arguments)
+      }
+    },
+    mounted () {
+      this.$nextTick(() => {
+        window.uploader = this.$refs.uploader.uploader
+      })
+    }
+  }
+</script>
+
+<style>
+  .uploader-example {
+    width: 880px;
+    padding: 15px;
+    margin: 40px auto 0;
+    font-size: 12px;
+    box-shadow: 0 0 10px rgba(0, 0, 0, .4);
+  }
+  .uploader-example .uploader-btn {
+    margin-right: 4px;
+  }
+  .uploader-example .uploader-list {
+    max-height: 440px;
+    overflow: auto;
+    overflow-x: hidden;
+    overflow-y: auto;
+  }
+</style>

+ 11 - 0
node_modules/vue-simple-uploader/example/index.html

@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>vue-uploader</title>
+  </head>
+  <body>
+    <div id="app"></div>
+    <!-- built files will be auto injected -->
+  </body>
+</html>

+ 12 - 0
node_modules/vue-simple-uploader/example/main.js

@@ -0,0 +1,12 @@
+import Vue from 'vue'
+import uploader from '../src'
+import App from './App.vue'
+
+Vue.use(uploader)
+
+/* eslint-disable no-new */
+new Vue({
+  render(createElement) {
+    return createElement(App)
+  }
+}).$mount('#app')

BIN
node_modules/vue-simple-uploader/example/simple-uploader.gif


+ 43 - 0
node_modules/vue-simple-uploader/node_modules/simple-uploader.js/LICENSE

@@ -0,0 +1,43 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 doly mood
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+The MIT License (MIT)
+
+Copyright (c) 2011, 23, http://www.23developer.com
+              2013, Aidas Klimas
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

File diff suppressed because it is too large
+ 267 - 0
node_modules/vue-simple-uploader/node_modules/simple-uploader.js/README.md


BIN
node_modules/vue-simple-uploader/node_modules/simple-uploader.js/assets/simple-uploader-QQ-2.png


BIN
node_modules/vue-simple-uploader/node_modules/simple-uploader.js/assets/simple-uploader-QQ.jpg


File diff suppressed because it is too large
+ 1610 - 0
node_modules/vue-simple-uploader/node_modules/simple-uploader.js/dist/uploader.js


File diff suppressed because it is too large
+ 9 - 0
node_modules/vue-simple-uploader/node_modules/simple-uploader.js/dist/uploader.min.js


+ 70 - 0
node_modules/vue-simple-uploader/node_modules/simple-uploader.js/package.json

@@ -0,0 +1,70 @@
+{
+  "name": "simple-uploader.js",
+  "version": "0.5.6",
+  "author": "dolymood <dolymood@gmail.com>",
+  "license": "MIT",
+  "description": "Uploader library implements html5 file upload and provides multiple simultaneous, stable, fault tolerant and resumable uploads",
+  "keywords": [
+    "simple-uploader",
+    "simple-uploader.js",
+    "uploader",
+    "uploader.js",
+    "resumable.js",
+    "flow.js",
+    "file upload",
+    "resumable upload",
+    "chunk upload",
+    "html5 upload",
+    "javascript upload",
+    "upload"
+  ],
+  "main": "src/uploader.js",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/simple-uploader/Uploader.git"
+  },
+  "bugs": "https://github.com/simple-uploader/Uploader/issues",
+  "homepage": "https://github.com/simple-uploader/Uploader",
+  "scripts": {
+    "dev": "gulp watch",
+    "build": "gulp build",
+    "codecov": "codecov",
+    "test": "gulp",
+    "test:unit": "gulp test",
+    "test:cover": "gulp cover",
+    "test:ci": "gulp ci",
+    "release": "gulp release"
+  },
+  "devDependencies": {
+    "browserify-versionify": "^1.0.6",
+    "codecov": "^3.6.5",
+    "eslint": "^4.2.0",
+    "eslint-config-standard": "^6.1.0",
+    "eslint-plugin-promise": "^3.5.0",
+    "eslint-plugin-standard": "^3.0.1",
+    "gulp": "^3.9.1",
+    "gulp-browserify": "^0.5.1",
+    "gulp-concat": "^2.6.1",
+    "gulp-eslint": "^4.0.0",
+    "gulp-git": "^2.4.1",
+    "gulp-header": "^1.8.8",
+    "gulp-sourcemaps": "^2.6.0",
+    "gulp-tag-version": "^1.3.0",
+    "gulp-uglify": "^3.0.0",
+    "jasmine": "^2.6.0",
+    "jasmine-core": "^2.6.4",
+    "karma": "^1.7.0",
+    "karma-chrome-launcher": "^2.1.1",
+    "karma-commonjs": "^1.0.0",
+    "karma-coverage": "^1.1.1",
+    "karma-firefox-launcher": "^1.0.1",
+    "karma-jasmine": "^1.1.0",
+    "karma-phantomjs-launcher": "^1.0.4",
+    "karma-sauce-launcher": "^1.1.0",
+    "pump": "^1.0.2",
+    "sinon": "1.7.3"
+  },
+  "__npminstall_done": "Wed Jun 02 2021 14:28:26 GMT+0800 (中国标准时间)",
+  "_from": "simple-uploader.js@0.5.6",
+  "_resolved": "https://registry.npm.taobao.org/simple-uploader.js/download/simple-uploader.js-0.5.6.tgz"
+}

+ 315 - 0
node_modules/vue-simple-uploader/node_modules/simple-uploader.js/src/chunk.js

@@ -0,0 +1,315 @@
+var utils = require('./utils')
+
+function Chunk (uploader, file, offset) {
+  utils.defineNonEnumerable(this, 'uploader', uploader)
+  utils.defineNonEnumerable(this, 'file', file)
+  utils.defineNonEnumerable(this, 'bytes', null)
+  this.offset = offset
+  this.tested = false
+  this.retries = 0
+  this.pendingRetry = false
+  this.preprocessState = 0
+  this.readState = 0
+  this.loaded = 0
+  this.total = 0
+  this.chunkSize = this.uploader.opts.chunkSize
+  this.startByte = this.offset * this.chunkSize
+  this.endByte = this.computeEndByte()
+  this.xhr = null
+}
+
+var STATUS = Chunk.STATUS = {
+  PENDING: 'pending',
+  UPLOADING: 'uploading',
+  READING: 'reading',
+  SUCCESS: 'success',
+  ERROR: 'error',
+  COMPLETE: 'complete',
+  PROGRESS: 'progress',
+  RETRY: 'retry'
+}
+
+utils.extend(Chunk.prototype, {
+
+  _event: function (evt, args) {
+    args = utils.toArray(arguments)
+    args.unshift(this)
+    this.file._chunkEvent.apply(this.file, args)
+  },
+
+  computeEndByte: function () {
+    var endByte = Math.min(this.file.size, (this.offset + 1) * this.chunkSize)
+    if (this.file.size - endByte < this.chunkSize && !this.uploader.opts.forceChunkSize) {
+      // The last chunk will be bigger than the chunk size,
+      // but less than 2 * this.chunkSize
+      endByte = this.file.size
+    }
+    return endByte
+  },
+
+  getParams: function () {
+    return {
+      chunkNumber: this.offset + 1,
+      chunkSize: this.uploader.opts.chunkSize,
+      currentChunkSize: this.endByte - this.startByte,
+      totalSize: this.file.size,
+      identifier: this.file.uniqueIdentifier,
+      filename: this.file.name,
+      relativePath: this.file.relativePath,
+      totalChunks: this.file.chunks.length
+    }
+  },
+
+  getTarget: function (target, params) {
+    if (!params.length) {
+      return target
+    }
+    if (target.indexOf('?') < 0) {
+      target += '?'
+    } else {
+      target += '&'
+    }
+    return target + params.join('&')
+  },
+
+  test: function () {
+    this.xhr = new XMLHttpRequest()
+    this.xhr.addEventListener('load', testHandler, false)
+    this.xhr.addEventListener('error', testHandler, false)
+    var testMethod = utils.evalOpts(this.uploader.opts.testMethod, this.file, this)
+    var data = this.prepareXhrRequest(testMethod, true)
+    this.xhr.send(data)
+
+    var $ = this
+    function testHandler (event) {
+      var status = $.status(true)
+      if (status === STATUS.ERROR) {
+        $._event(status, $.message())
+        $.uploader.uploadNextChunk()
+      } else if (status === STATUS.SUCCESS) {
+        $._event(status, $.message())
+        $.tested = true
+      } else if (!$.file.paused) {
+        // Error might be caused by file pause method
+        // Chunks does not exist on the server side
+        $.tested = true
+        $.send()
+      }
+    }
+  },
+
+  preprocessFinished: function () {
+    // Compute the endByte after the preprocess function to allow an
+    // implementer of preprocess to set the fileObj size
+    this.endByte = this.computeEndByte()
+    this.preprocessState = 2
+    this.send()
+  },
+
+  readFinished: function (bytes) {
+    this.readState = 2
+    this.bytes = bytes
+    this.send()
+  },
+
+  send: function () {
+    var preprocess = this.uploader.opts.preprocess
+    var read = this.uploader.opts.readFileFn
+    if (utils.isFunction(preprocess)) {
+      switch (this.preprocessState) {
+        case 0:
+          this.preprocessState = 1
+          preprocess(this)
+          return
+        case 1:
+          return
+      }
+    }
+    switch (this.readState) {
+      case 0:
+        this.readState = 1
+        read(this.file, this.file.fileType, this.startByte, this.endByte, this)
+        return
+      case 1:
+        return
+    }
+    if (this.uploader.opts.testChunks && !this.tested) {
+      this.test()
+      return
+    }
+
+    this.loaded = 0
+    this.total = 0
+    this.pendingRetry = false
+
+    // Set up request and listen for event
+    this.xhr = new XMLHttpRequest()
+    this.xhr.upload.addEventListener('progress', progressHandler, false)
+    this.xhr.addEventListener('load', doneHandler, false)
+    this.xhr.addEventListener('error', doneHandler, false)
+
+    var uploadMethod = utils.evalOpts(this.uploader.opts.uploadMethod, this.file, this)
+    var data = this.prepareXhrRequest(uploadMethod, false, this.uploader.opts.method, this.bytes)
+    this.xhr.send(data)
+
+    var $ = this
+    function progressHandler (event) {
+      if (event.lengthComputable) {
+        $.loaded = event.loaded
+        $.total = event.total
+      }
+      $._event(STATUS.PROGRESS, event)
+    }
+
+    function doneHandler (event) {
+      var msg = $.message()
+      $.processingResponse = true
+      $.uploader.opts.processResponse(msg, function (err, res) {
+        $.processingResponse = false
+        if (!$.xhr) {
+          return
+        }
+        $.processedState = {
+          err: err,
+          res: res
+        }
+        var status = $.status()
+        if (status === STATUS.SUCCESS || status === STATUS.ERROR) {
+          // delete this.data
+          $._event(status, res)
+          status === STATUS.ERROR && $.uploader.uploadNextChunk()
+        } else {
+          $._event(STATUS.RETRY, res)
+          $.pendingRetry = true
+          $.abort()
+          $.retries++
+          var retryInterval = $.uploader.opts.chunkRetryInterval
+          if (retryInterval !== null) {
+            setTimeout(function () {
+              $.send()
+            }, retryInterval)
+          } else {
+            $.send()
+          }
+        }
+      }, $.file, $)
+    }
+  },
+
+  abort: function () {
+    var xhr = this.xhr
+    this.xhr = null
+    this.processingResponse = false
+    this.processedState = null
+    if (xhr) {
+      xhr.abort()
+    }
+  },
+
+  status: function (isTest) {
+    if (this.readState === 1) {
+      return STATUS.READING
+    } else if (this.pendingRetry || this.preprocessState === 1) {
+      // if pending retry then that's effectively the same as actively uploading,
+      // there might just be a slight delay before the retry starts
+      return STATUS.UPLOADING
+    } else if (!this.xhr) {
+      return STATUS.PENDING
+    } else if (this.xhr.readyState < 4 || this.processingResponse) {
+      // Status is really 'OPENED', 'HEADERS_RECEIVED'
+      // or 'LOADING' - meaning that stuff is happening
+      return STATUS.UPLOADING
+    } else {
+      var _status
+      if (this.uploader.opts.successStatuses.indexOf(this.xhr.status) > -1) {
+        // HTTP 200, perfect
+        // HTTP 202 Accepted - The request has been accepted for processing, but the processing has not been completed.
+        _status = STATUS.SUCCESS
+      } else if (this.uploader.opts.permanentErrors.indexOf(this.xhr.status) > -1 ||
+          !isTest && this.retries >= this.uploader.opts.maxChunkRetries) {
+        // HTTP 415/500/501, permanent error
+        _status = STATUS.ERROR
+      } else {
+        // this should never happen, but we'll reset and queue a retry
+        // a likely case for this would be 503 service unavailable
+        this.abort()
+        _status = STATUS.PENDING
+      }
+      var processedState = this.processedState
+      if (processedState && processedState.err) {
+        _status = STATUS.ERROR
+      }
+      return _status
+    }
+  },
+
+  message: function () {
+    return this.xhr ? this.xhr.responseText : ''
+  },
+
+  progress: function () {
+    if (this.pendingRetry) {
+      return 0
+    }
+    var s = this.status()
+    if (s === STATUS.SUCCESS || s === STATUS.ERROR) {
+      return 1
+    } else if (s === STATUS.PENDING) {
+      return 0
+    } else {
+      return this.total > 0 ? this.loaded / this.total : 0
+    }
+  },
+
+  sizeUploaded: function () {
+    var size = this.endByte - this.startByte
+    // can't return only chunk.loaded value, because it is bigger than chunk size
+    if (this.status() !== STATUS.SUCCESS) {
+      size = this.progress() * size
+    }
+    return size
+  },
+
+  prepareXhrRequest: function (method, isTest, paramsMethod, blob) {
+    // Add data from the query options
+    var query = utils.evalOpts(this.uploader.opts.query, this.file, this, isTest)
+    query = utils.extend(this.getParams(), query)
+
+    // processParams
+    query = this.uploader.opts.processParams(query, this.file, this, isTest)
+
+    var target = utils.evalOpts(this.uploader.opts.target, this.file, this, isTest)
+    var data = null
+    if (method === 'GET' || paramsMethod === 'octet') {
+      // Add data from the query options
+      var params = []
+      utils.each(query, function (v, k) {
+        params.push([encodeURIComponent(k), encodeURIComponent(v)].join('='))
+      })
+      target = this.getTarget(target, params)
+      data = blob || null
+    } else {
+      // Add data from the query options
+      data = new FormData()
+      utils.each(query, function (v, k) {
+        data.append(k, v)
+      })
+      if (typeof blob !== 'undefined') {
+        data.append(this.uploader.opts.fileParameterName, blob, this.file.name)
+      }
+    }
+
+    this.xhr.open(method, target, true)
+    this.xhr.withCredentials = this.uploader.opts.withCredentials
+
+    // Add data from header options
+    utils.each(utils.evalOpts(this.uploader.opts.headers, this.file, this, isTest), function (v, k) {
+      this.xhr.setRequestHeader(k, v)
+    }, this)
+
+    return data
+  }
+
+})
+
+module.exports = Chunk

+ 49 - 0
node_modules/vue-simple-uploader/node_modules/simple-uploader.js/src/event.js

@@ -0,0 +1,49 @@
+var each = require('./utils').each
+
+var event = {
+
+  _eventData: null,
+
+  on: function (name, func) {
+    if (!this._eventData) this._eventData = {}
+    if (!this._eventData[name]) this._eventData[name] = []
+    var listened = false
+    each(this._eventData[name], function (fuc) {
+      if (fuc === func) {
+        listened = true
+        return false
+      }
+    })
+    if (!listened) {
+      this._eventData[name].push(func)
+    }
+  },
+
+  off: function (name, func) {
+    if (!this._eventData) this._eventData = {}
+    if (!this._eventData[name] || !this._eventData[name].length) return
+    if (func) {
+      each(this._eventData[name], function (fuc, i) {
+        if (fuc === func) {
+          this._eventData[name].splice(i, 1)
+          return false
+        }
+      }, this)
+    } else {
+      this._eventData[name] = []
+    }
+  },
+
+  trigger: function (name) {
+    if (!this._eventData) this._eventData = {}
+    if (!this._eventData[name]) return true
+    var args = this._eventData[name].slice.call(arguments, 1)
+    var preventDefault = false
+    each(this._eventData[name], function (fuc) {
+      preventDefault = fuc.apply(this, args) === false || preventDefault
+    }, this)
+    return !preventDefault
+  }
+}
+
+module.exports = event

+ 541 - 0
node_modules/vue-simple-uploader/node_modules/simple-uploader.js/src/file.js

@@ -0,0 +1,541 @@
+var utils = require('./utils')
+var Chunk = require('./chunk')
+
+function File (uploader, file, parent) {
+  utils.defineNonEnumerable(this, 'uploader', uploader)
+  this.isRoot = this.isFolder = uploader === this
+  utils.defineNonEnumerable(this, 'parent', parent || null)
+  utils.defineNonEnumerable(this, 'files', [])
+  utils.defineNonEnumerable(this, 'fileList', [])
+  utils.defineNonEnumerable(this, 'chunks', [])
+  utils.defineNonEnumerable(this, '_errorFiles', [])
+  utils.defineNonEnumerable(this, 'file', null)
+  this.id = utils.uid()
+
+  if (this.isRoot || !file) {
+    this.file = null
+  } else {
+    if (utils.isString(file)) {
+      // folder
+      this.isFolder = true
+      this.file = null
+      this.path = file
+      if (this.parent.path) {
+        file = file.substr(this.parent.path.length)
+      }
+      this.name = file.charAt(file.length - 1) === '/' ? file.substr(0, file.length - 1) : file
+    } else {
+      this.file = file
+      this.fileType = this.file.type
+      this.name = file.fileName || file.name
+      this.size = file.size
+      this.relativePath = file.relativePath || file.webkitRelativePath || this.name
+      this._parseFile()
+    }
+  }
+
+  this.paused = uploader.opts.initialPaused
+  this.error = false
+  this.allError = false
+  this.aborted = false
+  this.completed = false
+  this.averageSpeed = 0
+  this.currentSpeed = 0
+  this._lastProgressCallback = Date.now()
+  this._prevUploadedSize = 0
+  this._prevProgress = 0
+
+  this.bootstrap()
+}
+
+utils.extend(File.prototype, {
+
+  _parseFile: function () {
+    var ppaths = parsePaths(this.relativePath)
+    if (ppaths.length) {
+      var filePaths = this.uploader.filePaths
+      utils.each(ppaths, function (path, i) {
+        var folderFile = filePaths[path]
+        if (!folderFile) {
+          folderFile = new File(this.uploader, path, this.parent)
+          filePaths[path] = folderFile
+          this._updateParentFileList(folderFile)
+        }
+        this.parent = folderFile
+        folderFile.files.push(this)
+        if (!ppaths[i + 1]) {
+          folderFile.fileList.push(this)
+        }
+      }, this)
+    } else {
+      this._updateParentFileList()
+    }
+  },
+
+  _updateParentFileList: function (file) {
+    if (!file) {
+      file = this
+    }
+    var p = this.parent
+    if (p) {
+      p.fileList.push(file)
+    }
+  },
+
+  _eachAccess: function (eachFn, fileFn) {
+    if (this.isFolder) {
+      utils.each(this.files, function (f, i) {
+        return eachFn.call(this, f, i)
+      }, this)
+      return
+    }
+    fileFn.call(this, this)
+  },
+
+  bootstrap: function () {
+    if (this.isFolder) return
+    var opts = this.uploader.opts
+    if (utils.isFunction(opts.initFileFn)) {
+      opts.initFileFn.call(this, this)
+    }
+
+    this.abort(true)
+    this._resetError()
+    // Rebuild stack of chunks from file
+    this._prevProgress = 0
+    var round = opts.forceChunkSize ? Math.ceil : Math.floor
+    var chunks = Math.max(round(this.size / opts.chunkSize), 1)
+    for (var offset = 0; offset < chunks; offset++) {
+      this.chunks.push(new Chunk(this.uploader, this, offset))
+    }
+  },
+
+  _measureSpeed: function () {
+    var smoothingFactor = this.uploader.opts.speedSmoothingFactor
+    var timeSpan = Date.now() - this._lastProgressCallback
+    if (!timeSpan) {
+      return
+    }
+    var uploaded = this.sizeUploaded()
+    // Prevent negative upload speed after file upload resume
+    this.currentSpeed = Math.max((uploaded - this._prevUploadedSize) / timeSpan * 1000, 0)
+    this.averageSpeed = smoothingFactor * this.currentSpeed + (1 - smoothingFactor) * this.averageSpeed
+    this._prevUploadedSize = uploaded
+    if (this.parent && this.parent._checkProgress()) {
+      this.parent._measureSpeed()
+    }
+  },
+
+  _checkProgress: function (file) {
+    return Date.now() - this._lastProgressCallback >= this.uploader.opts.progressCallbacksInterval
+  },
+
+  _chunkEvent: function (chunk, evt, message) {
+    var uploader = this.uploader
+    var STATUS = Chunk.STATUS
+    var that = this
+    var rootFile = this.getRoot()
+    var triggerProgress = function () {
+      that._measureSpeed()
+      uploader._trigger('fileProgress', rootFile, that, chunk)
+      that._lastProgressCallback = Date.now()
+    }
+    switch (evt) {
+      case STATUS.PROGRESS:
+        if (this._checkProgress()) {
+          triggerProgress()
+        }
+        break
+      case STATUS.ERROR:
+        this._error()
+        this.abort(true)
+        uploader._trigger('fileError', rootFile, this, message, chunk)
+        break
+      case STATUS.SUCCESS:
+        this._updateUploadedChunks(message, chunk)
+        if (this.error) {
+          return
+        }
+        clearTimeout(this._progeressId)
+        this._progeressId = 0
+        var timeDiff = Date.now() - this._lastProgressCallback
+        if (timeDiff < uploader.opts.progressCallbacksInterval) {
+          this._progeressId = setTimeout(triggerProgress, uploader.opts.progressCallbacksInterval - timeDiff)
+        }
+        if (this.isComplete()) {
+          clearTimeout(this._progeressId)
+          triggerProgress()
+          this.currentSpeed = 0
+          this.averageSpeed = 0
+          uploader._trigger('fileSuccess', rootFile, this, message, chunk)
+          if (rootFile.isComplete()) {
+            uploader._trigger('fileComplete', rootFile, this)
+          }
+        } else if (!this._progeressId) {
+          triggerProgress()
+        }
+        break
+      case STATUS.RETRY:
+        uploader._trigger('fileRetry', rootFile, this, chunk)
+        break
+    }
+  },
+
+  _updateUploadedChunks: function (message, chunk) {
+    var checkChunkUploaded = this.uploader.opts.checkChunkUploadedByResponse
+    if (checkChunkUploaded) {
+      var xhr = chunk.xhr
+      utils.each(this.chunks, function (_chunk) {
+        if (!_chunk.tested) {
+          var uploaded = checkChunkUploaded.call(this, _chunk, message)
+          if (_chunk === chunk && !uploaded) {
+            // fix the first chunk xhr status
+            // treated as success but checkChunkUploaded is false
+            // so the current chunk should be uploaded again
+            _chunk.xhr = null
+          }
+          if (uploaded) {
+            // first success and other chunks are uploaded
+            // then set xhr, so the uploaded chunks
+            // will be treated as success too
+            _chunk.xhr = xhr
+          }
+          _chunk.tested = true
+        }
+      }, this)
+      if (!this._firstResponse) {
+        this._firstResponse = true
+        this.uploader.upload(true)
+      } else {
+        this.uploader.uploadNextChunk()
+      }
+    } else {
+      this.uploader.uploadNextChunk()
+    }
+  },
+
+  _error: function () {
+    this.error = this.allError = true
+    var parent = this.parent
+    while (parent && parent !== this.uploader) {
+      parent._errorFiles.push(this)
+      parent.error = true
+      if (parent._errorFiles.length === parent.files.length) {
+        parent.allError = true
+      }
+      parent = parent.parent
+    }
+  },
+
+  _resetError: function () {
+    this.error = this.allError = false
+    var parent = this.parent
+    var index = -1
+    while (parent && parent !== this.uploader) {
+      index = parent._errorFiles.indexOf(this)
+      parent._errorFiles.splice(index, 1)
+      parent.allError = false
+      if (!parent._errorFiles.length) {
+        parent.error = false
+      }
+      parent = parent.parent
+    }
+  },
+
+  isComplete: function () {
+    if (!this.completed) {
+      var outstanding = false
+      this._eachAccess(function (file) {
+        if (!file.isComplete()) {
+          outstanding = true
+          return false
+        }
+      }, function () {
+        if (this.error) {
+          outstanding = true
+        } else {
+          var STATUS = Chunk.STATUS
+          utils.each(this.chunks, function (chunk) {
+            var status = chunk.status()
+            if (status === STATUS.ERROR || status === STATUS.PENDING || status === STATUS.UPLOADING || status === STATUS.READING || chunk.preprocessState === 1 || chunk.readState === 1) {
+              outstanding = true
+              return false
+            }
+          })
+        }
+      })
+      this.completed = !outstanding
+    }
+    return this.completed
+  },
+
+  isUploading: function () {
+    var uploading = false
+    this._eachAccess(function (file) {
+      if (file.isUploading()) {
+        uploading = true
+        return false
+      }
+    }, function () {
+      var uploadingStatus = Chunk.STATUS.UPLOADING
+      utils.each(this.chunks, function (chunk) {
+        if (chunk.status() === uploadingStatus) {
+          uploading = true
+          return false
+        }
+      })
+    })
+    return uploading
+  },
+
+  resume: function () {
+    this._eachAccess(function (f) {
+      f.resume()
+    }, function () {
+      this.paused = false
+      this.aborted = false
+      this.uploader.upload()
+    })
+    this.paused = false
+    this.aborted = false
+  },
+
+  pause: function () {
+    this._eachAccess(function (f) {
+      f.pause()
+    }, function () {
+      this.paused = true
+      this.abort()
+    })
+    this.paused = true
+  },
+
+  cancel: function () {
+    this.uploader.removeFile(this)
+  },
+
+  retry: function (file) {
+    var fileRetry = function (file) {
+      if (file.error) {
+        file.bootstrap()
+      }
+    }
+    if (file) {
+      file.bootstrap()
+    } else {
+      this._eachAccess(fileRetry, function () {
+        this.bootstrap()
+      })
+    }
+    this.uploader.upload()
+  },
+
+  abort: function (reset) {
+    if (this.aborted) {
+      return
+    }
+    this.currentSpeed = 0
+    this.averageSpeed = 0
+    this.aborted = !reset
+    var chunks = this.chunks
+    if (reset) {
+      this.chunks = []
+    }
+    var uploadingStatus = Chunk.STATUS.UPLOADING
+    utils.each(chunks, function (c) {
+      if (c.status() === uploadingStatus) {
+        c.abort()
+        this.uploader.uploadNextChunk()
+      }
+    }, this)
+  },
+
+  progress: function () {
+    var totalDone = 0
+    var totalSize = 0
+    var ret = 0
+    this._eachAccess(function (file, index) {
+      totalDone += file.progress() * file.size
+      totalSize += file.size
+      if (index === this.files.length - 1) {
+        ret = totalSize > 0 ? totalDone / totalSize : this.isComplete() ? 1 : 0
+      }
+    }, function () {
+      if (this.error) {
+        ret = 1
+        return
+      }
+      if (this.chunks.length === 1) {
+        this._prevProgress = Math.max(this._prevProgress, this.chunks[0].progress())
+        ret = this._prevProgress
+        return
+      }
+      // Sum up progress across everything
+      var bytesLoaded = 0
+      utils.each(this.chunks, function (c) {
+        // get chunk progress relative to entire file
+        bytesLoaded += c.progress() * (c.endByte - c.startByte)
+      })
+      var percent = bytesLoaded / this.size
+      // We don't want to lose percentages when an upload is paused
+      this._prevProgress = Math.max(this._prevProgress, percent > 0.9999 ? 1 : percent)
+      ret = this._prevProgress
+    })
+    return ret
+  },
+
+  getSize: function () {
+    var size = 0
+    this._eachAccess(function (file) {
+      size += file.size
+    }, function () {
+      size += this.size
+    })
+    return size
+  },
+
+  getFormatSize: function () {
+    var size = this.getSize()
+    return utils.formatSize(size)
+  },
+
+  getRoot: function () {
+    if (this.isRoot) {
+      return this
+    }
+    var parent = this.parent
+    while (parent) {
+      if (parent.parent === this.uploader) {
+        // find it
+        return parent
+      }
+      parent = parent.parent
+    }
+    return this
+  },
+
+  sizeUploaded: function () {
+    var size = 0
+    this._eachAccess(function (file) {
+      size += file.sizeUploaded()
+    }, function () {
+      utils.each(this.chunks, function (chunk) {
+        size += chunk.sizeUploaded()
+      })
+    })
+    return size
+  },
+
+  timeRemaining: function () {
+    var ret = 0
+    var sizeDelta = 0
+    var averageSpeed = 0
+    this._eachAccess(function (file, i) {
+      if (!file.paused && !file.error) {
+        sizeDelta += file.size - file.sizeUploaded()
+        averageSpeed += file.averageSpeed
+      }
+      if (i === this.files.length - 1) {
+        ret = calRet(sizeDelta, averageSpeed)
+      }
+    }, function () {
+      if (this.paused || this.error) {
+        ret = 0
+        return
+      }
+      var delta = this.size - this.sizeUploaded()
+      ret = calRet(delta, this.averageSpeed)
+    })
+    return ret
+    function calRet (delta, averageSpeed) {
+      if (delta && !averageSpeed) {
+        return Number.POSITIVE_INFINITY
+      }
+      if (!delta && !averageSpeed) {
+        return 0
+      }
+      return Math.floor(delta / averageSpeed)
+    }
+  },
+
+  removeFile: function (file) {
+    if (file.isFolder) {
+      while (file.files.length) {
+        var f = file.files[file.files.length - 1]
+        this._removeFile(f)
+      }
+    }
+    this._removeFile(file)
+  },
+
+  _delFilePath: function (file) {
+    if (file.path && this.filePaths) {
+      delete this.filePaths[file.path]
+    }
+    utils.each(file.fileList, function (file) {
+      this._delFilePath(file)
+    }, this)
+  },
+
+  _removeFile: function (file) {
+    if (!file.isFolder) {
+      utils.each(this.files, function (f, i) {
+        if (f === file) {
+          this.files.splice(i, 1)
+          return false
+        }
+      }, this)
+      file.abort()
+      var parent = file.parent
+      var newParent
+      while (parent && parent !== this) {
+        newParent = parent.parent
+        parent._removeFile(file)
+        parent = newParent
+      }
+    }
+    file.parent === this && utils.each(this.fileList, function (f, i) {
+      if (f === file) {
+        this.fileList.splice(i, 1)
+        return false
+      }
+    }, this)
+    if (!this.isRoot && this.isFolder && !this.files.length) {
+      this.parent._removeFile(this)
+      this.uploader._delFilePath(this)
+    }
+    file.parent = null
+  },
+
+  getType: function () {
+    if (this.isFolder) {
+      return 'folder'
+    }
+    return this.file.type && this.file.type.split('/')[1]
+  },
+
+  getExtension: function () {
+    if (this.isFolder) {
+      return ''
+    }
+    return this.name.substr((~-this.name.lastIndexOf('.') >>> 0) + 2).toLowerCase()
+  }
+
+})
+
+module.exports = File
+
+function parsePaths (path) {
+  var ret = []
+  var paths = path.split('/')
+  var len = paths.length
+  var i = 1
+  paths.splice(len - 1, 1)
+  len--
+  if (paths.length) {
+    while (i <= len) {
+      ret.push(paths.slice(0, i++).join('/') + '/')
+    }
+  }
+  return ret
+}

+ 514 - 0
node_modules/vue-simple-uploader/node_modules/simple-uploader.js/src/uploader.js

@@ -0,0 +1,514 @@
+var utils = require('./utils')
+var event = require('./event')
+var File = require('./file')
+var Chunk = require('./chunk')
+
+var version = '__VERSION__'
+
+var isServer = typeof window === 'undefined'
+
+// ie10+
+var ie10plus = isServer ? false : window.navigator.msPointerEnabled
+var support = (function () {
+  if (isServer) {
+    return false
+  }
+  var sliceName = 'slice'
+  var _support = utils.isDefined(window.File) && utils.isDefined(window.Blob) &&
+                utils.isDefined(window.FileList)
+  var bproto = null
+  if (_support) {
+    bproto = window.Blob.prototype
+    utils.each(['slice', 'webkitSlice', 'mozSlice'], function (n) {
+      if (bproto[n]) {
+        sliceName = n
+        return false
+      }
+    })
+    _support = !!bproto[sliceName]
+  }
+  if (_support) Uploader.sliceName = sliceName
+  bproto = null
+  return _support
+})()
+
+var supportDirectory = (function () {
+  if (isServer) {
+    return false
+  }
+  var input = window.document.createElement('input')
+  input.type = 'file'
+  var sd = 'webkitdirectory' in input || 'directory' in input
+  input = null
+  return sd
+})()
+
+function Uploader (opts) {
+  this.support = support
+  /* istanbul ignore if */
+  if (!this.support) {
+    return
+  }
+  this.supportDirectory = supportDirectory
+  utils.defineNonEnumerable(this, 'filePaths', {})
+  this.opts = utils.extend({}, Uploader.defaults, opts || {})
+
+  this.preventEvent = utils.bind(this._preventEvent, this)
+
+  File.call(this, this)
+}
+
+/**
+ * Default read function using the webAPI
+ *
+ * @function webAPIFileRead(fileObj, fileType, startByte, endByte, chunk)
+ *
+ */
+var webAPIFileRead = function (fileObj, fileType, startByte, endByte, chunk) {
+  chunk.readFinished(fileObj.file[Uploader.sliceName](startByte, endByte, fileType))
+}
+
+Uploader.version = version
+
+Uploader.defaults = {
+  chunkSize: 1024 * 1024,
+  forceChunkSize: false,
+  simultaneousUploads: 3,
+  singleFile: false,
+  fileParameterName: 'file',
+  progressCallbacksInterval: 500,
+  speedSmoothingFactor: 0.1,
+  query: {},
+  headers: {},
+  withCredentials: false,
+  preprocess: null,
+  method: 'multipart',
+  testMethod: 'GET',
+  uploadMethod: 'POST',
+  prioritizeFirstAndLastChunk: false,
+  allowDuplicateUploads: false,
+  target: '/',
+  testChunks: true,
+  generateUniqueIdentifier: null,
+  maxChunkRetries: 0,
+  chunkRetryInterval: null,
+  permanentErrors: [404, 415, 500, 501],
+  successStatuses: [200, 201, 202],
+  onDropStopPropagation: false,
+  initFileFn: null,
+  readFileFn: webAPIFileRead,
+  checkChunkUploadedByResponse: null,
+  initialPaused: false,
+  processResponse: function (response, cb) {
+    cb(null, response)
+  },
+  processParams: function (params) {
+    return params
+  }
+}
+
+Uploader.utils = utils
+Uploader.event = event
+Uploader.File = File
+Uploader.Chunk = Chunk
+
+// inherit file
+Uploader.prototype = utils.extend({}, File.prototype)
+// inherit event
+utils.extend(Uploader.prototype, event)
+utils.extend(Uploader.prototype, {
+
+  constructor: Uploader,
+
+  _trigger: function (name) {
+    var args = utils.toArray(arguments)
+    var preventDefault = !this.trigger.apply(this, arguments)
+    if (name !== 'catchAll') {
+      args.unshift('catchAll')
+      preventDefault = !this.trigger.apply(this, args) || preventDefault
+    }
+    return !preventDefault
+  },
+
+  _triggerAsync: function () {
+    var args = arguments
+    utils.nextTick(function () {
+      this._trigger.apply(this, args)
+    }, this)
+  },
+
+  addFiles: function (files, evt) {
+    var _files = []
+    var oldFileListLen = this.fileList.length
+    utils.each(files, function (file) {
+      // Uploading empty file IE10/IE11 hangs indefinitely
+      // Directories have size `0` and name `.`
+      // Ignore already added files if opts.allowDuplicateUploads is set to false
+      if ((!ie10plus || ie10plus && file.size > 0) && !(file.size % 4096 === 0 && (file.name === '.' || file.fileName === '.'))) {
+        var uniqueIdentifier = this.generateUniqueIdentifier(file)
+        if (this.opts.allowDuplicateUploads || !this.getFromUniqueIdentifier(uniqueIdentifier)) {
+          var _file = new File(this, file, this)
+          _file.uniqueIdentifier = uniqueIdentifier
+          if (this._trigger('fileAdded', _file, evt)) {
+            _files.push(_file)
+          } else {
+            File.prototype.removeFile.call(this, _file)
+          }
+        }
+      }
+    }, this)
+    // get new fileList
+    var newFileList = this.fileList.slice(oldFileListLen)
+    if (this._trigger('filesAdded', _files, newFileList, evt)) {
+      utils.each(_files, function (file) {
+        if (this.opts.singleFile && this.files.length > 0) {
+          this.removeFile(this.files[0])
+        }
+        this.files.push(file)
+      }, this)
+      this._trigger('filesSubmitted', _files, newFileList, evt)
+    } else {
+      utils.each(newFileList, function (file) {
+        File.prototype.removeFile.call(this, file)
+      }, this)
+    }
+  },
+
+  addFile: function (file, evt) {
+    this.addFiles([file], evt)
+  },
+
+  cancel: function () {
+    for (var i = this.fileList.length - 1; i >= 0; i--) {
+      this.fileList[i].cancel()
+    }
+  },
+
+  removeFile: function (file) {
+    File.prototype.removeFile.call(this, file)
+    this._trigger('fileRemoved', file)
+  },
+
+  generateUniqueIdentifier: function (file) {
+    var custom = this.opts.generateUniqueIdentifier
+    if (utils.isFunction(custom)) {
+      return custom(file)
+    }
+    /* istanbul ignore next */
+    // Some confusion in different versions of Firefox
+    var relativePath = file.relativePath || file.webkitRelativePath || file.fileName || file.name
+    /* istanbul ignore next */
+    return file.size + '-' + relativePath.replace(/[^0-9a-zA-Z_-]/img, '')
+  },
+
+  getFromUniqueIdentifier: function (uniqueIdentifier) {
+    var ret = false
+    utils.each(this.files, function (file) {
+      if (file.uniqueIdentifier === uniqueIdentifier) {
+        ret = file
+        return false
+      }
+    })
+    return ret
+  },
+
+  uploadNextChunk: function (preventEvents) {
+    var found = false
+    var pendingStatus = Chunk.STATUS.PENDING
+    var checkChunkUploaded = this.uploader.opts.checkChunkUploadedByResponse
+    if (this.opts.prioritizeFirstAndLastChunk) {
+      utils.each(this.files, function (file) {
+        if (file.paused) {
+          return
+        }
+        if (checkChunkUploaded && !file._firstResponse && file.isUploading()) {
+          // waiting for current file's first chunk response
+          return
+        }
+        if (file.chunks.length && file.chunks[0].status() === pendingStatus) {
+          file.chunks[0].send()
+          found = true
+          return false
+        }
+        if (file.chunks.length > 1 && file.chunks[file.chunks.length - 1].status() === pendingStatus) {
+          file.chunks[file.chunks.length - 1].send()
+          found = true
+          return false
+        }
+      })
+      if (found) {
+        return found
+      }
+    }
+
+    // Now, simply look for the next, best thing to upload
+    utils.each(this.files, function (file) {
+      if (!file.paused) {
+        if (checkChunkUploaded && !file._firstResponse && file.isUploading()) {
+          // waiting for current file's first chunk response
+          return
+        }
+        utils.each(file.chunks, function (chunk) {
+          if (chunk.status() === pendingStatus) {
+            chunk.send()
+            found = true
+            return false
+          }
+        })
+      }
+      if (found) {
+        return false
+      }
+    })
+    if (found) {
+      return true
+    }
+
+    // The are no more outstanding chunks to upload, check is everything is done
+    var outstanding = false
+    utils.each(this.files, function (file) {
+      if (!file.isComplete()) {
+        outstanding = true
+        return false
+      }
+    })
+    // should check files now
+    // if now files in list
+    // should not trigger complete event
+    if (!outstanding && !preventEvents && this.files.length) {
+      // All chunks have been uploaded, complete
+      this._triggerAsync('complete')
+    }
+    return outstanding
+  },
+
+  upload: function (preventEvents) {
+    // Make sure we don't start too many uploads at once
+    var ret = this._shouldUploadNext()
+    if (ret === false) {
+      return
+    }
+    !preventEvents && this._trigger('uploadStart')
+    var started = false
+    for (var num = 1; num <= this.opts.simultaneousUploads - ret; num++) {
+      started = this.uploadNextChunk(!preventEvents) || started
+      if (!started && preventEvents) {
+        // completed
+        break
+      }
+    }
+    if (!started && !preventEvents) {
+      this._triggerAsync('complete')
+    }
+  },
+
+  /**
+   * should upload next chunk
+   * @function
+   * @returns {Boolean|Number}
+   */
+  _shouldUploadNext: function () {
+    var num = 0
+    var should = true
+    var simultaneousUploads = this.opts.simultaneousUploads
+    var uploadingStatus = Chunk.STATUS.UPLOADING
+    utils.each(this.files, function (file) {
+      utils.each(file.chunks, function (chunk) {
+        if (chunk.status() === uploadingStatus) {
+          num++
+          if (num >= simultaneousUploads) {
+            should = false
+            return false
+          }
+        }
+      })
+      return should
+    })
+    // if should is true then return uploading chunks's length
+    return should && num
+  },
+
+  /**
+   * Assign a browse action to one or more DOM nodes.
+   * @function
+   * @param {Element|Array.<Element>} domNodes
+   * @param {boolean} isDirectory Pass in true to allow directories to
+   * @param {boolean} singleFile prevent multi file upload
+   * @param {Object} attributes set custom attributes:
+   *  http://www.w3.org/TR/html-markup/input.file.html#input.file-attributes
+   *  eg: accept: 'image/*'
+   * be selected (Chrome only).
+   */
+  assignBrowse: function (domNodes, isDirectory, singleFile, attributes) {
+    if (typeof domNodes.length === 'undefined') {
+      domNodes = [domNodes]
+    }
+
+    utils.each(domNodes, function (domNode) {
+      var input
+      if (domNode.tagName === 'INPUT' && domNode.type === 'file') {
+        input = domNode
+      } else {
+        input = document.createElement('input')
+        input.setAttribute('type', 'file')
+        // display:none - not working in opera 12
+        utils.extend(input.style, {
+          visibility: 'hidden',
+          position: 'absolute',
+          width: '1px',
+          height: '1px'
+        })
+        // for opera 12 browser, input must be assigned to a document
+        domNode.appendChild(input)
+        // https://developer.mozilla.org/en/using_files_from_web_applications)
+        // event listener is executed two times
+        // first one - original mouse click event
+        // second - input.click(), input is inside domNode
+        domNode.addEventListener('click', function (e) {
+          if (domNode.tagName.toLowerCase() === 'label') {
+            return
+          }
+          input.click()
+        }, false)
+      }
+      if (!this.opts.singleFile && !singleFile) {
+        input.setAttribute('multiple', 'multiple')
+      }
+      if (isDirectory) {
+        input.setAttribute('webkitdirectory', 'webkitdirectory')
+      }
+      attributes && utils.each(attributes, function (value, key) {
+        input.setAttribute(key, value)
+      })
+      // When new files are added, simply append them to the overall list
+      var that = this
+      input.addEventListener('change', function (e) {
+        that._trigger(e.type, e)
+        if (e.target.value) {
+          that.addFiles(e.target.files, e)
+          e.target.value = ''
+        }
+      }, false)
+    }, this)
+  },
+
+  onDrop: function (evt) {
+    this._trigger(evt.type, evt)
+    if (this.opts.onDropStopPropagation) {
+      evt.stopPropagation()
+    }
+    evt.preventDefault()
+    this._parseDataTransfer(evt.dataTransfer, evt)
+  },
+
+  _parseDataTransfer: function (dataTransfer, evt) {
+    if (dataTransfer.items && dataTransfer.items[0] &&
+      dataTransfer.items[0].webkitGetAsEntry) {
+      this.webkitReadDataTransfer(dataTransfer, evt)
+    } else {
+      this.addFiles(dataTransfer.files, evt)
+    }
+  },
+
+  webkitReadDataTransfer: function (dataTransfer, evt) {
+    var self = this
+    var queue = dataTransfer.items.length
+    var files = []
+    utils.each(dataTransfer.items, function (item) {
+      var entry = item.webkitGetAsEntry()
+      if (!entry) {
+        decrement()
+        return
+      }
+      if (entry.isFile) {
+        // due to a bug in Chrome's File System API impl - #149735
+        fileReadSuccess(item.getAsFile(), entry.fullPath)
+      } else {
+        readDirectory(entry.createReader())
+      }
+    })
+    function readDirectory (reader) {
+      reader.readEntries(function (entries) {
+        if (entries.length) {
+          queue += entries.length
+          utils.each(entries, function (entry) {
+            if (entry.isFile) {
+              var fullPath = entry.fullPath
+              entry.file(function (file) {
+                fileReadSuccess(file, fullPath)
+              }, readError)
+            } else if (entry.isDirectory) {
+              readDirectory(entry.createReader())
+            }
+          })
+          readDirectory(reader)
+        } else {
+          decrement()
+        }
+      }, readError)
+    }
+    function fileReadSuccess (file, fullPath) {
+      // relative path should not start with "/"
+      file.relativePath = fullPath.substring(1)
+      files.push(file)
+      decrement()
+    }
+    function readError (fileError) {
+      throw fileError
+    }
+    function decrement () {
+      if (--queue === 0) {
+        self.addFiles(files, evt)
+      }
+    }
+  },
+
+  _assignHelper: function (domNodes, handles, remove) {
+    if (typeof domNodes.length === 'undefined') {
+      domNodes = [domNodes]
+    }
+    var evtMethod = remove ? 'removeEventListener' : 'addEventListener'
+    utils.each(domNodes, function (domNode) {
+      utils.each(handles, function (handler, name) {
+        domNode[evtMethod](name, handler, false)
+      }, this)
+    }, this)
+  },
+
+  _preventEvent: function (e) {
+    utils.preventEvent(e)
+    this._trigger(e.type, e)
+  },
+
+  /**
+   * Assign one or more DOM nodes as a drop target.
+   * @function
+   * @param {Element|Array.<Element>} domNodes
+   */
+  assignDrop: function (domNodes) {
+    this._onDrop = utils.bind(this.onDrop, this)
+    this._assignHelper(domNodes, {
+      dragover: this.preventEvent,
+      dragenter: this.preventEvent,
+      dragleave: this.preventEvent,
+      drop: this._onDrop
+    })
+  },
+
+  /**
+   * Un-assign drop event from DOM nodes
+   * @function
+   * @param domNodes
+   */
+  unAssignDrop: function (domNodes) {
+    this._assignHelper(domNodes, {
+      dragover: this.preventEvent,
+      dragenter: this.preventEvent,
+      dragleave: this.preventEvent,
+      drop: this._onDrop
+    }, true)
+    this._onDrop = null
+  }
+})
+
+module.exports = Uploader

+ 171 - 0
node_modules/vue-simple-uploader/node_modules/simple-uploader.js/src/utils.js

@@ -0,0 +1,171 @@
+var oproto = Object.prototype
+var aproto = Array.prototype
+var serialize = oproto.toString
+
+var isFunction = function (fn) {
+  return serialize.call(fn) === '[object Function]'
+}
+
+var isArray = Array.isArray || /* istanbul ignore next */ function (ary) {
+  return serialize.call(ary) === '[object Array]'
+}
+
+var isPlainObject = function (obj) {
+  return serialize.call(obj) === '[object Object]' && Object.getPrototypeOf(obj) === oproto
+}
+
+var i = 0
+var utils = {
+  uid: function () {
+    return ++i
+  },
+  noop: function () {},
+  bind: function (fn, context) {
+    return function () {
+      return fn.apply(context, arguments)
+    }
+  },
+  preventEvent: function (evt) {
+    evt.preventDefault()
+  },
+  stop: function (evt) {
+    evt.preventDefault()
+    evt.stopPropagation()
+  },
+  nextTick: function (fn, context) {
+    setTimeout(utils.bind(fn, context), 0)
+  },
+  toArray: function (ary, start, end) {
+    if (start === undefined) start = 0
+    if (end === undefined) end = ary.length
+    return aproto.slice.call(ary, start, end)
+  },
+
+  isPlainObject: isPlainObject,
+  isFunction: isFunction,
+  isArray: isArray,
+  isObject: function (obj) {
+    return Object(obj) === obj
+  },
+  isString: function (s) {
+    return typeof s === 'string'
+  },
+  isUndefined: function (a) {
+    return typeof a === 'undefined'
+  },
+  isDefined: function (a) {
+    return typeof a !== 'undefined'
+  },
+
+  each: function (ary, func, context) {
+    if (utils.isDefined(ary.length)) {
+      for (var i = 0, len = ary.length; i < len; i++) {
+        if (func.call(context, ary[i], i, ary) === false) {
+          break
+        }
+      }
+    } else {
+      for (var k in ary) {
+        if (func.call(context, ary[k], k, ary) === false) {
+          break
+        }
+      }
+    }
+  },
+
+  /**
+   * If option is a function, evaluate it with given params
+   * @param {*} data
+   * @param {...} args arguments of a callback
+   * @returns {*}
+   */
+  evalOpts: function (data, args) {
+    if (utils.isFunction(data)) {
+      // `arguments` is an object, not array, in FF, so:
+      args = utils.toArray(arguments)
+      data = data.apply(null, args.slice(1))
+    }
+    return data
+  },
+
+  extend: function () {
+    var options
+    var name
+    var src
+    var copy
+    var copyIsArray
+    var clone
+    var target = arguments[0] || {}
+    var i = 1
+    var length = arguments.length
+    var force = false
+
+    // 如果第一个参数为布尔,判定是否深拷贝
+    if (typeof target === 'boolean') {
+      force = target
+      target = arguments[1] || {}
+      i++
+    }
+
+    // 确保接受方为一个复杂的数据类型
+    if (typeof target !== 'object' && !isFunction(target)) {
+      target = {}
+    }
+
+    // 如果只有一个参数,那么新成员添加于 extend 所在的对象上
+    if (i === length) {
+      target = this
+      i--
+    }
+
+    for (; i < length; i++) {
+      // 只处理非空参数
+      if ((options = arguments[i]) != null) {
+        for (name in options) {
+          src = target[name]
+          copy = options[name]
+
+          // 防止环引用
+          if (target === copy) {
+            continue
+          }
+          if (force && copy && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) {
+            if (copyIsArray) {
+              copyIsArray = false
+              clone = src && isArray(src) ? src : []
+            } else {
+              clone = src && isPlainObject(src) ? src : {}
+            }
+            target[name] = utils.extend(force, clone, copy)
+          } else if (copy !== undefined) {
+            target[name] = copy
+          }
+        }
+      }
+    }
+    return target
+  },
+
+  formatSize: function (size) {
+    if (size < 1024) {
+      return size.toFixed(0) + ' bytes'
+    } else if (size < 1024 * 1024) {
+      return (size / 1024.0).toFixed(0) + ' KB'
+    } else if (size < 1024 * 1024 * 1024) {
+      return (size / 1024.0 / 1024.0).toFixed(1) + ' MB'
+    } else {
+      return (size / 1024.0 / 1024.0 / 1024.0).toFixed(1) + ' GB'
+    }
+  },
+
+  defineNonEnumerable: function (target, key, value) {
+    Object.defineProperty(target, key, {
+      enumerable: false,
+      configurable: true,
+      writable: true,
+      value: value
+    })
+  }
+}
+
+module.exports = utils

+ 14 - 0
node_modules/vue-simple-uploader/node_modules/simple-uploader.js/test.sh

@@ -0,0 +1,14 @@
+#!/bin/bash
+set -e
+
+if [ $TESTNAME = "unit-tests" ]; then
+  echo "Running unit-tests"
+  export DISPLAY=:99.0
+  sh -e /etc/init.d/xvfb start
+  sleep 1
+  npm run test
+  npm run codecov
+elif [ $TESTNAME = "browser-tests" ]; then
+  echo "Running browser-tests"
+  npm run test:ci
+fi

+ 86 - 0
node_modules/vue-simple-uploader/package.json

@@ -0,0 +1,86 @@
+{
+  "name": "vue-simple-uploader",
+  "version": "0.7.6",
+  "description": "A Vue.js upload component powered by simple-uploader.js",
+  "main": "dist/vue-uploader.js",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/simple-uploader/vue-uploader.git"
+  },
+  "bugs": "https://github.com/simple-uploader/vue-uploader/issues",
+  "homepage": "https://github.com/simple-uploader/vue-uploader",
+  "author": "dolymood@gmail.com",
+  "license": "MIT",
+  "scripts": {
+    "dev": "node build/dev-server.js",
+    "start": "node build/dev-server.js",
+    "build": "node build/build.js",
+    "lint": "eslint --ext .js,.vue src test/unit/specs"
+  },
+  "dependencies": {
+    "simple-uploader.js": "^0.5.6"
+  },
+  "peerDependencies": {
+    "vue": ">=2.2"
+  },
+  "devDependencies": {
+    "autoprefixer": "^7.1.2",
+    "babel-core": "^6.22.1",
+    "babel-eslint": "^7.1.1",
+    "babel-loader": "^7.1.1",
+    "babel-plugin-add-module-exports": "^0.2.1",
+    "babel-plugin-transform-es2015-modules-umd": "^6.24.1",
+    "babel-plugin-transform-runtime": "^6.22.0",
+    "babel-preset-env": "^1.3.2",
+    "babel-preset-stage-2": "^6.22.0",
+    "babel-register": "^6.22.0",
+    "chalk": "^2.0.1",
+    "connect-history-api-fallback": "^1.3.0",
+    "copy-webpack-plugin": "^4.0.1",
+    "css-loader": "^0.28.0",
+    "cssnano": "^3.10.0",
+    "eslint": "^4.18.2",
+    "eslint-config-standard": "^6.2.1",
+    "eslint-friendly-formatter": "^3.0.0",
+    "eslint-loader": "^1.7.1",
+    "eslint-plugin-html": "^3.0.0",
+    "eslint-plugin-promise": "^3.4.0",
+    "eslint-plugin-standard": "^2.0.1",
+    "eventsource-polyfill": "^0.9.6",
+    "express": "^4.14.1",
+    "extract-text-webpack-plugin": "^2.0.0",
+    "file-loader": "^0.11.1",
+    "friendly-errors-webpack-plugin": "^1.1.3",
+    "html-webpack-plugin": "^2.28.0",
+    "http-proxy-middleware": "^0.17.3",
+    "opn": "^5.1.0",
+    "optimize-css-assets-webpack-plugin": "^2.0.0",
+    "ora": "^1.2.0",
+    "rimraf": "^2.6.0",
+    "semver": "^5.3.0",
+    "shelljs": "^0.7.6",
+    "url-loader": "^0.5.8",
+    "vue": "^2.3.3",
+    "vue-loader": "^12.1.0",
+    "vue-style-loader": "^3.0.1",
+    "vue-template-compiler": "^2.3.3",
+    "webpack": "^2.6.1",
+    "webpack-bundle-analyzer": "^3.3.2",
+    "webpack-dev-middleware": "^1.10.0",
+    "webpack-hot-middleware": "^2.18.0",
+    "webpack-merge": "^4.1.0"
+  },
+  "engines": {
+    "node": ">= 4.0.0",
+    "npm": ">= 3.0.0"
+  },
+  "browserslist": [
+    "> 1%",
+    "not ie <= 8",
+    "Android >= 4.0",
+    "iOS >= 8"
+  ],
+  "__npminstall_done": "Wed Jun 02 2021 14:28:26 GMT+0800 (中国标准时间)",
+  "_from": "vue-simple-uploader@0.7.6",
+  "_resolved": "https://registry.npm.taobao.org/vue-simple-uploader/download/vue-simple-uploader-0.7.6.tgz?cache=0&sync_timestamp=1603790927320&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-simple-uploader%2Fdownload%2Fvue-simple-uploader-0.7.6.tgz"
+}

+ 0 - 0
node_modules/vue-simple-uploader/src/common/file-events.js


Some files were not shown because too many files changed in this diff