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"><ititle="缩小"class="iconfont icon-segi-icon-minus"style="font-size: 14px"@click="toCollapse"></i><ititle="关闭"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"><ititle="放大"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弹窗最小化的实现方式,发现是对节点进行克隆,所以每最小化一个弹窗就会多产生一个节点。