文件夹上传1.0版本
This commit is contained in:
parent
3a4591c75b
commit
b500c53218
@ -1,33 +1,62 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-upload class="upload-demo" ref="upload" action="#"
|
||||
:before-upload="beforeUpload"
|
||||
:on-change="onChange"
|
||||
:on-remove="handleRemove"
|
||||
:multiple="isMultiple"
|
||||
:on-exceed="handleExceed"
|
||||
:accept="acceptType"
|
||||
:limit="limit"
|
||||
:file-list="fileList"
|
||||
:auto-upload="true"
|
||||
:http-request="uploadFile"
|
||||
show-file-list>
|
||||
<el-button slot="trigger" size="small" type="primary">选取文件</el-button>
|
||||
<div slot="tip" class="el-upload__tip">只能上传{{acceptType}}文件</div>
|
||||
</el-upload>
|
||||
<div v-show="progressFlag" class="head-img">
|
||||
<el-progress :text-inside="true" :stroke-width="14" :percentage="progressPercent" status="success"></el-progress>
|
||||
<div class="container">
|
||||
<div class="item">
|
||||
<el-upload action="#"
|
||||
ref="uploadFolder"
|
||||
:on-change="onChange"
|
||||
:on-remove="handleRemove"
|
||||
:show-file-list="false"
|
||||
:headers="headers"
|
||||
:accept="accept"
|
||||
:file-list="fileList"
|
||||
:before-upload="beforeUploadsg"
|
||||
:on-success="uploadSuccess"
|
||||
:on-error="uploadError"
|
||||
:on-exceed="exceed"
|
||||
:auto-upload="true"
|
||||
:http-request="uploadFile"
|
||||
:multiple="true">
|
||||
<el-button slot="trigger" id="myButton" size="small" type="primary" @click="uploadBtn.click()">选取文件夹</el-button>
|
||||
</el-upload>
|
||||
</div>
|
||||
<div></div>
|
||||
<div class="item">
|
||||
<el-upload class="upload-demo" ref="upload" action="#"
|
||||
:before-upload="beforeUpload"
|
||||
:on-change="onChange"
|
||||
:on-remove="handleRemove"
|
||||
:multiple="isMultiple"
|
||||
:on-exceed="handleExceed"
|
||||
:accept="acceptType"
|
||||
:limit="limit"
|
||||
:file-list="fileList"
|
||||
:auto-upload="true"
|
||||
:http-request="uploadFile"
|
||||
show-file-list>
|
||||
<el-button id="myButton" slot="trigger" size="small" type="primary">选取文件</el-button>
|
||||
<div slot="tip" class="el-upload__tip">只能上传{{acceptType}}文件</div>
|
||||
</el-upload>
|
||||
<div v-show="progressFlag" class="head-img">
|
||||
<el-progress :text-inside="true" :stroke-width="14" :percentage="progressPercent" status="success"></el-progress>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
import sgUploadTray from "./sgUploadTray"
|
||||
import {
|
||||
getToken
|
||||
} from "@/utils/auth";
|
||||
|
||||
export default {
|
||||
|
||||
components: {
|
||||
sgUploadTray,
|
||||
},
|
||||
props: {
|
||||
uploadUrl: {
|
||||
type: String,
|
||||
@ -75,8 +104,22 @@ export default {
|
||||
progressFlag: false, //进度条初始值隐藏
|
||||
progressPercent: 0, //进度条初始值
|
||||
partSize: 5 * 1024 * 1024,
|
||||
headers: { kkToken: localStorage.token, }, //获取token(注意仔细看后端接受token的字段名是不是叫做“token”)
|
||||
accept:`.${["png", "jpg", "jpeg", "bmp", "gif"].join(",.")}`,//允许上传的后缀名
|
||||
actionURL: process.env.VUE_APP_BASE_API + "/common/initUpload",
|
||||
dur: 100,
|
||||
percent: 100,
|
||||
uploadBtn: null,//上传按钮
|
||||
uploadList: [],
|
||||
showUploadTray: false,
|
||||
};
|
||||
},
|
||||
mounted(d) {
|
||||
this.$nextTick(() => {
|
||||
this.uploadBtn = this.$refs.uploadFolder.$children[0].$refs.input;
|
||||
this.uploadBtn.webkitdirectory = true;//让el-upload支持上传文件夹
|
||||
})
|
||||
},
|
||||
watch: {
|
||||
dataFile: {
|
||||
handler(newValue, oldValue) {
|
||||
@ -90,6 +133,98 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showFakeLoading(file) {
|
||||
file = this.uploadList.find(v => v.uid == file.uid);
|
||||
clearInterval(file.interval);
|
||||
file.percent = 0;
|
||||
file.interval = setInterval(() => {
|
||||
file.percent++;
|
||||
file.percent >= 99 && this.hideFakeLoading(file);
|
||||
}, this.dur);
|
||||
},
|
||||
hideFakeLoading(file, { type, tip, color } = {}) {
|
||||
console.log('file======>');
|
||||
console.log(file)
|
||||
file = this.uploadList.find(v => v.uid == file.uid);
|
||||
clearInterval(file.interval);
|
||||
switch (type) {
|
||||
case 'error':
|
||||
file.percent = 0;
|
||||
break;
|
||||
case 'success':
|
||||
default:
|
||||
file.percent = 100;
|
||||
}
|
||||
type && (file.type = type);
|
||||
tip && (file.tip = tip);
|
||||
color && (file.color = color);
|
||||
},
|
||||
exceed(file, fileList) {
|
||||
this.$message.error("上传文件数量太大,分散上传吧!");
|
||||
},
|
||||
stopUpload(d) {
|
||||
this.$refs.uploadFolder.abort();
|
||||
//console.log(`取消上传`, d);
|
||||
},
|
||||
beforeUploadsg(file, id) {
|
||||
console.log(this.uploadList)
|
||||
this.uploadList = []
|
||||
console.info("this.uploadList==", JSON.stringify(this.uploadList))
|
||||
this.uploadList.unshift({
|
||||
interval: false,
|
||||
uid: file.uid,
|
||||
percent: 0,//加载进度
|
||||
name: file.name,
|
||||
size: file.size,
|
||||
type: file.type,
|
||||
webkitRelativePath: file.webkitRelativePath,
|
||||
tip: '',
|
||||
color: '',
|
||||
});
|
||||
this.showUploadTray = true;
|
||||
this.accept = ["png", "jpg", "jpeg", "bmp", "gif", "txt", "json", "pdf"]
|
||||
// 判断是不是特定的格式________________________
|
||||
let isFile = this.accept.includes(file.name.toLocaleLowerCase().split(".").pop());
|
||||
const maxSize = 50; //限制大小
|
||||
const isAllowSize = file.size / 1024 / 1024 <= maxSize;
|
||||
isFile || this.$message.error("上传文件只能是" + this.accept+ "格式");
|
||||
isAllowSize || this.$message.error("上传文件大小不能超过" + maxSize + "MB");
|
||||
let allowUpload = isFile && isAllowSize;
|
||||
return allowUpload; //若返回false则停止上传
|
||||
},
|
||||
|
||||
uploadSuccess(response, file, fileList) {
|
||||
if (response.data && response.data.key) {
|
||||
// 下载失败原因的描述文件
|
||||
this.$d.customer_downloadImportCustomerExcel({ key: response.data.key }, {
|
||||
s: (d) => {
|
||||
this.hideFakeLoading(file, { type: 'error', tip: "上传失败", color: "red" });
|
||||
this.$g.downloadFile(d, `${file.name}-上传失败原因`, '.xls');
|
||||
this.$message.error(`${file.name}-上传失败,请查看失败原因`);
|
||||
// this.initList();//刷新列表
|
||||
//console.log('上传失败', response, file, fileList);
|
||||
}
|
||||
});
|
||||
} else if (response.success) {
|
||||
// 上传成功了
|
||||
this.hideFakeLoading(file, { type: 'success', tip: "上传成功", color: "green" });
|
||||
this.$message.success(`“${file.name}上传成功`);
|
||||
// this.initList();//刷新列表
|
||||
//console.log('上传成功', response, file, fileList);
|
||||
} else {
|
||||
// 其他失败原因
|
||||
this.hideFakeLoading(file, { type: 'error', tip: "上传失败", color: "red" });
|
||||
this.$message.error(response.msg);
|
||||
//console.log('上传失败', response, file, fileList);
|
||||
}
|
||||
},
|
||||
//上传失败
|
||||
uploadError(err, file, fileList) {
|
||||
this.hideFakeLoading(file, { type: 'error', tip: "上传失败", color: "red" });
|
||||
this.$message.error("上传失败");
|
||||
//console.log('上传失败', err, file, fileList);
|
||||
},
|
||||
|
||||
// 上传前
|
||||
beforeUpload(file) {
|
||||
/* const fileType = file.type.toLowerCase()
|
||||
@ -325,3 +460,12 @@ export default {
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
#myButton {
|
||||
width: 90px; /* 设置按钮长度为200像素 */
|
||||
}
|
||||
.container {
|
||||
display: flex; /* 设定为flex布局 */
|
||||
}
|
||||
</style>
|
||||
|
||||
|
133
src/components/FileUpload/sgCollapseBtn.vue
Normal file
133
src/components/FileUpload/sgCollapseBtn.vue
Normal file
@ -0,0 +1,133 @@
|
||||
<template>
|
||||
<div :class="$options.name" @click="show = !show" :placement="placement">
|
||||
<div class="collapse-btns">
|
||||
<div class="collapse-btn" v-if="show">
|
||||
<i class="el-icon-caret-top"></i>
|
||||
<div class="label">折叠{{ collapseLabel || expandLabel || `` }}</div>
|
||||
</div>
|
||||
<div class="collapse-btn" v-else>
|
||||
<i class="el-icon-caret-bottom"></i>
|
||||
<div class="label">展开{{ expandLabel || collapseLabel || `` }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "sgCollapseBtn",
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
show: false,
|
||||
};
|
||||
},
|
||||
props: ["data", "value", "collapseLabel", "expandLabel", "placement"],
|
||||
watch: {
|
||||
value: {
|
||||
handler(d) {
|
||||
this.show = d;
|
||||
},
|
||||
deep: true,
|
||||
immediate: true,
|
||||
},
|
||||
show(d) {
|
||||
this.$emit("input", d);
|
||||
},
|
||||
},
|
||||
created() {},
|
||||
mounted() {},
|
||||
computed: {},
|
||||
methods: {},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.sgCollapseBtn {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
/*禁止选中文本*/
|
||||
user-select: none;
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
background-color: white;
|
||||
cursor: pointer;
|
||||
|
||||
&[placement="bottom"] {
|
||||
position: absolute;
|
||||
top: revert;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.collapse-btns {
|
||||
width: 100%;
|
||||
$side: 20%; //渐变边界
|
||||
/*左右渐变遮罩(兼容IOS)*/
|
||||
-webkit-mask-image: linear-gradient(
|
||||
to right,
|
||||
transparent,
|
||||
white $side,
|
||||
white calc(100% - #{$side}),
|
||||
transparent
|
||||
);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
background-color: white;
|
||||
&::after {
|
||||
content: "";
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
background: linear-gradient(white, #e2f0ff);
|
||||
opacity: 0;
|
||||
transition: 1s ease !important;
|
||||
z-index: -1;
|
||||
}
|
||||
.collapse-btn {
|
||||
transition: 0.4s !important;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
transform: translateX(40%);
|
||||
i {
|
||||
transition: 0.4s !important;
|
||||
color: #d3dce6;
|
||||
}
|
||||
.label {
|
||||
transition: 0.2s !important;
|
||||
margin-left: 25px;
|
||||
color: #409eff;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:active,
|
||||
&:hover {
|
||||
.collapse-btns {
|
||||
&::after {
|
||||
opacity: 1;
|
||||
}
|
||||
.collapse-btn {
|
||||
transform: translateX(0%);
|
||||
|
||||
i {
|
||||
color: #409eff;
|
||||
}
|
||||
.label {
|
||||
margin-left: 5px;
|
||||
color: #409eff;
|
||||
opacity: 1;
|
||||
// width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
255
src/components/FileUpload/sgDragMove.vue
Normal file
255
src/components/FileUpload/sgDragMove.vue
Normal file
@ -0,0 +1,255 @@
|
||||
<template>
|
||||
<div :class="$options.name"></div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: "sgDragMove",
|
||||
data() {
|
||||
return {
|
||||
offset: { x: 0, y: 0 }, //偏移量
|
||||
style: { top: "0px", left: "0px" },
|
||||
canDragDom: null, //触发拖拽事件的物体
|
||||
moveDom: null, //可以移动的物体
|
||||
};
|
||||
},
|
||||
props: [
|
||||
"data", //可以被拖拽的元素数组(必选)
|
||||
/* data格式说明:
|
||||
[
|
||||
...
|
||||
{
|
||||
canDragDom: elementDOM,//可以拖拽的位置元素数组or单个元素
|
||||
moveDom: elementDOM,//拖拽同步移动的元素数组or单个元素
|
||||
},
|
||||
...
|
||||
]
|
||||
*/
|
||||
"disabled", //屏蔽
|
||||
"mousedownNearSide", //按下鼠标吸附边界
|
||||
"mousemoveNearSide", //移动鼠标吸附边界
|
||||
"mouseupNearSide", //弹起鼠标吸附边界
|
||||
"nearPadding", //距离边界多少像素自动吸附(mousedownNearSide||mousemoveNearSide||mouseupNearSide=true的时候才能生效),值可以是number或array类型,例如:是5代表[5,5,5,5],其中数组以此代表[上,右,下,左]
|
||||
"stopBoundary", //停靠边界距离,移动物体将按照这个值作为界限不再移出该范围(mousedownNearSide||mousemoveNearSide||mouseupNearSide=true的时候才能生效,值可以是number或array类型,例如:是5代表[5,5,5,5],其中数组以此代表[上,右,下,左]
|
||||
"cursor", //鼠标样式
|
||||
/*cursor格式说明:{
|
||||
grab:'default',//移入可拖拽区域的鼠标样式
|
||||
grabbing:'default',//拖拽过程中鼠标样式
|
||||
} */
|
||||
],
|
||||
watch: {
|
||||
data: {
|
||||
handler(newValue, oldValue) {
|
||||
newValue ? this.__addDragsEvents(newValue) : this.__removeDragsEvents(oldValue);
|
||||
},
|
||||
deep: true,
|
||||
immediate: true,
|
||||
},
|
||||
disabled: {
|
||||
handler(newValue, oldValue) {
|
||||
newValue ? this.__removeAllEvents() : this.__addAllEvents();
|
||||
},
|
||||
deep: true,
|
||||
immediate: true,
|
||||
},
|
||||
style: {
|
||||
handler(newValue, oldValue) {
|
||||
if (this.moveDom && newValue && Object.keys(newValue).length) {
|
||||
let d = newValue;
|
||||
Object.keys(d).forEach((k) => (this.moveDom.style[k] = d[k]));
|
||||
this.moveDom.style.right = "revert";
|
||||
this.moveDom.style.bottom = "revert";
|
||||
this.$emit(`getStyle`, d); //用这个方式抛出被移动物体的位置数据(解决多处修改同一DOM属性导致的冲突问题)
|
||||
}
|
||||
},
|
||||
deep: true, //深度监听
|
||||
immediate: true, //立即执行
|
||||
},
|
||||
},
|
||||
destroyed() {
|
||||
this.__removeAllEvents();
|
||||
},
|
||||
mounted() {
|
||||
this.$parent.$el.style.setProperty(
|
||||
"--sgDragMove-grab",
|
||||
(this.cursor || {}).grab || "grab"
|
||||
); //js往css传递局部参数
|
||||
this.$parent.$el.style.setProperty(
|
||||
"--sgDragMove-grabbing",
|
||||
(this.cursor || {}).grabbing || "grabbing"
|
||||
); //js往css传递局部参数
|
||||
},
|
||||
|
||||
methods: {
|
||||
__addAllEvents() {
|
||||
this.__addDragsEvents(this.data);
|
||||
},
|
||||
__removeAllEvents() {
|
||||
this.__removeWindowEvents();
|
||||
this.__removeDragsEvents(this.data);
|
||||
},
|
||||
__addWindowEvents() {
|
||||
this.__removeWindowEvents();
|
||||
addEventListener("mousemove", this.mousemove_window);
|
||||
addEventListener("mouseup", this.mouseup_window);
|
||||
},
|
||||
__removeWindowEvents() {
|
||||
removeEventListener("mousemove", this.mousemove_window);
|
||||
removeEventListener("mouseup", this.mouseup_window);
|
||||
},
|
||||
// 初始化需要拖拽的DIV
|
||||
__addDragsEvents(doms) {
|
||||
(doms || []).forEach((dom) => {
|
||||
this.__removeDraggedEvents(dom.canDragDom);
|
||||
this.__addDraggedEvents(dom.canDragDom);
|
||||
});
|
||||
},
|
||||
__removeDragsEvents(doms) {
|
||||
(doms || []).forEach((dom) => {
|
||||
this.__removeDraggedEvents(dom.canDragDom);
|
||||
});
|
||||
},
|
||||
__addDraggedEvents(dom) {
|
||||
dom.setAttribute("sgDragMove_grab", "ready");
|
||||
dom.addEventListener("dragstart", this.dragstart);
|
||||
dom.addEventListener("mousedown", this.mousedown);
|
||||
},
|
||||
__removeDraggedEvents(dom) {
|
||||
dom.removeEventListener("dragstart", this.dragstart);
|
||||
dom.removeEventListener("mousedown", this.mousedown);
|
||||
},
|
||||
dragstart(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
return false;
|
||||
},
|
||||
mousedown(e) {
|
||||
if (this.disabled) return this.mouseup_window(e);
|
||||
if (e.button === 2) return this.mouseup_window(e); //点击了鼠标右键
|
||||
this.canDragDom = e.currentTarget;
|
||||
this.moveDom = this.data.find((v) => v.canDragDom == this.canDragDom).moveDom;
|
||||
this.canDragDom.setAttribute("sgDragMove_grab", "down");
|
||||
this.moveDom.setAttribute("sgDragMove_move", "ready");
|
||||
let or = this.moveDom.getBoundingClientRect();
|
||||
this.offset = { x: e.clientX - or.x, y: e.clientY - or.y };
|
||||
(this.mousedownNearSide || this.mousedownNearSide === "") && this.nearSide();
|
||||
this.$emit("dragStart", this.getResult(e));
|
||||
this.__addWindowEvents();
|
||||
},
|
||||
setOffset(d) {
|
||||
this.offset = {
|
||||
...this.offset,
|
||||
...d,
|
||||
};
|
||||
},
|
||||
mousemove_window(e) {
|
||||
this.canDragDom.setAttribute("sgDragMove_grab", "down");
|
||||
this.moveDom.setAttribute("sgDragMove_move", "ing");
|
||||
let x = e.clientX - this.offset.x;
|
||||
let y = e.clientY - this.offset.y;
|
||||
this.style = { left: x + "px", top: y + "px" };
|
||||
this.style["transition-property"] = "left,top";
|
||||
this.style["transition-duration"] = "0s,0s";
|
||||
this.$nextTick(() => {
|
||||
(this.mousemoveNearSide || this.mousemoveNearSide === "") && this.nearSide();
|
||||
this.$emit("dragging", this.getResult(e));
|
||||
});
|
||||
},
|
||||
mouseup_window(e) {
|
||||
this.$emit("dragEnd", this.getResult(e));
|
||||
(this.mouseupNearSide || this.mouseupNearSide === "") && this.nearSide();
|
||||
this.offset = null;
|
||||
this.style = null;
|
||||
this.canDragDom.setAttribute("sgDragMove_grab", "ready");
|
||||
this.moveDom.setAttribute("sgDragMove_move", "end");
|
||||
setTimeout(() => {
|
||||
this.moveDom && this.moveDom.removeAttribute("sgDragMove_move");
|
||||
}, 100);
|
||||
this.canDragDom = null;
|
||||
this.moveDom = null;
|
||||
this.__removeWindowEvents();
|
||||
},
|
||||
// 自动吸附网页边界
|
||||
nearSide() {
|
||||
let arr = this.nearPadding ? JSON.parse(JSON.stringify(this.nearPadding)) : 0;
|
||||
Array.isArray(arr) || (arr = [...Array(4)].map((v) => arr));
|
||||
let [dis_top, dis_right, dis_bottom, dis_left] = arr;
|
||||
arr = this.stopBoundary ? JSON.parse(JSON.stringify(this.stopBoundary)) : 0;
|
||||
Array.isArray(arr) || (arr = [...Array(4)].map((v) => arr));
|
||||
let [
|
||||
stopBoundary_top,
|
||||
stopBoundary_right,
|
||||
stopBoundary_bottom,
|
||||
stopBoundary_left,
|
||||
] = arr;
|
||||
|
||||
let x = parseFloat(this.moveDom.style.left);
|
||||
let y = parseFloat(this.moveDom.style.top);
|
||||
let rect = this.moveDom.getBoundingClientRect();
|
||||
|
||||
let min_side_x = 0,
|
||||
min_x = min_side_x + dis_left,
|
||||
min_left = min_side_x + stopBoundary_left;
|
||||
let min_side_y = 0,
|
||||
min_y = min_side_y + dis_top,
|
||||
min_top = min_side_y + stopBoundary_top;
|
||||
x < min_x && (this.moveDom.style.left = `${min_left}px`);
|
||||
y < min_y && (this.moveDom.style.top = `${min_top}px`);
|
||||
|
||||
let max_side_x = innerWidth - rect.width,
|
||||
max_x = max_side_x - dis_right,
|
||||
max_right = max_side_x - stopBoundary_right;
|
||||
let max_side_y = innerHeight - rect.height,
|
||||
max_y = max_side_y - dis_bottom,
|
||||
max_bottom = max_side_y - stopBoundary_bottom;
|
||||
x > max_x && (this.moveDom.style.left = `${max_right}px`);
|
||||
y > max_y && (this.moveDom.style.top = `${max_bottom}px`);
|
||||
},
|
||||
getResult(e) {
|
||||
return {
|
||||
$event: e,
|
||||
canDragDom: this.canDragDom,
|
||||
moveDom: this.moveDom,
|
||||
canDragDomRect: this.canDragDom ? this.canDragDom.getBoundingClientRect() : null,
|
||||
moveDomRect: this.moveDom ? this.moveDom.getBoundingClientRect() : null,
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss">
|
||||
[sgDragMove_grab="ready"] {
|
||||
cursor: var(--sgDragMove-grab); //css获取js传递的参数
|
||||
|
||||
* {
|
||||
cursor: var(--sgDragMove-grab); //css获取js传递的参数
|
||||
}
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&:active {
|
||||
opacity: 0.9;
|
||||
}
|
||||
}
|
||||
|
||||
[sgDragMove_grab="down"] {
|
||||
cursor: var(--sgDragMove-grabbing); //css获取js传递的参数
|
||||
|
||||
* {
|
||||
cursor: var(--sgDragMove-grabbing); //css获取js传递的参数
|
||||
}
|
||||
}
|
||||
|
||||
[sgDragMove_move="ready"] {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
[sgDragMove_move="ing"] {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
[sgDragMove_move="end"] {
|
||||
transition: 0.1s;
|
||||
}
|
||||
</style>
|
281
src/components/FileUpload/sgDragSize.vue
Normal file
281
src/components/FileUpload/sgDragSize.vue
Normal file
@ -0,0 +1,281 @@
|
||||
<template>
|
||||
<div :class="$options.name" :disabled="disabled" draggable="false">
|
||||
<div :class="`resize-handle resize-${a}`" draggable="false" @mousedown.stop="clickResizeHandle(a)"
|
||||
@dblclick="dblclickResizeHandle(a, $event)" v-for="(a, i) in sizeIndexs" :key="i"></div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'sgDragSize',
|
||||
data() {
|
||||
return {
|
||||
tbHeight: 0,
|
||||
dragSizeIndex: '',
|
||||
originRect: {},
|
||||
dblclickOriginRect: {},
|
||||
sizeIndexs: [
|
||||
'top',
|
||||
'right',
|
||||
'bottom',
|
||||
'left',
|
||||
'top-left',
|
||||
'top-right',
|
||||
'bottom-left',
|
||||
'bottom-right',
|
||||
],
|
||||
}
|
||||
},
|
||||
props: [
|
||||
"disabled",//屏蔽
|
||||
"taskbarHeight",//任务栏高度
|
||||
"minWidth",//拖拽的最小宽度
|
||||
"minHeight",//拖拽的最小高度
|
||||
"maxWidth",//拖拽的最大宽度
|
||||
"maxHeight",//拖拽的最大高度
|
||||
],
|
||||
watch: {
|
||||
disabled: {
|
||||
handler(newValue, oldValue) {
|
||||
newValue && this.__removeWindowEvents();
|
||||
}, deep: true, immediate: true,
|
||||
},
|
||||
taskbarHeight: {
|
||||
handler(d) {
|
||||
this.tbHeight = d || 0;
|
||||
}, deep: true, immediate: true,
|
||||
},
|
||||
},
|
||||
destroyed() {
|
||||
this.__removeWindowEvents();
|
||||
},
|
||||
methods: {
|
||||
view_innerHeight() {
|
||||
return innerHeight - this.tbHeight;
|
||||
},
|
||||
clickResizeHandle(d) {
|
||||
this.dragSizeIndex = d;
|
||||
this.mousedown(d);
|
||||
},
|
||||
dblclickResizeHandle(d, $event) {
|
||||
let rect = this.$el.getBoundingClientRect();
|
||||
rect.width < innerWidth && rect.height < this.view_innerHeight() && (this.dblclickOriginRect = rect);
|
||||
this.dblResize(d, rect, $event);
|
||||
},
|
||||
__addWindowEvents() {
|
||||
this.__removeWindowEvents();
|
||||
addEventListener('mousemove', this.mousemove_window);
|
||||
addEventListener('mouseup', this.mouseup_window);
|
||||
},
|
||||
__removeWindowEvents() {
|
||||
removeEventListener('mousemove', this.mousemove_window);
|
||||
removeEventListener('mouseup', this.mouseup_window);
|
||||
},
|
||||
mousedown(e) {
|
||||
this.originRect = this.$el.getBoundingClientRect();
|
||||
this.originRect.bottomRightX = this.originRect.x + this.originRect.width;//右下角坐标.x
|
||||
this.originRect.bottomRightY = this.originRect.y + this.originRect.height;//右下角坐标.y
|
||||
this.$emit('dragStart', e);
|
||||
this.__addWindowEvents();
|
||||
},
|
||||
mousemove_window(e) {
|
||||
let { x, y } = e;
|
||||
let minWidth = this.minWidth || 50, minHeight = this.minHeight || 50, maxWidth = this.maxWidth || innerWidth, maxHeight = this.maxHeight || innerHeight;
|
||||
x < 0 && (x = 0), y < 0 && (y = 0), x > innerWidth && (x = innerWidth), y > this.view_innerHeight() && (y = this.view_innerHeight());
|
||||
let style = {};
|
||||
switch (this.dragSizeIndex) {
|
||||
case 'top-left':
|
||||
style.left = x;
|
||||
style.top = y;
|
||||
style.width = this.originRect.bottomRightX - x;
|
||||
style.width <= minWidth && (style.width = minWidth, style.left = this.originRect.bottomRightX - minWidth);
|
||||
style.height = this.originRect.bottomRightY - y;
|
||||
style.height <= minHeight && (style.height = minHeight, style.top = this.originRect.bottomRightY - minHeight);
|
||||
break;
|
||||
case 'top':
|
||||
style.left = this.originRect.x;
|
||||
style.top = y;
|
||||
style.width = this.originRect.width;
|
||||
style.height = this.originRect.bottomRightY - y;
|
||||
style.height <= minHeight && (style.height = minHeight, style.top = this.originRect.bottomRightY - minHeight);
|
||||
break;
|
||||
case 'top-right':
|
||||
style.left = this.originRect.x;
|
||||
style.top = y;
|
||||
style.width = x - this.originRect.x;
|
||||
style.width <= minWidth && (style.width = minWidth, style.left = this.originRect.x);
|
||||
style.height = this.originRect.bottomRightY - y;
|
||||
style.height <= minHeight && (style.height = minHeight, style.top = this.originRect.bottomRightY - minHeight);
|
||||
break;
|
||||
case 'left':
|
||||
style.left = x;
|
||||
style.top = this.originRect.y;
|
||||
style.width = this.originRect.bottomRightX - x;
|
||||
style.width <= minWidth && (style.width = minWidth, style.left = this.originRect.bottomRightX - minWidth);
|
||||
style.height = this.originRect.height;
|
||||
break;
|
||||
case 'right':
|
||||
style.left = this.originRect.x;
|
||||
style.top = this.originRect.y;
|
||||
style.width = x - this.originRect.x;
|
||||
style.width <= minWidth && (style.width = minWidth, style.left = this.originRect.x);
|
||||
style.height = this.originRect.height;
|
||||
break;
|
||||
case 'bottom-left':
|
||||
style.left = x;
|
||||
style.top = this.originRect.y;
|
||||
style.width = this.originRect.bottomRightX - x;
|
||||
style.width <= minWidth && (style.width = minWidth, style.left = this.originRect.bottomRightX - minWidth);
|
||||
style.height = y - this.originRect.y;
|
||||
style.height <= minHeight && (style.height = minHeight, style.top = this.originRect.y);
|
||||
break;
|
||||
case 'bottom':
|
||||
style.left = this.originRect.x;
|
||||
style.top = this.originRect.y;
|
||||
style.width = this.originRect.width;
|
||||
style.height = y - this.originRect.y;
|
||||
style.height <= minHeight && (style.height = minHeight, style.top = this.originRect.y);
|
||||
break;
|
||||
case 'bottom-right':
|
||||
style.left = this.originRect.x;
|
||||
style.top = this.originRect.y;
|
||||
style.width = x - this.originRect.x;
|
||||
style.width <= minWidth && (style.width = minWidth, style.left = this.originRect.x);
|
||||
style.height = y - this.originRect.y;
|
||||
style.height <= minHeight && (style.height = minHeight, style.top = this.originRect.y);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
style.width > maxWidth && (style.width = maxWidth);
|
||||
style.height > maxHeight && (style.height = maxHeight);
|
||||
Object.keys(style).forEach(k => style[k] = `${style[k]}px`);
|
||||
style['transition-property'] = 'width,height';
|
||||
style['transition-duration'] = '0,0';
|
||||
this.$emit('dragging', { e, style });
|
||||
},
|
||||
dblResize(d, rect, e) {
|
||||
let style = {};
|
||||
switch (d) {
|
||||
case 'top-left':
|
||||
break;
|
||||
case 'top':
|
||||
case 'bottom':
|
||||
style.left = this.originRect.x;
|
||||
style.top = rect.height >= this.view_innerHeight() ? this.dblclickOriginRect.y : 0;
|
||||
style.width = this.originRect.width;
|
||||
style.height = rect.height >= this.view_innerHeight() ? this.dblclickOriginRect.height : this.view_innerHeight();
|
||||
break;
|
||||
case 'top-right':
|
||||
break;
|
||||
case 'left':
|
||||
case 'right':
|
||||
style.left = rect.width >= innerWidth ? this.dblclickOriginRect.x : 0;
|
||||
style.top = this.originRect.y;
|
||||
style.width = rect.width >= innerWidth ? this.dblclickOriginRect.width : innerWidth;
|
||||
style.height = this.originRect.height;
|
||||
break;
|
||||
case 'bottom-left':
|
||||
break;
|
||||
case 'bottom-right':
|
||||
break;
|
||||
default:
|
||||
}
|
||||
Object.keys(style).forEach(k => style[k] = `${style[k]}px`);
|
||||
style['transition-property'] = 'width,height';
|
||||
style['transition-duration'] = '0.1s,0.1s';
|
||||
this.$emit('dragging', { e, style });
|
||||
},
|
||||
mouseup_window(e) {
|
||||
this.$emit('dragEnd', e);
|
||||
this.__removeWindowEvents();
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.sgDragSize {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
pointer-events: none;
|
||||
|
||||
.resize-handle {
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
display: block;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
&[disabled] {
|
||||
.resize-handle {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.resize-top {
|
||||
cursor: n-resize;
|
||||
top: -3px;
|
||||
left: 0px;
|
||||
height: 7px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.resize-right {
|
||||
cursor: e-resize;
|
||||
right: -3px;
|
||||
top: 0px;
|
||||
width: 7px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.resize-bottom {
|
||||
cursor: s-resize;
|
||||
bottom: -3px;
|
||||
left: 0px;
|
||||
height: 7px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.resize-left {
|
||||
cursor: w-resize;
|
||||
left: -3px;
|
||||
top: 0px;
|
||||
width: 7px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.resize-top-right {
|
||||
cursor: ne-resize;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
right: -8px;
|
||||
top: -8px;
|
||||
}
|
||||
|
||||
.resize-bottom-right {
|
||||
cursor: se-resize;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
right: -8px;
|
||||
bottom: -8px;
|
||||
background: url('/static/img/desktop/Windows7/sgDragSize/resize_corner.png') no-repeat;
|
||||
}
|
||||
|
||||
.resize-bottom-left {
|
||||
cursor: sw-resize;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
left: -8px;
|
||||
bottom: -8px;
|
||||
}
|
||||
|
||||
.resize-top-left {
|
||||
cursor: nw-resize;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
left: -8px;
|
||||
top: -8px;
|
||||
}
|
||||
}
|
||||
</style>
|
999
src/components/FileUpload/sgUploadTray.vue
Normal file
999
src/components/FileUpload/sgUploadTray.vue
Normal file
@ -0,0 +1,999 @@
|
||||
<template>
|
||||
<div :class="$options.name" :show="show" :size="size" :style="style">
|
||||
<div class="upload-list-tray">
|
||||
<!-- 托盘头部 -->
|
||||
<div class="header" ref="header" @dblclick.stop.prevent="dblclickHeader">
|
||||
<div class="left">
|
||||
<div class="title">
|
||||
<span class="upload-count" slot="reference">上传队列</span>
|
||||
<div class="upload-info" v-if="liveSpeed && liveSpeed > 0">
|
||||
<el-divider :direction="`vertical`" />
|
||||
<div class="info-item live-speed">
|
||||
<label>速度</label>
|
||||
<span>{{ $g.getSize(liveSpeed) }}/s</span>
|
||||
</div>
|
||||
<div class="info-item taken-time" v-if="takenTime && takenTime > 0">
|
||||
<label>已耗时</label>
|
||||
<span>{{
|
||||
$g.date.toHourMinuteSecondByMillisecond(takenTime * 1000, {
|
||||
zh: true,
|
||||
hideMilliSecond: true,
|
||||
hideZero: true,
|
||||
})
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="info-item remain-time" v-if="remainTime && remainTime > 0">
|
||||
<label>剩余</label
|
||||
><span>{{
|
||||
$g.date.toHourMinuteSecondByMillisecond(remainTime * 1000, {
|
||||
zh: true,
|
||||
hideMilliSecond: true,
|
||||
hideZero: true,
|
||||
})
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right" @mousedown.stop>
|
||||
<!-- 控制文件的图标按钮 -->
|
||||
<div class="file-btns" v-if="showDelSuccessIconBtn || showErrorIconBtn">
|
||||
<div
|
||||
class="icon-btn"
|
||||
v-if="showDelSuccessIconBtn"
|
||||
@click.stop="clearAllSuccessFile"
|
||||
title="清除所有已经成功的上传记录"
|
||||
>
|
||||
<i class="el-icon-delete" style="color: #67c23a"></i>
|
||||
</div>
|
||||
<div
|
||||
class="icon-btn"
|
||||
v-if="showErrorIconBtn"
|
||||
@click.stop="clearAllErrorFile"
|
||||
title="清除所有失败的上传记录"
|
||||
>
|
||||
<i class="el-icon-delete-solid" style="color: #f56c6c"></i>
|
||||
</div>
|
||||
<div
|
||||
class="icon-btn"
|
||||
v-if="showErrorIconBtn"
|
||||
@click.stop="uploadAllErrorFile"
|
||||
title="重新上传所有失败的文件"
|
||||
>
|
||||
<i class="el-icon-upload2" style="color: #409eff"></i>
|
||||
</div>
|
||||
<template v-if="uploadList.length > maxShowUploadFileCount">
|
||||
<div
|
||||
class="icon-btn"
|
||||
v-if="expandAllUploadList"
|
||||
@click.stop="expandAllUploadList = false"
|
||||
title="折叠只显示前10条上传记录"
|
||||
>
|
||||
<i class="el-icon-folder" style="color: #409eff"></i>
|
||||
</div>
|
||||
<el-tooltip
|
||||
v-else
|
||||
:content="`请谨慎展开列表,这将导致您的网页很卡!`"
|
||||
:effect="`dark`"
|
||||
:enterable="false"
|
||||
:placement="`top-start`"
|
||||
:popper-class="`sg-el-tooltip`"
|
||||
:transition="`none`"
|
||||
:disabled="uploadList.length < 200"
|
||||
>
|
||||
<div
|
||||
class="icon-btn"
|
||||
@click.stop="expandAllUploadList = true"
|
||||
title="展开所有上传记录"
|
||||
>
|
||||
<i class="el-icon-folder-opened" style="color: #409eff"></i>
|
||||
</div>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</div>
|
||||
<!-- 控制托盘的图标按钮 -->
|
||||
<div class="tray-btns">
|
||||
<div
|
||||
class="icon-btn"
|
||||
v-if="size !== 'lg' && showRightBottomBtn"
|
||||
@click.stop="toRightBottomPosition"
|
||||
title="回到原来的位置"
|
||||
>
|
||||
<i class="el-icon-bottom-right"></i>
|
||||
</div>
|
||||
<div
|
||||
class="icon-btn"
|
||||
v-if="size !== 'mn'"
|
||||
@click.stop="size = 'mn'"
|
||||
title="最小化"
|
||||
>
|
||||
<i class="el-icon-minus"></i>
|
||||
</div>
|
||||
<div
|
||||
class="icon-btn"
|
||||
v-if="size !== 'md'"
|
||||
@click.stop="size = 'md'"
|
||||
title="还原"
|
||||
>
|
||||
<i :class="size === 'lg' ? 'el-icon-copy-document' : 'el-icon-d-caret'"></i>
|
||||
</div>
|
||||
<div
|
||||
class="icon-btn"
|
||||
v-if="size !== 'lg'"
|
||||
@click.stop="size = 'lg'"
|
||||
title="全屏"
|
||||
>
|
||||
<i class="el-icon-full-screen"></i>
|
||||
</div>
|
||||
<div class="icon-btn" @click.stop="close">
|
||||
<i class="el-icon-close"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 上传中的文件列表 -->
|
||||
<div class="upload-file-list">
|
||||
<ul>
|
||||
<li
|
||||
v-for="(a, i) in expandAllUploadList ? uploadList : uploadList.slice(0, 10)"
|
||||
:key="i"
|
||||
:title="
|
||||
a.size > 1024 * 1024 * 500
|
||||
? `超大文件上传中,请耐心等待,切勿关闭或刷新浏览器!`
|
||||
: ''
|
||||
"
|
||||
>
|
||||
<div class="left">
|
||||
<div class="icon-btns">
|
||||
<el-button
|
||||
title="移出上传队列"
|
||||
:show="a.status === 'error'"
|
||||
class="remove-icon-btn icon-btn"
|
||||
type="danger"
|
||||
icon="el-icon-delete-solid"
|
||||
size="mini"
|
||||
plain
|
||||
circle
|
||||
@click.stop="removeUploadFile(a)"
|
||||
></el-button>
|
||||
</div>
|
||||
<!-- 动画加载旋转 -->
|
||||
<div
|
||||
class="fileLoading"
|
||||
v-loading="a.percent < 100"
|
||||
v-if="a.percent < 100 && a.status !== 'error'"
|
||||
></div>
|
||||
<!-- 上传成功icon -->
|
||||
<div class="loadingSuccessIcon" v-if="a.percent === 100">
|
||||
<i class="el-icon-success" style="color: #67c23a"></i>
|
||||
</div>
|
||||
<!-- 上传失败icon -->
|
||||
<div class="loadingEorrorIcon" v-if="a.status === 'error'">
|
||||
<i class="el-icon-error" style="color: #f56c6c"></i>
|
||||
</div>
|
||||
<span class="name" :title="a.filePath || a.name">
|
||||
{{ a.filePath || a.name }}
|
||||
<!-- {{ a.filePath && a.filePath.includes(`/`) ? `[路径:${a.filePath}]` : "" }} -->
|
||||
</span>
|
||||
<el-tag class="size" size="mini"
|
||||
>{{ $g.getSize(a.size * (a.percent / 100)) }}/{{
|
||||
$g.getSize(a.size)
|
||||
}}</el-tag
|
||||
>
|
||||
<!-- <el-progress class="progress" :percentage="a.percent"></el-progress> -->
|
||||
<el-progress
|
||||
class="progress"
|
||||
style="width: 100%"
|
||||
type="line"
|
||||
:percentage="parseInt(a.percent)"
|
||||
:show-text="true"
|
||||
:stroke-width="10"
|
||||
:text-inside="false"
|
||||
:color="'#409EFF'"
|
||||
:define-back-color="'#eee'"
|
||||
/>
|
||||
</div>
|
||||
<div class="right">
|
||||
<span class="tip" :color="a.color">{{ a.tip }}</span>
|
||||
<div class="icon-btns">
|
||||
<el-button
|
||||
:show="a.status === 'error'"
|
||||
title="重新上传"
|
||||
class="upload-icon-btn icon-btn"
|
||||
type="primary"
|
||||
icon="el-icon-upload2"
|
||||
size="mini"
|
||||
plain
|
||||
circle
|
||||
@click.stop="startUploadFile(a)"
|
||||
></el-button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<!-- 折叠按钮 -->
|
||||
<sgCollapseBtn
|
||||
style="z-index: 1"
|
||||
:collapseLabel="`上传记录`"
|
||||
v-model="expandAllUploadList"
|
||||
v-if="uploadList.length > maxShowUploadFileCount"
|
||||
/>
|
||||
<div class="footer">
|
||||
<div class="text" v-html="popoverContent"></div>
|
||||
<div class="progress" v-if="uploadList.length > 1 && totalPercentage < 100">
|
||||
<label>。总进度</label>
|
||||
<el-progress
|
||||
style="width: 100%"
|
||||
type="line"
|
||||
:percentage="parseInt(totalPercentage)"
|
||||
:show-text="true"
|
||||
:stroke-width="10"
|
||||
:text-inside="false"
|
||||
:color="'#409EFF'"
|
||||
:define-back-color="'#eee'"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 拖拽移动窗体 -->
|
||||
<sgDragMove
|
||||
:data="dragMoveDoms"
|
||||
:cursor="{
|
||||
grab: 'default',
|
||||
grabbing: 'default',
|
||||
}"
|
||||
nearPadding="10"
|
||||
:disabled="size === 'lg' && disabledDragMove"
|
||||
@dragStart="$emit(`dragStart`, dragMoveDoms)"
|
||||
@dragging="
|
||||
showRightBottomBtn = true;
|
||||
$emit(`dragging`, dragMoveDoms);
|
||||
"
|
||||
@dragEnd="$emit(`dragEnd`, dragMoveDoms)"
|
||||
mousemoveNearSide
|
||||
/>
|
||||
|
||||
<!-- 拖拽改变窗体尺寸 -->
|
||||
<sgDragSize
|
||||
v-if="resizeable_"
|
||||
:disabled="size === 'lg'"
|
||||
@dragStart="disabledDragMove = true"
|
||||
@dragging="draggingSize"
|
||||
@dragEnd="disabledDragMove = false"
|
||||
:minWidth="minWidth"
|
||||
:minHeight="minHeight"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import sgCollapseBtn from "./sgCollapseBtn";
|
||||
import sgDragMove from "./sgDragMove";
|
||||
import sgDragSize from "./sgDragSize";
|
||||
export default {
|
||||
name: "sgUploadTray",
|
||||
components: {
|
||||
sgCollapseBtn,
|
||||
sgDragMove,
|
||||
sgDragSize,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
maxShowUploadFileCount: 10, //默认展示上传数量
|
||||
expandAllUploadList: false, //默认折叠
|
||||
|
||||
minWidth: 800,
|
||||
minHeight: 40,
|
||||
style_bk: null,
|
||||
style: {},
|
||||
resizeable_: true,
|
||||
disabledDragMove: false, //屏蔽移动
|
||||
show: false,
|
||||
showRightBottomBtn: false,
|
||||
size: "md", //lg全屏、md普通、mn最小
|
||||
uploadList: [],
|
||||
dragMoveDoms: [
|
||||
/* {
|
||||
canDragDom: elementDOM,//可以拖拽的位置元素
|
||||
moveDom: elementDOM,//拖拽同步移动的元素
|
||||
} */
|
||||
], //可以拖拽移动的物体
|
||||
|
||||
lastUploadedTotalSize: 0, //记录上次已经下载完成的总大小
|
||||
liveSpeed: 0, //瞬时下载速度(单位B)
|
||||
takenTime: 0, //已耗时
|
||||
remainTime: 0, //剩余下载时长
|
||||
interval: null,
|
||||
second: 1, //轮训间隔秒钟
|
||||
|
||||
successFileList: [], //成功文件列表
|
||||
errorFileList: [], //失败文件列表
|
||||
remainFileList: [], //剩余文件列表
|
||||
};
|
||||
},
|
||||
props: ["data", "value", "resizeable", "position"],
|
||||
watch: {
|
||||
value: {
|
||||
handler(d) {
|
||||
this.show = d;
|
||||
},
|
||||
deep: true,
|
||||
immediate: true,
|
||||
},
|
||||
show: {
|
||||
handler(d) {
|
||||
this.$emit(`input`, d);
|
||||
},
|
||||
deep: true,
|
||||
immediate: true,
|
||||
},
|
||||
data: {
|
||||
handler(d) {
|
||||
this.uploadList = d || [];
|
||||
},
|
||||
deep: true,
|
||||
immediate: true,
|
||||
},
|
||||
uploadList: {
|
||||
handler(newValue, oldValue) {
|
||||
if (newValue && Object.keys(newValue).length) {
|
||||
this.interval || this.startUploadCalcLiveSpeed();
|
||||
this.successFileList = newValue.filter((v) => v.percent == 100);
|
||||
this.errorFileList = newValue.filter((v) => v.status === "error");
|
||||
this.remainFileList = newValue.filter(
|
||||
(v) => v.status !== "error" && v.status !== "success"
|
||||
);
|
||||
} else {
|
||||
this.successFileList = [];
|
||||
this.errorFileList = [];
|
||||
this.remainFileList = [];
|
||||
}
|
||||
},
|
||||
deep: true, //深度监听
|
||||
immediate: true, //立即执行
|
||||
},
|
||||
resizeable: {
|
||||
handler(newValue, oldValue) {
|
||||
this.resizeable_ = newValue === "" || newValue;
|
||||
},
|
||||
deep: true, //深度监听
|
||||
immediate: true, //立即执行
|
||||
},
|
||||
size: {
|
||||
handler(newValue, oldValue) {
|
||||
switch (newValue) {
|
||||
case "lg":
|
||||
case "mn":
|
||||
this.style_bk = JSON.parse(JSON.stringify(this.style));
|
||||
delete this.style.width, delete this.style.height;
|
||||
break;
|
||||
case "md":
|
||||
this.style_bk && (this.style = JSON.parse(JSON.stringify(this.style_bk)));
|
||||
break;
|
||||
}
|
||||
},
|
||||
deep: true, //深度监听
|
||||
immediate: true, //立即执行
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
showDelSuccessIconBtn(d) {
|
||||
return this.uploadList.some((v) => v.percent == 100);
|
||||
},
|
||||
showErrorIconBtn(d) {
|
||||
return this.uploadList.some((v) => v.status == "error");
|
||||
},
|
||||
popoverContent(d) {
|
||||
let r = [];
|
||||
this.successFileList.length &&
|
||||
r.push(
|
||||
`已上传成功<span style="color: #67C23A;">${this.successFileList.length}</span>个`
|
||||
);
|
||||
this.errorFileList.length &&
|
||||
r.push(`失败<span style="color: #F56C6C;">${this.errorFileList.length}</span>个`);
|
||||
this.remainFileList.length &&
|
||||
r.push(
|
||||
`剩余<span style="color: #409EFF;">${this.remainFileList.length}</span>个`
|
||||
);
|
||||
if (this.uploadList.length) {
|
||||
return `共计${this.uploadList.length}个文件,${
|
||||
r.length ? `${r.join(",")}文件` : ``
|
||||
}`;
|
||||
} else {
|
||||
return `暂无待上传文件`;
|
||||
}
|
||||
},
|
||||
// 总体进度
|
||||
totalPercentage() {
|
||||
if (this.uploadList.length) {
|
||||
return parseFloat(
|
||||
((this.successFileList.length / this.uploadList.length) * 100).toFixed(2)
|
||||
);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
// 真正上传中的文件对象数组
|
||||
uploadingFiles(d) {
|
||||
let uploadingFiles_ = (this.uploadList || []).filter(
|
||||
(v) => v.percent < 100 && v.status !== "error" && v.status !== "success"
|
||||
);
|
||||
if (uploadingFiles_.length) {
|
||||
this.$emit(`changeUploadingListClose`, {
|
||||
path: this.position,
|
||||
close: this.close,
|
||||
});
|
||||
} else {
|
||||
this.$emit(`changeUploadingListClose`, null);
|
||||
}
|
||||
return uploadingFiles_;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.$el.style.setProperty("--minWidth", `${this.minWidth}px`); //js往css传递局部参数
|
||||
this.$el.style.setProperty("--minHeight", `${this.minHeight}px`); //js往css传递局部参数
|
||||
this.dragMoveDoms = [
|
||||
{
|
||||
canDragDom: this.$refs.header, //托盘的头部可以拖拽
|
||||
moveDom: this.$el, //拖拽的时候,整个上传列表一起跟随移动
|
||||
},
|
||||
];
|
||||
},
|
||||
destroyed() {
|
||||
clearInterval(this.interval);
|
||||
},
|
||||
methods: {
|
||||
// 开始计算瞬时下载速度
|
||||
startUploadCalcLiveSpeed() {
|
||||
clearInterval(this.interval);
|
||||
this.interval = setInterval(() => {
|
||||
this.calcLiveSpeed();
|
||||
}, 1000 * this.second);
|
||||
},
|
||||
// 结束计算瞬时下载速度
|
||||
endUploadCalcLiveSpeed(d) {
|
||||
clearInterval(this.interval);
|
||||
this.interval = null;
|
||||
this.liveSpeed = 0;
|
||||
this.takenTime = 0;
|
||||
this.remainTime = 0;
|
||||
},
|
||||
// 没有上传进程文件才结束计算速度
|
||||
ifNoUploadingFile_EndCalcLiveSpeed(d) {
|
||||
this.uploadingFiles.length || this.endUploadCalcLiveSpeed();
|
||||
},
|
||||
// 计算瞬时下载速度
|
||||
calcLiveSpeed(d) {
|
||||
this.takenTime++;
|
||||
let uploadList = this.uploadList;
|
||||
if (uploadList.length) {
|
||||
let totalSize = uploadList.reduce(
|
||||
(prevResult, current) => prevResult + current.size,
|
||||
0
|
||||
); //求和需要上传的文件总大小
|
||||
let uploadedTotalSize = uploadList.reduce(
|
||||
(prevResult, current) => prevResult + current.size * (0.01 * current.percent),
|
||||
0
|
||||
); //求和已经上传的文件总大小
|
||||
let remainTotalSize = totalSize - uploadedTotalSize; //剩余需要上传的文件
|
||||
if (this.lastUploadedTotalSize) {
|
||||
this.liveSpeed = (uploadedTotalSize - this.lastUploadedTotalSize) / this.second; //瞬时速度
|
||||
this.remainTime = remainTotalSize / this.liveSpeed; //瞬时剩余时长
|
||||
} else {
|
||||
this.liveSpeed = 0;
|
||||
}
|
||||
this.lastUploadedTotalSize = uploadedTotalSize; //记录本次已经上传的总大小
|
||||
} else {
|
||||
this.endUploadCalcLiveSpeed();
|
||||
}
|
||||
},
|
||||
clearAllSuccessFile() {
|
||||
let successFileList = this.uploadList.filter((v) => v.percent == 100);
|
||||
if (successFileList.length === 0)
|
||||
return this.$message(`暂无可以移除的成功记录,请稍后再试!`);
|
||||
this.$emit(`clearAllSuccessFile`, successFileList);
|
||||
this.$nextTick(() => {
|
||||
successFileList.forEach((file) => {
|
||||
file.removeFile(); //移除原始队列
|
||||
});
|
||||
this.uploadList = this.uploadList.filter((v) => v.percent < 100);
|
||||
});
|
||||
},
|
||||
clearAllErrorFile() {
|
||||
let errorFileList = this.uploadList.filter((v) => v.status == "error");
|
||||
if (errorFileList.length === 0)
|
||||
return this.$message(`暂无可以移除的失败记录,请稍后再试!`);
|
||||
this.$emit(`clearAllErrorFile`, errorFileList);
|
||||
this.$nextTick(() => {
|
||||
errorFileList.forEach((file) => {
|
||||
file.removeFile(); //移除原始队列
|
||||
});
|
||||
this.uploadList = this.uploadList.filter((v) => v.status !== "error");
|
||||
});
|
||||
},
|
||||
uploadAllErrorFile(d) {
|
||||
let errorFileList = this.uploadList.filter((v) => v.status == "error");
|
||||
errorFileList.forEach((fileData) => this.startUploadFile(fileData));
|
||||
if (errorFileList.length === 0) return this.$message(`暂无失败记录,请稍后再试!`);
|
||||
this.$emit(`uploadAllErrorFile`, errorFileList);
|
||||
},
|
||||
draggingSize({ style }) {
|
||||
this.disabledDragMove = true;
|
||||
this.style = style;
|
||||
},
|
||||
toRightBottomPosition(d) {
|
||||
this.showRightBottomBtn = false;
|
||||
let rect = this.$el.getBoundingClientRect();
|
||||
this.$el.style.left = `${innerWidth - rect.width}px`;
|
||||
this.$el.style.top = `${innerHeight - rect.height}px`;
|
||||
// 用下面的写法会清除掉setProperty属性
|
||||
/* this.$el.style = {
|
||||
left: innerWidth - rect.width + "px",
|
||||
top: innerHeight - rect.height + "px",
|
||||
}; */
|
||||
},
|
||||
dblclickHeader(d) {
|
||||
switch (this.size) {
|
||||
case "lg":
|
||||
this.size = "md";
|
||||
break;
|
||||
case "md":
|
||||
this.size = "mn";
|
||||
break;
|
||||
case "mn":
|
||||
this.size = "md";
|
||||
break;
|
||||
default:
|
||||
}
|
||||
},
|
||||
removeAllFilesFromList() {
|
||||
this.uploadList.forEach((file) => {
|
||||
file.removeFile(); //移除原始队列
|
||||
});
|
||||
this.uploadList = []; //移除托盘队列
|
||||
},
|
||||
removeFileFromList(d) {
|
||||
let file = this.uploadList.find((v) => v.uid == d.uid);
|
||||
file.removeFile(); //移除原始队列
|
||||
this.uploadList.splice(
|
||||
this.uploadList.findIndex((v) => v.uid == d.uid),
|
||||
1
|
||||
); //移除托盘队列
|
||||
},
|
||||
// 重新上传失败的记录
|
||||
startUploadFile(fileData) {
|
||||
fileData.startUpload({ handleTrigger: true });
|
||||
},
|
||||
// 移出某一个队列文件
|
||||
removeUploadFile(d) {
|
||||
if (d.status === "error" || d.status === "success") {
|
||||
this.removeFileFromList(d);
|
||||
} else if (d.percent < 100) {
|
||||
this.$confirm(`${d.name}正在上传中,确定要取消吗?`, `提示`, {
|
||||
dangerouslyUseHTMLString: true,
|
||||
confirmButtonText: `确定`,
|
||||
cancelButtonText: `取消`,
|
||||
type: "warning",
|
||||
})
|
||||
.then(() => {
|
||||
this.$emit(`stopUpload`, [d]);
|
||||
this.$nextTick(() => {
|
||||
this.removeFileFromList(d);
|
||||
this.ifNoUploadingFile_EndCalcLiveSpeed();
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
} else {
|
||||
this.removeFileFromList(d);
|
||||
}
|
||||
},
|
||||
// 关闭托盘
|
||||
close({ cb } = {}) {
|
||||
let stopUploadList = this.uploadingFiles;
|
||||
if (stopUploadList.length) {
|
||||
this.$confirm(`您还有正在上传中的文件,确定要取消吗?`, `提示`, {
|
||||
dangerouslyUseHTMLString: true,
|
||||
confirmButtonText: `确定`,
|
||||
cancelButtonText: `取消`,
|
||||
type: "warning",
|
||||
})
|
||||
.then(() => {
|
||||
this.show = false;
|
||||
this.$emit(`stopUpload`, stopUploadList);
|
||||
this.$nextTick(() => {
|
||||
this.endUploadCalcLiveSpeed();
|
||||
this.removeAllFilesFromList();
|
||||
cb && cb(stopUploadList); //完成后回调
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
} else {
|
||||
this.show = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.sgUploadTray {
|
||||
$headerHeight: 40px; //头部高度
|
||||
$footerHeight: 40px; //底部统计上传进度高度
|
||||
$collapseBtnHeight: 30px; //折叠按钮高度
|
||||
$minWidth: var(--minWidth); //托盘最小宽度
|
||||
$minHeight: var(--minHeight); //托盘最小高度
|
||||
$loadingWidth: 30px; //加载动画宽度(旋转全全)
|
||||
$rightWidth: 200px; //右侧宽度
|
||||
$sizeWidth: 200px; //文件大小宽度宽度
|
||||
$progressWidth: 100px; //进度条宽度
|
||||
$tipWidth: 100px; //提示文本宽度
|
||||
// ----------------------------------------
|
||||
z-index: 2001; //根据情况自己拿捏(太大了会遮住element的其他弹窗组件),v-loading默认是2000的z-index
|
||||
user-select: none;
|
||||
position: fixed;
|
||||
right: 10px;
|
||||
bottom: 10px;
|
||||
width: $minWidth;
|
||||
background-color: white;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #eee;
|
||||
font-size: 14px;
|
||||
display: none;
|
||||
|
||||
&[show] {
|
||||
display: block;
|
||||
}
|
||||
|
||||
&[size="lg"] {
|
||||
left: 0 !important;
|
||||
top: 0 !important;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
transition: none;
|
||||
.upload-file-list {
|
||||
max-height: calc(100vh - 60px) !important;
|
||||
}
|
||||
}
|
||||
|
||||
&[size="md"] {
|
||||
width: $minWidth;
|
||||
height: revert;
|
||||
}
|
||||
|
||||
&[size="mn"] {
|
||||
width: $minWidth;
|
||||
height: $minHeight;
|
||||
}
|
||||
|
||||
.upload-list-tray {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
padding-bottom: 20px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
|
||||
.header {
|
||||
flex-shrink: 0;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
width: 100%;
|
||||
height: $headerHeight;
|
||||
box-sizing: border-box;
|
||||
padding: 10px 20px;
|
||||
/*从上往下线性渐变背景*/
|
||||
background: linear-gradient(#409eff11, white);
|
||||
color: #409eff;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-grow: 1;
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: nowrap;
|
||||
.upload-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: black;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
font-weight: normal;
|
||||
.info-item {
|
||||
margin-right: 5px;
|
||||
&:last-of-type {
|
||||
margin-right: 0;
|
||||
}
|
||||
span {
|
||||
font-family: DIN-Light;
|
||||
color: #409eff;
|
||||
}
|
||||
&.live-speed {
|
||||
span {
|
||||
font-family: DIN-Black;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.icon-btns {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: nowrap;
|
||||
.icon-btn {
|
||||
cursor: pointer;
|
||||
margin-right: 5px;
|
||||
&:last-of-type {
|
||||
margin-right: 0;
|
||||
}
|
||||
i {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
opacity: 0.618;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
flex-shrink: 0;
|
||||
pointer-events: auto;
|
||||
|
||||
.icon-btn {
|
||||
margin-left: 10px;
|
||||
cursor: pointer;
|
||||
|
||||
i {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
opacity: 0.618;
|
||||
}
|
||||
&:first-of-type {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.file-btns {
|
||||
margin-left: 10px;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: flex-end;
|
||||
box-sizing: border-box;
|
||||
padding: 0 10px;
|
||||
border-right: 1px solid #eee;
|
||||
}
|
||||
.tray-btns {
|
||||
margin-left: 10px;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.upload-file-list {
|
||||
width: 100%;
|
||||
flex-grow: 1;
|
||||
overflow-y: auto;
|
||||
height: max-content;
|
||||
max-height: calc(
|
||||
100vh - #{$headerHeight} - #{$footerHeight} - #{$collapseBtnHeight} - 40px
|
||||
);
|
||||
box-sizing: border-box;
|
||||
padding: 0 20px;
|
||||
|
||||
ul {
|
||||
width: 100%;
|
||||
|
||||
li {
|
||||
line-height: 1.6;
|
||||
box-sizing: border-box;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
|
||||
.left {
|
||||
width: calc(100% - #{$rightWidth});
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-grow: 1;
|
||||
flex-shrink: 0;
|
||||
// 上传队列记录左侧侧操作按钮
|
||||
.icon-btns {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
.icon-btn {
|
||||
display: none;
|
||||
&[show] {
|
||||
margin-right: 15px;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
.fileLoading {
|
||||
flex-shrink: 0;
|
||||
width: 30px;
|
||||
margin-right: 5px;
|
||||
height: 0;
|
||||
transform: scale(0.5);
|
||||
}
|
||||
.loadingSuccessIcon,
|
||||
.loadingEorrorIcon {
|
||||
margin-right: 5px;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.name {
|
||||
text-align: left;
|
||||
margin-right: 10px;
|
||||
width: calc(
|
||||
100% - #{$loadingWidth} - #{$sizeWidth} - #{$progressWidth} - #{$rightWidth} -
|
||||
20px
|
||||
);
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
flex-shrink: 0;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.size {
|
||||
margin-right: 10px;
|
||||
max-width: $sizeWidth;
|
||||
/*单行省略号*/
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.progress {
|
||||
max-width: $progressWidth;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: nowrap;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
width: $rightWidth;
|
||||
|
||||
.tip {
|
||||
width: $tipWidth;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
flex-shrink: 0;
|
||||
text-align: right;
|
||||
|
||||
&[color="red"] {
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
&[color="green"] {
|
||||
color: #67c23a;
|
||||
}
|
||||
|
||||
&[color="blue"] {
|
||||
color: #409eff;
|
||||
}
|
||||
}
|
||||
// 上传队列记录右侧操作按钮
|
||||
.icon-btns {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
.icon-btn {
|
||||
display: none;
|
||||
&[show] {
|
||||
margin-left: 15px;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #409eff11;
|
||||
color: #409eff;
|
||||
.left {
|
||||
// 移入上传队列记录左侧操作按钮
|
||||
.icon-btns {
|
||||
.icon-btn {
|
||||
display: block;
|
||||
&:last-of-type {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.right {
|
||||
.tip {
|
||||
margin-right: 15px;
|
||||
}
|
||||
// 移入上传队列记录右侧操作按钮
|
||||
.icon-btns {
|
||||
.icon-btn {
|
||||
display: block;
|
||||
&:first-of-type {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
font-weight: normal;
|
||||
flex-shrink: 0;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
width: 100%;
|
||||
height: $footerHeight;
|
||||
box-sizing: border-box;
|
||||
padding: 10px 20px;
|
||||
margin-bottom: -20px;
|
||||
background: linear-gradient(white, #eff2f7);
|
||||
color: #909399;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: nowrap;
|
||||
white-space: nowrap;
|
||||
* {
|
||||
font-weight: normal;
|
||||
}
|
||||
.text {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.progress {
|
||||
max-width: 200px;
|
||||
flex-grow: 1;
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: nowrap;
|
||||
label {
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
margin-right: 5px;
|
||||
}
|
||||
>>> .el-progress {
|
||||
white-space: nowrap;
|
||||
.el-progress__text {
|
||||
font-weight: normal;
|
||||
font-size: 14px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
Loading…
x
Reference in New Issue
Block a user