【Vue】NavBar 顶部弹窗点击弹窗外部区域关闭弹窗实现

需求场景

很常见的功能,NavBar 顶部菜单按钮点击显示自定义的弹窗,【点击页面空白区域关闭弹窗】,类似 el-Popover 弹出框的效果。点击区域外自动关闭并且联动其他弹框,并且同时只能存在一个。

image

代码实现

template 部分

image

scrtpt 部分

image

完整代码

2022-7-11 更新完整代码

点击查看代码
<template>
  <div :class="classObj" class="app-wrapper">
    <div
      v-if="device === 'mobile' && sidebar.opened"
      class="drawer-bg"
      @click="handleClickOutside"
    />
    <div class="main-container">
      <navbar
        @handleAvatarClick="handleAvatarClick"
        @handleCompanySeclect="handleCompanySeclect"
        @handleSearchInputChange="handleSearchInputChange"
        @handleMessageClick="handleMessageClick"
        @handleShareClick="handleShareClick"
      />
      <app-main />
    </div>
    <!--弹窗部分 -->
    <AvatarModal
      class="avtar-modal modal-zIndex"
      ref="Avatar"
      v-if="showModalType === 'Avatar'"
      @closeModal="handleCloseModal"
      @removeEventPageClick="removeEventPageClick"
      @addEventPageClick="addEventPageClick"
    />
    <ShareModal
      class="share-modal modal-zIndex"
      ref="Share"
      v-if="showModalType === 'Share'"
      @closeModal="handleCloseModal"
      @removeEventPageClick="removeEventPageClick"
      @addEventPageClick="addEventPageClick"
    />
    <CompanySelectModal
      class="company-select-modal"
      ref="Company"
      v-if="showModalType === 'Company'"
    />
    <SearchModal
      class="search-modal modal-zIndex"
      ref="Search"
      v-if="showModalType === 'Search'"
    />
    <MessageModal
      class="message-modal modal-zIndex"
      ref="Message"
      v-if="showModalType === 'Message'"
      @closeModal="handleCloseModal"
    />
  </div>
</template>

<script>
import { Navbar, AppMain, TagsView, HeadNavbar } from './components'
import ResizeMixin from './mixin/ResizeHandler'
import AvatarModal from './components/ModalComponents/AvatarModal.vue'
import CompanySelectModal from './components/ModalComponents/CompanySelectModal.vue'
import SearchModal from './components/ModalComponents/SearchModal.vue'
import MessageModal from './components/ModalComponents/MessageModal.vue'
import ShareModal from './components/ModalComponents/ShareModal.vue'

export default {
  name: 'Layout',
  components: {
    Navbar,
    AppMain,
    TagsView,
    HeadNavbar,
    AvatarModal,
    CompanySelectModal,
    SearchModal,
    MessageModal,
    ShareModal,
  },
  mixins: [ResizeMixin],
  data() {
    return {
      showModalType: '', // 控制弹框的显示类型
    }
  },
  computed: {
    sidebar() {
      return this.$store.state.app.sidebar
    },
    device() {
      return this.$store.state.app.device
    },
    classObj() {
      return {
        hideSidebar: !this.sidebar.opened,
        openSidebar: this.sidebar.opened,
        withoutAnimation: this.sidebar.withoutAnimation,
        mobile: this.device === 'mobile',
      }
    },
  },
  beforeDestroy() {
    document.removeEventListener('click', this.bodyCloseModal)
  },
  methods: {
    handleClickOutside() {
      this.$store.dispatch('closeSideBar', {
        withoutAnimation: false,
      })
    },
    handleAvatarClick() {
      this.isShowModal('Avatar')
    },
    handleCompanySeclect() {
      this.isShowModal('Company')
    },
    handleSearchInputChange() {
      this.isShowModal('Search')
    },
    handleMessageClick() {
      this.isShowModal('Message')
    },
    handleShareClick() {
      this.isShowModal('Share')
    },
    // 控制弹窗显隐
    isShowModal(type) {
      // 移除页面监听事件(防止用户通过点击 btn 产生 bug)
      this.removeEventPageClick()
      if (this.showModalType === type) {
        this.showModalType = ''
      } else {
        this.showModalType = type
      }
      // 触发监听
      this.addEventPageClick()
    },
    // 触发页面监听
    addEventPageClick() {
      setTimeout(() => {
        document.addEventListener('click', this.bodyCloseModal)
      }, 100)
    },
    // 移除页面监听
    removeEventPageClick() {
      document.removeEventListener('click', this.bodyCloseModal)
    },
    // 点击外部区域关闭
    bodyCloseModal(e) {
      let self = this
      if (
        this.showModalType &&
        this.$refs[this.showModalType] &&
        !this.$refs[this.showModalType].$el.contains(e.target)
      ) {
        if (self.showModalType) {
          self.showModalType = ''
          self.removeEventPageClick()
        }
      }
    },
    // 主动关闭
    handleCloseModal() {
      this.showModalType = ''
    },
  },
}
</script>

<style rel="stylesheet/scss" lang="scss" scoped>
  @import '~@/styles/mixin.scss';

  .app-wrapper {
    @include clearfix;
    position: relative;
    height: 100%;
    width: 100%;

    &.mobile.openSidebar {
      position: fixed;
      top: 0;
    }
  }

  .drawer-bg {
    background: #000;
    opacity: 0.3;
    width: 100%;
    top: 0;
    height: 100%;
    position: absolute;
    z-index: 999;
  }

  .modal-zIndex {
    z-index: 2000;
  }

  .avtar-modal {
    position: fixed;
    right: 5PX;
    top: 60PX;
  }

  .company-select-modal {
    position: fixed;
    right: 196PX;
    top: 60PX;
  }

  .search-modal {
    position: fixed;
    right: 406PX;
    top: 60PX;
  }

  .message-modal {
    position: fixed;
    right: 75PX;
    top: 60PX;
  }

  .share-modal {
    position: fixed;
    right: 126PX;
    top: 60PX;
  }
</style>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值