自制弹出框组件

一. VUE

没有使用vue-popover插件,因为功能不需要太多,只需要上下左右弹出

ww 视口宽
elm.offsetLeft 这div的左侧偏移量,不算margin;(也没margin)
elm.clientWidth 这div的宽,不包括border和margin;是用按钮撑开宽
this.width 设置弹出窗口的宽

<template>
  <div
    class="zoehis_popover"
  >
    <div ref="popRef" :placement="placement" @mouseenter="mouseenterEvn" @mouseleave="mouseleaveEvn" v-clickoutside="() => clickEvt(false)" @click="clickEvt(true)" class="zoehis_popover_el">
        <slot name="reference"></slot>
    </div>
    <div class="zoehis_popover_content" v-show="contMenu.isShowContMenu"
        :style="{left:contMenu.offsetX+'px',top:contMenu.offsetY+'px',width:newWidth,height:newHeight }"
        @mouseenter="popMouseenterEvt" @mouseleave="hidePop" @click="clickEvt(true)">
        <slot></slot>
    </div>
  </div>
</template>
<script type="text/babel">
  export default {
    name: 'ZoehisPopover',
    data () {
        return {
            contMenu: {
                offsetX: 0,//弹窗与左侧距离
                offsetY: 0,//弹窗与顶部距离
                isShowContMenu: false//弹窗显示
            },
        }
    },
    props: {
        width:{
            type:[Number,String],
            default:80
        },
        height:{
            type:[Number,String],
            default:60
        },
        trigger:{// 触发方式hover/click
            type:String,
            default:'hover'
        },
        placement:{// 弹出位置right/left/bottom/top
            type:String,
            default:'right'
        }
    },
    computed: {
        newWidth() {
            const re = /^[0-9]+\.?[0-9]*$/;
            if (re.test(this.width)) {
                return `${this.width}px`
            }
            return this.width
            },
        newHeight() {
            const re = /^[0-9]+\.?[0-9]*$/;
            if (re.test(this.height)) {
                return `${this.height}px`
            }
            return this.height
        },
        nWidth() {
            const re = /^\d+px$/
            if (re.test(this.width)) {
                return this.width.substring(0,this.width.length - 2)
            }
            return this.width
        },
        nHeight() {
            const re = /^\d+px$/
            if (re.test(this.height)) {
                return this.height.substring(0,this.height.length - 2)
            }
            return this.height
        }

    },
    methods: {
        mouseenterEvn() {
            if(this.trigger==='hover'){
                this.show()
            }
        },
        mouseleaveEvn() {
            if(this.trigger==='hover'){
                this.hide()
            }
        },
        clickEvt(flag) {
            if(this.trigger==='click'){
                flag?this.show():this.hide()
            }
        },
        show() {
            const elm = this.$refs.popRef
            let offsetWw, offsetWh
            let ww = document.body.clientWidth
            let wh = document.body.clientHeight
            if (this.placement === 'right') {
              offsetWw = ww - elm.offsetLeft - elm.clientWidth < this.nWidth && elm.offsetLeft>=this.nWidth ? elm.offsetLeft - this.nWidth : elm.offsetLeft + elm.clientWidth
              offsetWh = wh - elm.offsetTop < this.nHeight && elm.offsetTop>=this.nHeight ? elm.offsetTop - this.nHeight + elm.clientHeight : elm.offsetTop
            } else if (this.placement === 'left') {
              offsetWw = ww - elm.offsetLeft - elm.clientWidth >= this.nWidth && elm.offsetLeft < this.nWidth ? elm.offsetLeft + elm.clientWidth : elm.offsetLeft - this.nWidth
              console.log(offsetWw)
              offsetWh = wh - elm.offsetTop < this.nHeight && elm.offsetTop>=this.nHeight ? elm.offsetTop - this.nHeight + elm.clientHeight : elm.offsetTop
            } else if (this.placement === 'top') {
              offsetWw = ww - elm.offsetLeft < this.nWidth && elm.offsetLeft >= this.nWidth ? elm.offsetLeft - this.nWidth + elm.clientWidth : elm.offsetLeft
              offsetWh = wh - elm.offsetTop < this.nHeight && elm.offsetTop>=this.nHeight ? elm.offsetTop + elm.clientHeight : elm.offsetTop - this.nHeight
            } else if (this.placement === 'bottom') {
              offsetWw = ww - elm.offsetLeft < this.nWidth && elm.offsetLeft >= this.nWidth ? elm.offsetLeft - this.nWidth + elm.clientWidth : elm.offsetLeft
              offsetWh = wh - elm.offsetTop - elm.clientHeight < this.nHeight && elm.offsetTop>=this.nHeight ? elm.offsetTop - this.nHeight :elm.offsetTop + elm.clientHeight
            }
            this.contMenu = {
                offsetX: offsetWw,
                offsetY: offsetWh,
                isShowContMenu: true
            };
        },
        hide(){
            if(this.trigger==='hover'){
                if (this.hideTimer) {
                    clearTimeout(this.hideTimer);
                }
                this.hideTimer = setTimeout(() => {
                    this.contMenu = {
                        offsetX: 0,//弹窗与左侧距离
                        offsetY: 0,//弹窗与顶部距离
                        isShowContMenu: false//弹窗显示
                    };
                }, 100);
            }else {
                this.contMenu.isShowContMenu=false
            }
        },
        popMouseenterEvt() {
            if (this.hideTimer) {
                clearTimeout(this.hideTimer);
            }
        },
        hidePop() {
            if(this.trigger==='hover'){
                this.contMenu.isShowContMenu=false
            }
        }
    }
  };
</script>

二. index

/**
 * Created by liman on 2021/12/13.
 */
 import ZoehisPopover from './src/popover.vue';
 ZoehisPopover.install = function(Vue) {
   Vue.component(ZoehisPopover.name,ZoehisPopover);
 };
 export default ZoehisPopover;

三. SCSS

.zoehis_popover {
    .zoehis_popover_el {width: max-content;}
    .zoehis_popover_content {
        background: $MAINBG; box-sizing: border-box;box-shadow: $SHADOWVALUE;position: absolute;border-radius: 4px;z-index: 99;
    }
}
posted @ 2022-01-25 17:01  暗鸦08  阅读(65)  评论(0)    收藏  举报