heyujie
2021-09-18 44a3abe105e9b9b4d1a7173772fc9d18c9b59529
应用中心ui改动
15个文件已添加
11个文件已修改
4075 ■■■■ 已修改文件
public/apps.json 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
public/images/appCenter/Group-112.png 补丁 | 查看 | 原始文档 | blame | 历史
public/images/appCenter/Group-113.png 补丁 | 查看 | 原始文档 | blame | 历史
public/images/appCenter/app-banner.png 补丁 | 查看 | 原始文档 | blame | 历史
src/components/subComponents/FileUpload/btn.vue 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/subComponents/FileUpload/file.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/subComponents/FileUpload/index.vue 173 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/subComponents/FileUpload/uploader.vue 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/ai/FileUpload/btn.vue 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/ai/FileUpload/common/file-events.js 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/ai/FileUpload/common/mixins.js 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/ai/FileUpload/common/uploader-simple.js 1612 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/ai/FileUpload/common/utils.js 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/ai/FileUpload/drop.vue 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/ai/FileUpload/file.vue 471 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/ai/FileUpload/files.vue 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/ai/FileUpload/index.vue 302 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/ai/FileUpload/list.vue 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/ai/FileUpload/unsupport.vue 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/ai/FileUpload/uploader.vue 168 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/ai/index/App.vue 856 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/ai/index/api.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/ai/index/detail.vue 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/ai_c/index/App.vue 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/desktop/index/components/Desktop.vue 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/settings/views/generalSettings.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
public/apps.json
@@ -54,7 +54,7 @@
      "type": "2",
      "url": "/view/ai/",
      "title": "应用中心",
      "width": 1243,
      "width": 1024,
      "height": 742,
      "iconBlob": "",
      "icon": "../../images/app-mid/algorithm-store.png",
public/images/appCenter/Group-112.png
public/images/appCenter/Group-113.png
public/images/appCenter/app-banner.png
src/components/subComponents/FileUpload/btn.vue
@@ -34,8 +34,6 @@
 
  mounted() {
    this.$nextTick(() => {
      console.log('btn attrs', this.attrs)
      console.log("btn sourceType", this.sourceType)
      let props = {accept:''};
      if(this.sourceType == 1){
        props.accept = '.mp4';
@@ -63,6 +61,6 @@
  outline: none;
}
.uploader-btn:hover {
  background-color: rgba(0, 0, 0, 0.08);
  /* background-color: rgba(0, 0, 0, 0.08); */
}
</style>
src/components/subComponents/FileUpload/file.vue
@@ -35,7 +35,7 @@
        <div class="uploader-file-status">
          <span v-show="status !== 'uploading'">{{statusText}}</span>
          <span v-show="status === 'uploading'">
            <!-- <span>{{progressStyle.progress}}</span> -->
            <span>{{progressStyle.progress}}</span>
            <em>{{formatedAverageSpeed}}</em>
            <i>&nbsp;&nbsp;{{formatedTimeRemaining}}</i>
src/components/subComponents/FileUpload/index.vue
@@ -9,12 +9,46 @@
      @file-added="onFileAdded"
      @complete="onComplete"
    >
      <el-input :placeholder="uploadPlaceholder" size="small" :readonly="true" v-model="fileName">
      <!-- <uploader-drop v-if="isDrag == true">
        <div class="drag-txt">拖拽文件到这里</div>
        <span class="icon iconfont" @click.stop="showUpload = false"
          >&#xe70b;</span
        >
        <uploader-btn>选择文件</uploader-btn>
      </uploader-drop> -->
      <div class="up-bar" v-if="isDrag == true">
        <div class="name">{{ fileName || uploadPlaceholder }}</div>
        <uploader-btn slot="suffix">
          <el-tooltip :content="tipWords" placement="top" v-if="tip">
            <i class="el-icon-upload2" style="font-size:18px; color:#0088ff"></i>
            <div class="open-file-btn">
              <span class="icon iconfont">&#xe712;</span>
            </div>
          </el-tooltip>
          <i v-else class="el-icon-upload2" style="font-size:18px; color:#0088ff"></i>
        </uploader-btn>
        <!-- <div class="open-file-btn">
          <span class="icon iconfont">&#xe712;</span>
        </div> -->
      </div>
      <el-input
        :placeholder="uploadPlaceholder"
        v-if="isDrag == false"
        size="small"
        :readonly="true"
        v-model="fileName"
      >
        <uploader-btn slot="suffix">
          <el-tooltip :content="tipWords" placement="top" v-if="tip">
            <i
              class="el-icon-upload2"
              style="font-size: 18px; color: #0088ff"
            ></i>
          </el-tooltip>
          <i
            v-else
            class="el-icon-upload2"
            style="font-size: 18px; color: #0088ff"
          ></i>
        </uploader-btn>
      </el-input>
      <uploader-list />
@@ -32,7 +66,7 @@
      @close="closeHandle"
    >
      <uploader-btn ref="button" :sourceType="sourceType">
        <i class="el-icon-upload2" style="font-size:18px; color:#0088ff"></i>
        <i class="el-icon-upload2" style="font-size: 18px; color: #0088ff"></i>
        上传
      </uploader-btn>
      <uploader-list />
@@ -41,16 +75,18 @@
</template>
<script>
import uploader from "./uploader"
import SparkMD5 from 'spark-md5';
import UploaderBtn from "./btn"
import UploaderList from "./list"
import uploader from "./uploader";
import SparkMD5 from "spark-md5";
import UploaderBtn from "./btn";
import UploaderList from "./list";
import UploaderDrop from "./drop";
export default {
  components: {
    uploader,
    UploaderBtn,
    UploaderList
    UploaderList,
    UploaderDrop,
  },
  props: {
    sourceType: {
@@ -58,48 +94,47 @@
    },
    tip: {
      type: Boolean,
      default: false
      default: false,
    },
    tipWords: {
      type: String,
      default: ''
      default: "",
    },
    single: {
      type: Boolean,
      default: false
      default: false,
    },
    uploadPlaceholder: {
      type: String,
      default: ''
      default: "",
    },
    isDrag: {
      type: Boolean,
      default: false,
    },
    url: {
      type: String,
      default: "/data/api-f/file/upload" //"//192.168.20.10:3000/upload"
      default: "/data/api-f/file/upload",
    },
    attrs: {
      type: Object,
      default () {
        return {
        }
      }
    }
      default() {
        return {};
      },
    },
  },
  data() {
    return {
      fileName: "",
      fileMd5: "",
      // attrs: {
      //   accept: 'image/*'
      // },
      statusText: {
        success: '上传成功',
        error: '上传失败',
        uploading: '上传中',
        paused: '暂停中',
        waiting: '等待中'
      }
    }
        success: "上传成功",
        error: "上传失败",
        uploading: "上传中",
        paused: "暂停中",
        waiting: "等待中",
      },
    };
  },
  computed: {
    uploader() {
@@ -110,32 +145,37 @@
        target: this.url,
        testChunks: true,
        headers: {
          Authorization: sessionStorage.getItem('loginedInfo') && JSON.parse(sessionStorage.getItem('loginedInfo')).access_token
        }
      }
    }
          Authorization:
            sessionStorage.getItem("loginedInfo") &&
            JSON.parse(sessionStorage.getItem("loginedInfo")).access_token,
        },
      };
    },
  },
  methods: {
    onFileAdded(file) {
      if (this.single) {
        this.uploader.fileList = this.uploader.fileList.slice([-1]);
        this.$emit("file-added")
        this.$emit("file-added");
      }
      this.computeMD5(file);
    },
    computeMD5(file) {
      let fileReader = new FileReader();
      let time = new Date().getTime();
      let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
      let blobSlice =
        File.prototype.slice ||
        File.prototype.mozSlice ||
        File.prototype.webkitSlice;
      let currentChunk = 0;
      const chunkSize = 10 * 1024 * 1000;
      let chunks = Math.ceil(file.size / chunkSize);
      let spark = new SparkMD5.ArrayBuffer();
      // 文件状态设为"计算MD5"
      this.statusText.paused = "准备上传,正在检查文件"
      this.statusText.paused = "准备上传,正在检查文件";
      file.pause();
      loadNext();
      fileReader.onload = (e => {
      fileReader.onload = (e) => {
        spark.append(e.target.result);
        if (currentChunk < chunks) {
          currentChunk++;
@@ -145,16 +185,16 @@
          this.computeMD5Success(md5, file);
          this.fileName = file.name;
          this.fileMd5 = md5;
          // console.log(`MD5计算完毕:${file.name} \nMD5:${md5} \n分片:${chunks} 大小:${file.size} 用时:${new Date().getTime() - time} ms`);
        }
      });
      };
      fileReader.onerror = function () {
        this.error(`文件${file.name}读取出错,请检查该文件`)
        this.error(`文件${file.name}读取出错,请检查该文件`);
        file.cancel();
      };
      function loadNext() {
        let start = currentChunk * chunkSize;
        let end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize;
        let end =
          start + chunkSize >= file.size ? file.size : start + chunkSize;
        fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end));
      }
    },
@@ -163,32 +203,33 @@
      if (location.href.indexOf("dataStack") >= 0) {
        Object.assign(this.uploader.opts, {
          query: {
            stackId: this.DataStackPool.selectedDir.id
            stackId: this.DataStackPool.selectedDir.id,
            // ...this.params,
          }
        })
          },
        });
      }
      file.uniqueIdentifier = md5;
      file.resume();
      this.statusText.paused = "暂停中";
    },
    onComplete() {
      this.$emit("complete", { filename: this.fileName, identifier: this.fileMd5 });
      this.$emit("complete", {
        filename: this.fileName,
        identifier: this.fileMd5,
      });
    },
    fileComplete() {
      // console.log('file complete', arguments)
    },
    fileComplete() {},
    closeHandle() {
      this.$emit("close")
    }
      this.$emit("close");
    },
  },
  mounted() {
    this.isDrag;
    this.$nextTick(() => {
      console.log(this.sourceType)
      window.uploader = this.$refs.uploader.uploader
    })
  }
}
      window.uploader = this.$refs.uploader.uploader;
    });
  },
};
</script>
<style lang="scss">
@@ -237,6 +278,26 @@
        display: none;
      }
    }
    .up-bar {
      height: 30px;
      margin: 25px;
      background: #f2f2f7;
      border-radius: 2px;
      display: flex;
      align-items: center;
      justify-content: space-between;
      box-sizing: border-box;
      padding: 0 20px;
      .iconfont {
        font-size: 16px;
        color: #333;
      }
      .name {
        color: #bdbdbd;
        font-size: 14px;
      }
    }
  }
}
</style>>
src/components/subComponents/FileUpload/uploader.vue
@@ -3,11 +3,12 @@
    <!-- <div class="close" @click="closeHandle">x</div> -->
    <slot :files="files" :file-list="fileList" :started="started">
      <uploader-unsupport></uploader-unsupport>
      <uploader-drop>
        <!-- <p>拖动文件到该区域上传</p> -->
      <UploaderDrop>
        <p>拖动文件到该区域上传</p>
        <uploader-btn >选择文件</uploader-btn>
        <uploader-btn :directory="true" >选择文件夹</uploader-btn>
      </uploader-drop>
      </UploaderDrop>
      <uploader-list></uploader-list>
    </slot>
  </div>
