新项目开始接触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>