From 44a3abe105e9b9b4d1a7173772fc9d18c9b59529 Mon Sep 17 00:00:00 2001
From: heyujie <516346543@qq.com>
Date: 星期六, 18 九月 2021 16:24:02 +0800
Subject: [PATCH] 应用中心ui改动
---
src/pages/ai_c/index/App.vue | 15
src/pages/ai/FileUpload/uploader.vue | 168 ++
src/pages/ai/FileUpload/common/mixins.js | 14
src/pages/ai/FileUpload/file.vue | 471 ++++++
src/pages/ai/FileUpload/unsupport.vue | 30
public/apps.json | 2
src/pages/ai/index/App.vue | 856 ++++++------
src/pages/ai/index/detail.vue | 147 +
src/pages/ai/FileUpload/btn.vue | 66
src/pages/ai/FileUpload/drop.vue | 64
src/components/subComponents/FileUpload/index.vue | 173 +
src/pages/ai/FileUpload/common/file-events.js | 3
public/images/appCenter/Group-113.png | 0
public/images/appCenter/app-banner.png | 0
src/components/subComponents/FileUpload/uploader.vue | 12
src/pages/ai/FileUpload/common/uploader-simple.js | 1612 +++++++++++++++++++++++
src/pages/desktop/index/components/Desktop.vue | 12
src/components/subComponents/FileUpload/file.vue | 2
src/pages/ai/FileUpload/list.vue | 43
src/components/subComponents/FileUpload/btn.vue | 4
public/images/appCenter/Group-112.png | 0
src/pages/ai/FileUpload/index.vue | 302 ++++
src/pages/ai/FileUpload/common/utils.js | 28
src/pages/ai/FileUpload/files.vue | 42
src/pages/settings/views/generalSettings.vue | 1
src/pages/ai/index/api.ts | 8
26 files changed, 3,523 insertions(+), 552 deletions(-)
diff --git a/public/apps.json b/public/apps.json
index 5736af4..cd5f077 100644
--- a/public/apps.json
+++ b/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",
diff --git a/public/images/appCenter/Group-112.png b/public/images/appCenter/Group-112.png
new file mode 100644
index 0000000..25166fd
--- /dev/null
+++ b/public/images/appCenter/Group-112.png
Binary files differ
diff --git a/public/images/appCenter/Group-113.png b/public/images/appCenter/Group-113.png
new file mode 100644
index 0000000..f74ee8d
--- /dev/null
+++ b/public/images/appCenter/Group-113.png
Binary files differ
diff --git a/public/images/appCenter/app-banner.png b/public/images/appCenter/app-banner.png
new file mode 100644
index 0000000..008f99a
--- /dev/null
+++ b/public/images/appCenter/app-banner.png
Binary files differ
diff --git a/src/components/subComponents/FileUpload/btn.vue b/src/components/subComponents/FileUpload/btn.vue
index 9eccf99..0bffa02 100644
--- a/src/components/subComponents/FileUpload/btn.vue
+++ b/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>
diff --git a/src/components/subComponents/FileUpload/file.vue b/src/components/subComponents/FileUpload/file.vue
index 333a547..1d8c5c7 100644
--- a/src/components/subComponents/FileUpload/file.vue
+++ b/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> {{formatedTimeRemaining}}</i>
diff --git a/src/components/subComponents/FileUpload/index.vue b/src/components/subComponents/FileUpload/index.vue
index 796d99f..2068da8 100644
--- a/src/components/subComponents/FileUpload/index.vue
+++ b/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"
+ ></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"></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"></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 = "鍑嗗涓婁紶,姝e湪妫�鏌ユ枃浠�"
+ this.statusText.paused = "鍑嗗涓婁紶,姝e湪妫�鏌ユ枃浠�";
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>>
diff --git a/src/components/subComponents/FileUpload/uploader.vue b/src/components/subComponents/FileUpload/uploader.vue
index 6ada9f3..14735e4 100644
--- a/src/components/subComponents/FileUpload/uploader.vue
+++ b/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>
diff --git a/src/pages/ai/FileUpload/btn.vue b/src/pages/ai/FileUpload/btn.vue
new file mode 100644
index 0000000..0bffa02
--- /dev/null
+++ b/src/pages/ai/FileUpload/btn.vue
@@ -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>
diff --git a/src/pages/ai/FileUpload/common/file-events.js b/src/pages/ai/FileUpload/common/file-events.js
new file mode 100644
index 0000000..2aba807
--- /dev/null
+++ b/src/pages/ai/FileUpload/common/file-events.js
@@ -0,0 +1,3 @@
+const events = ['fileProgress', 'fileSuccess', 'fileComplete', 'fileError']
+
+export default events
diff --git a/src/pages/ai/FileUpload/common/mixins.js b/src/pages/ai/FileUpload/common/mixins.js
new file mode 100644
index 0000000..efab3b0
--- /dev/null
+++ b/src/pages/ai/FileUpload/common/mixins.js
@@ -0,0 +1,14 @@
+export const uploaderMixin = {
+ inject: ['uploader']
+}
+
+export const supportMixin = {
+ data () {
+ return {
+ support: true
+ }
+ },
+ mounted () {
+ this.support = this.uploader.uploader.support
+ }
+}
diff --git a/src/pages/ai/FileUpload/common/uploader-simple.js b/src/pages/ai/FileUpload/common/uploader-simple.js
new file mode 100644
index 0000000..b0a0d16
--- /dev/null
+++ b/src/pages/ai/FileUpload/common/uploader-simple.js
@@ -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 = {}
+ }
+
+ // 濡傛灉鍙湁涓�涓弬鏁帮紝閭d箞鏂版垚鍛樻坊鍔犱簬 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)
+ });
\ No newline at end of file
diff --git a/src/pages/ai/FileUpload/common/utils.js b/src/pages/ai/FileUpload/common/utils.js
new file mode 100644
index 0000000..92c775a
--- /dev/null
+++ b/src/pages/ai/FileUpload/common/utils.js
@@ -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()}`)
+}
diff --git a/src/pages/ai/FileUpload/drop.vue b/src/pages/ai/FileUpload/drop.vue
new file mode 100644
index 0000000..9c88666
--- /dev/null
+++ b/src/pages/ai/FileUpload/drop.vue
@@ -0,0 +1,64 @@
+<template>
+ <div class="uploader-drop" :class="dropClass" ref="drop" v-show="support">
+ <slot></slot>
+ </div>
+</template>
+
+<script>
+import { uploaderMixin, supportMixin } from './common/mixins'
+
+const COMPONENT_NAME = 'uploader-drop'
+
+export default {
+ name: COMPONENT_NAME,
+ mixins: [uploaderMixin, supportMixin],
+ data() {
+ return {
+ dropClass: ''
+ }
+ },
+ methods: {
+ onDragEnter() {
+ this.dropClass = 'uploader-dragover'
+ },
+ onDragLeave() {
+ this.dropClass = ''
+ },
+ onDrop() {
+ this.dropClass = 'uploader-droped'
+ }
+ },
+ mounted() {
+ this.$nextTick(() => {
+ const dropEle = this.$refs.drop
+ const uploader = this.uploader.uploader
+ uploader.assignDrop(dropEle)
+ uploader.on('dragenter', this.onDragEnter)
+ uploader.on('dragleave', this.onDragLeave)
+ uploader.on('drop', this.onDrop)
+ })
+ },
+ beforeDestroy() {
+ const dropEle = this.$refs.drop
+ const uploader = this.uploader.uploader
+ uploader.off('dragenter', this.onDragEnter)
+ uploader.off('dragleave', this.onDragLeave)
+ uploader.off('drop', this.onDrop)
+ uploader.unAssignDrop(dropEle)
+ }
+}
+</script>
+
+<style>
+.uploader-drop {
+ position: relative;
+ padding: 10px;
+ overflow: hidden;
+ border: 1px dashed #ccc;
+ background-color: #f5f5f5;
+}
+.uploader-dragover {
+ border-color: #999;
+ background-color: #f7f7f7;
+}
+</style>
diff --git a/src/pages/ai/FileUpload/file.vue b/src/pages/ai/FileUpload/file.vue
new file mode 100644
index 0000000..fca3993
--- /dev/null
+++ b/src/pages/ai/FileUpload/file.vue
@@ -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> {{ 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>
diff --git a/src/pages/ai/FileUpload/files.vue b/src/pages/ai/FileUpload/files.vue
new file mode 100644
index 0000000..1380ba9
--- /dev/null
+++ b/src/pages/ai/FileUpload/files.vue
@@ -0,0 +1,42 @@
+<template>
+ <div class="uploader-files">
+ <slot :files="files">
+ <ul>
+ <li v-for="file in files" :key="file.id">
+ <uploader-file :file="file"></uploader-file>
+ </li>
+ </ul>
+ </slot>
+ </div>
+</template>
+
+<script>
+import { uploaderMixin } from './common/mixins'
+import UploaderFile from './file.vue'
+
+const COMPONENT_NAME = 'uploader-files'
+
+export default {
+ name: COMPONENT_NAME,
+ mixins: [uploaderMixin],
+ computed: {
+ files() {
+ return this.uploader.files
+ }
+ },
+ components: {
+ UploaderFile
+ }
+}
+</script>
+
+<style>
+.uploader-files {
+ position: relative;
+}
+.uploader-files > ul {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+</style>
diff --git a/src/pages/ai/FileUpload/index.vue b/src/pages/ai/FileUpload/index.vue
new file mode 100644
index 0000000..2a8ccf2
--- /dev/null
+++ b/src/pages/ai/FileUpload/index.vue
@@ -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"
+ ></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"></span>
+ </div>
+ </el-tooltip>
+ </uploader-btn>
+ <!-- <div class="open-file-btn">
+ <span class="icon iconfont"></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 = "鍑嗗涓婁紶,姝e湪妫�鏌ユ枃浠�";
+ 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瀹炰緥鐨刼pts涓�
+ 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>>
diff --git a/src/pages/ai/FileUpload/list.vue b/src/pages/ai/FileUpload/list.vue
new file mode 100644
index 0000000..9951595
--- /dev/null
+++ b/src/pages/ai/FileUpload/list.vue
@@ -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>
diff --git a/src/pages/ai/FileUpload/unsupport.vue b/src/pages/ai/FileUpload/unsupport.vue
new file mode 100644
index 0000000..59626ee
--- /dev/null
+++ b/src/pages/ai/FileUpload/unsupport.vue
@@ -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>
diff --git a/src/pages/ai/FileUpload/uploader.vue b/src/pages/ai/FileUpload/uploader.vue
new file mode 100644
index 0000000..14735e4
--- /dev/null
+++ b/src/pages/ai/FileUpload/uploader.vue
@@ -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>
diff --git a/src/pages/ai/index/App.vue b/src/pages/ai/index/App.vue
index 324e7a1..a4ea71c 100644
--- a/src/pages/ai/index/App.vue
+++ b/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"></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"></span>
+ <span>閾佽矾鍦烘櫙</span>
+ </el-option>
+ <el-option label="瀹夊叏鍦烘櫙" value="2">
+ <span class="icon iconfont"></span>
+ <span>瀹夊叏鍦烘櫙</span>
+ </el-option>
+ <el-option label="閫氱敤鍦烘櫙" value="3">
+ <span class="icon iconfont"></span>
+ <span>閫氱敤鍦烘櫙</span>
+ </el-option>
+ <el-option label="鏍″洯鍥尯" value="4">
+ <span class="icon iconfont"></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"></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"
+ ></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"></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"
+ ></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
+ "
+ ></span
+ >
+ <span
+ class="icon iconfont rocket-icon"
+ v-if="
+ activeName == '鏇存柊' &&
+ item.isUpgrade &&
+ item.upgradeLoading &&
+ !rocketIf
+ "
+ ></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"
+ ></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"></span>
<span class="txt">涓婁紶瀹夎杞欢</span>
</div>
- <div class="right">
- <span class="icon iconfont"></span>
- <span class="icon iconfont"></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;
- }
}
}
}
diff --git a/src/pages/ai/index/api.ts b/src/pages/ai/index/api.ts
index 7232d34..88b586b 100644
--- a/src/pages/ai/index/api.ts
+++ b/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({
diff --git a/src/pages/ai/index/detail.vue b/src/pages/ai/index/detail.vue
index aaa1c11..c8fa510 100644
--- a/src/pages/ai/index/detail.vue
+++ b/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 = ''"
></span
- >
+ > -->
</div>
</div>
<div class="back-btn" @click="goback">
<span class="icon iconfont"></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;
diff --git a/src/pages/ai_c/index/App.vue b/src/pages/ai_c/index/App.vue
index 2a13830..fa975eb 100644
--- a/src/pages/ai_c/index/App.vue
+++ b/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;
diff --git a/src/pages/desktop/index/components/Desktop.vue b/src/pages/desktop/index/components/Desktop.vue
index c14e20e..7e4e343 100644
--- a/src/pages/desktop/index/components/Desktop.vue
+++ b/src/pages/desktop/index/components/Desktop.vue
@@ -46,7 +46,6 @@
<div class="ask">
濡傛灉浣犲叿鏈塖martAI鎻愪緵鐨勪骇鍝佸瘑閽ワ紝璇峰湪姝ゅ杈撳叆婵�娲籗martAI銆�
</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);
}
);
},
diff --git a/src/pages/settings/views/generalSettings.vue b/src/pages/settings/views/generalSettings.vue
index 62220d2..ba74441 100644
--- a/src/pages/settings/views/generalSettings.vue
+++ b/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;
--
Gitblit v1.8.0