@@ -134,9 +135,9 @@
    uploader.on('fileRemoved', this.fileRemoved)
    uploader.on('filesSubmitted', this.filesSubmitted)
  },
  mounted() {
  },
  destroyed() {
    //this.unBindUploader();
    const uploader = this.uploader
    uploader.off('catchAll', this.allEvent)
    uploader.off(FILE_ADDED_EVENT, this.fileAdded)
@@ -163,4 +164,5 @@
    cursor: pointer;
  }
}
</style>
src/pages/ai/FileUpload/btn.vue
New file
@@ -0,0 +1,66 @@
<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 {}
      }
    },
    sourceType: {
      type: Number,
    }
  },
  mounted() {
    this.$nextTick(() => {
      let props = {accept:''};
      if(this.sourceType == 1){
        props.accept = '.mp4';
      }else if(this.sourceType == 2){
        props.accept = '.jpg,.jpeg,.png';
      }
      this.uploader.uploader.assignBrowse(this.$refs.btn, this.directory, this.single, props)
    })
  }
}
</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, 0.08); */
}
</style>
src/pages/ai/FileUpload/common/file-events.js
New file
@@ -0,0 +1,3 @@
const events = ['fileProgress', 'fileSuccess', 'fileComplete', 'fileError']
export default events
src/pages/ai/FileUpload/common/mixins.js
New file
@@ -0,0 +1,14 @@
export const uploaderMixin = {
  inject: ['uploader']
}
export const supportMixin = {
  data () {
    return {
      support: true
    }
  },
  mounted () {
    this.support = this.uploader.uploader.support
  }
}
src/pages/ai/FileUpload/common/uploader-simple.js
New file
@@ -0,0 +1,1612 @@
/*!
 * Uploader - Uploader library implements html5 file upload and provides multiple simultaneous, stable, fault tolerant and resumable uploads
 * @version v0.5.4
 * @author dolymood <dolymood@gmail.com>
 * @link https://github.com/simple-uploader/Uploader
 * @license MIT
 */
!function(e){if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.Uploader=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
    var utils = _dereq_('./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
    },{"./utils":5}],2:[function(_dereq_,module,exports){
    var each = _dereq_('./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
    },{"./utils":5}],3:[function(_dereq_,module,exports){
    var utils = _dereq_('./utils')
    var event = _dereq_('./event')
    var File = _dereq_('./file')
    var Chunk = _dereq_('./chunk')
    var version = '0.5.4'
    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
            Array.from(domNode.children).forEach(function(child){
              if(child.type=='file'){
                  console.log(child)
                  domNode.removeChild(child)
              }
            })
            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
    },{"./chunk":1,"./event":2,"./file":4,"./utils":5}],4:[function(_dereq_,module,exports){
    var utils = _dereq_('./utils')
    var Chunk = _dereq_('./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 () {
            var STATUS = Chunk.STATUS
            utils.each(this.chunks, function (chunk) {
              var status = chunk.status()
              if (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
    }
    },{"./chunk":1,"./utils":5}],5:[function(_dereq_,module,exports){
    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
    },{}]},{},[3])
    (3)
    });
