封装一个可以最小化和展开的弹窗组件

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弹窗最小化的实现方式,发现是对节点进行克隆,所以每最小化一个弹窗就会多产生一个节点。

相关推荐

  1. react写一个从下往上划出组件

    2024-05-10 07:12:14       29 阅读
  2. 一个展开收起业务组件(React)

    2024-05-10 07:12:14       41 阅读
  3. Vue3封装可拖拽

    2024-05-10 07:12:14       50 阅读
  4. el-dialog 实现可以拖动

    2024-05-10 07:12:14       27 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-05-10 07:12:14       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-05-10 07:12:14       100 阅读
  3. 在Django里面运行非项目文件

    2024-05-10 07:12:14       82 阅读
  4. Python语言-面向对象

    2024-05-10 07:12:14       91 阅读

热门阅读

  1. Jenkins中构建Spring服务并推送到AWS ECR+AWS EKS

    2024-05-10 07:12:14       34 阅读
  2. github 大项目断点续传

    2024-05-10 07:12:14       32 阅读
  3. android 手机和平板如何适配

    2024-05-10 07:12:14       28 阅读
  4. Github 学习使用随手记

    2024-05-10 07:12:14       33 阅读
  5. 【Web漏洞指南】DOM XSS

    2024-05-10 07:12:14       26 阅读