类似Tree结构的循环多条数据下,Popover弹出框的显隐实现

831 阅读3分钟

背景

最近项目中需要实现一个需求,“点击结果列表中的每一项都出现其对应的详情弹框。”

这种需求最适合使用popover来实现了。

trigger属性用于设置何时触发 Popover,支持四种触发方式:hoverclickfocus  和 manual。对于触发 Popover 的元素,有两种写法:

  1. 使用 slot="reference"的具名插槽.
  2. 使用自定义指令v-popover指向 Popover 的索引ref。

两种元素实现方式可查看官方文档:弹出框 Popover。在本文中,只使用第一种slot方式。

需求设计和实现

触发方式的选用

现在分析上面我的需求,出现了“点击”二字,貌似看起来使用click点击触发和manual手动触发都可以实现。
但是,经过我的实践,手动触发被我pass掉了。
原因是,manual的实现方式是靠v-model判断显隐条件。

<el-popover 
    placement="right" 
    width="400" 
    trigger="manual" 
    v-model="visible" 
>
     <el-form ref="form" 
     ...
     >
     ...
     </el-form>
     <el-button slot="reference">click出现</el-button>
</el-popover>

通过v-model的方式设置一个visible,控制visible的值,是可以解决单个表单的Popover弹出框显隐问题的。

但是我的需求是要在Tree树结构上做每条数据的popover。只靠一个visible值会导致所有popover弹窗同时出现和同时隐藏

这一点儿也不满足要求!!!

所以,要用click了。同时还有一个好处,使用click的触发方式还能自带一些功能。比如

  1. 二次点击该数据可隐藏弹框。
  2. 点击页面空白处也可隐藏弹框。

Tree树结构中的显隐条件实现

解决办法是:通过每条数据的唯一id给每个popover增加一个ref,用来判断自己的显隐。

<el-tree
    :data="attrsList"
    node-key="index"
  >
    <div
      class="attr-list"
      slot-scope="{ node, data }"
    >
      <div
        class="attr-list-item"
        @click="handleClick(data, node)"
      >
          <el-popover
            :ref="'handle' + node.data.id"
            placement="left"
            width="250"
            trigger="click"
            >
              <el-form ref="form" :model="form" >
                  <el-form-item>
                  ...
                  </el-form-item>
               </el-form>
               <el-button @click="handleffirm" >确定</el-button>
               <div class="attr-list-item-content" slot="reference">
                     {{node.label}}
               </div>
          </el-popover>
      </div>
   </div>
 </el-tree>

我给每条数据增加了一个点击事件handleClick,和一个data字段selectedPoppverId用于控制前一个popover的隐藏和最新一个popover的展示。

<script>
    export default { 
        name: 'treeTest', 
        data () { 
            return { 
                form: {...},
                selectedPoppverId:''
            }
        },
        methods: { 
            handleClick () { 
              //隐藏上一个显示的popover
              let refName = 'handle' + this.selectedPoppverId;
              //第一次点击时,this.$refs[refName]为空,所以需要做个是否为空的判断
              this.$refs[refName] && this.$refs[refName].doClose();
              //关闭上一个popover后,给selectedPoppverId赋值新点击的那条数据的id
              this.selectedPoppverId = node.data.id;
              //显示新点击的数据的popover
              refName = 'handle' + this.selectedPoppverId;
              this.$refs[refName].doShow();
            },
        } 
    } 
</script>

一些问题和优化点

有时候doClose不生效?

在实现时,ref使用变量的形式,因为是多个循环的popover。如果需要点击确定来关闭el-popover窗口,却发现只引用doClose方法不生效,关不掉。
那么可以通过模拟点击页面空白处来实现。简单高效。。。

//点击确定时,关闭popover,最简单直接的方式就是模拟点击页面空白处
handleffirm () {
  document.body.click();
},

Tree结构中,我想让他点击父节点不出现popover,点击子节点的时候才出现。

handleClick里加个判断就好了。

if(node.childNodes.length === 0) {
    ...//当前节点没有子节点的时候才执行doShow()
}

popover中内容过高,丢到可视范围外了

在使用2.x版本的element-UI时,里面的Popover 气泡卡片组件,有自带的自动适应位置的功能;在 element plus 中,直接使用,已经无法自适应气泡弹窗的位置了。

想要在页面里,当 Popover 弹出框的位置接近边界时,应该自动切换到相反的方向进行显示。

<fin-popover
    placement="left"
    width="250"
    trigger="click"
    :fallback-placements="['bottom', 'top', 'right', 'left']"
>
</fin-popover>
<!----**注意:**  适用于 `ele-plus`版本, `2.x` 版本的 `element` 自带有这个功能;--->

我popover中展示的都是动态获取的数据,怎么数据不对呀

上面实现了多条数据popover的显隐效果。当然,一般的项目需求中,popover内的数据也都是动态获取的。那么就需要手动做实时更新。在执行doShow方法前先执行updatePopper方法。

if( this.$refs[refName]) {
    this.$refs[refName].updatePopper()
    this.$nextTick(() => {
      this.$refs[refName].doShow();
    })
}

以上,大致就能实现普通的项目需求了。其实也能看出来,popover和tooltip的功能很相似。

官方文档上都说了,“Popover 的属性与 Tooltip 很类似,它们都是基于Vue-popper开发的,因此对于重复属性,请参考 Tooltip 的文档,在此文档中不做详尽解释。”