src/pages/ai/FileUpload/common/utils.js
New file
@@ -0,0 +1,28 @@
export function secondsToStr(temp) {
  const years = Math.floor(temp / 31536000)
  if (years) {
    return years + ' 年' + numberEnding(years)
  }
  const days = Math.floor((temp %= 31536000) / 86400)
  if (days) {
    return days + ' 天' + numberEnding(days)
  }
  const hours = Math.floor((temp %= 86400) / 3600)
  if (hours) {
    return hours + ' 小时' + numberEnding(hours)
  }
  const minutes = Math.floor((temp %= 3600) / 60)
  if (minutes) {
    return minutes + ' 分' + numberEnding(minutes)
  }
  const seconds = temp % 60
  return seconds + ' 秒' + numberEnding(seconds)
  function numberEnding(number) {
    // return (number > 1) ? 's' : ''
    return ''
  }
}
export function kebabCase(s) {
  return s.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`)
}
src/pages/ai/FileUpload/drop.vue
New file
@@ -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>
src/pages/ai/FileUpload/file.vue
New file
@@ -0,0 +1,471 @@
<template>
  <div class="uploader-file" :status="status">
    <slot
      :file="file"
      :list="list"
      :status="status"
      :paused="paused"
      :error="error"
      :response="response"
      :average-speed="averageSpeed"
      :formated-average-speed="formatedAverageSpeed"
      :current-speed="currentSpeed"
      :is-complete="isComplete"
      :is-uploading="isUploading"
      :size="size"
      :formated-size="formatedSize"
      :uploaded-size="uploadedSize"
      :progress="progress"
      :progress-style="progressStyle"
      :progressing-class="progressingClass"
      :time-remaining="timeRemaining"
      :formated-time-remaining="formatedTimeRemaining"
      :type="type"
      :extension="extension"
      :file-category="fileCategory"
    >
      <div
        class="uploader-file-progress"
        :class="progressingClass"
        :style="progressStyle"
      ></div>
      <div class="uploader-file-info">
        <div class="uploader-file-name">
          <i class="uploader-file-icon" :icon="fileCategory"></i>
          {{ file.name }}
        </div>
        <div class="uploader-file-size">{{ formatedSize }}</div>
        <div class="uploader-file-meta"></div>
        <div class="uploader-file-status">
          <span v-show="status !== 'uploading'">{{ statusText }}</span>
          <span v-show="status === 'uploading'">
            <span>{{ progressStyle.progress }}</span>
            <em>{{ formatedAverageSpeed }}</em>
            <i>&nbsp;&nbsp;{{ formatedTimeRemaining }}</i>
          </span>
        </div>
        <div class="uploader-file-actions">
          <span class="uploader-file-pause" @click="pause"></span>
          <span class="uploader-file-resume" @click="resume">️</span>
          <span class="uploader-file-retry" @click="retry"></span>
          <span class="uploader-file-remove" @click="remove"></span>
        </div>
      </div>
    </slot>
  </div>
</template>
<script>
import Uploader from "simple-uploader.js";
import events from "./common/file-events";
import { secondsToStr } from "./common/utils";
const COMPONENT_NAME = "uploader-file";
export default {
  name: COMPONENT_NAME,
  props: {
    file: {
      type: Object,
      default() {
        return {};
      },
    },
    list: {
      type: Boolean,
      default: false,
    },
  },
  data() {
    return {
      response: null,
      paused: false,
      error: false,
      averageSpeed: 0,
      currentSpeed: 0,
      isComplete: false,
      isUploading: false,
      size: 0,
      formatedSize: "",
      uploadedSize: 0,
      progress: 0,
      timeRemaining: 0,
      type: "",
      extension: "",
      progressingClass: "",
    };
  },
  computed: {
    fileCategory() {
      const extension = this.extension;
      const isFolder = this.file.isFolder;
      let type = isFolder ? "folder" : "unknown";
      const categoryMap = this.file.uploader.opts.categoryMap;
      const typeMap = categoryMap || {
        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",
        ],
      };
      Object.keys(typeMap).forEach((_type) => {
        const extensions = typeMap[_type];
        if (extensions.indexOf(extension) > -1) {
          type = _type;
        }
      });
      return type;
    },
    progressStyle() {
      const progress = Math.floor(this.progress * 100);
      const style = `translateX(${Math.floor(progress - 100)}%)`;
      return {
        progress: `${progress}%`,
        webkitTransform: style,
        mozTransform: style,
        msTransform: style,
        transform: style,
      };
    },
    formatedAverageSpeed() {
      return `${Uploader.utils.formatSize(this.averageSpeed)} / s`;
    },
    status() {
      const isUploading = this.isUploading;
      const isComplete = this.isComplete;
      const isError = this.error;
      const paused = this.paused;
      if (isComplete) {
        return "success";
      } else if (isError) {
        return "error";
      } else if (isUploading) {
        return "uploading";
      } else if (paused) {
        return "paused";
      } else {
        return "waiting";
      }
    },
    statusText() {
      const status = this.status;
      const fileStatusText = this.file.uploader.fileStatusText;
      let txt = status;
      if (typeof fileStatusText === "function") {
        txt = fileStatusText(status, this.response);
      } else {
        txt = fileStatusText[status];
      }
      return txt || status;
    },
    formatedTimeRemaining() {
      const timeRemaining = this.timeRemaining;
      const file = this.file;
      if (timeRemaining === Number.POSITIVE_INFINITY || timeRemaining === 0) {
        return "";
      }
      let parsedTimeRemaining = secondsToStr(timeRemaining);
      const parseTimeRemaining = file.uploader.opts.parseTimeRemaining;
      if (parseTimeRemaining) {
        parsedTimeRemaining = parseTimeRemaining(
          timeRemaining,
          parsedTimeRemaining
        );
      }
      return parsedTimeRemaining;
    },
  },
  watch: {
    status(newStatus, oldStatus) {
      if (oldStatus && newStatus === "uploading" && oldStatus !== "uploading") {
        this.tid = setTimeout(() => {
          this.progressingClass = "uploader-file-progressing";
        }, 200);
      } else {
        clearTimeout(this.tid);
        this.progressingClass = "";
      }
    },
  },
  methods: {
    _actionCheck() {
      this.paused = this.file.paused;
      this.error = this.file.error;
      this.isUploading = this.file.isUploading();
    },
    pause() {
      this.file.pause();
      this._actionCheck();
      this._fileProgress();
    },
    resume() {
      this.file.resume();
      this._actionCheck();
    },
    remove() {
      this.file.cancel();
    },
    retry() {
      this.file.retry();
      this._actionCheck();
    },
    processResponse(message) {
      let res = message;
      try {
        res = JSON.parse(message);
      } catch (e) {}
      this.response = res;
    },
    fileEventsHandler(event, args) {
      const rootFile = args[0];
      const file = args[1];
      const target = this.list ? rootFile : file;
      if (this.file === target) {
        if (this.list && event === "fileSuccess") {
          this.processResponse(args[2]);
          return;
        }
        this[`_${event}`].apply(this, args);
      }
    },
    _fileProgress() {
      this.progress = this.file.progress();
      this.averageSpeed = this.file.averageSpeed;
      this.currentSpeed = this.file.currentSpeed;
      this.timeRemaining = this.file.timeRemaining();
      this.uploadedSize = this.file.sizeUploaded();
      this._actionCheck();
    },
    _fileSuccess(rootFile, file, message) {
      if (rootFile) {
        this.processResponse(message);
      }
      this._fileProgress();
      this.error = false;
      this.isComplete = true;
      this.isUploading = false;
      console.log("rootFile, file, message", rootFile, file, message);
    },
    _fileComplete() {
      this._fileSuccess();
    },
    _fileError(rootFile, file, message) {
      this._fileProgress();
      console.log("rootFile, file, message", rootFile, file, message);
      this.processResponse(message);
      this.error = true;
      this.isComplete = false;
      this.isUploading = false;
    },
  },
  mounted() {
    const staticProps = ["paused", "error", "averageSpeed", "currentSpeed"];
    const fnProps = [
      "isComplete",
      "isUploading",
      {
        key: "size",
        fn: "getSize",
      },
      {
        key: "formatedSize",
        fn: "getFormatSize",
      },
      {
        key: "uploadedSize",
        fn: "sizeUploaded",
      },
      "progress",
      "timeRemaining",
      {
        key: "type",
        fn: "getType",
      },
      {
        key: "extension",
        fn: "getExtension",
      },
    ];
    staticProps.forEach((prop) => {
      this[prop] = this.file[prop];
    });
    fnProps.forEach((fnProp) => {
      if (typeof fnProp === "string") {
        this[fnProp] = this.file[fnProp]();
      } else {
        this[fnProp.key] = this.file[fnProp.fn]();
      }
    });
    const handlers = (this._handlers = {});
    const eventHandler = (event) => {
      handlers[event] = (...args) => {
        this.fileEventsHandler(event, args);
      };
      return handlers[event];
    };
    events.forEach((event) => {
      this.file.uploader.on(event, eventHandler(event));
    });
  },
  destroyed() {
    events.forEach((event) => {
      this.file.uploader.off(event, this._handlers[event]);
    });
    this._handlers = null;
  },
};
</script>
<style>
.uploader-file {
  position: relative;
  height: 49px;
  line-height: 49px;
  overflow: hidden;
}
.uploader-file[status="waiting"] .uploader-file-pause,
.uploader-file[status="uploading"] .uploader-file-pause {
  display: block;
}
.uploader-file[status="paused"] .uploader-file-resume {
  display: block;
}
.uploader-file[status="error"] .uploader-file-retry {
  display: block;
}
.uploader-file[status="success"] .uploader-file-remove {
  display: none;
}
.uploader-file[status="error"] .uploader-file-progress {
  background: #ffe0e0;
}
.uploader-file-progress {
  position: absolute;
  width: 100%;
  height: 4px;
  border-radius: 4px;
  background: #1dd4ec;
  transform: translateX(-100%);
}
.uploader-file-progressing {
  transition: all 0.4s linear;
}
.uploader-file-info {
  position: relative;
  z-index: 1;
  height: 100%;
  overflow: hidden;
  font-size: 13px;
}
.uploader-file-info:hover {
  background-color: rgba(240, 240, 240, 0.2);
}
.uploader-file-info i,
.uploader-file-info em {
  font-style: normal;
}
.uploader-file-name,
.uploader-file-size,
.uploader-file-meta,
.uploader-file-status,
.uploader-file-actions {
  float: left;
  position: relative;
  height: 100%;
}
.uploader-file-name {
  width: 50%;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  text-indent: 14px;
  text-align: left;
}
.uploader-file-icon {
  width: 24px;
  height: 24px;
  display: inline-block;
  vertical-align: top;
  margin-top: 13px;
  margin-right: 8px;
}
.uploader-file-icon::before {
  content: "📋";
  display: block;
  /* height: 49px; */
  font-size: 18px;
  line-height: 26px;
  text-indent: 0;
}
.uploader-file-icon[icon="folder"]::before {
  content: "📂";
}
.uploader-file-icon[icon="image"]::before {
  content: "🌆";
}
.uploader-file-icon[icon="video"]::before {
  content: "🎬";
}
.uploader-file-icon[icon="audio"]::before {
  content: "🎵";
}
.uploader-file-icon[icon="document"]::before {
  content: "📋";
}
.uploader-file-size {
  width: 11%;
  text-indent: 10px;
}
.uploader-file-meta {
  width: 8%;
}
.uploader-file-status {
  width: 24%;
  text-indent: 20px;
}
.uploader-file-actions {
  width: 6%;
}
.uploader-file-actions > span {
  display: none;
  float: left;
  width: 16px;
  height: 16px;
  margin-top: 16px;
  margin-right: 10px;
  cursor: pointer;
  background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAABkCAYAAAD0ZHJ6AAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAAJcEhZcwAACxMAAAsTAQCanBgAAARkSURBVGje7ZnfS1NRHMAH4ptPkvQSuAdBkCxD8FUQJMEULUgzy1KyyPVQ4JMiiP4Bvg6EwUQQfMmwhwRDshwaKUjDVCgoSdDNHkzTJZ6+Z37Purve8+PeTb2TM/ggu+ew89l33x8H9BBCPG7GowXTJej3+wnDvEm0JuLC04+EYWftVAUv+fiCvDUdQR1BHUEdQR3BTIygvixoQS14XgTtthLVdpNWwXRLqvQ724LplFRtyrYF0yVpFLQrKRVMh6RZ0I6kkmCqklaCqpKZH0FX56Crq9jVfdDVk0RfFrSgFsxkQVmLcdKCVrKySCrryhPEyYShhzOcrFtG0EoilfHHk1CRU5rF6ZjNZhlVOW6RnMSVyyilKies4pO41diVy8wIujoHXV3FGdMHXTtJKLFYTLhZtq4vC1rwXApCZTIqgR6g1PBMCO9DL3bMMSqBHqDU8EyISDAHiGKvWwcCQG2KgjlAFCDAOhAAap0K5gKLphk8mqJgLrCIgoxRJ4J5wKpJ7gAoMkn5EBXBPGDVJHcAFJmkfIhQcAql1oBpTvTol9gG9pm4RHAKpdaAaU706JfYBvaZuJVgPQrt4sFlnOh5MC/p3lmJYD0K7eLBZZzoeTAv6d5ZnuAYHjpgEOnk5F0ufhG6v1ggOIaHDhhEOjl5l4tfhO4vthLcwAMrFNvLJO5vEwhu4IEViu1lEve3WQmyoihQFBzG/V0CQVYUBYqCw7i/SxTBcpsRbFeIYLnNCLZbCY5b5KAnxRwct8hBj9McZFVMW0ihRNBuFdMWUigRlFaxuQ9WWYjRMTiIe5z0wSoLMToGB3GPsA9aTZIJoB+nRgBnM1tzOkkmgH6cGgGczWzNpzqLx3n/aULJJgezeNw07oxQySbVywKjBOgFRnDs+VEsx8FlgVEC9AIjOPb8KJYjvSzoG7UW1IJaUAtqQS14toLNM5fN5APdwBJA8G83Pk/aK/rgzVvXzeQD3cASQPBvNz5P2ssTzAaGUIrHEO6zI5gNDKEUjyHcxxWkh4Ylcowwk1QQpIeGJXKMMJO0EgwqyjGCioJBJvDrxRMSuVOTJEXfbz1/bHwWtBL0yoQehK6RucgE+bGzanzulQh6E3IgQV+xpc8kcrfuSO7eTfJ3ZYmQw0Oy9azVKOk1C/bJ5D5F38YPeLfx0rjWJxHsS0SqsSYuxySjj5qO5Oj7xQWy2VBtFOwzCy6ryH3YfE3uh64Y1xckgstJPydEjkkeHv07Iy4Xaao15+KCWTBx6M/db+T9xivSErqaJDdzXI6yLRE8Vgg0coex/SPJvT0SbWu0KpZtbgSpCH3NRt7I5OxHkObc6heU+/M/J5vrpBFM5GBLqCQux14COXs5CNXK5OjPGm1tSMrJSOMNYQ4mVTGV/L6zTL7+DovkbFUxbSW0Wo05l8hJWsU+cRWfSh+Mt5Lb1ck/J1TvVsdDaR/MiEni+llsdZuZp62EViu+96bpNjNPWwmtVnzvFd5m9IVVC54x/wA7gNvqFG9vXQAAAABJRU5ErkJggg==")
    no-repeat 0 0;
}
.uploader-file-actions > span:hover {
  background-position-x: -21px;
}
.uploader-file-actions .uploader-file-pause {
  background-position-y: 0;
}
.uploader-file-actions .uploader-file-resume {
  background-position-y: -17px;
}
.uploader-file-actions .uploader-file-retry {
  background-position-y: -53px;
}
.uploader-file-actions .uploader-file-remove {
  display: block;
  background-position-y: -34px;
}
</style>
src/pages/ai/FileUpload/files.vue
New file
@@ -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>
src/pages/ai/FileUpload/index.vue
New file
@@ -0,0 +1,302 @@
<template>
  <div class="file-uploader">
    <uploader
      v-if="single"
      ref="uploader"
      :options="options"
      :file-status-text="statusText"
      class="uploader-single"
      @file-added="onFileAdded"
      @complete="onComplete"
    >
      <!-- <uploader-drop v-if="isDrag == true">
        <div class="drag-txt">拖拽文件到这里</div>
        <span class="icon iconfont" @click.stop="showUpload = false"
          >&#xe70b;</span
        >
        <uploader-btn>选择文件</uploader-btn>
      </uploader-drop> -->
      <div class="up-bar" v-if="isDrag == true">
        <div class="name">{{ fileName || uploadPlaceholder }}</div>
        <uploader-btn slot="suffix">
          <el-tooltip :content="tipWords" placement="top" v-if="tip">
            <div class="open-file-btn">
              <span class="icon iconfont">&#xe712;</span>
            </div>
          </el-tooltip>
        </uploader-btn>
        <!-- <div class="open-file-btn">
          <span class="icon iconfont">&#xe712;</span>
        </div> -->
      </div>
      <el-input
        :placeholder="uploadPlaceholder"
        v-if="isDrag == false"
        size="small"
        :readonly="true"
        v-model="fileName"
      >
        <uploader-btn slot="suffix">
          <el-tooltip :content="tipWords" placement="top" v-if="tip">
            <i
              class="el-icon-upload2"
              style="font-size: 18px; color: #0088ff"
            ></i>
          </el-tooltip>
          <i
            v-else
            class="el-icon-upload2"
            style="font-size: 18px; color: #0088ff"
          ></i>
        </uploader-btn>
      </el-input>
      <uploader-list />
    </uploader>
    <uploader
      v-else
      ref="uploader"
      :options="options"
      :file-status-text="statusText"
      class="uploader-example"
      @file-added="onFileAdded"
      @file-complete="fileComplete"
      @complete="onComplete"
      @close="closeHandle"
    >
      <uploader-btn ref="button" :sourceType="sourceType">
        <i class="el-icon-upload2" style="font-size: 18px; color: #0088ff"></i>
        上传
      </uploader-btn>
      <uploader-list />
    </uploader>
  </div>
</template>
<script>
import uploader from "./uploader";
import SparkMD5 from "spark-md5";
import UploaderBtn from "./btn";
import UploaderList from "./list";
import UploaderDrop from "./drop";
export default {
  components: {
    uploader,
    UploaderBtn,
    UploaderList,
    UploaderDrop,
  },
  props: {
    sourceType: {
      type: Number,
    },
    tip: {
      type: Boolean,
      default: false,
    },
    tipWords: {
      type: String,
      default: "",
    },
    single: {
      type: Boolean,
      default: false,
    },
    uploadPlaceholder: {
      type: String,
      default: "",
    },
    isDrag: {
      type: Boolean,
      default: false,
    },
    url: {
      type: String,
      default: "/data/api-f/file/upload",
    },
    attrs: {
      type: Object,
      default() {
        return {};
      },
    },
  },
  data() {
    return {
      fileName: "",
      fileMd5: "",
      statusText: {
        success: "上传成功",
        error: "上传失败",
        uploading: "上传中",
        paused: "暂停中",
        waiting: "等待中",
      },
    };
  },
  computed: {
    uploader() {
      return this.$refs.uploader.uploader;
    },
    options() {
      return {
        target: this.url,
        testChunks: true,
        headers: {
          Authorization:
            sessionStorage.getItem("loginedInfo") &&
            JSON.parse(sessionStorage.getItem("loginedInfo")).access_token,
        },
      };
    },
  },
  methods: {
    onFileAdded(file) {
      if (this.single) {
        this.uploader.fileList = this.uploader.fileList.slice([-1]);
        this.$emit("file-added");
      }
      this.computeMD5(file);
    },
    computeMD5(file) {
      let fileReader = new FileReader();
      let time = new Date().getTime();
      let blobSlice =
        File.prototype.slice ||
        File.prototype.mozSlice ||
        File.prototype.webkitSlice;
      let currentChunk = 0;
      const chunkSize = 10 * 1024 * 1000;
      let chunks = Math.ceil(file.size / chunkSize);
      let spark = new SparkMD5.ArrayBuffer();
      // 文件状态设为"计算MD5"
      this.statusText.paused = "准备上传,正在检查文件";
      file.pause();
      loadNext();
      fileReader.onload = (e) => {
        spark.append(e.target.result);
        if (currentChunk < chunks) {
          currentChunk++;
          loadNext();
        } else {
          let md5 = spark.end();
          this.computeMD5Success(md5, file);
          this.fileName = file.name;
          this.fileMd5 = md5;
        }
      };
      fileReader.onerror = function () {
        this.error(`文件${file.name}读取出错,请检查该文件`);
        file.cancel();
      };
      function loadNext() {
        let start = currentChunk * chunkSize;
        let end =
          start + chunkSize >= file.size ? file.size : start + chunkSize;
        fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end));
      }
    },
    computeMD5Success(md5, file) {
      //将自定义参数直接加载uploader实例的opts上
      if (location.href.indexOf("dataStack") >= 0) {
        Object.assign(this.uploader.opts, {
          query: {
            stackId: this.DataStackPool.selectedDir.id,
            // ...this.params,
          },
        });
      }
      file.uniqueIdentifier = md5;
      file.resume();
      this.statusText.paused = "暂停中";
    },
    onComplete() {
      this.$emit("complete", {
        filename: this.fileName,
        identifier: this.fileMd5,
      });
    },
    fileComplete() {},
    closeHandle() {
      this.$emit("close");
    },
  },
  mounted() {
    this.isDrag;
    this.$nextTick(() => {
      window.uploader = this.$refs.uploader.uploader;
    });
  },
};
</script>
<style lang="scss">
.file-uploader {
  .el-input__suffix,
  .el-input__suffix-inner {
    outline: none !important;
  }
  .el-tooltip.focusing {
    outline: none;
  }
  .uploader-example {
    width: 99%;
    // padding: 15px;
    // margin: 40px auto 0;
    font-size: 12px;
    // box-shadow: 0 0 10px rgba(0, 0, 0, 0.4);
    background-color: #fff;
  }
  .uploader-example .uploader-btn {
    position: relative;
    display: none;
    // float: right;
    // top: -45px;
  }
  .uploader-example .uploader-list {
    max-height: 440px;
    overflow: auto;
    overflow-x: hidden;
    overflow-y: auto;
  }
  .uploader-single {
    position: unset;
    .close {
      display: none;
    }
    .uploader-btn {
      border: 0px;
    }
    .uploader-file {
      // height: 2px;
      .uploader-file-progress {
        // background: #3d68e1;
      }
      .uploader-file-info {
        // display: none;
      }
    }
    .up-bar {
      height: 30px;
      background: #f2f2f7;
      border-radius: 2px;
      display: flex;
      align-items: center;
      justify-content: space-between;
      box-sizing: border-box;
      padding: 0 20px;
      .iconfont {
        font-size: 16px;
        color: #333;
      }
      .name {
        color: #bdbdbd;
        font-size: 14px;
      }
    }
  }
}
</style>>
src/pages/ai/FileUpload/list.vue
New file
@@ -0,0 +1,43 @@
<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;    margin-top: 12px;
}
.uploader-list > ul {
  list-style: none;
  margin: 0;
  padding: 0;
}
</style>
src/pages/ai/FileUpload/unsupport.vue
New file
@@ -0,0 +1,30 @@
<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>
src/pages/ai/FileUpload/uploader.vue
New file
@@ -0,0 +1,168 @@
<template>
  <div class="uploader">
    <!-- <div class="close" @click="closeHandle">x</div> -->
    <slot :files="files" :file-list="fileList" :started="started">
      <uploader-unsupport></uploader-unsupport>
      <UploaderDrop>
        <p>拖动文件到该区域上传</p>
        <uploader-btn >选择文件</uploader-btn>
        <uploader-btn :directory="true" >选择文件夹</uploader-btn>
      </UploaderDrop>
      <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'
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: {
    attrs: {
      type: Object,
      default() {
        return {}
      }
    },
    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) {
      console.log(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)
    },
    closeHandle() {
      this.$emit("close")
    }
  },
  created() {
    // console.log('uploader attrs',this.attrs);
    // this.bindUploader();
    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)
  },
  mounted() {
  },
  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
  }
}
</script>
<style lang="scss">
.uploader {
  .close {
    position: absolute;
    right: 5px;
    top: 3px;
    cursor: pointer;
  }
}
</style>
src/pages/ai/index/App.vue
@@ -19,15 +19,11 @@
                  @click="pickMenu(name)"
                >
                  {{ name }}
                  <el-badge
                    v-if="index == 3"
                    class="update-badge"
                    :value="updateNum"
                    :hidden="updateNum == 0"
                  ></el-badge>
                  <sup v-if="index == 3 && updateNum != 0">{{ updateNum }}</sup>
                </div>
              </div>
              <div class="nav-box-search">
                <span class="icon iconfont all-scene">&#xe703;</span>
                <el-input
                  placeholder="搜索"
                  v-model="input3"
@@ -37,53 +33,71 @@
                  <el-select
                    v-model="select"
                    slot="append"
                    placeholder="所有场景"
                    placeholder="选择场景"
                    :popper-append-to-body="false"
                  >
                    <el-option label="铁路场景" value="1"></el-option>
                    <el-option label="安全场景" value="2"></el-option>
                    <el-option label="通用场景" value="3"></el-option>
                    <el-option label="校园园区" value="4"></el-option>
                    <el-option label="铁路场景" value="1">
                      <span class="icon iconfont">&#xe713;</span>
                      <span>铁路场景</span>
                    </el-option>
                    <el-option label="安全场景" value="2">
                      <span class="icon iconfont">&#xe702;</span>
                      <span>安全场景</span>
                    </el-option>
                    <el-option label="通用场景" value="3">
                      <span class="icon iconfont">&#xe70d;</span>
                      <span>通用场景</span>
                    </el-option>
                    <el-option label="校园园区" value="4">
                      <span class="icon iconfont">&#xe70e;</span>
                      <span>校园园区</span>
                    </el-option>
                  </el-select>
                </el-input>
              </div>
              <div class="bg-img-wrap">
                <img src="/images/appCenter/app-banner.jpg" alt="" />
                <img src="/images/appCenter/Group-112.png" alt="" />
              </div>
            </div>
            <div class="quick-path" v-if="showQuickPath">
              <div
                class="quick-item"
                v-for="(item, index) in recomandUpdateList"
                :key="index"
                @click="checkDetail(item, 'active')"
              >
                <div class="icon-img">
                  <img
                    v-if="item.iconBlob"
                    :src="
                      item.iconBlob.indexOf(',') > 0
                        ? item.iconBlob
                        : `data:image/png;base64,${item.iconBlob}`
                    "
                    alt
                  />
                  <img v-else :src="item.icon" alt />
                </div>
                <div class="desc">
                  <div class="desc-1">{{ item.sdk_name || item.name }}</div>
                  <div class="desc-2">版本 {{ item.version }}</div>
                </div>
                <div class="right-icon">
                  <span class="icon iconfont">&#xe6b0;</span>
              <div class="left-items">
                <div
                  class="quick-item"
                  v-for="(item, index) in recomandUpdateList"
                  :key="index"
                  @click="checkDetail(item, 'inactive')"
                >
                  <div class="icon-img">
                    <span class="icon iconfont" v-if="item.isUpgrade"
                      >&#xe719;</span
                    >
                    <img
                      v-if="item.iconBlob"
                      :src="
                        item.iconBlob.indexOf(',') > 0
                          ? item.iconBlob
                          : `data:image/png;base64,${item.iconBlob}`
                      "
                      alt
                    />
                    <img v-else :src="item.icon" alt />
                  </div>
                  <div class="desc">
                    <div class="desc-1">{{ item.sdk_name || item.name }}</div>
                    <div class="desc-2">版本 {{ item.version }}</div>
                  </div>
                  <div class="right-icon">
                    <span class="icon iconfont">&#xe71a;</span>
                  </div>
                </div>
              </div>
              <div class="down-all-btn">
              <!-- <div class="down-all-btn">
                <el-button plain size="small" @click="batchUpdate('both')" round
                  >全部更新</el-button
                >
              </div>
              </div> -->
            </div>
            <div class="main-content">
@@ -119,7 +133,10 @@
                  >
                </div>
              </div>
              <div class="front-page-items" v-if="activeName != '离线升级/安装'">
              <div
                class="front-page-items"
                v-if="activeName != '离线升级/安装'"
              >
                <div
                  class="front-page-item"
                  v-for="(item, index) in tempList"
@@ -127,6 +144,9 @@
                  @click="checkDetail(item)"
                >
                  <div class="icon-img">
                    <span class="icon iconfont" v-if="item.isUpgrade"
                      >&#xe719;</span
                    >
                    <img
                      v-if="item.iconBlob"
                      :src="
@@ -146,6 +166,7 @@
                    <el-button
                      size="small"
                      type="primary"
                      class="other-btn"
                      round
                      @click="checkDetail(item)"
                      v-if="activeName == '应用中心'"
@@ -154,19 +175,23 @@
                    <el-button
                      size="small"
                      type="primary"
                      class="check-btn"
                      round
                      v-if="
                        activeName == '已激活' &&
                        (activeTab == 'sdk' ||
                          (activeTab == 'app' && !item.isDefault))
                        ((activeTab == 'sdk' && !item.isUpgrade) ||
                          (activeTab == 'app' &&
                            !item.isUpgrade &&
                            !item.isDefault))
                      "
                      @click.stop="unLoadSdkOrApp(item)"
                      >卸载</el-button
                      >查看</el-button
                    >
                    <!-- @click.stop="unLoadSdkOrApp(item)" -->
                    <el-button
                      size="small"
                      type="primary"
                      class="check-btn"
                      round
                      v-if="
                        activeName == '已激活' &&
@@ -179,6 +204,7 @@
                    <el-button
                      size="small"
                      type="primary"
                      class="other-btn"
                      round
                      v-if="
                        activeName == '已激活' &&
@@ -188,6 +214,58 @@
                      "
                      >升级</el-button
                    >
                    <el-button
                      size="small"
                      type="primary"
                      class="update-btn"
                      round
                      @click.stop="donwloadSDK(item)"
                      v-if="
                        (activeName == '更新' || activeName == '已激活') &&
                        activeTab == 'sdk' &&
                        item.isUpgrade &&
                        !item.upgradeLoading
                      "
                      >更新</el-button
                    >
                    <span
                      class="icon iconfont rocket-icon"
                      v-if="
                        activeName == '更新' &&
                        item.isUpgrade &&
                        item.upgradeLoading &&
                        rocketIf
                      "
                      >&#xe70c;</span
                    >
                    <span
                      class="icon iconfont rocket-icon"
                      v-if="
                        activeName == '更新' &&
                        item.isUpgrade &&
                        item.upgradeLoading &&
                        !rocketIf
                      "
                      >&#xe717;</span
                    >
                    <el-button
                      size="small"
                      type="primary"
                      class="update-btn"
                      round
                      @click.stop="downloadApp(item, 'upgrade')"
                      v-if="
                        (activeName == '更新' || activeName == '已激活') &&
                        activeTab == 'app' &&
                        item.isUpgrade &&
                        !item.upgradeLoading
                      "
                      >更新</el-button
                    >
                    <div class="status">
                      {{ item.progressMsg }}
                    </div>
                  </div>
                </div>
@@ -195,6 +273,7 @@
                  class="front-page-item item-dimmed"
                  v-for="(item, index) in tempDarkList"
                  :key="index"
                  @click="checkDetail(item, 'activeNotInstall')"
                >
                  <div class="icon-img">
                    <img
@@ -216,31 +295,55 @@
                    <el-button
                      size="small"
                      type="primary"
                      class="other-btn"
                      round
                      @click="donwload(item)"
                      v-if="activeTab == 'sdk' && !item.upgradeLoading"
                      @click.stop="donwloadSDK(item)"
                      >安装</el-button
                    >
                    <el-button
                      size="small"
                      type="primary"
                      class="other-btn"
                      round
                      v-if="activeTab == 'app' && !item.upgradeLoading"
                      @click.stop="downloadApp(item)"
                      >安装</el-button
                    >
                    <div class="spin-icon">
                      <span
                        class="icon iconfont anz-font"
                        v-if="item.upgradeLoading"
                        >&#xe70a;</span
                      >
                    </div>
                    <div class="status">
                      {{ item.progressMsg }}
                    </div>
                  </div>
                </div>
              </div>
              <div class="upload-pkg">
              <div class="upload-pkg" v-if="activeName == '离线升级/安装'">
                <div class="upload-head">
                  <div class="left">
                    <span class="icon iconfont">&#xe754;</span>
                    <span class="txt">上传安装软件</span>
                  </div>
                  <div class="right">
                    <span class="icon iconfont">&#xe711;</span>
                    <span class="icon iconfont">&#xe712;</span>
                  </div>
                </div>
                <FileUploader
                  class="upload-demo"
                  single
                  tip
                  :isDrag="true"
                  tipWords="点击上传"
                  uploadPlaceholder="算法软件"
                  url="/data/api-v/sdk/upload"
                  @complete="onFileUpload"
                  @file-added="onFileAdded"
                />
              </div>
            </div>
          </div>
@@ -253,6 +356,53 @@
        </div>
      </div>
    </div>
    <el-dialog
      title="安装包信息"
      :visible.sync="installDialogVisible"
      width="40%"
      :close-on-click-modal="false"
    >
      <div class="installInfo">
        <template v-if="installAppPackage != null">
          <div>
            <div>
              <span>安装包名称:</span>
              <span>{{ installAppPackage.productName }}</span>
            </div>
            <div>
              <span>安装版本:</span>
              <span>{{ installAppPackage.version }}</span>
            </div>
            <div>
              <span>更新内容:</span>
              <span>{{ installAppPackage.installContent }}</span>
            </div>
          </div>
        </template>
        <template v-if="installSdkPackage != null">
          <div>
            <div>
              <span>安装包名称:</span>
              <span>{{ installSdkPackage.productName }}</span>
            </div>
            <div>
              <span>安装版本:</span>
              <span>{{ installSdkPackage.version }}</span>
            </div>
            <div>
              <span>更新内容:</span>
              <span>{{ installSdkPackage.installContent }}</span>
            </div>
          </div>
        </template>
        <p>确定安装?</p>
      </div>
      <div slot="footer" class="dialog-footer">
        <el-button @click="installDialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="offlineInstall">安 装</el-button>
      </div>
    </el-dialog>
  </div>
</template>
<script>
@@ -262,6 +412,7 @@
  installSdk,
  getInstallInfo,
  removeSdk,
  uploadSDK,
} from "./api";
import {
  getApps,
@@ -274,7 +425,7 @@
  actApp,
} from "@/api/app";
import { getUrlKey } from "@/api/utils";
import FileUploader from "@/components/subComponents/FileUpload/index";
import FileUploader from "../FileUpload/index";
import detailPage from "./detail";
export default {
  name: "algorithmManage",
@@ -296,22 +447,17 @@
      }
      return false;
    },
    recomandUpdateList() {
      if (this.activeTab == "sdk") {
        return this.hasNewVersionSdk.slice(0, 3);
      } else {
        return this.hasNewVersionApp.slice(0, 3);
      }
    },
  },
  data() {
    return {
      installedList: [],
      recomandUpdateList: [],
      hasNewVersionSdk: [],
      activeTab: "sdk",
      hasNewVersionApp: [],
      tempDarkList: [],
      notInstalledList: [],
      showUpload: false,
      detailType: "",
      detailProductID: "",
      buttonAuthority: sessionStorage.getItem("buttonAuthoritys") || [],
@@ -359,6 +505,7 @@
      backStack: [],
      toUpdateArr1: [],
      forwardStack: [],
      rocketIf: false,
      backDisable: true,
      forwardDisable: true,
      showInputCode: false,
@@ -407,7 +554,7 @@
    },
    batchUpdateSDK() {
      this.hasNewVersionSdk.forEach((sdk) => {
        this.donwload(sdk, 0);
        this.donwloadSDK(sdk);
      });
    },
    batchUpdateApp() {
@@ -458,6 +605,7 @@
          this.activeTab == "sdk"
            ? this.hasNewVersionSdk
            : this.hasNewVersionApp;
        this.tempDarkList = [];
      }
    },
@@ -474,12 +622,6 @@
      } else {
        this.detailType = this.activeName == "应用中心" ? "inactive" : "active";
      }
    },
    resetStack() {
      this.forwardStack = [];
      this.backStack = [];
      this.backDisable = true;
      this.forwardDisable = true;
    },
    checkInWindow(item) {
      this.backStack.push([this.productDetail, this.otherProducts]);
@@ -553,66 +695,44 @@
        .then((res) => {
          if (res.success) {
            this.isInstall = false;
            this.$message({
              type: "success",
              message: '安装成功,将跳转至"已激活"中查看',
            });
            this.$message.success('安装成功,将跳转至"已激活"中查看');
            setTimeout(() => {
              this.getAllSdk();
              window.parent.postMessage({ msg: "AppUpdate" }, "*");
              this.activeName = "已激活";
            }, 3000);
          }
        })
        .catch((e) => {
          this.isInstall = false;
          this.$message({
            type: "error",
            message: e.data,
          });
          this.$message.error(e.data);
        });
    },
    downloadApp(app, action) {
      if (action == "upgrade") {
        app.upgradeLoading = true;
      } else {
        app.installLoading = true;
      }
      let _this = this;
      let timer = null;
      app.upgradeLoading = true;
      timer = setInterval(() => {
        this.rocketIf = !this.rocketIf;
      }, 350);
      let _this = this;
      installApp({ path: app.id })
        .then((res) => {
          if (res && res.success) {
            _this.$notify({
              title: "成功",
              message: "安装应用成功",
              type: "success",
            });
            _this.$notify.success("安装应用成功");
            clearInterval(timer);
            app.upgradeLoading = false;
            setTimeout(() => {
              if (action == "upgrade") {
                app.upgradeLoading = false;
              } else {
                app.installLoading = false;
              }
              window.parent.postMessage({ msg: "AppUpdate" }, "*");
            }, 3000);
          } else {
          }
        })
        .catch((e) => {
          _this.$notify({
            title: "安装失败",
            message: e.data,
            type: "warning",
          });
          if (action == "upgrade") {
            app.upgradeLoading = false;
          } else {
            app.installLoading = false;
          }
          _this.$notify.warning(e.data);
          clearInterval(timer);
          app.upgradeLoading = false;
        });
      // 开启自动刷新
      this.appUpgreading = true;
    },
@@ -630,17 +750,17 @@
            { unloadLoading: false, upgradeLoading: false },
            item
          );
          if (obj.progressMsg !== "" && obj.progressMsg !== "已安装") {
          if (
            obj.progressMsg !== "" &&
            obj.progressMsg !== "已安装" &&
            obj.progressMsg != "100%"
          ) {
            obj.upgradeLoading = true;
            this.appUpgreading = true;
          }
          if (obj.upgradeDone) {
            this.$notify({
              type: "success",
              message: 1 ? "算法安装成功" : "算法升级成功",
            });
            this.$notify.success(1 ? "算法安装成功" : "算法升级成功");
          }
          item.installed ? iArry.push(obj) : sArry.push(obj);
@@ -664,12 +784,7 @@
                app.unloadLoading = false;
                _this.getAllApps();
                window.parent.postMessage({ msg: "AppUpdate" }, "*");
                _this.$notify({
                  title: "成功",
                  message: "卸载应用成功",
                  type: "success",
                });
                _this.$notify.success("卸载应用成功");
              }
            })
            .catch((e) => {
@@ -704,6 +819,21 @@
      getUnActivedSdk().then((res) => {
        if (res.code == 200) {
          this.unActivedSDKList = res.data;
          const len = this.unActivedSDKList.length;
          const set = new Set();
          if (len <= 3) {
            this.recomandUpdateList = [...this.unActivedSDKList];
          } else {
            for (let i = 0; i < 3; i++) {
              const pickI = Math.floor(Math.random() * len);
              if (set.has(pickI)) {
                i--;
                continue;
              }
              set.add(pickI);
              this.recomandUpdateList.push(this.unActivedSDKList[pickI]);
            }
          }
          v == 1 ? (this.tempList = res.data) : null;
        }
      });
@@ -820,9 +950,12 @@
            { unloadLoading: false, upgradeLoading: false },
            item
          );
          if (obj.progressMsg !== "" && obj.progressMsg !== "已安装") {
          if (
            obj.progressMsg !== "" &&
            obj.progressMsg !== "已安装" &&
            obj.progressMsg !== "100%"
          ) {
            obj.upgradeLoading = true;
            this.sdkUpgreading = true;
          }
@@ -875,64 +1008,17 @@
        })
        .catch((e) => {});
    },
    upgradeSDKinWin() {
      this.isUpgrading = !this.isUpgrading;
      let _this = this;
      if (this.productDetail.productTypeName == "应用") {
        installApp({ path: this.productDetail.productBaseId })
          .then((res) => {
            if (res && res.success) {
              this.$notify.success("升级完成");
              this.needToUpgradeInWin = false;
              this.productDetail.isUpgrade = false;
              this.isUpgrading = !this.isUpgrading;
            }
          })
          .catch((e) => {
            this.$notify.warning("升级失败");
            this.isUpgrading = !this.isUpgrading;
          });
        // 开启自动刷新
        this.appUpgreading = true;
      } else {
        downloadSdk({ path: this.productDetail.productBaseId })
          .then((res) => {
            this.needToUpgradeInWin = false;
            this.productDetail.isUpgrade = false;
            this.isUpgrading = !this.isUpgrading;
            this.$notify.success("升级完成");
          })
          .catch((err) => {
            this.$notify.warning("升级失败");
            this.isUpgrading = !this.isUpgrading;
          });
        this.sdkUpgreading = true;
      }
    },
    downloadSdkInSide() {
      this.downloadItem = this.productDetail.productBaseId;
      this.isUpgrading = true;
      downloadSdk({ path: this.productDetail.productBaseId })
        .then((rsp) => {
          this.productDetailVisible = false;
          this.downloadItem = "";
          this.$notify.success("算法已安装");
          this.isUpgrading = false;
        })
        .catch((err) => {
          this.$notify.warning(err.data);
          this.downloadItem = "";
          this.isUpgrading = false;
        });
      // 开启自动刷新
      this.sdkUpgreading = true;
    },
    donwload(item) {
    donwloadSDK(item) {
      let timer = null;
      item.upgradeLoading = true;
      timer = setInterval(() => {
        this.rocketIf = !this.rocketIf;
      }, 350);
      this.downloadItem = item.id;
      downloadSdk({ path: item.id })
        .then((rsp) => {
          clearInterval(timer);
          item.upgradeLoading = false;
          this.downloadItem = "";
          window.parent.postMessage({ msg: "AppUpdate" }, "*");
@@ -944,9 +1030,6 @@
        });
      // 开启自动刷新
      this.sdkUpgreading = true;
    },
    inputBlur(item) {
      this.$set(item, "isEdit", false);
    },
    autoRefreshAppAndSdkState() {
      // 关闭后退出
@@ -973,31 +1056,6 @@
  height: 100%;
  box-sizing: border-box;
  text-align: left;
  min-width: 1106px;
}
.el-loading-mask .el-loading-spinner {
  top: 40px !important;
}
.el-loading-mask .el-loading-spinner svg {
  transform: none !important;
  top: 20px !important;
  left: 40% !important;
}
.el-loading-mask .el-loading-spinner p.el-loading-text {
  display: block !important;
  text-align: center !important;
  bottom: 10px !important;
  top: 80px !important;
  right: 0 !important;
  color: #78adf7;
}
.el-loading-mask .el-loading-spinner .path {
  stroke: #78adf7;
}
.update-badge .el-badge__content.is-fixed {
  top: 10px;
  right: 4px;
}
.task-manage {
@@ -1067,12 +1125,14 @@
            border-left: 3px solid #f3f6fc;
            cursor: pointer;
            caret-color: transparent;
            .el-badge__content {
              border-radius: 50%;
            sup {
              background-color: #f52323;
              font-size: 12px;
              height: 14px;
              line-height: 14px;
              padding: 0px 3px;
              color: #fff;
              height: 18px;
              line-height: 18px;
              padding: 0 3.5px;
              border-radius: 50%;
            }
          }
          .menu-item:hover {
@@ -1084,12 +1144,20 @@
            border-left: 3px solid #23d7ee;
            font-weight: 700;
            font-size: 16px;
            //  transition: transform .3s cubic-bezier(.645,.045,.355,1);
            color: #333;
          }
        }
        .nav-box-search {
          z-index: 99;
          position: relative;
          .all-scene {
            position: absolute;
            z-index: 100;
            right: 64px;
            top: 10px;
            font-size: 12px;
          }
          .el-input {
            position: relative;
            font-size: 12px;
@@ -1099,17 +1167,19 @@
              border: none;
              height: 30px;
              line-height: 30px;
              padding: 0 12px;
            }
            .el-input-group__prepend {
              border-right: 0;
              border: none;
              border-radius: 20px;
              background: #fff;
              padding: 0 25px;
              padding: 0 0 0 15px;
              i {
                font-weight: 600;
                color: #333;
                font-size: 19px;
                font-size: 16px;
              }
            }
            .el-input-group--append .el-input__inner,
@@ -1122,7 +1192,7 @@
              border-left: 0;
              border: none;
              border-radius: 20px;
              width: 64px;
              width: 52px;
              background: linear-gradient(
                180deg,
                #ecfcfe 0%,
@@ -1138,11 +1208,38 @@
                  font-size: 12px;
                  font-weight: bold;
                  letter-spacing: 0.5px;
                  padding: 0 0px 0 30px;
                  color: #474747;
                }
                .el-input__suffix {
                  display: none;
                }
              }
              .el-input__inner::placeholder {
                color: rgb(71, 71, 71);
                // font-weight: 600;
              }
              .el-select-dropdown {
                min-width: 120px !important;
                left: -26px !important;
                background: rgba(236, 245, 253, 0.6);
                border-radius: 2px;
                border: none;
                .el-select-dropdown__item {
                  height: 22px;
                  line-height: 22px;
                  font-size: 12px;
                  letter-spacing: 0.4px;
                  color: #333333;
                  .icon {
                    margin-right: 5px;
                    font-size: 15px;
                  }
                }
                .el-select-dropdown__item.hover,
                .el-select-dropdown__item:hover {
                  background-color: #ebf4fd;
                  font-weight: 600;
                }
              }
            }
            .el-input-group__prepend .el-select {
@@ -1163,8 +1260,8 @@
          position: absolute;
          z-index: 1;
          right: 0;
          top: 70px;
          height: 200px;
          top: 41px;
          height: 229px;
          img {
            height: 100%;
          }
@@ -1185,16 +1282,26 @@
        height: 75px;
        display: flex;
        align-items: center;
        .left-items {
          display: flex;
        }
        .quick-item {
          display: flex;
          cursor: pointer;
          padding: 2px 20px;
          height: 52px;
          box-sizing: border-box;
          border-right: 1px solid #e0e0e0;
          .icon-img {
            width: 48px;
            height: 48px;
            position: relative;
            .icon {
              position: absolute;
              right: -7px;
              top: -7px;
              font-size: 13px;
            }
            img {
              width: 48px;
              height: 48px;
@@ -1219,11 +1326,17 @@
            }
          }
          .right-icon {
            padding: 5px 0;
            padding: 2px 0;
            .icon {
              font-size: 21px;
            }
          }
        }
        .quick-item:not(:last-child) {
          border-right: 1px solid #e0e0e0;
        }
        .down-all-btn {
          // margin-left: 25px;
          position: absolute;
          right: 30px;
          .el-button {
@@ -1236,7 +1349,7 @@
        }
      }
      .main-content {
        padding: 35px 60px;
        padding: 35px 82px;
        z-index: 99;
        .main-title {
          line-height: 25px;
@@ -1250,7 +1363,7 @@
          .group-left {
            display: flex;
            .tab {
              font-size: 15px;
              font-size: 14px;
              margin-right: 25px;
              line-height: 28px;
              cursor: pointer;
@@ -1279,30 +1392,43 @@
          }
        }
        .front-page-items {
          padding-top: 30px;
          padding-top: 25px;
          .front-page-item {
            float: left;
            display: flex;
            margin-bottom: 30px;
            padding: 2px 0;
            height: 60px;
            margin: 0 5px;
            margin-bottom: 15px;
            box-sizing: border-box;
            width: 278px;
            width: 274px;
            justify-content: center;
            cursor: pointer;
            border-radius: 6px;
            padding: 10px 0;
            height: 76px;
            border-radius: 4px;
            .icon-img {
              width: 58px;
              height: 58px;
              position: relative;
              .icon {
                position: absolute;
                right: -9px;
                top: -9px;
                font-size: 16px;
              }
              img {
                width: 58px;
                height: 58px;
                border-radius: 10px;
              }
            }
            .desc {
              box-sizing: border-box;
              padding: 0 12px;
              width: 140px;
              width: 147px;
              .desc-1 {
                font-size: 14px;
                font-weight: bold;
@@ -1320,26 +1446,66 @@
              }
            }
            .right-btn {
              padding: 5px 0;
              .el-button {
                padding: 3px 12px;
              padding: 2px 0;
              width: 50px;
              text-align: center;
              position: relative;
              .check-btn {
                background-color: #f2f2f7 !important;
                border-color: #f2f2f7 !important;
                color: #4f4f4f;
              }
              .update-btn {
                border-color: #23d7ee !important;
                background-color: rgba(29, 212, 236, 0.1) !important;
                color: #4f4f4f;
              }
              .other-btn {
                background-color: #1dd4ec !important;
                border-color: #1dd4ec !important;
                border-radius: 22px;
                color: #ffffff;
              }
              .el-button--primary:hover {
                background: #089fb3 !important;
                border-color: #089fb3 !important;
              @keyframes spin {
                from {
                  transform: rotate(0deg);
                }
                to {
                  transform: rotate(360deg);
                }
              }
              .anz-font {
                font-size: 28px;
                color: #333;
              }
              .spin-icon {
                animation: spin 0.8s linear infinite;
              }
              .status {
                font-size: 12px;
                color: #828282;
                white-space: nowrap;
                overflow: hidden;
                text-overflow: ellipsis;
                min-width: 100px;
                text-align: end;
                position: absolute;
                right: 0;
                margin-top: 2px;
              }
              .rocket-icon {
                font-size: 20px;
              }
              .el-button {
                padding: 3px 12px;
                border-radius: 22px;
                font-weight: bold;
                letter-spacing: 0.5px;
              }
            }
          }
          .front-page-item:hover {
            background: linear-gradient(
              180deg,
              #ecfcfe 0%,
              #ebf4fd 47.92%,
              #f4f4fe 100%
            );
            // background-color: #f2f2f7;
          }
          .item-dimmed {
            color: gray;
@@ -1353,29 +1519,53 @@
          clear: both;
          visibility: hidden;
        }
        .upload-pkg{
          .upload-head{
        .upload-pkg {
          .upload-head {
            display: flex;
    justify-content: space-between;
    align-items: center;
            .left{
            justify-content: space-between;
            align-items: center;
            .left {
              display: flex;
              align-items: center;
              .iconfont{
              .iconfont {
                font-size: 18px;
                margin-right: 6px;
              }
              .txt{
                    font-size: 16px;
              .txt {
                font-size: 16px;
              }
            }
            .right{
              .icon{
                margin-right: 10px;
            .right {
              .icon {
                margin-right: 11px;
                font-size: 17px;
                cursor: pointer;
              }
            }
          }
        }
        .upload-demo {
          margin: 30px 0px;
          .drag-txt {
            width: 340px;
            height: 45px;
            border: 1px dashed #bdbdbd;
            display: flex;
            margin-top: 30px;
            align-items: center;
            justify-content: center;
            color: #828282;
            font-size: 14px;
          }
          .txt-btn {
            width: 78px;
            margin-top: 18px;
            height: 19px;
            background: #23d7ee;
            border-radius: 22px;
            color: #ffffff;
            font-weight: bold;
            font-size: 12px;
          }
        }
      }
@@ -1415,148 +1605,11 @@
      }
    }
    .list-choose-item {
      cursor: pointer;
      position: relative;
      font-size: 14px;
      display: inline-block;
      @media screen and(min-width: 1640px) {
        margin: 30px 20px 20px 20px;
      }
      @media screen and(min-width: 1460px) and(max-width: 1640px) {
        margin: 30px 20px 20px 10px;
      }
      @media screen and(max-width: 1460px) {
        margin: 30px 10px 20px 10px;
      }
      min-width: 126px;
      height: 120px;
      transition: all 1s;
      background: #fff;
      border: 1px solid #e2e2e2;
      box-shadow: 0 5px 12px 0 rgba(0, 0, 0, 0.07);
      border-radius: 4px;
    }
    .list-choose-item:hover {
      .mask {
        display: block;
      }
    }
    .list-choose-item-left {
      cursor: pointer;
      position: relative;
      font-size: 14px;
      transition: all 1s;
      border-radius: 4px;
      p {
        display: none;
        text-align: right;
        width: 100%;
        position: absolute;
        right: 10px;
        top: 5px;
      }
      .click-download {
        position: absolute;
        left: 80%;
        top: 5%;
      }
    }
    .list-choose-item-left:hover {
      .mask {
        display: flex;
        align-items: flex-end;
        flex-wrap: wrap;
        justify-content: center;
        top: 0;
        .bot-btn {
          flex: 1;
        }
        &.flex-center {
          align-items: center;
          justify-content: center;
        }
      }
    }
    .list-choose-item-left-uninstal {
      color: gray;
      filter: grayscale(100%);
    }
    .list-complete-item.sortable-chosen {
      background: #4ab7bd;
    }
    .list-choose-item.sortable-ghost {
      background: #30b08f;
    }
    .list-choose-header {
      position: relative;
      width: 74px;
      height: 74px;
      background-image: linear-gradient(-137deg, #7076f2 0%, #3d63e1 100%);
      box-shadow: 0 5px 12px 0 rgba(0, 0, 0, 0.07);
      border-radius: 37px;
      margin: 10px 25px;
    }
    .b-top {
      width: 100%;
      padding-top: 10px;
    }
    .b-bottom {
      width: 100%;
      border-bottom: 1px solid rgba(24, 28, 33, 0.5);
    }
    .i-set-right {
      position: absolute;
      left: 80px;
      top: -11px;
      font-size: 24px;
    }
    .i-remove-right {
      position: absolute;
      right: -1px;
      top: -11px;
      font-size: 24px;
      color: red;
    }
    .alg-t {
      line-height: 31px;
      font-family: PingFangSC-Medium;
      font-size: 14px;
      color: #222222;
    }
    .alg-name {
      line-height: 20px;
      font-family: PingFangSC-Regular;
      font-size: 14px;
      letter-spacing: 0.05em;
      color: #333;
      .el-input {
        position: relative;
        font-size: 14px;
        display: inline-block;
        width: 100%;
      }
    }
    .el-input {
      position: relative;
      font-size: 14px;
      // display: inline-block;
      // width: 80%;
    }
    .drag-info {
      min-width: 126px;
      height: 120px;
      border: 1px dashed #3d68e1;
      box-shadow: 0 5px 12px 0 rgba(0, 0, 0, 0.07);
      border-radius: 4px;
      margin: 30px 10px 20px 10px;
    }
    .el-button--cancle {
@@ -1567,39 +1620,6 @@
      font-size: 13px;
      color: #222222;
      margin-right: 12px;
    }
    .task-name-google {
      position: relative;
      top: 30px;
      width: 126px;
      height: 120px;
      border: 1px solid #fff;
      background: #fff;
      border-radius: 4px;
      cursor: pointer;
      .set-task {
        display: none;
        cursor: pointer;
      }
      .el-switch__core {
        width: 27px !important;
        height: 14px;
      }
      .el-switch__core:after {
        width: 10px;
        height: 10px;
      }
      .el-switch.is-checked .el-switch__core::after {
        left: 100%;
        margin-left: -11px;
      }
    }
    .task-name-google:hover {
      .mask {
        display: block;
      }
    }
  }
}
src/pages/ai/index/api.ts
@@ -142,6 +142,14 @@
    params: data
  })
}
export const uploadSDK = (data: any) => {
  return request({
    url: "/data/api-v/sdk/upload",
    method: "post",
    data
  })
}
//安装已上传的算法接口
export const installSdk = (file: any) => {
  return request({
src/pages/ai/index/detail.vue
@@ -16,6 +16,7 @@
            <el-button
              size="mini"
              round
              class="update-btn"
              @click="upgradeSDKinWin"
              v-if="productDetail.isUpgrade"
              >更新</el-button
@@ -34,10 +35,20 @@
              @click="unloadSDKinWin"
              >卸载</el-button
            >
            <el-button
              size="mini"
              round
              v-if="!isActive && showInstallNotActive"
              @click="downloadSdkInSide"
              >安装</el-button
            >
            <!-- suffix-icon="iconfont iconchushaixuanxiang" -->
            <el-input
              class="activeInput"
              placeholder="请输入激活码"
              size="mini"
              clearable
              :autofocus="true"
              v-model="activeCode"
              v-if="!isActive && !showInstallNotActive"
            >
@@ -50,17 +61,17 @@
              @click="actived"
              >激活</el-button
            >
            <span
            <!-- <span
              class="icon iconfont"
              v-if="!isActive && !showInstallNotActive"
              @click="activeCode = ''"
              >&#xe6cb;</span
            >
            > -->
          </div>
        </div>
        <div class="back-btn" @click="goback">
          <span class="icon iconfont">&#xe680;</span>
          <span class="back-text">返回上页</span>
          <span class="back-text">返回</span>
        </div>
      </div>
      <div class="text-area">
@@ -161,6 +172,43 @@
        </div>
      </div>
    </div>
    <el-dialog
      :visible.sync="showActivateSuccess"
      title="激活成功!"
      width="30%"
      class="active-Dial"
      :before-close="handleClose"
    >
      <div class="dialog-active">
        <ul class="desc">
          <li>
            <label>激活码:</label>
            <span>{{ activedSdkOrApp.activateCode }}</span>
          </li>
          <li>
            <label>产品名称:</label>
            <span>{{ activedSdkOrApp.productName }}</span>
          </li>
          <li>
            <label>配置详情:</label>
            <span>{{ activedSdkOrApp.setting }}</span>
          </li>
          <li>
            <label>服务到期日:</label>
            <span>{{ activedSdkOrApp.expireTime }}</span>
          </li>
          <li>
            <label>许可证:</label>
            <span>{{ activedSdkOrApp.licence }}</span>
          </li>
        </ul>
        <div class="text-right">
          <el-button type="primary" @click="checkMyAlgorith">确定</el-button>
          <p class="tip">提示:请在“已激活”中查看并安装算法</p>
        </div>
      </div>
    </el-dialog>
  </div>
</template>
<script>
@@ -182,11 +230,8 @@
  actApp,
} from "@/api/app";
import { getUrlKey } from "@/api/utils";
// import FileUploader from "@/components/subComponents/FileUpload/index";
export default {
  components: {
    // FileUploader,
  },
  components: {},
  computed: {
    updateNum() {
      return this.hasNewVersionApp.length + this.hasNewVersionSdk.length;
@@ -207,7 +252,6 @@
    return {
      installedList: [],
      hasNewVersionSdk: [],
      activeTab: 0,
      hasNewVersionApp: [],
      notInstalledList: [],
      buttonAuthority: sessionStorage.getItem("buttonAuthoritys") || [],
@@ -258,8 +302,6 @@
      showInputCode: false,
      needToUpgradeInWin: false,
      showInstallNotActive: false,
      activeIndex: 0,
      menuArr: ["应用中心", "已激活", "离线升级/安装", "更新"],
    };
  },
  props: {
@@ -334,7 +376,7 @@
        this.actId = this.productDetail.productBaseId;
        this.isSDKDetail = this.productDetail.productTypeName == "SDK";
        this.isDefaultApp = this.productDetail.productBaseId.length < 10;
        this.otherProducts = res.data.randoms;
      });
    },
@@ -613,7 +655,8 @@
    },
    checkMyAlgorith() {
      this.showActivateSuccess = false;
      this.activeName = "myAlgorithm";
      this.goback();
      this.activeName = "已激活";
    },
    onFileUpload(file) {
      this.patchFile = { ...file };
@@ -720,18 +763,9 @@
            .then((res) => {
              if (res && res.success) {
                sdk.unloadLoading = false;
                this.$notify({
                  title: "成功",
                  message: "卸载完成",
                  type: "success",
                });
                this.$notify.success("卸载完成");
                _this.getAllSdk();
                window.parent.postMessage(
                  {
                    msg: "AppUpdate",
                  },
                  "*"
                );
                window.parent.postMessage({ msg: "AppUpdate" }, "*");
              }
            })
            .catch((e) => {
@@ -781,17 +815,11 @@
        .then((rsp) => {
          this.productDetailVisible = false;
          this.downloadItem = "";
          this.$notify({
            type: "success",
            message: "算法已安装",
          });
          this.$notify.success("算法已安装");
          this.isUpgrading = false;
        })
        .catch((err) => {
          this.$notify({
            type: "warning",
            message: err.data,
          });
          this.$notify.warning(err.data);
          this.downloadItem = "";
          this.isUpgrading = false;
        });
@@ -849,14 +877,12 @@
.detail-page {
  width: 100% !important;
  height: 100%;
  //   background-color: cornsilk;
  box-sizing: border-box;
  text-align: left;
  //   min-width: 1106px;
  .detail-top {
    padding: 35px 60px;
    padding-bottom: 20px;
    //   margin-bottom: 40px;
    border-bottom: 3px solid #f2f2f2;
    .title-area {
      display: flex;
@@ -866,7 +892,7 @@
        position: absolute;
        right: 0;
        top: 0;
        width: 80px;
        width: 51px;
        height: 20px;
        line-height: 20px;
        display: flex;
@@ -874,12 +900,12 @@
        align-items: center;
        cursor: pointer;
        .icon {
          margin-right: 2px;
          margin-right: 3px;
          font-size: 18px;
        }
        .back-text {
          font-size: 14px;
          letter-spacing: 0.1px;
          letter-spacing: 0.5px;
        }
      }
      .icon-img {
@@ -915,6 +941,19 @@
        .right-info-2 {
          display: flex;
          align-items: center;
          .activeInput {
            .el-input__suffix {
              display: flex;
              align-items: center;
              .el-input__suffix-inner {
                border-color: none;
                .el-icon-circle-close:before {
                  content: "\e79d" !important;
                  font-size: 14px;
                }
              }
            }
          }
          .iconfont {
            margin-left: 10px;
            font-size: 15px;
@@ -922,7 +961,8 @@
          }
          .el-input {
            margin-right: 12px;
            width: 220px;
            width: 224px;
            font-size: 12px;
            .el-input__inner {
              border: 1px solid #bdbdbd;
              border-radius: 20px;
@@ -938,19 +978,23 @@
            background: #f2f2f7;
            border: 1px solid #f2f2f7;
          }
          .update-btn {
            border-color: #23d7ee;
            background-color: rgba(29, 212, 236, 0.1);
          }
          .act-btn {
            background: #23d7ee;
            border: 1px solid #23d7ee;
            color: #f2f2f2;
          }
          .act-btn:hover {
            background: #089fb3 !important;
            border-color: #089fb3 !important;
          }
          .el-button:hover {
            border-color: #23d7ee;
            background-color: rgba(29, 212, 236, 0.1);
          }
          // .act-btn:hover {
          //   background: #089fb3 !important;
          //   border-color: #089fb3 !important;
          // }
          // .el-button:hover {
          //   border-color: #23d7ee;
          //   background-color: rgba(29, 212, 236, 0.1);
          // }
          .el-button + .el-button {
            margin-left: 12px;
          }
@@ -961,8 +1005,6 @@
      .text-line {
        line-height: 17px;
        height: 25px;
        // margin-top: 6px;
        .icon {
          font-size: 14px;
          margin-right: 2px;
@@ -1046,7 +1088,7 @@
      font-weight: bold;
      letter-spacing: 0.5px;
      line-height: 26px;
          margin-bottom: 20px;
      margin-bottom: 20px;
    }
  }
  .rec-items {
@@ -1054,7 +1096,7 @@
      float: left;
      display: flex;
      margin: 0 15px;
         margin-bottom: 30px;
      margin-bottom: 30px;
      padding: 2px 0;
      height: 62px;
      box-sizing: border-box;
@@ -1086,7 +1128,6 @@
        }
      }
      .right-btn {
        // width: 20px;
        padding: 5px 0;
        .el-button {
          padding: 3px 12px;
@@ -1094,10 +1135,6 @@
          border-color: #1dd4ec !important;
          border-radius: 22px;
        }
        // .el-button--primary:focus {
        //   background: #0cabc0 !important;
        //   border-color: #0cabc0 !important;
        // }
        .el-button--primary:hover {
          background: #089fb3 !important;
          border-color: #089fb3 !important;
src/pages/ai_c/index/App.vue
@@ -336,7 +336,7 @@
              >
                <div class="tab-content">
                  <div class="action-bar">
                    <file-uploader
                    <FileUploader
                      single
                      tip
                      tipWords="上传算法"
@@ -440,7 +440,6 @@
                      <div class="list-choose-item-left">
                        <div class="list-complete-item-handle">
                          <div class="alg-icon svg-wrap">
                            <div
                              class="mask"
                              v-if="!item.isDefault || item.isUpgrade"
@@ -1191,7 +1190,7 @@
        this.showInstallNotActive = false;
        this.isActive = true;
      } else if (type == "activeNotInstall") {
        debugger
        debugger;
        this.showInputCode = false;
        this.isActive = false;
        this.showInstallNotActive = true;
@@ -1296,7 +1295,7 @@
            this.isInstall = false;
            this.$message({
              type: "success",
              message: '安装成功,将跳转至“已激活”中查看',
              message: "安装成功,将跳转至“已激活”中查看",
            });
            setTimeout(() => {
              this.getAllSdk();
@@ -1553,11 +1552,11 @@
                installContent: res.data.sdks[0].installContent,
              };
            }
          }else{
          } else {
            this.$message({
              type: "error",
              message: res.msg,
          });
            });
          }
        })
        .catch((e) => {
@@ -1657,7 +1656,7 @@
      this.isUpgrading = !this.isUpgrading;
      let _this = this;
      if (this.productDetail.productTypeName == "应用") {
        debugger
        debugger;
        installApp({ path: this.productDetail.productBaseId })
          .then((res) => {
            if (res && res.success) {
@@ -1683,7 +1682,7 @@
        // 开启自动刷新
        this.appUpgreading = true;
      } else {
        debugger
        debugger;
        downloadSdk({ path: this.productDetail.productBaseId })
          .then((res) => {
            this.needToUpgradeInWin = false;
src/pages/desktop/index/components/Desktop.vue
@@ -46,7 +46,6 @@
      <div class="ask">
        如果你具有SmartAI提供的产品密钥,请在此处输入激活SmartAI。
      </div>
      <div class="validate">
        <form id="myForm">
          <el-input
@@ -138,15 +137,20 @@
      this.dialogVisible = true;
    },
    uploadKey(params) {
      debugger
      let param = new FormData();
      param.append("code", params.file);
      uploadKey(param).then(
        (res) => {
          this.$message.success("导入成功");
          this.secrectKey = res.data;
          if (res.code == 200) {
            this.$message.success("导入成功");
            this.secrectKey = res.data;
          } else {
            this.$message.error(res.msg);
          }
        },
        (err) => {
          this.$message.error("导入失败");
          this.$message.error(err.msg);
        }
      );
    },
src/pages/settings/views/generalSettings.vue
@@ -632,7 +632,6 @@
        transition: all 0.5s;
        position: absolute;
        bottom: -40px;
        left: calc(50% - 145px);
        .el-upload-dragger {
          width: 290px;