gl-dialog
大概思路:
在弹窗组件内部引入gl-dialog-collapse,这个组件主要用于存储已经被最小化的弹窗(基础数据)
弹窗内部的数据如何在父组件拿到是通过作用域插槽来实现的
gl-dialog接收一个tempData这个数据会在内部被记录下来,然后通过插槽的形式传递给父组件,供父组件使用
<template>
<div class="gl-dialog" ref="dialogRef">
<el-dialog
:close-on-click-modal="false"
v-bind="$attrs"
:visible.sync="selfVisible"
:show-close="false"
>
<div class="dialog-header" slot="title">
<div class="left-title">{{ currentData.dialogTitle }}</div>
<div class="right-icon">
<i
title="缩小"
class="iconfont icon-segi-icon-minus"
style="font-size: 14px"
@click="toCollapse"
></i>
<i
title="关闭"
class="iconfont icon-Close"
style="font-size: 14px;font-weight: bold;"
@click="closeDialog"
></i>
</div>
</div>
<slot :tempData="currentData"></slot>
<footer>
<slot name="footer" :tempData="currentData" />
</footer>
</el-dialog>
<gl-dialog-collapse :dialogList="dialogList"></gl-dialog-collapse>
</div>
</template>
<script>
import _ from "lodash";
export default {
name: "gl-dialog",
props: {
visible: {
type: Boolean,
default: () => false,
},
title: String,
type: {
type: String,
default: () => "default",
},
tempData: Object,
},
data() {
return {
ddd: "okok",
dialogList: [],
currentData: {},
count: 0,
isExpand: false,
// dialogId: "",
};
},
computed: {
dialogId() {
return this.tempData.dialogTitle + this.count;
},
selfVisible: {
get() {
return this.visible;
},
set(value) {
this.$emit("update:visible", value);
},
},
},
watch: {
visible(N) {
if (N) {
if (!this.isExpand) {
this.currentData = _.cloneDeep(this.tempData);
this.currentData.dialogTitle = this.title;
this.currentData.dialogId = this.dialogId;
} else {
this.count++;
}
}
},
},
methods: {
toCollapse() {
const targetIndex = this.dialogList.findIndex((item) => {
const { dialogId } = this.currentData;
return dialogId == item.dialogId;
});
const isExist = targetIndex >= 0;
if (!isExist) {
this.dialogList.push({
type: this.type,
title: this.currentData.dialogTitle,
dialogId: this.dialogId,
tempData: this.currentData,
expandCallBack: (tempData) => {
this.isExpand = true;
this.currentData = tempData;
this.currentData.dialogId = tempData.dialogId;
this.selfVisible = true;
},
});
this.count++;
}
this.isExpand = false;
this.selfVisible = false;
},
closeDialog() {
this.isExpand = false;
this.selfVisible = false;
if (!this.dialogList.length) return;
const targetIndex = this.dialogList.findIndex((item) => {
const dialogId = this.isExpand
? this.currentData.dialogId
: this.dialogId;
return dialogId == item.dialogId;
});
if (targetIndex >= 0) {
this.dialogList.splice(targetIndex, 1);
}
},
},
};
</script>
<style lang="scss" scoped>
.dialog-header {
display: flex;
align-items: center;
justify-content: space-between;
}
// ::v-deep .dialog-footer {
// text-align: right!important;
// }
.iconfont {
cursor: pointer;
}
footer {
text-align: right;
}
</style>
gl-dialog-collapse.vue
<template>
<div class="gl-dialog-collapse">
<div class="collapse-item" v-for="(item, index) in dialogList" :key="index">
<div class="title">{{ item.title }}</div>
<div class="right-icons">
<i
title="放大"
class="iconfont icon-icf_full_screen_arrow"
@click="toExpand(item)"
></i>
<i title="关闭" class="iconfont icon-Close" @click="closeDialog"></i>
</div>
</div>
</div>
</template>
<script>
export default {
name: "gl-dialog-collapse",
props: {
dialogList: {
type: Array,
default: [],
},
},
methods: {
toExpand(item) {
const { expandCallBack, tempData } = item;
expandCallBack(tempData);
this.closeDialog(item);
},
closeDialog(item) {
const { dialogId } = item;
const targetIndex = this.dialogList.findIndex(
(item) => item.dialogId == dialogId
);
this.dialogList.splice(targetIndex, 1);
},
},
};
</script>
<style lang="scss" scoped>
.gl-dialog-collapse {
display: flex;
column-gap: 5px;
position: fixed;
left: 201px;
bottom: 0px;
}
.collapse-item {
padding: 5px 10px;
display: flex;
align-items: center;
column-gap: 20px;
border: 1px solid #ccc;
background-color: #fff;
.title {
font-size: 14px;
}
}
.right-icons {
i {
cursor: pointer;
font-size: 16px;
}
}
</style>
实现效果:
每个最小化的弹窗内部数据都是独立的,因为gl-dialog-collapse内部维护了一个已经被折叠的弹窗数组。
内部的数据结构:
{
type: this.type,
title: this.currentData.dialogTitle,
dialogId: this.dialogId,
tempData: this.currentData,
expandCallBack: (tempData) => {
this.isExpand = true;
this.currentData = tempData;
this.currentData.dialogId = tempData.dialogId;
this.selfVisible = true;
},
}
dialogId用于记录唯一弹窗,方便回显数据,以及关闭目标弹窗
反思:当时考虑用cloneNode来实现弹窗的复制,但是考虑到vue里面是通过数据来驱动视图,能够成功复制弹窗,但是里面的交互会失效,所以感觉这种方案会很复杂,所以放弃。中途参考过layui弹窗最小化的实现方式,发现是对节点进行克隆,所以每最小化一个弹窗就会多产生一个节点。