vue3.2+element-plus 关于el-table+el-popover的虚拟触发

3,835 阅读1分钟

新项目开始接触vue3.2,主要是把项目从div+css升级到vue3.2,挑战挺多,心得也挺多的。刚好昨晚一个 el-table+el-popover的组件,来分享一下,抛砖引玉。 逻辑是通过virtual-ref去获取poppver出现的位置,定义动态的tempRef去做虚拟触发,让一个vue页面能复用同一个popover,避免重复加载dom影响性能。 tempRef一直获取dom失败,差点怀疑人生,后来发现我犯了个低级错误,类型定义错了,定义为string,改为 Object就好了..

目前组件还有一个小bug:点击空白处时,popover没有消失,目前了解到 可以通过import { vClickOutside } from 'element-plus' 解决,等晚点我试验成功再把代码补充上来。(已通过其他方法解决)

-------分割线-------

补充1. 不知为何我这边用v-click-outside会报错,所以改为判断popover是否失去焦点blur,去控制隐藏浮窗。

封装 el-popover 子组件

<script setup name="SamplePopover">
import { ref, toRefs, onMounted, onUnmounted, computed } from 'vue'
const props = defineProps({
  modelValue: Boolean,
  tempRef: {
    type: Object,
    default: () => ({})
  },
  parentInfo: {
    type: Object,
    default: () => ({
      id: String,
      title: String
    })
  }
})

const { delInfo, modelValue } = toRefs(props)

const emit = defineEmits(['update:parentInfo', 'update:modelValue', 'on-submit'])

const cancel = () => {
  emit('update:parentInfo',{
    id: '',
    title: ''
  })
  emit('update:modelValue', false)
}

const isSubmit = ref(false)
const submit = () => {
    isSubmit.value = true
    emit('on-submit')
}

const visible = computed({
  get: () => modelValue.value,
  set: (val) => {
    emit('update:modelValue', val)
  }
})

const handleBlur = async () => {
  // addEventListener('blur') 执行比按钮事件快,因此需要加个延时
  setTimeout(() => {
    if (isSubmit.value) {
    } else {
      cancelDel()
    }
  }, 100)
}

onMounted(() => {
    const _popover = document.querySelector('.delete-popper')
    nextTick(() => {
       _popover.value.focus()
       _popover.value.addEventListener('blur', handleBlur)
    })

     // 监听table是否滚动,滚动则隐藏popover
     document.querySelector('.table-view__body .el-scrollbar__wrap').addEventListener('scroll', cancelDel)
})
onUnmounted(() => {
  if (!isSubmit.value) isSubmit.value = false
  if (delPop.value) delPop.value = false
  document.querySelector('.table-view__body .el-scrollbar__wrap').removeEventListener('scroll', cancelDel)
})
</script>

<template>

  <ElPopover
      :visible="visible"
      :virtual-ref="props.tempRef"
      virtual-triggering
      trigger="click">
    <div>获取的{{id}} 和 {{title}}</div>
    <div class="popover-footer">
        <ElButton link @click="cancel">取消</ElButton>
        <ElButton type="primary" @click="submit">确定</ElButton>
    </div>
  </ElPopover>
</template>

关于el-popover的 hooks文件

/* sample popover hooks */
import { ref, nextTick } from 'vue'

export function usePop() {
    const visiblePop = ref(false)
    const tempRef = ref()
    const parentData = ref({
        id: '',
        title: ''
    })

    const showPop = (e, row,attr) => {
        const evt = e || window.e || window.event
        parentData.value.id = row.id
        parentData.value.title = row[attr]

        // 通过设置visiblePop=false 销毁poppover, 解决闪现问题
        if (tempRef.value) visiblePop.value = false
        nextTick(() => {
            tempRef.value = evt.currentTarget
            visiblePop.value = true
        }).then()
    }

    const canceled = () => {
        delData.value.id = ''
        delData.value.title = ''
        visiblePop.value = false
    }

    return {
        canceled,
        parentData,
        visiblePop,
        tempRef,
        showPop
    }
}

父组件 el-table 中引入使用

<script setup name="Father">
import SamplePopover from '@/components/SamplePopover.vue'
import { usePop } from '@/hooks/use-popover'
const { canceled, parentData, visiblePop, tempRef, showPop } = useDelPop()
const popFunc=()=>{
    ...
}
</script>

<template>
  <div>
    <ElTable :data="data">
      <ElTableColumn prop="sort" label="排序" width="100"/>
      <ElTableColumn prop="remarks" label="描述"/>
      <ElTableColumn label="操作" width="140" align="center" fixed="right" #default="{row}">
        <ElButton size="small" @click="e=>showPop(e,row,'paramName')">打开popover</ElButton>
      </ElTableColumn>
    </ElTable>
   <!--  popover 子组件 需要利用v-if再每次关闭后销毁  -->
    <template v-if="visiblePop">
      <SamplePopover :temp-ref="tempRef" v-model:parent-info="parentData" v-model="visiblePop" @on-submit="popFunc"/>
    </div>
</template>