拖动组件:
<template>
<div
ref="file-list-win"
class="file-list-win"
v-draggable="{limit: true, left: 256, top: 0 }"
:class="{ min: min == true }"
style="bottom: 50px"
>
<!-- 注意: 如果有拖动的要求,bottom最好不要写在样式文件中,直接写在div的style中,拖动时会清除bottom样式 -->
<!-- 头部 -->
<div class="title dragTitle">
<div>
<i v-if="getIfAllDone == 1" class="el-icon-success success"></i>
<div v-else class="loading">
<img :src="require('@/assets/images/management/file/loading.png')">
</div>
</div>
<div class="msg">
<p v-if="getIfAllDone == 1">上传 成功(<span class="success-color">{{ getSuccessCount }}</span>项) 失败(<span class="fail-color">{{ getFailCount }}</span>项)</p>
<p v-else>正在上传 - 剩余 {{ fileList.length - getDoneCount }} 项</p>
</div>
<div class="btns">
<el-button type="info" circle @click="close()">
<i class="el-icon-close"></i>
</el-button>
<el-button type="primary" circle v-if="!min" @click="clickSlide()">
<i class="el-icon-arrow-up"></i>
</el-button>
<el-button type="info" circle v-if="min" @click="clickSlide()">
<i class="el-icon-arrow-down"></i>
</el-button>
</div>
</div>
<div class="content">
<ul>
<li
v-for="(file, index) in fileList"
:key="`item.taskId-${index}`"
>
<div class="bg" v-if="file.status == 'loading' && file.progress > 0" :style="`width: ${file.progress * 100}%`"></div>
<div class="bg" v-else style="width: 0"></div>
<div class="info">
<div class="tb">
<template v-if="file.status == 'loading'">
<img :src="require('@/assets/images/management/file/file.png')">
</template>
<template v-else>
<img v-if="file.ext =='mp4'" :src="require('@/assets/images/management/file/video.png')"/>
<img v-else-if="file.ext =='mp3'" :src="require('@/assets/images/management/file/video.png')"/>
<img v-else-if="file.ext =='docx' || file.ext =='doc'" :src="require('@/assets/images/management/file/word.png')"/>
<img v-else-if="file.ext =='pptx' || file.ext =='ppt'" :src="require('@/assets/images/management/file/file.png')"/>
<img v-else-if="file.ext =='xlsx' || file.ext =='xls'" :src="require('@/assets/images/management/file/excel.png')"/>
<img v-else-if="file.ext =='pdf'" :src="require('@/assets/images/management/file/pdf.png')"/>
<img v-else :src="require('@/assets/images/management/file/file.png')"/>
</template>
</div>
<div class="msg">
<p class="name">{{ file.name }}</p>
<p class="other">
<span>{{ file.size }}MB</span>
<span class="success" v-if="file.status=='success'">上传成功</span>
<span class="fail" v-if="file.status=='fail'">上传失败</span>
</p>
</div>
</div>
</li>
</ul>
</div>
</div>
</template>
<script>
import { mapState } from 'vuex';
export default {
name: 'FileList',
components: {},
props: {
fileList: {
type: Array,
require: true
}
},
data () {
return {
min: false,
height: 0 //存储div本身的高度,展开时计算用到
};
},
computed: {
// 计算完成的数量
getDoneCount () {
return this.fileList.filter(
(item) => item.status == 'success' || item.status == 'fail'
).length;
},
// 计算全部导出任务是否完成
getIfAllDone () {
let doneCount = this.getDoneCount;
return doneCount == this.fileList.length ? 1 : 2;
},
// 计算成功的数量
getSuccessCount () {
return this.fileList.filter(
(item) => item.status == 'success'
).length;
},
// 计算失败的数量
getFailCount () {
return this.fileList.filter(
(item) => item.status == 'fail'
).length;
},
},
watch: {},
mounted () {
},
methods: {
// 收起展开
clickSlide () {
this.min = !this.min;
if (!this.min) {
// 展开
if (this.$refs['file-list-win'].style.top) {
// 移动过div
let top = this.$refs['file-list-win'].style.top;
let topValue = 0;
if (top.indexOf('px') > -1) {
let temp = top.substring(0, top.indexOf('px'));
topValue = parseInt(temp);
} else {
topValue = parseInt(top);
}
let h1 = window.innerHeight;
if (topValue + this.height > h1) {
// 如果div本身的高度+离顶部的高度 大于 页面的高度,则重新计算div离顶部的高度
let temp_top = h1 - this.height - 50;
this.$refs['file-list-win'].style.top = temp_top+'px';
}
} else {
// 未移动过div,不处理
}
} else {
// 收起的时候存下div的高度
this.height = this.$refs['file-list-win'].offsetHeight;
}
},
// 关闭
close () {
if (this.getIfAllDone == 1) {
this.$emit('closeFileList');
} else {
this.$message({
type: "warning",
message: "有正在进行的任务,请勿关闭!",
});
}
},
}
};
</script>
<style lang="scss" scoped>
.file-list-win {
position: fixed;
right: vw(15);
// bottom: 50px
width: vw(400);
border-radius: 6px;
z-index:999;
background: #ffffff;
box-shadow: rgba(0, 0, 0, 0.1) 0px 3px 5px, rgba(0, 0, 0, 0.06) 0px 6px 10px,
rgba(0, 0, 0, 0.06) 0px 1px 16px;
overflow: hidden;
.title {
display: flex;
padding: vh(16) vw(24);
line-height: vh(24);
border-bottom: 1px solid #e9e9e9;
.success {
color: #66b165;
font-size: 22px;
margin-top: 2px;
}
.loading {
width: vw(30);
height: vw(30);
img {
position: relative;
top: -2px;
width: 100%;
height: 100%;
}
}
.msg {
flex: 1;
margin-left: vw(10);
.total {
color: #909399;
}
}
.btns {
margin-top: -1px;
.el-button--medium.is-circle {
padding: 2px;
}
i {
cursor: pointer;
font-size: 20px;
}
}
}
.content {
max-height: vh(380);
overflow: auto;
ul {
padding: 0;
li {
position: relative;
margin-bottom: vh(10);
.bg {
height: vh(70);
background: #F5F5F6;
border-bottom: 2px solid #1890FF;
z-index: 0;
}
.info {
position: absolute;
left: 0;
top: 0;
z-index: 1;
padding: vh(10) vw(20) vh(10) vw(30);
height: vh(50);
display: flex;
margin-bottom: vh(10);
// &:hover {
// background-color: #f5f6f6;
// }
.tb {
width: vw(40);
height: vw(40);
line-height: vw(40);
background: #EAF5FF;
border-radius: 6px;
text-align: center;
}
.msg {
margin-left: vw(10);
flex: 1;
.name {
width: vw(240);
height: vh(30);
word-wrap: break-word; /*强制换行*/
overflow: hidden; /*超出隐藏*/
text-overflow: ellipsis; /*隐藏后添加省略号*/
white-space: nowrap; /*强制不换行*/
margin: 0;
}
.other {
color: #999999;
margin: 0;
.success {
color: #20A80D;
margin-left: vw(10);
}
.fail {
color: red;
margin-left: vw(10);
}
}
}
.btns {
i {
cursor: pointer;
margin-left: vw(10);
}
.percent {
margin-left: vw(10);
}
}
}
}
}
}
.success-color {
color: #20A80D;
}
.fail-color {
color: red;
}
}
.min {
height: vh(60);
.content {
display: none;
}
}
</style>
拖动指令:
export default {
install (Vue) {
// 拖动指令,绑定在div上,
// 被绑定的div的css样式不要有bottom属性,如果初始化需要用到bottom定位,请直接写在div的style中
// binding 传值可选 {limit: true, left: 256, top: 0 }
// limit - 有范围限制,如传入false,可能会拖到父级DOM元素外
// left - 拖动div离父级DOM左侧最小距离
// top - 拖动div离父级DOM上侧侧最小距离
Vue.directive('draggable', {
// 当被绑定的元素挂载到 DOM 上时...
bind (el, binding, vnode) {
const dragTitleEl = el.querySelector('.dragTitle'); // 获取弹窗标题栏元素
dragTitleEl.style.cssText += ';cursor:move;'
console.log('binding: ', binding);
// 添加鼠标按下事件监听
el.onmousedown = function(e) {
// 获取鼠标点击位置相对于元素左上角的x,y坐标
let disX = e.clientX - el.offsetLeft;
let disY = e.clientY - el.offsetTop;
// 添加鼠标移动事件监听
document.onmousemove = function(e) {
// 计算元素的新位置
let l = e.clientX - disX;
let t = e.clientY - disY;
// 限制拖拽范围
if (binding.value.limit) {
let limit_l = binding.value.left || 0;
let limit_t = binding.value.top || 0;
if (l < limit_l) {
l = limit_l;
}
if (t < limit_t) {
t = limit_t;
}
if (l > window.innerWidth - el.offsetWidth) {
l = window.innerWidth - el.offsetWidth;
}
if (t > window.innerHeight - el.offsetHeight) {
t = window.innerHeight - el.offsetHeight;
}
}
// 清空之前的样式
el.style.cssText = "";
// 设置元素的位置
el.style.left = l + 'px';
el.style.top = t + 'px';
};
// 添加鼠标抬起事件监听,用于停止拖拽
document.onmouseup = function(e) {
document.onmousemove = null;
document.onmouseup = null;
};
};
}
})
}
}
出现错误的原因:
由于最初要求div是定位到右下角,所以使用了bottom属性(且写在样式文件中),在拖动计算过程中,只计算left 和 top属性,所以向上拖动时,由于bottom属性一直存在,div离下边缘的距离不变,但是left属性使div离上边缘的距离变了,就出现了把div高度拖高的问题。
解决方法:
1、bottom直接写在div的style中;
2、拖动过程中使用el.style.cssText = ""; 清空样式。