el-popover嵌套内层子el-popover自动关闭失效

682 阅读1分钟

项目依赖

{
  "dependencies": {
    "element-ui": "^2.12.0",
    "vue": "2.7.16"
  }
}

现象

popover(外层)嵌套的内容区域再嵌套一层popover(内层),当popover都打开了,点父popover(外层)的内容区域但是popover(内层)无法关闭隐藏!

简化代码后效果如下

1733661482029.gif

示例代码

<template>
  <div class="popover-example">
    <el-popover popperClass="parent-popover">
      <template #reference>
        <h6>parent-popover reference</h6>
      </template>

      <div class="parent-popover__content">
        <p>parent-popover reference</p>
        <p>parent-popover reference</p>
        <p>parent-popover reference</p>

        <el-popover popperClass="child-popover">
          <template #reference>
            <h6>child-popover reference</h6>
          </template>

          <div class="child-popover__content">
            <p>parent-popover reference</p>
            <p>parent-popover reference</p>
            <p>parent-popover reference</p>
            <p>parent-popover reference</p>
          </div>
        </el-popover>
      </div>
    </el-popover>
  </div>
</template>

解决

关键思路

点击popover(内层)内容插槽区域外的执行关闭popover(内层)

利用 element-ui 内置 v-click-out-side 指令

由于popover组件使用频率高,需要对popover进行二次封装处理

./components/base-poopver.vue

<template>
  <el-popover
    ref="popover"
    v-bind="popoverOptions"
    v-clickoutslide="onClickOueSide"
    v-on="$listeners"
  >
    <template v-for="(value, name) in $slots" #[name]>
      <slot :name="name"></slot>
    </template>
  </el-popover>
</template>

<script>
import clickOutSide from 'element-ui/src/utils/clickoutside';

export default {
  directives: {
    clickoutslide: clickOutSide,
  },
  inheritAttrs: true,
  data() {
    return {
      popperElm: null,
    };
  },
  computed: {
    popperClassList() {
      let className = 'el-popover-base';
      if (this.$attrs.popperClass) {
        className += ` ${this.$attrs.popperClass}`;
      }
      return className;
    },
    popoverOptions() {
      return {
        ...this.$attrs,
        popperClass: this.popperClassList,
      };
    },
    popperId() {
      return this.$refs.popover.tooltipId;
    },
  },
  mounted() {
    // 【必须】v-clickoutslide 内部使用包含元素
    this.popperElm = document.querySelector(`#${this.popperId}`); // 即当前popover点击弹出弹窗元素
  },
  methods: {
    closePopover() {
      this.$refs.popover.doClose();
    },
    onClickOueSide() {
      this.closePopover();
    },
  },
};
</script>

项目使用

<template>
  <div class="popover-example">
    <base-popover popperClass="parent-popover">
      <template #reference>
        <h6>parent-popover reference</h6>
      </template>

      <div class="parent-popover__content">
        <p>parent-popover reference</p>
        <p>parent-popover reference</p>
        <p>parent-popover reference</p>

        <base-popover popperClass="child-popover">
          <template #reference>
            <h6>child-popover reference</h6>
          </template>

          <div class="child-popover__content">
            <p>parent-popover reference</p>
            <p>parent-popover reference</p>
            <p>parent-popover reference</p>
            <p>parent-popover reference</p>
          </div>
        </base-popover>
      </div>
    </base-popover>
  </div>
</template>

最终效果

1733663518972.gif